C++的多態(tài)真的是動態(tài)查找虛函數(shù)表嗎?
C++虛函數(shù)調(diào)用的真正過程
如標(biāo)題所言,在沒有看到我這篇文章分析之前,你可能一直以為C++中的動態(tài)多態(tài)實(shí)現(xiàn)的機(jī)制是在運(yùn)行時去動態(tài)的查找虛函數(shù)表的元素。
"運(yùn)行時動態(tài)的查找虛函數(shù)表的元素",這句話是否正確,要看你對這里的動態(tài)查找的定義了,如果你的意思是根據(jù)某個函數(shù)符號去動態(tài)到虛函數(shù)表中查找對應(yīng)的實(shí)現(xiàn)(類似于遍歷數(shù)組的形式),那么這個說法就是錯誤的,若動態(tài)只是指的去虛函數(shù)表(數(shù)組)中取值那么這就是正確的。
虛函數(shù)表的生成
廢話不多說,我們直接上源代碼和匯編一一對應(yīng):
查看匯編的網(wǎng)站:https://godbolt.org/
* 源代碼
* 匯編

介于很多人可能不太懂匯編,我就簡單描述下上述匯編對應(yīng)到C++函數(shù)中的行為,前面的main以及它之前的所有匯編都是具體的函數(shù)實(shí)現(xiàn)對應(yīng)二進(jìn)制的代碼段,而從 `vtable for Son` 開始,后面的都類似于C++的全局變量,對應(yīng)到二進(jìn)制的data段中。
虛函數(shù)的調(diào)用
我們發(fā)現(xiàn)匯編中生成的虛函數(shù)表數(shù)據(jù)都是固定的而且就是一個數(shù)組結(jié)構(gòu),那么具體會如何調(diào)用呢?是會動態(tài)掃描整個數(shù)組的數(shù)據(jù)嗎?我們通過基類指針來調(diào)用虛函數(shù)表的函數(shù)來試著生成匯編看看:
* 源代碼
* 匯編

上述匯編關(guān)于虛函數(shù)表的查找我都已經(jīng)標(biāo)紅并逐行對應(yīng)到編程語言的語言概念中了。
我們發(fā)現(xiàn)具體的虛函數(shù)調(diào)用并不會在運(yùn)行時去掃描虛函數(shù)表,而是已經(jīng)提前知道了你要調(diào)用的函數(shù)在虛函數(shù)表中的第幾個元素,然后直接取地址解引用調(diào)用即可。
不信你可以把 `b->test()` 的代碼換成調(diào)用 `b->test1()` 你會發(fā)現(xiàn)整個調(diào)用過程就多了一行算偏移值的指令,如下:

總結(jié)
經(jīng)過上述匯編的驗(yàn)證,所謂虛函數(shù)的動態(tài)調(diào)用,我們不如說是編譯器根據(jù)C++源代碼的動態(tài)生成。
這個動態(tài)生成有兩個方面:
1. 虛函數(shù)表:根據(jù)不同類型單獨(dú)生成對應(yīng)的虛函數(shù)表,具體的值符合符合多態(tài)的行為(子類重寫則把具體的值換掉)。
2. 虛函數(shù)調(diào)用:根據(jù)C++源代碼中調(diào)用函數(shù)的行為,識別到需要調(diào)用虛函數(shù)表中的第幾個元素,然后生成對應(yīng)的匯編去調(diào)用。
如果需要討論虛函數(shù)的引入是否會有較多的性能損失,那么我想說的是,只是多了三四條匯編指令的區(qū)別。
