1、c++實現多型的方法
其實很多人都知道,虛函式在c++中的實現機制就是用虛表和虛指標,但是具體是怎樣的呢?從more effecive c++其中一篇文章裡面可以知道:是每個類用了乙個虛表,每個類的物件用了乙個虛指標。具體的用法如下:
class a
;class b : public a
;//a,b的實現省略
因為a有virtual void f(),和g(),所以編譯器為a類準備了乙個虛表vtablea,內容如下:
a::f 的位址
a::g 的位址
b因為繼承了a,所以編譯器也為b準備了乙個虛表vtableb,內容如下:
a::f 的位址
b::g 的位址
然後某處有語句 b bb;的時候,編譯器分配空間時,除了a的int a,b的成員int b;以外,還分配了乙個虛指標vptr,指向b的虛表vtableb,bb的布局如下:
vptr : 指向b的虛表vtableb
int a: 繼承a的成員
int b: b成員
當如下語句的時候:
a *pa = &bb;
pa的結構就是a的布局(就是說用pa只能訪問的到bb物件的前兩項,訪問不到第三項int b)
那麼pa->g()中,編譯器知道的是,g是乙個宣告為virtual的成員函式,而且其入口位址放在**(無論是vtalbea表還是vtalbeb表)的第2項,那麼編譯器編譯這條語句的時候就如是轉換:call *(pa->vptr)[1](c語言的陣列索引從0開始哈~)。
另外要注意的是,如上的實現並不是唯一的,c++標準只要求用這種機制實現多型,至於虛指標vptr到底放在乙個物件布局的**,標準沒有要求,每個編譯器自己決定。我以上的結果是根據g++ 4.3.4經過反彙編分析出來的。
2、兩種多型實現機制及其優缺點
除了c++的這種多型的實現機制之外,還有另外一種實現機制,也是查表,不過是按名稱查表,是smalltalk等語言的實現機制。這兩種方法的優缺點如下:
(1)、按照絕對位置查表,這種方法由於編譯階段已經做好了索引和表項(如上面的call *(pa->vptr[1]) ),所以執行速度比較快;缺點是:當a的virtual成員比較多(比如1000個),而b重寫的成員比較少(比如2個),這種時候,b的vtableb的剩下的998個表項都是放a中的virtual成員函式的指標,如果這個派生體系比較大的時候,就浪費了很多的空間。
比如:gui庫,以mfc庫為例,mfc有很多類,都是乙個繼承體系;而且很多時候每個類只是1,2個成員函式需要在派生類重寫,如果用c++的虛函式機制,每個類有乙個虛表,每個表裡面有大量的重複,就會造成空間利用率不高。於是mfc的訊息對映機制不用虛函式,而用第二種方法來實現多型,那就是:
3、總結:
如果繼承體系的基類的virtual成員不多,而且在派生類要重寫的部分佔了其中的大多數時候,用c++的虛函式機制是比較好的;
但是如果繼承體系的基類的virtual成員很多,或者是繼承體系比較龐大的時候,而且派生類中需要重寫的部分比較少,那就用名稱查詢表,這樣效率會高一些,很多的gui庫都是這樣的,比如mfc,qt
ps. 其實,自從計算機出現之後,時間和空間就成了永恆的主題,因為兩者在98%的情況下都無法協調,此長彼消;這個就是電腦科學中的根本瓶頸之所在。軟體科學和演算法的發展,就看能不能突破這對時空權衡了。呵呵
何止電腦科學如此,整個宇宙又何嘗不是如此呢?最基本的宇宙之謎,還是時間和空間~
原文:
c 虛函式的實現機制 筆記
1 c 實現多型的方法 其實很多人都知道,虛函式在c 中的實現機制就是用虛表和虛指標,但是具體是怎樣的呢?從more effecive c 其中一篇文章裡面可以知道 是每個類用了乙個虛表,每個類的物件用了乙個虛指標。具體的用法如下 class a class b public a a,b 的實現省略...
c 虛函式的實現機制 筆記
1 c 實現多型的方法 其實很多人都知道,虛函式在c 中的實現機制就是用虛表和虛指標,但是具體是怎樣的呢?從more effecive c 其中一篇文章裡面可以知道 是每個類用了乙個虛表,每個類的物件用了乙個虛指標。具體的用法如下 class a class b public a a,b的實現省略 ...
c 虛函式的實現機制 筆記
1 c 實現多型的方法 其實很多人都知道,虛函式在c 中的實現機制就是用虛表和虛指標,但是具體是怎樣的呢?從more effecive c 其中一篇文章裡面可以知道 是每個類用了乙個虛表,每個類的物件用了乙個虛指標。具體的用法如下 class a class b public a a,b的實現省略 ...