Although the throw
-catch
mechanism is similar to function arguments and the function return mechanism, there are a few differences. One, which you’ve already encountered, is that a return statement in a function fun()
transfers execution to the function that called fun()
, but a throw
transfers execution all the way up to the first function having a try
-catch
combination that catches the exception. For example, in Listing 15.12, when hmean()
throws an exception, control passes up to means()
, but when gmean()
throws an exception, control passes up to main()
.
Another difference is that the compiler always creates a temporary copy when throwing an exception, even if the exception specifier and catch
blocks specify a reference. For instance, consider this code:
class problem {...};
...
void super() throw (problem)
{
...
if (oh_no)
{
problem oops; // construct object
throw oops; // throw it
...
}
...
try {
super();
}
catch(problem & p)
{
// statements
}
Here, p
would refer to a copy of oops
rather than to oops
. That’s a good thing because oops
no longer exists after super()
terminates. By the way, it is simpler to combine construction with the throw
:
throw problem(); // construct and throw default problem object
At this point you might wonder why the code uses a reference if the throw
generates a copy. After all, the usual reason for using a reference return value is the efficiency of not having to make a copy. The answer is that references have another important property: A base-class reference can also refer to derived-class objects. Suppose you have a collection of exception types that are related by inheritance. In that case, the exception specification need only list a reference to the base type, and that would also match any derived objects thrown.
Suppose you have a class hierarchy of exceptions and you want to handle the different types separately. A base-class reference can catch all objects of a family, but a derived-class object can only catch that object and objects of classes derived from that class. A thrown object is caught by the first catch
block that matches it. This suggests arranging the catch
blocks in inverse order of derivation:
class bad_1 {...};
class bad_2 : public bad_1 {...};
class bad_3 : public bad 2 {...};
...
void duper()
{
...
if (oh_no)
throw bad_1();
if (rats)
throw bad_2();
if (drat)
throw bad_3();
}
...
try {
duper();
}
catch(bad_3 &be)
{ // statements }
catch(bad_2 &be)
{ // statements }
catch(bad_1 &be)
{ // statements }
If the bad_1 &
handler were first, it would catch the bad_1
, bad_2
, and bad_3
exceptions. With the inverse order, a bad_3
exception would be caught by the bad_3 &
handler.
If you have an inheritance hierarchy of exception classes, you should arrange the order of the catch
blocks so that the exception of the most-derived class (that is, the class furthest down the class hierarchy sequence) is caught first and the base-class exception is caught last.
Arranging catch
blocks in the proper sequence allows you to be selective about how each type of exception is handled. But sometimes you might not know what type of exception to expect. For instance, say you write a function that calls another function, and you don’t know whether that other function throws exceptions. You can still catch the exception even if you don’t know the type. The trick to catching any exception is to use an ellipsis for the exception type:
catch (...) { // statements } // catches any type exception
If you do know some exceptions that are thrown, you can place this catchall form at the end of the catch
block sequence, a bit like the default
case for a switch
:
try {
duper();
}
catch(bad_3 &be)
{ // statements }
catch(bad_2 &be)
{ // statements }
catch(bad_1 &be)
{ // statements }
catch(bad_hmean & h)
{ // statements }
catch (...) // catch whatever is left
{ // statements }
You can set up a handler to catch an object instead of a reference. A base-class object will catch a derived class object, but the derived aspects will be stripped off. Thus, base-class versions of virtual methods will be used.
13.58.121.8