C++ 支持两种初始化形式):直接初始化和复制初始化。复制初始化使用 = 符号,而直接初始化将初始化式放在圆括号中。
当形参或返回值为类类型时,由复制构造函数进行复制
string make_plural(size_t, const string&, const string&);
这个函数隐式使用 string 复制构造函数返回给定单词的复数形式。形参是 const 引用,不能复制。
复制构造函数可用于初始化顺序容器中的元素。vector<string> svec(5);
编译器首先使用 string 默认构造函数创建一个临时值来初始化 svec,然后使用复制构造函数将临时值复制到svec 的每个元素。
如果没有为类类型数组提供元素初始化式,则将用默认构造函数初始化每个元素。然而,如果使用常规的花括号括住的数组初始化列表(第 4.1.1 节)来提供显式元素初始化式,则使用复制初始化来初始化每个元素。根据指定值创建适当类型的元素,然后用复制构造函数将该值复制到相应元素:
Sales_item primer_eds[] = { string("0-201-16487-6"), string("0-201-54848-8"), string("0-201-82470-1"), Sales_item() };
如果我们没有定义复制构造函数,编译器就会为我们合成一个。与合成的默认构造函数(第 12.4.3 节)不同,即使我们定义了其他构造函数,也会合成复制构造函数。合成复制构造函数的行为是,执行逐个成员初始化,将新对象初始化为原对象的副本。
所谓“逐个成员”,指的是编译器将现在对象的每个非 static 成员,依次复制到正创建的对象。只有一个例外,每个成员搂类型决定了复制该成员的含义。合成复制构造函数直接复制内置类型成员的值,类类型成员使用该类的复制构造函数进行复制。数组成员的复制是个例外。虽然一般不能复制数组,但如果一个类具有数组成员,则合成复制构造函数将复制数组。复制数组时合成复制构造函数将复制数组的每一个元素。
制构造函数就是接受单个类类型引用形参(通常用 const 修饰)的构造函数:
class Foo { public: Foo(); // default constructor Foo(const Foo&); // copy constructor // ... };
无须显式地定义复制构造函数,也可以复制。
然而,有些类必须对复制对象时发生的事情加以控制。这样的类经常有一个数据成员是指针,或者有成员表示在构造函数中分配的其他资源。而另一些类在创建新对象时必须做一些特定工作。这两种情况下,都必须定义复制构造函数。
为了防止复制,类必须显式声明其复制构造函数为 private。
与复制构造函数一样,如果类没有定义自己的赋值操作符,则编译器会合成一个。
例如,Sales_item 的赋值操作符可以声明为:
class Sales_item { public: // other members as before // equivalent to the synthesized assignment operator Sales_item& operator=(const Sales_item &); };
合成赋值操作符与合成复制构造函数的操作类似。它会执行逐个成员赋值:右操作数对象的每个成员赋值给左操作数对象的对应成员。除数组之外,每个成员用所属类型的常规方式进行赋值。对于数组,给每个数组元素赋值。
As an example, the synthesized Sales_item assignment operator would look something like:
例如,Sales_item 的合成赋值操作符可能如下所示:
// equivalent to the synthesized assignment operator Sales_item& Sales_item::operator=(const Sales_item &rhs) { isbn = rhs.isbn; // calls string::operator= units_sold = rhs.units_sold; // uses built-in int assignment revenue = rhs.revenue; // uses built-in double assignment return *this; }
可以使用合成复制构造函数的类通常也可以使用合成赋值操作符。我们的 Sales_item 类无须定义复制构造函数或赋值操作符,而,类也可以定义自己的赋值操作符。一般而言,如果类需要复制构造函数,它也会需要赋值操作符。