In this (hopefully) simple lab, you will add some better error-handling capabilities to the Rational_C class that you created in Lab08. Specifically, you will add what are called exceptions.
Thus far in your programming, when an error has occurred (for instance, bad input or something like that), your program has either (1) crashed, or (2) told the user about it and possibly exited. If the error occurred inside a function (other than main), then that function had to deal with the error, either by returning an error value, telling the user about the error, or exiting.
But sometimes it is not clear exactly what should be done when an error
occurs, especially within code that is reusable. For example, consider
your Rational_C
class from lab08. An error can occur if a
program tries to construct an object with a denominator of zero (since
this will not be a rational number). But if such an error occurs, the
correct action to take depends on the situation. For example, if the
rational number was being constructed based on user input, maybe the
program should tell the user and propt for a new, non-zero denominator
value. But if the number is being constructed based on some data file,
say, then maybe the only thing to do is to exit.
The point is, sometimes we can't know in the context where the exception occurs (i.e. in the function or class definition), what should be done with it. In these cases, it is sometimes a good idea to use C++ exceptions. The function throws an exception and the program that called the function catches it, and then does what it needs to do. This way the function itself doesn't need to know how to handle the error, it just needs to tell its caller that one occurred.
Here's a paragraph from page 795 of Savitch:
Exception handling is commonly used to handle error situations, but perhaps a better way to view exceptions is as a way to handle exceptional situations. After all, if your code correctly handles an "error", then it no longer is an errorIn general, Walter Savitch is much better about explaining things than your TA is; that is why he got lots of money to write a book and your TA did not. So you should read chapter 18 in your Savitch textbook.
First, it is important to keep in mind that an exception can be of
any type, from int
to Rational_C
and
everyting in between. To throw an exception, you simply use the operator
throw
, as follows:
int x = 5; if( /* some sort of error condition */ ) { throw x; //or throw 5; }
If you have a function, say foo
, that
throws an exception, then it should declare that at the beginning of the
function prototype and definition, as follows:
// function prototype int foo(double x, bool y) throw (int); // ... the rest of your program // function specification int foo(double x, bool y) throw (int) { //... the code for the function if( /* error condition */ ) throw 5; }
You can also declare that you might throw multiple types of exceptions,
with something like int foo() throw (int,bool,char*);
. In this
lab, you won't have to deal with this condition, though.
When an exception is thrown, execution stops and goes back up to the
caller of the current function. It is up to the caller to catch
the exception. You do this with a try-catch block. The way this works is
you try to do something which may result in an exception being
thrown, and then catch any exception that gets thrown in the try
block. If any exception is thrown and not caught, the function
terminate()
will be called, which by default ends your program.
Here's an little fake example of two functions, which each throw one
exception, and a main program with tries to call these two functions and
catches their exceptions:
#include <csdio> #include <cstdlib> using namespace std; // shame on me //the function prototypes - pretend like they're defined later. void fun1() throw (int); void fun2() throw (char); int main(void) { try { int x = 5; fun1(); x += 2; fun2(); x += 3; } catch( int x ) { cout << "The function threw this int:" << x <<endl; } catch( char x ) { cout << "The function threw a char" << endl; cout << "I'm going to terminate." << endl; exit(0); } return 0; }
Notice a couple things here. First, you can have anything you want in the
try block, including things that will never ever throw an
exception (such as x += 3;
). Also, in your catch blocks, you
have to assign the exception to a variable (in this example, I used x). Then
that variable is assigned to the value of whatever was thrown, for the
duration of the catch block. Also, make sure you can figure out what the value
of x is at the end of the program if fun1 throws an exception, if fun2 throws
an exception, and if neither does. If you can't, copy and paste this code,
compile it, and find out for yourself.
The files for this lab are in the lab09 directory. I put a link to this directory at the following location on strauss: ~roche/lab09
. See previous labs for examples of how to copy these files into your directory. And remember, of course, to make a new lab09 subdirectory before you get started.
Copy the files into your lab09 directory. Replace the files rational.h and rational.cc with the completed versions you have from lab08.
Add another class to the rational.h file called DenomIsZero. Its entire definition will be:
class DenomIsZero {};
This will be the type of the exception you throw. In your functions, to
throw an exception of this type, just write throw DenomIsZero();
. See page 811 in Savitch for a great example of this sort of
thing.Look at the code in the file testExceptions.cc. You don't need to do too much to make this test pass, just basically add throw statements to two member functions in rational.cc and in rational.h.
Once your class passes both tests, make a script file where you do a make clean, then make all, then run both tests and show they all pass. Also be sure to cat your rational.h and rational.cc files.
For this lab, you do NOT have to make a tarfile. Just be sure to cat both your source files in your lab09.txt script file, and submit this both on paper and on WebCT.