Notice that these examples catch exceptions by reference, although for these classes it isn’t important because there are no additional members in the derived classes, and there are no argument identifiers in the handlers anyway. You’ll usually want to use reference arguments rather than value arguments in your handlers to avoid slicing off information.
Catching any exception
Sometimes you want to create a handler that catches
Catch(...) {
cout << "an exception was thrown" << endl;
}
An ellipsis catches any exception, so you’ll want to put it at the
Because the ellipsis gives you no possibility to have an argument, you can’t know anything about the exception or its type. It’s a "catchall." Such a catch clause is often used to clean up some resources and then rethrow the exception.
Re-throwing an exception
You usually want to re-throw an exception when you have some resource that needs to be released, such as a network connection or heap memory that needs to be deallocated. (See the section "Resource Management" later in this chapter for more detail). If an exception occurs, you don’t necessarily care what error caused the exception—you just want to close the connection you opened previously. After that, you’ll want to let some other context closer to the user (that is, higher up in the call chain) handle the exception. In this case the ellipsis specification is just what you want. You want to catch
catch(...) {
cout << "an exception was thrown" << endl;
// Deallocate your resource here, and then re-throw…
throw;
}
Any further catch clauses for the same try block are still ignored—the throw causes the exception to go to the exception handlers in the next-higher context. In addition, everything about the exception object is preserved, so the handler at the higher context that catches the specific exception type can extract any information the object may contain.
Uncaught exceptions
As we explained in the beginning of this chapter, exception handling is considered better than the traditional return-an-error-code technique because exceptions can’t be ignored. If none of the exception handlers following a particular try block matches an exception, that exception moves to the next-higher context, that is, the function or try block surrounding the try block that did not catch the exception. (The location of this try block is not always obvious at first glance, since it’s higher up in the call chain.) This process continues until, at some level, a handler matches the exception. At that point, the exception is considered "caught," and no further searching occurs.
The terminate( ) function
If no handler at any level catches the exception, the special library function terminate( ) (declared in the
The set_terminate( ) function
You can install your own terminate( ) function using the standard set_terminate( ) function, which returns a pointer to the terminate( ) function you are replacing (which will be the default library version the first time you call it), so you can restore it later if you want. Your custom terminate( ) must take no arguments and have a void return value. In addition, any terminate( ) handler you install must not return or throw an exception, but instead must execute some sort of program-termination logic. If terminate( ) is called, the problem is unrecoverable.
The following example shows the use of set_terminate( ). Here, the return value is saved and restored so that the terminate( ) function can be used to help isolate the section of code in which the uncaught exception is occurring:.
//: C01:Terminator.cpp