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

132

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


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

1
如果可以的话,请使用boost shared_ptr和标准C++ auto_ptr。它们传达了所有权语义。
当您返回一个auto_ptr时,您告诉调用者您正在将内存的所有权交给他们。
当您返回一个shared_ptr时,您告诉调用者您拥有对其的引用,并且他们承担部分所有权,但这不完全是他们的责任。
这些语义也适用于参数。如果调用者向您传递一个auto_ptr,则表示他们正在将所有权交给您。

1

如果您要手动管理内存,有两种情况:

  1. 我创建了对象(可能是间接地通过调用分配新对象的函数),我使用它(或者我调用的函数使用它),然后我释放它。
  2. 有人给了我引用,所以我不应该释放它。

如果您需要打破这些规则,请记录下来。

这一切都与指针所有权有关。


1

其他人已经提到了避免内存泄漏的方法(如智能指针)。但是一款性能分析和内存分析工具通常是你在出现内存问题后追踪问题的唯一途径。

Valgrind memcheck 是一款优秀的免费工具。


0

您可以拦截内存分配函数并查看程序退出时是否有一些未释放的内存区域(尽管这不适用于所有应用程序)。

也可以在编译时通过替换new和delete运算符以及其他内存分配函数来完成。

例如,在此网站 [调试C++中的内存分配]中检查。 注意:对于delete运算符也有类似的技巧:

#define DEBUG_DELETE PrepareDelete(__LINE__,__FILE__); delete
#define delete DEBUG_DELETE

您可以将文件名存储在某些变量中,当重载的删除运算符知道它被调用的位置时,就可以将其记录下来。这样,您就可以跟踪程序中的每个删除和分配操作。在内存检查序列的结尾,您应该能够报告哪些分配的内存块没有被“删除”,并通过文件名和行号进行标识,这也是您想要的。

您还可以尝试一些类似于Visual Studio下的BoundsChecker ,它非常有趣且易于使用。


0

关于在不同位置分配和销毁的唯一示例之一是线程创建(您传递的参数)。 但即使在这种情况下也很容易。 这是创建线程的函数/方法:

struct myparams {
int x;
std::vector<double> z;
}

std::auto_ptr<myparams> param(new myparams(x, ...));
// Release the ownership in case thread creation is successfull
if (0 == pthread_create(&th, NULL, th_func, param.get()) param.release();
...

在这里替换线程函数

extern "C" void* th_func(void* p) {
   try {
       std::auto_ptr<myparams> param((myparams*)p);
       ...
   } catch(...) {
   }
   return 0;
}

很容易对吧?如果线程创建失败,资源将由auto_ptr释放(删除),否则所有权将转移到线程。 那么如果线程创建后非常快地释放了资源怎么办?
param.release();

在主函数/方法中调用了什么?什么都没有!因为我们会“告诉”auto_ptr忽略释放。

C++内存管理很容易,不是吗?

干杯,

Ema!


0

管理内存的方式与您管理其他资源(句柄、文件、数据库连接、套接字等)的方式相同。垃圾回收也无法帮助您处理它们。


0
我们将所有的分配函数都包装在一个层中,该层在前面附加一个简短的字符串和一个哨兵标志。例如,您可以调用“myalloc(pszSomeString,iSize,iAlignment);或new(“description”,iSize)MyObject();内部分配指定大小加上足够的空间来存储头和哨兵。当然,不要忘记为非调试版本注释掉这个功能!这需要更多的内存,但好处远远超过成本。
这有三个好处-首先,它允许您轻松快速地跟踪哪些代码正在泄漏,通过快速搜索在某些“区域”中分配的代码,但在这些区域应该释放时没有清除。检查以确保所有哨兵完好无损,也可以用于检测边界是否已被覆盖。在寻找那些隐藏得很好的崩溃或数组错误时,这已经多次拯救了我们。第三个好处是跟踪内存使用情况,以查看谁是主要的玩家-在MemDump中收集某些描述的内容告诉您,“sound”占用的空间比您预期的要大得多,例如。

0

C ++ 的设计考虑了 RAII。我认为在 C ++ 中管理内存没有更好的方式。 但是要小心不要在本地作用域上分配非常大的块(例如缓冲对象)。这可能会导致堆栈溢出,并且如果在使用该块时边界检查存在缺陷,则可以覆盖其他变量或返回地址,从而导致各种安全漏洞。


-3

任何函数都只返回一个值。这样你就可以在那里进行释放,而不会错过它。

否则很容易犯错误:

new a()
if (Bad()) {delete a; return;}
new b()
if (Bad()) {delete a; delete b; return;}
... // etc.

你的回答和这里的示例代码不一致吗?我同意“只有一个返回”的答案,但示例代码展示了什么不该做。 - simon
1
C++中RAII的重点正是为了避免你编写的这种代码。在C语言中,这可能是正确的做法。但在C++中,你的代码是有缺陷的。例如:如果new b()抛出异常,那么你就会泄漏a。 - paercebal

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