复制构造函数、赋值操作符和析构函数总称为复制控制。编译器自动实现这些操作,但类也可以定义自己的版本。
实现复制控制操作最困难的部分,往往在于识别何时需要覆盖默认版本。有一种特别常见的情况需要类定义自己的复制控制成员:类具有指针成员。
一、复制构造函数
特点:只有单个形参;该形参是对本类型对象的引用(const T&)。
复制构造函数可以用于:
1. 根据另一个同类型的对象显式或隐式地初始化一个对象。
2. 复制一个对象,将它作为实参传给一个函数。
3. 从函数返回时复制一个对象。
4. 初始化顺序容器中的元素。
5. 根据元素初始化式列表初始化数组元素。
1.1、初始化:复制初始化和直接初始化
int i(1024); //direct initialization.
int a = 1024; //copy initialization.
两者之间的不同:
直接初始化直接调用与实参匹配的构造函数;复制初始化总是调用复制构造函数。复制初始化首先使用指定构造函数创建一个临时对象,然后用复制构造函数将那个临时对象复制到正在创建的对象:
string null_book = "aaaaaaaaaa"; //copy initialization
string dot(10, '.'); //direct initialization
string empty_copy = string(); //copy
string empty_direct; //direct
1.2、形参与返回值
正如我们所知,当形参为非饮用类型的时候,将复制实参的值。类似地,以非引用类型作为返回值时,将返回return语句中的副本。
当形参或返回值为类类型时,由复制构造函数进行复制。当形参或返回值为类型引用时,不能复制。如:
//copy constructor used to copy the return value.
//parameters are references, so they aren't copied.
string make(size_t, const string&, const string&);
1.3、合成的复制构造函数
如果我们没有定义复制构造函数,编译器就会为我们合成一个。
与合成的默认构造函数不同,即使我们定义了其他构造函数,也会合成复制构造函数。合成复制构造函数的行为是:执行逐个成员(非static 成员)初始化,将对新对象初始化为原对象的副本。
例如:
class Sales_item { private:str::string isbn;int sold;double revenue; }合成复制构造函数如下:Sales_item::Sales_item(const Sales_item&obj) {isbn(obj.isbn); //uses string copy construtor sold(obj.sold);revenue(obj.revenue); }
二、禁止复制
有的类需要完全禁止复制。例如:iostream类。
如果复制构造函数是private的,将不会允许用户代码复制该类类型的对象,编译器将拒绝任何进行复制的尝试。
然而,友元和成员仍可以进行复制。如果想要连友元和成员中的复制也禁止,就可以声明一个private复制构造函数但不对其定义。
这是因为:声明而不定义成员函数是合法的,但是,使用未定义的成员的任何尝试将导致链接失败。