【摘】new 和 delete的讨论

摘自:http://topic.csdn.net/u/20080826/16/0c479e3d-737f-45c7-995e-bd316f5fa166.html

赫然发现,其实这已经是个老问题了,以前csdn上已经有过讨论了。http://topic.csdn.net/u/20070712/07/57c7cfc6-7314-400d-86d2-230a72581ea5.html。或许也有不少启发。

现将各位c/c++友所述意见,结合相关资料,总结如下,希望有所帮助:

1.delete 与delete【】都会释放所有内存,它们的不同点仅在于,根据得到的类型信息是单个指针还是指向数组的指针,来决定调用析构函数的次数。而编译器对所要删除的那个指针到底是指向的单一对象还是对象数组的判断依据就是'【】'的有无。若有'[]'编译器得到的类型信息就是指向数组的指针,然后调用多次析构函数;若没有‘【】’编译器得到的类型信息就是单个指针,只调用一次析构函数。
其实delete操作本身就是做两件事:
(1)针对此处内存调用析构函数
(2)然后释放该处内存。
(详见《Effective C++》(2nd)item 5 或 (3rd)item 16)。

2.vc6.0和vs05中,delete和delete[]语句都是调用operator  delete(),在vs05中调试,如86楼‘sungoco2’兄所贴代码所显示,跟进operator delete()中,可看到有这样一句“_free_dbg( pUserData, pHead->nBlockUse );”而free要正确工作,有一个必要前提,即传给它的地址确实是当初申请的内存首地址,否则,会出现lihao2008123 所说的assert错误。这也就是我在24,25楼所提到的问题的表象解释。即在重载delete操作符后,某种情况下发现delete操作的指针地址不同于new操作所获取的地址。从而出现assert错误,程序崩溃。

3.那么什么情况下,二者操作的指针地址不匹配呢?
大家还记得那个前面讨论过的4字节吗?问题就在这里,且不说这4字节是干什么的,单说它的来源。经过反复实验测试,发现只要对象类型定义有显式析构函数,那么这个4字节就肯定存在。也就是说,哪怕是我们自定义的一个类类型对象,只要我们没有为这个类定义析构函数,那么这个4字节问题就不会出现;那么该用delete【】的时候,用delete也不会报错;那么该用delete的时候,用delete【】也不会报错,那么...
本论题,由于内置类型没有析构函数,而string有析构函数,这样一种巧合,而把问题局限在了内置类型与自定义类型这样的分类范围上。所以我们这个问题准确的说,应该是显式析构函数的存在影响了delete与delete【】的处理。

4.好,现在,我们可以思考这个4字节的来源了。其实,91楼的‘guyue6670 ’兄已经提到过,“差的这4个字节为一个隐藏的vtable指针”。相信拜读过Lippman的《Inside The C++ Object Model》的c/c++友们都对他在第1章中对c++对象模型的描述印象深刻。其中就将类定义的析构函数划归到虚表中,而对象本身保存的是指向该虚表的指针(一般情况下,指针本身是4字节的)。这样问题就解决了。(详见Stanley B.Lippman著的《Inside The C++ Object Model》)

附注:
为慎重起见,听取taodm大师之言,本人不敢保证以上解释能应用于所有厂家编译器的处理方式。仅在vc6.0,vs05,dev c++中测试。可能与编译器相关。

//****************************************************************************

#include "stdafx.h"
#include 
"Windows.h"

void* operator new[] (size_t size)
{
    
void *p=::HeapAlloc(::GetProcessHeap(),HEAP_ZERO_MEMORY,size);
    printf(
"new[]: %p; size: %u\n",p,size);
    
return p;
}
void operator delete[] (void* p)
{
    printf(
"delete[]: %p\n",p);
    ::HeapFree(::GetProcessHeap(),
0,p);
}

class A
{
public:
    
int i;
    A()
    {
        i
=0;
    }
    
~A(){}
};

class B
{
public:
    
int i;
    B()
    {
        i
=0;
    }
};

int main(int argc, char* argv[])
{
    
char *p=new char[10];
    printf(
"return: %p\n",p);
    delete [] p;
    A 
*a=new A[10];
    printf(
"return: %p\n",a);
    delete [] a;
    B 
*b=new B[10];
    printf(
"return: %p\n",b);
    delete [] b;
    
return 0;
}

