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::g是重寫了的,所以b的虛表的g放的是b::g的入口位址,但是f是從上面的a繼承下來的,所以f的位址是a::f的入口位址。
然後某處有語句 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開始哈~)。
這一項放的是b::g()的入口位址,則就實現了多型。(注意bb的vptr指向的是b的虛表vtableb)
另外要注意的是,如上的實現並不是唯一的,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
C 虛函式實現 虛函式表 虛表指標
內聯函式 編譯時展開的,虛函式是為了實現多型,是動態行為,兩者是矛盾的。內聯函式展開就不存在了,也就不存在函式位址了,無法呼叫。但是,由於inline只是向編譯器建議,所以編譯器不會讓inline和virtual同時起作用,所以也不會報錯。建構函式 虛函式使用虛指標呼叫函式,呼叫建構函式之前沒有構造...
虛函式(虛函式表vtable 的實現
乙個動態的鏈結究竟怎樣實現的呢?首先,如果乙個基類中有虛函式,則編譯器自動生成乙個指向虛函式位址的表。有 幾個虛函式,對應的虛函式表 vftable 的長就是幾。然後,在基類中生成乙個指向虛函式表的指標。對應的每個虛函式由編譯器將其對映為虛函式表指標 vfptr 加上乙個數字 這個數 字就是真正的虛...
虛函式實現機制
c 中的虛函式的作用主要是實現了多型的機制。關於多型,簡而言之就是用父型別別的指標指向其子類的例項,然後通過父類的指標呼叫實際子類的成員函式。這種技術可以讓父類的指標有 多種形態 這是一種泛型技術。虛函式的作用是實現動態聯編,也就是在程式的執行階段動態地選擇合適的成員函式。當程式發現虛函式名前的關鍵...