C++多態是其作爲面嚮對象語言相當重要的屬性,對於其理解一直以來相對較模糊。結合gdb工具對C++多態理解分析是一個相當不錯的選擇。上一篇博客(https://blog.csdn.net/ddazz0621/article/details/95459993)在計算類大小時提及到了類中虛指針及虛函數表的結構,但並沒有相對較爲充足的依據,下來藉助gdb工具來分析一下C++中虛指針及虛函數表的詳細結構(以下代碼運行環境均爲Linux下的64位操作系統)。
無繼承的單類
案例代碼如下:
#include <iostream>
using namespace std;
class A{
public:
virtual void Fun1(){}
virtual void Fun2(){}
virtual void Fun3(){}
};
int main(){
A a;
return 0;
}
gdb調試結構結果及分析如下:
此時對象a內存佈局結構如下:
單繼承無覆蓋的類
案例代碼如下:
#include <iostream>
using namespace std;
class A{
public:
virtual void Fun_A1(){}
virtual void Fun_A2(){}
virtual void Fun_A3(){}
};
class B : public A{
virtual void Fun_B1(){}
virtual void Fun_B2(){}
virtual void Fun_B3(){}
};
int main(){
B b;
return 0;
}
gdb調試結構結果及分析如下:
此時對象b內存佈局結構如下:
單繼承有覆蓋的類
案例代碼如下:
#include <iostream>
using namespace std;
class A{
public:
virtual void Fun_A1(){}
virtual void Fun_A2(){}
virtual void Fun_A3(){}
};
class B : public A{
void Fun_A1(){}
virtual void Fun_B2(){}
virtual void Fun_B3(){}
};
int main(){
B b;
return 0;
}
gdb調試結構結果及分析如下:
此時對象b內存佈局結構如下:
多繼承無覆蓋的類
案例代碼如下:
#include <iostream>
using namespace std;
class A{
public:
virtual void Fun_A1(){}
virtual void Fun_A2(){}
virtual void Fun_A3(){}
};
class B{
virtual void Fun_B1(){}
virtual void Fun_B2(){}
virtual void Fun_B3(){}
};
class C : public A, public B{
virtual void Fun_C1(){}
virtual void Fun_C2(){}
virtual void Fun_C3(){}
};
int main(){
C c;
return 0;
}
gdb調試結構結果及分析如下:
此時對象c內存佈局結構如下:
多繼承有覆蓋的類
案例代碼如下:
#include <iostream>
using namespace std;
class A{
public:
virtual void Fun_A1(){}
virtual void Fun_A2(){}
virtual void Fun_A3(){}
};
class B{
virtual void Fun_B1(){}
virtual void Fun_B2(){}
virtual void Fun_B3(){}
};
class C : public A, public B{
void Fun_A1(){}
void Fun_B2(){}
void Fun_A3(){}
};
int main(){
C c;
return 0;
}
gdb調試結構結果及分析如下:
此時對象c內存佈局結構如下:
結論: 帶有虛函數的類對象中都存在一個虛指針指向對應對象類的虛函數表。派生類對象自己的虛指針與其繼承順序的第一個基類的虛函數表指針合併。此外若派生類重寫了第一個基類中同名的虛函數,則在對應虛函數表的對應位置予以修改,若重新了其它非第一個基類中同名的虛函數,則將其添加至第一個虛函數表的最後面,包括派生類自己的定義虛函數也如此,並將其它非第一個基類中同名的虛函數致爲無效函數。