Possible Duplicate:
When is a function try block useful?
Difference between try-catch syntax for function
This code throws an int
exception while constructing the Dog
object inside class UseResources
. The int
exception is caught by a normal try-catch
block and the code outputs :
Cat()
Dog()
~Cat()
Inside handler
#include <iostream>
using namespace std;
class Cat
{
public:
Cat() { cout << "Cat()" << endl; }
~Cat() { cout << "~Cat()" << endl; }
};
class Dog
{
public:
Dog() { cout << "Dog()" << endl; throw 1; }
~Dog() { cout << "~Dog()" << endl; }
};
class UseResources
{
class Cat cat;
class Dog dog;
public:
UseResources() : cat(), dog() { cout << "UseResources()" << endl; }
~UseResources() { cout << "~UseResources()" << endl; }
};
int main()
{
try
{
UseResources ur;
}
catch( int )
{
cout << "Inside handler" << endl;
}
}
Now, if we replace the definition of the UseResources()
constructor, with one that uses a function try block
, as below,
UseResources() try : cat(), dog() { cout << "UseResources()" << endl; } catch(int) {}
the output is the same
Cat()
Dog()
~Cat()
Inside handler
i.e., with exactly the same final result.
What is then, the purpose of a function try block
?
Imagine if
UseResources
was defined like this:If
Dog::Dog()
throws, thencat
will leak memory. BecaseUseResources
's constructor never completed, the object was never fully constructed. And therefore it does not have its destructor called.To prevent this leak, you must use a function-level try/catch block:
To answer your question more fully, the purpose of a function-level try/catch block in constructors is specifically to do this kind of cleanup. Function-level try/catch blocks cannot swallow exceptions (regular ones can). If they catch something, they will throw it again when they reach the end of the catch block, unless you rethrow it explicitly with
throw
. You can transform one type of exception into another, but you can't just swallow it and keep going like it didn't happen.This is another reason why values and smart pointers should be used instead of naked pointers, even as class members. Because, as in your case, if you just have member values instead of pointers, you don't have to do this. It's the use of a naked pointer (or other form of resource not managed in a RAII object) that forces this kind of thing.
Note that this is pretty much the only legitimate use of function try/catch blocks.
More reasons not to use function try blocks. The above code is subtly broken. Consider this:
So, what happens in
UseResources
's constructor? Well, the expressionnew Cat
will throw, obviously. But that means thatcat
never got initialized. Which means thatdelete cat
will yield undefined behavior.You might try to correct this by using a complex lambda instead of just
new Cat
:That theoretically fixes the problem, but it breaks an assumed invariant of
UseResources
. Namely, thatUseResources::cat
will at all times be a valid pointer. If that is indeed an invariant ofUseResources
, then this code will fail because it permits the construction ofUseResources
in spite of the exception.Basically, there is no way to make this code safe unless
new Cat
isnoexcept
(either explicitly or implicitly).By contrast, this always works:
In short, look on a function-level try-block as a serious code smell.