Do class objects declared on the stack have the same lifetime as other stack variables?
I have this code:
#include <stdio.h>
#include <vector>
using std::vector;
#include <string>
using std::string;
class Child;
class Parent
{
public:
Parent(string s) : name(s) { };
vector<Child> children;
string name;
};
class Child
{
public:
Child() { /* I need this for serialization */ };
Child(Parent *p) : parent(p) { };
Parent *parent;
};
Parent
family()
{
Parent p("John Doe");
int i;
printf("family:\n\tparent: 0x%x\n\ti: %x\n", &p, &i);
for (i = 0; i < 2; ++i)
p.children.push_back(Child(&p));
return p;
}
int
main(void)
{
Parent p = family();
printf("main:\n\tparent: 0x%x\n", &p);
for (unsigned int i = 0; i < p.children.size(); ++i)
printf
(
"\t\tchild[%d]: parent: 0x%x parent.name '%s'\n",
i,
p.children[i].parent,
p.children[i].parent->name.c_str()
);
return 0;
}
My questions:
- In function
family, isParent pdeclared on the stack? From looking at the output, it would seem so - Each created
Childgoes on the stack too, right? - When I create each
Childinstance, I pass it a pointer to a stack variable. I imagine this is a big no-no, because stack variables are guaranteed to live only until the end of the function. After that the stack should get popped and the variables will be destroyed. Is this correct? vector.push_back()passes arguments by reference, so at the end of thefamilyfunction,p.childrenjust contains references to the local variables, right?- Why is it all working? In
main, why can I access the parent and each of its children? Is it all because the local variables fromfamilyare still intact and haven’t been overwritten by some subsequent function call?
I think I’m misunderstanding where stuff lives in memory in C++. I’d really like to be pointed a resource that explains it well. Thanks in advance.
EDIT
Output from compiling the source and running:
misha@misha-K42Jr:~/Desktop/stackoverflow$ ./a.out
family:
parent: 0x2aa47470
i: 2aa47438
main:
parent: 0x2aa47470
child[0]: parent: 0x2aa47470 parent.name 'John Doe'
child[1]: parent: 0x2aa47470 parent.name 'John Doe'
The Child objects that are in the vector survive for the reason that Mark Ransom pointed out, but the pointer to Parent * that each Child contains (which points to p) becomes invalid just as you expected.
If it appears to work, what likely happend is the compiler’s optimizer inlined family(), and then combined the storage of
main(){p}andfamily(){p}to avoid copying the returned object. This optimization would be likely even without inlining, but nearly certain with it.It’s easy to see why it would be allowed in this case, since your
Parentclass doesn’t customize the copy constructor, but it’s actually allowed regardless. The C++ standard makes special reference to return value optimization, and permits the compiler to pretend that a copy constructor has no side effects, even if it can’t prove this.To fix this, the Parent needs to be allocated on the heap, and some other provision would need to be made to free it. Assuming that no time-travel is involved (so that no object can become its own ancestor), this could be easily accomplished by using tr1::shared_ptr (or boost::shared_pointer for pre-TR1 compilers) for the pointer each child holds to its parent.