C ++中避免内存泄漏的一般准则

132

有什么一般性质的技巧可以确保我在C++程序中不会泄漏内存? 我如何确定谁应该释放动态分配的内存?


26
听起来对我来说非常有建设性。 - Shoerob
11
这是有建设性的。而且回答受到事实、专业知识、参考资料等的支持。看看点赞数/回答数量..!! - Samitha Chathuranga
29个回答

4

尽可能地使用用户智能指针!这样可以消除整个类别的内存泄漏。


4

在您的项目中分享和了解内存所有权规则。使用COM规则可以获得最佳的一致性([in]参数由调用方拥有所有权,被调用方必须复制; [out]参数由调用方拥有所有权,如果保留引用,则被调用方必须进行复制等)。


4

Valgrind是一个很好的工具,可以在运行时检查程序的内存泄漏问题。

它可用于大多数Linux版本(包括Android)和Darwin。

如果您习惯为程序编写单元测试,那么您应该养成系统地在测试中运行Valgrind的习惯。这将有可能在早期阶段避免许多内存泄漏问题。通常在简单测试中定位问题比在完整软件中更容易。

当然,这个建议同样适用于任何其他内存检查工具。


3

此外,如果存在std库类(例如vector),请勿使用手动分配的内存。如果违反此规则,请确保有一个虚析构函数。


2
如果您不能/不使用智能指针来处理某些事情(尽管这应该是一个巨大的警示信号),请在代码中输入以下内容:
allocate
if allocation succeeded:
{ //scope)
     deallocate()
}

这很显然,但请确保在作用域中输入任何代码之前先输入它。


2
经常出现这些错误的原因是你有一个接受对象引用或指针的方法,但所有权不明确。可以通过风格和注释约定来减少这种情况发生。
让函数获取对象所有权的情况成为特殊情况。在所有发生这种情况的情况下,请务必在头文件中的函数旁边编写一条注释,指示此情况。您应该努力确保在大多数情况下分配对象的模块或类也负责释放它。
在某些情况下,使用const可以帮助很多。如果函数不会修改对象,并且在返回后不存储对其的引用,则接受const引用。从阅读调用方的代码中,很明显您的函数没有接受对象的所有权。您可以使同一个函数接受非const指针,而调用方可能已经假定被调用者接受了所有权,但是使用const引用就没有问题了。
在参数列表中不要使用非const引用。当阅读调用方代码时,很不清楚被调用者是否会保留对参数的引用。
我不同意建议使用引用计数指针的评论。这通常运行良好,但是当出现错误并且它无法正常工作时,尤其是在多线程程序中,您的析构函数执行一些非平凡操作时。如果不太难,肯定要尝试调整设计,以不需要引用计数。

2

按重要性顺序的技巧:

- 技巧#1:始终记得声明您的析构函数“虚拟的”。

- 技巧#2:使用RAII。

- 技巧#3:使用boost的智能指针。

- 技巧#4:不要编写自己的有缺陷的智能指针,使用boost(在我正在处理的项目上,我无法使用boost,并且我必须调试我的智能指针,我肯定不会再采用同样的方法,但无论如何,现在我不能将boost添加到我们的依赖项中)

- 技巧#5:如果是一些休闲/非性能关键(例如具有数千个对象的游戏),请查看Thorsten Ottosen的boost指针容器。

- 技巧#6:为您选择的平台找到泄漏检测头文件,例如Visual Leak Detection的“vld”头文件。


我可能有所疏忽,但是“游戏”和“非性能关键”的概念怎么能出现在同一个句子中呢? - Adam Naylor
游戏是关键场景的一个例子。可能没有表达清楚。 - Robert Gould
只有在类至少有一个虚方法时才应该使用Tip#1。我永远不会在不打算作为多态继承树中的基类的类上强制实施无用的虚析构函数。 - antred

1
  • 尽量避免动态分配对象。只要类具有适当的构造函数和析构函数,就使用该类类型的变量而不是指向它的指针,这样可以避免动态分配和释放内存,因为编译器会为你完成。
    实际上这也是“智能指针”使用的机制,并被其他一些作者称为RAII;-)。
  • 将对象传递给其他函数时,更喜欢使用引用参数而不是指针。这样可以避免一些可能的错误。
  • 在可能的情况下,声明参数为const,特别是指向对象的指针。这样对象就不会被“意外释放”(除非你取消了const;-)))。
  • 尽量减少程序中进行内存分配和释放的次数。例如,如果您多次分配或释放相同类型,请为其编写一个函数(或工厂方法;-))。
    这样,如果需要,您可以轻松创建调试输出(已分配和释放的地址等)。
  • 使用工厂函数从单个函数中分配多个相关类的对象。
  • 如果您的类具有带有虚拟析构函数的公共基类,则可以使用相同的函数(或静态方法)释放所有这些类。
  • 使用诸如purify之类的工具检查您的程序(不幸的是,许多$/€/...)。

1

仅适用于MSVC,请在每个.cpp文件的顶部添加以下内容:

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

然后,当使用VS2003或更高版本进行调试时,你将在程序退出时被告知任何泄漏情况(它跟踪new/delete)。虽然简单,但这在过去对我有所帮助。


1

valgrind(仅适用于*nix平台)是一个非常好的内存检查器


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