Должен ли идиома «Копировать и поменять» идиома «Копировать и переместить» в C ++ 11?
Как объяснено вэтот ответидиома копирования и замены реализована следующим образом:
class MyClass
{
private:
BigClass data;
UnmovableClass *dataPtr;
public:
MyClass()
: data(), dataPtr(new UnmovableClass) { }
MyClass(const MyClass& other)
: data(other.data), dataPtr(new UnmovableClass(*other.dataPtr)) { }
MyClass(MyClass&& other)
: data(std::move(other.data)), dataPtr(other.dataPtr)
{ other.dataPtr= nullptr; }
~MyClass() { delete dataPtr; }
friend void swap(MyClass& first, MyClass& second)
{
using std::swap;
swap(first.data, other.data);
swap(first.dataPtr, other.dataPtr);
}
MyClass& operator=(MyClass other)
{
swap(*this, other);
return *this;
}
};
Имея значение MyClass в качестве параметра для operator =, параметр может быть создан либо конструктором копирования, либо конструктором перемещения. Затем вы можете безопасно извлечь данные из параметра. Это предотвращает дублирование кода и способствует безопасности исключений.
В ответе упоминается, что вы можете поменять или переместить переменные во временную. В первую очередь речь идет об обмене. Однако обмен, если он не оптимизирован компилятором, включает три операции перемещения и в более сложных случаях выполняет дополнительную дополнительную работу. Когда все, что вы хотите, этопереехать временный в назначенный объект.
Рассмотрим этот более сложный пример, включающийсхема наблюдателя, В этом примере я написал код оператора присваивания вручную. Акцент делается на конструкторе перемещения, операторе присваивания и методе обмена:
class MyClass : Observable::IObserver
{
private:
std::shared_ptr<Observable> observable;
public:
MyClass(std::shared_ptr<Observable> observable) : observable(observable){ observable->registerObserver(*this); }
MyClass(const MyClass& other) : observable(other.observable) { observable.registerObserver(*this); }
~MyClass() { if(observable != nullptr) { observable->unregisterObserver(*this); }}
MyClass(MyClass&& other) : observable(std::move(other.observable))
{
observable->unregisterObserver(other);
other.observable.reset(nullptr);
observable->registerObserver(*this);
}
friend void swap(MyClass& first, MyClass& second)
{
//Checks for nullptr and same observable omitted
using std::swap;
swap(first.observable, second.observable);
second.observable->unregisterObserver(first);
first.observable->registerObserver(first);
first.observable->unregisterObserver(second);
second.observable->registerObserver(second);
}
MyClass& operator=(MyClass other)
{
observable->unregisterObserver(*this);
observable = std::move(other.observable);
observable->unregisterObserver(other);
other.observable.reset(nullptr);
observable->registerObserver(*this);
}
}
Очевидно, что дублированная часть кода в этом написанном вручную операторе присваивания идентична части конструктора перемещения. Вы могли бы выполнить обмен в операторе присваивания, и поведение было бы правильным, но оно потенциально могло бы выполнить больше перемещений и выполнить дополнительную регистрацию (в обмене) и отмену регистрации (в деструкторе).
Разве не имело бы смысла многократно использовать код конструктора перемещения?
private:
void performMoveActions(MyClass&& other)
{
observable->unregisterObserver(other);
other.observable.reset(nullptr);
observable->registerObserver(*this);
}
public:
MyClass(MyClass&& other) : observable(std::move(other.observable))
{
performMoveActions(other);
}
MyClass& operator=(MyClass other)
{
observable->unregisterObserver(*this);
observable = std::move(other.observable);
performMoveActions(other);
}
Мне кажется, что этот подход никогда не уступает своп-подходу. Правильно ли я считаю, что идиома «копировать и менять» будет лучше, чем идиома «копируй и двигай» в C ++ 11, или я упустил что-то важное?