第 13 章 复制控制
1, 复制构造函数 copy constructor
2, 复制操作符 assignment operator
3, 析构函数 destructor
4, 管理指针成员
复制控制 copy control {copy constructor, assignment operator, destructor}
有一种特别常见的情况需要类定义自己的复制控制成员的 : 类具有指针成员。
13.1 复制构造函数
1>, 对象的定义形式 :
string book = "9999"; // copy-initialization 临时对象 再副本给 book
string dots(10, '.'); // direct-init
string empty_copy = string(); // copy-init 临时对象
string empty_direct; // 默认构造函数
ifstream file1("filename"); // ok : direct init
ifstream file2 = "filename"; // error : copy constructor is private 由于不能复制 IO 类型对象!所以不能对那些类型使用复制初始化!
Sales_item item = string("9999"); // ok or error 取决于正在使用哪一个版本的 Sales_item, explicit 如果构造函数为显式的,则初始化失败!否则成功。
2>, 形参与返回值
当形参为非引用类型的时候,将复制实参的值!类似地,以非引用类型作返回值时,将返回 return 语句中的值的副本。
当形参或返回值为类类型时,由复制构造函数进行复制。
3>, 初始化容器元素
vector<string> sec(5);
编译器首先使用 string 默认构造函数创建一个临时值来初始化 sec, 然后使用 string 的复制构造函数将临时值复制到 sec 的每个元素。
作为一般规则,除非你想使用容器元素的默认初始值,更有效的办法是,分配一个空容器并将已知元素的值加入容器。 顺序容器一定是的!别的就不知道了!
4>, 构造函数与数组元素
如果没有为类类型数组提供元素初始化式,则将用默认构造函数初始化每个元素。
如果使用常规的花括号括住的数组初始化列表来提供显式元素初始化式,则使用复制初始化来初始化每个元素!复制构造函数。。。
13.2 定义自己的复制构造函数
1>, 复制构造函数就是接受单个类类型引用形参 (通常用 const 修饰) 的构造函数。
对许多类而言合成复制构造函数只完成必要的工作。只包含类类型成员或内置类型(非指针成员)成员的类。也可复制。
13.1.3 禁止复制
注解 : 为了防止复制,类必须显式声明其复制构造函数为 private 例如 iostream 就是! 通过声明但不定义 private 复制构造函数。 是一个绝招!
“ 大多数类应该定义 复制构造函数 和 默认构造函数。”
13.2 赋值操作符
1>, 介绍重载赋值
2>, 合成赋值操作符 synthesized assignment operator
与合成复制构造函数类似。
例如 :
Sales_item& Sales_item::operator=(const Sales_item &rhs) {
isbn = rhs.isbn;
units_sold = rhs.units_sold;
revenue = rhs.revenue;
return *this;
}
3>, 复制和赋值常一起使用
应该将两个操作看作一个单元。如果需要其中一个,我们几乎肯定也需要另一个!
13.3 析构函数
构造函数的用途是自动获取资源。例如 : 构造函数可以分配一个缓冲区或打开一个文件,
在构造函数中分配了资源之后,需要一个对应操作自动回收或释放资源。
所以 : 析构函数就是这样的一个特殊函数,它可以完成所需的资源回收,作为类构造函数的补充。
1>, 何时调用析构函数
撤销类对象时会自动调用析构函数。
动态分配的对象只有在指向该对象的指针被删除时才撤销。
注解 : 当对象的引用或指针超出作用域时,不会运行析构函数。只有删除
指向动态分配对象的指针或实际对象(而不是对象引用)超出作用域时,才运行析构函数。
撤销一个容器时,也会运行容器中的类类型元素的析构函数。逆序撤销。
三法则 : 如果类需要析构函数,则它也需要赋值操作符和复制构造函数,这是一个有用的经验法则。
3>, 合成析构函数
编译器总会为我们合成一个析构函数!合成析构函数按对象创建时的逆序撤销每个非static成员。
但是 合成析构函数 : 并不删除指针成员所指向的对象!
4>, 如何编写析构函数
Sales_item
虽然可以为一个类定义多个构造函数,但只能提供一个析构函数,应用于类的所有对象!
析构函数与复制构造函数或赋值操作符之间的一个重要区别是,即使我们编写了自己的析构函数
,合成析构函数仍然运行!
class Sales_item {
public :
~Sales_item() {}
private :
string isbn;
}
13.5 管理指针成员
使用标准库能够大大减少现代 C++ 程序中对指针的需要!
包含指针的类需要特别注意 : “复制控制” {
原因 : 复制指针时,只复制指针中的地址,而不会复制指针指向的对象!
}
具有指针成员且使用默认合成复制构造函数的类具有普通指针的所有缺陷。
尤其是,类本身无法避免悬垂指针。
大多数 C++ 类采用以下三种方法之一管理指针成员。
1>, 指针成员采取常规指针型行为。 这样的类具有指针的所有缺陷但无需特殊的复制控制。
2>, 类可以实现所谓的 “智能指针” 行为。指针所指向的对象是共享的,但类能够防止悬垂指针。
3>, 类采取值型行为。指针所指向的对象是唯一的,由每个类对象独立管理。