Reputation: 25855
Consider the following simple C++ code:
void foo() {
throw(my_exception());
}
void check_exc(const my_exception &exc) {
/* Do stuff with exc */
}
void bar() {
try {
foo();
} catch(const my_exception &exc) {
check_exc(exc);
}
}
In bar
's exception handler, how comes the exception referenced by exc
is still alive, seeing as how it was allocated in foo
's stack frame? Shouldn't that frame have been unwound by the time the exception handler gets to run, and any values allocated there already be considered dead? Especially so since I'm explicitly calling another function which would need that stack space.
As a C programmer trying to learn C++, what am I misunderstanding here? Where do these various values actually exist in memory, more precisely?
Upvotes: 3
Views: 854
Reputation: 145279
Exceptions are thrown by value.
That is, the specified object is copied (possibly sliced, as with any copy initialization) or moved.
The exception object that you can reference via e.g. a catch
clause's reference parameter, is not allocated in the original throwing code's stack frame, but in “an unspecified way”.
In C++11 the possible ways that exception objects could be allocated (internally in the run time library) was restricted, by the requirement that exception objects could be referenced by essentially shared ownership smart pointers, std::exception_ptr
.
The possibility of shared ownership means that in C++ an exception object can have a guaranteed lifetime beyond the completed handling of the exception.
This is mainly in support of passing exceptions through non-exception-aware C code, and in order to pass nested exception information.
Upvotes: 3
Reputation: 254461
The temporary created in the throw-expression is used to initialise the exception object itself, which (to quote the standard) "is allocated in an unspecified way". That object lasts (at least) until the exception has been handled, so the handler's reference to it is valid within the handler, or any function called from the handler.
Upvotes: 8
Reputation: 259
He,
the line
throw(my_exception())
generates a new object of the type my_exception. You can specify whatever you want (int, enums, char * or classes). Of course, Classes make more sense as you can define additional data to it.
After this, the whole stack will be cleaned up and all recursions are terminated until it gets to the first try/catch block. The exception is still alive there. The code in the catch block is just like implementing a if/else block, just a little smarter.
cheers,
Upvotes: 0
Reputation: 748
The implementation would vary from platform to platform. The life cycle is more complicated that one might imagine since the throw statement starts executing in the stack frame of foo().
It's possible that the exception object gets copies and reallocated, or the catch block might execute in a frame on top of foo but with a pointer to the bar frame in order to reference bar's variables.
Upvotes: 1