什么时候一个空类(empty class)不再是一个空类(empty class)呢?当C++处理过它之后。如果你自己不声明,编译器就会为你声明(一个编译器版本)的一个copy constructor、一个copy assignment操作符、一个析构函数。此外,如果你没有声明任何构造函数(constructor),编译器还会为你生成一个默认构造函数(default constructor)。所有这些函数都是public和inline(参见Item30)。因此,如果你写下:
这就好像你写了如下的代码:
只有这些函数被需要,它们才会被创建(These functions are generated only if they are needed)。如下的代码造成上述每个函数被编译器产生:
好,现在我们已经知道编译器会为你写函数,但这些函数做了什么呢?唔,default构造函数和析构函数(destructor)给编译器一个地方用来放置“幕后代码”("behind the scenes "code),如调用基类(base class)与非静态数据成员(non-static data members)的构造函数(constructor)和析构函数(destructor)。注意生成的析构函数是non-virtual的(见Item7)。除非这个类(class)继承自一个基类(base class),而这个基类声明了一个虚析构函数(virtual destructor)。
至于copy构造函数和copy assignment操作符,编译器生成的版本只是简单地将源对象(source object)中每个non-static数据成员拷贝至目标对象(Target object)。考虑一个NamedObject template,它允许你将名字与类型为T的对象相关联:
因为NamedObject当中已经声明了一个constructor,编译器不会生成一个default constructor。这很重要,意味着你用心设计一个class,其构造函数要求实参,你就无须担心编译器会为你添加一个无实参的构造函数(即default 构造函数)来覆盖你的版本。
NamedObject即没有声明copy constructor也没有声明copy assignment操作符。所以编译器会为你生成这些函数(如果有需要的话)。现在看看copy构造函数的用法:
编译器生成的copy constructor必须以no1.nameValue和no1.objectValue来初始化no2.nameValue和no2.objectValue。nameValue的类型是string,标准的string类型有其copy构造函数。所以no2.nameValue是通过调用string的copy构造函数并以no1.nameValue作为实参来进行初始化的。另一个成员NamedObject<int>::objectValue是int类型,int是一个内置类型,所以no2.objectValue通过拷贝no1.objectValue里的每一个bit来初始化。
编译器为NameObject<int>所生成的copy assignment操作符,其行为基本上与copy构造函数如出一辙。但通常,compiler-generated copy assignment operators behave as I've described only when the resulting code is both legal and has a reasonable chance of making sense。万一两个条件都不满足,编译器会拒绝为class生成operator=。
举个例子,例如NamedObject定义如下,nameValue是一个指向string的reference,objectValue是一个cosnt T:
现在考虑下会发生什么事情:
赋值之前,不论p.nameValue和s.nameValue都指向string对象(当然不是同一个)。赋值动作该如何影响p.nameValue呢?赋值之后p.nameValue应该指向s.nameValue所指的那个string吗?也就是说reference本身可以被改变吗?如果是,那可就开辟了新天地了。因为C++是不允许reference指向不同的对象。换一个想法,是不是reference所指向的string本身被修改呢?这又影响了拥有指向该string的指针或reference的对象了。或者对象并不直接参与到赋值中?编译器生成的copy assignment操作符到底应该怎么做呢?
面对这个难题,C++的响应是拒绝编译那一行赋值动作。如果你打算在一个“内含reference成员”的class内支持赋值操作(assignment),你就必须自己定义copy assignment操作符。对于内含const成员的类(如上面的objectValue)编译器的反应也是一样。修改const成员是不合法的,所以编译器生成的赋值函数(compiler-generated assignment function)不知道如何处理了。最后还有一种情况:如果某个base class将copy assignment操作符声明为private,编译器将拒绝为其derived classes生成一个copy assignment操作符。毕竟,编译器为derived classes生成的copy assignment操作符是可以处理基类部分(base class parts)。(参见item12),但是这样做呢,它们当然不能调用derived class无权调用的成员函数。(but in doing so, they certainly can't invoke member functions the derived class has no right to call)。
请记住:
1、编译器可暗自为class创建default构造函数、copy构造函数、copy assignment操作符,以及析构函数。