今天小生學習了虛繼承,做了一下總結,不足之處請多多批評指正: 首先,菱形繼承有一個父類例如class AA,再有兩個子類分別繼承AA,如class BB:public AA、class CC:public AA,最后,將BB、CC作為父類繼承給子類DD,這就形成了菱形繼承。請看以下代碼: 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; }; AA類中包含一個int變量_aa,BB、CC分別繼承了AA,各自又都有一個int型變量,DD繼承了BB和CC,它自己又另外有一個自己的int型變量,請往下看: void Test1() { DD d; d._aa = 1; d._bb = 2; d._cc = 3; d._dd = 4; } 在Test1中定義了一個DD類型的d,分別給d._aa、d._bb、d._cc,d._dd賦值,運行代碼發現出現了以下錯誤:
對_aa的訪問不明確,為什么呢?是因為d中包含了一個BB類型和一個CC類型,而它們都繼承了AA類型,所以d中包含了兩個_aa,當我們直接用d._aa訪問時,編譯器不知道我們訪問的是哪一個,所以出錯,這既是菱形繼承的二義性,堅決這個問題的一種方法是指定類域,如下圖
代碼: 上圖代碼運行后得到右面的結果,大家可以看到,我們給_aa賦值時指定了給BB類型 中的_aa賦值,而CC中的_aa并不會發生變化。這種方法雖然解決了菱形繼承的二義性,確沒有解決代碼冗余的問題,下面給大家介紹另一種方法虛繼承,它能夠同時解決了這兩個問題。虛繼承中我們用到了關鍵字virtual,請看以下代碼: class BB:virtual public AA { public: int _bb; }; class CC:virtual public AA { public: int _cc; }; class DD:public BB,public CC { public: int _dd; }; BB與CC在繼承AA時在public AA前面加了virtual這個關鍵字,這樣我們再運行下面這段代碼時發現原來的錯誤沒有了。 void Test1()
} 這是為什么呢,因為加上virtual后我,DD型變量d在內存中存儲方式方式的變化,下圖為大家展示了二者的不同:
大家從內存中可以看到前后發生的變化,_aa只有一個了,所以解決了二義性和代碼冗余的問題,大家可能會覺得這里占用的內存反而用的多了,并且_bb、_CC上面都多了一個地址,那我們看看這兩個地址里面到底有什么:
我們可以看到_bb上面的這個地址里面存了一個十六進制數字14,_cc上面的地址里面存了一個十六進制的數字0c,這是什么呢,這其實是d中包含的BB、CC類型各自的地址到_a地址的偏移量,請看以下代碼: DD* pd = new DD; BB* pb = pd; 繼承有復制兼容性規則,而虛繼承也要滿足這個規則,如果沒有這兩個指針,上述代碼運行時_aa與_bb,不在一起,這里也就無法截斷賦值,賦值兼容性規則就不滿足了,而有了這兩個指針,我們這樣賦值時,通過這兩個指針找到了偏移量,加上偏移量就可以訪問到_aa,也就滿足了復制兼容性規則。
新聞熱點
疑難解答