C++ 虛擬函式表 存在哪
阿新 • • 發佈:2019-01-08
C++通過虛擬函式實現多型。那麼虛擬函式表具體儲存在哪?是每一個物件都有虛擬函式表,還是每一類有虛擬函式表?讓我們通過程式碼分析一下。程式碼執行在Windows平臺,使用Visual Studio2010編譯。
虛擬函式基礎知識
C++中,一個類存在虛擬函式,那麼編譯器就會為這個類生成一個虛擬函式表,在虛擬函式表裡存放的是這個類所有虛擬函式的地址。當生成類物件的時候,編譯器會自動的將類物件的前四個位元組設定為虛表的地址,而這四個位元組就可以看作是一個指向虛擬函式表的指標。虛擬函式表可以看做一個函式指標陣列。
程式碼分析
class Base
{
public:
virtual void Hello()
{
cout << "Base Hello" << endl;
}
};
class Derived:public Base
{
public:
virtual void Hello()
{
cout << "Derived Hello" << endl;
}
};
int main()
{
//獲取程序基址
HANDLE hBase = GetModuleHandle(NULL);
//基類
Base* base = new Base();
//獲取虛擬函式表地址偏移
DWORD baseVirtualTable = 0;
memcpy(&baseVirtualTable, base,sizeof(DWORD));
baseVirtualTable -= (DWORD)hBase;
printf("base VirtualTable offset is 0x%08X\n", baseVirtualTable);
//派生類
Derived* derived= new Derived();
//獲取虛擬函式表地址偏移
DWORD derivedVirtualTable = 0 ;
memcpy(&derivedVirtualTable, derived,sizeof(DWORD));
derivedVirtualTable -= (DWORD)hBase;
printf("derived VirtualTable is 0x%08X\n",derivedVirtualTable);
//基類指標指向子類物件
Base* pBaseToDerived = new Derived();
//獲取虛擬函式表地址偏移
DWORD pBaseToDerivedVirtualTable = 0;
memcpy(&pBaseToDerivedVirtualTable, pBaseToDerived,sizeof(DWORD));
pBaseToDerivedVirtualTable -= (DWORD)hBase;
printf("pBaseToDerived VirtualTable is 0x%08X\n",pBaseToDerivedVirtualTable);
}
程式碼分別打印出,基類物件,子類物件,以及指向子類的基類指標的虛擬函式表相對於程序基址的偏移,結果如下圖
這裡可以看出,虛擬函式表是屬於類,類的所有物件共享這個類的虛擬函式表。並且,子類物件與指向子類的基類指標指向的物件,使用同一個虛擬函式表,符合C++的多型要求。
隨後,使用PE工具,開啟程式碼生成的exe檔案,各個Section的偏移地址如下圖
剛才虛擬函式表的相對偏移地址為0x000183E8和0x00017834,屬於.rdata段。由此可見,虛擬函式表儲存在程序的只讀資料段。
結論
- 虛擬函式表屬於類,類的所有物件共享這個類的虛擬函式表。
- 虛擬函式表由編譯器在編譯時生成,儲存在.rdata只讀資料段。