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
析构函数、拷贝 / 移动构造、拷贝 / 移动赋值都不碰。如果要实现上允许,请尽可能这样做。
特殊成员函数的生成规则
- 提供任何构造函数会禁止编译器生成默认构造函数。
- 提供移动构造函数 / 赋值操作符禁用拷贝构造函数、拷贝赋值操作符。
- 提供 Rule of 5 中相关的任何一个会禁用编译器生成的移动构造函数、移动赋值操作符。
- 提供析构函数会导致拷贝构造函数、拷贝赋值操作符被标记为过时。(就算用了也是不符合 Rule of 3 的。)
简记:
- 有构造,禁默认构造。
- 有移动,禁拷贝。
- 有任何,禁移动。
- 有析构,拷贝过时。
Tip
顺带一提,如果要 delete
的指针关联类型和指向对象的真实类型不同,那么当前类型必须有虚析构函数,否则是未定义行为。 https://stackoverflow.com/questions/461203/when-to-use-virtual-destructors