Saturday, November 6, 2010

Common Resource Leak with Smart Pointers

One of the most common errors when dealing with smart pointers is creating a smart pointer object as a part of a function / method call.

For example:

DoSomething(shared_ptr<MyClass>(new MyClass), MyFunction());

Order in which function parameters are evaluated in C++ is not defined. (It is not from left to right like in C# and Java). Therefore, it is possible that a new MyClass object will be allocated first, and then MyFunction() called. In case when MyFunction() throws an exception, just created MyClass object will never be passed to a shared_ptr object. Therefore, it will never be be deallocated and this would result in a resource leak.

The following simple program demonstrates this error:

#include <memory>
#include <iostream>

using namespace std;
using namespace tr1;

class MyClass
{
public:
MyClass() { cout << "MyClass()" << endl; }
~MyClass() { cout << "~MyClass()" << endl; }
};

int MyFunction()
{
throw exception("New Exception!");
}

void DoSomething(shared_ptr<MyClass> myClass, int n)
{
// Add code here...
}

int main()
{
try
{
DoSomething(shared_ptr<MyClass>(new MyClass), MyFunction());
}
catch (exception e)
{
cout << "Exception caught: " << e.what() << endl;
}

return 0;
}

And in fact, most of the time I run this program, it results in a memory leak, which can be concluded from watching the output:

MyClass()
Exception caught: New Exception!

Obviously MyClass destructor wasn’t called.

To conclude the story, DoSomething() function call should be transformed to:

shared_ptr<MyClass> myClass(new MyClass);
DoSomething(myClass, MyFunction());

Now, if MyFunction() throws an exception, myClass object will be destroyed (since it is on the stack) and it will also deallocate MyClass object passed to it. We can take a look at the program output again and verify that the MyClass object has been destroyed.

MyClass()
~MyClass()
Exception caught: New Exception!

No comments: