class AA{public: int _aa;};class BB : public AA{public: int _bb;};class CC : public AA{public: int _cc;};class DD :public BB, public CC{public: int _dd;};int main(){ DD d; d.BB::_aa = 0; d._bb = 1; d.CC::_aa = 2; d._cc = 3; d._dd = 4; cout << sizeof(DD) << endl; system("pause"); return 0;}
通過內存我們可以看到菱形繼承的缺點是:1.二義性,2.數據的冗余

為了更加直觀我們畫圖看一下:

2.如何解決菱形繼承的缺點
二.虛繼承:
class AA{public: int _aa;};class BB : public AA{public: int _bb;};class CC : public AA{public: int _cc;};class DD :public BB, public CC{public: int _dd;};int main(){ DD d; d.BB::_aa = 0; d._bb = 1; d.CC::_aa = 2; d._cc = 3; d._dd = 4; cout << sizeof(DD) << endl; system("pause"); return 0;}
通過內存我們發現此時_aa的對象只有一個就是CC,這樣二義性就解決了,同時我們發現派生類BB中的_aa消失,這樣就解決了數據的冗余的問題。但是我們發現有多處兩組地址。通過查看oxo13bdd94和oxo13be308的地址我們可以看到一個偏移了第一個偏移20個字節剛好到基類AA,第二個偏移12個字節剛好到基類AA.
菱形的虛繼承我們可以通過圖形表示如下:


結果是24,按虛繼承的優點內存的大小應該是18,為什么會多出8個字節,根據前面我們知道,多出來的字節用于存放偏移地址。通過虛繼承我們發現雖然解決了菱形繼承的問題,但是一定程度上是空間換時間為代價的。三.虛函數&多態
class Shopping{public: void PRice() { cout << "不優惠" << endl; }};class OnLine :public Shopping{public: void Price() { cout << "優惠" << endl; }};void fun(Shopping &s){ s.Price();}int main(){ Shopping s; OnLine o; fun(s); fun(o); system("pause"); return 0;}
結果顯示“不優惠”調了兩次,按照兼容規則可知道父類的指針和引用可以指向子類的對象。1.虛函數:
類的成員函數前面加關鍵字virtual,那么這個成員函數就是虛函數。如:#include<iostream>using namespace std;class Shopping{public: virtual void Price() { cout << "不優惠" << endl; }};class OnLine :public Shopping{ virtual void Price() { cout << "優惠" << endl; }};void fun(Shopping &s){ s.Price();}int main(){ Shopping s; OnLine o; fun(s); fun(o); system("pause"); return 0;}
結果顯示“優惠”和“不優惠”虛函數的重寫:當子類的虛函數與父類相同時,子類的這個函數重寫了父類的虛函數。可以理解為把這個父類的虛函數覆蓋了。2.多態:
當基類的指針或引用調用的重寫的虛函數時,當指向父類調用的就是父類的虛函數,當指向子類調用的就是子類的虛函數。3.協變:
協變也是一種重寫,只不過父類和字類中的返回值不同。class Person{public: Person() :_id(1) {} virtual void Display() { cout << "_id(1)"<< endl; }protected: int _id; // 身份證號};class Student : public Person{public: Student() :_id(2) {} virtual void Display() { cout << "_id(2)" << endl; }protected: int _id; // 身份證號};int main(){ Student s; s.Display(); cout << sizeof(s) << endl; system("pause"); return 0;}
通過結果我們可以看到,協變其實就是重寫,基類函數的虛函數被子類的虛函數覆蓋了。id(1)只是被隱藏但仍然存在。查看監視窗口我們會發現基類下面除了內存外還存在一個虛函數表指針_vfptr指向虛函數表,用于存放函數地址
查看內存窗口我們可以看到原來多出的4個字節用于存放虛表指針的地址。

通過上面我們知道多態的原理實際就是虛函數的重寫,(協變例外)
新聞熱點
疑難解答