Exception Handling in C++

This page might seem like it duplicates some of what we have just seen, but it is valuable because it gives a different perspective on the topic. Read chapter 1 on pages 15-60.

Catching an Exception

Exception handlers

Of course, the thrown exception must end up some place. This place is the exception handler, and you need one exception handler for every exception type you want to catch. However, polymorphism also works for exceptions, so one exception handler can work with an exception type and classes derived from that type.

Exception handlers immediately follow the try block and are denoted by the keyword catch:

try {
  // Code that may generate exceptions
} catch(type1 id1) {
  // Handle exceptions of type1
} catch(type2 id2) {
  // Handle exceptions of type2
} catch(type3 id3)
  // Etc...
} catch(typeN idN)
  // Handle exceptions of typeN
}
// Normal execution resumes here...
 

The syntax of a catch clause resembles functions that take a single argument. The identifier (id1id2, and so on) can be used inside the handler, just like a function argument, although you can omit the identifier if it’s not needed in the handler. The exception type usually gives you enough information to deal with it.

The handlers must appear directly after the try block. If an exception is thrown, the exception-handling mechanism goes hunting for the first handler with an argument that matches the type of the exception. It then enters that catch clause, and the exception is considered handled. (The search for handlers stops once the catch clause is found). Only the matching catch clause executes; control then resumes after the last handler associated with that try block.

Notice that, within the try block, a number of different function calls might generate the same type of exception, but you need only one handler.

To illustrate try and catch, the following variation of Nonlocal.cpp replaces the call to setjmp( ) with a try block and replaces the call to longjmp( ) with a  throw statement:

//: C01:Nonlocal2.cpp
// Illustrates exceptions.
#include 
using namespace std;
 
class Rainbow {
public:
  Rainbow() { cout << "Rainbow()" << endl; }
  ~Rainbow() { cout << "~Rainbow()" << endl; }
};
 
void oz() {
  Rainbow rb;
  for(int i = 0; i < 3; i++)
    cout << "there's no place like home" << endl;
  throw 47;
}
 
int main() {
  try {
    cout << "tornado, witch, munchkins..." << endl;
    oz();
  } catch(int) {
    cout << "Auntie Em! I had the strangest dream..."
         << endl;
  }
} ///:~
When the throw statement in oz( ) executes, program control backtracks until it finds the catch clause that takes an int parameter. Execution resumes with the body of that catch clause. The most important difference between this program and Nonlocal.cpp is that the destructor for the object rb is called when the throw statement causes execution to leave the function oz( ).