C++ 虛表虛函數怎么就實現了多態?


虛表vftable,編譯器為每個擁有虛函數的類都建有一張虛函數表,里面存有虛函數的入口指針(地址)。在類對象的內存布局中,先是一個vfptr虛表指針,指向虛表首地址,而后通過偏移量的形式來訪問虛表中的地址。

看許多文章都在那里侃侃而談,然能實際展示類(對象)內存布局者寥寥,不可見內里實現的終究是借他人文字的空想。now, 我們來一窺究竟!


 

PS:如何利用VS查看類內存布局見文末鏈接

1. 帶虛函數類內存布局

2. 發生單繼承時,派生類內存布局,先是復制一份基類內存布局,然后是自己的布局(注意內存對齊)。虛表指針指向自己的虛表,派生類虛函數地址如果自己未覆蓋,那么就是基類的,否則是自己的函數地址。

3. 發生多繼承時:先按照繼承順序,從左到右排布基類的布局包括虛標指針,然后排布自己的指針和數據;派生類虛表排布形式是按照繼承順序,是繼承來的虛函數,如果有覆蓋則換成自己的函數地址;然后是下一個基類,直至基類排布完畢。繼承來的多張表是獨立的(從內存布局中的多個虛表指針可以看出),且使用首地址+偏移量的形式來訪問。

4. 發生虛繼承時:無論是對象內存排布還是虛表,虛基類的部分都被放到最后排布,且如果派生類有自己的虛函數則會加在第一個基類的虛表末尾。

除vfptr和vftable之外,增加了vbtable 虛基類表(存放繼承的虛基類的地址)和 vbptr(指向虛基類表的指針)

 

測試代碼

 1 #include <iostream>
 2 class Base
 3 {
 4     int a;
 5     int b;
 6 public:
 7     virtual void foo() {};
 8     virtual void bar() {};
 9     virtual void bar2(){};
10     virtual void bar3(){};
11 };
12 
13 class Base2
14 {
15     int d;
16 public:
17     virtual void foo() {};
18 };
19 
20 class Derived : virtual public Base, public Base2
21 {
22     int c;
23 public:
24     //void foo() {};
25     void bar() override {};
26     virtual void bar4() {};
27 };
28 
29 int main()
30 {
31     Base b;
32     Derived d;
33     return 0;
34 }
測試代碼

多態原理?個人理解

1. C++多態動態建立在虛函數上,使用virtual關鍵字指明不要在編譯器綁定函數地址,而是在運行時訪問虛函數表,即動態綁定。

2. 使用基類指針指向派生類對象且調用虛函數時(前提是派生類覆蓋了基類虛函數),運行時該指針指向的地址是派生類對象地址,在派生類對象地址頭就是指向虛函數表的vfptr,這張虛表自然是派生類的,而在構造虛表的過程中該虛函數早就被派生類自己的函數地址所覆蓋,所以調用函數自然是派生類的函數。

3. 多態的含義:基類指針可以指向基類對象以及不同派生類對象,實現了一種寫法,多種訪問方式的效果,稱為多態。


 【參考資料】

如何利用VS查看類內存排布:https://www.cnblogs.com/jerry19880126/p/3616999.html

(在VS項目右鍵屬性->C/C++->命令行->添加 /d1 reportAllClassLayout  應用即可,注意debug/release還有平台類型)

C++多態以及虛函數的不錯文章:https://www.cnblogs.com/cxq0017/p/6074247.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM