青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

關于 C++中new背后的行為, 以前已經(jīng)寫過一篇了 理解C++中new背后的行為, 但是里面也只是泛泛而談,沒有真憑實據(jù), 下面我們從匯編的角度看C++編譯器究竟在背后干了什么?
我們的代碼很簡單, 如下:
#include <iostream>
class A
{
public:
virtual void print()
{
std::cout << 10;
}
virtual ~A()
{
std::cout << "~A()";
}
};
class B: public A
{
public:
virtual void print()
{
std::cout << 100;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
A* p = new B();
p->print();
delete p;
return 0;
}
我用WinDbg可以看到main函數(shù)生成的匯編代碼如下: 
NewTest!wmain:
00aa1020 56              push    esi
00aa1021 6a04            push    4 
00aa1023 e8b4030000      call    NewTest!operator new (00aa13dc) //調(diào)用operator new分配大小為4字節(jié)的空間
00aa1028 83c404          add     esp,4
00aa102b 85c0            test    eax,eax
00aa102d 740a            je      NewTest!wmain+0x19 (00aa1039)
00aa102f c7005421aa00    mov     dword ptr [eax],offset NewTest!B::`vftable' (00aa2154) //將虛表地址寫入對象地址的頭4個字節(jié)(虛表指針)
00aa1035 8bf0            mov     esi,eax
00aa1037 eb02            jmp     NewTest!wmain+0x1b (00aa103b)
00aa1039 33f6            xor     esi,esi
00aa103b 8b06            mov     eax,dword ptr [esi]
00aa103d 8b10            mov     edx,dword ptr [eax]
00aa103f 8bce            mov     ecx,esi
00aa1041 ffd2            call    edx //調(diào)用虛表內(nèi)的第一個函數(shù)print
00aa1043 8b06            mov     eax,dword ptr [esi]
00aa1045 8b5004          mov     edx,dword ptr [eax+4]
00aa1048 6a01            push    1
00aa104a 8bce            mov     ecx,esi
00aa104c ffd2            call    edx //調(diào)用虛表內(nèi)的第二個函數(shù)(析構函數(shù))
00aa104e 33c0            xor     eax,eax
00aa1050 5e              pop     esi
00aa1051 c3              ret
00aa1052 cc              int     3
從上面代碼中我們可以看到我們構造的B對象一共只有4個字節(jié),而這四個字節(jié)包含的就是對象的虛表指針,對于C++對象內(nèi)存布局, 對于C++對象的內(nèi)存布局,可以看我這篇 探索C++對象模型。同時我們可以看到, C++里確實是通過虛表來實現(xiàn)多態(tài)的。

上面的代碼也告訴了我們?yōu)槭裁床荒茉跇嬙旌瘮?shù)里通過調(diào)用虛函數(shù)實現(xiàn)多態(tài)? 因為虛表是在最終派生類的構造函數(shù)中生成的的, 執(zhí)行基類構造函數(shù)時虛表都還沒有生成。

接下來我們看看operator new背后的行為:
0:000> u 00aa13dc
NewTest!operator new:
00aa13dc ff25cc20aa00    jmp     dword ptr [NewTest!_imp_??2YAPAXIZ (00aa20cc)]
里面是一個直接跳轉:
0:000> u poi(00aa20cc) L10
MSVCR90!operator new:
74603e99 8bff            mov     edi,edi
74603e9b 55              push    ebp
74603e9c 8bec            mov     ebp,esp
74603e9e 83ec0c          sub     esp,0Ch
74603ea1 eb0d            jmp     MSVCR90!operator new+0x17 (74603eb0)
74603ea3 ff7508          push    dword ptr [ebp+8]
74603ea6 e859dcfbff      call    MSVCR90!_callnewh (745c1b04)
74603eab 59              pop     ecx
74603eac 85c0            test    eax,eax
74603eae 740f            je      MSVCR90!operator new+0x26 (74603ebf)
74603eb0 ff7508          push    dword ptr [ebp+8]
74603eb3 e887feffff      call    MSVCR90!malloc (74603d3f)
74603eb8 59              pop     ecx
74603eb9 85c0            test    eax,eax
74603ebb 74e6            je      MSVCR90!operator new+0xa (74603ea3)
74603ebd c9              leave
我們可以看到operator new最終調(diào)用的是malloc, 如果再深入下去, 會發(fā)現(xiàn)malloc調(diào)用的是Kernel32!HeapAlloc, 而HeapAlloc調(diào)用的又是ntdll!RtlAllocateHeap, 關于heap的布局和分配算法,可以看張銀奎的 軟件調(diào)試

上面論證了new操作符背后的行為: 
首先調(diào)用operator new分配空間, 我們可以重載operator new, 定義自己的內(nèi)存分配算法
然后在分配的空間上調(diào)用構造函數(shù)創(chuàng)建對象, 構造函數(shù)內(nèi)部可能會賦值虛表指針。

接下來我們看下delete背后的行為。
我們看到delete調(diào)用的是虛表里的第二個函數(shù), 我們先看虛表內(nèi)容:
0:000> dps 00aa2154
00aa2154  00aa1010 NewTest!B::print [f:\test\newtest\newtest\newtest.cpp @ 26]
00aa2158  00aa1060 NewTest!B::`scalar deleting destructor'
00aa215c  00000000
00aa2160  00000048
00aa2164  00000000
上面看到虛表里有2個函數(shù), 一個是print, 還有一個是destructor, 我們看下第二個函數(shù)的內(nèi)容:
0:000> u 00aa1060  L10
NewTest!B::`scalar deleting destructor':
00aa1060 56              push    esi
00aa1061 8bf1            mov     esi,ecx
00aa1063 c7064821aa00    mov     dword ptr [esi],offset NewTest!A::`vftable' (00aa2148)
00aa1069 a15820aa00      mov     eax,dword ptr [NewTest!_imp_?coutstd (00aa2058)]
00aa106e 50              push    eax
00aa106f e84c010000      call    NewTest!std::operator<<<std::char_traits<char> > (00aa11c0)
00aa1074 83c404          add     esp,4
00aa1077 f644240801      test    byte ptr [esp+8],1
00aa107c 7409            je      NewTest!B::`scalar deleting destructor'+0x27 (00aa1087)
00aa107e 56              push    esi
00aa107f e806030000      call    NewTest!operator delete (00aa138a)
00aa1084 83c404          add     esp,4
00aa1087 8bc6            mov     eax,esi
00aa1089 5e              pop     esi
00aa108a c20400          ret     4
我們可以看到虛表里放的是 B 的 scalar deleting destructor , 它里面包含兩部分代碼, 一個是我們真正定義的析構函數(shù)的代碼,還有一部分就是operator delete ( operator delete又會去調(diào)用free, free調(diào)用kernel32!HeapFree)。這里的 scalar deleting destructor顯然不是B的析構函數(shù)~B(), 這是編譯器幫我產(chǎn)生的一個函數(shù),它就是給delete B類型對象用的。 

接下來我們看看對于數(shù)組類型的指針, C++編譯器背后是如何處理的, 把代碼改成如下:
int _tmain(int argc, _TCHAR* argv[])
{
A* p = new A[10];
delete []p;
return 0;
}
下面是生成的匯編代碼:
NewTest!wmain:
01181030 6a2c            push    2Ch 
01181032 e8c4030000      call    NewTest!operator new[] (011813fb) //通過operator new分配44自己
01181037 83c404          add     esp,4
0118103a 85c0            test    eax,eax
0118103c 7444            je      NewTest!wmain+0x52 (01181082)
0118103e 56              push    esi
0118103f 6810101801      push    offset NewTest!A::~A (01181010) //A的析構函數(shù)
01181044 6800111801      push    offset NewTest!A::A (01181100)  //A的構造函數(shù)
01181049 6a0a            push    0Ah //10
0118104b 8d7004          lea     esi,[eax+4] //跨過了頭四個字節(jié)
0118104e 6a04            push    4    //對象大小
01181050 56              push    esi //esi里放的是對象列表的起始地址(跨過了頭四個字節(jié)) 
01181051 c7000a000000    mov     dword ptr [eax],0Ah //頭四個字節(jié)寫入對象列表數(shù)量(10)
01181057 e812040000      call    NewTest!`eh vector constructor iterator' (0118146e)
0118105c 85f6            test    esi,esi
0118105e 7421            je      NewTest!wmain+0x51 (01181081)
01181060 837efc00        cmp     dword ptr [esi-4],0 //判斷對象數(shù)量是否 為 0
01181064 8d46fc          lea     eax,[esi-4] //包含對象數(shù)量的地址保存到  eax
01181067 740f            je      NewTest!wmain+0x48 (01181078)
01181069 8b06            mov     eax,dword ptr [esi] //取A的虛表地址
0118106b 8b5004          mov     edx,dword ptr [eax+4] //虛表里的第二個函數(shù)
0118106e 6a03            push    3
01181070 8bce            mov     ecx,esi
01181072 ffd2            call    edx
01181074 5e              pop     esi
01181075 33c0            xor     eax,eax
01181077 c3              ret
重點看上面紅色的代碼, 我們可以看到, 在new一個數(shù)組時,編譯器幫我們做了下面一些事情:
(1)調(diào)用數(shù)組的operator new[] 分配內(nèi)存, 大小為 4 + sizeof(object) * count, 其中頭四個字節(jié)為對象數(shù)量
(2)調(diào)用NewTest!`eh vector constructor iterator(pArrayAddress, sizeof(object),  object_count, pFunConstructor, pFunDestructor), 
其中 pFunDestructor為析構函數(shù), pFunConstructor為構造函數(shù), object_count為對象數(shù)量, sizeof(object)為對象大小,pArrayAddress為起始地址。, 
下面我們反匯編 NewTest!`eh vector constructor iterator: 
0:000> u 0118146e L50
NewTest!`eh vector constructor iterator':
0118146e 6a10            push    10h
01181470 6890221801      push    offset NewTest!__rtc_tzz+0x8 (01182290)
01181475 e8d2040000      call    NewTest!__SEH_prolog4 (0118194c)
0118147a 33c0            xor     eax,eax
0118147c 8945e0          mov     dword ptr [ebp-20h],eax
0118147f 8945fc          mov     dword ptr [ebp-4],eax
01181482 8945e4          mov     dword ptr [ebp-1Ch],eax
01181485 8b45e4          mov     eax,dword ptr [ebp-1Ch] //臨時計數(shù),初始為0
01181488 3b4510          cmp     eax,dword ptr [ebp+10h]  //將臨時計數(shù)和對象數(shù)量比較
0118148b 7d13            jge     NewTest!`eh vector constructor iterator'+0x32 (011814a0) //如果臨時計數(shù)大于對象數(shù)量則退出循環(huán)
0118148d 8b7508          mov     esi,dword ptr [ebp+8] //保存第一個參數(shù)(起始地址)到 esi
01181490 8bce            mov     ecx,esi //賦this指針到ecx
01181492 ff5514          call    dword ptr [ebp+14h] //調(diào)用構造函數(shù)
01181495 03750c          add     esi,dword ptr [ebp+0Ch] //移動指針, 加上對象大小
01181498 897508          mov     dword ptr [ebp+8],esi //保存新對象地址到第一個參數(shù)
0118149b ff45e4          inc     dword ptr [ebp-1Ch] //增加臨時計數(shù)
0118149e ebe5            jmp     NewTest!`eh vector constructor iterator'+0x17 (01181485)
011814a0 c745e001000000  mov     dword ptr [ebp-20h],1
011814a7 c745fcfeffffff  mov     dword ptr [ebp-4],0FFFFFFFEh
011814ae e808000000      call    NewTest!`eh vector constructor iterator'+0x4d (011814bb)
011814b3 e8d9040000      call    NewTest!__SEH_epilog4 (01181991)
011814b8 c21400          ret     14h
我們可以看到NewTest!`eh vector constructor iterator是編譯器幫我們生成的函數(shù), 它的作用就是為數(shù)組中的每個對象都調(diào)用構造函數(shù)。
接下我們再看看數(shù)組形式的delete []在背后究竟干了什么?
重點看上面紫色的代碼:
NewTest!wmain:
....
01181060 837efc00        cmp     dword ptr [esi-4],0 //判斷對象數(shù)量是否 為 0
01181064 8d46fc          lea     eax,[esi-4] //包含對象數(shù)量的地址保存到  eax
01181067 740f            je      NewTest!wmain+0x48 (01181078)
01181069 8b06            mov     eax,dword ptr [esi] //取A的虛表地址
0118106b 8b5004          mov     edx,dword ptr [eax+4] //虛表里的第二個函數(shù)
0118106e 6a03            push    3
01181070 8bce            mov     ecx,esi
01181072 ffd2            call    edx
....
 可以看到它將對象列表起始地址保存到ecx, 然后調(diào)用對象虛表里的第二個函數(shù), 并且傳入?yún)?shù)是3, 我們先看對象虛表內(nèi)容:
0:000> dps 01182148 
01182148  01181000 NewTest!A::print [f:\test\newtest\newtest\newtest.cpp @ 11]
0118214c  01181090 NewTest!A::`vector deleting destructor'
我們看看該函數(shù)究竟干了什么:
0:000> u 01181090  L40
NewTest!A::`vector deleting destructor':
01181090 53              push    ebx
01181091 8a5c2408        mov     bl,byte ptr [esp+8]
01181095 56              push    esi
01181096 8bf1            mov     esi,ecx
01181098 f6c302          test    bl,2 //是否需要調(diào)用析構函數(shù)
0118109b 742b            je      NewTest!A::`vector deleting destructor'+0x38 (011810c8)
0118109d 8b46fc          mov     eax,dword ptr [esi-4]
011810a0 57              push    edi
011810a1 6810101801      push    offset NewTest!A::~A (01181010)
011810a6 8d7efc          lea     edi,[esi-4]
011810a9 50              push    eax
011810aa 6a04            push    4
011810ac 56              push    esi
011810ad e87f040000      call    NewTest!`eh vector destructor iterator' (01181531)
011810b2 f6c301          test    bl,1 //是否需要釋放內(nèi)存
011810b5 7409            je      NewTest!A::`vector deleting destructor'+0x30 (011810c0)
011810b7 57              push    edi
011810b8 e85f030000      call    NewTest!operator delete[] (0118141c)
011810bd 83c404          add     esp,4
011810c0 8bc7            mov     eax,edi
011810c2 5f              pop     edi
011810c3 5e              pop     esi
011810c4 5b              pop     ebx
011810c5 c20400          ret     4
可以看到它內(nèi)部調(diào)用的是NewTest!`eh vector destructor iterator, 而如果再跟蹤NewTest!`eh vector destructor iterator,
會看所有數(shù)組里的對象調(diào)用析構函數(shù), 最后調(diào)用operator delete[]釋放所有內(nèi)存。

我們可以看到數(shù)組new[]和delete[]的關鍵是, C++編譯器在數(shù)組起始地址之前的4個字節(jié)保存了對象的數(shù)量N,后面會根據(jù)這個數(shù)量值進行N次的構造和析構 。 
最后申明下, 上面的分析僅限于VS2008, 實際上在符合C++標準的前提下, 各個C++編譯器有各自不同的實現(xiàn)。
我們可以看到C++ 編譯器在背后干了很多事情,可能會內(nèi)聯(lián)我們的函數(shù), 也可以修改和產(chǎn)生其他一些函數(shù), 而這是很多C開發(fā)者受不了的事情, 所以在內(nèi)核級別, 很多人寧愿用C來減少編譯器背后的干擾。
最后思考一下, 如果我們代碼這樣寫,會怎么樣? 
int _tmain(int argc, _TCHAR* argv[])
{
A* p = new B[10];
delete []p;
return 0;
}
答案請看 這里 
posted on 2013-11-17 21:17 Richard Wei 閱讀(5655) 評論(0)  編輯 收藏 引用 所屬分類: 匯編

只有注冊用戶登錄后才能發(fā)表評論。
網(wǎng)站導航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            国产精品99久久久久久久女警| 亚洲欧美日韩一区二区三区在线| 欧美国产国产综合| 久久午夜电影网| 裸体丰满少妇做受久久99精品| 老司机精品视频一区二区三区| 久久亚洲影音av资源网| 免费国产一区二区| 欧美日韩不卡视频| 国产精品影音先锋| 亚洲国产美女久久久久| 亚洲视频在线观看免费| 欧美一区免费视频| 久久综合一区| 日韩午夜激情| 久久久www免费人成黑人精品| 美女爽到呻吟久久久久| 欧美日韩免费在线| 国内精品久久久久影院薰衣草| 亚洲国产免费| 欧美制服丝袜第一页| 欧美激情一区二区久久久| 亚洲视频精选| 欧美精品123区| 国产一区二三区| 亚洲一区二区少妇| 欧美电影免费网站| 性色一区二区| 欧美性开放视频| 亚洲国产欧美精品| 久久精品国产一区二区三区| 日韩午夜电影| 欧美福利影院| 在线成人国产| 久久激五月天综合精品| 一道本一区二区| 男人的天堂亚洲| 韩国精品在线观看| 午夜精品久久久久久久蜜桃app| 欧美激情网站在线观看| 欧美在线一区二区| 国产精品嫩草99av在线| 99在线|亚洲一区二区| 国产精品日韩一区二区三区| 亚洲精品日韩一| 久久亚洲精品一区| 午夜视频一区| 国产精自产拍久久久久久蜜| 亚洲一级二级在线| 亚洲黑丝在线| 久久影院午夜论| 国产亚洲成av人在线观看导航| 亚洲图片在线| 日韩午夜视频在线观看| 欧美激情综合色| 亚洲六月丁香色婷婷综合久久| 欧美va亚洲va香蕉在线| 久久久久久久激情视频| 好吊视频一区二区三区四区| 久久免费黄色| 久久在线免费观看| 亚洲黄色在线视频| 亚洲欧洲日本国产| 欧美日韩精品免费观看视一区二区| 亚洲理论在线| 99综合在线| 国产美女精品免费电影| 久久久久久久一区二区三区| 久久精品夜色噜噜亚洲a∨| 在线电影欧美日韩一区二区私密| 欧美成人日韩| 欧美日韩美女在线观看| 亚洲一区二区视频在线观看| 亚洲一区二区三区国产| 国产视频观看一区| 嫩草国产精品入口| 欧美激情亚洲自拍| 亚洲亚洲精品在线观看 | 欧美激情精品久久久久久大尺度 | 国产中文一区二区| 免费成人在线视频网站| 欧美成人一区二区三区在线观看 | 亚洲视频国产视频| 国产日韩欧美综合| 欧美电影免费| 国产精品福利片| 久久亚洲欧美| 欧美日韩不卡在线| 久久精彩免费视频| 欧美激情一区二区三级高清视频 | 亚洲日本aⅴ片在线观看香蕉| 亚洲精品国产欧美| 久久av一区二区三区漫画| 在线观看的日韩av| 99在线精品视频| 狠狠v欧美v日韩v亚洲ⅴ| 亚洲第一伊人| 国产欧美精品日韩区二区麻豆天美| 免费看成人av| 国产精品乱子乱xxxx| 欧美成人高清| 国产精品美女www爽爽爽| 欧美aa国产视频| 国产精品夜夜夜| 91久久精品www人人做人人爽| 国产精品区一区二区三区| 欧美黄色大片网站| 国产亚洲激情在线| 正在播放亚洲| 亚洲人成网站在线观看播放| 午夜精品久久久久久99热| 日韩午夜在线播放| 久久全球大尺度高清视频| 性欧美精品高清| 欧美日韩另类综合| 欧美成人一区二区三区在线观看| 国产精品系列在线| 一区二区三区日韩欧美| 99热在这里有精品免费| 久热re这里精品视频在线6| 久久久精品午夜少妇| 国产免费成人| 亚洲一区在线播放| 亚洲综合国产| 欧美亚州在线观看| 一本高清dvd不卡在线观看| 亚洲精品在线免费观看视频| 久久综合久久88| 欧美+日本+国产+在线a∨观看| 国产亚洲在线| 欧美自拍丝袜亚洲| 久久久精品999| 国产一区日韩二区欧美三区| 性高湖久久久久久久久| 欧美一区二区三区另类 | 亚洲午夜在线视频| 欧美日韩午夜剧场| 一本色道精品久久一区二区三区| 一区二区三区高清视频在线观看| 欧美国产日韩在线| 91久久夜色精品国产九色| 亚洲精品影视| 欧美午夜电影在线| 亚洲一区影院| 久久久久综合网| 亚洲大片一区二区三区| 免费日韩视频| 亚洲精品欧美日韩| 亚洲欧美日本国产专区一区| 国产美女精品视频免费观看| 久久国产精品72免费观看| 久久免费高清| 亚洲精品久久久蜜桃| 欧美日韩亚洲一区二区三区在线观看| 日韩午夜在线视频| 久久av二区| 亚洲国产欧美日韩精品| 欧美人成网站| 亚洲欧美一区二区三区久久| 美女国内精品自产拍在线播放| 91久久精品日日躁夜夜躁欧美| 亚洲美女中文字幕| 欧美有码视频| 亚洲国产毛片完整版| 欧美网站在线观看| 欧美综合国产精品久久丁香| 亚洲激情电影在线| 欧美一区二区三区久久精品| 亚洲国产一区二区三区高清 | 亚洲美女视频| 国产精品久久久久77777| 久久av一区二区三区漫画| 亚洲黄一区二区三区| 久久久久国产精品一区| 99精品视频免费在线观看| 国产欧美精品| 欧美精品久久久久久久久老牛影院| 亚洲深夜福利| 亚洲福利久久| 久久精品成人一区二区三区蜜臀| 亚洲精品少妇| 国内成人自拍视频| 国产精品成人在线| 欧美高清在线视频| 久久黄色网页| 亚洲一级在线| 亚洲激情亚洲| 男人的天堂亚洲在线| 久久成人亚洲| 亚洲一区在线播放| 亚洲精品久久久久久下一站| 今天的高清视频免费播放成人 | 在线欧美日韩国产| 国产伦精品一区二区三区| 欧美日韩国产色综合一二三四 | 亚洲美女少妇无套啪啪呻吟| 韩国av一区二区| 国产精品久久久久久久电影 | 日韩一级在线| 亚洲第一福利社区|