欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費電子書(shū)等14項超值服

開(kāi)通VIP
潘凱:C 對象布局及多態(tài)實(shí)現的探索(十一)

菱形結構的虛繼承(3)

  最后我們看看,如果在上篇例子的基礎上,子類(lèi)及左、右父類(lèi)都各自定義了自己的虛函數,這時(shí)的情況又會(huì )怎樣。
struct C140 : public virtual C041
{
    C140() : c_(0x02) {}
    virtual void foo() { c_ = 0x11; }
    char c_;
};
struct C160 : public virtual C041
{
    C160() : c_(0x02) {}
    virtual void foo() { c_ = 0x12; }
    virtual void f160() { c_ = 0x12; }
    char c_;
};
struct C161 : public virtual C041
{
    C161() : c_(0x03) {}
    virtual void foo() { c_ = 0x13; }
    virtual void f161() { c_ = 0x13; }
    char c_;
};
struct C170 : public C160, public C161
{
    C170() : c_(0x04) {}
    virtual void foo() { c_ = 0x14; }
    virtual void f170() { c_ = 0x14; }
    char c_;
};
  首先運行如下的代碼,看看內存的布局。
PRINT_SIZE_DETAIL(C041)
PRINT_SIZE_DETAIL(C160)
PRINT_SIZE_DETAIL(C161)
PRINT_SIZE_DETAIL(C170)
  結果為:
The size of C041 is 5
The detail of C041 is f0 b2 45 00 01
The size of C160 is 18
The detail of C160 is 84 b3 45 00 88 b3 45 00 02 00 00 00 00 80 b3 45 00 01
The size of C161 is 18
The detail of C161 is 98 b3 45 00 9c b3 45 00 03 00 00 00 00 94 b3 45 00 01
The size of C170 is 28
The detail of C170 is b0 b3 45 00 c8 b3 45 00 02 ac b3 45 00 bc b3 45 00 03 04 00 00 00 00 a8 b3 45 00 01
  C170對象的布局為:
|C160,9             |C161,9             |C170,1 |zero,4 |C041,5   |
|vp,4 |op,4,19 |m,1 |vp,4 |op,4,10 |m,1 |m,1    |       |vp,4 |m1 |
  (注:為了不折行,我用了縮寫(xiě)。op代表偏移值指針、m代表成員變量、vp代表虛表指針。第一個(gè)數字是該區域的大小,即字節數。只有偏移值指針有第二個(gè)數字,第二個(gè)數字就是偏移值指針指向的偏移值的大小。)
  左右父類(lèi)由于各自定義了自己的新的虛函數,因此都擁有了自己的虛表指針。奇怪的是子類(lèi)雖然也定義了自己的新的虛函數,我們在上面的布局中卻看到它并沒(méi)有自己的虛表指針。那么它應該是和頂層類(lèi)或是某一父類(lèi)共用了虛表。我們可以在后面通過(guò)對調用的跟蹤來(lái)找到答案。
  另一個(gè)奇怪的地方是在左右父類(lèi)中的偏移值指針指向的偏移值不再是到祖父類(lèi)的偏移量,而是變成了到祖父類(lèi)之前的4字節0值的偏移量。同時(shí)在前面第八篇中我們說(shuō)過(guò)偏移值指針指向的地址的前4個(gè)字節為零,接下來(lái)的4個(gè)字節才是真正的偏移量。在這個(gè)例子中,前4個(gè)字節不再為0,而是0xFFFFFFFC,即整數-4。
  照例我們先通過(guò)對象來(lái)調用一下。
C170 obj;
PRINT_OBJ_ADR(obj);
obj.foo();
  結果為:
obj's address is : 0012F54C
  最后一行調用對應的匯編指令為:
004245B8  lea         ecx,[ebp+FFFFF687h]
004245BE  call        0041D122
  ecx中的值(即this指針的值)為0x0012F563,和前面一樣是指向祖父類(lèi)的起始部分。同樣函數中的指令也是通過(guò)將this-5字節來(lái)定位到正確的成員變量的地址,這里不再列出函數的匯編指令。
  再看看調用它自己新定義的虛函數。
obj.f170();
  對應的匯編指令為:
004245C3  lea         ecx,[ebp+FFFFF670h]
004245C9  call        0041D127
  讓我非常驚奇的是這次this指針的值居然是0x0012F54C。和前面的對象地址輸出是一樣的,也就是指向了整個(gè)對象的起始位置。這就讓人非常的奇怪了,在同一個(gè)對象上調用的兩個(gè)虛函數,編譯器為它們傳遞的this指針卻是不同的。
  讓我們跟到函數中,看它怎樣取得正確的成員變量的地址。
