首页 > 代码库 > Default Constructor的构建操作
Default Constructor的构建操作
class Foo {public: int val; Foo *pnext;};void f00_bar(){ Foo bar; //程序要求bar‘s members都被清为零,不是编译器需要 if(bar.val || bar.pnext) //...do something}
编译器并不会为上述代码生成一个默认的构造函数,只有在下面四种情况下,编译器才会生成默认构造函数(nontrivial default constructor):
(1) 如果一个类里面某个成员对象有nontrivial default constructor,编译器就会为我们的类产生nontrivial default constructor
class Foo { public: Foo(); Foo(int); ...};class Bar {public: Foo foo; char *str;};void foo_bar(){ Bar bar; if(str) {} ...}
被合成的Bar default constructor 内含必要的代码,能够调用class Foo的default constructor 来处理member object Bar::foo,但它并不产生任何代码来初始化Bar::str。被合成的default constructor看起来可能像这样:
inline Bar::Bar(){ //C++伪代码 foo.Foo::Foo();}
如果程序员定义了default constrcutor
Bar::Bar() { str = 0;}
现在程序员的需求满足了,但是编译器还需要初始化member object foo。编译器会扩张现存的constructor,在其中安插一些代码,使得user code在被执行前,先调用必要的default constructors。
Bar::Bar(){ foo.Foo::Foo(); //附加上的compiler code str = 0;}
如果有多个class member objects都要求constructor初始化操作,C++语言要求以 “member objects在clsss中的声明次序”来调用各个constructor。
class Dopey {public: Dopey(); ...};class Sneezy {public: Sneezy(int); Sneezy(); ...};class Bashful {public: Bashful(); ...};class Snow_White{public: Dopey dopey; Sneezy Sneezy; Bashful bashful; ....private: int mumbae;};
如果Snow_White没有定义default constructor,就会有一个nontrivial constructor被合成出来,一次调用Dopey、Sneezy、Bashful的default constructor。如果定义了下面这样的default constructor:
Snow_White::Snow_White() : sneezy(1024){ mumble = 2048;}他会被扩展为:Snow_White::Snow_White(){ //按照声明的顺序调用其constructor dopey.Dopey::Dopey(); sneezy.Sneezy::Sneezy(); bashful.Bashful::Bashful(); mumble = 2048;}
(2) 如果一个派生类的基类有nontrivial default constructor,那么编译器会为派生类合成一个nontrivial default constructor
编译器这样的理由是:因为派生类被合成时需要显式调用基类的默认构造函数。
如果设计者提供多个constructor,但其中都没有调用default constructor,编译器不会合成一个新的default constructor,它会扩张现有的每一个constructor,将“用以调用所有必要之default constructor”的程序代码加进去。
(3) 如果一个类带有一个Virtual Function,那么编译器会为派生类合成一个nontrivial default constructor
编译器这样做的理由和(3)类似:因为虚继承需要维护一个类似指针一样,可以动态的决定内存地址的东西(不同编译器对虚继承的实现不全相同)。
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; };//无法在编译时期决定出pa->X::i 的位置void foo(const A* pa) { pa->i = 1024; }mian(){ foo(new A); foo(new C); ...}
编译器无法固定住foo()之中“经由pa而存取的X::i”的实际偏移量,以为pa的真正类型可以改变。foo()可以被改写如下:
void foo(const A* pa) { pa->_vbcX->i = 1024; }
其中,_vbcX表示编译器所产生的指针,指向virtual base class X。
总结
Default Constructor的构建操作