C++深入理解虛表

簡述虛表

虛表是記錄本類中所有虛函數地址的一個表格。

虛表的內存結構

如下,我們設計了一個類,存在兩個虛函數。

class A
{
public:
	virtual void fun1() {
		cout << "a" << endl;
	}

	virtual void fun2() {
		cout << "b" << endl;
	}
};

通過A實例化a,再看看a的內存結構。_vfptr就是虛表!!!可以把它看成存放了void*型對象的數組。 而_vfptr的兩個成員分別代表fun1函數的指針和fun2函數的指針。這也正好驗證了虛表就是記錄本類中所有虛函數地址的一個表格。
在這裏插入圖片描述

獲取虛表

經測試,通過如下代碼可以獲取虛表。

A a;
int *vptr = (int *)(*(int*)(&a));

再看內存監視信息,我們發現vptr的地址和_vfptr的地址一樣,測試通過-v-
在這裏插入圖片描述

通過虛表調用虛函數

看上圖,我們發現vptr[0]的值與虛表中第一個成員的值相等。那我麼可以通過吧vptr[0]轉換成fun1函數類型,來達到不可告人的目的。

typedef void (*Fun)(void);
Fun f1 = (Fun)vptr[0];
f1();

調用後,控制檯打印“a”,象徵着我們嘗試成功。同理,我們也可以通過vptr[1]來調用fun2函數。

虛表中虛函數的排列順序

通過上圖,我們很容易就可以看出,是按函數的聲明順序排列的。雖然簡單,但還是請記住了。不要一不小心調用了虛析構函數,程序直接崩潰。

繼承中覆蓋虛函數

我們再設計一個AA類,繼承A,並且在A中實現fun1()函數。

class AA : public A
{
public:
	virtual void fun1() {
		cout << "aa" << endl;
	}
};

通過AA實例化aa,再看看aa的內存結構。
在這裏插入圖片描述
發現沒有,AA::fun1。我們只實現了fun1(),虛表中的fun1函數使用的是AA類中的實現,而fun2函數使用的時A類中的實現。有沒有隱隱約約嗅到動態綁定的味道?

結語

看完本篇文章,是不是對神祕莫測的虛表有了更一步的認識。
這裏只是從內存層面講解了虛表,欲瞭解更加透徹,請查閱“繼承”,“動態綁定”等知識。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章