第十三章 拷贝控制
- 在定义任何C++类时,拷贝控制操作都是必要部分。
- 拷贝构造函数的第一个参数必须是引用类型,且是自身类类型的引用。
- 拷贝构造函数在几种情况会被隐式使用,所以不应该是explicit
- 合成拷贝构造函数会进其参数的成员逐个拷贝到正在创建的对象中
- 每个成员的类型决定了它的拷贝方式:对于类类型的成员,使用拷贝构造函数来拷贝;内置类型的成员则直接拷贝。不能直接拷贝一个数组,合成拷贝构造函数会逐元素地拷贝一个数组
- 直接初始化时,编译器选择与提供的参数最匹配的构造函数
- 拷贝初始化时,编译器将右侧运算对象拷贝到正在创建的对象中,如有需要将进行类型转换
- 拷贝初始化通常由拷贝构造函数来完成,但如果一个类有移动构造函数,则使用移动构造函数来完成
- 拷贝初始化发生的情况:a,使用=定义变量;b,将一个对象作为实参传递给一个非引用类型的形参;c,从一个返回类型为非引用类型的函数返回一个对象;d,用花括号列表初始化一个数组中 的元素或一个聚合类的成员
- 某些类类型还会对他们分配的对象进行拷贝初始化。例如,初始化标准库容器或者调用其insert或push成员,容器会对其元素进行拷贝初始化;用emplace成员进行直接初始化.P308
- 在函数调用过程中,具有非引用类型的参数要进行拷贝初始化
- 拷贝构造函数被用来初始化非引用类类型参数,这一特性解释了为什么拷贝构造函数自己的参数必须是引用类型的。如果其参数不是引用类型,则调用永远不会成功
- 赋值运算符通常应该返回一个指向其左侧运算对象的引用
- 析构函数释放对象使用的资源,并销毁对象的非static数据成员。不接受参数也没有返回值。由于不接受参数,故不能重载。对于一个给定类,只有唯一的析构函数
- 在一个构造函数中,成员的初始化是在函数体执行之前完成,且按照他们在类中出现的顺序初始化。
- 在一个析构函数中,首先执行函数体,然后销毁成员。成员按初始化顺序的逆序销毁
- 析构部分是隐式的。内置类型没有析构函数,类类型的成员执行成员自己的析构函数。
- 隐式销毁一个内置指针类型的成员 不会delete它所指向的对象。
- 与普通指针不同,智能指针是类类型,有析构函数
- 无论一个对象何时被销毁,都会自动调用其析构函数:a,变量在离开其作用域时被销毁;B,当一个对象被销毁,其成员也被销毁 ;C,容器被销毁时,其元素被销毁;D,对于动态分配的对象,当对指向它的指针应用delete运算符时被销毁;E,对于临时对象,当创建它的表达式结束时被销毁
- 当指向一个对象的引用或指针离开作用域时,析构函数不会执行。
- 析构函数本身并不直接销毁成员,成员是在析构函数体之后隐含的析构部分被销毁。
- 如果合成析构函数是空的,则销毁成员;如果非空,一般是类用来阻止该类型的对象被销毁
- 需要析构函数的类也几乎肯定需要拷贝和赋值操作
- 需要拷贝操作的类也需要赋值操作,反之亦然
- 需要拷贝函数/赋值运算符,不必然意味着需要析构函数
- 当我们在类内用=default修饰成员的声明时,合成的函数将隐式地声明为内联
- 我们只能对具有合成版本的成员函数使用=default
- 我们可以将拷贝构造函数和拷贝赋值运算符定义为删除的函数来阻止拷贝。删除的函数:我们虽然声明了它们,但不能以任何方式使用他们。在参数列表后加上=delete。
- =deleted必须出现在函数第一次声明的时候
- 析构函数不能是删除的成员
- 本质上,当不可能拷贝,赋值或销毁类的成员时,类的合成拷贝控制成员就被定义为删除的
- 希望组织拷贝的类应该使用=delete来定义它们自己的拷贝构造函数和拷贝赋值运算,而不应该将它们声明为private
- 管理类外资源的类必须定义拷贝控制成员。
- IO类型和unique_ptr不允许拷贝或赋值,所以它们的行为既不像值也不像指针
- 使用拷贝和交换的赋值运算符自动就是异常安全的,且能正确处理自赋值
- P461~P489 控制操作案例+动态内存管理(尚未学习)