關(guān)于new和delete 一些不得不說的事
當(dāng)你寫下new和delete的時候,到底發(fā)生了什么事呢,讓我們來做個試驗看看。
寫一段小代碼:
- class a
- {
- public:
- a()
- {
- foo();
- }
- int foo()
- {
- return 0;
- }
- ~a()
- {
- bar();
- }
- int bar()
- {
- return 1;
- }
- };
- int _tmain(int argc, _TCHAR* argv[])
- {
- a* tmp = new a();
- delete tmp;
- return 0;
- }
在main函數(shù)的第一句下斷點,調(diào)試,然后開匯編窗口輸出結(jié)果:
- int _tmain(int argc, _TCHAR* argv[])
- {
- 004113F0 push ebp
- 004113F1 mov ebp,esp
- 004113F3 push 0FFFFFFFFh
- 004113F5 push offset __ehhandler$_wmain (41478Eh)
- 004113FA mov eax,dword ptr fs:[00000000h]
- 00411400 push eax
- 00411401 sub esp,100h
- 00411407 push ebx
- 00411408 push esi
- 00411409 push edi
- 0041140A lea edi,[ebp-10Ch]
- 00411410 mov ecx,40h
- 00411415 mov eax,0CCCCCCCCh
- 0041141A rep stos dword ptr es:[edi]
- 0041141C mov eax,dword ptr [___security_cookie (418000h)]
- 00411421 xor eax,ebp
- 00411423 push eax
- 00411424 lea eax,[ebp-0Ch]
- 00411427 mov dword ptr fs:[00000000h],eax
- /*a* tmp = new a();*/
- 0041142D push 1
- 0041142F call operator new (4111A4h)
- 00411434 add esp,4
- 00411437 mov dword ptr [ebp-0F8h],eax
- 0041143D mov dword ptr [ebp-4],0
- 00411444 cmp dword ptr [ebp-0F8h],0
- 0041144B je wmain+70h (411460h)
- 0041144D mov ecx,dword ptr [ebp-0F8h]
- 00411453 call a::a (41101Eh)
- 00411458 mov dword ptr [ebp-10Ch],eax
- 0041145E jmp wmain+7Ah (41146Ah)
- 00411460 mov dword ptr [ebp-10Ch],0
- 0041146A mov eax,dword ptr [ebp-10Ch]
- 00411470 mov dword ptr [ebp-104h],eax
- 00411476 mov dword ptr [ebp-4],0FFFFFFFFh
- 0041147D mov ecx,dword ptr [ebp-104h]
- 00411483 mov dword ptr [ebp-14h],ecx
- /*delete tmp;*/
- 00411486 mov eax,dword ptr [ebp-14h]
- 00411489 mov dword ptr [ebp-0E0h],eax
- 0041148F mov ecx,dword ptr [ebp-0E0h]
- 00411495 mov dword ptr [ebp-0ECh],ecx
- 0041149B cmp dword ptr [ebp-0ECh],0
- 004114A2 je wmain+0C9h (4114B9h)
- 004114A4 push 1
- 004114A6 mov ecx,dword ptr [ebp-0ECh]
- 004114AC call a::`scalar deleting destructor' (41117Ch)
- 004114B1 mov dword ptr [ebp-10Ch],eax
- 004114B7 jmp wmain+0D3h (4114C3h)
- 004114B9 mov dword ptr [ebp-10Ch],0
- /*return 0;*/
- 004114C3 xor eax,eax
- }
- 004114C5 mov ecx,dword ptr [ebp-0Ch]
- 004114C8 mov dword ptr fs:[0],ecx
- 004114CF pop ecx
- 004114D0 pop edi
- 004114D1 pop esi
- 004114D2 pop ebx
- 004114D3 add esp,10Ch
- 004114D9 cmp ebp,esp
- 004114DB call @ILT+345(__RTC_CheckEsp) (41115Eh)
- 004114E0 mov esp,ebp
- 004114E2 pop ebp
- 004114E3 ret
前面一片調(diào)整stack,插入安全代碼,設(shè)置異常處理等的操作不是今天我們要說的重點,直接跳到a* tmp = new a();這一句產(chǎn)生的反匯編:
- 0041142F call operator new (4111A4h)
我們很明確的看到調(diào)用了一個函數(shù)operator new。繼續(xù)跟進operator new看到底做了什么事情:
- void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
- { // try to allocate size bytes
- void *p;
- while ((p = malloc(size)) == 0)
- if (_callnewh(size) == 0)
- { // report no memory
- static const std::bad_alloc nomem;
- _RAISE(nomem);
- }
- return (p);
- }
很意外吧,其實operator new函數(shù)就做了那么一件事情:調(diào)用malloc函數(shù)分配內(nèi)存。有沒有負責(zé)調(diào)用構(gòu)造函數(shù)?這個真沒有。。。
#p#
那構(gòu)造函數(shù)到底是誰調(diào)用的?看operator new下面的那片匯編代碼:
- 00411434 add esp,4
- 00411437 mov dword ptr [ebp-0F8h],eax
- 0041143D mov dword ptr [ebp-4],0
- 00411444 cmp dword ptr [ebp-0F8h],0
- 0041144B je wmain+70h (411460h)
- 0041144D mov ecx,dword ptr [ebp-0F8h]
- 00411453 call a::a (41101Eh)
出去將返回值賦給tmp的操作,我們看到了一處函數(shù)調(diào)用:
- 00411453 call a::a (41101Eh)
沒錯,對類a的構(gòu)造函數(shù)的調(diào)用,是編譯器偷偷在你的函數(shù)里插入的,當(dāng)時的情況就是如此。delete的情況也是一摸一樣。
再來看針對對象數(shù)組的new和delete:
- class a
- {
- public:
- a()
- {
- int i1;
- int j1 = 0;
- static int k1;
- static int l1 = 0;
- foo();
- }
- int foo()
- {
- return 0;
- }
- ~a()
- {
- int i2;
- int j2 = 0;
- static int k2;
- static int l2 = 0;
- bar();
- }
- int bar()
- {
- return 1;
- }
- };
- int _tmain(int argc, _TCHAR* argv[])
- {
- a* tmp = new a[10];
- delete[] tmp;
- return 0;
- }
反匯編之后的結(jié)果如下:
- int _tmain(int argc, _TCHAR* argv[])
- {
- 004113F0 push ebp
- 004113F1 mov ebp,esp
- 004113F3 push 0FFFFFFFFh
- 004113F5 push offset __ehhandler$_wmain (41478Eh)
- 004113FA mov eax,dword ptr fs:[00000000h]
- 00411400 push eax
- 00411401 sub esp,100h
- 00411407 push ebx
- 00411408 push esi
- 00411409 push edi
- 0041140A lea edi,[ebp-10Ch]
- 00411410 mov ecx,40h
- 00411415 mov eax,0CCCCCCCCh
- 0041141A rep stos dword ptr es:[edi]
- 0041141C mov eax,dword ptr [___security_cookie (418000h)]
- 00411421 xor eax,ebp
- 00411423 push eax
- 00411424 lea eax,[ebp-0Ch]
- 00411427 mov dword ptr fs:[00000000h],eax
- a* tmp = new a[10];
- 0041142D push 0Eh
- 0041142F call operator new (4111A4h)
- 00411434 add esp,4
- 00411437 mov dword ptr [ebp-0F8h],eax
- 0041143D mov dword ptr [ebp-4],0
- 00411444 cmp dword ptr [ebp-0F8h],0
- 0041144B je wmain+97h (411487h)
- 0041144D mov eax,dword ptr [ebp-0F8h]
- 00411453 mov dword ptr [eax],0Ah
- 00411459 push offset a::`scalar deleting destructor' (41100Ah)
- 0041145E push offset a::a (41101Eh)
- 00411463 push 0Ah
- 00411465 push 1
- 00411467 mov ecx,dword ptr [ebp-0F8h]
- 0041146D add ecx,4
- 00411470 push ecx
- 00411471 call `eh vector constructor iterator' (4111F9h)
- 00411476 mov edx,dword ptr [ebp-0F8h]
- 0041147C add edx,4
- 0041147F mov dword ptr [ebp-10Ch],edx
- 00411485 jmp wmain+0A1h (411491h)
- 00411487 mov dword ptr [ebp-10Ch],0
- 00411491 mov eax,dword ptr [ebp-10Ch]
- 00411497 mov dword ptr [ebp-104h],eax
- 0041149D mov dword ptr [ebp-4],0FFFFFFFFh
- 004114A4 mov ecx,dword ptr [ebp-104h]
- 004114AA mov dword ptr [ebp-14h],ecx
- delete[] tmp;
- 004114AD mov eax,dword ptr [ebp-14h]
- 004114B0 mov dword ptr [ebp-0E0h],eax
- 004114B6 mov ecx,dword ptr [ebp-0E0h]
- 004114BC mov dword ptr [ebp-0ECh],ecx
- 004114C2 cmp dword ptr [ebp-0ECh],0
- 004114C9 je wmain+0F0h (4114E0h)
- 004114CB push 3
- 004114CD mov ecx,dword ptr [ebp-0ECh]
- 004114D3 call a::`vector deleting destructor' (4111F4h)
- 004114D8 mov dword ptr [ebp-10Ch],eax
- 004114DE jmp wmain+0FAh (4114EAh)
- 004114E0 mov dword ptr [ebp-10Ch],0
- return 0;
- 004114EA xor eax,eax
- }
- 004114EC mov ecx,dword ptr [ebp-0Ch]
- 004114EF mov dword ptr fs:[0],ecx
- 004114F6 pop ecx
- 004114F7 pop edi
- 004114F8 pop esi
- 004114F9 pop ebx
- 004114FA add esp,10Ch
- 00411500 cmp ebp,esp
- 00411502 call @ILT+345(__RTC_CheckEsp) (41115Eh)
- 00411507 mov esp,ebp
- 00411509 pop ebp
- 0041150A ret
其他部分都大同小異,關(guān)鍵的不同在編譯器插入的,用于初始化的代碼:
- 00411459 push offset a::`scalar deleting destructor' (41100Ah)
- 0041145E push offset a::a (41101Eh)
- 00411463 push 0Ah
- 00411465 push 1
- 00411467 mov ecx,dword ptr [ebp-0F8h]
- 0041146D add ecx,4
- 00411470 push ecx
- 00411471 call `eh vector constructor iterator' (4111F9h)
我們看到數(shù)組大小0Ah,構(gòu)造函數(shù)的地址41101Eh都被壓入棧中,作為某函數(shù)的參數(shù)。到底是什么函數(shù)呢?就是:
- 00411471 call `eh vector constructor iterator' (4111F9h)
一個名為`eh vector constructor iterator' 的函數(shù)。我們還注意到a類的析構(gòu)函數(shù)的地址也被當(dāng)成參數(shù)傳入,這是干什么用的呢?構(gòu)造函數(shù)里為什么要析構(gòu)函數(shù)的地址?比如在遍歷調(diào)用構(gòu)造函數(shù)的過程中,前8個都是沒問題的,到第9個突然資源不足調(diào)用失敗了,那么在返回前無論如何也要先把前8個的析構(gòu)函數(shù)調(diào)用一遍,防止資源泄露。
delete[]的過程也大同小異,不過一個很有趣的地方是,“vector deleting destructor'”是a類的成員函數(shù),而與‘eh vector constructor iterator’對應(yīng)的`eh vector destructor iterator'函數(shù)在“vector deleting destructor'”函數(shù)內(nèi)部:
- 004134AD call `eh vector destructor iterator' (411203h)
。。。
- 004134C1 call operator delete (4110A0h)
回收內(nèi)存的操作,也在a::`vector deleting destructor'里。
【編輯推薦】