Кто удаляет скопированный экземпляр в + оператор? (C++)

Я искал, как реализовать + оператор правильно на всем протяжении Интернета и всех результатов, которые я нашел, делают следующие шаги:

const MyClass MyClass::operator+(const MyClass &other) const
{
    MyClass result = *this;  // Make a copy of myself. Same as MyClass result(*this);
    result += other;         // Use += to add other to the copy.
    return result;           // All done!
}

У меня есть немного вопросов об этом "процессе":

  1. Не настолько глупо реализовать + оператор этот путь, он называет оператор присваивания (который копирует класс) в первой строке, и затем конструктор копии в возврате (который также копирует класс, вследствие того, что возврат значением, таким образом, он уничтожает первую копию и создает новую.. который откровенно не действительно умен...),

  2. Когда я пишу a=b+c, b+c часть создает новую копию класса, затем '=' часть копирует копию в себя. кто удаляет копию это созданный b+c?

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

заранее спасибо

9
задан Didier Trosset 19 May 2010 в 13:50
поделиться

8 ответов

При таких обстоятельствах я бы, вероятно, подумал о чем-то вроде:

MyClass MyClass::operator+(MyClass other) { 
     other += *this;
     return other;
}

Дэйв Абрахамс недавно написал статью , объясняющую, как это работает и почему такой код обычно достаточно эффективен, хотя изначально похоже, что этого не должно быть.

Изменить (спасибо, MSalters): Да, это предполагает / зависит от коммутативного свойства, сохраняемого для MyClass . Если a + b! = B + a , то исходный код - это то, что вам нужно (в основном те же рассуждения).

5
ответ дан 4 December 2019 в 09:35
поделиться

Похоже, это правильный способ реализации оператора + . Несколько моментов:

  • MyClass result = * this не использует оператор присваивания, он должен вызывать конструктор копирования, как если бы он был написан MyClass result (* this) .
  • Возвращаемое значение при использовании в a = b + c называется временным , и компилятор отвечает за его удаление (что, вероятно, произойдет в конце инструкции то есть точка с запятой после того, как все остальное было сделано). Вам не нужно об этом беспокоиться, компилятор всегда очищает временные файлы.
  • Нет лучшего способа, вам нужна копия. Однако компилятору разрешено оптимизировать временные копии, поэтому их будет не так много, как вы думаете. Однако в C ++ 0x вы можете использовать конструкторы перемещения для повышения производительности, передавая владение содержимым временного, а не копируя его целиком.
3
ответ дан 4 December 2019 в 09:35
поделиться

он вызывает оператор присваивания (который копирует класс) в первой строке

Нет, это инициализация копирования (через конструктор).

затем конструктор копирования в возврате (который также копирует класс

. Компиляторы могут (и обычно делают) исключить эту копию с помощью NRVO.

Когда я пишу a = b + c, часть b + c создает новая копия класса, затем часть 'a =' копирует копию самому себе. кто удаляет копию, созданную b + c

Компилятор, как и любое другое временное значение. Они удаляются в конце полного- выражение (в данном случае это означает в или после ; в конце строки.

Есть ли лучший способ реализовать оператор +, не копируя класс дважды, а также без каких-либо проблем с памятью?

Не совсем. Это не так уж и неэффективно.

3
ответ дан 4 December 2019 в 09:35
поделиться

Я постараюсь ответить:

Пункт (1): Нет, он не вызывает оператора присваивания. Вместо этого он вызывает конструктор. Поскольку вам все равно нужно создать объект (поскольку operator + возвращает копию), это не вводит дополнительных операций.

Точка (2): временный результат создается в стеке и, следовательно, не вызывает проблем с памятью (он уничтожается при выходе из функции). На return создается временный объект, чтобы можно было использовать назначение (или конструктор копирования) для присвоения результатов a a = b + c; ]) даже после уничтожения результата . Этот временный объект автоматически уничтожается компилятором.

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

P.S. Иногда я предпочитаю реализовать operator + как не член, чтобы использовать неявное преобразование для обеих сторон операторов (только если это имеет смысл).

2
ответ дан 4 December 2019 в 09:35
поделиться

Нет проблем с памятью (при условии, что оператор присваивания и конструктор копирования хорошо написаны). Просто потому, что вся память для этих объектов берется в стек и управляется компилятором. Более того, компиляторы оптимизируют это и выполняют все операции непосредственно с конечным a вместо того, чтобы копировать дважды.

1
ответ дан 4 December 2019 в 09:35
поделиться
  1. По сути, это не оператор присваивания, а конструктор копирования. В конце концов, такая операция, как сложение, создает новое значение, поэтому его нужно где-то создать. Это более эффективно, чем кажется, поскольку компилятор может выполнять оптимизацию возвращаемого значения, что означает, что он может создать значение непосредственно там, где оно будет использоваться в следующий раз.

  2. Результат объявляется как локальная переменная и, следовательно, исчезает при вызове функции - кроме случаев, когда используется RVO (см. Выше), и в этом случае он никогда не создавался в функции, но в звонилке.

  3. Не совсем; этот метод намного эффективнее, чем кажется на первый взгляд.

6
ответ дан 4 December 2019 в 09:35
поделиться
  1. Это правильный способ реализации оператора+ в C++. Большинство копий, которых вы так боитесь, будут исключены компилятором и будут подчиняться семантике перемещения в C++0x.

  2. Класс является временным и будет удален. Если вы свяжете временный класс с const&, время жизни временного класса будет увеличено до времени жизни ссылки const.

  3. Реализация в виде свободной функции немного более очевидна. Первый параметр в MyClass::operator+ является неявным this, и компилятор в любом случае перепишет функцию в operator+(const MyClass&, const MyClass&).

1
ответ дан 4 December 2019 в 09:35
поделиться

Насколько я помню, «Язык программирования C ++» Страуструпа рекомендует реализовывать операторы как функции-члены только тогда, когда операция влияет на внутреннее представление, и как внешние функции, когда нет. Оператору + не требуется доступ к внутреннему представлению, если он реализован на основе оператора + =, что имеет место.

Таким образом, у вас будет:

class MyClass
{
public:
  MyClass& operator+=(const MyClass &other)
  {
    // Implementation
    return *this;
  }
};

MyClass operator+(const MyClass &op1, const MyClass &op2)
{
    MyClass r = op1;
    return r += op2;
}
1
ответ дан 4 December 2019 в 09:35
поделиться
Другие вопросы по тегам:

Похожие вопросы: