Programming Tips & Tricks

Home Categories: C# | C++ | General | Other

C++ Tips & Tricks: Avoiding Subtle Memory Leaks With Smart Pointers

Sometimes even when you use smart pointers to avoid resource leaks, they can still occur if you are not careful.
Assume we have the following function in our code:
void Func (auto_ptr<MyClass> m, int x, int y)
{ ... }
MyClass is some class, and our intent is to not use raw pointers, but to use a smart pointer like std::auto_ptr to prevent memory leaks.
If we want to call the function with a new instance of MyClass, we could do it this way:
	Func(auto_ptr<MyClass>(new MyClass), 1, 2);
and all is well. We create a new instance of MyClass and pass the pointer we get from the new statement to the constructor of auto_ptr, which will wrap it and save us from needing to call delete on it ourselves later on.
But what if the function call changes?
Assume it looks like this now:
	Func(auto_ptr<MyClass>(new MyClass), f1(), f2());
instead of passing two ints directly, we now pass the return values of f1 and f2.
The problem with this? Now its possible that we created a memory leak!
The reason is this:
When we write code like expression1; expression2; expression3; the compiler has to execute them in the order they appear. But in code like f(expression1, expression2, expression3), where the expressions are arguments to a method call, the compiler is free to decide the order in which to execute them, if the expressions don't depend on each other.
In our example, there are four expressions: new Myclass, the auto_ptr constructor, f1() and f2(). If the compiler decides to execute them in this order, all is well. But it can happen that they will be executed in the order new MyClass, f1(), f2(), auto_ptr constructor.
As you can see, between the allocation of the MyClass object, and its safe wrapping in auto_ptr, the compiler now executes f1() and f2(). But this means that if one of those methods throws an exception, the auto_ptr constructor is never called, and we have a dangling pointer to the newly allocated MyClass object, which is now leaking and can never be deleted.

Luckily its easy to prevent such leaks from being possible, by simply never calling a smart pointer constructor inside a method argument list. Instead, simply construct the smart pointer before passing it to the method:
	auto_ptr<MyClass> p (new MyClass);
Func(p, f1(), f2());