内存泄露----关于内置对象和类对象的delete与delete【】问题

hu7100 2008-08-26 04:42:05
加精
#include <string>
using namespace std;

int main()
{
int *i_p = new int[5];
delete i_p; //1
// delete []i_p; //2

string *s_p = new string[5];
// delete s_p; //3
delete []s_p; //4

return 0;
}
/**症状:*/
上述代码,在vc6环境,debug运行下,第1与2行的效果是一样的,通过检测,均没有造成内存泄露;而第3与4行的效果则大相径庭。采用第3行,编译连接通过,但执行出错。第4行为正解。

/**试问:*/
对于内建类型,用delete【】和delete效果相同,系统会自动删除所有;而对于类类型,二者不同可能引起内存泄露。请问编译器在
此问题上如何处理的,它做了些什么?

/**附注:*/
上述所谓检测手段,分别采用了5种方法来检测,症状均类似。其中包括MSDN中提到的“#ifdef _DEBUG
CMemoryState oldMem, newMem, diffMem;
oldMem.Checkpoint();
......
newMem.Checkpoint();
diffMem.Difference( oldMem, newMem )
......”
...全文
5204 174 打赏 收藏 转发到动态 举报
写回复
用AI写文章
174 条回复
切换为时间正序
请发表友善的回复…
发表回复
debugeeker 2012-10-31
  • 打赏
  • 举报
回复
[Quote=引用 10 楼 的回复:]

可能是这样吧,new/delete对内置类型和类的处理方式不一样
因为以前重载过new[],它返回的指针和分配的指针是差了4个字节的,好像
那四个字节是记录个数的,但是对于内置数据类型就不是
所以不会异常,因为为win32堆释放内存的时候和分配的时候指针值是一样的
但对于string,就不一样了,差了4个字节,所以会失败
[/Quote]
是8个字节。第一个4字节记录着本块大小,前一块的大小,第二个4字节记录着这块内存的状态。
  • 打赏
  • 举报
回复
标记,到现在我都不知道,看来还要跟代码啊
airwolf1216 2012-07-24
  • 打赏
  • 举报
回复
留个言 晚上回家好好研究 这是好帖子
airwolf1216 2012-07-24
  • 打赏
  • 举报
回复
回头研究下 很好的帖子
壹只皮卡丘 2012-07-18
  • 打赏
  • 举报
回复
不懂啊
sada09 2012-07-12
  • 打赏
  • 举报
回复
笔试面试常考题。
sheldenwade1 2012-04-04
  • 打赏
  • 举报
回复
好多年前的帖子。自己发表一下自己的看法吧
先说几点基础的,
默认的构造函数和默认的析构函数是个什么样子的?
默认的构造函数里面实际就是分配了内存。
默认的析构函数里面实际就是释放了占用的内存,其他啥也没干了。
那么析构函数里面是怎么知道要释放多少内存的呢?就烦请大家去看看操作系统里的内存是怎么管理的,大致意思是:操作系统维护了一个内存分配表,指示内存的哪块用了,例如0x1000地址开始的100个字节是已经使用的内存,大概记录这种东西。释放内存的时候呢,必须一块一块的删除,例如0x1000开始的内存,不能从0x1001开始删除,必须从0x1000开始删除,因为free()或者delete函数都是给出的内存首地址,然后根据占用长度进行删除。就是这样的。举个例子,free(0x1000),首先要去操作系统的这个内存维护表里去找索引0x1000这个块,如果找到了,就说明给定的这个首地址是正确的,而且正使用着呢,于是就执行释放内存操作,实际就是把这一项从内存维护表里删除,至于操作系统做的其他的操作,大家去查查别的资料。再例如,free(0x1001),如果在内存维护表里面没有找到0x1001的已经使用的项,但存在某个内存使用块的内存范围里包含0x1001,例如(0x1000,100),这个范围就包含了0x1001,那么free操作就会出现运行时错误。如果出现的情况也不属于上面两种情况,free()实际上就什么也不做,也不会出现运行时错误,就返回了。
知道了上面这些,我们继续下面的讨论。
为什么带有析构函数的类,在创建对象数组时,会多出四个字节?为什么不带有析构函数的类,在创建对象数组时,就不需要这个四个字节呢?
因为带有析构函数的类的对象数组,在使用delete[] ptr时需要知道,到底需要执行多少次析构函数,然后再执行释放内存的操作。那么执行析构函数的次数,这个值就放到了申请空间的最最前面的四个字节,紧跟这个四个字节的才是实际的对象数组。但这个时候,编译器做了一下处理,因为这四个字节不属于任何对象,只是编译器知道就好了,因此返回对象数组的指针的时候,不是实际申请的内存空间,而是返回的跳过了开始的四个字节的地址。我在一个地方看到的编译器cookie的概念就包含这四个字节。
而没有析构函数的类的对象数组,它不需要知道要执行多少次析构函数,尽管!尽管课本上都有说有一个默认的析构函数。如果大家看过反汇编代码的话就知道了,这个默认析构函数里面什么都没有,甚至有些编译器直接处理成没有,只是逻辑上存在。因此C++编译器们在处理delete[]的时候,都选择了根本就不执行类的默认析构函数,而只需要执行释放内存的操作。因此它不需要这四个字节。
那么我们接着分析下面的问题就简单多了。
delete i_p; 没有析构函数,仅释放空间
delete []i_p; 没有析构函数,仅释放空间
大家读了上面的如何释放空间之后,就知道,这俩是一样的,但是也跟不同的编译器有关系,有些编译器就要求new[]的东西必须使用delete[]释放,比如VC++2010编译器的默认build选项就会报运行时错误。
string是带有析构函数的
delete s_p; //执行了一次s_p[0]对象的析构函数,析构函数中释放s_p[0]的空间,然后s_p数组的5个内存空间释放了。
delete[] s_p; //执行n次析构函数,每次执行析构函数就释放每个string对象的空间,最后s_p数组的5个内存空间释放了。
这样差异就很明显了,这个地方处理,大多数编译器都一样的,我还没见过不一样的。
讨论就到这里了,我试过的编译器里面VC++2010是对ISO C++ 98标准支持最好的,有人说g++设置一下build选项也可以完全支持98标准,我只想说,g++的内存管理方面还有待改善。很多时候即使使用了delete[],仍然可以使用一些释放的空间,这会让人摸不着头脚的
spangli 2012-03-08
  • 打赏
  • 举报
