delete 与 [ ] delete

johny_yg 2007-07-12 07:08:59
c++ primer 练习 14.11 中提到:
Account *parray=new Account[100];
delete parray;
delete [] parray;

方括号的存在会使编译器获取数组大小(size)然后析构函数再被依次应用在每个元素上,一共size次。否则,只有一个元素被析构。无论哪种情况,分配的全部空间被返还给自由存储区。

我的问题是: 为什么无论哪种情况,分配的全部空间被返还给自由存储区,对于 delete parray ,为什么不是删除单个 Account 元素,而是删除了100个,编译器怎么知道parray这个指针实际指向的是数组还是单个元素,即便知道指向的是自由存储区的数组,这个数组的大小又怎么知道。难道是编译器辅助行为?
...全文
1418 29 打赏 收藏 转发到动态 举报
写回复
用AI写文章
29 条回复
切换为时间正序
请发表友善的回复…
发表回复
taodm 2012-06-03
  • 打赏
  • 举报
回复
晕,又出考古队员了。
mars_man 2012-06-03
  • 打赏
  • 举报
回复
LZ已经成长到知其然,知其所以然的境界了。
IT互联网大叔 2012-06-02
  • 打赏
  • 举报
回复
http://blog.csdn.net/jiese1990/article/details/7625516
这篇博文里有,说介个问题....
IT互联网大叔 2012-06-02
  • 打赏
  • 举报
回复
http://blog.csdn.net/jiese1990/article/details/7625516
这篇博文里有,说介个问题....
lsmdiao0812 2008-04-14
  • 打赏
  • 举报
回复
强人真多
zsxcn 2008-04-14
  • 打赏
  • 举报
回复
mark
Leejun527 2008-04-13
  • 打赏
  • 举报
回复
mark
danny1221 2008-04-13
  • 打赏
  • 举报
回复
mark
clevercong 2007-07-13
  • 打赏
  • 举报
回复
学习了
晨星 2007-07-13
  • 打赏
  • 举报
回复
说得不准确。应该是运行时库必须想办法记住那块大小。
晨星 2007-07-13
  • 打赏
  • 举报
回复
难道是编译器辅助行为?

没错,就是。不同的编译器可能采用的具体方法有可能不一样,但不管采用什么方法,编译器必须记住那块大小。
奶糖人五号 2007-07-13
  • 打赏
  • 举报
回复
mark
deng2000 2007-07-12
  • 打赏
  • 举报
回复
正如Vitin所说,delete和delete[]都是依次作两件事:
(1) (一次或多次)调用析构函数.
(2) 释放对象(或对象数组)所占内存空间.
如果我没理解错的话,楼上几位的分析好象是说,选用delete或delete[]失当的话,第(1)步会有问题(漏调或多调了析构函数),但第(2)步是总会正确执行的.

但我的发现是,如果对象有非trivial析构函数,第(2)步也有问题! 而且后果不只是内存泻漏,还有可能会出现保护错. 分析如下:

C++中,通常情况下delete和delete[]语句都是调用operator delete(),后者再调用free(void *pr)完成. free(void *pr)能正常工作的前提是:传给它的指针参数pr值确实是当初申请的内存首地址.

粗看起来好象没问题,内存首地址不就是指向对象(或对象数组第一个元素)的指针吗?且慢,记得前面说的4字节日志吗(注意,如下的分析都假定对象类型有非trivial析构函数)?它的出现让事情变得复杂了.

首先,new[]的返回值(也就是数组第一个元素地址)不是申请的内存首地址,而是其后4字节,因为需要给日志留空间. 其次,delete[]传给free()的参数不是数组第一个元素地址,而是其之前4字节.因为它知道前面还有4字节日志空间.

看到问题所在了吗?如果new与delete[],或者new[]与delete配对,调用free()时其参数值与申请的内存地址相差4字节. 传给free()一个错误的内存地址会出现什么情况?最好结局是free()什么也不干:内存泻漏.更常见的结局是程序出现保护错.
以一个简短的例子验证我的结论.如下程序在VC下以debug方式编译后,运行时出现保护错.

class A
{
public:
~A() {};
public:
int n;
};

