I would forget manual deallocation, too, that's why I like automatic memory management (not garbage collection) that much...
Especially such a situation can be very annoying to do by hand:
int Function()
{
// Derived1, Derived2 are derived classes from Base
Base* a = new Derived1;
// if the Derived2() constructor or new throws an exception,
// the memory of a is irreversibly lost
Base* b = new Derived2;
// ...
if (condition)
{
// We must free the memory of a and b, but then
// we can't access a anymore, so we have to store it before
return a->GetXY();
}
else if (otherCondition)
{
// Again, we have to make sure everything is freed correctly
throw anyException();
}
// In fact, we have to monitor every point where a function can leave
return -1;
};
A correct handling would look like this:
int Function()
{
Base* a = new Derived1;
Base* b;
try
{
b = new Derived2;
}
catch (...)
{
// Free memory and rethrow exception
delete a;
throw;
}
// ...
if (condition)
{
int result = a->GetXY();
delete a;
delete b;
return result;
}
else if (otherCondition)
{
delete a;
delete b;
throw anyException();
}
delete a;
delete b;
return -1;
};
This code is not only tedious to write, but also confusing to read and hard to maintain. In C++, we can use the destructor to free memory when an object goes out of scope. Some classes taking advantage of this technique are called smart-pointers – intelligent pointer-like objects that bear responsibility for memory ownership management. Using them, a proper solution might look like this:
int Function()
{
// scoped_ptr is an example of a simple smart-pointer, that
// deletes the owned object when leaving the scope
scoped_ptr<Base> a(new Derived1);
scoped_ptr<Base> b(new Derived2);
// ...
if (condition)
{
return a->GetXY();
}
else if (otherCondition)
{
throw anyException();
}
return -1;
};
Containing not a single delete or try-catch, this code is still safe and correct. The destructor scoped_ptr::~scoped_ptr() takes care of the whole deallocation. The big advantage is, we can add a third smart-pointer c or another exit point of the function, without making the code uglier and more error-prone.