三五法则是指当类需要显式定义析构函数、拷贝构造函数、拷贝赋值运算符、移动构造函数或移动赋值运算符中的任一时,通常需手动实现全部五个以正确管理资源;其核心是确保资源安全,避免泄漏或重复释放;现代C++推荐使用RAII和标准库(如std::string、智能指针)实现“零法则”,即无需手动定义这些函数。

在C++中,并没有一个官方术语叫“零三五法则”,但根据你的描述,你很可能指的是与资源管理相关的“三五法则”(Rule of Three/Five)。这个规则是C++中关于类设计和资源管理的重要指导原则,尤其涉及特殊成员函数的定义。
什么是三五法则?
三五法则是指:如果你的类需要显式定义以下五个特殊成员函数中的任何一个,那么你很可能需要手动定义所有五个,以确保正确的资源管理:
- 析构函数(destructor)
- 拷贝构造函数(copy constructor)
- 拷贝赋值运算符(copy assignment operator)
- 移动构造函数(move constructor,C++11起)
- 移动赋值运算符(move assignment operator,C++11起)
这个规则源于这样一个事实:当类管理了某些资源(如动态内存、文件句柄、网络连接等),编译器自动生成的默认版本可能无法正确处理资源的复制或释放,从而导致资源泄漏、重复释放或悬空指针等问题。
从“三法则”到“五法则”
在C++98/03时代,只有前三个成员函数存在,因此称为“三法则”(Rule of Three):
立即学习“C++免费学习笔记(深入)”;
- 析构函数
- 拷贝构造函数
- 拷贝赋值运算符
到了C++11引入了移动语义后,增加了移动构造函数和移动赋值运算符,于是扩展为“五法则”(Rule of Five)。
例如,如果你的类中有指针并管理动态内存:
class MyString {
char* data;
public:
MyString(const char* str) {
data = new char[strlen(str) + 1];
strcpy(data, str);
}
~MyString() {
delete[] data;
}
// 需要手动定义拷贝构造函数
MyString(const MyString& other) {
data = new char[strlen(other.data) + 1];
strcpy(data, other.data);
}
// 需要手动定义拷贝赋值
MyString& operator=(const MyString& other) {
if (this != &other) {
delete[] data;
data = new char[strlen(other.data) + 1];
strcpy(data, other.data);
}
return *this;
}
// C++11 起还应定义移动操作
MyString(MyString&& other) noexcept : data(other.data) {
other.data = nullptr;
}
MyString& operator=(MyString&& other) noexcept {
if (this != &other) {
delete[] data;
data = other.data;
other.data = nullptr;
}
return *this;
}};
如何避免手动实现?使用 RAII 和标准库
现代C++提倡通过使用RAII(Resource Acquisition Is Initialization)和标准库类型(如 std::string、std::vector、std::unique_ptr 等)来自动管理资源,从而避免手动编写这些函数。
比如上面的例子可以简化为:
class MyString {
std::string data; // 使用 std::string 自动管理
public:
MyString(const char* str) : data(str) {}
// 不需要手动定义析构、拷贝、移动函数
// 编译器生成的默认版本已经足够安全高效
};
在这种情况下,你可以完全不写那五个特殊成员函数,也能获得正确的行为 —— 这也可以理解为一种“零法则”的实践:如果能用标准库或智能指针管理资源,就一个都不用写。
总结:三五法则的本质是资源管理责任
核心思想是:当你需要介入资源的生命周期管理时,就必须对所有相关操作负责。要么全自己写,要么一个都不写,交给更可靠的工具(如智能指针、容器)去处理。
基本上就这些。记住:不是每个类都需要这五个函数,但一旦你发现你需要其中一个,就要停下来想想其他几个是否也需要定制。










