In addition, you can throw as many different types of objects as you want. Typically, you’ll throw a different type for each category of error. The idea is to store the information in the object and in the
Catching an exception
As mentioned earlier, one of the advantages of C++ exception handling is that it allows you to concentrate on the problem you’re actually trying to solve in one place, and then deal with the errors from that code in another place.
The try block
If you’re inside a function and you throw an exception (or a called function throws an exception), the function exits in the process of throwing. If you don’t want a throw to leave a function, you can set up a special block within the function where you try to solve your actual programming problem (and potentially generate exceptions). This block is called the
try {
// Code that may generate exceptions
}
If you check for errors by carefully examining the return codes from the functions you use, you need to surround every function call with setup and test code, even if you call the same function several times. With exception handling, you put everything in a try block and handle exceptions after the try block. Thus, your code is a lot easier to write and easier to read because the goal of the code is not confused with the error checking.
Exception handlers
Of course, the thrown exception must end up some place. This place is the
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 (id1, id2, 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 using 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\n";
throw 47;
}
int main() {
try {
cout << "tornado, witch, munchkins...\n";
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, at which point 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( ).