Handling Errors Exceptionally Well in C++


By Alex Allain
One benefit of C++ over C is its exception handling system. An exception is a situation in which a program has an unexpected circumstance that the section of code containing the problem is not explicitly designed to handle. In C++, exception handling is useful because it makes it easy to separate the error handling code from the code written to handle the chores of the program. Doing so makes reading and writing the code easier.



Furthermore, exception handling in C++ propagates the exceptions up the stack; therefore, if there are several functions called, but only one function that needs to reliably deal with errors, the method C++ uses to handle exceptions means that it can easily handle those exceptions without any code in the intermediate functions. One consequence is that functions don't need to return error codes, freeing their return values for program logic.

When errors occur, the function generating the error can 'throw' an exception. For example, take a sample function that does division:
const int DivideByZero = 10;
//....
double divide(double x, double y)
{
    if(y==0)
    {
        throw DivideByZero;
    }
    return x/y;
}
The function will throw DivideByZero as an exception that can then be caught by an exception-handling catch statement that catches exceptions of type int. The necessary construction for catching exceptions is a try catch system. If you wish to have your program check for exceptions, you must enclose the code that may have exceptions thrown in a try block. For example:
try
{
    divide(10, 0);
}
catch(int i)
{
    if(i==DivideByZero)
    {
        cerr<<"Divide by zero error";
    }
}
The catch statement catches exceptions that are of the proper type. You can, for example, throw objects of a class to differentiate between several different exceptions. As well, once a catch statement is executed, the program continues to run from the end of the catch.

It is often more useful for you to create a class that stores information on exceptions as they occur. For example, it would be more useful if you had a class to handle exceptions.
class DivideByZero
{
    public:
        double divisor;
        DivideByZero(double x);
};
DivideByZero::DivideByZero(double x) : divisor(x)
{}
int divide(int x, int y)
{
    if(y==0)
    {
        throw DivideByZero(x);
    }
}
try
{
    divide(12, 0);
}
catch (DivideByZero divZero)
{
    cerr<<"Attempted to divide "<<divZero.divisor<<" by zero";
}

If you wish to catch more than one possible exception, you can specify separate catch blocks for each type of exception. It's also possible to have a general exception handler that will respond to any thrown exception. To use it, simply use catch(...) for the catch statement and print a general warning of some kind.

The handy thing to remember about exception handling is that the errors can be handled outside of the regular code. This means that it is easier to structure the program code, and it makes dealing with errors more centralized. Finally, because the exception is passed back up the stack of calling functions, you can handle errors at any place you choose.

In C, you might see some error handling code to free memory and close files repeated five or six times, once for each possible error. A solution some programmers preferred was to use a goto statement that jumped all the way to the cleanup code. Now, you can just surround your code with a try-catch block and handle any cleanup following the catch (possibly with an additional copy of your cleanup routine inside the catch block if you intend to throw the exception again to alert the calling function of an error).
Related articles
Error Handling in C Using Goto