先上**:
#include
using
namespace std;
class
base
;class
son1
:public base
;class
son2
:public base
;class
grandson
:public son1,
public son2
;int
main()
首先,說一下什麼是菱形繼承。如上**,兩個類son1和son2都繼承自base,之後又建立了乙個grandson類,其有兩個父類,都是繼承自base的son1和son2類。這就構成了菱形繼承。
注: son1和son2下面簡稱父類,base簡稱基類,grandson簡稱子類。
然後,出現了兩個問題
main函式中注釋行出現了錯誤,由於子類的兩個父類都繼承自base,所以直接寫 g.age=0; 會出現指向不明的錯誤,即二義性,因為兩個父類中都有age這個引數,編譯器不知道應該訪問哪乙個。
解決方法:
訪問屬性的時候新增作用域。如: g.son1::age = 20;
g.son2::age = 20; 。這樣編譯器就知道應該訪問哪乙個屬性了。
乙個子類物件中會出現兩個age屬性。但是,事實上乙個age屬性就已經足夠了。出翔=現兩個並沒有什麼實際用途,還會該訪問帶來麻煩,也浪費記憶體。
解決方法:
虛繼承。虛繼承就是在父類繼承基類的時候,在繼承方式前加上 virtual 關鍵字。這樣就可以解決該問題。程式如下:
#include
using
namespace std;
class
base
;class
son1
:virtual
public base
;class
son2
:virtual
public base
;class
grandson
:public son1,
public son2
;int
main()
此時在子類的例項化物件g中就只有乙個age屬性了,所以也就可以直接使用 g.age 進行訪問和修改了。執行結果如下:
由於僅有乙個age屬性,所以age的值是最後由最後的賦值語句 ==g.son2::age = 30;==賦予的30。
然後,再看一下占用記憶體的問題。
我們通過vs2015開發人員命令提示符來檢視一下grandson類的記憶體布局:
在此之前,可以在看一下虛繼承之前的記憶體布局:
接下來是虛繼承之後的:
可以看到,子類的兩個父類中並沒有age屬性,整個類中,有且僅有乙個來自base類的age屬性。在兩個父類中將各有乙個名為 vbptr 的東西代替了屬性age,其實這是乙個指標,叫做 虛基類指標 。
每個虛基類指標都給各自指向了 vbtable 虛基類表。這個虛基類表中儲存了虛繼承的屬性列表在記憶體中的偏移量。而偏移量的起始位置為物件的vbptr在記憶體中的位置。
將**修改為如下模式:
#include
using
namespace std;
class
base
;class
son1
:virtual
public base
;class
son2
:virtual
public base
;class
grandson
:public son1,
public son2
;int
main()
再次檢視記憶體布局結果如下:
得出如下結論:
只有虛繼承的屬性才會儲存在虛基類表中。因為son1類中的屬性a,不在虛基類表中。
由於虛基類表的存在,無論虛繼承了多少屬性,在每個父類中只佔4個位元組的記憶體,即乙個vbptr指標所佔的空間。
虛繼承之後虛繼承的屬性在記憶體中有且僅有乙份。
虛基類表並不占用類的記憶體.
c 委託(Delegates 基本概念及使用
在我這菜鳥理解上,委託就是可以用方法名呼叫另一方法的便捷方法,可以簡化switch等語句的重複。最近做專案的時候恰好需要用到委託,便來複習及學習委託的使用。嗯 本人以前並沒有用過,只是稍微知道而已。以下是整理的筆記。一.委託的概念 委託是使用者自定義的類,它定義了方法的型別。儲存的是一系列具有相同引...
匯流排的基本概念及分類
匯流排的基本概念及分類 my blog something 匯流排 連線多個部件的資訊傳輸線,是各部件共享的傳輸介質。在某一時刻,只允許有乙個部件向匯流排傳送資訊,而多個部件可以同時從匯流排上接受相同的資訊。匯流排是由許多傳輸線或通路組成,每條線都可以一位一位地傳輸二進位制 若干條傳輸線可以同時傳輸...
指標的基本概念及使用
指標 通過位址能找到所需的變數單元,因此說,位址指向該變數單元,將位址形象化的稱為指標 可理解為位址就是指標 指標的作用 通過指標間接訪問記憶體 指標變數的定義 型別名 指標變數名 乙個變數的指標的含義包含兩個方面,一是儲存單元的純位址,二是指向儲存單元的資料型別,故定義指標變數時要指定基型別,用來...