Exceptional Events
n This solution will work, but the problem is that -1 is a valid
integer, so we’d never know if the return value was legitimate
or signaling an error condition.
n We could set an arbitrary boolean flag in a global variable...
// Define a bad value to return when index is invalid
bool badIndex = false;
int badValue = -1;
// Overload [] to allow individual array element access.
int &MyIntArray::operator[](int index)
{
badIndex = false;
if ((index >= 0) && (index < stringLength))
return storagePtr[index];
badIndex = true;
return badValue; // still need to return something!
}
Exceptional Events
n OK, aside from the sheer ugliness of this solution, consider
the following problem…
n What happens if the array access happens in the middle of
an expression?
// Apply secret formula to two numbers at indices n and d
bool secretFormula(MyIntArray *array,int n,int d,
float &result)
{
result = array[n] / (1 + array[d]); // SECRET FORMULA !!!
// Check for bad index
if (badIndex)
return false; // Signal unsuccessful operation
// Signal that operation was successful
return true;
}
Exceptional Events
n If an invalid denominator index were passed,
MyIntArray::operator[] would have set badIndex to
true and returned badval which is -1.
n And, if you were on a machine that didn’t like divide by zero,
you might crash before ever getting to your validity check.
n So what we have here is a solution that is ugly and doesn’t
protect you from all situations!
n There must be a better way!
n Enter C++ exceptions.
n What is a C++ exception?
C++ Exceptions
n A C++ exception is an abrupt transfer of control, usually resulting
from an error condition.
n When an error condition is encountered, the programmer may
choose to throw an exception.
n This initiates an immediate transfer of control. But to where?
n An assumption is made that if the programmer has chosen to throw
an exception, he/she has also provided a place to catch the
exception.
n Perhaps a simple example would help...
enum MathErr { noErr, divByZero, genericOverflow };
float divide(float numerator,float denomiator)
{
if (denominator == 0)
throw divByZero;
return numerator/denominator;
}
Somebody Catch Me!!!
n An assumption is made that the programmer has set up a
place for exceptions to be caught when they occur.
n This is done with a try block.
n It looks something like this:
int main()
{
try {
cout << “3/2 is “ << divide(3,2) << endl;
cout << “2/0 is “ << divide(2,0) << endl;
}
catch(MathErr x) {
if (x == divByZero)
cerr << “Divide by zero caught. “ << endl;
else cerr << “Other error caught. “ << endl;
}
}
Somebody Catch Me!!!
n The try statement simply defines a scope inside which any
exceptions that occur might be caught by catch statements
immediately following the try.
n The catch statement is a little more complicated.
n It’s syntax is one of the following:
n catch(
type
variableName
) { }
n catch(…) { }
n The first form is somewhat like a function declaration.
n You specify a variable declaration which will be instantiated
by the value thrown if and only if that value matches (type
wise) the type declared in the catch statement.
n Inside the scope of the catch, the variable declared in the
catch statement is accessible as a local variable.