CS 250 Computer Programming and Problem Solving - FALL 1998 |
---|
[ Home | Syllabus | Notes | Glossary | CS250 WEB Version Home Page | Project Page | Final Study Guide]
Exception Handling
Error handling is a difficult business, particularly if we want to write general purpose objects which can be used in many applications.
Consider an example from test 2 which calculates the average of all values in an array.
long average(LongArray theArray) { long ave = 0; for(int i = 0; i < theArray.GetSize(); i++) { ave += theArray.Get(i); } return ave/theArray.GetSize(); }
What will happen if the array is empty?
How should this be handled. Consider several alternatives.
1. function tests size and if 0 issues error message and then returns
long average(LongArray theArray) { long ave = 0; for(int i = 0; i < theArray.GetSize(); i++) { ave += theArray.Get(i); } if(theArray.GetSize() == 0) { cerr << " attempt to divide by 0 in average\n"; return 0; } return ave/theArray.GetSize(); }
What do you think of this solution?
2. Try a different solution which aborts on error
long average(LongArray theArray) { long ave = 0; for(int i = 0; i < theArray.GetSize(); i++) { ave += theArray.Get(i); } assert(theArray.GetSize() != 0); // abort if error possible return ave/theArray.GetSize(); }
What do you think of this solution?
3. Let the calling program decide what to do.
long average(LongArray theArray) { long ave = 0; for(int i = 0; i < theArray.GetSize(); i++) { ave += theArray.Get(i); } if(theArray.GetSize() == 0) { return DivideZero; // where ErrorValue is a special value } return ave/theArray.GetSize(); }
4. Return two values - the one we want and an error value.
bool average(LongArray theArray, long & theAverage) { long ave = 0; for(int i = 0; i < theArray.GetSize(); i++) { ave += theArray.Get(i); } if(theArray.GetSize() == 0) { cerr << " attempt to divide by 0 in average\n"; return false; } theAverage = ave/theArray.GetSize(); return true; }
5. Use structured exceptions.
long average(LongArray theArray) { long ave = 0; for(int i = 0; i < theArray.GetSize(); i++) { ave += theArray.Get(i); } if(theArray.GetSize() == 0) { throw -1; } return ave/theArray.GetSize(); }
// In the calling program
try { cout << " the average is " << average(myArray); } catch(int error) { cerr << " exception:" << error << " occurred - ignoring it.\n"; }
long average(LongArray theArray) { long ave = 0; for(int i = 0; i < theArray.GetSize(); i++) { ave += theArray.Get(i); } if(theArray.GetSize() == 0) { throw -1; } return ave/theArray.GetSize(); }
long middleMan(LongArray someArray){ return average(someArray); }
// In the calling program
try { cout << " the average is " << middleMan(myArray); } catch(int error) { cerr << " exception:" << error << " occurred - ignoring it.\n"; }
6. Using Exception Classes
class DivideByZero{}; // define exception class
long average(LongArray theArray) { long ave = 0; for(int i = 0; i < theArray.GetSize(); i++) { ave += theArray.Get(i); } if(theArray.GetSize() == 0) { throw DivideByZero(); } return ave/theArray.GetSize(); }
// In the calling program
try { cout << " the average is " << average(myArray); } catch(const DivideByZero& ) { // notice unnamed parameter because not needed cerr << " exception: DivideBy Zero occurred - ignoring it.\n"; }
7. Handling two exceptions with different catch blocks
class DivideByZero{}; // define exception class
long average(LongArray theArray) { long ave = 0; for(int i = 0; i < theArray.GetSize(); i++) { ave += theArray.Get(i); } if(theArray.GetSize() == 0) { throw DivideByZero(); } if(theArray.GetSize() == 1) { // stupid to average one thing throw -1; } return ave/theArray.GetSize(); }
// In the calling program
try { cout << " the average is " << average(myArray); } catch(const DivideByZero& ) { // notice unnamed parameter because not needed cerr << " exception: DivideBy Zero occurred - ignoring it.\n"; } catch(int) { // don't use the parameter here either cerr << " why be so stupid to take the average of one thing!!!\n"; }
8. Handling two exceptions in different functions
class DivideByZero{}; // define exception class
long average(LongArray theArray) { long ave = 0; for(int i = 0; i < theArray.GetSize(); i++) { ave += theArray.Get(i); } if(theArray.GetSize() == 0) { throw DivideByZero(); } if(theArray.GetSize() == 1) { // stupid to average one thing throw -1; } return ave/theArray.GetSize(); }
long middleMan(LongArray someArray){ try{ return average(someArray); } catch(int) { // don't use the parameter here either cerr << " why be so stupid to take the average of one thing!!!\n"; } }
// In the calling program
try { cout << " the average is " << middleMan(myArray); } catch(const DivideByZero& ) { // notice unnamed parameter because not needed cerr << " exception: DivideBy Zero occurred - ignoring it.\n"; }
A more sophisticated example demonstrating
// RANGE.H - RangeError exception class #ifndef RANGE_H #define RANGE_H #include <iostream.h> #include <string.h> const unsigned FileNameSize = 40; // Make this >= longest filename on target system. class RangeError { public: RangeError( const char * fname, unsigned line, unsigned subscr ) { strncpy(fileName, fname, FileNameSize); lineNumber = line; value = subscr; } friend ostream & operator <<( ostream & os, const RangeError & R ) { os << "\nRangeError exception thrown: " << R.fileName << ", line " << R.lineNumber << " value = " << R.value << endl; return os; } private: char fileName[FileNameSize+1]; unsigned lineNumber; unsigned value; }; #endif
// EXCEPT3.CPP - Exception-handling example from Section 8.3.3. // This program uses a more complete specification for the // RangeError exception class, simplifying the calling program. #include <iostream.h> #include <fstream.h> #include "fstring.h" #include "range.h" #include <stdlib.h>
class MemoryExhausted{}; class LongArray { public: LongArray( unsigned sz = 0 ); ~LongArray(); unsigned GetSize() const; long Get( unsigned i ) const; void Put( unsigned i, long item ); private: long * data; // array of long integers unsigned size; // allocation size }; LongArray::LongArray(unsigned sz) { size = sz; data = new long[size]; if(!data) throw MemoryExhausted(); } inline LongArray::~LongArray() { delete [] data; } inline unsigned LongArray::GetSize() const { return size; } inline void LongArray::Put( unsigned i, long item ) { if( i >= size ) throw RangeError( __FILE__ ,__LINE__, i ); data[i] = item; } inline long LongArray::Get( unsigned i ) const { if( i >= size ) throw RangeError( __FILE__ ,__LINE__, i ); return data[i]; } //---------------( test program )-------------------- unsigned GetArraySize() { unsigned n; cout << "Number of array elements? "; cin >> n; return n; } // Fill the array with random integers. void FillArray( LongArray & L ) { int i; try { for( i = 0; i < L.GetSize(); i++ ) L.Put( i, rand() ); } catch( const RangeError & R ) { cout << R; throw R; } } // Retrieve an array element. void GetArrayElement( const LongArray & L ) { int ok = 0; while( !ok ) { unsigned i; cout << "Enter an array subscript (0-" << ( L.GetSize()-1 ) << "): "; cin >> i; long n; try { n = L.Get( i ); ok = 1; cout << "Element contains " << n << endl; } catch( const RangeError & R ) { cout << R; cout << "Caught at: " << __FILE__ << ", line " << __LINE__ << endl; throw; } } } int main() { try { LongArray L( GetArraySize() ); FillArray( L ); GetArrayElement( L ); } catch( ... ) { cout << "Exception caught in main().\n"; return 1; } return 0; }