//多重繼承的二義性以及解決方法 //學習目的:了解類的繼承原理及多重繼承二義性的解決方法。 /* //本程序代碼來源《MFC權威剖析》p68 */ ////////////////////第一種多重繼承的二義性//////////////// class Employee { public: char Name[40]; bool Sex; }; class Worker : public Employee { }; class Manager : public Employee { }; class Engineer : public Worker, public Manager { }; //Engineer的實例化對象內存中包含兩個Employee子對象,分別屬於Worker和Manager子對象, //這樣在該實例的內部就封裝了兩套員工個人檔案,顯然是錯誤的。 { Engineer eng; eng.Worker::Sex = true; eng.Manager::Sex = false;//eng的性別即使男又是女 } /////////////////////解決辦法//////////////////////// //在父類存在共同雙親的情況下,可以通過虛擬基類來解決。 //虛擬基類的作用是:如果在一個派生類的繼承結構中,存在兩個同樣的虛擬基類,那麼 //該類的實例化對象內存中只存在一個虛擬基類的子對象。 //事例 class Employee { public: char Name[40]; bool Sex; }; class Worker : public virtual Employee //Employee { }; class Manager : public virtual Employee //Employee { }; class Engineer : public Worker, public Manager { }; //這樣的繼承方式,虛擬基類的子對象不再包含在每個父類中,而是單獨存在。 //父類對象中存在一個指向其虛擬父類子對象的一個指針,該指針被稱為虛父類指針, //由編譯器生成。這樣就保證了在多重繼承時,子類實例中只存在一個虛擬基類的子對象。 //但同名的非虛擬基類的子對象沒有被合並。 /////////////////////第二種多重繼承的二義性//////////////// class ParentA { public: void fun() {printf("%d\n", m_Value); }; private: int m_Value; }; class ParentB { public: void fun() {printf("%d\n", m_Value); }; private: int m_Value; }; class Son : public ParentA, public ParentB { }; ///////////////////////解決辦法//////////////////////// //在父類沒有共同雙親的情況下,一般在子類中將名字沖突的成員重新定義, //以此解決二義性。 /////////////////////////////使用虛擬基類需要注意的問題//////////// [html] #include "stdio.h" class Base { public: Base(){printf("Base is constructed as default\n");} Base(int i) { m_iValue=i; printf("Base is constructed in constructing list\n"); } ~Base(){printf("Base is deconstructed\n");} int GetiValue()const { return m_iValue; } private: int m_iValue; }; class ParentA :public virtual Base { public: ParentA(){} ParentA(int i,float f):Base(i) { m_fValue=f; } float GetfValue()const { return m_fValue; } private: float m_fValue; }; class ParentB :public virtual Base { public: ParentB(){} ParentB(int i,char c):Base(i) { m_cValue=c; } char GetcValue()const { return m_cValue; } private: char m_cValue; }; class Son:public ParentA,public ParentB { public: Son(int i,float f,char c):ParentA(i,f),ParentB(i,c) { } void Output() { printf("son member is %d %8.2f %c\n", GetiValue(),GetfValue(),GetcValue()); } }; int main(int argc, char* argv[]) { Son son(6,92.33,'D'); son.Output(); return 0; } /*輸出 Base is constructed as default son member is -858993460 92.33 D ase is deconstructed */ //由程序輸出可以看出,虛擬基類只被構造一次,銷毀一次,所以只存在一個子對象。 //但Base基類被調用的是默認構造函數,ParentA和ParentB對虛擬基類的構造無效。 //其實,為了保證虛擬基類只被構造一次,虛擬基類的構造函數不能按照傳統的方式調用, //即不能被直接子類調用,而是由當前最底層次類的構造函數調用,該類被稱為最派生類。 //道理其實很簡單,和虛函數原理是一樣的。 //如果將Son的構造函數改成: Son(int i, float f, char c):ParentA(i, f),ParentB(i, c),Base(i){} /*則輸出 Base is constructed as default son member is 6 92.33 D ase is deconstructed */ //應用虛擬基類雖然解決了基類子對象重復問題,但還不能完全消除二義性的可能。 //如果兩個直接父類同時定義了同名的函數,則還會產生二義性。