← 模式

复制并交换 (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 行简单地将 *thisother 进行交换。

“复制并交换”范式具有固有的强异常安全性,因为所有内存分配(如果有的话)都发生在将实参复制到 other 形参时,此时尚未对 *this 做任何更改。然而,总的来说,它通常不如更定制化的赋值运算符实现优化得好。

注意:我们通常可以通过使用零法则,来完全避免手动管理内存以及编写复制/移动构造函数、赋值运算符和析构函数。

贡献者

  • Mattijs Kneppers
  • Joseph Mansfield

最后更新

2018年2月23日

来源

在 GitHub 上 Fork 此模式

分享