C++ Annatated Reference Manual告诉我们:“default construct会在需要的时候被编译器产生出来”。关键字眼在于什么时候需要以及被谁需要。一般说来,default construct的需求方有程序员和编译器。如果程序员需要,那么他就有责任在程序中声明construct function。如果是编译器需要,那么编译器就会在程序中合成construct。当然,合成construct只会执行编译器需要的行动。下面讲一下编译器合成的default construct有效的四种情况:
1)带有“default constructor”的menber class object
如果一个类没有任何construct,但它含有一个member object,而后者有default construct,那么这个class的implict default construct就是“nontrival”,编译器需要为此合成一个default constructor.不过这个合成操作只有在constructor真正调用时才会发生。
一个有趣的问题就发生了,编译器如何避免在不同的编译模块中合成出多个default constructor?就解决方法就是把合成的default construct ,destructor,assigment copy operator都已inline的方式完成。一个inline函数都有静态链接,不会被档案以外的部分看到。
举个例子,在下面的程序中,编译器为class Bar合成一个default constructor:
class Foo{
public;
Foo();
Foo(int );
}
class Bar{
public:
Foo foo;
char *str;
}
void foo_bar(){
Bar bar;//必须再次初始化
if(str){
}
}
被合成的Bar default constructor 内含有必要的代码,能够调用Foo default constructor来处理membert object Bar::foo,但它并不产生任何代码来初始化Bar::str。将Bar::str初始化是程序员的责任,将Bar::foo初始化时编译器的责任。被合成的default constructor看起来像这样:
inline Bar::Bar(){
foo.Foo::Foo();
}
再一次注意,被合成的default construct是编译器的需要,而不是程序员的需要。为了能让这个程序片段真正运行起来,字符串str也需要被初始化。假设程序员经由下面的default constructor提供str的初始化操作:
Bar::Bar(){
str=0;
}
现在程序的需求满足了,但是编译器还需要初始化member object foo,由于default constructor已经被明确定义出来了,编译器没办法合成第二个。编译器采取的行动是将初始化foo的代码插入到Bar default constructor中,使得用户代码在执行之前先调用必要的default constructor,扩大后的代码可能是:
Bar::Bar(){
foo.Foo::Foo();//附加的编译器代码
str=0;//explict user code
}
如果有多个class member object 都要求construct初始化操作,编译器将如何做呢?c++语言要求以“ member object”在class中的声明次序来调用各个construct,这一点由编译器来完成。它会为每一个member object安插程序代码,以“member声明次序”调用每一个member object的default constructor,这些代码将会被安插在explict user code之前。举个例子
class Dopey{
public:
Dopey();
}
class Sneezy{
public:
Sneezy();
Sneezy(int );
}
class Snow_white{public:
Dopey dopey;
Sneezy sneezy;
private;
int number;
}
如果class Snow_white没有定义default constructor,编译器就会合成一个nontrival constructor被合成出来。然而,Snow_white定义了下面的default constructor。
Snow_white::Snow_white();sneezy(1023){
number=2045;
}
它会被扩张如下:
Snow_white::Snow_white();sneezy(1023){
//插入member class object
dopey.Dopey::Dopey();
sneezy.Sneezy::Sneezy(1024);
//explict user code
number=2045;
}
2)带有default constructor的Base Class如果一个没有任何constructor的class派生自一个带有“default constructor”的base class,那么derive class的default constructor会被是为nontrivial,并因此需要被合成出来。他将调用上一层base class的default constructor(按照他们的声明次序),对一个后继派生的class而言,这个合成的construtor和一个被明确提供的default constructor没有什么差异。
如果设计者提供多个constructor,但其中都没有Base Class的default constructor呢?编译器会扩张现有每一个constructor,将所有用以调用必要的dafault constructor的代码加进去,它不会合成一个新的default constructor,这是因为其它由“user 所提供的constructor存在的缘故”,如果同时存在“带有default constructor”的membe class object,那些default constructor也会被调用----在所有base class constructor都被调用之后。