题记:本系列学习笔记(C++ Primer学习笔记)主要目的是讨论一些容易被大家忽略或者容易形成错误认识的内容。只适合于有了一定的C++基础的读者(至少学完一本C++教程)。
作者: tyc611, 2007-01-29
本文主要讨论C++类定义中的拷贝控制(copy control):拷贝构造函数、赋值操作符和析构函数。
如果文中有错误或遗漏之处,敬请指出,谢谢!
C++类中有四个不可或缺的部分,那就是构造函数、拷贝构造函数、赋值操作符和析构函数。如果类中没有定义这些函数,那么编译器将为类自动生成这些函数。当然,你也可以通过private控制策略限定不使用拷贝构造函数和赋值操作符。
其中,拷贝构造函数、赋值操作符和析构函数总称为拷贝控制(copy control)。
当类中有指针类数据成员时,一般都需要自已实现类的拷贝控制。通常有两种处理策略:一是定义值型类,每个类保留一份指针指向的对象的拷贝;另一种更常用的策略是使用智能指针(smart pointer),其通用技术是采用引用计数(reference count)来实现共享指针指向的对象。
拷贝构造函数
我们知道C++中变量初始化有两种形式:直接初始化和拷贝初始化。直接初始化将初始化放在圆括号中,而拷贝初始化使用=符号。对于内置类型,这两者基本上没有区别。但对于类类型,两种方式是有区别的:直接初始化直接调用与实参匹配的构造函数;而拷贝初始化总是调用拷贝构造函数,具体而言,就是拷贝初始化首先使用指定构造函数创建一个临时对象,然后用拷贝构造函数将那个临时对象拷贝到正在创建的对象。
支持拷贝初始化主要是为了与C的用法兼容。当情况允许时,可以允许编译器跳过拷贝构造函数直接创建对象,但编译器没有义务这样做。注:事实上大多数编译器都跳过了拷贝构造函数,因为这完全可以跳过,比如在VC6.0和MinGW2.05。
我们知道可以用表示容量的单个参数来初始化容器,容器的这种构造方式使用了默认构造函数和拷贝构造函数。例如:
vector<string> svec(5);
编译器首先使用string的默认构造函数创建一个临时值来初始化svec,然后使用拷贝构造函数将临时对象拷贝到svec的每个元素。示例代码如下:
#include <iostream> |
输出结果为:(MinGW 2.05和VC6.0) |
对于元素为类类型的数组,可以使用数组初始化列表来提供显示元素初始化。此时,使用拷贝初始化来初始化每个元素。根据指定值创建适当类型的元素,然后用拷贝构造函数将该值拷贝到相应元素。当然,同前面一样,是否跳过拷贝构造函数取决于编译器(事实上,大多数编译器跳过了这步)。
拷贝构造函数的形参是一个类类型引用(否则,参数本身就需要过拷贝构造函数了),但一般情况下,我们使用const修饰。并且,一般不应该设置为explicit。
有时需要禁止拷贝类,例如,iostream类就不允许拷贝。这时,应当显示声明拷贝构造函数为private,此时可以不定义该函数。若声明为private且进行了函数定义,则类的友元和成员仍然可以进行拷贝。
注意:声明而不定义成员函数是合法的,但是,使用未定义成员的任何尝试将导致链接失败。 |
合成的拷贝构造函数
如果我们没有定义拷贝构造函数,则编译器会自动生成一个,把这个自动生成的拷贝构造函数叫合成的拷贝构造函数(Synthesized Copy Constructor)。合成的拷贝构造函数执行逐个成员初始化,将新对象初始化为原对象的副本。如果成员是内置类型,则执行位拷贝;如果成员是类类型,则调用相应的拷贝构造函数;如果成员是数组类型,则分别对每个数组元素进制拷贝。
赋值操作符
赋值操作符的右操作数一般以const引用传递,为了与内置类型的行为一致,常返回该类类型的引用。通常,拷贝构造函数和赋值操作符是同时出现的,定义了一个就应当定义另一个。
赋值操作符一个必须小心的地方时检查自赋值。
析构函数
三法则(rule of three):如果类需要自定义析构函数,则它往往也需要拷贝构造函数和赋值操作符。
合成的析构函数按成员在类中声明次序的逆序来撤销成员。
析构函数与拷贝构造函数或者赋值操作符之间的一个重要区别是:即使我们编写了自己的析构函数,在自定义析构函数运行结束后,合成析构函数仍然将继续运行,它来完成成员数据的撤销工作。
如果文中有错误或遗漏之处,敬请指出,谢谢!
参考文献:
[1] C++ Primer(Edition 4)
[2] Thinking in C++(Volume Two, Edition 2)
[3] International Standard:ISO/IEC 14882:1998
[2] Thinking in C++(Volume Two, Edition 2)
[3] International Standard:ISO/IEC 14882:1998