Перемещение ctor и перемещение dtor

Как я попросил в конструкторе/операторе Перемещения =, и через некоторое время я согласовал и признал, что право отвечает на тот вопрос, я просто думал, если бы будет он быть полезным иметь что-то как "движущийся деструктор", который был бы вызван на перемещенный объект каждый раз, мы использовали перемещение ctor или оператор =.
Таким образом мы должны были бы указать только в перемещении dtor, что мы хотим от него и как наш объект shuld быть аннулированными, используясь конструктором перемещения. Без этой семантики это похоже каждый раз, что я пишу перемещение ctor или оператор =, я должен явно заявить в каждом из них (введение повторения/ошибки кода), как аннулировать перемещенный объект, который не является самой лучшей опцией, я думаю. Нетерпеливое ожидание мнений об этом предмете.

1
задан Community 23 May 2017 в 12:23
поделиться

3 ответа

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

this->swap(rhv); 

. Метод подкачки, вероятно, полезен в любом случае, если класс извлекает выгоду из семантики перемещения. Это красиво делегирует работу по освобождению старых ресурсов * this обычному деструктору.

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

Кроме того, согласно последним изменениям, оператор перемещения конструктора / присваивания может быть установлен по умолчанию. Это означает, что очень вероятно мои классы будут выглядеть так:

class X
{
    well_behaved_raii_objects;
public:
    X(X&& ) = default;
    X& operator=(X&&) = default;
}; 

Никакого деструктора! Что может сделать меня привлекательным иметь вместо два деструктора?

Также примите во внимание, что у оператора присваивания есть старые ресурсы, с которыми нужно иметь дело. В соответствии с текущим стандартом вы должны быть осторожны, чтобы нормальный вызов деструктора был прекрасен как после построения, так и после присваивания и IMO, аналогично с предлагаемым деструктором перемещения вам нужно будет позаботиться в конструкторе и операторе присваивания, что один и тот же деструктор перемещения может быть благополучно позвонил.Или вам нужно два деструктора перемещения - по одному на каждый? :)


Переработанный пример пример msdn в комментариях с конструктором перемещения / присваиванием

#include <algorithm>

class MemoryBlock
{
public:

   // Simple constructor that initializes the resource.
   explicit MemoryBlock(size_t length)
      : length(length)
      , data(new int[length])
   {
   }

   // Destructor.
   ~MemoryBlock()
   {
      delete[] data; //checking for NULL is NOT necessary
   }

   // Copy constructor.
   MemoryBlock(const MemoryBlock& other)
      : length(other.length)
      , data(new int[other.length])
   {
      std::copy(other.data, other.data + length, data);
   }

   // Copy assignment operator (replaced with copy and swap idiom)
   MemoryBlock& operator=(MemoryBlock other)  //1. copy resource
   {
       swap(other);  //2. swap internals with the copy
       return *this; //3. the copy's destructor releases our old resources
   }

   //Move constructor
   //NB! C++0x also allows delegating constructors
   //alternative implementation:
   //delegate initialization to default constructor (if we had one), then swap with argument
   MemoryBlock(MemoryBlock&& other)
    : length(other.length)
    , data(other.data)
    {
        other.data = 0; //now other can be safely destroyed
        other.length = 0; //not really necessary, but let's be nice
    }

    MemoryBlock& operator=(MemoryBlock&& rhv)
    {
        swap(rhv);
        //rhv now contains previous contents of *this, but we don't care:
        //move assignment is supposed to "ruin" the right hand value anyway
        //it doesn't matter how it is "ruined", as long as it is in a valid state
        //not sure if self-assignment can happen here: if it turns out to be possible
        //a check might be necessary, or a different idiom (move-and-swap?!)
        return *this;
    }


   // Retrieves the length of the data resource.
   size_t Length() const
   {
      return length;
   }

   //added swap method (used for assignment, but recommended for such classes anyway)
   void swap(MemoryBlock& other) throw () //swapping a pointer and an int doesn't fail
   {
        std::swap(data, other.data);
        std::swap(length, other.length);
    }

private:
   size_t length; // The length of the resource.
   int* data; // The resource.
};

Некоторые комментарии к исходному образцу MSDN:

1) проверка на NULL перед delete не требуется (возможно, это сделано здесь для вывода, который я вырезал, возможно, это указывает на недоразумение)

2) удаление ресурсов в операторе присваивания: дублирование кода. При использовании идиомы копирования и обмена удаление ранее удерживаемых ресурсов делегируется деструктору.

3) идиома копирования и обмена также делает ненужными проверки самоназначения. Это не проблема, если ресурс скопирован до того, как будет удален. - («Скопировать ресурс независимо», с другой стороны, вредит только тогда, когда вы ожидаете, что с этим классом будет выполнено много самоприсваиваний.)

4) Оператору присваивания в примере MSDN не хватает какой-либо защиты от исключений: при выделении нового хранилища сбой, класс остается в недопустимом состоянии с недопустимым указателем. При уничтожении произойдет неопределенное поведение.

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

5) Проверка самоприсваивания выглядит особенно сомнительной в операторе присваивания перемещения. Я не понимаю, как значение левой руки может быть таким же, как значение правой руки. Потребуется ли a = std :: move (a); для достижения идентичности (похоже, что в любом случае это будет неопределенное поведение?)?

6) Опять же, назначение перемещения - это излишнее управление ресурсами, что, по моему мнению, version просто делегирует обычному деструктору.

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

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

... если дублирование кода вас устраивает, в противном случае повторно используйте деструктор.

Чтобы предотвратить невосстановимость уничтожение ресурсов, должным образом справляться с самостоятельным назначением на ходу оператор присваивания.

... или убедитесь, что вы ничего не удаляете, прежде чем будете уверены, что можете это заменить. Или, скорее, вопрос SO: возможно ли, чтобы самоназначение произошло в случае назначения перемещения в четко определенной программе.


Кроме того, из моего черновика (3092) я обнаружил, что если класс не имеет определяемого пользователем конструктора копирования / оператора присваивания и ничто не препятствует существованию конструктора перемещения / присваивания, он будет неявно объявлен как установленный по умолчанию .Если я не ошибаюсь, это означает: если членами являются такие вещи, как строки, вектор, shared_ptrs и т. Д., И в этом случае вы обычно не пишете конструктор / присваивание копирования, вы получите конструктор перемещения / присваивание перемещения бесплатно .

2
ответ дан 2 September 2019 в 22:29
поделиться

Что с этим не так:

struct Object
{
  Object(Object&&o) { ...move stuff...; nullify_object(o); }
  Object & operator = (Object && o) { ...; nullify_object(o); }

  void nullify_object(Object && o);
};

Или альтернатива вызова nullify_object для цели: o.nullify ();

Я не вижу серьезных преимуществ от добавления YANLF .

0
ответ дан 2 September 2019 в 22:29
поделиться

Конструктор / назначение перемещения - это когда вы крадете ресурсы и оставляете состояние ресурсов украденного объекта в состоянии, которое обеспечивает безопасное уничтожение объекта при вызове его деструктора. Вы не можете увидеть или получить доступ к временному «значению», из которого вы крадете ресурсы, кроме как в конструкторе / назначении перемещения.

В качестве примера возьмем строку. Вот где вы должны украсть выделенные ресурсы и размер временного объекта и установить его значения на свои собственные значения (которые должны быть нулевыми и 0, если ваш объект был создан по умолчанию).

0
ответ дан 2 September 2019 в 22:29
поделиться
Другие вопросы по тегам:

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