【程式設計筆記】虛擬函式表
阿新 • • 發佈:2019-01-09
如果一個C++類中包含有虛擬函式,C++編譯器在進行編譯時,會通過動態聯編機制,為這個類生成一個“虛擬函式表”。
我們通常把所有的方法都是純虛擬函式的類,叫做:介面類。
class BasicTable
{
public:
virtual void Function1() = 0;
virtual void Function2() = 0;
};
對於這個介面類裡面方法的實現,需要編寫一個子類繼承於介面類:
class ChildTable : public BasicTable
{
public:
void Function1()
{
printf("Function1.\n" );
};
void Function2()
{
printf("Function2.\n");
};
};
我們在呼叫的時候,就會涉及到虛擬函式表:
int _tmain(int argc, _TCHAR* argv[])
{
BasicTable* pBT = new ChildTable;
pBT->Function1();
pBT->Function2();
delete pBT;
pBT = NULL;
return 0;
}
那麼指標pBT具體是怎麼呼叫到子類的Function方法,我們來追蹤下記憶體資訊:
指標地址為:0x00429800,我們看下指標這塊記憶體的資訊:
其實這塊記憶體的前4位位元組就是虛擬函式表的指標地址,這裡涉及到一個知識點:大端,小端的記憶體儲存位置:
Intel X86體系結構的CPU使用Little Endian(小位元組序)來儲存數字。例如有一個數字為0x11223344,那麼這個資料在記憶體中序列的順序是44 33 22 11,即記憶體的低地址存放這個數字的低位。與此相對應的,還有Big Endia(大位元組序),如PowerPC,所以數字0x11223344,在記憶體中的儲存的序列為 11 22 33 44。
所以這塊記憶體地址正確為:0x01245748,我們看下這塊記憶體的資訊:
我們在右邊的資訊,看到Function的方法,並且前4個位元組0x012410a0就是Function1的地址,後4個位元組0x01241140就是Function2的地址,我們確認下是不是:
通過Vs監視窗體可以詳細看到:
通過一遍的跟蹤,對虛擬函式表具體的資訊,有了叫深的理解,我們直接模擬虛擬函式表的呼叫,寫份程式碼:
class BasicTable
{
public:
virtual void Function1() = 0;
virtual void Function2() = 0;
};
class ChildTable : public BasicTable
{
public:
void Function1()
{
printf("Function1.\n");
};
void Function2()
{
printf("Function2.\n");
};
};
int _tmain(int argc, _TCHAR* argv[])
{
BasicTable* pBT = new ChildTable;
pBT->Function1();
pBT->Function2();
//!<獲取虛擬函式表指標
LPDWORD VtablePtr = (LPDWORD)(*((LPDWORD)pBT));
//!<拿到Function方法的指標
DWORD Fn_0 = *(VtablePtr + 0);
DWORD Fn_1 = *(VtablePtr + 1);
//!<函式型別宣告
typedef void(_stdcall* FUN)();
FUN _Fun1 = (FUN)Fn_0;
FUN _Fun2 = (FUN)Fn_1;
//!<直接呼叫
_Fun1();
_Fun2();
delete pBT;
pBT = NULL;
return 0;
}
輸出:
Function1.
Function2.
Function1.
Function2.
結論:含有虛擬函式的C++類,它的物件的this指標所指向的地址即是虛擬函式表指標的所在。