续杯版:new和deletee未配对使用的后果

续杯版:new和deletee未配对使用的后果

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

各位看官,接上回

问题:如果你使用了new分配内存,但是使用了delete[]来释放它,会发生什么事情?
答案:new将分配没有隐藏计数器的单个对象,而delete[]将查找它认为存在实际上却不存在的隐藏计数器,因此程序将发生崩溃(因为访问了不存在的内存)或获取随机数并尝试破坏其他内存空间。
如果此随机数大于1,则将在对象之后开始破坏内存。
如果随机数为零,则不会破坏任何内容。
如果随机数恰好是1,则将破坏一个对象的内存。

接下来,delete[]将尝试释放实际存储块前面一个size_t处的存储块。根据堆内存释放的原理,此参数可能会被检测为无效参数并被忽略,否则可能导致堆内存被破坏。

所以,结论是:这样做的结果不会太好。

问题:如果我使用new[]来分配内存,而使用delete来释放,又会如何?
答案:new[]将会分配多个对象,并将对象数目存储在隐藏的计数器中。delete破坏向量中的第一个对象。如果它是零个对象的向量,则会导致内存损坏。
如果它是两个或更多对象的向量,则不会破坏对象2或者其他对象。结果是:内存泄漏。

接下来,delete将直接释放存储块,这将失败,因为存储块实际上是从向量前面的隐藏size_t开始的。由于释放的内存不是有效的堆指针,因此这再次破坏了堆内存。

结论:这同样不是一个好做法。

问题:如果从类定义中删除析构函数MyClass::~MyClass(),可以执行哪些优化?
答案:如果该类没有析构函数,则除了释放内存之外,释放向量时无需执行任何特殊工作。 在这种情况下,不需要隐藏计数器。 该块可以直接分配而没有开销,也可以释放而没有开销。

更具体地说,如果该类具有琐碎的析构函数(其基类或子对象(如果有)均没有析构函数),则标量和向量new/delete会以相同的方式分配和释放内存,并将它们混合在一起不会产生运行时错误: 你十分幸运。

当然,明天可能有人会在你的类中添加析构函数,这样你就不会再如此幸运了。

当然,请注意,所有讨论都假定使用了昨天描述的编译器行为。 该行为取决于实现,因此不应依赖它。
你今天可能很幸运,但是下一版编译器可能会改变其管理向量的方式,你的运气将会用光。

总结
再次记住:new分配的内存使用delete释放,new[]分配的内存使用delete[]释放。

最后
Raymond Chen的《The Old New Thing》是我非常喜欢的博客之一,里面有很多关于Windows的小知识,对于广大Windows平台开发者来说,确实十分有帮助。
本文来自:《Answers to exercises – mismatching new/delete》

评论已关闭。