Exception Handling

An exception is a situation which needs to be handled in a particular way and which is not part of the normal, expected flow of a program.


Some examples of exceptions

It is worth noticing the differences among these:


What can we do about exceptions?

There are a couple of elementary ways to handle the divide-by zero exception:

Write code each time and exit if necessary

        if (Count == 0)
        {
         cerr << "Unable to compute the average as Count is 0" << endl;
         exit(1);
        }
       else
        Average = (float) Sum / Count;

In order to do this, we would need:

        #include <iostream>
        #include <cstdlib>

and

        using std::cerr;
        using std::endl;
        using std::exit;

Write code each time and use assert

        assert(Count == 0);
        Average = (float) Sum / Count;

In order to use the assert macro, we need:

        #include <cassert>

and

        using std::assert;

These methods have disadvantages:


How does C++ do this?

C++ contains a mechanism for managing exceptions. It has three parts, known as try, throw and catch. We refer to a Try block and a Catch block. The format looks something like this:

        try
         {
          if (Count == 0)
           throw Div_By_Zero;  // This is the exception value thrown.
                               // Here Div_By_Zero is a constant int value.
         }

        catch(int I)
         { 
          if (I == Div_By_Zero)
           {
            cerr << "Unable to compute the average as Count is 0" << endl;
            exit(1);
           }
          }

The idea here is that if the throw statement is executed, normal execution is suspended and the catch block will be activated. It accepts an int argument, matching the value that is thrown.

We could instead have a function throw the exception, a much more common technique:

        float Divide(int S, int C) throw(int)
         {
          if (C == 0)
           throw Div_By_Zero;
          else
           return (float) S / C;
         }

         try  // in the main program
          {
           Average = Divide(Sum, Count);
          }

where the Catch block would be as before (in the main program).

The advantage of using Try and Catch is that error-handling is centralized. If we need to change an error message, we need to change it in only one place.

The code in a Try block is sometimes called a guarded section of code.


What kind of value is thrown?

The value that is thrown will be of some type: int, char, float, etc, or an instance of a class. It could be a defined constant, a literal, or an expression. The standard C++ library includes classes for exceptions with names such as out_of_range and domain_error. We could use one of those, as in:

         throw out_of_range("attempt to divide by 0");
(P>in which case the catch block would have an out_of_range argument. Here the expression
         out_of_range("attempt to divide by 0")

is using the out_of_range constructor with a C-string argument to create an instance of the out_of_range class. The C-string is copied into a data member of the class, and there is an access function named what() to allow use of that data member in the catch block.

We could invent our own exception class. Normally such a class will contain at least:

In the example above, notice that the heading for the function Divide says that it may thrown an exception of type int. This means it may throw an exception, but only of type int.

If we didn't specify a type, as in

        float Divide(int S, int C)

the function would be allowed to throw any type of exception at all.

If instead we wrote

        float DivideIint S, int C) throw()

the function would not be allowed to throw any exceptions at all.


How do catch blocks work?

A catch block has one argument, naming the type of exception it is intended to handle, or "..." if it is for all types. The Catch block will be executed if the value thrown matches the type of the argument. The value can then be used in the Catch block under the name of the Catch block parameter. For instance, we could have a Catch block with an int argument and use a switch statement to decide what to do depending on the specific int value thrown.

The argument of a catch block may be used by value or by reference, much like the argument of a function. If it is used by value, the catch block is given a copy to work with. If it is used by reference, the catch block is handed the actual argument itself. As usual, one reason to use a reference is to avoid unnecessary copy operations, saving time and memory.

We could have a series of catch blocks, each to catch one kind of exception. If an exception is thrown, the type of value will be checked against each Catch block in turn until a match is found. At most one Catch block will be executed; the others will be ignored. If we have a catch block with argument list "...", it will normally be at the end of the series of catch blocks.

Suppose the Catch block handles the exception but does not terminate the overall program. What happens next? Control will go to the first statement after the last Catch block in the sequence. (Again, we may have a sequence of Catch blocks, each to handle one type of thrown value. At most one of them will be executed.)

An option in a catch block is to rethrow the current exception, passing it up the line (as a reference, not a copy) to catch blocks elsewhere in a "higher execution context" such as the function that called the current function. This is done with:

          throw;

The g++ compiler (which we use in this course) requires that we have at least one catch block wherever we have at least one try block. If we want to put the exception-handling code elsewhere, we will need to have at least a minimal catch block such as

          catch(...)
          {
           throw;
          }

in the same function as the try block. (This requirement may be compiler-dependent, but that is not clear.)

Suppose an exception is thrown but we never find a matching catch block. This results in an unhandled exception exception, and the overall program will probably terminate.

If no exception is thrown, the code in the catch block will not be executed; there is no other way to reach it.


What can a catch block do?

A catch block can do any of a number of things:

If the catch block does not terminate the program, execution resumes at the next line after the last catch block in the series of catch blocks.