C++小坑一枚:new和deletee未配对使用的后果

C++小坑一枚:new和deletee未配对使用的后果

作者:BlogUpdater |  时间:2020-11-22 |  浏览:1387 |  评论已关闭 条评论

蝎子
在之前的文章中,我提到过,如果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》

标签:

评论已关闭。