Software Design Using C++
There's an exception to every rule.
What Are Exceptions?
In these Web pages we typically handle error conditions by detecting them
with some sort of
if test and then taking appropriate action
when the condition is true. Often the action is to print an error message
and exit the program. An alternate way to handle error conditions is to
"throw" an exception when some error condition becomes true. One then "tries"
any code that might cause such an exception, "catching" any exception that
was raised, and taking appropriate action when an exception is caught. An
example will help to clarify things:
A Simple Example
Take a look through the code for the above program. Note how the
PrintSequence function contains what appears to be an infinite
loop. Of course, it is not usually sensible to use an infinite loop, even
if one has a way to jump out of the middle of it. A much more sensible way
to print a sequence of numbers, which is what this function does, is to use an
for loop. So, this example was invented just to
illustrate exceptions, not to give a practical way to print a sequence of
numbers. As for the infinite loop in the example,
what saves the day is the following section of code:
if (Num >= StopNum)
If the variable
Num becomes too big we get out of the loop by
throwing an exception. The most important thing about the exception is that
it is an integer exception. The value of the exception is the current value
of Num, but sometimes the actual value isn't used for anything.
So, what happens when the exception is thrown? It depends on whether or not
we have any code that catches the exception. In this program we do. It is
found in the main function:
cout << "Caught an exception with value: " << ExNum
The main function uses the "try" construct to enclose the call of the
PrintSequence function. There could be many lines of code
inside of the
try section if need be.
catch is used in this
case to catch any integer exception that might be generated. The variable
ExNum will be given the integer value given to the exception
throw statement. So, when
Num becomes too
big and an integer exception is generated inside
control is passed to the statement(s) inside the
then proceeds to whatever follows the "try...catch..." construct. In our
case a message is printed. The value of
ExNum doesn't have to
be used unless you want to.
Note that if any other type of exception were generated (say a float or a
pointer to a
char), the above catch would not catch the exception.
An uncaught exception would cause the program to terminate.
Our catch was for integer exceptions only. If you want to catch all possible
types of exceptions you can use the following:
cout << "Caught an exception" << endl;
You can also catch various types of exceptions separately. For example, you might use:
cout << "Caught an exception with float value: " << FloatNum
cout << "Caught an exception with int value: " << IntNum
catch(...) // catch anything else
cout << "Caught an exception with type not int or float" << endl;
Exceptions in an Array-Based Stack
Let's look at a more practical example of where we might use exceptions.
The following is a revised version of our earlier
array-based stack program.
Exception handling has been used to handle the various error cases that might occur.
As you can see in the next to the last file, if there is no room to push
an item, an integer exception is thrown. Similarly, if there is no item to
pop, that operation generates an integer exception. In the
Header file that sets up the abstract base class for stacks.
Header file that sets up the derived class for array-based stacks.
Revised to throw an exception if push or pop cannot complete.
Revised to catch an exception generated by a push operation.
reverse.cpp program, the call to
Push is inside
try statement. If an integer exception is caught, a warning
message is printed to tell the user that there was no room to push the item.
Although it would be possible to use a try...catch... construct with the
Pop operation, no exception will be generated by this code
since it is inside of a
while (! Stack.Empty()) loop. Thus no
attempt is made to pop from an empty stack.
Exceptions in a List-Based Stack
A more involved example is provided by revising our old
list-based stack program
to use exception handling.
Let's look first at the
list.cpp file. In particular, let's look at
GetNode function. Part of the code for this function is repeated below:
NodePtr = new ListNodeClass(Item, NextPtr);
if (NodePtr == NULL)
throw "Cannot allocate memory";
Note that the value of the exception is a literal string. It is stored as
a character array. Remember that an array name is seen as a pointer to the
first character in the array. Thus, the type of the exception is seen as
Warning: Check your compiler to see how the
new operation behaves
when it cannot allocate space. Look in your documentation or try the help menu.
According to the most recent C++ standard as of this writing, it should throw an
exception of type
bad_alloc, but Visual C++ (as of version 6.0) apparently
just returns a value of
NULL, which is what many other compilers do as well.
In the same file, look at the
ListClass constructor. It allocates
a dummy node for the front of the list. If an exception of type
char * is raised when trying to do this, an error message is
printed that contains the (string) value of the exception and the program
is exited. Of course, the string printed is the literal string used when
the exception is thrown by
GetNode: "Cannot allocate memory".
The choice to exit the program was made because it seemed to be a rather
serious error if we cannot even allocate space for the dummy node of a list.
InsertFront function uses
GetNode, as shown
below, to create a new node holding the data to be inserted. (The
InsertInOrder functions also do this.)
If an exception of type
char * is caught, then we print an error
message containing the (string) value of the exception and re-throw the
exception. Re-throwing the exception is accomplished by the
statement below. Whatever called
InsertFront should try to
catch this exception and take appropriate action. (If not, the program
NodePtr = GetNode(Item, Front->Next);
catch(char * Str)
cout << "Error: Could not insert at front -- " << Str
RemoveFront function checks the count of how many items are
on the list before trying to remove. If that number is zero, it throws an
exception. Note that this one is an integer exception. The relevant
section of code follows:
if (Count == 0)
Next, look at the
lststack.cpp file. It uses
InsertFront to implement the
and so needs to check if
InsertFront has thrown an exception.
char * exception is raised,
Push prints an
error message. However, we have chosen not to exit the program, thinking
that perhaps this error is not fatal and that the user might want to continue
in spite of the error.
In the same file you see the
Pop function. It checks for an
integer exception, since that is the type that might be raised by the
RemoveFront function that is called by
If such an exception is caught, a warning message is printed.
We could also have chosen to re-throw the exception if either
Pop catches an exception. Then the
application program would need to contain code to catch such exceptions, much
like we did in the above array-based stack program.
It seemed to be sufficient to let it go as is.
There is a lot more to exception handling than has been explained here.
See a good reference book, such as one of the following, for more details.
- C++ from the Ground Up, 2nd ed. Herbert Schildt.
- Teach Yourself C++, 5th ed. Al Stevens.
- C++ How to Program, 3rd ed. Deitel & Deitel.
Back to the main page for Software Design Using C++