找回密码
 立即注册
首页 业界区 业界 C++ 三之法则、五之法则和零之法则

C++ 三之法则、五之法则和零之法则

任娅翠 2025-9-23 09:59:08
1、三之法则

如果一个类需要显式定义以下三个特殊成员函数中的任意一个,通常需要同时定义全部三个:

  • 析构函数(Destructor):释放资源(如 delete 动态内存)。
  • 拷贝构造函数(Copy Constructor):定义深拷贝逻辑,避免多个对象共享同一资源。
  • 拷贝赋值运算符(Copy Assignment Operator):处理赋值时的资源释放和深拷贝。
1.1 典型问题

若仅定义析构函数而未定义拷贝操作,默认的浅拷贝会导致两个对象共享同一资源。例如:
  1. class Bad {
  2. public:
  3.     Bad() : data(new int(0)) {}
  4.     ~Bad() { delete data; } // 析构函数释放内存
  5.     // 未定义拷贝构造函数和赋值运算符
  6. };
  7. Bad a, b = a; // 默认浅拷贝,a.data 和 b.data 指向同一内存
  8. // 析构时两次 delete 同一地址,导致未定义行为
复制代码
1.2 解决方案

显式定义三个函数,确保资源深拷贝和正确释放:
  1. class Good {
  2. public:
  3.     Good() : data(new int(0)) {}
  4.     ~Good() { delete data; }
  5.     Good(const Good& other) : data(new int(*other.data)) {} // 深拷贝
  6.     Good& operator=(const Good& other) {
  7.         if (this != &other) {
  8.             delete data;
  9.             data = new int(*other.data); // 深拷贝
  10.         }
  11.         return *this;
  12.     }
  13. };
复制代码
2、五之法则

C++11 及更高版本,引入移动语义后。
核心规则:如果一个类需要显式定义以下五个特殊成员函数中的任意一个,通常需要同时定义全部五个:

  • 析构函数(同三法则)。
  • 拷贝构造函数(同三法则)。
  • 拷贝赋值运算符(同三法则)。
  • 移动构造函数(Move Constructor):通过 “窃取” 资源(如转移指针所有权)避免深拷贝。
  • 移动赋值运算符(Move Assignment Operator):高效转移资源而非复制。
典型优化:移动语义允许将临时对象的资源直接转移,避免不必要的深拷贝
  1. class Efficient {
  2. public:
  3.     Efficient() : data(new int(0)) {}
  4.     ~Efficient() { delete data; }
  5.    
  6.     // 拷贝操作(深拷贝)
  7.     Efficient(const Efficient& other) : data(new int(*other.data)) {}
  8.     Efficient& operator=(const Efficient& other) { /* 同三法则 */ }
  9.    
  10.     // 移动操作(资源转移)
  11.     Efficient(Efficient&& other) noexcept : data(other.data) {
  12.         other.data = nullptr; // 置空源对象,防止重复释放
  13.     }
  14.     Efficient& operator=(Efficient&& other) noexcept {
  15.         if (this != &other) {
  16.             delete data;
  17.             data = other.data;
  18.             other.data = nullptr;
  19.         }
  20.         return *this;
  21.     }
  22. };
复制代码
编译器行为

  • 若用户定义拷贝操作,编译器不会自动生成移动操作。
  • 若用户定义移动操作,编译器会删除拷贝操作(标记为 =delete)。
  • 若用户定义析构函数,编译器不会自动生成移动操作,可能导致意外的深拷贝。
3、零之法则

现代 C++(推荐优先使用)。
核心规则:尽量不手动定义任何特殊成员函数,而是通过 RAII(资源获取即初始化) 和标准库组件(如智能指针、容器)自动管理资源。
0 之法则的本质是 “资源管理与类的分离”:

  • 类的职责应聚焦于 “业务逻辑”(如数据聚合、行为封装),而非 “资源管理”。
  • 若类需要使用资源(如动态内存),应通过资源管理类(如std::vector、std::string、std::unique_ptr)间接持有资源,而非自己管理。
  • 由于资源管理类已正确实现了三 / 五法则,外层类无需干预,编译器生成的默认特殊成员函数会自动调用成员的对应函数(“逐成员操作”),行为正确。
3.1 适用场景与示例

适用场景
当类的所有成员都是 “自管理资源” 的类型(如内置类型、标准库容器、智能指针等),且类本身不直接持有需要手动释放的资源(如裸指针指向的动态内存、文件描述符)时,适用 0 之法则。
实现方式:将资源封装在具有完整语义的成员对象中,利用其自动生成的特殊成员函数。
[code]#include #include #include // 遵循0之法则:不声明任何特殊成员函数class Student {public:    // 仅包含自管理资源的成员    std::string name;    // string管理动态内存    int age;             // 内置类型(无资源)    std::vector scores;  // vector管理动态数组};int main() {    Student s1{"Alice", 18, {90, 85, 95}};        // 1. 拷贝初始化(调用编译器生成的拷贝构造函数)    Student s2 = s1;  // s2.name、s2.scores均为s1的深拷贝(string和vector的拷贝是深拷贝)    std::cout

相关推荐

您需要登录后才可以回帖 登录 | 立即注册