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
UseResourceswas defined like this:If
Dog::Dog()throws, thencatwill 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 Catwill throw, obviously. But that means thatcatnever got initialized. Which means thatdelete catwill 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::catwill at all times be a valid pointer. If that is indeed an invariant ofUseResources, then this code will fail because it permits the construction ofUseResourcesin spite of the exception.Basically, there is no way to make this code safe unless
new Catisnoexcept(either explicitly or implicitly).By contrast, this always works:
In short, look on a function-level try-block as a serious code smell.