在 C++ 中,当程序退出时,调用 delete 有什么原因吗?

56

比如说,在我的C++ main 函数中,如果我有一个指向使用堆内存(而不是栈内存)的变量的指针——那么这个变量在我的应用程序结束后会自动释放吗?我认为应该是这样。

即使如此,在情况下内存在退出时会自动释放,是否总是删除堆分配是一个好的实践呢?

例如,这样做有没有意义呢?

int main(...)
{
    A* a = new A();
    a->DoSomething();
    delete a;
    return 0;
}

我在考虑,以防我重构代码(或其他人重构代码)并将其放到应用程序的其他位置,需要真正需要delete时。

除了Brian R. Bondy的答案(它具体讨论了C++中的影响),Paul Tomblin也对一个关于C的问题给出了很好的答案,该答案还谈到了C++析构函数。


1
提到的重复是关于C语言的,它遗漏了一些关于析构函数的重要信息,这在C++中是一个问题。 - Brian R. Bondy
@Brian,我在重复问题的答案中也提到了析构函数。我认为这个问题应该被删除。 - Paul Tomblin
不确定您所说的“destructor”是什么意思 - 请澄清。 - Nick Bolton
基本上,对于C和C++来说,这个问题有不同的答案。在C++中,您还必须考虑到删除程序中内存的更大原因,因为如果您不这样做,您类的析构函数将不会被调用。这可能包含非常重要的代码。 - Brian R. Bondy
啊,说得好。我认为我的问题也表达了“出于良好的实践,我应该删除吗?”而不仅仅是“退出时我应该删除吗?”。 - Nick Bolton
显示剩余2条评论
8个回答

85

调用delete是非常重要的,因为你可能在析构函数中有一些需要执行的代码,比如将一些数据写入日志文件。如果让操作系统释放内存,那么你的析构函数中的代码将不会被执行。

大多数操作系统会在程序结束时释放内存。但手动释放内存是一个好习惯。正如我上面所说,操作系统不会调用你的析构函数。

至于总体上是否都需要调用delete,答案是肯定的。否则会导致程序出现内存泄漏,从而导致新的分配失败。


12
别忘了,类成员可能也持有一些程序结束时不会自动消失的东西,比如套接字、连接等等。内存泄漏并不是唯一的问题,也不是所有泄漏都能自动清除。 - David Thornley
1
如果我已经为树(特别是trie)分配了内存,在析构函数中,我只需要执行delete操作。我是否仍然需要显式调用delete?还是依赖于操作系统来代替我执行? - Vinayak Garg

29

是的,当你通过内存泄漏检测工具运行程序时,它有助于消除误报。


1
请问您能否指导我一些好的内存泄漏检测工具? - Nick Bolton
3
在Windows上,Compuware的BoundsChecker非常出色,但价格昂贵。在Linux上,我听说Valgrind不错,但我还没有尝试过。 - Ferruccio
2
一个免费的MSVC++内存泄漏检测工具是Visual Leak Detector(在互联网上搜索即可轻松找到)。 - Nemanja Trifunovic
开源工具Cppcheck目前在检测资源泄漏方面做得相当不错。 - Wolf

13

可以。

  • 标准并不保证操作系统会清理内存。在主流平台上,您可以期望这样做,但为什么要冒险呢?
  • 如果您不故意泄漏内存,则可以减少像valgrind这样的工具报告的混乱。
  • 如果您养成了这个习惯,谁能说您不会有一天在重要位置无意中采用这种方法?
  • 可能需要对象销毁。通常只需假设您需要。这不会损害您。

2
对我来说,第三点最具有吸引力。总是删除你的分配比尝试决定何时适当要少费力。这让我想起使用汽车转向信号 - 我一直想知道为什么人们只是有时使用它; 最好养成习惯,不需要考虑它。 - tenfour
@tenfour:确实。如果你不能保证前面的车在适当时候总是打灯示意,那么你永远无法确定他什么时候不会转弯。整个系统就会崩溃。 - Lightness Races in Orbit
@LightnessRacesinOrbit:赞同你说的“标准并不保证操作系统会清理内存。”我也喜欢你的第三点。 - Destructor

8
想象一下你的类 A 需要被销毁。
如果你不调用 a 上的 delete,那么析构函数将不会被调用。通常情况下,如果进程结束了,这并不会真正有影响。但是如果析构函数必须释放例如数据库中的对象?刷新缓存到日志文件?将内存缓存写回磁盘呢?
你看,删除对象不仅仅是“良好的实践”,在某些情况下是必需的。

7
考虑这样一种情况:要删除的对象获取了外部资源,但操作系统不能安全地释放该资源。如果你不调用delete删除该对象,那么就会造成真正的内存泄漏。

4

明确删除对象的另一个原因是,如果您的应用程序存在真正的内存泄漏,那么如果您不必筛选来自未清理的“泄漏”的内容,使用像valgrind这样的工具查找泄漏变得更加容易。


4
另一个删除的原因是为了避免未来可能使用的泄漏检测器发出虚假警报。如果您遇到虚假警报,可能会忽略泄漏检测器报告的真实泄漏情况 - 它将被埋在报告中的虚假警报之间。

2
公平地说,鉴于OP的代码,这些不会是“错误”警报 ;) - Lightness Races in Orbit

3

始终确保您自己删除它。操作系统会处理此问题,但要排除可以轻松避免的错误。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接