Chapter 19 - Exception Handling


Chapter Goals



19.1 Handling Exceptional Situations


19.1 Handling Exceptional Situations (cont.)


19.1 Handling Exceptional Situations - Example

Consider a container class (chptr. 16), and programmers P1 and P2:


19.1 Handling Exceptional Situations - Example


19.2 Alternative Mechanisms for Handling Exceptions

Alternative (historical) ways of handling exceptional conditions:


19.3 Exceptions

try-catch-throw mechanism


19.3 Exceptions

Syntax 19.1: Throwing an Exception
throw expression;
Example:
throw logic_error("illegal future_value parameter");
Purpose: Abandon this function and throw a value to an exception handler.

19.3 Exceptions - Example

double future_value(double initial_balance, double p, int n)
{
   if (p < 0 || n < 0)
   {
      logic_error description("illegal future_value parameter");
      throw description;
   }
   return initial_balance * pow(1 + p / 100, n);
}

Note: logic_error is a standard exception class, declared in <stdexcept>


19.3.1 Catching Exceptions


19.3.1 Catching Exceptions - Syntax

Syntax 19.2 : try Block
try
{
   statements
}
catch (type_name1 variable_name1)
{
   statements
}
catch (type_name2 variable_name2)
{
   statements
}
...
catch (type_namen variable_namen)
{
	statements
}

19.3.1 Catching Exceptions - Syntax (cont.)

Syntax 19.2 (cont.)
Example:
try
{
   List staff = read_list();
   process_list(staff);
}
catch (logic_error& e)
{
   cout << "Processing error " << e.what() << "\n";
}
Purpose: Provide one or more handlers for types of exceptions that may be thrown when executing a block of statements.

19.3.1 try Block - Example


19.3.1 try Block - Example

int main()
{
   bool more = true;
   while (more)
   {
      try
      {
         code
      }
      catch (logic_error& e)
      {
         cout << "A logic error has occurred: "
            << e.what() << "\n" << "Retry? (y/n)";
         string input;
         getline(cin, input);
         if (input == "n") more = false;
      }
   }
}

19.3.2 Values Thrown and Caught


19.3.2 Values Thrown and Caught (cont.)

This doesn't work as intended:

try
{
   . . .
   throw "Stack Underflow";
   . . .
}
catch (string err)
{
   cerr << err << "\n";
}

19.3.2 (cont.) Throwing Objects

Users often define their own exceptions:

class MyApplicationError
{
public:
   MyApplicationError(const string& r);
   string& what() const;
private:
   string reason;
};

MyApplicationError::MyApplicationError(const string& r)
   : reason(r) {}

string& what() const
{
   return reason;
}

19.3.2 (cont.) Throwing Objects

Errors are now indicated by throwing an instance of this class:

try
{
   . . .
   throw MyApplicationError("illegal value");
   . . .
}
catch (MyApplicationError& e)
{
   cerr << "Caught exception " << e.what() << "\n";
}

19.3.2 (cont.) Objects as Exceptions


19.3.2 (cont.) Standard exception hierarchy (partial), in <stdexcept>


19.3.2 (cont.) Inheriting From Standard Exceptions


19.3.2 (cont.) Exceptions & Inheritance


19.3.2 Exceptions & Inheritance (cont.)


19.3.2 (cont.) catch Clauses


19.3.2 (cont.) Example - catch Clauses

Consider the function process_record, which calls read, which in turn calls future_value, which throws an exception. Who catches it?

void process_record()
{
   try
   {
      read();
   }
   catch (logic_error& e)
   {
      cout << "caught logic_error " << e.what() << "\n";
   }
}

19.3.2 (cont.) Example - catch Clauses

void read()
{
   try
   {
      . . .
      double d = future_value();
   }
   catch (bad_alloc& e)
   {
      cout << "caught bad_alloc error " << e.what() << "\n";
   }
}

double future_value(...)
{
   . . .
   throw FutureValueError("illegal future_value parameter");
}

19.3.2 (cont.) catch(...), rethrow


19.3.2 catch(...), rethrow (cont.)

try
{
   code
}
catch (FutureValueError& e)
{
   statements1
}
catch (...) // Catch any remaining exceptions
{
   statements2
   throw; // Rethrow the error
}
(See 19.3.3 for example)

Common Error 19.2

Throwing Objects versus Throwing Pointers

19.3.3 Stack Unwinding

Example:  reading input


19.3.3 Stack Unwinding (cont.)

bool Product::read(fstream& fs)
{
   getline(fs, name);
   if (name == "") return false; // End of file
   fs >> price >> score;
   if (fs.fail())
      throw runtime_error("Error while reading product");
   string remainder;
   getline(fs, remainder);
   return true;
}

19.3.3 Stack Unwinding (cont.)


19.3.3 (cont.) Exceptions and Cleaning Up

  1. Make sure all stack values are instances of a class, or
  2. Catch the error, clean up, and rethrow the error:
    Product* p = NULL;
    try
    {
       p = new Product();
       if (p->read())
       { . . . }
       delete p;
    }
    catch (...)
    {
       delete p;
       throw;
    }

19.3.4 Exceptions and Constructors / Destructors


19.3.4 (cont.) Memory Leak in a Constructor

class DataArray
{
public:
   DataArray(int s);
   ~DataArray();
   void init(int s);
private:
   int* data;
};

19.3.4 Memory Leak in a Constructor (cont.)

DataArray::DataArray(int s)
{
   data = new int[s];
   init(s); // What happens if init throws exception?
}

DataArray::~DataArray()
{
   delete[] data;
}

void DataArray::init(int s) throw (overflow_error)
{ . . . }

19.3.4 Memory Leak in a Constructor (cont.)

Here's one solution:

DataArray::DataArray(int s)
{
   data = new int[s];
   try
   {
      init(s);
   }
   catch (...) // Catch any exception init throws
   {
      delete[] data;
      data = NULL;
      throw; // Rethrow exception
   }
}

19.3.5 Exception Specifications


19.3.5 Exception Specifications (cont.)

Syntax 19.3 : Exception Specification
return_type function_name(parameters)
	 throw (type_name1, type_name2, ..., type_namen)
}
Example:
void process_products(fstream& fs)
   throw (UnexpectedEndOfFile, bad_alloc)
Purpose: List the types of all exceptions that a function can throw.

19.3.5 Exception Specifications (cont.)

Caveats:


19.4 Case Study: Matrices


19.4 Case Study: Matrices (matrix3.h)


19.4 Case Study: Matrices (matrix3.cpp)


19.4 Case Study: Matrices (matrixtest3.cpp)


Chapter Summary


  1. Programs should be robust
  2. Select appropriate exception handling mechanism for the task
  3. Exception management becomes more important on multiple-developer projects
  4. throw an exception to signal an error. One of the callers must supply a try block w/an appropriate catch clause