delete与delete[]在此版中已是老生常谈的问题了,隔三差五就要冒出来一次, 楼上几位也已似说得很清楚,本不应再啰嗦.不过俺觉得这回楼主的问题颇有新意(此段代码似是高人所为,呵呵),不禁想凑热闹聊几句.
首先,delete与delete[]混用的后果不只是楼上几位说的内存泄漏(否则如何解释runtime error). 事实上, C++标准5.3.5/2说得很清楚:
...In the first alternative (delete object), the value of the operand of delete shall be a pointer to a non-array object or a pointer to a sub-object (1.8) representing a base class of such an object (clause 10). If not, the behavior is undefined. In the second alternative (delete array), the value of the operand of delete shall be the pointer value which resulted from a previous array new-expression.72) If not, the behavior is undefined.
注意后果是很恐怖的"undefined". 如果你混用, 谁也不能保证会是什么结果(与对象类型有关,更重要的,与编译器有关). 幸运的话,一切正常, 稍不幸的,内存泄漏. 最不幸的情况给你个runtime error导致程序终止也不能怨它.
在标准C++的范畴就只能说到此. 实际工作中知道这些也完全够用了. 如果象楼主要细究runtime error的原因,就不能不涉及与编译器有关的内容. 以下讨论局限于VC debug模式(VC6.0, VS2003, VS2005). 我猜测楼主的编译环境也是VC,不知对否.
在VC中,对于有显式析构函数的对象, 在分配数组时其前会有一个4字节前缀用来保存数组元素个数. 如果用delete来释放数组, 就会导致释放的内存地址与分配时的内存地址出现4字节偏差, 而导致程序挂掉. 详情可参考 http://topic.csdn.net/u/20070712/07/57c7cfc6-7314-400d-86d2-230a72581ea5.html 此程序runtime error的原因就在于此4字节前缀.
先看第一个runtime error的地方 CTest* t =(CTest*) new CTest2[2]; delete [] t; 先表明一下态度,呵呵.在实际中把CTest2数组cast成一个毫无关系的CTest数组是毫无意义且非常危险的.
再分析一下这样作有没有什么直接后果.注意CTest和CTest2都是有(显式)析构函数的.析构函数体是空的,因此调用析构函数时(即使对象指针是错的)倒不会出错.
因为CTest2有析构函数,故为数组t申请的内存开始地址是&t[0]前4字节. 而在delete []t时,编译器以为t是CTest数组,它注意到CTest有析构函数,故释放内存时调用free(p)的参数值也是&t[0]前4字节,很幸运,通过了.
而如果把delete []t换成delete t,编译器不再理会那4字节前缀,而直接用&t[0]的值调用free(p). 因此,释放的内存指针与申请时相差4字节! 在VC debug版中,为了便于用户发现bug,凡是以错误地址调用free(p)的都会触发Assertion.因此,runtime error发生了.
第二个runtime error也类似: CTest* t =(CTest*) new int[2]; delete t; int型是没有析构函数的.因此数组t申请的内存开始地址就是&t[0]. delete t直接以&t[0]为参数值调用free(p), 正常通过.
而如果把delete t换成delete []t, 编译器看到CTest是有析购函数的,因此它以为数组t申请的内存开始地址是&t[0]前4字节,而实际上不是这样.因此,又发生runtime error了.
以一个小实验结束讨论. 不增减[],而注释掉CTest的析构函数,也一样导致runtime error. 有兴趣的可以试试. |