拷贝
(1):拷贝构造函数
- 如果一个构造函数的第一个参数是自身类类型的引用,且任何额外参数都有默认值,则此构造函数为拷贝构造函数;
拷贝初始化不仅在我们用 = 定义变量时发生,以下情况也会发生:
- 将一个对象作为实参传递给一个非引用类型的形参;
- 从一个返回类型为非引用类型的函数返回一个对象;
- 用花括号列表初始化一个数组中的元素或一个聚合类的成员;
拷贝构造函数用来初始化非引用类型的参数,解释了为什么拷贝构造函数自己的参数必须为引用类型:
- 如果其参数不为引用类型,则调用永远不会成功,为了调用拷贝构造函数,必须拷贝其实参,但为了拷贝实参,又需要调用拷贝构造函数,如此无限循环;
(2):拷贝赋值运算符
- 赋值运算符通常应该返回一个指向其左侧运算对象的引用;
(3):析构函数
在一个构造函数中,成员初始化是在函数体执行之前完成的,且按照它们在类中出现的顺序进行初始化。在一个析构函数中,首先执行函数体,然后销毁成员,成员按初始化顺序的逆序销毁;
隐式销毁一个内置指针类型的成员不会delete 它所指向的对象;
- 当指向一个对象的引用或指针离开作用域时,析构函数不会执行;
- 析构函数体自身并不直接销毁成员,成员是在析构函数体之后隐含的析构阶段中销毁的,在整个对象销毁过程中,析构函数体是作为成员销毁步骤之外的另一部分进行的;
(4):三/五法则
- 如果一个类需要自定义析构函数,几乎可以肯定它也需要自定义拷贝赋值运算符和拷贝构造函数;
- 如果一个类需要一个拷贝构造函数,几乎可以肯定它也需要一个拷贝赋值运算符;
- 我们只能对具有合成版本的成员函数使用 =default(即默认构造函数或拷贝控制成员);
- 大多数类应该定义默认构造函数、拷贝构造函数和拷贝赋值运算符,无论隐式的还是显式的;
- 对于析构函数已删除的类型,不能定义该类型的变量或释放指向该类型动态分配对象的指针;
- 本质上,当不可能拷贝、赋值或销毁类的成员时,类的合成拷贝控制成员就被定义为删除的;
- 试图访问一个未定义的成员将导致链接时错误,通过声明(但不定义)private的拷贝构造函数,可以预先阻止任何拷贝该类型对象的用户代码在编译阶段标记为错误,成员函数或友元函数中的拷贝操作将会导致链接时错误;
希望阻止拷贝的类应该使用 = delete来定义它们自己的拷贝构造函数和拷贝赋值运算符,而不应该将它们声明为private;
当编写赋值运算符时,有两点需要注意:
- 如果将一个对象赋予它自身,赋值运算符必须能正常工作;(销毁左侧运算对象资源之前拷贝右侧运算对象)
- 大多数赋值运算符组合了析构函数和拷贝构造函数的工作;