Only the Up and Fit exceptions are caught because those are the only exceptions that the programmer of f( ) said would be thrown. Version two of g( ) causes my_unexpected( ) to be called because f( ) then throws an int.
In the call to set_unexpected( ), the return value is ignored, but it can also be saved in a pointer to function and be restored later, as we did in the set_terminate( ) example earlier in this chapter.
A typical unexpected handler logs the error and terminates the program by calling exit( ). It can, however, throw another exception (or re-throw the same exception) or call abort( ). If it throws an exception of a type allowed by the function whose specification was originally violated, the search resumes at the
If the exception thrown from your unexpected handler is not allowed by the original function’s specification, one of the following occurs:
1. If std::bad_exception (defined in
2. If the original function’s specification did not include std::bad_exception, terminate( ) is called.
The following program illustrates this behavior.
//: C01:BadException.cpp
//{-bor}
#include
#include
#include
using namespace std;
// Exception classes:
class A {};
class B {};
// terminate() handler
void my_thandler() {
cout << "terminate called\n";
exit(0);
}
// unexpected() handlers
void my_uhandler1() {
throw A();
}
void my_uhandler2() {
throw;
}
// If we embed this throw statement in f or g,
// the compiler detects the violation and reports
// an error, so we put it in its own function.
void t() {
throw B();
}
void f() throw(A) {
t();
}
void g() throw(A, bad_exception) {
t();
}
int main() {
set_terminate(my_thandler);
set_unexpected(my_uhandler1);
try {
f();
}
catch (A&) {
cout << "caught an A from f\n";
}
set_unexpected(my_uhandler2);
try {
g();
}
catch (bad_exception&) {
cout << "caught a bad_exception from g\n";
}
try {
f();
}
catch (...) {
cout << "This will never print\n";
}
} ///:~
The my_uhandler1( ) handler throws an acceptable exception (A), so execution resumes at the first catch, which succeeds. The my_uhandler2( ) handler does not throw a valid exception (B), but since g specifies bad_exception, the B exception is replaced by a bad_exception object, and the second catch also succeeds. Since f does not include bad_exception in its specification, my_thandler( ) is called as a terminate handler. Thus, the output from this program is as follows:.
caught an A from f
caught a bad_exception from g
terminate called
Better exception specifications?
You may feel that the existing exception specification rules aren’t very safe, and that
void f();
void f() throw(...); // Not in C++
This would surely be an improvement because function declarations would be more explicit. Unfortunately, you can’t always know by looking at the code in a function whether an exception will be thrown—it could happen because of a memory allocation, for example. Worse, existing functions written before exception handling was introduced may find themselves inadvertently throwing exceptions because of the functions they call (which might be linked into new, exception-throwing versions). Hence, the uninformative situation whereby.
void f();
means, "Maybe I’ll throw an exception, maybe I won’t." This ambiguity is necessary to avoid hindering code evolution. If you want to specify that f throws no exceptions, use the empty list, as in:.
void f() throw();
Exception specifications and inheritance