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