当编译器需要的时候,而不是程序员需要(初始化为0的操作),一个nontrivial default constructor就是编译器所需要的那种,必要的话由编译器合成出来。
nontrivial default constructor 的4种情况:
1. “带有Default Constructor”的Member Class Object
如果一个class 没有任何constructor,但它内含一个member object,而后者由default constructor,那么这个class的implicit default constructor就是“nontrivial”,编译器需要为该class合成出一个default constructor。不过这个合成操作只有在constructor真正需要被调用时才会发生。
class Foo {
public:
Foo();
Foo(int);
};
class Bar {
private:
Foo foo; // 初始化foo是编译器的责任
char*str; // 初始化str为是程序员的责任
};
void foo_bar(){
Bar bar; // Bar::foo 必须在此处初始化,是一个member object,class Foo 拥有default constructor。
/* Bar的default constructor可能会被这样合成
inline Bar::Bar()
{
foo.Foo::Foo();
}
*/
}
2. “带有Default Constructor”的Base Class
如果一个没有任何constructors的class派生自一个“带有default constructor”的base class,那么这个derived class 的default constructor会被视为nontrivial,并因此需要被合成出来。它将调用上一层base classes的default constructor(根据它们的声明顺序)。如果同时也存在“带有Default Constructor”的Member Class Object,那些default constructor也会被调用–在所有base class constructor都被调用之后。
3. “带有一个Virtual Function”的Class
a. class 声明(或继承)一个virtual function。
b. class 派生自一个继承串链,其中有一个或更多的virtual base classes。
下面两个扩张行动会在编译期间发生:
a. 一个virtual function table(vtbl)会被编译器产生出来,内放class的virtual functions地址。
b.在每一个class object中。一个额外的pointer member(vptr)会被编译器合成出来,内含相关之class vtbl的地址。
class Widget {
public:
virtual void flip() = 0;
}
void flip(const Widget& widget)
{
widget.flip();
// (*widget.vptr[1](&widget))
// 1 表示flip()在virtual table 中的固定索引
// &widget代表要交给“被调用的某个flip()函数实例”的this指针
}
编译器必须为每一个类对象的vptr设定初值,放置适当的virtual table地址。对于class所定义的每一个constructor,编译器会安插一些代码做这样的事情。对于那些未声明任何constructors的classes,编译器会为它们合成一个default constructor,以便正确地初始化每一个class object的vptr。
4.“带有一个Virtual Base Class”的Class
Virtual Base Class的实现法在不同的编译器之间有极大的差异。然而,每一种实现法的共同点在于必须使virtual base class 在其每一个derived class object中的位置,能够于执行期准备妥当。
class X {public: int i;};
class A : public virtual X{public:int j;};
class B : public virtual X{public: double d;};
class C : public A, public B {public: int k;};
// 无法在编译时期决定(resolve)出pa->X::i的位置
void foo(const A* pa) {pa->i = 1024;}
// 可能的编译器转变操作 void foo (const A* pa) {pa->_vbcX->i = 1024;} _vbcX表示编译器所产生的指针,指向virtual base class X
main(){
foo(new A);
foo(new C);
}
原先cfront的做法是靠“在derived class object的每一个virtual base classes 中安插一个指针”完成。所有“经由reference或pointer来存取一个virtual base class”的操作都可以通过相关指针完成。vbcX(或编译器所做出的某个什么东西)是在class object 构造期间被完成的。对于class定义的每一个constructor,编译器会安插那些“允许每一个virtual base class的执行期存取操作”的代码。如果class没有声明任何constructors,编译器必须为它合成一个default constructor。