Rule of Five 是 C++11 资源管理约定:当类需自定义析构函数、拷贝构造函数或拷贝赋值运算符时,通常也应显式定义移动构造函数和移动赋值运算符,共五个特殊成员函数,语义耦合紧密,漏写易致资源泄漏、双重释放或性能退化。

什么是 Rule of Five?
Rule of Five 是 C++11 引入的资源管理约定:当类需要自定义析构函数、拷贝构造函数或拷贝赋值运算符时,通常也应显式定义移动构造函数和移动赋值运算符。这五个特殊成员函数共同构成“五法则”。
哪些函数必须一起出现?
这五个函数是:~T()、T(const T&)、T& operator=(const T&)、T(T&&)、T& operator=(T&&)。它们不是语法强制要求同时存在,但语义上高度耦合——漏掉移动版本,会导致本可高效移动的对象被意外拷贝;漏掉拷贝版本,可能让本应禁止拷贝的类意外支持(比如用 std::vector 存储时触发拷贝)。
- 若你写了
~T()(比如手动delete[]内存),大概率也需要自定义所有五个,否则资源泄漏或双重释放风险陡增 - 若只禁用拷贝(
= delete),但没禁用移动,编译器仍可能生成移动函数——结果是对象可移动但不可拷贝,这有时是预期行为;但若你没写任何移动函数,编译器又可能因存在用户定义的拷贝/析构而抑制移动函数的隐式生成,导致std::move(x)后无法转移资源 - 使用
= default可以显式启用编译器生成的版本,例如:T(T&&) = default;
这比不写更明确,且能避免编译器因其他用户定义函数而跳过生成
不遵守 Rule of Five 的典型错误现象
最常见的是“浅拷贝后双重释放”:自定义了析构函数释放 int*,但没写拷贝构造函数,编译器生成的默认拷贝只是复制指针值;两个对象析构时都 delete 同一块内存,触发未定义行为。
- 编译期无报错,运行时崩溃(
double free or corruption) -
std::vector或::push_back() std::sort()等算法触发移动失败,回退到拷贝,性能骤降甚至崩溃 - 移动后原对象状态未置为有效可析构态(如没将
ptr设为nullptr),导致析构时二次释放 - 移动赋值未处理自赋值(
x = std::move(x)),可能提前释放自身资源再尝试访问已释放内存
现代 C++ 中更推荐的做法
Rule of Five 不是目标,而是警示信号:它说明你正在手动管理资源。更好的做法是“Rule of Zero”——把资源封装进 RAII 类型(如 std::unique_ptr、std::vector),让编译器自动生成全部五个函数,且语义正确。
立即学习“C++免费学习笔记(深入)”;
- 用
std::unique_ptr替代裸int*,就无需写任何特殊成员函数 - 若必须手写,优先用
= default生成移动操作,再检查拷贝是否真需自定义(多数情况应禁用或委托给成员) - 移动操作里务必清空源对象资源(如设
other.ptr = nullptr),这是最容易被忽略的一步
ptr = nullptr,就可能让整个类变成定时炸弹。











