C++三五法则

 若类中有资源在构造函数中创建,并在析构函数中释放,此时需要显式定义拷贝构造赋值析构等操作,若在程序没有显示声明并定义时,会被隐式生成,对于不包含联合体的类,隐式生成的拷贝构造函数和赋值运算在执行时,会按成员对象依次复制,隐式生成的析构函数为空

如下面的类T管理资源int*

class T{
public:
    T(int i){
        pi= new int(i);
        // pi= std::make_shared<int>(i);
    }
private:
    int* pi;
    // std::shared_ptr<int> pi;
};

1. 显式定义拷贝构造函数的情况

T(const T& t){
    // this->pi= new int(*t.pi);
    this->pi= new int; // 申请内存,并复制对象
    *this->pi= *t.pi;
}

2. 显式重载赋值运算符的情况

实现赋值运算时,应考虑的问题:

  • 自检查
    防止自赋值
  • 删除自身资源
    若在new时抛出异常,则会无法恢复原状态,可用临时变量或后面介绍的copy-and-swap解决
  • 复制资源
    代码冗余,和拷贝构造中做一样的操作
T& operator= (const T& t){
    if(this!=&t){
        this->pi= new int;
        *this->pi= *t.pi;
        return *this;
    }
}

3. 显式定义析构函数

~T(){
    delete pi; // 释放资源
}

不可复制的资源

如文件句柄和互斥量等是不可复制的,此时拷贝构造函数赋值运算需要特殊处理

  • 定义在private
    private:
      T(const T& t);
      T& operator=(const T& t);
    
  • 用C++11中的delete实现
    T(const T& t) = delete;
    T& operator=(const T& t) = delete;
    

C++11五法则

C++11中引入了右值引用,多了移动语义,所以相应地新增了移动构造移动赋值运算

T(T&&) noexcept= default;
T& operator=(T&&)noexcept= default;

建议:

  1. 尽量不要在一个类中管理多个资源,否则日后会知道什么是痛苦
  2. 若要显式定义析构函数、复制构造函数或赋值运算符中的一个,则也应显式定义另外两个
  3. 大多数情况下都没必要自己实现管理资源的类,std中基本上都有实现,只要避免原始指针,”五法则”基本上也就用不到

copy-and-swap

 任何资源管理类,都需要遵循三法则,其中,拷贝构造和析构函数的实现相对简单,赋值运算符重载会复杂许多

 copy-and-swap是实现赋值运算符重载的完美解决方案,既能避免代码冗余,又可以提供强异常安全保证
大致思路:先用拷贝构造创建一个副本,再利用swap交换数据成员,在作用域结束时,副本会自动调用析构函数

在实现赋值运算时,使用copy-and-swap解决上述问题

friend void swap(T& src, T& dest){
    std::swap(src.str, src.str); // <algorithm>
}
T& operator=(T t){
    swap(*this, t);
    return *this;
}
posted @   sgqmax  阅读(22)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 提示词工程——AI应用必不可少的技术
· 地球OL攻略 —— 某应届生求职总结
· 字符编码:从基础到乱码解决
· SpringCloud带你走进微服务的世界
点击右上角即可分享
微信分享提示