虛函式表裡邊儲存的不一定是虛函式的位址
我一直以為虛函式表裡邊儲存的就是虛函式的位址,前幾天做測試的時候才發現這想法不一定是對的。
測試**:
分析一、反彙編分析通過測試結果可以發現虛函式表裡邊儲存的可能並非虛函式的位址,但是肯定跟虛函式有一點關聯,因為最後通過虛函式表裡的表項成功的呼叫了虛函式。通過反彙編分析,結果表明derived類的第二張虛函式表裡邊儲存的跟derived::show()函式相關的表項,並非該函式的位址。分析過程如下://虛函式表裡邊儲存的不一定是虛函式的位址.cpp
/*分析:通過最後的輸出結果可以發現,通過derived類的虛函式表呼叫所有的虛函式,
發現第一張虛函式表的輸出①和第二張虛函式表的輸出④它們是同乙個函式的輸出,
在虛函式表項上的值卻是不同的。
如果虛函式表上的項的值都是虛函式的位址,那麼derived的兩張表裡邊用於呼叫show()函式的表項的值應該是相同的,但事實上它們不同。
這說明,虛函式表裡邊儲存的未必就是虛函式的位址。
這種情況在之前一直沒有遇到過(或者沒注意到),那麼那兩個不同的值哪乙個才是derived::show()函式的位址呢?
反彙編分析。。
//code::blocks vs2005/2008
*/#include using namespace std;
class basea
virtual void showaa() };
class baseb
virtual void showbb() };
class derived : public basea, public baseb
virtual void showd() };
int main()
/*-----basea類的物件-----
①basea: 00401320 basea::show()
②basea: 00401350 basea::showaa()
-----baseb類的物件-----
①baseb: 004013a0 baseb::show()
②baseb: 004013d0 baseb::showbb()
-----derived類的物件-----
①derived: 00401440 derived::show()
②derived: 00401350 basea::showaa()
③derived: 00401470 derived::showd()
④derived: 00405430 derived::show()
⑤derived: 004013d0 baseb::showbb()
*/
圖 1 通過第一張虛函式表呼叫show()函式
call edx 按f7跟進之後見下圖:
圖 2 derived::show()函式
圖 3通過第二張虛函式表呼叫show()函式
call edx 按f7跟進之後見下圖:
圖 4 跳轉
可以發現,跳轉的目標位址正是derived::show()函式。
二、不直接儲存函式位址的原因
現在已經明白,虛函式表裡邊儲存的是什麼東西了。還有另外乙個問題,為什麼第二張虛函式表裡邊不儲存derived::show()函式的位址,偏偏要儲存跳轉的位址,然後再跳過去,這樣子有什麼用途?
這個跟類的成員函式呼叫會傳遞this指標有關。
假如有這樣的語句:
baseb* pb = &dobj;
pb->show();
如果沒有中間的跳轉,直接就去呼叫show()函式,那麼傳遞的this指標是derived物件中baseb類例項的位址,也就是第二張虛函式表位址。這樣的話,如果要訪問dobj中的成員變數,通過這個this指標訪問就會出錯。可能derived::show()會認為傳遞進來的是derived物件的basea例項的位址。所以就需要圖4中的**,第二張虛函式表的表項儲存的是那個sub的位址。在跳轉之前先ecx減去4,在例子中可以發現,減去4使得this指標指向了dobj的位址(就是basea例項的位址)。
也就是說,之所以第二張虛函式表裡邊儲存的不是函式位址,是為了保證this指標是正確。
原因猜測:可以通過a類指標去呼叫d::show(),也可以通過b類指標去呼叫d::show(),如果this指標不加調整,d::show()要訪問成員變數的時候是this+偏移值來定址的,這樣就會有錯誤。所以必須調整。
三、這種情況什麼時候出現
另乙個問題, 什麼時候虛函式表裡邊儲存的不是函式位址?
如果要全面測試的話,那實在是件費力的事,所以猜測可能是這種情況:
派生類d有兩個基類a和b,其中a定義了虛函式show(),b也定義了虛函式show(),且d類重寫了虛函式show(),這樣的d類中的第二張虛函式表(b類例項)裡邊儲存的表項就不是d::show()虛函式位址。
也就是說通過b類指標呼叫d類物件的show()函式時,需要調整this指標。
ps:以上分析以vs2005/2008為編譯器,不過,結論對gcc編譯器也適應。
學習參考:
2010.8.20
cs_wuyg@126.com
-----------------
注:本文是在不考慮跳轉表的情況下分析的。--2010.9.5
路不一定是死的
前段時間給樓下座報表開發,asp.net2.0,客戶有乙個需求,就是報表中要求有 pdf格式的。起初,我有 gridview 匯出到word 或者excel 的資料,所以實現起來很容易,而匯出成 pdf,就犯難了,因為 pdf不是微軟自己的技術,所以在 net2.0 下是不可能有這樣現成的介面了。後...
路不一定是死的
前段時間給樓下座報表開發,asp.net2.0,客戶有乙個需求,就是報表中要求有 pdf格式的。起初,我有 gridview 匯出到word 或者excel 的資料,所以實現起來很容易,而匯出成 pdf,就犯難了,因為 pdf不是微軟自己的技術,所以在 net2.0 下是不可能有這樣現成的介面了。後...
會抓老鼠的貓不一定是好貓
好貓一定會抓老鼠,但是會抓老鼠的貓不一定是好貓。捉老鼠,本來是貓的天職,是主人賦予貓的責任。如果貓捉住老鼠之後,恃功而嬌,在主人還吃不飽飯的時候,天天要主人給買湯姆牌高階貓糧,不給買就罷工不捉老鼠了,這就不是好貓。小時候我鄰居張大.好貓一定會抓老鼠,但是會抓老鼠的貓不一定是好貓。捉老鼠,本來是貓的天...