General guidelines to avoid memory leaks in C++ [closed]
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.
Don't write
Object* x = new Object;
or even
shared_ptr<Object> x(new Object);
when you can just write
Object x;
Use 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).
This post seem to be repetitive, but in C++, the most basic pattern to know is 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).
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).
See below a comparison of RAII and non RAII code:
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
To summarize (after the comment from Ogre Psalm33), RAII relies on three concepts:
- 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.).
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).
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)...
Edit (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.
– wilhelmtell
wilhelmtell is quite right about that: There are exceptional ways to cheat RAII, all leading to the process abrupt stop.
Those are exceptional ways because C++ code is not littered with terminate, exit, etc., or in the case with exceptions, we do want an unhandled exception to crash the process and core dump its memory image as is, and not after cleaning.
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).
Instead of managing memory manually, try to use smart pointers where applicable.
Take a look at the Boost lib, TR1, and smart pointers.
Also smart pointers are now a part of C++ standard called C++11.