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》
相关推荐
- 第 79 期:微软推出新工具助力用户升级至 Windows 11
- Posted on 06月17日
- 第 271 期:Windows 11 的 Chrome 浏览器正在进行 Mica 设计的改版
- Posted on 10月27日
- 为什么有些人喜欢管任务栏叫”托盘(Tray)”?
- Posted on 03月25日
- 小技巧:VS2010中添加控件成员变量没有反应的问题
- Posted on 07月19日



评论已关闭。