任娅翠 发表于 2025-9-23 09:59:08

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

1、三之法则

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

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

若仅定义析构函数而未定义拷贝操作,默认的浅拷贝会导致两个对象共享同一资源。例如:
class Bad {
public:
    Bad() : data(new int(0)) {}
    ~Bad() { delete data; } // 析构函数释放内存
    // 未定义拷贝构造函数和赋值运算符
};

Bad a, b = a; // 默认浅拷贝,a.data 和 b.data 指向同一内存
// 析构时两次 delete 同一地址,导致未定义行为1.2 解决方案

显式定义三个函数,确保资源深拷贝和正确释放:
class Good {
public:
    Good() : data(new int(0)) {}
    ~Good() { delete data; }
    Good(const Good& other) : data(new int(*other.data)) {} // 深拷贝
    Good& operator=(const Good& other) {
      if (this != &other) {
            delete data;
            data = new int(*other.data); // 深拷贝
      }
      return *this;
    }
};2、五之法则

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

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

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

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

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

适用场景
当类的所有成员都是 “自管理资源” 的类型(如内置类型、标准库容器、智能指针等),且类本身不直接持有需要手动释放的资源(如裸指针指向的动态内存、文件描述符)时,适用 0 之法则。
实现方式:将资源封装在具有完整语义的成员对象中,利用其自动生成的特殊成员函数。
#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
页: [1]
查看完整版本: C++ 三之法则、五之法则和零之法则