1. 程式人生 > >C++對象模型之默認構造函數

C++對象模型之默認構造函數

如何 bsp 根據 存在 def via 能夠 設計 AS

在不聲明自定義構造函數時,編譯器會自動生成一個默認構造函數。但是這個默認構造函數有可能是一個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++對象模型之默認構造函數