合成的复制构造函数与自定义复制构造函数
合成的复制构造函数
如果我们没有定义复制构造函数,编译器就会为我们合成一个。与合成的默认构造函数不同,即使我们定义了其他构造函数,也会合成复制构造函数。合成复制构造函数的行为是,执行逐个成员初始化,将新对象初始化为原对象的副本。所谓“逐个成员”,指的是编译器将现在对象的每个非 static 成员,依次复制到正创建的对象。只有一个例外, 每个成员搂类型决定了复制该成员的含义。合成复制构造函数直接复制内置类型成员的值, 类类型成员使用该类的复制构造函数进行复制。数组成员的复制是个例外。虽然一般不能复制数组,但如果一个类具有数组成员,则合成复制构造函数将复制数组。复制数组时合成复制构造函数将复制数组的每一个元素。
逐个成员初始化最简单的概念模型是,将合成复制构造函数看作这样一个构造函数:其中每个数据成员在构造函数初始化列表中进行初始化。例如,对于Sales_item 类,它有三个数据成员:
class Sales_item {
// other members and constructors as before
private:
std::string isbn;
int units_sold;
double revenue;
};
合成复制构造函数如下所示:
Sales_item::Sales_item(const Sales_item &orig):
isbn(orig.isbn), // uses string copy constructor
units_sold(orig.units_sold), // copies orig.units_sold
revenue(orig.revenue) // copy orig.revenue
{ } // empty body
定义自己的复制构造函数
复制构造函数就是接受单个类类型引用形参(通常用 const 修饰)的构造函数:
class Foo {
public:
Foo(); // default constructor
Foo(const Foo&); // copy constructor
// ...
};
虽然也可以定义接受非 const 引用的复制构造函数,但形参通常是一个const 引用。因为用于向函数传递对象和从函数返回对象,该构造函数一般不应设置为 explicit。复制构造函数应将实参的成员复制到正在构造的对象。
对许多类而言,合成复制构造函数只完成必要的工作。只包含类类型成员或内置类型(但不是指针类型)成员的类,无须显式地定义复制构造函数,也可以复制。
然而,有些类必须对复制对象时发生的事情加以控制。这样的类经常有一个数据成员是指针,或者有成员表示在构造函数中分配的其他资源。而另一些类在创建新对象时必须做一些特定工作。这两种情况下,都必须定义复制构造函数。
通常,定义复制构造函数最困难的部分在于认识到需要复制构造函数。只要能认识到需要复制构造函数,定义构造函数一般非常简单。复制构造函数的定义与其他构造函数一样:它与类同名,没有返回值,可以(而且应该)使用构造函数初始化列表初始化新创建对象的成员,可以在函数体中做任何其他必要工作。
禁止复制
有些类需要完全禁止复制。例如,iostream 类就不允许复制。如果想要禁止复制,似乎可以省略复制构造函数,然而,如果不定义复制构造函数,编译器将合成一个。为了防止复制,类必须显式声明其复制构造函数为 private。
如果复制构造函数是私有的,将不允许用户代码复制该类类型的对象,编译
器将拒绝任何进行复制的尝试。然而,类的友元和成员仍可以进行复制。如果想要连友元和成员中的复制也禁止,就可以声明一个(private)复制构造函数但不对其定义。声明而不定义成员函数是合法的,但是,使用未定义成员的任何尝试将导致链接失败。通过声明(但不定义)private 复制构造函数,可以禁止任何复制类类型对象的尝试:用户代码中复制尝试将在编译时标记为错误,而成员函数和友元中的复制尝试将在链接时导致错误。
大多数类应定义复制构造函数和默认构造函数
不定义复制构造函数或默认构造函数,会严重局限类的使用。不允许复制的类对象只能作为引用传递给函数或从函数返回,它们也不能用作容器的元素。
一般来说,最好显式或隐式定义默认构造函数和复制构造函数。只有不存在其他构造函数时才合成默认构造函数。如果定义了复制构造函数,也必须定义默认构造函数。

浙公网安备 33010602011771号