在 C++ 中避免内存泄漏的一般准则
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/76796/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
General guidelines to avoid memory leaks in C++
提问by
What are some general tips to make sure I don't leak memory in C++ programs? How do I figure out who should free memory that has been dynamically allocated?
有哪些一般技巧可以确保我不会在 C++ 程序中泄漏内存?如何确定谁应该释放已动态分配的内存?
采纳答案by Andri M?ll
回答by
I thoroughly endorse all the advice about RAII and smart pointers, but I'd also like to add a slightly higher-level tip: the easiest memory to manage is the memory you never allocated. Unlike languages like C# and Java, where pretty much everything is a reference, in C++ you should put objects on the stack whenever you can. As I've see several people (including Dr Stroustrup) point out, the main reason why garbage collection has never been popular in C++ is that well-written C++ doesn't produce much garbage in the first place.
我完全赞同有关 RAII 和智能指针的所有建议,但我还想添加一个稍微高级一点的提示:最容易管理的内存是您从未分配过的内存。与 C# 和 Java 等几乎所有东西都是引用的语言不同,在 C++ 中,您应该尽可能将对象放在堆栈上。正如我看到一些人(包括 Stroustrup 博士)指出的那样,垃圾收集在 C++ 中从未流行的主要原因是编写良好的 C++ 一开始不会产生太多垃圾。
Don't write
不要写
Object* x = new Object;
or even
甚至
shared_ptr<Object> x(new Object);
when you can just write
当你可以写
Object x;
回答by paercebal
Use RAII
使用RAII
- Forget Garbage Collection(Use RAII instead). Note that even the Garbage Collector can leak, too (if you forget to "null" some references in Java/C#), and that Garbage Collector won't help you to dispose of resources (if you have an object which acquired a handle to a file, the file won't be freed automatically when the object will go out of scope if you don't do it manually in Java, or use the "dispose" pattern in C#).
- Forget the "one return per function" rule. This is a good C advice to avoid leaks, but it is outdated in C++ because of its use of exceptions (use RAII instead).
- And while the "Sandwich Pattern"is a good C advice, it is outdated in C++because of its use of exceptions (use RAII instead).
- 忘记垃圾收集(改用 RAII)。请注意,即使垃圾收集器也可能泄漏(如果您忘记将 Java/C# 中的某些引用“设为空”),并且垃圾收集器不会帮助您处理资源(如果您有一个对象获得了一个文件,如果您不在 Java 中手动执行此操作,或者在 C# 中使用“处置”模式,则当对象超出范围时,该文件将不会自动释放)。
- 忘记“一个函数一个返回”的规则。这是一个很好的 C 建议,可以避免泄漏,但它在 C++ 中已经过时,因为它使用了异常(改用 RAII)。
- 虽然“三明治模式”是一个很好的 C 建议,但它在 C++ 中已经过时了,因为它使用了异常(改用 RAII)。
This post seem to be repetitive, but in C++, the most basic pattern to know is RAII.
这篇文章似乎是重复的,但在 C++ 中,要知道的最基本的模式是RAII。
Learn to use smart pointers, both from boost, TR1 or even the lowly (but often efficient enough) auto_ptr (but you must know its limitations).
学习使用智能指针,无论是 boost、TR1 还是低级(但通常足够有效)的 auto_ptr(但您必须知道它的局限性)。
RAII is the basis of both exception safety and resource disposal in C++, and no other pattern (sandwich, etc.) will give you both (and most of the time, it will give you none).
RAII 是 C++ 中异常安全和资源处理的基础,没有其他模式(三明治等)可以同时为您提供这两种方式(而且大多数情况下,它不会为您提供任何东西)。
See below a comparison of RAII and non RAII code:
请参阅下面的 RAII 和非 RAII 代码的比较:
void doSandwich()
{
T * p = new T() ;
// do something with p
delete p ; // leak if the p processing throws or return
}
void doRAIIDynamic()
{
std::auto_ptr<T> p(new T()) ; // you can use other smart pointers, too
// do something with p
// WON'T EVER LEAK, even in case of exceptions, returns, breaks, etc.
}
void doRAIIStatic()
{
T p ;
// do something with p
// WON'T EVER LEAK, even in case of exceptions, returns, breaks, etc.
}
About RAII
关于RAII
To summarize (after the comment from Ogre Psalm33), RAII relies on three concepts:
总结一下(在Ogre Psalm33的评论之后),RAII 依赖于三个概念:
- Once the object is constructed, it just works!Do acquire resources in the constructor.
- Object destruction is enough!Do free resources in the destructor.
- It's all about scopes!Scoped objects (see doRAIIStatic example above) will be constructed at their declaration, and will be destroyed the moment the execution exits the scope, no matter how the exit (return, break, exception, etc.).
- 一旦对象被构造,它就可以工作了!在构造函数中获取资源。
- 对象销毁就够了!在析构函数中释放资源。
- 这都是关于范围的!作用域对象(参见上面的 doRAIIStatic 示例)将在其声明时构造,并会在执行退出作用域的那一刻被销毁,无论退出方式如何(返回、中断、异常等)。
This means that in correct C++ code, most objects won't be constructed with new
, and will be declared on the stack instead. And for those constructed using new
, all will be somehow scoped(e.g. attached to a smart pointer).
这意味着在正确的 C++ 代码中,大多数对象不会用 构造new
,而是在堆栈上声明。对于那些使用 构造的new
,所有的都将以某种方式限定范围(例如附加到智能指针)。
As a developer, this is very powerful indeed as you won't need to care about manual resource handling (as done in C, or for some objects in Java which makes intensive use of try
/finally
for that case)...
作为开发人员,这确实非常强大,因为您不需要关心手动资源处理(如在 C 中所做的那样,或者对于在这种情况下大量使用try
/ 的Java 中的某些对象finally
)...
Edit (2012-02-12)
编辑 (2012-02-12)
"scoped objects ... will be destructed ... no matter the exit" that's not entirely true. there are ways to cheat RAII. any flavour of terminate() will bypass cleanup. exit(EXIT_SUCCESS) is an oxymoron in this regard.
“作用域对象......将被破坏......无论退出”并不完全正确。有很多方法可以欺骗 RAII。任何形式的 terminate() 都将绕过清理。exit(EXIT_SUCCESS) 在这方面是矛盾的。
–威廉特尔
wilhelmtellis quite right about that: There are exceptionalways to cheat RAII, all leading to the process abrupt stop.
wilhelmtell 说得很对:有很多特殊的方法可以欺骗 RAII,所有这些方法都会导致进程突然停止。
Those are exceptionalways because C++ code is not littered with terminate, exit, etc., or in the case with exceptions, we do want an unhandled exceptionto crash the process and core dump its memory image as is, and not after cleaning.
这些是特殊的方式,因为 C++ 代码没有终止、退出等,或者在有异常的情况下,我们确实希望一个未处理的异常使进程崩溃并按原样核心转储其内存映像,而不是在清理之后。
But we must still know about those cases because, while they rarely happen, they can still happen.
但是我们仍然必须了解这些案例,因为虽然它们很少发生,但它们仍然可能发生。
(who calls terminate
or exit
in casual C++ code?... I remember having to deal with that problem when playing with GLUT: This library is very C-oriented, going as far as actively designing it to make things difficult for C++ developers like not caring about stack allocated data, or having "interesting" decisions about never returning from their main loop... I won't comment about that).
(谁调用terminate
或exit
使用随意的 C++ 代码?...我记得在玩GLUT时不得不处理这个问题:这个库是非常面向 C 的,积极设计它以使 C++ 开发人员的事情变得困难,比如不关心关于堆栈分配的数据,或者关于永不从主循环返回的“有趣”决定......我不会对此发表评论)。
回答by Doug T.
You'll want to look at smart pointers, such as boost's smart pointers.
您将需要查看智能指针,例如boost 的智能指针。
Instead of
代替
int main()
{
Object* obj = new Object();
//...
delete obj;
}
boost::shared_ptr will automatically delete once the reference count is zero:
一旦引用计数为零,boost::shared_ptr 将自动删除:
int main()
{
boost::shared_ptr<Object> obj(new Object());
//...
// destructor destroys when reference count is zero
}
Note my last note, "when reference count is zero, which is the coolest part. So If you have multiple users of your object, you won't have to keep track of whether the object is still in use. Once nobody refers to your shared pointer, it gets destroyed.
请注意我的最后一个注释,“当引用计数为零时,这是最酷的部分。因此,如果您的对象有多个用户,您将不必跟踪该对象是否仍在使用中。一旦没有人引用您的共享指针,它会被销毁。
This is not a panacea, however. Though you can access the base pointer, you wouldn't want to pass it to a 3rd party API unless you were confident with what it was doing. Lots of times, your "posting" stuff to some other thread for work to be done AFTER the creating scope is finished. This is common with PostThreadMessage in Win32:
然而,这不是灵丹妙药。尽管您可以访问基指针,但除非您对它正在做什么有信心,否则您不会希望将它传递给第 3 方 API。很多时候,您的“发布”内容到其他线程,以便在创建范围完成后完成工作。这在 Win32 中的 PostThreadMessage 中很常见:
void foo()
{
boost::shared_ptr<Object> obj(new Object());
// Simplified here
PostThreadMessage(...., (LPARAM)ob.get());
// Destructor destroys! pointer sent to PostThreadMessage is invalid! Zohnoes!
}
As always, use your thinking cap with any tool...
与往常一样,将您的思维上限用于任何工具......
回答by Jeroen Dirks
Most memory leaks are the result of not being clear about object ownership and lifetime.
大多数内存泄漏是由于不清楚对象所有权和生命周期的结果。
The first thing to do is to allocate on the Stack whenever you can. This deals with most of the cases where you need to allocate a single object for some purpose.
首先要做的是尽可能在堆栈上进行分配。这处理了大多数需要为某些目的分配单个对象的情况。
If you do need to 'new' an object then most of the time it will have a single obvious owner for the rest of its lifetime. For this situation I tend to use a bunch of collections templates that are designed for 'owning' objects stored in them by pointer. They are implemented with the STL vector and map containers but have some differences:
如果您确实需要“新建”一个对象,那么在大多数情况下,它将在其生命周期的剩余时间内拥有一个明显的所有者。对于这种情况,我倾向于使用一堆集合模板,这些模板旨在通过指针“拥有”存储在其中的对象。它们是用 STL 向量和映射容器实现的,但有一些区别:
- These collections can not be copied or assigned to. (once they contain objects.)
- Pointers to objects are inserted into them.
- When the collection is deleted the destructor is first called on all objects in the collection. (I have another version where it asserts if destructed and not empty.)
- Since they store pointers you can also store inherited objects in these containers.
- 无法复制或分配这些集合。(一旦它们包含对象。)
- 指向对象的指针被插入其中。
- 当集合被删除时,首先对集合中的所有对象调用析构函数。(我有另一个版本,如果被破坏并且不为空,它会断言。)
- 由于它们存储指针,您还可以将继承的对象存储在这些容器中。
My beaf with STL is that it is so focused on Value objects while in most applications objects are unique entities that do not have meaningful copy semantics required for use in those containers.
我对 STL 的看法是它非常关注值对象,而在大多数应用程序中,对象是独特的实体,没有在这些容器中使用所需的有意义的复制语义。
回答by DarenW
Bah, you young kids and your new-fangled garbage collectors...
呸,你们这些年幼的孩子和你们这些新奇的垃圾收集者……
Very strong rules on "ownership" - what object or part of the software has the right to delete the object. Clear comments and wise variable names to make it obvious if a pointer "owns" or is "just look, don't touch". To help decide who owns what, follow as much as possible the "sandwich" pattern within every subroutine or method.
关于“所有权”的非常严格的规则 - 软件的哪些对象或部分有权删除该对象。清晰的注释和明智的变量名称,使指针“拥有”或“只是看,不要碰”一目了然。为了帮助决定谁拥有什么,在每个子程序或方法中尽可能多地遵循“三明治”模式。
create a thing
use that thing
destroy that thing
Sometimes it's necessary to create and destroy in widely different places; i think hard to avoid that.
有时需要在不同的地方创建和销毁;我认为很难避免这种情况。
In any program requiring complex data structures, i create a strict clear-cut tree of objects containing other objects - using "owner" pointers. This tree models the basic hierarchy of application domain concepts. Example a 3D scene owns objects, lights, textures. At the end of the rendering when the program quits, there's a clear way to destroy everything.
在任何需要复杂数据结构的程序中,我都会创建一个包含其他对象的严格清晰的对象树 - 使用“所有者”指针。此树对应用程序域概念的基本层次结构进行建模。示例 3D 场景拥有对象、灯光、纹理。在程序退出的渲染结束时,有一种明确的方法可以销毁所有内容。
Many other pointers are defined as needed whenever one entity needs access another, to scan over arays or whatever; these are the "just looking". For the 3D scene example - an object uses a texture but does not own; other objects may use that same texture. The destruction of an object does notinvoke destruction of any textures.
每当一个实体需要访问另一个实体时,根据需要定义许多其他指针,以扫描数组或其他任何东西;这些是“只是看”。对于 3D 场景示例 - 对象使用纹理但不拥有;其他对象可能使用相同的纹理。对象的销毁不会调用任何纹理的销毁。
Yes it's time consuming but that's what i do. I rarely have memory leaks or other problems. But then i work in the limited arena of high-performance scientific, data acquisition and graphics software. I don't often deal transactions like in banking and ecommerce, event-driven GUIs or high networked asynchronous chaos. Maybe the new-fangled ways have an advantage there!
是的,这很耗时,但这就是我所做的。我很少遇到内存泄漏或其他问题。但后来我在高性能科学、数据采集和图形软件的有限领域工作。我不经常处理像银行和电子商务、事件驱动的 GUI 或高度联网的异步混乱之类的交易。也许新奇的方式在那里有优势!
回答by ugasoft
Great question!
好问题!
if you are using c++ and you are developing real-time CPU-and-memory boud application (like games) you need to write your own Memory Manager.
如果您正在使用 C++ 并且正在开发实时 CPU 和内存应用程序(如游戏),则您需要编写自己的内存管理器。
I think the better you can do is merge some interesting works of various authors, I can give you some hint:
我觉得你最好能把不同作者的一些有趣的作品合并起来,我可以给你一些提示:
Fixed size allocator is heavily discussed, everywhere in the net
Small Object Allocation was introduced by Alexandrescu in 2001 in his perfect book "Modern c++ design"
A great advancement (with source code distributed) can be found in an amazing article in Game Programming Gem 7 (2008) named "High Performance Heap allocator" written by Dimitar Lazarov
A great list of resources can be found in thisarticle
固定大小分配器被大量讨论,在网络中随处可见
小对象分配是 Alexandrescu 于 2001 年在他的完美著作《现代 c++ 设计》中介绍的
在游戏编程 Gem 7 (2008) 的一篇名为“高性能堆分配器”的惊人文章中可以找到一个巨大的进步(源代码已分发),作者是 Dimitar Lazarov
一种很大的资源列表中可以找到这个文章
Do not start writing a noob unuseful allocator by yourself... DOCUMENT YOURSELF first.
不要开始自己编写一个 noob 无用的分配器......首先记录你自己。
回答by fabiopedrosa
There's already a lot about how to not leak, but if you need a tool to help you track leaks take a look at:
已经有很多关于如何不泄漏的信息,但是如果您需要一个工具来帮助您跟踪泄漏,请查看:
- BoundsCheckerunder VS
- MMGR C/C++ lib from FluidStudio http://www.paulnettle.com/pub/FluidStudios/MemoryManagers/Fluid_Studios_Memory_Manager.zip(its overrides the allocation methods and creates a report of the allocations, leaks, etc)
- VS下的BoundsChecker
- FluidStudio 的 MMGR C/C++ 库 http://www.paulnettle.com/pub/FluidStudios/MemoryManagers/Fluid_Studios_Memory_Manager.zip(它覆盖分配方法并创建分配、泄漏等的报告)
回答by Jason Dagit
One technique that has become popular with memory management in C++ is RAII. Basically you use constructors/destructors to handle resource allocation. Of course there are some other obnoxious details in C++ due to exception safety, but the basic idea is pretty simple.
在 C++ 中的内存管理中流行的一种技术是RAII。基本上你使用构造函数/析构函数来处理资源分配。当然,由于异常安全,C++ 中还有一些其他令人讨厌的细节,但基本思想非常简单。
The issue generally comes down to one of ownership. I highly recommend reading the Effective C++ series by Scott Meyers and Modern C++ Design by Andrei Alexandrescu.
这个问题通常归结为所有权之一。我强烈推荐阅读 Scott Meyers 的 Effective C++ 系列和 Andrei Alexandrescu 的 Modern C++ Design。