這里先給出結論,在貼出代碼與執行結果~
一個派生類構造函數的執行順序如下:
第一步執行:虛擬基類的構造函數(多個虛擬基類則按照繼承的順序執行構造函數)。
第二步執行:基類的構造函數(多個普通基類也按照繼承的順序執行構造函數)。
第三步執行:類類型的成員對象的構造函數(按照初始化順序)。
第四部執行:派生類自己的構造函數。
如果一個派生類不僅繼承於一個基類,而且還有這個基類的成員對象,那么會進行兩次構造函數的執行(一個用於初始化派生類中基類部分的內部成員,另一個是初始化派生類的基類類型成員變量的內部成員),詳細看派生類Son2的執行結果。
你
下面聲明了A,B,C,D,Object1,Object2留個基類以及Son1,Son2,Son3,Son4,Son5(Son5是一個錯誤的例子,編譯不能通過)
每個基類都有兩個構造函數,默認的構造函數不接受參數,輸出的Default+字符串
帶參數的輸出的自己類的特有信息。
(為了方便觀看,我在后面會再貼一下結論)
參考Son1:可以驗證上述派生類構造函數的執行順序;
參考Son2:可以驗證構造函數是嚴格照上面所說的順序執行,與初始化的順序無關。同時,如果不顯示的執行基類構造函數的初始化,就會按照順序調用默認的構造函數。
參考Son3:可以說明繼承下執行的構造函數與類類型的成員變量的構造函數是占用兩個不同的內存地址空間,二者不會相互影響。
參考Son4:可以說明,無論是否顯示的初始化類類型的成員變量,都會按照成員變量在類中的聲明順序執行構造函數。
參考Son5:這個解決了我之前的疑問,如果在派生類的構造函數中初始化類類型的成員對象會怎么樣,發現這樣是不可取的,因為在類的聲明中是不可以實際分配內存的,但是可以聲明。
(關於Son5的理解需要進一步闡明:這里可能涉及到了C++內存分配,拷貝賦值的原理,如果不是很懂的話這里我們暫且按我說的方式去理解。)
classA { public: A(intnum){Show();} A(){cout<<"Defaultaaa"<<endl;} voidShow(){cout<<"aaa"<<endl;} }; classB { public: B(intnum){Show();} B(){cout<<"Defaultbbb"<<endl;} voidShow(){cout<<"bbb"<<endl;} }; classC { public: C(intnum){Show();} C(){cout<<"Defaultccc"<<endl;} voidShow(){cout<<"ccc"<<endl;} }; classD { public: D(intnum){Show();} D(){cout<<"Defaultddd"<<endl;} voidShow(){cout<<"ddd"<<endl;} }; classObject1 { public: Object1(intnum){Show();} Object1(){cout<<"DefaultObject1"<<endl;} voidShow(){cout<<"Object1"<<endl;} }; classObject2 { public: Object2(intnum){Show();} Object2(){cout<<"DefaultObject2"<<endl;} voidShow(){cout<<"Object2"<<endl;} }; classSon1:publicA,virtualpublicB,publicC,virtualpublicD { public: Son1():A(1),B(1),C(1),D(1),ob1(1),ob2(1){ cout<<"son1"<<endl; } Object1ob1; Object2ob2; }; classSon2:publicA,virtualpublicB,publicC,virtualpublicD { public: Son2():C(1),A(1),ob1(1),ob2(1){ //結果仍然是先執行A的構造函數,其次是C的,證明與初始 cout<<"son2"<<endl; //化的順序無關 } Object1ob1; Object2ob2; }; classSon3:publicA,virtualpublicB,publicC,virtualpublicD,virtualpublicObject1,publicObject2 { public: Son3():ob1(1),ob2(1){ cout<<"son3"<<endl; } Object1ob1; Object2ob2; }; classSon4:publicA,virtualpublicB,publicC,virtualpublicD,virtualpublicObject1,publicObject2 { public: Son4():ob2(1){ //注意,如果Object1沒有默認構造函數,這里將無法編譯通過 cout<<"son4"<<endl; } Object1ob1; Object2ob2; }; //class Son5:publicA,virtual public B,publicC,virtual public D //{ //public: // Son5():A(1),B(1),C(1),D(1){ // cout<<"son5"<<endl; // ob2=ob1; //這里編譯不通過,沒有與這些操作數匹配的”=”運算符(二者類型不同,需要重新定義‘=’) // ob1(1); // ob2(2); //這里編譯不通過,在沒有適當的operate()的情況下調用類類型對象 // } // Object1 ob1(1); // 這里編譯不通過,因為類的成員聲明不需要分配內存,這樣寫就相當於執行 //構造函數並分配內存了 // Object2 ob2; //}; int_tmain(intargc, _TCHAR* argv[]) { cout<<"------------SON1------------"<<endl; Son1son1; cout<<"------------SON2------------"<<endl; Son2son2; cout<<"------------SON3------------"<<endl; Son3son3; cout<<"------------SON4------------"<<endl; Son4 son4; Object1obj; //son4.ob1(1); //這句話是錯誤的編譯不通過,在沒有適當的operate()的情況下調用類類型對象 son4.ob1=obj; system("pause"); //這句只是為了讓cmd停留顯示,以免閃退(VS控制台程序需要) return 0; }
以上代碼是在VS2012ConsoleApplication控制台下編譯測試的,結果如下:
再貼一遍:
參考Son1:可以驗證一開始介紹的派生類構造函數的執行順序;
參考Son2:可以驗證構造函數是嚴格照上面所說的順序執行,與初始化的順序無關(盡管表面上C比A初始化的要早)。同時,如果不顯示的執行基類構造函數的初始化,就會按照順序調用默認的構造函數。
參考Son3:可以說明繼承下執行的構造函數與類類型的成員變量的構造函數是占用兩個不同的內存地址空間,二者不會相互影響。
參考Son4:可以說明,無論是否顯示的初始化類類型的成員變量,都會按照成員變量在類中的聲明順序執行構造函數。
參考Son5:這個解決了我之前的疑問,如果在派生類的構造函數中初始化類類型的成員對象會怎么樣,發現這樣是不可取的,因為在類的聲明中是不可以實際分配內存的,但是可以聲明。