01 00426F80  push        ebp  
02 00426F81  mov         ebp,esp
03 00426F83  sub         esp,0CCh
04 00426F89  push        ebx  
05 00426F8A  push        esi  
06 00426F8B  push        edi  
07 00426F8C  push        ecx  
08 00426F8D  lea         edi,[ebp+FFFFFF34h]
09 00426F93  mov         ecx,33h
10 00426F98  mov         eax,0CCCCCCCCh
11 00426F9D  rep stos    dword ptr [edi]
12 00426F9F  pop         ecx  
13 00426FA0  mov         dword ptr [ebp-8],ecx
14 00426FA3  mov         eax,dword ptr [ebp-8]
15 00426FA6  mov         byte ptr [eax+12h],14h
16 00426FAA  pop         edi  
17 00426FAB  pop         esi  
18 00426FAC  pop         ebx  
19 00426FAD  mov         esp,ebp
20 00426FAF  pop         ebp  
21 00426FB0  ret       
  看看第15行可以知道,是直接在this指針上加了18字節(即16進(jìn)制的12h)來(lái)定位到子類(lèi)的成員變量。
  由于函數中的指令是以這種方式來(lái)定位子類(lèi)成員變量,所以即使我們是通過(guò)指針來(lái)調用,不同的只是怎樣定位函數地址,而this指針的值是肯定不會(huì )變的。我們來(lái)驗證一下。
C170 * pt = &obj;
pt->f170();
  第二行代碼對應的匯編指令如下:
01 004245DA  mov         eax,dword ptr [ebp+FFFFF664h]
02 004245E0  mov         edx,dword ptr [eax]
03 004245E2  mov         esi,esp
04 004245E4  mov         ecx,dword ptr [ebp+FFFFF664h]
05 004245EA  call        dword ptr [edx+4]
06 004245ED  cmp         esi,esp
07 004245EF  call        0041DDF2
  第一行把整個(gè)對象的起始地址放到eax中,第2行把eax當指針,并把所指地址放到edx中。對象的起始地址正好也是左父類(lèi)中的虛表指針,第5行進(jìn)行調用的時(shí)候果然是把edx指向的地址后移了4字節后取值,做為函數地址。這也就回答了前面的一個(gè)問(wèn)題,子類(lèi)沒(méi)有虛表,它的虛表實(shí)際合并到了左父類(lèi)的虛表中,左父類(lèi)定義了一個(gè)自己的虛函數,占用了虛函數表的第一個(gè)條目,子類(lèi)的虛函數則占用了第二個(gè)條目。因此在尋址時(shí)要加上4個(gè)字節。ecx中的this指針值和我們前面估計一樣,是整個(gè)對象的起始地址。
 
  最后我們看看怎樣得到祖父類(lèi)地址。
pt->C041::c_ = 0x33;
  對應的匯編指令為:
01 004245F4  mov         eax,dword ptr [ebp+FFFFF664h]
02 004245FA  mov         ecx,dword ptr [eax+4]
03 004245FD  mov         edx,dword ptr [ecx+4]
04 00424600  mov         eax,dword ptr [ebp+FFFFF664h]
05 00424606  mov         byte ptr [eax+edx+8],33h
  首先把對象的起始地址賦給eax。第2行把eax+4字節后得到的指針指向的地址賦給ecx,這個(gè)值就是偏移值指針指向的地址。果然第3行把它+4字節后取值,再賦給edx。這時(shí)edx的值為13h,照理這應該是到祖父類(lèi)區域的偏移值,但實(shí)際是只到我們在對象布局中列出的4字節0值,也就是真正的祖父類(lèi)起始地址的前4個(gè)字節。我們在前面討論C170的對象布局時(shí)已經(jīng)提到這個(gè)問(wèn)題。所以我們看到第5行定位到成員變量時(shí)再加了8字節,以跳過(guò)4字節的0值為4字節的祖父類(lèi)的虛表指針,而不是只加4字節跳過(guò)虛表指針。在C150對象中我們可以看到偏移值是直接跳過(guò)4字節0值,定位到祖父類(lèi)起始地址的。

  我們始終沒(méi)有清楚的解釋過(guò)祖父類(lèi)之前的4字節0值及偏移值指針指向地址的前4字節的語(yǔ)義。有可能是出于兼容的原因,也有可能是為編譯器提供一些薄記信息。而且,引入虛繼承后的對象繼承的拓樸結構可以比我們討論過(guò)的菱形結構要復雜得多。這兩個(gè)值也可能是用來(lái)處理更復雜的繼承結構。要想通過(guò)表象去揣測出使用它們的動(dòng)機太困難了。

  (未完待續)
本站僅提供存儲服務(wù),所有內容均由用戶(hù)發(fā)布,如發(fā)現有害或侵權內容,請點(diǎn)擊舉報。
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
c++虛函數機制
淺析C++中的this指針 - 數組指針 - 龍行天下
C++對象布局及多態(tài)之虛成員函數如何調用 -- RocketMan'sRoom -- 編程...
VC2008多重繼承下的Virtual Functions:Adjustor Thunk技術(shù)
潘凱:C++對象布局及多態(tài)實(shí)現的探索(8-12)
IDA Pro權威指南 (第2版)
更多類(lèi)似文章 >>
生活服務(wù)
分享 收藏 導長(cháng)圖 關(guān)注 下載文章
綁定賬號成功
后續可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服

欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久