输出如下:
new[]: 00142A90; size: 10
return: 00142A90
delete[]: 00142A90
new[]: 00142A90; size: 44
return: 00142A94
delete[]: 00142A90
new[]: 00142A90; size: 40
return: 00142A90
delete[]: 00142A90

//***********************************************************

class Two
{
    int a;
    public:
    Two( int b=0)
    {
        a = b;
    }
    ~Two( void )
    {
        a= 0;
    }
};


int main(int argc, char* argv[])
{
    char *p = new char;
    char *p1= new char[10];
    Two *p2 = new Two( 10 );
    Two *p3 = new Two[20];

    p1[0] = *p;

    delete p;
    delete p1;
    delete p2;
    delete p3;
    delete []p3;
    return 0;
}
汇编代码:
0041EF30  push        ebp
0041EF31  mov        ebp,esp
0041EF33  push        0FFh
0041EF35  push        offset __ehhandler$_main (00426f36)
0041EF3A  mov        eax,fs:[00000000]
0041EF40  push        eax
0041EF41  mov        dword ptr fs:[0],esp
0041EF48  sub        esp,9Ch
0041EF4E  push        ebx
0041EF4F  push        esi
0041EF50  push        edi
0041EF51  lea        edi,[ebp-0A8h]
0041EF57  mov        ecx,27h
0041EF5C  mov        eax,0CCCCCCCCh
0041EF61  rep stos    dword ptr [edi]

16:      char *p = new char;
0041EF63  push        1
0041EF65  call        operator new (0040ec40)
0041EF6A  add        esp,4
0041EF6D  mov        dword ptr [ebp-20h],eax
0041EF70  mov        eax,dword ptr [ebp-20h]
0041EF73  mov        dword ptr [ebp-10h],eax

17:      char *p1= new char[10];
0041EF76  push        0Ah
0041EF78  call        operator new (0040ec40)      调用new
0041EF7D  add        esp,4
0041EF80  mov        dword ptr [ebp-24h],eax
0041EF83  mov        ecx,dword ptr [ebp-24h]
0041EF86  mov        dword ptr [ebp-14h],ecx

18:      Two *p2 = new Two( 10 );
0041EF89  push        4
0041EF8B  call        operator new (0040ec40)      调用new
0041EF90  add        esp,4
0041EF93  mov        dword ptr [ebp-2Ch],eax
0041EF96  mov        dword ptr [ebp-4],0
0041EF9D  cmp        dword ptr [ebp-2Ch],0
0041EFA1  je          main+82h (0041efb2)
0041EFA3  push        0Ah
0041EFA5  mov        ecx,dword ptr [ebp-2Ch]
0041EFA8  call        @ILT+510(Two::Two) (00401203)    调用构造器
0041EFAD  mov        dword ptr [ebp-58h],eax
0041EFB0  jmp        main+89h (0041efb9)
0041EFB2  mov        dword ptr [ebp-58h],0
0041EFB9  mov        edx,dword ptr [ebp-58h]
0041EFBC  mov        dword ptr [ebp-28h],edx
0041EFBF  mov        dword ptr [ebp-4],0FFFFFFFFh
0041EFC6  mov        eax,dword ptr [ebp-28h]
0041EFC9  mov        dword ptr [ebp-18h],eax

