C++对象模型(一):The Semantics of Constructors The Default Constructor (默认构造函数什么时候会被创建出来)

时间:2022-04-03 08:57:45

本文是 Inside The C++ Object Model, Chapter 2的部分读书笔记。

C++ Annotated Reference Manual中明确告诉我们: default constructor会在需要的时候被编译器产生出来。注意,这里是编译器需要,而不是程序需要。后来的C++ Standard 95修改了这种说法,但是实质上仍是相同的: For class X, if there is none user declared constrator, one default constructor will be implicitly generated by the Compiler.

但实际上,如果default constructor是trivial(无用的),那么编译器根本就不会产生它!只有一下四种情况,non-trivial default constructor会被产生出来,来保证C++语言的机制能够按照预期工作:

1)带有Default Constructor的Member Class Object

如果该class X 包含有带有默认构造函数的成员class object (member object),那么编译器需要对此class X合成一个default constructor。不过该合成只在该合成操作真正被调用时。

被合成的这个default constructor,包含有调用这个member class的default constructor的代码,但是它不会初始化它自身其他的成员变量,比如char *str; int data;这些成员变量的初始化时设计者的责任,而不是编译器的职责!

由此有进一步的思考,如果是设计者已经定义了constructor,来初始化比如char *str; int data; 那么编译器如何初始化其他的member class object呢? 答案就是编译器需要扩张该class的constructor,以按照member class object的声明顺序来调用各个class的default constructor。

2) 带有Default Constructor的Base Class

如果class X继承自一个带有default constructor的class, 那么编译器将为这个class生成non-trivial的default constructor,并且按照base class的顺序逐次调用。

如果user declared 许多constructors,但是没有default constructor(就是没有任何参数的那个constructor),那么编译器不会产生default constructor了。而是扩张每个constructor,使其包含必要的default constructor的扩张代码,比如初始化它的member class object。 参考第一种情况

3) 带有virtual function

对于有virtual function的class,一个virtual function table会被编译器产生出来,存着virtual functions的地址。 而在每一个class的object中,会有一个pointer member(称为vptr)会被编译器生成出来,内容那个class virtual function table的地址。

所以编译器为了使得virtual function的机制生效,必须要为每个这种class的object生成合理的vptr,而vptr的赋值就发上在扩张后的constructors里。如果class没有任何的constructors,那么default constructor会被认为是non-trivial并且会被生成。如果class有constructor/s,那么所有的constructor都会扩张以赋予vptr以合理的值。

4)带有一个virtual base class的class

Virtual base class的实现在不同 的编译器间有极大的差异。共同点就是virtual base class 在其每一个derived class object的位置,在执行期间能够准备妥当。这些工作都要放到constructor中去完成。

总结

  在合成的default constructor中,只有base class subobjects 和member class objects才会初始化。所有其他的nonstatic data member,都不会被初始化,这些工作应该由设计者(程序猿)而不是编译器去完成。