C 虚函数表及多态内部原理详解
C 中的虛函數的作用主要是實現了多態的機制。關于多態,簡而言之就是用父類型別的指針指向其子類的實例,然后通過父類的指針調用實際子類的成員函數。這種技術可以讓父類的指針有“多種形態”,這是一種泛型技術。
虛函數表
每個含有虛函數的類都有一個虛函數表(Virtual Table)來實現的。簡稱為V-Table。C 的編譯器應該是保證虛函數表的指針存在于對象實例中最前面的位置(這是為了保證取到虛函數表的有最高的性能——如果有多層繼承或是多重繼承的情況下)。這意味著我們通過對象實例的地址得到這張虛函數表,然后就可以遍歷其中函數指針,并調用相應的函數。
1、 每一個類都有虛函數列表。
2、 虛表可以繼承,如果子類沒有重寫虛函數,那么子類虛表中仍然會有該函數的地址,只不過這個地址指向的是基類的虛函數實現。如果基類3個虛函數,那么基類的虛表中就有三項(虛函數地址),派生類也會有虛表,至少有三項,如果重寫了相應的虛函數,那么虛表中的地址就會改變,指向自身的虛函數實現。如果派生類有自己的虛函數,那么虛表中就會添加該項。
3、 派生類的虛表中虛函數地址的排列順序和基類的虛表中虛函數地址排列順序相同,子類獨有的虛函數放在后面。
當定義一個有虛函數類的對象時,對象的第一塊的內存空間就是一個指向虛函數列表的指針。
在這舉個例子
假設我們有這樣的一個類:
由于例程的操作環境是64位系統,所以用long*強轉。其中(long*)(&b)就是虛函數表地址,(long*)*(long*)(&b)就是第一個函數地址,代碼運行結果如下:
在程序中取出對象b的地址,根據對象的布局可以得出就是虛表的地址,根據這個地址可以把虛表的第一個內存單元的內容取出,然后強制轉換成一個函數指針,利用這個函數指針來訪問虛函數。又因為虛表是連續的,利用每次 1可以來訪問下一個內存單元。
如果對代碼不理解的話,可以看這幅圖就會懂了
注意:虛函數表在最后會有一個結束標志,為1說明還有虛表,為0表示沒有虛表了 。(編譯器不同,結束標志可能存在差異)
下面,將分別具體說明“無虛函數覆蓋”和“有虛函數覆蓋”時的虛函數表的情況。
(一)無虛函數覆蓋
沒有任何的繼承,虛函數表如下圖
根據示意圖,編寫的代碼如下圖所示:
和上一個程序一樣,根據取出虛表里面的地址強制轉換成函數指針,同樣,利用虛表的連續性,每次指針 1調用對應的虛函數。可以得出虛函數按照其聲明順序存放于虛函數表中的,子類自己的虛函數是排在父類虛函數之后的。運行結果如下圖
(二)一般繼承(有虛函數覆蓋)
如果子類中有虛函數重載了父類的虛函數,會是一個什么樣子?假設,我們有下面這樣的一個繼承關系。如圖所示:
在這個類的設計中,只覆蓋了父類的一個函數:f()。那么,對于派生類的實例,其虛函數表會是下面的一個樣子:
從表中可以看到下面幾點,
1)覆蓋的f()函數被放到了虛表中原來父類虛函數的位置。
2)沒有被覆蓋的函數依舊。
這樣就會出現虛調用
base *b = new Derive();
b->f();
由b所指的內存中的虛函數表的f()的位置已經被Derive::f()函數地址所取代,于是在實際調用發生時,是Derive::f()被調用了。這就實現了多態。下面我們用一個示例代碼來看一下
運行結果如下,確實如我們以上分析的那樣,由b所指的內存中的虛函數表的f()的位置已經被Derive::f()函數地址所取代:
(三)多重繼承(無虛函數覆蓋)
下面我們再看看多重繼承的情況
對于子類實例中的虛函數表,是下面這個樣子:
從圖上我們可以看到
1)每個父類都有自己的虛表。
2) 子類的成員函數被放到了第一個父類的表中。(所謂的第一個父類是按照聲明順序來判斷的)
這樣做就是為了解決不同的父類類型的指針指向同一個子類實例,而能夠調用到實際的函數。
下面我們根據上圖來實現一下
運行結果如下:
在這個程序中,子類有多個父類,因此從每個父類都繼承了一個虛表,因此會有3個虛表,根據代碼和運行結果會發現,排列的順序和繼承的順序一樣,子類自己的虛函數排在第一個虛表的后面。程序中沒有改寫虛函數 ,因此沒有覆蓋。同時主函數中應用的是一個二重指針,利用二維數組取每個虛函數地址。
(四)多重繼承(有虛函數覆蓋)
下面我們再來看看,如果發生虛函數覆蓋的情況。
下圖中,我們在子類中覆蓋了父類的f()函數。
子類虛函數列表如圖所示
三個父類虛函數表中的f()的位置被替換成了子類的函數指針。這樣,我們就可以任一靜態類型的父類來指向子類,并調用子類的f()了。
子類虛函數列表訪問代碼如下:
程序運行結果如下所示:
本程序中,子類重寫了f函數,把所有父類里面的f函數都屏蔽了。在虛表中父類f函數的位置全部換成了子類f函數的地址。因此在輸出時父類的f函數沒有了,全部是子類f函數的輸出。
總結
以上是生活随笔為你收集整理的C 虚函数表及多态内部原理详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux查看文件内容命令vim(lin
- 下一篇: 中国化妆品非特备案网站(化妆品非特备案网