关于delete p和delete []p的区别所在

Featured 2005-11-22 10:07:07
(问题参见:http://community.csdn.net/Expert/topic/4402/4402653.xml)

在VC下,对于char int之类的内建数据类型,在debug下效果的确相同,也就是
char *p=new char[10];
delete p;跟delete []p;效果相同。(可能因为#define new DEBUG_NEW这句话)
但在Release下,
前者存在一定问题,内存貌似并没有被回收。(这个问题还需要进一步测试验证。)

对于非内建类型,正如Mackz指出:
“delete只会调用一次析构函数,而delete[]会调用每一个成员的析构函数。”
在MoreEffectiveC++中有更为详细的解释:
“当delete操作符用于数组时,它为每个数组元素调用析构函数,然后调用operator delete来释放内存。”

在其他编译器上没有测试。
因此,出于谨慎考虑,还是用delete[]来释放比较好。
至少看上去可读性更好一点。

望一起讨论的几位哥们进来领分。
...全文
1298 46 打赏 收藏 转发到动态 举报
写回复
用AI写文章
46 条回复
切换为时间正序
请发表友善的回复…
发表回复
liumang9527 2006-01-22
  • 打赏
  • 举报
回复
mark
pomelowu 2006-01-21
  • 打赏
  • 举报
回复
to 楼上,尽信书不如无书。
另,真理越辩越明
wd_6532 2006-01-21
  • 打赏
  • 举报
回复
有结论没有?

delete m_aryobj

到底是只删除第一个元素啊
还是删除第一个元素然后调用它的析构函数
还是 只删除所有的元素,但是不调用析构函数

如果是删除第一个元素然后调用它的析构函数

我不明白这么多 星星 在这儿讨论这个问题的原因了。
按理说,这是一个比较明显的问题,任何c++书籍都有分析。
AvalonFX 2005-12-21
  • 打赏
  • 举报
回复
喔,sorry,下次补上。要补就补100分!
Seu_why 2005-12-14
  • 打赏
  • 举报
回复
收藏
dingpiao 2005-12-14
  • 打赏
  • 举报
回复
学习&&接分
krfstudio 2005-12-14
  • 打赏
  • 举报
回复
MARK
pomelowu 2005-12-14
  • 打赏
  • 举报
回复
寒……我也参与了讨论滴……
Featured 2005-12-14
  • 打赏
  • 举报
回复
出于承诺,分数都给了参与QQ群内讨论的哥们。
StarLee 2005-12-14
  • 打赏
  • 举报
回复
我来抢分!
双杯献酒 2005-12-14
  • 打赏
  • 举报
回复
new 对应用 delete
new[] 对应用 的delete[]
一般而言 delete 和 delete[] 的区别仅仅在于调用析构函数的次数不同,
而不管是delete,或者delete[],内存都会全部收回。
所以对于对析构函数没有特别处理的对象,用delete和delete[] 不会产生区别。

但是,强烈建议使用标准的方法。

但是,这里有另外一个问题,可能使问题复杂化了,
那就是,new,delete,new[],delete[]都是可以重载的运算符,
可以合理假设,重载运算符实现者会自然保证new和delete,new[]和delete[]正确匹配的情况下正常工作。但是,如果使用者不是严格的配对使用,则结果就真正象C++标准所言,是"未定义的"。
anothervip 2005-12-14
  • 打赏
  • 举报
回复
mark
kelinwang19 2005-11-24
  • 打赏
  • 举报
回复
我觉得delete p只是释放这块内存的首地址,而delete[] p是释放整个内存。所以我觉得如果p是指向数组的指针的话,最好用delete[] p
alen_ghl 2005-11-24
  • 打赏
  • 举报
回复
这个问题讨论好久了
可还是没怎么搞懂原理,反正一般都
new [] 就 delete [] 不省[]这个事
菜牛 2005-11-24
  • 打赏
  • 举报
回复
有兴趣再看看生成的汇编

源代码:

MemTest *mTest1 = new MemTest[20];
MemTest *mTest2 = new MemTest;
int *pInt1 = new int[10];
int *pInt2 = new int;

delete []pInt1; //-1-
delete pInt2; //-2-
delete []mTest1; //-3-
delete mTest2; //-4-

=====================================================

Debug生成的:

; 20 : MemTest *mTest1 = new MemTest[20];

push 164 ; 000000a4H
call ??_U@YAPAXI@Z ; operator new[]
add esp, 4
mov DWORD PTR $T11544[ebp], eax
mov DWORD PTR __$EHRec$[ebp+8], 0
cmp DWORD PTR $T11544[ebp], 0
je SHORT $L11545
mov eax, DWORD PTR $T11544[ebp]
mov DWORD PTR [eax], 20 ; 00000014H
push OFFSET FLAT:??1MemTest@@QAE@XZ ; MemTest::~MemTest
push OFFSET FLAT:??0MemTest@@QAE@XZ ; MemTest::MemTest
push 20 ; 00000014H
push 8
mov ecx, DWORD PTR $T11544[ebp]
add ecx, 4
push ecx
call ??_L@YGXPAXIHP6EX0@Z1@Z
mov edx, DWORD PTR $T11544[ebp]
add edx, 4
mov DWORD PTR tv76[ebp], edx
jmp SHORT $L11546
$L11545:
mov DWORD PTR tv76[ebp], 0
$L11546:
mov eax, DWORD PTR tv76[ebp]
mov DWORD PTR $T11543[ebp], eax
mov DWORD PTR __$EHRec$[ebp+8], -1
mov ecx, DWORD PTR $T11543[ebp]
mov DWORD PTR _mTest1$[ebp], ecx

; 21 : MemTest *mTest2 = new MemTest;

push 8
call ??2@YAPAXI@Z ; operator new
add esp, 4
mov DWORD PTR $T11548[ebp], eax
mov DWORD PTR __$EHRec$[ebp+8], 1
cmp DWORD PTR $T11548[ebp], 0
je SHORT $L11549
mov ecx, DWORD PTR $T11548[ebp]
call ??0MemTest@@QAE@XZ ; MemTest::MemTest
mov DWORD PTR tv83[ebp], eax
jmp SHORT $L11550
$L11549:
mov DWORD PTR tv83[ebp], 0
$L11550:
mov eax, DWORD PTR tv83[ebp]
mov DWORD PTR $T11547[ebp], eax
mov DWORD PTR __$EHRec$[ebp+8], -1
mov ecx, DWORD PTR $T11547[ebp]
mov DWORD PTR _mTest2$[ebp], ecx

; 22 : int *pInt1 = new int[10];

push 40 ; 00000028H
call ??_U@YAPAXI@Z ; operator new[]
add esp, 4
mov DWORD PTR $T11551[ebp], eax
mov eax, DWORD PTR $T11551[ebp]
mov DWORD PTR _pInt1$[ebp], eax

; 23 : int *pInt2 = new int;

push 4
call ??2@YAPAXI@Z ; operator new
add esp, 4
mov DWORD PTR $T11552[ebp], eax
mov eax, DWORD PTR $T11552[ebp]
mov DWORD PTR _pInt2$[ebp], eax

; 24 :
; 25 : delete []pInt1; //-1-

mov eax, DWORD PTR _pInt1$[ebp]
mov DWORD PTR $T11553[ebp], eax
mov ecx, DWORD PTR $T11553[ebp]
push ecx
call ??_V@YAXPAX@Z ; operator delete[]
add esp, 4

; 26 : delete pInt2; //-2-

mov eax, DWORD PTR _pInt2$[ebp]
mov DWORD PTR $T11554[ebp], eax
mov ecx, DWORD PTR $T11554[ebp]
push ecx
call ??3@YAXPAX@Z ; operator delete
add esp, 4

; 27 : delete []mTest1; //-3-

mov eax, DWORD PTR _mTest1$[ebp]
mov DWORD PTR $T11556[ebp], eax
mov ecx, DWORD PTR $T11556[ebp]
mov DWORD PTR $T11555[ebp], ecx
cmp DWORD PTR $T11555[ebp], 0
je SHORT $L11557
push 3
mov ecx, DWORD PTR $T11555[ebp]
call ??_EMemTest@@QAEPAXI@Z
mov DWORD PTR tv93[ebp], eax
jmp SHORT $L11558
$L11557:
mov DWORD PTR tv93[ebp], 0
$L11558:

; 28 : delete mTest2; //-4-

mov eax, DWORD PTR _mTest2$[ebp]
mov DWORD PTR $T11560[ebp], eax
mov ecx, DWORD PTR $T11560[ebp]
mov DWORD PTR $T11559[ebp], ecx
cmp DWORD PTR $T11559[ebp], 0
je SHORT $L11561
push 1
mov ecx, DWORD PTR $T11559[ebp]
call ??_GMemTest@@QAEPAXI@Z
mov DWORD PTR tv130[ebp], eax
jmp SHORT $L11562
$L11561:
mov DWORD PTR tv130[ebp], 0
$L11562:

==================================================

Release生成的:

; 20 : MemTest *mTest1 = new MemTest[20];

push 164 ; 000000a4H
call ??_U@YAPAXI@Z ; operator new[]
add esp, 4
mov DWORD PTR $T9726[esp+24], eax
xor ebp, ebp
cmp eax, ebp
mov DWORD PTR __$EHRec$[esp+32], ebp
je SHORT $L9727
push OFFSET FLAT:??1MemTest@@QAE@XZ ; MemTest::~MemTest
push OFFSET FLAT:??0MemTest@@QAE@XZ ; MemTest::MemTest
push 20 ; 00000014H
lea esi, DWORD PTR [eax+4]
push 8
push esi
mov DWORD PTR [eax], 20 ; 00000014H
call ??_L@YGXPAXIHP6EX0@Z1@Z
jmp SHORT $L9728
$L9727:
xor esi, esi
$L9728:

; 21 : MemTest *mTest2 = new MemTest;

push 8
mov DWORD PTR __$EHRec$[esp+36], -1
call ??2@YAPAXI@Z ; operator new
add esp, 4
cmp eax, ebp
je SHORT $L9732
mov DWORD PTR [eax+4], ebp
mov ebp, eax
$L9732:
push ebx
push edi

; 22 : int *pInt1 = new int[10];

push 40 ; 00000028H
call ??_U@YAPAXI@Z ; operator new[]

; 23 : int *pInt2 = new int;

push 4
mov edi, eax
call ??2@YAPAXI@Z ; operator new

; 24 :
; 25 : delete []pInt1; //-1-

push edi
mov ebx, eax
call ??_V@YAXPAX@Z ; operator delete[]

; 26 : delete pInt2; //-2-

push ebx
call ??3@YAXPAX@Z ; operator delete
add esp, 16 ; 00000010H

; 27 : delete []mTest1; //-3-

test esi, esi
je SHORT $L9739
mov eax, DWORD PTR [esi-4]
push OFFSET FLAT:??1MemTest@@QAE@XZ ; MemTest::~MemTest
lea edi, DWORD PTR [esi-4]
push eax
push 8
push esi
call ??_M@YGXPAXIHP6EX0@Z@Z
push edi
call ??_V@YAXPAX@Z ; operator delete[]
add esp, 4
$L9739:

; 28 : delete mTest2; //-4-

test ebp, ebp
pop edi
pop ebx
je SHORT $L9785
mov eax, DWORD PTR [ebp+4]
test eax, eax
je SHORT $L9778
push eax
call ??_V@YAXPAX@Z ; operator delete[]
add esp, 4
$L9778:
push ebp
call ??3@YAXPAX@Z ; operator delete
add esp, 4
$L9785:
fireinsky 2005-11-24
  • 打赏
  • 举报
回复
好东西,学习!
菜牛 2005-11-24
  • 打赏
  • 举报
回复
再回过头来看,到底编译器在内建简单类型和自定义复杂类型的内存分配上有什么不同?
MemTest *mTest1 = new MemTest[20];
MemTest *mTest2 = new MemTest;
int *pInt1 = new int[10];
int *pInt2 = new int;

cout << *((int*)mTest1 - 1) << endl;
cout << *((int*)mTest2 - 1) << endl;
cout << *((int*)pInt1 - 1) << endl;
cout << *((int*)pInt2 - 1) << endl;

delete []pInt1; //-1-
delete pInt2; //-2-
delete []mTest1; //-3-
delete mTest2; //-4-
在Release下的运行结果:
20
524786
524784
786938
在DEBUG下的运行结果:
20
-33686019
-33686019
-33686019
可见:自定义复杂类型在分配内存的时候,编译器保留了数组大小的信息。这就使调用delete[]时有章可循。
菜牛 2005-11-24
  • 打赏
  • 举报
回复
我们知道,在DEBUG下面,编译器使用了不同的分配/释放内存机制,重载了new/delete操作符,因此会出现在DEBUG下面运行会报错,在Release下面不会报错。(很多问DEBUG和Release版本区别的注意了)。有兴趣的可以看看CRT源码,在VS的安装目录下面有,我的在:
$(VCInstallDir)crt\src
这个CRT源码可是个好东西,凡是搞不清楚原理的,请仔细好好看看。
菜牛 2005-11-24
  • 打赏
  • 举报
回复
我们来看一个试验:

// Memory.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"

class MemTest
{
int nTest;
char *pTest;
public:
MemTest() : pTest(NULL) { };
MemTest(int nSize) : nTest(nSize) { pTest = new char[nTest]; };
~MemTest() { if (pTest) delete []pTest;};
};

int _tmain(int argc, _TCHAR* argv[])
{
MemTest *mTest = new MemTest[10];
int *pInt = new int[10];
delete pInt; //-1-
delete mTest; //-2-

return 0;
}

在Release下面编译运行都不报错;在DEBUG下面,编译不报错,运行出错。定位错误在:
void operator delete(
void *pUserData
)
{
_CrtMemBlockHeader * pHead;

RTCCALLBACK(_RTC_Free_hook, (pUserData, 0));

if (pUserData == NULL)
return;

_mlock(_HEAP_LOCK); /* block other threads */
__TRY

/* get a pointer to memory block header */
pHead = pHdr(pUserData);

/* verify block type */
_ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse)); <----在这里报错

_free_dbg( pUserData, pHead->nBlockUse );

__FINALLY
_munlock(_HEAP_LOCK); /* release other threads */
__END_TRY_FINALLY

return;
}


