← 模式
复制并交换 (Copy-and-swap)
123456789101112131415161718192021222324252627282930313233343536373839404142434445 | #include <utility> class resource { int x = 0; }; class foo { public: foo() : p{new resource{}} { } foo(const foo& other) : p{new resource{*(other.p)}} { } foo(foo&& other) : p{other.p} { other.p = nullptr; } foo& operator=(foo other) { swap(*this, other); return *this; } ~foo() { delete p; } friend void swap(foo& first, foo& second) { using std::swap; swap(first.p, second.p); } private: resource* p; }; |
此模式采用 CC0 公共领域贡献 许可。
需要 c++11 或更新版本。
意图
以强异常安全的方式实现赋值运算符。
描述
“复制并交换” (copy-and-swap) 范式指出,我们可以基于类的复制/移动构造函数来实现其复制/移动赋值运算符,并达到强异常安全。
在第 7-45 行的 foo
类,其实现与五法则相似,但它的复制和移动赋值运算符已被第 24-29 行的单个赋值运算符所取代。这个赋值运算符通过传值方式接收参数,从而利用了已有的复制和移动构造函数的实现。
要实现这个赋值运算符,我们只需简单地交换 *this
和参数 other
的内容。当 other
在函数末尾离开作用域时,它将销毁最初与当前对象关联的所有资源。
为实现这一点,我们在第 36-41 行为我们的类定义了一个 swap
函数,该函数会对其成员调用 swap
(第 40 行)。我们在第 38 行使用 using-声明,以便在回退到使用 std::swap
之前,通过实参依赖查找 (argument-dependent lookup) 来找到 swap
函数——在我们的例子中这并非绝对必要,因为我们只交换一个指针,但这在通常情况下是很好的实践。然后,我们的赋值运算符在第 26 行简单地将 *this
与 other
进行交换。
“复制并交换”范式具有固有的强异常安全性,因为所有内存分配(如果有的话)都发生在将实参复制到 other
形参时,此时尚未对 *this
做任何更改。然而,总的来说,它通常不如更定制化的赋值运算符实现优化得好。
注意:我们通常可以通过使用零法则,来完全避免手动管理内存以及编写复制/移动构造函数、赋值运算符和析构函数。