General guidelines to avoid memory leaks in C++

121,678

Solution 1

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.

Solution 2

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).

Solution 3

You'll want to look at smart pointers, such as boost's smart pointers.

Instead of

int main()
{ 
    Object* obj = new Object();
    //...
    delete obj;
}

boost::shared_ptr will automatically delete once the reference count is zero:

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:

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...

Solution 4

Read up on RAII and make sure you understand it.

Solution 5

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.

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 not invoke destruction of any textures.

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!

Share:
121,678
Admin
Author by

Admin

Updated on September 28, 2020

Comments

  • Admin
    Admin over 3 years

    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?

  • simon
    simon over 15 years
    Your answer does not match the example code here? I agree with the answer "only one return" but the example code is showing what NOT to do.
  • simon
    simon over 15 years
    I totaly agree. Working in an embedded environment you may also not have the luxury of third party libraries.
  • paercebal
    paercebal over 15 years
    C++ RAII's point is exactly to avoid the kind of code you wrote. In C, this is probably the right thing to do. But in C++, your code is flawed. For example: What if new b() throws ? You leak a.
  • paercebal
    paercebal over 15 years
    I disagree. in the part of "use that thing", if a return or an exception is thrown, then you'll miss the deallocation. As for performance, the std::auto_ptr would cost you nothing. Not that I never code the same way you do. It's just that there is a difference between 100% and 99% secure code. :-)
  • Adam Naylor
    Adam Naylor over 15 years
    I may be missing a trick, but how can 'game' and 'non-performance-critical' be in the same sentence?
  • Robert Gould
    Robert Gould over 15 years
    Games are an example of the critical scenario of course. Might have failed to be clear there
  • Daniel O
    Daniel O about 15 years
    Must not the T class use RAII to be sure that doRAIIStatic() doesn't leak memory? For example T p(); p.doSandwich(); I don't really know much about this though.
  • paercebal
    paercebal almost 14 years
    @Ogre Psalm33 : Thanks for the comment. Of course, you're right. I added both links to the RAII Wikipedia page, and a small summary of what is RAII.
  • Ogre Psalm33
    Ogre Psalm33 almost 14 years
    Awesome...top notch! :-)
  • Admin
    Admin over 13 years
    How would you implement RAII for a container/collection such as a list or vector?
  • paercebal
    paercebal over 13 years
    @Shiftbit: Three ways, in order of preference: _ _ _ 1. Put real object inside the STL container. _ _ _ 2. Put smart pointers (shared_ptr) of objects inside the STL container. _ _ _ 3. Put raw pointers inside the STL container, but wrap the container to control any access to the data. The wrapper will make sure the destructor will free the allocated objects, and the wrapper accessors will make sure nothing is broken when accessing/modifying the container.
  • Dean Burge
    Dean Burge over 12 years
    "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.
  • paercebal
    paercebal over 12 years
    @wilhelmtell : Thanks for the comment. I edited my answer with your comment in mind.
  • Paweł Szczur
    Paweł Szczur over 11 years
    To compile using g++ one needs to add param: -std=c++0x
  • nishantjr
    nishantjr over 11 years
    @paercebal ____4. Use boost pointer containers
  • nishantjr
    nishantjr over 11 years
    Though I suppose thats the same as 3
  • TankorSmash
    TankorSmash over 10 years
    BoundsChecker is 404ing.
  • Robert
    Robert over 9 years
    When would you doRAIIDynamic vrs doRAIIStatic? Am I right in thinking the static case is allocated on the heap so if the T object is large you would want to use the dynamic version?
  • paercebal
    paercebal over 9 years
    @Robert : In C++03, you would use doRAIIDynamic in a function that must give the ownership to either a child or parent function (or global scope). Or when you are receiving an interface to a polymorph object through a factory (returning a smart pointer, if it is correctly written). In C++11, this is less the case because you can make your object movable, so giving the ownership of an object declared on the stack is easier...
  • paercebal
    paercebal over 9 years
    @Robert: ... Note that declaring an object on the stack doesn't mean that object doesn't use the heap internally (note the double negation... :-) ...). For example, std::string implemented with Small String Optimization will have a buffer "on the stack of the class" for small strings (~15 chars), and will use a pointer to a memory in the heap for larger strings... But from the outside, std::string is still a value type that you declare (usually) on the stack and you use as you would use an integer (as opposed to: as you would use an interface for a polymorph class).
  • antred
    antred about 9 years
    Tip#1 should only be applied if the class has at least one virtual method. I would never impose a useless virtual destructor on a class that isn't meant to serve as a base class in a polymorphic inheritance tree.
  • Prabhash Rathore
    Prabhash Rathore about 8 years
    or you can compile with g++ using flag value -std=c++11