C++: Rule of 3/5/0

https://en.cppreference.com/w/cpp/language/rule_of_three

Rule of 3

析构函数、拷贝构造、拷贝赋值三者要是定义了其一,最好把其他两个都补上,因为编译生成的很可能不是我们想要的。此外,定义三者之一会导致移动构造、移动赋值被删除

Note

显式删除拷贝构造函数(MyClass(const MyClass&) = delete;)也是一种声明,这样会导致移动构造函数被删除,如果有移动尝试则会匹配上(被删除的)拷贝构造函数,从而编译失败。

Tip

复制 / 移动赋值操作符可以用复制 / 移动构造函数 + std::swap 实现。

Rule of 5

还要加上移动构造和移动赋值(如果这个类不需要移动,或者移动和拷贝的过程完全一样,那就算了)。C++ Core Guidelines C.21 中建议在碰了其中一个时,将它们全部删除或者全部定义。

Unlike Rule of Three, failing to provide move constructor and move assignment is usually not an error, but a missed optimization opportunity.

Rule of 0

析构函数、拷贝 / 移动构造、拷贝 / 移动赋值都不碰。如果要实现上允许,请尽可能这样做。

特殊成员函数的生成规则

Special_member_functions

  1. 提供任何构造函数会禁止编译器生成默认构造函数。
  2. 提供移动构造函数 / 赋值操作符禁用拷贝构造函数、拷贝赋值操作符。
  3. 提供 Rule of 5 中相关的任何一个会禁用编译器生成的移动构造函数、移动赋值操作符。
  4. 提供析构函数会导致拷贝构造函数、拷贝赋值操作符被标记为过时。(就算用了也是不符合 Rule of 3 的。)

简记:

  1. 有构造,禁默认构造。
  2. 有移动,禁拷贝。
  3. 有任何,禁移动。
  4. 有析构,拷贝过时

Tip

顺带一提,如果要 delete 的指针关联类型和指向对象的真实类型不同,那么当前类型必须有虚析构函数,否则是未定义行为。 https://stackoverflow.com/questions/461203/when-to-use-virtual-destructors