一般说来,C++ 编译器会为我们生成四个函数,默认构造函数,析构函数,拷贝构造函数,赋值函数,如果我们没有声明它们的话。
但情况真的是这样的吗?不,这不是永恒的真理!
首先,明确一点,上面的函数没有声明时,编译器只在认为它们有必要被创建时才去创建,具体而言:
对于构造函数:
只有以下四种情况下会合成默认构造函数(且只会在需要被调用时才合成):
1.某一个成员含有constructor,不论这个函数是用户自己写的,还是编译器生成的。因为编译器必须生成一个构造函数并在里面安放调用那个成员的构造函数的代码,如果有多个成员, 则以它们的声明顺序去调用。
class B
{
char *str;//初始化这个变量不是编译器的事情,不会在合成的构造函数里面初始化它。
Foo foo;
B() //如果不存在任何构造函数,则会生成下面这个构造函数
{
foo.Foo::Foo(); //安插 foo 的构造代码
}
//如果用户定义了构造函数,则会在每个已定义的构造函数里面,安插 foo 的构造代码
}
2.类的基类带有 constructor ,不论这个函数是用户自己写的,还是编译器生成的。编译器必须生成一个构造函数并在里面安放调用基类的构造函数的代码(比调用成员的构造函数要早)。
不再举例,与 1 类似。
3.带有一个 virutual Function ,此时,这个类需要保存一个虚表指针 pvtbl 用于指向虚函数表 vtbl,vtbl 里面存放的是各个虚函数的函数指针。pvtbl 暗含在类中,它在构造函数最开始的地方被初始化指向那个 vtbl。
4.带有一个 "Virtual Base Class"的 class,如菱形继承
class X{ int i; };
class A : public virtual X{};
class B : public virtual X{};
class C : public A,public B{};
在这个继承体系中,如果有这样的代码:
A * pa;
pa -> i = 1;
则 pa -> i 的这个数据必须要在运行时,才能决议出它的地址,因为 A* 的动态类型可能是 C,在 C 里面,i 的地址是不确定的,必须要在 A 的构造函数,且在里面合成一个指针,指向具体类型。
另外,需要注意一点,在合成的 defalt construtor 中,只有 base class subobjects 和 member class objects 会被初始化,所有其它的 nonstatic data member,如整数,整数指针,整数数组等都不会被初始化,因为它们对于编译器来说完全是没有必要的。