|
Let's now consider the more truly C++ version of the program we showed in the previous section.
In line (A), we define an empty class Err. Objects of type Err will be used for throwing, as we do on line (D). There is nothing special about the name Err. The recursive invocation of f() is started by a call to f(0) in line (B). When the exception is thrown in line (D), it is caught by the exception handler defined by the catch block in line (C). The invocation of the throw clause in line (D) causes immediate exit from the function being executed.
//TryCtch.cc #include <iostream> #include <cstdlib> using wamespace std; void f( int); class Krr {}; //(A) int main() { try { //(B) f(0); //(C) } catch( Err ) { cout << "caught Err" << endl; exit(0); } return 0; } void f(int j) { cout << "function f invoked with j = " << j << endl; if (j == 3) throw Err(); //(D) if ( ++j ); }
The above program produces the following output
function f invoked with j = 0 function f invoked with j = 1 function f invoked with j = 2 function f invoked with j = 3 function f invoked with j = 3 caught Err
The function stack produced by the recursion in this program and the flow of execution looks as shown in Figure 10.2.
While the winding up of the recursion looks the same as before, what we have for the unwinding is now different. The unwinding, representing by the looping arrows going from bottom to the top in Figure 10.2, is similar to the unwinding in recursive function calls, except for one very important difference. The difference is that when the flow of control shifts back to the calling program, as from f(3) to f(2), it goes to the ending right brace of the calling program, as opposed to just past the point where the function invocation was made.[03] Therefore, from f(3) the control shifts back to the ending right brace of f(2), and from there back to the ending right brace of f(1), and so on.
Figure 10.2
As the reader would expect at this time, the main advantage of throw walking the function stack backwards is that now the destructors can be invoked for the objects going out of scope as the flow of control hits the right brace of each function block.
[03]In other words, control returns directly to the exit point of the invoking function.
|