Stack Unwinding
In the last issue we talked about throwing exceptions. Before discussing how exceptions are handled, we need to talk about an intermediate step, stack unwinding.
The exception handling mechanism is dynamic in that a record is kept of the flow of program execution, for example via stack frames and program counter mapping tables. When an exception is thrown, control transfers to the nearest suitable handler. “nearest” in this sense means the nearest dynamically surrounding try block containing a handler that matches the type of the thrown
exception. We will talk more about exception handlers in a future issue. Transfer of control from the point at which an exception is thrown to the exception handler implies jumping out of one program context into another. What about cleanup of the old program context? For example, what about local class objects that have been allocated? Are their destructors called?
The answer is “yes”. All stack-allocated (“automatic”) objects allocated since the try block was entered will have their destructors invoked. Let’s look at an example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | #include <iostream.h> class A { int x; public: A(int i) {x = i; cerr << "ctor " << x << endl;} ~A() {cerr << "dtor " << x << endl;} }; void f() { A a1(1); throw "this is a test"; A a2(2); } int main() { try { A a3(3); f(); A a4(4); } catch (const char* s) { cerr << "exception: " << s << endl; } return 0; } |
Output of this program is:
1 2 3 4 5 | ctor 3 ctor 1 dtor 1 dtor 3 exception: this is a test |
In this example, we enter the try block in main(), allocate a3, then call f(). f() allocates a1, then throws an exception, which will transfer control to the catch clause in main().
In this example, the a1 and a3 objects have their destructors called. a2 and a4 do not, because they were never allocated.
It’s possible to have class objects containing other class objects, or arrays of class objects, with partial construction taking place followed by an exception being thrown. In this case, only the constructed subobjects will be destructed.