有4种情况,编译器必须为未声明构造函数的类合成默认构造函数,C++Standard将其称作有用的隐式默认构造函数。被合成出来的构造函数只能满足编译器的需要,而非程序。
在合成的默认构造函数中,只有基类子对象和成员对象会被初始化,所有其他的非静态数据成员都不会被初始化。提供初始化操作的人应该是程序员。
一、成员对象带有默认构造函数
若一个类没有任何构造函数,但他有一个成员对象,该对象具有默认构造函数。则编译器需要对这个类生成一个默认构造函数。但这个生成行为只有在构造函数真正需要被调用时才会发生。
在多个不同编译模块中,编译器如何避免合成多个默认构造函数呢?办法是将合成的默认构造函数、拷贝构造函数、析构函数、拷贝赋值运算符都以内联形式完成,内联函数有静态链接,不会被文件外部看到,若函数过于复杂不能内联,则合成一个显式的非内联静态实例。
二、基类带有默认构造函数
若一个没有构造函数的类继承自一个带有默认构造函数的基类,则这个类的默认构造函数需要合成出来,它将调用上一层基类的默认构造函数,对于一个派生类而言,这个合成的构造函数和一个被显式提供的默认构造函数没有什么差异。
若设计者提供多个构造函数,但都没有默认构造函数,那么编译器会扩展现有的每一个构造函数,将所有必要的代码加进去,它不会额外合成一个新的默认构造函数。同时,如果存在带有默认构造函数的成员对象,他们的构造函数也会在所有基类的构造函数被调用之后被调用。
三、带有虚函数
若类声明或继承了一个虚函数,编译器会合成一个虚函数。下面两个扩张行为会在编译期间完成。
1.一个虚函数表会产生,存放类的虚函数地址
2.在每个对象中,一个额外的成员指针会合成出来,其指向虚函数表地址。
四、类派生自一个继承链,其中存在一个或更多的虚基类
虚继承必须使虚基类在每一个派生类对象中的位置在执行期准备妥当。由于对象类型可以改变,编译器无法固定其成员实际偏移位置,因此必须改变执行存取操作的代码,使其可以延迟至执行期才决定下来。不同编译器对虚继承的实现有较大差异,可以通过在派生类对象的每一个虚基类中安插一个指针来完成(cfront做法)。所有经过引用或指针来对虚基类进行存取的操作都可以通过指针完成。
合成的默认构造函数之所以能完成任务,是借着调用成员对象或基类的默认构造函数,或为每个对象初始化其虚函数机制或虚基类机制而完成的。至于不符合这几种情况,而又未声明构造函数的类,他们的默认构造函数是无用的,实际上也并不会合成出来。
摘自《深度探索C++对象模型》