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的訊息對映機制不用虛函式,而用第二種方法來實現多型,那就是:
(2)、按照函式名稱查表,這種方案可以避免如上的問題;但是由於要比較名稱,有時候要遍歷所有的繼承結構,
3、總結:
如果繼承體系的基類的virtual成員不多,而且在派生類要重寫的部分佔了其中的大多數時候,用c++的虛函式機制是比較好的;
但是如果繼承體系的基類的virtual成員很多,或者是繼承體系比較龐大的時候,而且派生類中需要重寫的部分比較少,那就用名稱查詢表,這樣效率會高一些,很多的gui庫都是這樣的,比如mfc,qt
ps. 其實,自從計算機出現之後,時間和空間就成了永恆的主題,因為兩者在98%的情況下都無法協調,此長彼消;這個就是電腦科學中的根本瓶頸之所在。軟體科學和演算法的發展,就看能不能突破這對時空權衡了。呵呵
何止電腦科學如此,整個宇宙又何嘗不是如此呢?最基本的宇宙之謎,還是時間和空間~
虛函式實現機制
c 中的虛函式的作用主要是實現了多型的機制。關於多型,簡而言之就是用父型別別的指標指向其子類的例項,然後通過父類的指標呼叫實際子類的成員函式。這種技術可以讓父類的指標有 多種形態 這是一種泛型技術。虛函式的作用是實現動態聯編,也就是在程式的執行階段動態地選擇合適的成員函式。當程式發現虛函式名前的關鍵...
虛函式實現機制
說到虛函式的實現方法,我們就不得不說到動態聯編 dynamic binding 和靜態聯編 static binding 靜態聯編意味著編譯器能夠直接將識別符號和儲存的實體地址聯絡在一起。每乙個函式都有乙個唯一的實體地址,當編譯器遇到乙個函式呼叫時,它將用乙個機械語言說明來替代函式呼叫,用來告訴cp...
虛函式的實現機制
將函式宣告為virtual時,在背後發生了什麼呢?編譯器在編譯的時候,發現animal類中有虛函式,此時編譯器會為每個包含虛函式的類建立乙個虛表 即vtable 該表是乙個一維陣列,在這個陣列中存放每個虛函式的位址。對於例1 2的程式,animal和fish類都包含了乙個虛函式breathe 因此編...