原书第二章第一节的内容
关于Default constructor的概念:没有任何形参的constructor(注意,不是说编译器自动为类添加的才叫Default constructor)。
这一节主要讲了编译器什么时候为类添加Default constructor?
先说结论:四种情况下,编译器会自动合成implicit nontrivial default constructor。
- 带有Default constructor的member class object
- 带有Default constructor的Base class
- 带有Virtual function
- 带有Virtual base class
情况一:带有Default constructor的member class object
比如类A的某个成员变量是类B的一个对象,而类B是有Default constructor的,比如:
class Foo{public: Foo(); Foo(int); ...};
class Bar{public: Foo foo; char *str; ...};
这种情况下,Bar没有(显式地)定义自己的Default constructor,编译器会自动合成一个。合成出来的Default constructor也许类似下面的形式:
Bar::Bar()
{
foo.Foo::Foo();
}
说明:
- 编译器在这里完成了对类成员对象的默认构造(调用了foo的默认构造函数),这属于编译器的职责。但是没有对str变量进行初始化(比如也许程序要求str=0),因为这属于类Bar的设计者(程序员)的工作。
- 如果类已经显式声明了一个default constructor并在其中对str初始化了。这时编译器会扩张原有的default constructor(实际上,会扩张所有的显式定义的constructor),在constructor的user code之前调用类成员对象的constructor。
- 如果一个类有多个member class objects,按照声明的顺序,扩张constructor的时候依次调用这些类成员变量的constructor。
情况二:带有Default constructor的Base class
如下:
class Base{public: Base(); ...};
class A : public Base{...};
说明:
- 如果类A没有任何constructor。编译器会自动合成一个Default constructor,然后调用Base的Default constructor。
- 如果类A有多个已定义的constructor。即使已定义的constructor中没有Default constructor,编译器也不会合成一个Default constructor,只会扩张每个已有的constructor,调用Base的Default constructor。
情况三:带有Virtual function
带有虚函数(自己定义的,或者继承而来的)时,需要对vtbl(虚函数表)和vptr(指向虚函数表的指针)进行相应设置以支持多态。
- 如果类没有任何constructor,编译器会自动合成一个Default constructor,完成对vtbl和vptr的设置。
- 如果类定义了constructor,编译器会扩展这些constructor,完成对vtbl和vptr的设置。
情况四:带有Virtual base class
与情况三类似,为了支持虚基类,编译器需要做一些工作。
总结:
这个四种情况下被合成出来的constructor只能满足编译器的需要(而不是程序需要)。可以看出来这四种情况要么调用了类成员或者基类的Default constructor,要么是为了支持virtual机制。
不满足这四种情况时,实际上不会合成一个Default constructor。常见误解会认为如果类没有定义Default constructor,编译器就会自动合成一个,这是不对的。
还有一点值得注意:合成的Default constructor只会初始化那些有构造函数的成员变量。对于整数、数组、指针等基本类型,编译器合成的Default constructor不会处理。所以,又有一个误解认为编译器合成的Default constructor会为我们自动初始化所有的类成员变量,这也是错的。