Function try block handlers (C++ only)
The scope and lifetime of the parameters of a function or constructor
extend into the handlers of a function try block. The following example
demonstrates this:
void f(int &x) try {
throw 10;
}
catch (const int &i)
{
x = i;
}
int main() {
int v = 0;
f(v);
}The value of v after f() is
called is 10.A
function try block on main() does not catch exceptions
thrown in destructors of objects with static storage duration, or
constructors of namespace scope objects.
The following example throws an exception from a destructor of
a static object:
#include <iostream>
using namespace std;
class E {
public:
const char* error;
E(const char* arg) : error(arg) { }
};
class A {
public: ~A() { throw E("Exception in ~A()"); }
};
class B {
public: ~B() { throw E("Exception in ~B()"); }
};
int main() try {
cout << "In main" << endl;
static A cow;
B bull;
}
catch (E& e) {
cout << e.error << endl;
}The following is the output of the above example: In main
Exception in ~B()The runtime will not catch the exception
thrown when object cow is destroyed at the end of
the program.The following example throws an exception from a constructor of
a namespace scope object:
#include <iostream>
using namespace std;
class E {
public:
const char* error;
E(const char* arg) : error(arg) { }
};
namespace N {
class C {
public:
C() {
cout << "In C()" << endl;
throw E("Exception in C()");
}
};
C calf;
};
int main() try {
cout << "In main" << endl;
}
catch (E& e) {
cout << e.error << endl;
}The following is the output of the above example: In C()The
compiler will not catch the exception thrown when object calf is
created.In a function try block's handler, you cannot have a jump into the body of a constructor or destructor.
A return statement cannot appear in a function try block's handler of a constructor.
When the function try block's handler of an object's constructor
or destructor is entered, fully constructed base classes and members
of that object are destroyed. The following example demonstrates this:
#include <iostream>
using namespace std;
class E {
public:
const char* error;
E(const char* arg) : error(arg) { };
};
class B {
public:
B() { };
~B() { cout << "~B() called" << endl; };
};
class D : public B {
public:
D();
~D() { cout << "~D() called" << endl; };
};
D::D() try : B() {
throw E("Exception in D()");
}
catch(E& e) {
cout << "Handler of function try block of D(): " << e.error << endl;
};
int main() {
try {
D val;
}
catch(...) { }
}The following is the output of the above example: ~B() called
Handler of function try block of D(): Exception in D()When
the function try block's handler of D() is entered,
the run time first calls the destructor of the base class of D,
which is B. The destructor of D is
not called because val is not fully constructed.The runtime will rethrow an exception at the end of a function
try block's handler of a constructor or destructor. All other functions
will return once they have reached the end of their function try block's
handler. The following example demonstrates this:
#include <iostream>
using namespace std;
class E {
public:
const char* error;
E(const char* arg) : error(arg) { };
};
class A {
public:
A() try { throw E("Exception in A()"); }
catch(E& e) { cout << "Handler in A(): " << e.error << endl; }
};
int f() try {
throw E("Exception in f()");
return 0;
}
catch(E& e) {
cout << "Handler in f(): " << e.error << endl;
return 1;
}
int main() {
int i = 0;
try { A cow; }
catch(E& e) {
cout << "Handler in main(): " << e.error << endl;
}
try { i = f(); }
catch(E& e) {
cout << "Another handler in main(): " << e.error << endl;
}
cout << "Returned value of f(): " << i << endl;
}The following is the output of the above example: Handler in A(): Exception in A()
Handler in main(): Exception in A()
Handler in f(): Exception in f()
Returned value of f(): 1
Beginning of C++0x only.
If the delegating process exists and an exception occurs in the
body of a target constructor, the exception can be caught by an appropriate
handler in the try block of the delegating constructor. The following
example demonstrates this:
#include <cstdio>
using std::printf;
int global_argc;
struct A{
int _x;
A();
A(int);
};
A::A(int x):_x((printf("In A::A(int) initializer for A::_x.\n"),x)){
printf("In A::A(int) constructor body.\n");
if(global_argc % 2 !=0){
printf("Will throw.\n");
throw 0;
}
printf("Will not throw.\n");
}
A::A() try:A((printf("In A::A() initializer for delegating to A::A(int).\n"),42)){
printf("In A::A() function-try-block body.\n");
}
catch(...){
printf("In catch(...) handler for A::A() function-try-block.\n");
}
int main(int argc, char **argv){
printf("In main().\n");
global_argc = argc;
try{
A a;
printf("Back in main().\n");
}
catch(...){
printf("In catch(...) handler for try-block in main().\n");
}
return 0;
}The example can produce different output depending on how many
arguments are passed on the invocation of the resulting program. With
an even number of arguments, the exception is thrown. The output is:
In main().
In A::A() initializer for delegating to A:A(int).
In A::A(int) initializer for A::_x.
In A::A(int) constructor body.
Will throw.
In catch(...) handler for A::A() function-try-block.
In catch(...) handler for try-block in main().With an odd number of arguments, there is no exception thrown.
The output is:
In main().
In A::A() initializer for delegating to A::A(int).
In A::A(int) initializer for A::_x.
In A::A(int) constructor body.
Will not throw.
In A::A() function-try-block body.
Back in main().For more information, see Delegating constructors (C++0x)
End of C++0x only.