菱形繼承就是如下圖所示的繼承關系,其中A是B和C的父類,B和C是D的父類,由于C++允許多重繼承,所以便出現這種菱形繼承關系,是問題變得復雜,成員關系以及對象模型更為復雜。
在VS2015小端32位機器下面我們通過一個簡單的菱形繼承例子來研究一下這種繼承關系的對象模型和虛繼承的對象模型。
我們知道菱形繼承有兩個缺點,一個是二義性,所以在main函數中d1.B::_a=0;d1.C::_a=0;這兩行代碼的_a變量前都加了域作用限定符,以此來區分是繼承自哪一個類的變量。另一個缺點是數據冗余,這里我們通過內存來研究,在mian函數中編寫如下代碼:
int main(){ D d1; d1.B::_a = 0; d1.C::_a = 1; d1._b = 2; d1._c = 3; d1._d = 4;}通過逐行運行代碼觀察內存中的數據變化:

整理一下可以得到如下所示的抽象圖形(上方為高地址,下方為低地址): 
在這里我們可以看到這個簡單的菱形繼承關系的對象模型是如上圖所示的一個樣子,從圖中可以很直觀的看出菱形繼承的數據冗余這一缺點,在子類D中,由于是繼承B類和C類所來的,所以將B類和C類從A類繼承來的A類的成員—_a保存了兩份,很明顯這在很大程度上就造成了數據的多余,我們知道用virtual可以解決這一問題,那么使用virtual進行虛繼承它的底層到底是怎么實現的呢?讓我們通過內存來探究。
同樣的整理之后如下圖所示: 
觀察這個圖我們可以看到,虛繼承以后,編譯系統將D類對象中從最初的父類A中繼承來的成員放在了最下方,在繼承B類和C類時在B類和C類的成員之前添加了一個指針,通過追蹤這個指針我們發現這個指針所指向的值是當前位置到D類對象d1的成員_a的偏移量(20和12),所以,通過這樣的一個方式,就不用將_a兩次寫入內存,節省了空間,但是若要使用,則需要編譯系統通過指針去使用,這使得程序的效率變低。
新聞熱點
疑難解答