C++單繼承、多繼承情況下的虛函數表分析


C++的三大特性之一的多態是基於虛函數實現的,而大部分編譯器是采用虛函數表來實現虛函數,虛函數表(VTAB)存在於可執行文件的只讀數據段中,指向VTAB的虛表指針(VPTR)是包含在類的每一個實例當中。當使用引用或指針調用虛函數時,首先通過VPTR找到VTAB,然后通過偏移量找到虛函數地址並調用。

 

本文參考:1.http://blog.lucode.net/programming-language/cpp-vtab-and-call-convention.html

  2.https://blog.csdn.net/tangaowen/article/details/5830803

  3.《深度探索C++對象模型》

一、單繼承

 1 #include<iostream>
 2 #include <stdio.h>
 3 using namespace std;
 4 class A {
 5 public:
 6     void func() {
 7         cout << "A::func()" << endl;
 8     }
 9     virtual void func1() {
10         cout << "A::func1(): " << endl;
11     }
12 
13     virtual void func3() {
14         cout << "A::func3(): " << endl;
15     }
16 };
17 
18 class B: public A {
19 public:
20     virtual void func() {
21         cout << "B::func()" << endl;
22     }
23     virtual void vfunc() {
24         cout << "B::vfunc()" << endl;
25     }
26     void func1() {
27         cout << "B::func1(): " << endl;
28     }
29 };
30 int main() {
31     typedef void (*Fun)(void);
32     B a;
33 
34     Fun *fun = NULL;
35     fun = (Fun*) ((int *) *(int *) &a);
36 //    fun = *(Fun **) &a;
37     fun[0]();
38     fun[1]();
39     fun[2]();
40     fun[3]();
41 
42     return 0;
43 }

運行結果:

B::func1():
A::func3():
B::func()
B::vfunc()

二、多重繼承

 1 #include<iostream>
 2 #include <stdio.h>
 3 using namespace std;
 4 class B1 {
 5 public:
 6     virtual void barB1() {cout << "B1::bar" << endl;}
 7     virtual void fooB1() {cout << "B1::foo" << endl;}
 8 };
 9 
10 class B2 {
11 public:
12     virtual void barB2() {cout << "B2::bar" << endl;}
13     virtual void fooB2() {cout << "B2::foo" << endl;}
14 };
15 
16 class D : public B1, B2 {
17 public:
18     void fooB1() {cout << "D::foo" << endl;}
19     void barB2() {cout << "D::bar" << endl;}
20 };
21 
22 typedef void (*Func)();
23 int main() {
24     D tt;
25     Func* vptr1 = *(Func**)&tt;
26     Func* vptr2 = *((Func**)&tt + 1);
27 
28     vptr1[0]();
29     vptr1[1]();
30     vptr1[2]();
31     cout<<"\\\\\\\\\\\\"<<endl;
32     vptr2[0]();
33     vptr2[1]();
34 
35     return 0;
36 }

運行結果:

B1::bar
D::foo
D::bar
\\\\\\
D::bar
B2::foo

結論:

     多重繼承會有多個虛函數表,幾重繼承,就會有幾個虛函數表。這些表按照派生的順序依次排列,如果子類改寫了父類的虛函數,那么就會用子類自己的虛函數覆蓋虛函數表的相應的位置,如果子類有新的虛函數,那么就添加到第一個虛函數表的末尾。

  

再簡單總結一下 覆蓋 隱藏 重載 的區別:

覆蓋 是C++虛函數的實現原理,基類的虛函數被子類重寫,要求函數參數列表相同;

隱藏 是C++的名字解析過程,分兩種情況,基類函數有virtual,參數列表不同,或基類函數沒有virtual,無論參數列表是否相同。此時基類指針指向基類實例則調用基類函數,指向子類則調用子類函數。

重載 是在同一命名空間中根據參數對同名函數的區別。

 

 

// 我的博客即將同步至騰訊雲+社區,邀請大家一同入駐:https://cloud.tencent.com/developer/support-plan?invite_code=3ph7kzdx2saoo


免責聲明!

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



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