Перегрузка оператора с выделением памяти? - PullRequest
3 голосов
/ 25 марта 2009

Следующее предложение взято из Положительного наследия C ++ и Java Брюса Эккеля о перегрузке операторов в C ++:

C ++ имеет как выделение стека, так и кучу распределение и вы должны перегрузить операторы для обработки всех ситуаций и не вызывает утечки памяти. Сложно на самом деле.

Я не понимаю, как перегрузка операторов имеет какое-либо отношение к выделению памяти. Кто-нибудь может объяснить, как они соотносятся?

Ответы [ 4 ]

3 голосов
/ 25 марта 2009

Я могу представить пару возможных интерпретаций:

Во-первых, в C ++ new и delete оба фактически являются операторами; если вы решите обеспечить пользовательское поведение выделения для объекта путем перегрузки этих операторов, вы должны быть очень осторожны при этом, чтобы не допустить утечки.

Во-вторых, некоторые типы объектов требуют перегрузки operator=, чтобы избежать ошибок управления памятью. Например, если у вас есть объект интеллектуального указателя подсчета ссылок (например, Boost shared_ptr), вы должны реализовать operator=, и вы должны быть уверены, что делаете это правильно. Рассмотрим этот неработающий пример:

template <class T>
class RefCountedPtr {
public:
    RefCountedPtr(T *data) : mData(data) { mData->incrRefCount(); }
    ~RefCountedPtr() { mData->decrRefCount(); }
    RefCountedPtr<T>& operator=(const RefCountedPtr<T>& other) {
        mData = other.mData;
        return *this;
    }
    ...
protected:
    T *mData;
};

Реализация operator= здесь не работает, поскольку она не управляет подсчетом ссылок на mData и other.mData: она не уменьшает счетчик ссылок на mData, что приводит к утечке; и он не увеличивает счетчик ссылок на other.mData, что приводит к возможному отказу памяти в будущем, поскольку объект, на который указывают, может быть удален до того, как все фактические ссылки исчезнут.

Обратите внимание, что если вы явно не объявите свой собственный operator= для своих классов, компилятор предоставит реализацию по умолчанию, поведение которой будет идентично реализации, показанной здесь, то есть полностью не работает для этого конкретного случая. *

Так, как говорится в статье, - в некоторых случаях вы должны перегружать операторов, и вы должны быть осторожны, чтобы правильно обрабатывать все ситуации.

РЕДАКТИРОВАТЬ : Извините, я не понял, что ссылка была онлайн-статья, а не книга. Даже после прочтения полной статьи не ясно, что было задумано, но я думаю, что Экел, вероятно, имел в виду ситуации, подобные второй, описанной мной выше.

1 голос
/ 25 марта 2009

new и delete на самом деле являются операторами в C ++, которые вы можете переопределить, чтобы обеспечить собственное управление памятью. Взгляните на пример здесь.

0 голосов
/ 25 марта 2009

Если вы сравниваете перегрузку операторов между Java и C ++, вы не будете говорить о new и delete - Java не предоставляет достаточно подробностей управления памятью для новых и не нуждается в удалении.

Вы не можете перегружать другие операторы для типов указателей - по крайней мере один аргумент должен быть классом или перечислимым типом, поэтому он не может говорить о предоставлении различных операторов для указателей.

Таким образом, операторы в C ++ работают со значениями или постоянными ссылками на значения.

Было бы очень необычно для операторов, которые оперируют со значениями или постоянными ссылками на значения, возвращать что-либо кроме значения.

Помимо очевидных ошибок, общих для всех функций C ++ - возвращение ссылки на объект, выделенный из стека (что противоположно утечке памяти), или возврат ссылки на объект, созданный с помощью new, а не значения (которое обычно делается не чаще, чем один раз в карьере, прежде чем учиться), было бы трудно придумать сценарий, когда у общих операторов возникают проблемы с памятью.

Таким образом, нет необходимости создавать несколько версий в зависимости от того, выделяются ли операнды в стеке или в куче на основе обычных шаблонов использования.

Аргументами для оператора являются объекты, переданные в виде значений или ссылок. В C ++ нет переносимого механизма для проверки того, был ли объект выделен в куче или стеке. Если объект был передан по значению, он всегда будет в стеке. Таким образом, если бы требовалось изменить поведение операторов для двух случаев, это было бы невозможно сделать в C ++. (во многих ОС вы можете проверить, находится ли указатель на объект в пространстве, обычно используемом для стека, или в пространстве, обычно используемом для кучи, но это не является ни переносимым, ни полностью надежным.) (также, даже если у вас могут быть операторы в котором в качестве аргументов использовались два указателя, нет никаких оснований полагать, что объекты выделяются кучей только потому, что они являются указателями. Эта информация просто не существует в C ++)

Единственное дублирование, которое вы получаете, относится к случаям, таким как operator [], где один и тот же оператор используется и как метод доступа, и как мутатор. Тогда нормально иметь const и неконстантную версию, поэтому вы можете установить значение, если получатель не является const. Это хорошо - неспособность видоизменять (общедоступное состояние) объекты, помеченные как постоянные.

0 голосов
/ 25 марта 2009

Операторы являются функциями. То, что они добавляют синтаксический сахар, не означает, что вам не нужно быть осторожным с памятью. Вы должны управлять памятью так же, как и при использовании любой другой функции member / global / friend.

Это особенно важно при перегрузке при реализации класса указателя оболочки.

Затем происходит конкатенация строк, перегружая operator+ или operator+=. Взгляните на шаблон basic_string для получения дополнительной информации.

...