• If the newly thrown exception does not match the original exception specification and if the exception specification
• If the newly thrown exception does not match the original exception specification and if the original exception specification
In short, if you’d like to catch all exceptions, expected or otherwise, you can do something like the following. First, you make sure the exception header file declarations are available:
#include
using namespace std;
Next, you design a replacement function that converts unexpected exceptions to the bad_exception type and that has the proper prototype:
void myUnexpected()
{
throw std::bad_exception(); //or just throw;
}
Just using throw without an exception causes the original exception to be rethrown. However, the exception will be replaced with a bad_exception object if the exception specification includes that type.
Next, at the start of the program, you designate this function as your chosen unexpected exception action:
set_unexpected(myUnexpected);
Finally, you include the bad_exception type in exception specifications and catch block sequences:
double Argh(double, double) throw(out_of_bounds, bad_exception);
...
try {
x = Argh(a, b);
}
catch(out_of_bounds & ex)
{
...
}
catch(bad_exception & ex)
{
...
}
Exception Cautions
From the preceding discussion of using exceptions, you might gather (and gather correctly) that exception handling should be designed into a program rather than tacked on. Doing this has some disadvantages, though. For example, using exceptions adds to the size and subtracts from the speed of a program. Exception specifications don’t work well with templates because template functions might throw different kinds of exceptions, depending on the particular specialization used. Exceptions and dynamic memory allocation don’t always work that well together.
Let’s look a little further at dynamic memory allocation and exceptions. First, consider the following function:
void test1(int n)
{
string mesg("I'm trapped in an endless loop");
...
if (oh_no)
throw exception();
...
return;
}
The string class uses dynamic memory allocation. Normally, the string destructor for mesg would be called when the function reached return and terminated. Thanks to stack unwinding, the throw statement, even though it terminates the function prematurely, still allows the destructor to be called. So in this case, memory is managed properly.
Now consider this function:
void test2(int n)
{
double * ar = new double[n];
...
if (oh_no)
throw exception();
...
delete [] ar;
return;
}
Here there is a problem. Unwinding the stack removes the variable ar from the stack. But the premature termination of the function means that the delete [] statement at the end of the function is skipped. The pointer is gone, but the memory block it pointed to is still intact and inaccessible. In short, there is a memory leak.
The leak can be avoided. For example, you can catch the exception in the same function that throws it, put some cleanup code into the catch block, and rethrow the exception:
void test3(int n)
{
double * ar = new double[n];
...
try {
if (oh_no)
throw exception();
}
catch(exception & ex)
{
delete [] ar;
throw;
}
...
delete [] ar;
return;
}
However, this clearly enhances the opportunities for oversights and other errors. Another solution is to use one of the smart pointer templates discussed in Chapter 16, “The string Class and the Standard Template Library.”
In short, although exception handling is extremely important for some projects, it does have costs in terms of programming effort, program size, and program speed. On the other hand, the cost of no error checking can be much worse.
Exception Handling
In modern libraries, exception handling can appear to reach new levels of complexity—much of it due to undocumented or poorly documented exception-handling routines. Anyone familiar with the use of a modern operating system has surely seen the errors and problems caused by unhandled exceptions. The programmers behind these errors often face an uphill battle, learning the complexity that lies within the libraries: what exceptions are thrown, why and when they occur, and how to handle them.