Переместить конструктор/оператор=

Я пытаюсь узнать о новой функции C++, а именно о перемещении конструктора и назначении X::operator=(X&&), и я нашел интересный пример, но единственное, что я даже не понимаю, но больше не согласен, это одна строка в операторе перемещения и присваивания (отмечено в коде ниже):

MemoryBlock(MemoryBlock&& other)
   : _data(NULL)
   , _length(0)
{
   std::cout << "In MemoryBlock(MemoryBlock&&). length = " 
             << other._length << ". Moving resource." << std::endl;

   // Copy the data pointer and its length from the 
   // source object.
   _data = other._data;
   _length = other._length;

   // Release the data pointer from the source object so that
   // the destructor does not free the memory multiple times.
   other._data = NULL;
   other._length = 0;//WHY WOULD I EVEN BOTHER TO SET IT TO ZERO? IT DOESN'T MATTER IF IT'S ZERO OR ANYTHING ELSE IT IS JUST A VALUE.
}

Итак, мой вопрос: мне нужно установить значение length_ равным нулю или я могу оставить его нетронутым? Не будет утечки памяти и на одно выражение меньше афаиков.


person There is nothing we can do    schedule 27.06.2010    source источник
comment
Возможно, этот ответ также помогает...   -  person fredoverflow    schedule 28.06.2010
comment
Несколько мелких деталей, которые я хочу исправить. Во-первых, поскольку этот конструктор инициализирует _data и _length, а затем копирует в них, он должен просто инициализироваться с правильными значениями. Я бы не сказал, что other.data = NULL "освобождает", это больше похоже на "отключение", память не освобождается. Кроме того, вы должны использовать 'nullptr', а не 'NULL', примерно то же самое, но 'nullptr' правильнее C++. Также рекомендуется избегать использования std::endl, это останавливает вашу программу, ожидая сброса вывода. Если вам не нужна эта функциональность, предпочтите "\n".   -  person thecoshman    schedule 02.07.2013


Ответы (6)


Поскольку «перемещенный из» объект в конечном итоге все равно будет уничтожен, поэтому вы должны оставить его в согласованном состоянии. То, как именно вы это сделаете, конечно, зависит от вашего объекта, и в этом случае это, по-видимому, означает обнуление указателя данных и установку длины на ноль.

person Terry Mahaffey    schedule 27.06.2010
comment
@ Терри, но какая разница, ноль или тысяча? Он по-прежнему действителен int. - person There is nothing we can do; 27.06.2010
comment
@A-ha Существует разница между допустимым значением и правильным/непротиворечивым значением. - person Josh Matthews; 27.06.2010
comment
Невероятно, что люди будут голосовать за правильные ответы. Какая наглость. - person Dennis Zickefoose; 28.06.2010
comment
@ Терри, после некоторых размышлений, да, ты прав. Я принимаю ваш ответ. - person There is nothing we can do; 28.06.2010

Видимо, программист решил, что длина всегда должна иметь правильное значение. Если вы не установите его, код в деструкторе больше не будет печатать то, что нужно:

std::cout << "In ~MemoryBlock(). length = "
                << _length << ".";
person Sjoerd    schedule 27.06.2010
comment
вы не понимаете, в чем мой вопрос. - person There is nothing we can do; 27.06.2010
comment
Вы просто ошибаетесь. Неважно, ставлю ли я ноль или нет. Значение в dtor все равно будет напечатано. - person There is nothing we can do; 27.06.2010
comment
@A-ha: если вы не установите для параметра other._length значение 0, он все равно будет выводиться как 0 в деструкторе? - person Bill; 27.06.2010
comment
@Bill, но в dtor он печатает значения, установленные в ctor (не имеет значения, установили вы length_ на ноль или нет). - person There is nothing we can do; 28.06.2010

На этот вопрос трудно получить окончательный ответ. Семантика перемещения является новой, и сообщество C++ все еще учится правильно ее использовать.

Одно разумное правило, которое я увидел, состоит в том, что «перемещенный от» объект должен безопасно разрушаться и ему может быть присвоено новое значение. Установка _length в ноль не является обязательной для выполнения этого правила, и, честно говоря, я не уверен, что было бы хорошим значением для int, указывающим на недопустимое состояние; может быть -1 в вашем случае?

person Nemanja Trifunovic    schedule 27.06.2010

Объект, из которого вы перемещаете данные, может не быть временным (например, вы можете использовать std::move) — было бы дурным тоном оставлять ваш объект в недопустимом состоянии.

person DanDan    schedule 27.06.2010
comment
так что, по вашему мнению, сброс его значений будет приемлемым? Интересно, интересно... - person There is nothing we can do; 28.06.2010
comment
Да, потому что для этого и нужен конструктор перемещения. Вы передаете право собственности на содержимое одного объекта другому. - person DanDan; 28.06.2010

_length и _data являются семантически связанными элементами. Чтобы объект находился в согласованном состоянии, _length всегда должен сообщать вам, сколько памяти находится в блоке, на который указывает _data. Когда _data указывает на 100 блоков, _length должно быть 100. Когда _data указывает на 1 блок, _length должно быть 1. Если _data ни на что не указывает (NULL), то _length должно быть 0. В противном случае, если _data равно NULL и _length равно 100, то ваш объект находится в несогласованном состоянии. Когда я делаю:

for (int i = 0; i < _length; ++i)
{
  // do something with _data[i], such as:
  _data[i] = 0;
}

Я не должен разбиться. Если вы не установите _length правильно, произойдет сбой. Реальный вопрос заключается в том, почему вы намеренно оставляете объект в несогласованном состоянии, которое приведет к сбоям?

person Bill    schedule 27.06.2010
comment
но в этом случае у вас есть правильная длина_, уже назначенная в строке выше. В строке, о которой я говорю, делается только аннулирование объекта tmp, поэтому я повторяю еще раз: нет, на мой взгляд, не имеет значения, каким будет значение length_. - person There is nothing we can do; 27.06.2010
comment
продолжить в аннулированном объекте. - person There is nothing we can do; 27.06.2010
comment
@A-ha: other._length устанавливается только в одном месте: other._length = 0; Логически, по вашему мнению, нормально оставлять объект в несогласованном состоянии. Я не согласен. - person Bill; 27.06.2010
comment
но что неизменно в его состоянии, не оставляя length_ нетронутым? Ничего такого. И чем больше я об этом думаю, тем больше убеждаюсь, что даже строка other._data = NULL предназначена только для безопасности (просто чтобы указатель ни на что не указывал). - person There is nothing we can do; 28.06.2010
comment
@A-ha: если деструктор должен что-то делать с памятью, кроме вызова удаления (например, вызова деструктора для объектов, хранящихся в блоке), то ваш код выйдет из строя, если _length не установлен правильно. В этом конкретном экземпляре деструктор вызывает только delete и игнорирует значение _length. Однако расчет на то, что код никогда не изменится, является большим источником потенциальных ошибок. (Например, поместите цикл for в мой ответ в деструктор и посмотрите segfault.) - person Bill; 28.06.2010

Установка значения на ноль только покажет согласованность. Однако ответ на самом деле зависит от того, как вы намерены использовать _length для уничтожения объекта. Если длина не используется для определения необходимости удаления _data (когда перемещаемый объект выходит за пределы области видимости), то можно сказать, что безопасно игнорировать _length при перемещении объекта. Просто убедитесь, что вы сохраняете эту согласованность во всех производных типах, и убедитесь, что ваше решение игнорировать _length задокументировано.

person zackery.fix    schedule 07.10.2012