C++小坑一枚:new和deletee未配对使用的后果
蝎子
在之前的文章中,我提到过,如果new和delete这两条指令没有准确地配对使用,则可能会造成一些麻烦,那么,今天就来详细说道说道。
网上有这样一篇文章《C++ Gotchas: Avoiding Common Problems in Coding and Design》,里面有一个掌节《Failure to Distinguish Scalar and Array Allocation》我建议你在阅读下面的文章之前先看看,因为我会引用里面提到的一些内容。
微软C++编译器是这样来处理内存的矢量分配的。请注意,这里讨论的都是内部的实现细节,所以将来可能会发生改变,但是理解里面的工作原理,有助于理解为什么错误的使用new和delete是一件糟糕的事情。
实现细节
重要的是,当你使用标量化的”delete p”时,你实际上是在告诉编译器:指针p是指向的是一个单一的对象。编译器仅仅会调用析构器一次,对于该对象而言,它即将进行析构了。
当你调用矢量化的”delete [] p”时,你是在跟编译器说:指针p指向了一组对象,但是我不能告诉你对象的数量。在这种情况下,编译器需要生成额外的代码来记录它到底需要析构多少个对象。这个所谓的额外信息是存放在调用”new []”进行内存分配时创建的一个安全的地方。
我们来看看下面这个例子:
编译器对allocate_stuff函数生成汇编代码如下:
将上面的汇编代码转换为伪C++代码,如下所示:
换句话说,MyClass对象数组的内存布局,如下所示:
new[]操作符返回的指针不是已分配内存的起始位置,而是一个指向MyClass[0]的指针。分配对象的数量信息实际上保存在数组前面的隐藏空间。
让我们看看下面的代码:
void free_stuff(MyClass* p)
{
delete[] p;
}
它生成的汇编代码如下:
然后,我们再将汇编转为伪C++代码,如下所示:
void free_stuff(MyClass* p)
{
if (p) p->vector deleting destructor(3);
}
对于数组对象的析构流程,类似下图:
数组的析构操作会用到一些标志位,如果2被设置,则数组会被析构,否则,只有单个对象被析构。如果1被设置,则内存空间也会被释放。
在我们的例子中,标志位被设置为,因此我们将执行一个数组析构并释放内存空间。请注意,本来用于存储对象数量的隐藏区域会被释放,并且数组析构器会在内存空间被释放之前执行非常多次的析构操作。
所以,现在你应该也有点感觉了:使用new分配内存空间,并使用delete []来释放将会给你带来麻烦。
课后练习题
如果对象的析构函数MyClass::~MyClass()从类定义中被移除,可以有哪些方式来对代码进行优化?
总结
记住一点就够:用new必须用delete,用new[]必须用delete[]。
也算是C++的一个小坑吧。
最后
Raymond Chen的《The Old New Thing》是我非常喜欢的博客之一,里面有很多关于Windows的小知识,对于广大Windows平台开发者来说,确实十分有帮助。
本文来自:《Mismatching scalar and vector new and delete》
相关推荐
- 对话框模板简史-32位扩展模板
- Posted on 04月17日
- 对话框管理器第一章:先热热身
- Posted on 07月21日
- PAE真的能提升虚拟地址空间吗
- Posted on 09月18日
- 如何将一个 HRESULT 转换为 Win32 错误码?
- Posted on 11月11日
评论已关闭。