13.1 拷贝、赋值与销毁(2)

三/五法则

  需要析构函数的类也需要拷贝和赋值操作
  如果这个类需要一个析构函数,我们几乎可以肯定它也需要一个拷贝构造函数和一个拷贝赋值运算符。

  需要拷贝操作的类也需要赋值操作,反之亦然
  某些类所要完成的工作,只需要拷贝和赋值操作,不需要析构函数。一个类需要一个拷贝赋值运算符,几乎可以肯定它也需要一个拷贝构造函数。

使用=default

  我们可以通过将拷贝控制成员定义为=default来显式地要求编译器生成合成的版本:

class Sales_data {
public:
      //拷贝控制成员;使用default
      Sales_data()=default;
      Sales_data(constr Sales_data&)=default;
      Sales_data& operator=(const Sales_data &);
      ~Sales_data()=default;
      //其他成员的定义,如前
};
Sales_data& Sales_data::operator=(const Sales_data&)=default;

  当我们在类内用=default修饰成员的声明时,合成的函数将隐式地声明为内联的,如果我们不希望合成的成员是内联函数,应该只对类外定义使用=default。

阻止拷贝

  有些情况下,我们想要阻止或者改变类的拷贝。

  定义删除的函数
  我们可以通过将拷贝构造函数和拷贝赋值运算符定义为删除的函数来阻止拷贝。删除函数是这样一种函数:我们虽然声明了它们,但不能以任何方式使用它们。在函数的参数列表后面加上=delete来指出我们希望将它定义为删除的:

 struct NoCopy {
       NoCopy()=default;  //使用合成的默认构造函数
       NoCopy(const NoCopy&)=delete;  //阻止拷贝
        //....
};

  与=default不同,=delete必须出现在函数第一次声明的时候,我们可以对任何函数指定=delete,我们只能对编译器可以合成的默认构造函数或拷贝控制成员使用=default。

  析构函数不能是删除的成员
  我们不能删除析构函数,如果析构函数被删除,就无法销毁此类型的对象了,对于一个删除了析构函数的类型,编译器将不允许定义该类型的变量或创建该类的临时对象。对于删除了析构函数的类型,我们可以动态分配这种类型的对象,但是不能释放这些对象:

struct NoDtor {
      NoDtor()=default;  //使用合成默认构造函数
      ~NoDtor()=delete;  //我们不能销毁NoDtor类型的对象
};
NoDtor nd;  //错误:NoDtor的析构函数是删除的
NoDtor *p=new NoDtor();  //正确:但我们不能delete p
delete p;  //错误:NoDtor的析构函数是删除的

  合成的拷贝控制成员可能是删除的
  如果一个类为定义构造函数,编译器会为其合成一个默认构造函数,对某些类来说,编译器将这些合成的成员定义为删除的函数。如果一个类有数据成员不能默认构造、拷贝、复制或销毁,则对应的成员函数将被定义为删除的。

  private拷贝控制
  在新标准发布之前,类通过将其拷贝构造函数和拷贝赋值运算符声明为private的来阻止拷贝。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容