定义一个新类型的时候,需要显式或隐式地指定复制
、赋值
和撤销
该类型的对象时会发生什么,这是通过定义特殊成员:复制构造函数
、赋值操作符
和 析构函数
来达到的。如果没有显式定义复制构造函数或赋值操作符,编译器(通常)会为我们定义。
复制构造函数、赋值操作符和析构函数总称为复制控制。
编译器自动实现这些操作,但类也可以定义自己的版本。
编译器合成的复制控制函数是非常精练的,它们只做必需的工作。但对某些类而言,依赖于默认定义会导致灾难。实现复制控制操作最困难的部分,往往在于识别何时需要覆盖默认版本。有一种特别常见的情况需要类定义自己的复制控制成员的:类具有指针成员。
复制构造函数是一种特殊构造函数
,具有单个形参
,该形参(常用const修饰)是对该类类型的引用
。与默认构造函数一样 ,复制构造函数可由编译器隐式调用。
直接初始化
和复制初始化
。复制初始化使用“=”符号,而直接初始化将初始化式放在圆括号中。构造函数创建一个临时对象
,然后用复制构造函数
将那个临时对象复制到正在创建的对象。支持初始化的复制形式主要是为了与C的用法兼容。
当情况许可时,可以允许编译器跳过复制构造函数直接创建对象,但编译器没有义务这样做。定义了其他构造函数,也会合成复制构造函数。
非static成员
,依次复制到正创建的对象。只有一个例外,每个成员的类型决定了复制该成员的含义。直接复制内置类型成员的值
,类类型成员使用该类的复制构造函数进行复制
。合成复制构造函数将复制数组的每一个元素。
Foo(const
Foo&)
只包含类类型成员或内置类型(但不是指针类型)成员的类
,无须显式地定义复制构造函数,也可以复制。指针
,或者有成员表示在构造函数中分配的其他资源
。声明一个(private)复制构造函数但不对其定义。
与构造函数一样,赋值操作符可以通过指定不同类型的右操作数而重载。右操作数为类类型的版本比较特殊:如果我们没有编写这种版本,编译器将为我们合成一个。Sales_item
trans, accum;trans = accum;
是构造函数的互补:当对象超出作用域或动态分配的对象被删除时,将自动应用析构函数。析构函数可用于释放对象时构造或在对象的生命期中所获取的资源。不管类是否定义了自己的析构函数,编译器都自动执行类中非static数据成员的析构函数。
析构函数的调用
逆序撤销
。什么时候需要编写显式的析构函数
在类中声明次序的逆序撤销成员。
不能重载析构函数。
只能提供一个析构函数
,应用于类的所有对象即使我们编写了自己的析构函数,合成析构函数仍然运行。
[Code6]string null_book = "9-999-99999-9"; // copy-initialization string dots(10, ‘.‘); // direct-initialization string empty_copy = string(); // copy-initialization string empty_direct; // direct-initialization
ifstream file1("filename"); // ok: direct initialization ifstream file2 = "filename"; // error: copy constructor is private // This initialization is okay only if the Sales_item(const string&) constructor is not explicit Sales_item item = string("9-999-99999-9");
/*复制构造函数可用于初始化顺序容器中的元素。例如,可以用表示容量的单 个形参来初始化容器。容器的这种构造方式使用默认构造函数和复制构造函数*/ // default string constructor and five string copy constructors invoked 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() }; /*如前三个元素的初始化式中所示可以直接指定一个值,用于调用元素类型的 单实参构造函数。如果希望不指定实参或指定多个实参,就需要使用完整的构造 函数语法,正如最后一个元素的初始化那样。*/
// 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; } /*合成赋值操作符根据成员类型使用适合的内置或类定义的赋值操作符,依次给每个成员赋值,该操作符返回 *this,它是对左操作数对象的引用。*/
class Sales_item { public: // empty; no work to do other than destroying the members,which happens automatically ~Sales_item() { } // other members as before }; /*撤销 Sales_item 类型的对象时,将运行这个什么也不做的析构函数,它执 行完毕后,将运行合成析构函数以撤销类的成员。合成析构函数调用 string 析 构函数来撤销 string 成员,string 析构函数释放了保存 isbn 的内存。 units_sold 和 revenue 成员是内置类型,所以合成析构函数撤销它们不需要做什么。*/
原文:http://blog.csdn.net/liufei_learning/article/details/21312701