CMPT
225 Lab - Exception Handling
This lab will cover the basics of
handling errors in C++. It uses an amended version of the class from the
previous week's lab as an example. Note that this is just a brief introduction
to the mechanics of throwing and handling exceptions in C++. If you want to
know more this is not a bad place to start.
Preliminaries
Make a new project, add a class called ArrayClass and
copy and paste the code provided at the end of this document into your ArrayClass .h and .cpp files. This version of the class we
looked at in last week's lab has two new methods, get and set, which allow
individual array elements to be accessed and changed.
The get
and set methods will result in errors
if the index given to the method is invalid (greater than current size, or less
than zero).
Exception
Handling
There are three components to C++ exception
handling, a try block that contains
function calls that can throw
exceptions, and a catch block that
handles those exceptions.
Try
The try block is just that, the keyword try followed by a {
... } block that contains a function or functions that might throw
errors.
Catch
Every try
block should be followed by at least one catch
block (and often more than one). The catch
keyword is followed by ()s which contain the exception
type to be handled and a { ... } block which contains the exception handling
code.
Throw
Functions that throw exceptions contain an if statement that identifies the
exception condition. The if block contains a throw expression which consists of the throw keyword followed by a parameter. The parameter is often an
exception object, but it could be almost anything including integers or
strings.
Throwing
Exceptions
Let's start
by throwing an error in the get
method.
Get Method
The get
method should throw an exception when its index parameter is invalid. Here is
the method.
int ArrayClass::get(int i)
{
return arr[i];
}
First, we
need to identify what an illegal index is. The class attributes n and maxSize keep track of the current
(number of items in the array) and maximum size of the array. It only makes
sense to get elements of the array that have been inserted, that is elements
with indexes between 0 and n-1.
So, we can
insert an if statement to identify the error condition:
int ArrayClass::get(int i)
{
if(i < 0 || i >= n) /* throw exception */;
return arr[i];
}
The next decision is what exception to throw.
As mentioned above this can be pretty much anything. However, we will generally
follow the practice of throwing standard exceptions. These are defined in the stdexcept
library, so the class needs to have an #include
<stdexcept> statement in the header file.
This library includes classes that can be used to identify a
number of common exceptions. The one we want is the out_of_range exception.
The throw clause consists of the keyword
throw, and an out_of_range
object whose constructor has a string parameter that can be used to identify
the exception.
int ArrayClass::get(int i)
{
if(i < 0 || i >= n) throw std::out_of_range("get(i) out of
range");
return arr[i];
}
A throw expression
behaves somewhat similarly to a return statement.
When a throw expression is reached
the function terminates and the value specified in the throw expression is passed to another function (like a returned
value). The big difference is that a return
statement always returns a value to the preceding function call (on the
call stack). A throw statement will
bypass any preceding function calls that do not have an appropriate catch
statement, so may result in multiple functions being removed from the stack
(known as unwinding the stack).
Catching
Errors
Code (usually function calls) that might cause
errors should be enclosed in a try
block, and a catch block (or multiple
catch blocks) handle those errors.
The file below
contains a main function and a test function that demonstrates basic exception
handling for the get function.
There are a few things to note about this
small test.
The catch block doesn't do anything except report
on the error.
The catch block only deals with out_of_range
errors. If the code in the try block could throw errors of more than one type
it might be appropriate to have multiple catch blocks, where each block handles
a different error type.
In this small example, the try and catch blocks are
in the function that directly called the method that threw the error. They
don't have to be. For example, where function f1 calls f2, which calls f3 which calls f4, and f4 might throw
more than one exception type. The catch blocks that deal with these error types
could be in any of f1, f2 or f3 (or main), and catch
blocks for the different error types can be in different functions.
Over to
You
Add a throw clause to the set method (it's pretty much identical
to the get method), then add some
code to the test function to call the methods with some errors. Enclose the
method calls in a try block and write
catch blocks to print an error
message.
ArrayClass
The class definition and method definitions
for the ArrayClass class are provided below.
John Edgar (johnwill@sfu.ca)