昨天做題時候發現的問題
普通的繼承中,我們可以在當前類(C)建構函式的初始化表中指明如何去構造直接父類(B),然後在該父類(B)建構函式的初始化表中指明如何構造祖先類(A)。
範例程式碼
class A{public: A() {} A(int d):data(d) {}private: int data;};class B : public A{public: B() {} B(int x):A(x) {}};class C : public B{public: C() {} C(int x):B(x) {}};
這樣的寫法是正確的,在建立一個 C 類型的執行個體時,程式會根據其建構函式的選擇以及參數化表層層找到最上層的類,然後依次往下執行建構函式進行初始化。
但是在繼承鏈中如果存在虛繼承的話或許就不是這樣了。
程式中有四個類: animal、aqu_animal、amp_animal、test
其中類之間的繼承關係如下,除 test 以外其他繼承都採用虛繼承的方式。
範例程式碼
這裡還是按照原來的思路進行初始化的工作
#include <iostream>#include<stdio.h>using namespace std;class animal{protected: int height; int weight; char sex;public: animal() { cout<<"animal()"<<endl; } animal(int h,int w,char s): height(h),weight(w),sex(s) { cout<<"animal(int h,int w,char s)"<<endl; }};class aqu_animal:virtual public animal{protected: int swimming_speed;public: aqu_animal() { cout<<"aqu_animal()"<<endl; } aqu_animal(int h,int w,char s,int s_p): animal(h,w,s),swimming_speed(s_p) { cout<<"aqu_animal(int h,int w,char s,int s_p)"<<endl; }};class amp_animal:virtual public aqu_animal{public: amp_animal() { cout<<"amp_animal()"<<endl; } amp_animal(int h,int w,char s,int s_p,int r):aqu_animal(h,w,s,s_p),running_speed(r) { cout<<"amp_animal(int h,int w,char s,int s_p,int r)"<<endl; } void show() { cout<<"height:"<<height<<endl; cout<<"weight:"<<weight<<endl; cout<<"sex:"<<sex<<endl; cout<<"swimming_speed:"<<swimming_speed<<endl; cout<<"running_speed:"<<running_speed<<endl; }private: int running_speed;};class test:public amp_animal{public: test() {} test(int h,int w,char s,int s_p,int r):amp_animal(h,w,s,s_p,r) { cout<<"test(int h,int w,char s,int s_p,int r)"<<endl; cout<<"----------------"<<endl; amp_animal::show(); }};int main(){ test t(50,20,'m',100,120); return 0;}
執行結果
animal()aqu_animal()amp_animal(int h,int w,char s,int s_p,int r)test(int h,int w,char s,int s_p,int r)----------------height:4309616weight:7012244sex:swimming_speed:1978756045running_speed:120
可以看到,除了 running_speed 正常外其他的變數都沒有被正常賦值。
在函數的調用中,我們發現 test 類中的 test(int h,int w,char s,int s_p,int r) 正常執行了,並且它所附帶的初始化表中的建構函式同樣也被執行了( amp_animal(int h,int w,char s,int s_p,int r) )。
而在 amp_animal 類中利用 amp_animal(int h,int w,char s,int s_p,int r) 構造時的父類 aqu_animal 建構函式 aqu_animal(int h,int w,char s,int s_p) 並沒有被執行,同樣 animal 類中的 animal(int h,int w,char s) 也沒有被執行,實際上都是調用了它們預設的建構函式。
原因以及解決方案
在 C++ 中,如果繼承鏈上存在虛繼承的基類,則最底層的子類要負責完成該虛基類部分成員的構造。
即我們需要顯式調用虛基類的建構函式來完成初始化,如果不顯式調用,則編譯器會調用虛基類的預設建構函式,若虛基類中沒有定義的預設建構函式,則會編譯錯誤。
因為如果不這樣做,虛基類部分會在存在的多個繼承鏈上被多次初始化。
很多時候,對於繼承鏈上的中間類,我們也會在其建構函式中顯式調用虛基類的建構函式,因為一旦有人要建立這些中間類的對象,我們要保證它們能夠得到正確的初始化。
改法
#include <iostream>#include<stdio.h>using namespace std;class animal{protected: int height; int weight; char sex;public: animal() { cout<<"animal()"<<endl; } animal(int h,int w,char s): height(h),weight(w),sex(s) { cout<<"animal(int h,int w,char s)"<<endl; }};class aqu_animal:virtual public animal{protected: int swimming_speed;public: aqu_animal() { cout<<"aqu_animal()"<<endl; } aqu_animal(int h,int w,char s,int s_p): animal(h,w,s),swimming_speed(s_p) { cout<<"aqu_animal(int h,int w,char s,int s_p)"<<endl; }};class amp_animal:virtual public aqu_animal{public: amp_animal() { cout<<"amp_animal()"<<endl; } amp_animal(int h,int w,char s,int s_p,int r):aqu_animal(h,w,s,s_p),running_speed(r),animal(h,w,s) { cout<<"amp_animal(int h,int w,char s,int s_p,int r)"<<endl; } void show() { cout<<"height:"<<height<<endl; cout<<"weight:"<<weight<<endl; cout<<"sex:"<<sex<<endl; cout<<"swimming_speed:"<<swimming_speed<<endl; cout<<"running_speed:"<<running_speed<<endl; }private: int running_speed;};class test:public amp_animal{public: test() {} test(int h,int w,char s,int s_p,int r):amp_animal(h,w,s,s_p,r),aqu_animal(h,w,s,s_p),animal(h,w,s) { cout<<"test(int h,int w,char s,int s_p,int r)"<<endl; cout<<"----------------"<<endl; amp_animal::show(); }};int main(){ test t(50,20,'m',100,120); return 0;}
注意 test 的建構函式初始化表中我們顯式的調用了其間接父類的建構函式。
執行結果
animal(int h,int w,char s)aqu_animal(int h,int w,char s,int s_p)amp_animal(int h,int w,char s,int s_p,int r)test(int h,int w,char s,int s_p,int r)----------------height:50weight:20sex:mswimming_speed:100running_speed:120