这篇文章是CSDN C++论坛中多次讨论到的一个问题。
先看下面程序:
#includeusing namespace std; #include int main() { int *p=new int[5]; //delete p; delete []p; p=NULL; //string *p=new string[5]; //delete p; //delete []p; //p=NULL; return 0; }
对于int类型和string类型,delete p和delete []p编译器(vc6.0)会有不一样的结果,对于int类型delete p和delete []p均可以编译运行,而 对于string类型,delete p运行
后程序会崩溃。
下面是对这个问题网友的意见与我的总结:
(1)delete 与delete[]都会释放所有内存,它们的不同点仅在于,根据得到的类型信息是单个指针还是指向数组的指针,来决定调用析构函数的次数。而编译器对所要删除的那个指针到底是指向的单一对象还是对象数组的判断依据就是"[]"的有无。若有'[]'编译器得到的类型信息就是指向数组的指针,然后调用多次析构函数;若没有‘[]’编译器得到的类型信息就是单个指针,只调用一次析构函数。
其实delete操作本身就是做两件事:<1> 针对此处内存调用析构函数<2> 然后释放该处内存。(详见《Effective C++》(2nd)item 5 或 (3rd)item 16)。
(2)vc6.0和vs05中,delete和delete[]语句都是调用operator delete(),在vs05中调试,在跟进operator delete()中,可看到有这样一句“_free_dbg( pUserData, pHead->nBlockUse );”而free要正确工作,有一个必要前提,即传给它的地址确实是当初申请的内存首地址,否则,会出现assert错误。即在重载delete操作符后,某种情况下发现delete操作的指针地址不同于new操作所获取的地址。从而出现assert错误,程序崩溃。
(3)在VC中,对于有显式析构函数的对象, 在分配数组时其前会有一个4字节前缀用来保存数组元素个数. 如果用delete来释放数组, 就会导致释放的内存地址与分配时的内存地址出现4字节偏差, 而导致灾难性的错误.
(4)那么什么情况下,二者操作的指针地址不匹配呢?经过反复实验测试,发现只要对象类型定义有显式析构函数,那么这个4字节就肯定存在。也就是说,哪怕是我们自定义的一个类类型对象,只要我们没有为这个类定义析构函数,那么这个4字节问题就不会出现;那么该用delete[]的时候,用delete也不会报错;那么该用delete的时候,用delete[]也不会报错,那么...本论题,由于内置类型没有析构函数,而string有析构函数,这样一种巧合,而把问题局限在了内置类型与自定义类型这样的分类范围上。所以我们这个问题准确的说,应该是显式析构函数的存在影响了delete与delete[]的处理。
(5)一句话:该用delete[]时莫省略,这是C++标准。
原帖如下:
(1)
(2)
(3)