描写一瞬间心动的句子:关于new和delete 一些不得不说的事

来源:百度文库 编辑:中财网 时间:2024/04/28 17:19:02
http://developer.51cto.com/art/200906/131853.htm 2009-06-26 11:01  gussing  
    new和delete在C++中作用很大,但是当你写下new和delete的时候,到底发生了什么事,你了解过吗?本文为你讲述当你写下new和delete的时候,到底发生了什么事。

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

    写一段小代码:

            
    1. class a  
    2. {  
    3. public:  
    4.  a()  
    5.  {  
    6.   foo();  
    7.  }  
    8.  int foo()  
    9.  {  
    10.   return 0;  
    11.  }  
    12.  
    13.  ~a()  
    14.  {  
    15.   bar();  
    16.  }  
    17.  
    18.  int bar()  
    19.  {  
    20.   return 1;  
    21.  }  
    22. };  
    23.  
    24. int _tmain(int argc, _TCHAR* argv[])  
    25. {  
    26.  a* tmp = new a();  
    27.  delete tmp;  
    28.  return 0;  
    29. }  

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

            
    1. int _tmain(int argc, _TCHAR* argv[])  
    2. {  
    3. 004113F0  push        ebp    
    4. 004113F1  mov         ebp,esp   
    5. 004113F3  push        0FFFFFFFFh   
    6. 004113F5  push        offset __ehhandler$_wmain (41478Eh)   
    7. 004113FA  mov         eax,dword ptr fs:[00000000h]   
    8. 00411400  push        eax    
    9. 00411401  sub         esp,100h   
    10. 00411407  push        ebx    
    11. 00411408  push        esi    
    12. 00411409  push        edi    
    13. 0041140A  lea         edi,[ebp-10Ch]   
    14. 00411410  mov         ecx,40h   
    15. 00411415  mov         eax,0CCCCCCCCh   
    16. 0041141A  rep stos    dword ptr es:[edi]   
    17. 0041141C  mov         eax,dword ptr [___security_cookie (418000h)]   
    18. 00411421  xor         eax,ebp   
    19. 00411423  push        eax    
    20. 00411424  lea         eax,[ebp-0Ch]   
    21. 00411427  mov         dword ptr fs:[00000000h],eax   
    22.  /*a* tmp = new a();*/ 
    23. 0041142D  push        1      
    24. 0041142F  call        operator new (4111A4h)   
    25. 00411434  add         esp,4   
    26. 00411437  mov         dword ptr [ebp-0F8h],eax   
    27. 0041143D  mov         dword ptr [ebp-4],0   
    28. 00411444  cmp         dword ptr [ebp-0F8h],0   
    29. 0041144B  je          wmain+70h (411460h)   
    30. 0041144D  mov         ecx,dword ptr [ebp-0F8h]   
    31. 00411453  call        a::a (41101Eh)   
    32. 00411458  mov         dword ptr [ebp-10Ch],eax   
    33. 0041145E  jmp         wmain+7Ah (41146Ah)   
    34. 00411460  mov         dword ptr [ebp-10Ch],0   
    35. 0041146A  mov         eax,dword ptr [ebp-10Ch]   
    36. 00411470  mov         dword ptr [ebp-104h],eax   
    37. 00411476  mov         dword ptr [ebp-4],0FFFFFFFFh   
    38. 0041147D  mov         ecx,dword ptr [ebp-104h]   
    39. 00411483  mov         dword ptr [ebp-14h],ecx   
    40.  /*delete tmp;*/ 
    41. 00411486  mov         eax,dword ptr [ebp-14h]   
    42. 00411489  mov         dword ptr [ebp-0E0h],eax   
    43. 0041148F  mov         ecx,dword ptr [ebp-0E0h]   
    44. 00411495  mov         dword ptr [ebp-0ECh],ecx   
    45. 0041149B  cmp         dword ptr [ebp-0ECh],0   
    46. 004114A2  je          wmain+0C9h (4114B9h)   
    47. 004114A4  push        1      
    48. 004114A6  mov         ecx,dword ptr [ebp-0ECh]   
    49. 004114AC  call        a::`scalar deleting destructor' (41117Ch)   
    50. 004114B1  mov         dword ptr [ebp-10Ch],eax   
    51. 004114B7  jmp         wmain+0D3h (4114C3h)   
    52. 004114B9  mov         dword ptr [ebp-10Ch],0   
    53.  /*return 0;*/ 
    54. 004114C3  xor         eax,eax   
    55. }  
    56. 004114C5  mov         ecx,dword ptr [ebp-0Ch]   
    57. 004114C8  mov         dword ptr fs:[0],ecx   
    58. 004114CF  pop         ecx    
    59. 004114D0  pop         edi    
    60. 004114D1  pop         esi    
    61. 004114D2  pop         ebx    
    62. 004114D3  add         esp,10Ch   
    63. 004114D9  cmp         ebp,esp   
    64. 004114DB  call        @ILT+345(__RTC_CheckEsp) (41115Eh)   
    65. 004114E0  mov         esp,ebp   
    66. 004114E2  pop         ebp    
    67. 004114E3  ret     

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

            
    1. 0041142F call operator new (4111A4h) 

    我们很明确的看到调用了一个函数operator new。继续跟进operator new看到底做了什么事情:

            
    1. void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)  
    2.         {       // try to allocate size bytes  
    3.         void *p;  
    4.         while ((p = malloc(size)) == 0)  
    5.                 if (_callnewh(size) == 0)  
    6.                 {       // report no memory  
    7.                 static const std::bad_alloc nomem;  
    8.                 _RAISE(nomem);  
    9.                 }  
    10.  
    11.         return (p);  
    12.         }  

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

    那构造函数到底是谁调用的?看operator new下面的那片汇编代码:

            
    1. 00411434  add         esp,4   
    2. 00411437  mov         dword ptr [ebp-0F8h],eax   
    3. 0041143D  mov         dword ptr [ebp-4],0   
    4. 00411444  cmp         dword ptr [ebp-0F8h],0   
    5. 0041144B  je          wmain+70h (411460h)   
    6. 0041144D  mov         ecx,dword ptr [ebp-0F8h]   
    7. 00411453  call        a::a (41101Eh)  

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

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

    没错,对类a的构造函数的调用,是编译器偷偷在你的函数里插入的,当时的情况就是如此。delete的情况也是一摸一样。
    再来看针对对象数组的new和delete:

            
    1. class a  
    2. {  
    3. public:  
    4.  a()  
    5.  {  
    6.   int i1;  
    7.   int j1 = 0;  
    8.   static int k1;  
    9.   static int l1 = 0;  
    10.   foo();  
    11.  }  
    12.  int foo()  
    13.  {  
    14.   return 0;  
    15.  }  
    16.  
    17.  ~a()  
    18.  {  
    19.   int i2;  
    20.   int j2 = 0;  
    21.   static int k2;  
    22.   static int l2 = 0;  
    23.   bar();  
    24.  }  
    25.  
    26.  int bar()  
    27.  {  
    28.   return 1;  
    29.  }  
    30. };  
    31.  
    32. int _tmain(int argc, _TCHAR* argv[])  
    33. {  
    34.  a* tmp = new a[10];  
    35.  delete[] tmp;  
    36.  return 0;  
    37. }  

    反汇编之后的结果如下:

            
    1. int _tmain(int argc, _TCHAR* argv[])  
    2. {  
    3. 004113F0  push        ebp    
    4. 004113F1  mov         ebp,esp   
    5. 004113F3  push        0FFFFFFFFh   
    6. 004113F5  push        offset __ehhandler$_wmain (41478Eh)   
    7. 004113FA  mov         eax,dword ptr fs:[00000000h]   
    8. 00411400  push        eax    
    9. 00411401  sub         esp,100h   
    10. 00411407  push        ebx    
    11. 00411408  push        esi    
    12. 00411409  push        edi    
    13. 0041140A  lea         edi,[ebp-10Ch]   
    14. 00411410  mov         ecx,40h   
    15. 00411415  mov         eax,0CCCCCCCCh   
    16. 0041141A  rep stos    dword ptr es:[edi]   
    17. 0041141C  mov         eax,dword ptr [___security_cookie (418000h)]   
    18. 00411421  xor         eax,ebp   
    19. 00411423  push        eax    
    20. 00411424  lea         eax,[ebp-0Ch]   
    21. 00411427  mov         dword ptr fs:[00000000h],eax   
    22.  a* tmp = new a[10];  
    23. 0041142D  push        0Eh    
    24. 0041142F  call        operator new (4111A4h)   
    25. 00411434  add         esp,4   
    26. 00411437  mov         dword ptr [ebp-0F8h],eax   
    27. 0041143D  mov         dword ptr [ebp-4],0   
    28. 00411444  cmp         dword ptr [ebp-0F8h],0   
    29. 0041144B  je          wmain+97h (411487h)   
    30. 0041144D  mov         eax,dword ptr [ebp-0F8h]   
    31. 00411453  mov         dword ptr [eax],0Ah   
    32. 00411459  push        offset a::`scalar deleting destructor' (41100Ah)   
    33. 0041145E  push        offset a::a (41101Eh)   
    34. 00411463  push        0Ah    
    35. 00411465  push        1      
    36. 00411467  mov         ecx,dword ptr [ebp-0F8h]   
    37. 0041146D  add         ecx,4   
    38. 00411470  push        ecx    
    39. 00411471  call        `eh vector constructor iterator' (4111F9h)   
    40. 00411476  mov         edx,dword ptr [ebp-0F8h]   
    41. 0041147C  add         edx,4   
    42. 0041147F  mov         dword ptr [ebp-10Ch],edx   
    43. 00411485  jmp         wmain+0A1h (411491h)   
    44. 00411487  mov         dword ptr [ebp-10Ch],0   
    45. 00411491  mov         eax,dword ptr [ebp-10Ch]   
    46. 00411497  mov         dword ptr [ebp-104h],eax   
    47. 0041149D  mov         dword ptr [ebp-4],0FFFFFFFFh   
    48. 004114A4  mov         ecx,dword ptr [ebp-104h]   
    49. 004114AA  mov         dword ptr [ebp-14h],ecx   
    50.  delete[] tmp;  
    51. 004114AD  mov         eax,dword ptr [ebp-14h]   
    52. 004114B0  mov         dword ptr [ebp-0E0h],eax   
    53. 004114B6  mov         ecx,dword ptr [ebp-0E0h]   
    54. 004114BC  mov         dword ptr [ebp-0ECh],ecx   
    55. 004114C2  cmp         dword ptr [ebp-0ECh],0   
    56. 004114C9  je          wmain+0F0h (4114E0h)   
    57. 004114CB  push        3      
    58. 004114CD  mov         ecx,dword ptr [ebp-0ECh]   
    59. 004114D3  call        a::`vector deleting destructor' (4111F4h)   
    60. 004114D8  mov         dword ptr [ebp-10Ch],eax   
    61. 004114DE  jmp         wmain+0FAh (4114EAh)   
    62. 004114E0  mov         dword ptr [ebp-10Ch],0   
    63.  return 0;  
    64. 004114EA  xor         eax,eax   
    65. }  
    66. 004114EC  mov         ecx,dword ptr [ebp-0Ch]   
    67. 004114EF  mov         dword ptr fs:[0],ecx   
    68. 004114F6  pop         ecx    
    69. 004114F7  pop         edi    
    70. 004114F8  pop         esi    
    71. 004114F9  pop         ebx    
    72. 004114FA  add         esp,10Ch   
    73. 00411500  cmp         ebp,esp   
    74. 00411502  call        @ILT+345(__RTC_CheckEsp) (41115Eh)   
    75. 00411507  mov         esp,ebp   
    76. 00411509  pop         ebp    
    77. 0041150A  ret           
    78.   

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

            
    1. 00411459  push        offset a::`scalar deleting destructor' (41100Ah)   
    2. 0041145E  push        offset a::a (41101Eh)   
    3. 00411463  push        0Ah    
    4. 00411465  push        1      
    5. 00411467  mov         ecx,dword ptr [ebp-0F8h]   
    6. 0041146D  add         ecx,4   
    7. 00411470  push        ecx    
    8. 00411471  call        `eh vector constructor iterator' (4111F9h) 

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

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

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

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

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

    。。。

            
    1. 004134C1  call        operator delete (4110A0h) 

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

     

    (#)