回复
mark
快乐的小菜鸟 2011-09-26
  • 打赏
  • 举报
回复
学习了 顶一下
ljq550000 2011-07-16
  • 打赏
  • 举报
回复
[Quote=引用 1 楼 akirya 的回复:]

delete加括号的目的也就是调用数组中每个元素的析构函数.
如果不加[]就认为是1个
[/Quote]


学习了
cwh5635 2011-07-15
  • 打赏
  • 举报
回复
对于单个对象,delete与delete []确实效果一样。。。。虽然一般人不会这么写,会误解的哦~
对于一次连续分配的多个对象,显然是不一样的。。。用delete会泄露的哦,VS一次内存判断错误就会触发assert
再来一个,内存检查是依据实现版本。。。。
三岔口 2011-07-14
  • 打赏
  • 举报
回复
看完楼上的各位的讨论,我受益匪浅啊,嘻嘻。。。
a403635976 2011-02-23
  • 打赏
  • 举报
回复
正确结果应该是:
delete[] i_p;
delete[] s_p;
因为都是数组,只不过是不同类型数组罢了
sk_sakula 2011-02-18
  • 打赏
  • 举报
回复
[Quote=引用 1 楼 akirya 的回复:]

delete加括号的目的也就是调用数组中每个元素的析构函数.
如果不加[]就认为是1个
[/Quote]
正解
Tim_Chase 2011-02-01
  • 打赏
  • 举报
回复
学习了,以后再编码时要注意了。
  • 打赏
  • 举报
回复
C++primer,2.2节原文

分配动态数组时一个常令人迷惑的问题是返回值只是一个指针与分配单一动态对象
的返回类型相同例如pint 与pia 的不同之处在于pia 拥有四元素数组的第一个元素的地
址而pint 只是简单地包含单一对象的地址当用完了动态分配的对象或对象的数组时我
们必须显式地释放这些内存我们可以通过使用delete 表达式的两个版本之一来完成这件事
情而释放之后的内存则可以被程序重新使用单一对象的delete 表达式形式如下
// 删除单个对象
delete pint;
数组形式的delete 表达式如下
// 删除一个对象数组
delete [] pia;
江南烟雨梦 2010-12-14
  • 打赏
  • 举报
回复
char *p = new char[10];
delete []p释放整个数组,不管是类类型,还是内置类型;
delete p释放数组的第一个元素所占的内存,仅仅是第一个!
ronghuaqiu 2010-11-22
  • 打赏
  • 举报
回复
前来学习学习,是容易遇到的问题
yaneng 2010-11-07
  • 打赏
  • 举报
回复
谢谢two_ears,对内存分配理解又加深了
如果是内置类型或者没有显式的析构函数,当[]申请时,申请的内存会包括在一个内存边界内,如[AAAA][申请的内存][AAAA],此时delete时,有没有[]效果都是一样的。
如果是有显式的析构函数的类,当[]申请时,申请的内存会除了会包括在一个内存边界内,还会多出四个字节用于表示申请的数量,如[AAAA][NUM][申请的内存][AAAA],此时delete时,不加[]系统释放时就会找不到内存边界,当然要报错了。
cloud_xu 2010-10-24
  • 打赏
  • 举报
回复
进来学习
加载更多回复(153)

64,662

社区成员

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

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