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看起来可能像这样:bash
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:code
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对象
编译器这样的理由是:由于派生类被合成时须要显式调用基类的默认构造函数。blog
若是设计者提供多个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。
总结