C++:类-拷贝构造、拷贝赋值、析构

类通过5种特殊的成员函数:拷贝构造函数(copy constructor)、拷贝赋值运算符(copy-assignment operator)、移动构造函数(move constructor)、移动赋值运算符(move-assignment)和析构函数(destructor),来完成对象的拷贝、移动、赋值和销毁。

class Foo(){
    public:
        Foo();
        ~Foo();
        Foo(const Foo&);    //拷贝构造函数
        Foo& operator=(const Foo&); //拷贝赋值赋值运算符
        
}

拷贝构造函数

  • 即使定义了其他构造函数,编译器也会合成一个拷贝构造函数。
  • 合成拷贝构造函数将逐个拷贝非static成员变量。
    • 对类成员,使用其拷贝构造函数、
    • 对数组,逐个拷贝(元素类型的拷贝构造函数)
  • 拷贝初始化通常通过 拷贝构造函数 来完成
Foo a;
Foo b = a;  //拷贝初始化
String s = "a"; //拷贝初始化
  • 编译器有时可以跳过拷贝/移动构造函数
string s = "a"; //拷贝初始化
//可执行为
string s("a");  //直接初始化

拷贝赋值运算符

  • 未定义则生成合成拷贝复制运算符,行为类似拷贝构造函数
  • 定义拷贝复制运算符时需要注意:如果一个对象赋予它自身时,必须能正常工作
  • 使用swap的拷贝赋值运算符(拷贝并交换)。自动处理了自赋值情况其是异常安全的。
Foo& operator=(Foo rhs){    //注意rhs不是引用!!
    swap(*this, rhs);
    return *this;
}
//结束时,结束时原来该对象中的资源,被rhs释放

三/五法则

  • 需要析构函数的类也需要拷贝和赋值操作(需要拷贝/赋值操作 不一定 需要析构函数)
  • 需要拷贝操作的类也需要赋值操作,反之亦然
  • 一般来说一个类定义了任何一个拷贝操作,它就应该定义所有五个操作。

使用=delete来阻止拷贝

  • 不会生成合成的拷贝构造函数/拷贝赋值运算符
Foo(const Foo&) = delete;   //阻止拷贝
Foo& operator(const Foo&) = delete;     //阻止赋值
  • 删除了 析构函数的类,不能定义该变量或创建其临时变量。但可以动态分配,但无法释放。
  • 如果一个类的数据成员不能默认构造/拷贝/赋值/销毁,则对应的合成的成员函数将被定义为删除的。
    • 某个成员的析构函数是删除的不可访问的(如private)/类内有一个引用成员/类内有一个const成员且没有类内初始化,则该该类合成的构造函数被删除

swap

标准库的swap,需要一次拷贝和两次赋值对于有类外资源的对象来说,更好的方式是交换指针,而不是拷贝。 为了优化,需自己定义swap。

猜你喜欢

转载自blog.csdn.net/Qiao712/article/details/115450715