int main()
{
A* arr = new A[8];
delete arr;
return 0;
}
freshui 2007-07-12
  • 打赏
  • 举报
回复
都删了
但是前一个有99个没有调用析构函数
如果在类的构造函数中也申请了内存
则相应的99个中没有析构函数中释放内存
泄漏
北原狼 2007-07-12
  • 打赏
  • 举报
回复
我想这个问题需要涉及到编译器所能够识别的类型信息以及delete的实现原理。

对于
delete parray,编译器得到类型信息是Account单个的指针,那么释放时,只调用一次析构函数。

delete [] parray,编译器得到的类型信息是Account[]类型,则按照Account数组来处理,依次调用每个元素的析构函数。

注意,以上是在编译期间就确定下来的,编译器识别到类型信息的不同会决定调用析构函数的情况有不同。

但是对于内存释放,delete操作则不是通过类型信息来确定分配的内存大小,那么内存大小的信息从什么地方得到呢?

当我们使用 operator new 为一个自定义类型对象分配内存时,实际上我们得到的内存要比实际对象的内存大一些,这些内存除了要存储对象数据外,还需要记录这片内存的大小,此方法称为 cookie。这一点上的实现依据不同的编译器不同。(例如 MFC 选择在所分配内存的头部存储对象实际数据,而后面的部分存储边界标志和内存大小信息。g++ 则采用在所分配内存的头 4 个自己存储相关信息,而后面的内存存储对象实际数据。)当我们使用 delete operator 进行内存释放操作时,delete operator 就可以根据这些信息正确的释放指针所指向的内存块。

对于parray指针,可以根据这样的cookie信息来得到指向内存空间的大小,delete parray和delete parray[]都是一样的,同样一个指针,cookie信息是相同的,所以对应的内存都会被释放掉。但是由于编译器理解两种情况下的类型是不同的,所以调用析构函数会有不同。

希望以上能够回答你的疑问。
Vitin 2007-07-12
  • 打赏
  • 举报
回复
因为释放数组空间和为数组调用析构函数是两个独立的部分,可以使用不同的机制来实现。
释放空间的机制是需要绝对保证的。因此,即使你不写delete[] ,它也会将所有空间释放,其机制可以是前置的长度信息,也可以不是(如后置的特征分割符等等)。
而调用析构函数可以一般采用前置长度信息的方式(当然也可以有其他方式)。在没有[]提示时,编译器在调用析构就将它当一个元素,而不会使用数组方式来调用每一个析构函数了。

LS:“但是有个前提:对象类型(或其基类)有显式析构函数.换句话说,析构函数是非trivial的. 否则的话,数组前面是没有这个日志的.”——这说明,LS使用的编译器在释放数组空间时,并没有用前置的长度信息的方式。由此可见,释放数组空间和为数组调用析构函数确实可以使用不同的机制。
deng2000 2007-07-12
  • 打赏
  • 举报
回复
我在以前一个回贴(http://community.csdn.net/Expert/TopicView3.asp?id=5541588)中在VC下用汇编跟过delete[]的执行情况,发现这个"日志"就是一个4字节长的整数记录数组元素个数,紧挨在数组第一个元素之前. 但是有个前提:对象类型(或其基类)有显式析构函数.换句话说,析构函数是非trivial的. 否则的话,数组前面是没有这个日志的.其实对于析构函数是trivial的情况,delete[]时无需调用其析构函数,因此此时VC把delete[]当做delete同样处理.
jinwei1984 2007-07-12
  • 打赏
  • 举报
回复
mark
我啃 2007-07-12
  • 打赏
  • 举报
回复
一般在分配时分配器会自动写一个日志(一般在分配使用得内存之前又一个结构),用于记录分配的大小,分配内容的sizeof等等。
直观得想想,delete 和delete[]都是传入一个void *如果不保存日志就无法知道分配时到底是分配了一个还是多个单元
所以虽然delete 和delete[]不同但是分配器在执行释放过程中都会读取这个日志,从而了解到底应该释放多少内存,但是从程序员的角度来说,既然分配了数组,就应该用delete[]
yydrewdrew 2007-07-12
  • 打赏
  • 举报
回复
好像是存放数组parray地址前面内存区隐含存放了数组大小
加载更多回复(9)

64,660

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

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