关于new和delete 一些不得不说的事

new和delete在C++中作用很大,但是当你写下new和delete的时候,到底发生了什么事,你
首页 新闻资讯 行业资讯 关于new和delete 一些不得不说的事

当你写下new和delete的时候,到底发生了什么事呢,让我们来做个试验看看。

写一段小代码:

复制

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;  }
  • 1.

  • 2.

  • 3.

  • 4.

  • 5.

  • 6.

  • 7.

  • 8.

  • 9.

  • 10.

  • 11.

  • 12.

  • 13.

  • 14.

  • 15.

  • 16.

  • 17.

  • 18.

  • 19.

  • 20.

  • 21.

  • 22.

  • 23.

  • 24.

  • 25.

  • 26.

  • 27.

  • 28.

  • 29.

在main函数的第一句下断点,调试,然后开汇编窗口输出结果:

复制

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
  • 1.

  • 2.

  • 3.

  • 4.

  • 5.

  • 6.

  • 7.

  • 8.

  • 9.

  • 10.

  • 11.

  • 12.

  • 13.

  • 14.

  • 15.

  • 16.

  • 17.

  • 18.

  • 19.

  • 20.

  • 21.

  • 22.

  • 23.

  • 24.

  • 25.

  • 26.

  • 27.

  • 28.

  • 29.

  • 30.

  • 31.

  • 32.

  • 33.

  • 34.

  • 35.

  • 36.

  • 37.

  • 38.

  • 39.

  • 40.

  • 41.

  • 42.

  • 43.

  • 44.

  • 45.

  • 46.

  • 47.

  • 48.

  • 49.

  • 50.

  • 51.

  • 52.

  • 53.

  • 54.

  • 55.

  • 56.

  • 57.

  • 58.

  • 59.

  • 60.

  • 61.

  • 62.

  • 63.

  • 64.

  • 65.

  • 66.

  • 67.

前面一片调整stack,插入安全代码,设置异常处理等的操作不是今天我们要说的重点,直接跳到a* tmp = new a();这一句产生的反汇编:

复制

0041142F call operator new (4111A4h)
  • 1.

我们很明确的看到调用了一个函数operator new。继续跟进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);          }
  • 1.

  • 2.

  • 3.

  • 4.

  • 5.

  • 6.

  • 7.

  • 8.

  • 9.

  • 10.

  • 11.

  • 12.

很意外吧,其实operator new函数就做了那么一件事情:调用malloc函数分配内存。有没有负责调用构造函数?这个真没有。。。

#p#

那构造函数到底是谁调用的?看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)
  • 1.

  • 2.

  • 3.

  • 4.

  • 5.

  • 6.

  • 7.

出去将返回值赋给tmp的操作,我们看到了一处函数调用:

复制

00411453 call a::a (41101Eh)
  • 1.

没错,对类a的构造函数的调用,是编译器偷偷在你的函数里插入的,当时的情况就是如此。delete的情况也是一摸一样。
再来看针对对象数组的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;  }
  • 1.

  • 2.

  • 3.

  • 4.

  • 5.

  • 6.

  • 7.

  • 8.

  • 9.

  • 10.

  • 11.

  • 12.

  • 13.

  • 14.

  • 15.

  • 16.

  • 17.

  • 18.

  • 19.

  • 20.

  • 21.

  • 22.

  • 23.

  • 24.

  • 25.

  • 26.

  • 27.

  • 28.

  • 29.

  • 30.

  • 31.

  • 32.

  • 33.

  • 34.

  • 35.

  • 36.

  • 37.

反汇编之后的结果如下:

复制

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
  • 1.

  • 2.

  • 3.

  • 4.

  • 5.

  • 6.

  • 7.

  • 8.

  • 9.

  • 10.

  • 11.

  • 12.

  • 13.

  • 14.

  • 15.

  • 16.

  • 17.

  • 18.

  • 19.

  • 20.

  • 21.

  • 22.

  • 23.

  • 24.

  • 25.

  • 26.

  • 27.

  • 28.

  • 29.

  • 30.

  • 31.

  • 32.

  • 33.

  • 34.

  • 35.

  • 36.

  • 37.

  • 38.

  • 39.

  • 40.

  • 41.

  • 42.

  • 43.

  • 44.

  • 45.

  • 46.

  • 47.

  • 48.

  • 49.

  • 50.

  • 51.

  • 52.

  • 53.

  • 54.

  • 55.

  • 56.

  • 57.

  • 58.

  • 59.

  • 60.

  • 61.

  • 62.

  • 63.

  • 64.

  • 65.

  • 66.

  • 67.

  • 68.

  • 69.

  • 70.

  • 71.

  • 72.

  • 73.

  • 74.

  • 75.

  • 76.

  • 77.

  • 78.

其他部分都大同小异,关键的不同在编译器插入的,用于初始化的代码:

复制

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)
  • 1.

  • 2.

  • 3.

  • 4.

  • 5.

  • 6.

  • 7.

  • 8.

我们看到数组大小0Ah,构造函数的地址41101Eh都被压入栈中,作为某函数的参数。到底是什么函数呢?就是:

复制

00411471  call        `eh vector constructor iterator' (4111F9h)
  • 1.

一个名为`eh vector constructor iterator' 的函数。我们还注意到a类的析构函数的地址也被当成参数传入,这是干什么用的呢?构造函数里为什么要析构函数的地址?比如在遍历调用构造函数的过程中,前8个都是没问题的,到第9个突然资源不足调用失败了,那么在返回前无论如何也要先把前8个的析构函数调用一遍,防止资源泄露。

delete[]的过程也大同小异,不过一个很有趣的地方是,“vector deleting destructor'”是a类的成员函数,而与‘eh vector constructor iterator’对应的`eh vector destructor iterator'函数在“vector deleting destructor'”函数内部:

复制

004134AD  call        `eh vector destructor iterator' (411203h)
  • 1.

。。。

复制

004134C1  call        operator delete (4110A0h)
  • 1.

回收内存的操作,也在a::`vector deleting destructor'里。

【编辑推荐】

  1. 《Java编程思想》作者:C++不垃圾,只是Java很傲慢

  2. Java与C++语言在作用域上的差异浅析

  3. C/C++使用多种方法获取文件大小代码

  4. C++类成员函数的重载、覆盖与隐藏

  5. 在C++中使用Lambda函数提高代码性能

16    2009-06-26 11:01:38    new和delete