Not always throw an exception assures you to avoid memory leakages even using "automatic objects", consider this example:
class Foo {
public:
Foo();
~Foo()
private:
AType* aPointer;
BType* bPointer;
};
Foo::Foo()
:aPointer(new AType),
bPointer(new BType)
{ }
we know that the initializer list order depends on the order declaration in the definition of class,
so in this case aPointer is initialized first then bPointer. What happens if the "new BType" throws an exception? Well, given the fact aPointer is a plain pointer then we will have memory leakage. So a first thought can be to use not plain pointers but something like "smart pointer".
So a first approach can be the following:
class Foo {
public:
Foo();
~Foo()
private:
std::auto_ptr aPointer;
std::auto_ptrbPointer;
};
Foo::Foo()
:aPointer(new AType),
bPointer(new BType)
{ }
well this is still not safe. Let see the constructor execution sequence:
1) Constructor AType is executed (new AType)
2) Constructor BType is executed (new BType)
3) Constructor std::auto_ptr
4) Constructor std::auto_ptr
do you see know where the problem is? If still "new BType" throws an exception the address of memory allocated by new AType was still not saved anywhere; unfortunately the correct way to solve this problem is the following:
class Foo {
public:
Foo();
~Foo()
private:
std::auto_ptraPointer;
std::auto_ptrbPointer;
};
Foo::Foo()
:aPointer(),
bPointer()
{
aPointer = std::auto_ptr(new AType);
bPointer = std::auto_ptr(new BType);
}
throwing an exception can also leave the object in an inconsistent state, consider the following class (do not consider the fact that the class is useless):
class Foo {
public:
Foo()
:theStorage()
{ }
addInt(int anInteger) {
theStorage.push_back(anInteger);
}
void sumOne() {
int i;
for (i=0; i < theStorage.size(); ++i) {
theStorage[i] += 1;
if (i==2) {
throw std::runtime_error("OPS!");
}
}
private:
std::vectortheStorage;
};
and his usage:
Foo aFoo;
aFoo.addInt(0);
aFoo.addInt(1);
aFoo.addInt(2);
aFoo.addInt(3);
at this point calling:
aFoo.sumOne();
will throw an exception leaving aFoo with partial updated elements, and from user point of view the aFoo is in an inconsistent state, so the sumOne() function shows here another problem that can break the exception safety of a class. The solution on this kind of problems is to work on a copy of internal class state and then make a swap between the internal state and the modified state.