Software Design Using C++
C++ Exceptions
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
ordinary 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)
throw Num;
|
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:
try
{
PrintSequence(20);
}
catch(int ExNum)
{
cout << "Caught an exception with value: " << ExNum
<< endl;
}
|
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.
The 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
by the throw statement. So, when Num becomes too
big and an integer exception is generated inside PrintSequence ,
control is passed to the statement(s) inside the catch and
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:
try
{
PrintSequence(20);
}
catch(...)
{
cout << "Caught an exception" << endl;
}
|
You can also catch various types of exceptions separately. For example, you might use:
try
{
PrintSequence(20);
}
catch(float FloatNum)
{
cout << "Caught an exception with float value: " << FloatNum
<< endl;
}
catch(int IntNum)
{
cout << "Caught an exception with int value: " << IntNum
<< endl;
}
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.
- itemtype.h
- stack.h
Header file that sets up the abstract base class for stacks.
- arrstack.h
Header file that sets up the derived class for array-based stacks.
- arrstack.cpp
Revised to throw an exception if push or pop cannot complete.
- reverse.cpp
Revised to catch an exception generated by a push operation.
|
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
reverse.cpp program, the call to Push is inside
of a 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
the 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
char * .
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.
The InsertFront function uses GetNode , as shown
below, to create a new node holding the data to be inserted. (The
InsertRear and 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 throw
statement below. Whatever called InsertFront should try to
catch this exception and take appropriate action. (If not, the program
will terminate.)
try
{
NodePtr = GetNode(Item, Front->Next);
}
catch(char * Str)
{
cout << "Error: Could not insert at front -- " << Str
<< endl;
throw;
}
|
The 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)
{
throw Count;
}
|
Next, look at the lststack.cpp file. It uses
InsertFront to implement the Push operation,
and so needs to check if InsertFront has thrown an exception.
If a 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 Pop .
If such an exception is caught, a warning message is printed.
We could also have chosen to re-throw the exception if either Push
or Pop catches an exception. Then the reverse.cpp
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.
References:
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.
Osborne/McGraw-Hill (1998).
- Teach Yourself C++, 5th ed. Al Stevens.
MIS:Press (1997).
- C++ How to Program, 3rd ed. Deitel & Deitel.
Prentice-Hall (2001).
|
Related Item
Stacks
Back to the main page for Software Design Using C++
|