Reputation: 2316
I want to figure out where my thrown object is stored in memory. So I wrote a tiny program for it:
#include <iostream>
#define print_dist() int a;\
do { std::cout << __FUNCTION__ << "() a[" << (long)&a - (long)ptrMainStackBase << "]" << std::endl; } while (0)
#define print_distx(x) \
do { std::cout << __FUNCTION__ << "() " #x "[" << (long)&x - (long)ptrMainStackBase << "]" << std::endl; } while (0)
#define print_distxy(x, y) \
do { std::cout << __FUNCTION__ << "() " #x "(ex)[" << (long)&x - (long)y << "]" << std::endl; } while (0)
class CTest
{
public:
CTest()
{ std::cout << "CTest::CTest" << std::endl; }
// private:
CTest(const CTest&)
{ std::cout << "copy" << std::endl; }
};
const CTest *ptrException;
int *ptrMainStackBase;
void Test2()
{
print_dist();
CTest test;
print_distx(test);
std::cout << "&test=" << &test << std::endl;
throw test;
}
void Test1()
{
print_dist();
try
{
Test2();
}
catch (const CTest& test)
{
ptrException = &test;
print_dist();
print_distx(test);
print_distxy(test, ptrException);
std::cout << "&test=" << &test << std::endl;
throw test;
}
}
int main()
{
int b;
ptrMainStackBase = &b;
print_dist();
try
{
print_dist();
Test1();
}
catch (const CTest& test)
{
print_dist();
print_distx(test);
print_distxy(test, ptrException);
std::cout << "&test=" << &test << std::endl;
}
return 0;
}
and it prints:
main() a[-4]
main() a[-8]
Test1() a[-64]
Test2() a[-104]
CTest::CTest
Test2() test[-108]
&test=0x7fffd3b21628 <- test created here on stack
copy
Test1() a[-68]
Test1() test[-140736732956164]
Test1() test(ex)[0]
&test=0xb89090 <- and copied here
copy
main() a[-12]
main() test[-140736732956020]
main() test(ex)[144]
&test=0xb89120 <- and here
It looks like, when I throw an object, it is first copied to another stack which is far away from the normal one. Is this true? And why there are 144 byte distance between the two "exception stack frames"?
Upvotes: 7
Views: 187
Reputation: 33116
It looks like, when I throw an object it is copied first to an other stack which is far away from the normal one. Is this true?
The test
that was thrown does not exist when the exception is caught. That original test
has already been destructed. So it has to be a copy, and new object managed separately from arguments and local variables. In other words, not on the stack.
Where it lives? That's up to the implementation. Most likely it's dynamic memory (e.g., the heap) that the implementation manages for you.
And why there are 144 byte distance between the two "exception stack frame"?
The standard doesn't say how an implementation is to treat an exception when it is re-thrown in a catch
block. Since the throw
of a local variable must necessarily make a copy, the easiest way to implement throw
is to always make a copy.
Upvotes: 2
Reputation: 22020
When you throw an object it is indeed copied first to some temporary location. Otherwise, the stack unrolling would have taken it out of scope. Catching it then by reference would have resulted in undefined behavior, like so:
void foo() {
A a;
throw a;
}
void bar() {
try {
foo();
} catch (A& a) {
// use a
}
}
Unless a
had been copied to some temporary location, you'd have a reference to a variable that no longer exists in the catch
. For this to work, A
must have a public copy constructor (unless you're using VS, in which case it will use a private one as well...). Also, this is a good reason to catch by reference - otherwise, you'll have two copy constructions instead of one.
Upvotes: 6