C++對象模型之默認構造函數
在不聲明自定義構造函數時,編譯器會自動生成一個默認構造函數。但是這個默認構造函數有可能是一個trivial(無用的) constructor,也可能是nontrivial constructor。
舉個例子
class Foo { public: int val; Foo* pnext; } void foo_bar() { Foo bar; if(bar.val || bar.pnext) //...do something }
之前的想法是Foo有一個默認構造函數,可以將var和pnext初始化為0。
其實不然。
原因是將兩個members初始化為0,並不是編譯器所需要的。也就是說,編譯器合成了一個默認構造函數是trivial constructor,不會對兩個members做初始化。
那麽什麽情況下,編譯器會合成nontrivial constructor?四種情況。
1、帶有Default Constructor的Member Class Object
類中的一個member object有default constructor。
class Foo { public: Foo(); Foo(int); } class Bar { public: Foo foo;char* str; }
當創建Bar對象時,需要調用Bar的默認構造函數。被合稱的默認構造函數需要能夠調用Class Foo的 的默認構造,處理Bar::foo。
但是它並不產生任何代碼初始化Bar::str。正如前面所說,初始化foo是編譯器的責任,初始化str是程序員的責任。
如果為了初始化str,我們定義自己的構造函數:
Bar::Bar() {str = 0;}
此時編譯器不會為我們合成默認構造函數,那麽是如何實現上面的初始化foo的工作呢?
原來編譯器會擴張已存在的constructors,在其中安插代碼,在user code之前,根據member objectsd的聲明次序,依次調用必要的default constructors。
類似這樣:
Bar::Bar() { foo.Foo::Foo(); str = 0; }
2、帶有Default Constructor的Base Class
一個沒有任何構造函數的類派生自一個帶有default constructor的父類,那麽這個派生類的默認構造函數是nontrivial的。 它將調用base class的default constructor。
和上一條類似,當設計者提供多個構造函數時,編譯器會擴張現有構造函數,在最開始調用base class constructor。
3、帶有一個Virtual Function的Class
a) class聲明或繼承一個virtual function。
b)class派生自一個繼承鏈,其中有一個或多個virtual base function。
下面兩個擴張操作會在編譯期間發生:
1、一個virtual function table。裏面保存class的virtual functions地址
2、每個class object中的vptr,保存的是class vtbl的地址。
4、帶有一個 Virtual Base Class的Class
C++對象模型之默認構造函數