C++ 11 三、五法则

C++ 三法则(Rule of Three)

定义

当一个类显式定义了以下三个特殊成员函数中的任意一个时,通常需要同时定义另外两个:

  • 析构函数 ~T()
  • 拷贝构造函数 T(const T&)
  • 拷贝赋值运算符 T& operator=(const T&)

核心思想

如果需要自定义析构函数来释放资源(如动态内存),那么默认的拷贝构造函数和拷贝赋值运算符通常也会有问题,因为它们只会进行浅拷贝。

典型问题示例

class Bad {
public:
    Bad(const char* s) : p(new std::string(s)) {}
    ~Bad() { delete p; }   // 显式定义析构函数
private:
    std::string* p;
};

int main() {
    Bad a("hello");
    Bad b = a;   // 使用默认拷贝构造函数,进行浅拷贝
}                // a.p 和 b.p 指向同一块内存,析构时重复删除同一地址,导致未定义行为

解决方案

手动定义拷贝构造函数和拷贝赋值运算符来实现深拷贝:

class Good {
public:
    Good(const char* s) : p(new std::string(s)) {}
    
    // 析构函数
    ~Good() { delete p; }
    
    // 拷贝构造函数
    Good(const Good& other) : p(new std::string(*other.p)) {}
    
    // 拷贝赋值运算符
    Good& operator=(const Good& other) {
        if (this != &other) {
            delete p;
            p = new std::string(*other.p);
        }
        return *this;
    }
    
private:
    std::string* p;
};

C++ 五法则(Rule of Five)

定义

在C++11及以后,当一个类需要自定义析构函数时,通常需要同时定义以下五个特殊成员函数:

  • 析构函数 ~T()
  • 拷贝构造函数 T(const T&)
  • 拷贝赋值运算符 T& operator=(const T&)
  • 移动构造函数 T(T&&) noexcept
  • 移动赋值运算符 T& operator=(T&&) noexcept

核心思想

C++11引入了右值引用和移动语义,当类需要管理资源时,除了传统的拷贝操作外,还需要考虑移动操作以提高性能。如果需要自定义拷贝构造函数或拷贝赋值运算符,通常也需要自定义移动构造函数和移动赋值运算符。

完整示例

class Resource {
public:
    // 构造函数
    Resource(const char* s) : p(new std::string(s)) {}
    
    // 析构函数
    ~Resource() { delete p; }
    
    // 拷贝构造函数
    Resource(const Resource& other) : p(new std::string(*other.p)) {}
    
    // 拷贝赋值运算符
    Resource& operator=(const Resource& other) {
        if (this != &other) {
            delete p;
            p = new std::string(*other.p);
        }
        return *this;
    }
    
    // 移动构造函数
    Resource(Resource&& other) noexcept : p(other.p) {
        other.p = nullptr;
    }
    
    // 移动赋值运算符
    Resource& operator=(Resource&& other) noexcept {
        if (this != &other) {
            delete p;
            p = other.p;
            other.p = nullptr;
        }
        return *this;
    }
    
private:
    std::string* p;
};

移动语义的优势

移动操作可以避免不必要的深拷贝,直接转移资源所有权,提高性能:

Resource createResource() {
    return Resource("example");  // 返回临时对象,触发移动构造
}

int main() {
    Resource a("hello");
    Resource b = std::move(a);   // 显式移动,a的资源转移给b
}

现代C++建议

遵循五法则的同时,更推荐使用"零法则"(Rule of Zero):通过使用智能指针等RAII类型来自动管理资源,让编译器生成默认的特殊成员函数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序炼丹师

感谢您的打赏

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值