從 C++ 託管到 Visual C++,類構造函數的初始化順序已經發生變化。
構造函數初始化順序的比較
在 C++ 託管擴展中,構造函數的初始化是按以下順序進行的:
-
如果存在基類的構造函數則調用該構造函數。
-
計算該類的初始化列表。
-
執行類構造函數的代碼正文。
此執行順序與本機 C++ 編程遵守相同的約定。 新 Visual C++ 語言規定了 CLR 類的執行順序,如下所示:
-
計算該類的初始化列表。
-
如果存在基類的構造函數則調用該構造函數。
-
執行類構造函數的代碼正文。
上面的文字用代碼表示就是:
__gc class A { public: A() : _n(1) { } protected: int _n; }; __gc class B : public A { public: B() : _m(_n) { } private: int _m; }; 按照上述的構函數初始化順序,在構造類 B 的新實例,我們應看到如下的執行順序: 1.調用基類 A 的構造函數。 將 _n 成員初始化爲 1。 2.計算類 B 的初始化列表。 將 _m 成員初始化爲 1。 3.執行類 B 的代碼正文。 現在請考慮以新 Visual C++ 語法表示的相同代碼: ref class A { public: A() : _n(1) { } protected: int _n; }; ref class B : A { public: B() : _m(_n) { } private: int _m; }; 按照新語法構造的類 B 的新實例的執行順序如下: 1.計算類 B 的初始化列表。 將 _m 成員初始化爲 0(0 是 _m 類成員的未初始化值)。 2.調用基類 A 的構造函數。 將 _n 成員初始化爲 1。 3.執行類 B 的代碼正文。 請注意,對於這些代碼示例,相似的語法產生了不同的結果。 類 B 的構造函數依賴於基類 A 的值來初始化其成員。 但並未調用類 A 的構造函數。 如果需要分配內存或資源才能在基類構造函數中生成繼承類,則此種依賴關係將是非常危險的。
另外更爲詳細的構造順序如下:
C++構造函數按下列順序被調用:
(1)任何虛擬基類的構造函數按照它們被繼承的順序構造;
(2)任何非虛擬基類的構造函數按照它們被繼承的順序構造;
(3)任何成員對象的構造函數按照它們聲明的順序調用;
(4)類自己的構造函數。#include <iostream> using namespace std; class OBJ1 { public: OBJ1(){ cout <<"OBJ1\n"; } }; class OBJ2 { public: OBJ2(){ cout <<"OBJ2\n"; } }; class Base1 { public: Base1(){ cout <<"Base1\n"; } }; class Base2 { public: Base2(){ cout <<"Base2\n"; } }; class Base3 { public: Base3(){ cout <<"Base3\n"; } }; class Base4 { public: Base4(){ cout <<"Base4\n"; } }; class Derived :public Base1, virtual public Base2, public Base3, virtual public Base4 { public: Derived() :Base4(), Base3(), Base2(), Base1(), obj2(), obj1() { cout <<"Derived ok.\n"; } protected: OBJ1 obj1; OBJ2 obj2; }; int main() { Derived aa; cout <<"This is ok.\n"; int i; cin >> i; return 0; } 結果: Base2 Base4 Base1 Base3 OBJ1 OBJ2 Derived ok. This is ok.
分析以上輸出結果可以得出以下結論:在被繼承的基類中,虛基類優先級最高,其次是普通的基類,然後是本類的成員函數按照順序構造。在所有的虛基類中按照被繼承的順序對各個類中成員進行初始化。同樣在所有的普通基類中按照被繼承的順序對各個類中成員進行初始化。