如果改成:
delete []mTest; //-2-
则没有错误。

再看:
MemTest *mTest1 = new MemTest[10];
MemTest *mTest2 = new MemTest;
int *pInt1 = new int[10];
int *pInt2 = new int;

delete []pInt1; //-1-
delete []pInt2; //-2-
delete []mTest1; //-3-
delete []mTest2; //-4-
在-4-处报错。


这就说明:对于内建简单数据类型,delete和delete[]功能是相同的。对于自定义的复杂数据类型,delete和delete[]不能互用。

(再次重申:仅为了试验这么使用,编程中还是要遵守new/delete和new[]/delete[]配对使用的原则,不管是不是简单数据类型)
freemme 2005-11-24
  • 打赏
  • 举报
回复
强调用delete对应new,用delete[]对应new[],原因有几个:
1. 谨慎性。当你释放内存时,可能会疏忽当初是用内建类型还是自定义类型来分配内存。
2. 移植性。如果这样处理,移植性可能会好些。
3. 美观。对称性好。^_^

因为我曾经在这方面吃过亏,这算是一点经验总结吧,提供出来一起讨论!
加载更多回复(26)

16,472

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC相关问题讨论
社区管理员
  • 基础类社区
  • Web++
  • encoderlee
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

        VC/MFC社区版块或许是CSDN最“古老”的版块了,记忆之中,与CSDN的年龄几乎差不多。随着时间的推移,MFC技术渐渐的偏离了开发主流,若干年之后的今天,当我们面对着微软的这个经典之笔,内心充满着敬意,那些曾经的记忆,可以说代表着二十年前曾经的辉煌……
        向经典致敬,或许是老一代程序员内心里面难以释怀的感受。互联网大行其道的今天,我们期待着MFC技术能够恢复其曾经的辉煌,或许这个期待会永远成为一种“梦想”,或许一切皆有可能……
        我们希望这个版块可以很好的适配Web时代,期待更好的互联网技术能够使得MFC技术框架得以重现活力,……

试试用AI创作助手写篇文章吧