最近參加阿里巴巴公司的內推,面試官問了「虛函式表是在什麼時期建立的?」。因為以前對虛函式表的理解不夠多,所以就根據程式構建(build)的四個過程(預編譯、編譯、彙編和鏈結),推導出虛函式表應該是在編譯期確定的,原因如下:
1)預編譯器主要處理那些源**檔案中的以「#」開始的預編譯指令,如「#include」、「#define」。很明顯這個過程可以排除。
2)彙編器是將編譯器生成的彙編**轉變成機器可以執行的指令,每乙個彙編語句幾乎都對應一條機器指令。彙編過程相對於編譯期來說比較簡單,沒有複雜的語法,也沒有語義,也不需要做指令優化,只是根據彙編指令和機器指令的對照表一一翻譯就行了。所以,彙編期也是可以排除的。
3)鏈結器(現只考慮靜態鏈結)是將彙編器生成的目標檔案(和庫)鏈結成乙個可執行檔案,本質上做的是重定位(relocation)的工作,詳細可參考《程式設計師的自我修養》2.3、2.4節。很明顯鏈結期也是可以排除的。
4)編譯器要做的事情就比較多了,包括詞法分析、語法分析、語義分析及優化**等,是整個程式構建的核心。所以,排除了預編譯期、彙編期、鏈結期及考慮到編譯期所做的事情,虛函式表應該是在編譯期建立的。
上邊給出的答案還是有點不夠全面,因為忽略了動態鏈結。不過,我們在《深度探索c++物件模型》的4.2節能夠找到完美答案,具體摘抄如下:
「**中的virtual functions位址是如何被建構起來的?在c++中,virtual functions(可經由其class object被呼叫)可以在編譯時期獲知。此外,這一組位址是固定不變的,執行期不可能新增或替換之。由於程式執行時,**的大小和內容都不會改變,所以其建構和訪問皆可以由編譯器完全掌控,不需要執行期的任何介入。」
c++中的虛函式的作用主要是實現了多型機制,即父類類別的指標(或者引用)指向其子類的例項,然後通過父類的指標(或者引用)呼叫實際子類的成員函式。多型機制可以簡單地概括為「乙個介面,多種方法」。
虛函式是通過一張虛函式表(virtual table
)來實現的,簡稱為v-table
。在這個表中,主要是乙個類的虛函式的位址表,這張表解決了繼承、覆蓋的問題,保證其真實反應實際的函式。這樣,在有虛函式的類的例項中這個表被分配在了這個例項的記憶體中,所以,當我們用父類的指標來操作乙個子類的時候,這張虛函式表就顯得極為重要了,它就像乙個地圖一樣,指明了實際所應該呼叫的函式。
下邊我們通過乙個小程式來看看虛函式表到底是怎麼樣的?
1 #include 2對這個程式除錯,截圖如下:using
namespace
std;34
class
base58
};9class sub :public
base
1013
};14
15void
main()
16
由上圖可知,虛函式表_vfptr已經將父類base和子類sub的相同函式fn動態繫結到對應的類上,而且位址不一樣。
先看第乙個程式:
1 #include 2函式輸出如下:using
namespace
std;34
class
base58
};9class sub :public
base
1013
};14
15void test(base&b)
1619
20void
main()
21
sub物件sc在傳遞給test函式時,其另外新增的方法成員等(sub)會被截掉,只剩base部分,所以輸出是in base class而不是in sub class。
具體程式如下:
1 #include 2程式輸出如下:using
namespace
std;34
class
base58
};9class sub :public
base
1013
};14
15void test(base&b)
1619
20void
main()
21
這下程式就正確了。
其實還有另一種方法可以達成跟虛函式一樣的效果,不過這並不是一種好的做法:
1 #include 2具體輸出如下:using
namespace
std;34
class
base58
};9class sub :public
base
1013
};14
15void
main()
16
《深度探索c++物件模型》
《程式設計師的自我修養》
c 中的虛函式及虛函式表
有關鍵字virtual修飾的成員函式,為了實現多型。1.虛函式表用來存放虛函式的位址,也稱虛表。2.乙個含有虛函式的類中至少有乙個虛表。3.虛表指標 二級指標,虛函式指標的指標,存放在物件模型頭部,32位系統中佔4個位元組,在64位系統中佔8個位元組。虛表指標中存放著虛表的首位址。可以通過物件例項化...
C 虛函式表中的函式的索引
c 的虛函式表的結構在有析構函式和沒有析構函式的情況下,還是有一定的區別的,主要區別如下 首先是沒有析構函式的情況,如下 class test void moo virtual void goo virtual void hoo 當沒有虛析構函式的情況,函式的索引是從上往下,0,1,2逐步遞增的。當...
虛函式表和虛函式表的指標
有虛函式的類都有乙個虛函式表,它是實現多型的關鍵。虛函式表可以繼承,如果子類沒有重寫虛函式,那麼子類虛函式表中仍然會有該函式的位址,只不過這個位址指向的是基類的函式實現。如果子類重寫了相應的虛函式,那麼虛函式表中的位址就會改變,指向自身的函式實現。如果派生類中有自己的虛函式,那麼虛函式表中會新增該項...