19:      Two *p3 = new Two[20];
0041EFCC  push        54h
0041EFCE  call        operator new (0040ec40)
0041EFD3  add        esp,4
0041EFD6  mov        dword ptr [ebp-34h],eax
0041EFD9  mov        dword ptr [ebp-4],1
0041EFE0  cmp        dword ptr [ebp-34h],0
0041EFE4  je          main+0E4h (0041f014)
0041EFE6  push        offset @ILT+525(Two::~Two) (00401212)      保存了析构地址
0041EFEB  push        offset @ILT+505(Two::`default constructor closure') (004011fe)
0041EFF0  mov        ecx,dword ptr [ebp-34h]
0041EFF3  mov        dword ptr [ecx],14h
0041EFF9  push        14h
0041EFFB  push        4
0041EFFD  mov        edx,dword ptr [ebp-34h]
0041F000  add        edx,4
0041F003  push        edx
0041F004  call        `eh vector constructor iterator' (0041f1a0)  调用矢量构造迭代器
0041F009  mov        eax,dword ptr [ebp-34h]
0041F00C  add        eax,4
0041F00F  mov        dword ptr [ebp-5Ch],eax
0041F012  jmp        main+0EBh (0041f01b)
0041F014  mov        dword ptr [ebp-5Ch],0
0041F01B  mov        ecx,dword ptr [ebp-5Ch]
0041F01E  mov        dword ptr [ebp-30h],ecx
0041F021  mov        dword ptr [ebp-4],0FFFFFFFFh
0041F028  mov        edx,dword ptr [ebp-30h]
0041F02B  mov        dword ptr [ebp-1Ch],edx

20:
21:      p1[0] = *p;
0041F02E  mov        eax,dword ptr [ebp-14h]
0041F031  mov        ecx,dword ptr [ebp-10h]
0041F034  mov        dl,byte ptr [ecx]
0041F036  mov        byte ptr [eax],dl

22:
23:      delete p;
0041F038  mov        eax,dword ptr [ebp-10h]
0041F03B  mov        dword ptr [ebp-38h],eax
0041F03E  mov        ecx,dword ptr [ebp-38h]
0041F041  push        ecx
0041F042  call        operator delete (00409f20)    调用delete
0041F047  add        esp,4

24:      delete p1;
0041F04A  mov        edx,dword ptr [ebp-14h]
0041F04D  mov        dword ptr [ebp-3Ch],edx
0041F050  mov        eax,dword ptr [ebp-3Ch]
0041F053  push        eax
0041F054  call        operator delete (00409f20)  调用delete
0041F059  add        esp,4

25:      delete p2;
0041F05C  mov        ecx,dword ptr [ebp-18h]
0041F05F  mov        dword ptr [ebp-44h],ecx
0041F062  mov        edx,dword ptr [ebp-44h]
0041F065  mov        dword ptr [ebp-40h],edx
0041F068  cmp        dword ptr [ebp-40h],0
0041F06C  je          main+14Dh (0041f07d)
0041F06E  push        1
0041F070  mov        ecx,dword ptr [ebp-40h]
0041F073  call        @ILT+530(Two::`scalar deleting destructor') (00401217)    调用标量析构器
0041F078  mov        dword ptr [ebp-60h],eax
0041F07B  jmp        main+154h (0041f084)
0041F07D  mov        dword ptr [ebp-60h],0

26:      delete p3;
0041F084  mov        eax,dword ptr [ebp-1Ch]
0041F087  mov        dword ptr [ebp-4Ch],eax
0041F08A  mov        ecx,dword ptr [ebp-4Ch]
0041F08D  mov        dword ptr [ebp-48h],ecx
0041F090  cmp        dword ptr [ebp-48h],0
0041F094  je          main+175h (0041f0a5)
0041F096  push        1
0041F098  mov        ecx,dword ptr [ebp-48h]
0041F09B  call        @ILT+530(Two::`scalar deleting destructor') (00401217)  调用标量析构器
0041F0A0  mov        dword ptr [ebp-64h],eax
0041F0A3  jmp        main+17Ch (0041f0ac)
0041F0A5  mov        dword ptr [ebp-64h],0
 
27:      delete []p3;
0041F0AC  mov        edx,dword ptr [ebp-1Ch]
0041F0AF  mov        dword ptr [ebp-54h],edx
0041F0B2  mov        eax,dword ptr [ebp-54h]
0041F0B5  mov        dword ptr [ebp-50h],eax
0041F0B8  cmp        dword ptr [ebp-50h],0
0041F0BC  je          main+19Dh (0041f0cd)
0041F0BE  push        3
0041F0C0  mov        ecx,dword ptr [ebp-50h]
0041F0C3  call        @ILT+535(Two::`vector deleting destructor') (0040121c)  调用矢量析构器
0041F0C8  mov        dword ptr [ebp-68h],eax
0041F0CB  jmp        main+1A4h (0041f0d4)
0041F0CD  mov        dword ptr [ebp-68h],0

28:      return 0;
0041F0D4  xor        eax,eax

29:  }
0041F0D6  mov        ecx,dword ptr [ebp-0Ch]
0041F0D9  mov        dword ptr fs:[0],ecx
0041F0E0  pop        edi
0041F0E1  pop        esi
0041F0E2  pop        ebx
0041F0E3  add        esp,0A8h
0041F0E9  cmp        ebp,esp
0041F0EB  call        __chkesp (0040bfc0)
0041F0F0  mov        esp,ebp
0041F0F2  pop        ebp
0041F0F3  ret

原文地址:https://www.cnblogs.com/xuyuan77/p/1283957.html