如this answer中所述,复制和交换习语的实现方式如下:
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 中的复制和移动习语,还是我错过了一些重要的事情?