《深度探索C++對象模型》:構造函數

前言

C++新手一般的兩個常見誤解

        1、任何class,如果沒有定義default constructor,就會被合成出來一個;

        2、編譯器合成出來的default constructor會顯式設定“class內每個data member的默認值”。

        在沒有讀《深度探索C++對象模型》之前,我依然以爲第一點是成立的。直到看了這本書之後,才明瞭編譯器及其在需要的時候合成的default constructor函數在我們背後做了哪些事情。

        這裏“需要的時候”指的是以下四種情況:

               ①帶有default constructor的member class object;

               ②帶有default constructor的base class;

               ③帶有一個virtual function的class;

               ④帶有一個virtual base class的class。

        只有這四種情況,編譯器會爲未聲明constructor的class合成一個default constructor,這些被合成出來的constructor稱爲implicit nontrivial default constructor,它們只能滿足編譯器(而非程序)的需要;而沒有存在這四種情況而又沒有聲明任何constructor的class,稱這些class擁有的是implicit trivial default constructors,實際上它們並不會被合成出來。

下面就來分析這四種情況。

帶有default constructor的member class object

         如果一個class沒有任何constructor,但它內含一個member object,且該member object有default constructor,那麼編譯器就會爲這個class合成一個implicit nontrivial default constructor,這個合成操作只有在constructor真正需要被調用時纔會發生。

// Foo是一個含有default constructor的class
class Foo {
public:
    Foo();
    Foo(int);
    // ...
};

class Bar {
public:
    Foo foo;        // member object
    char* str;
    // ...
};

void foo_bar {
    Bar bar;        // Bar::foo必須在此處初始化
    if (str) {
        // ...
    }
}
        在上面的代碼中,定義一個Bar class object時,編譯器必須通過調用Foo的default constructor來初始化Bar的member object(foo),但是我們並沒有爲Bar提供default constructor,因此,編譯器會爲我們合成一個default constructor,被合成的default constructor看起來可能像這樣:
// 編譯器爲class Bar合成的default constructor僞代碼
inline Bar::Bar() {
    foo.Foo::foo();     //調用class Foo的default constructor
}
        注意,將Bar::foo初始化是編譯器的事,因此會合成default constructor,只滿足編譯器的需要,而將Bar::str初始化則是程序員的事

        現在讓我們以同樣的方式考慮另外一件事,就是當程序員爲class Bar提供了default constructor,編譯器該怎麼辦?如下:

// Foo是一個含有default constructor的class
class Foo {
public:
    Foo();
    Foo(int);
    // ...
};

// Bar也含有default constructor
class Bar {
public:
    Bar() {
        str = 0;
    }
    Foo foo;        // member object
    char* str;
    // ...
};
        上面的代碼中,我們自定義的default constructor並沒有初始化member object(foo),那麼它也就不能被初始化;然而由於class Bar已經存在了default constructor,自然編譯器也不可能爲我們去合成一個default constructor,因此foo的初始化依然不能解決。

        實際上,雖然編譯器不能合成default constructor,但爲了解決問題,它會在我們提供的default constructor中進行一些小動作。是的,沒錯,編譯器會我們的在default constructor中安插一些代碼。可能的改動如下:

// 改動後的default constructor僞代碼
Bar::Bar() {
    foo.Foo::foo();     //調用class Foo的default constructor
    str = 0;
}

        當然,同理:如果程序中自定義了constructor而沒定義default constructor,根據上述的分析,編譯器不會爲我們合成default constructor,而是對每一個自定義的constructor進行改動,改動方法類似。

        以上就是class不存在default constructor時,編譯器必須合成default constructor的第一種情況。

帶有default constructor的base class

        如果一個沒有任何constructor的class派生自一個帶有default constructor的base class,那麼編譯器會爲這個derivied class合成default constructor。因爲在定義一個derivied class object時,會先調用base class 的default constructor(當然,在destructor時順序是相反的)。

        當derivied class中定義了constructor或default constructor,編譯器不會合成default constructor,但會有必要的對每一個constructor進行改動,使其能夠調用到base class的default constructor。

        以上就是編譯器爲class合成default constructor的第二種情況。

        總結前兩種情況就是:只要滿足1、2的情況下,若class沒有任何constructor和default constructor,編譯器都會合成一個default constructor;若存在constructor或default constructor,則編譯器會進行改動安插代碼。


帶有一個virtual function的class

分兩種情況:

        1、class聲明或繼承了一個virtual function;

        2、class派生自一個繼承串鏈,其中有一個或多個virtual base classes。

        不管哪一種情況,由於virtual的存在,就會有vtbl(虛表)和vptr(指向vtbl的指針)的存在,在上一篇《深度探索C++對象模型》中講到vtbl和vptr的設定和重置在編譯器件由class的constructor、destructor和copy assignment運算符自動完成,但是由於沒有default constructor,因此編譯器會合成一個default constructor,以完成vtbl和vptr的構造和設定。

在編譯期間會發生(但不限於此):

        1、產生一個vtbl,內含class的virtual function地址;

        2、產生一個額外的指針vptr,指向class vtbl。

以上就是編譯器爲class合成default constructor的第三種情況。


帶有一個virtual base class的class

如下:

class X {
public:
    int i;
};

class A : public virtual X{
public:
    int j;
};

class B : public virtual X {
public:
    int d;
};

class C : public A, public B {
public:
    int k;
};
        class X是爲一個virtual base class,當然但凡是遇到virtual的class,都會存在有vtbl和vptr。因此必須要靠編譯器來合成default constructor,以完成一系列複雜的操作。

        以上就是編譯器爲class合成default constructor的第四種情況。


綜上所述

        在合成的default constructor中,只有base class object和member class object會被初始化,其他的所有的nonstatic data member(諸如int、int*、int[])等都不會被初始化,因爲這都不是編譯器必須的,而是由程序員負責。


參考資料

[1] 深度探索C++對象模型,[美]Stanley B. Lippman著,侯捷譯;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章