堆栈上声明的类对象是否与其他堆栈变量具有相同的生命周期?

我有这个代码:

#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;
}

我的问题:
  • 在函数 family 中,是否在堆栈上声明了 Parent p?从输出来看,它看起来是如此
  • 每个创建的 Child 也会进入堆栈,对吗?
  • 当我创建每个 Child 实例时,我将一个指向堆栈变量的指针传递给它。我想这是一个 大禁忌 ,因为堆栈变量保证只存在到函数结束。之后堆栈应该被弹出并且变量将被销毁。这样对吗?
  • vector.push_back() passes arguments by reference ,所以在 family 函数的末尾, p.children 只包含对局部变量的引用,对吗?
  • 为什么一切正常?在 main 中,为什么我可以访问父级及其每个子级?是不是因为 family 中的局部变量仍然完好无损并且没有被一些后续的函数调用覆盖?

  • 我想我误解了 C++ 中内存中的内容。我真的很想得到一个可以很好地解释它的资源。提前致谢。

    编辑

    编译源代码并运行的输出:
    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'
    

    最佳答案

    由于 Mark Ransom 指出的原因, vector 中的 Child 对象仍然存在,但是每个 Child 包含的指向 Parent * 的指针(指向 p)正如您预期的那样变得无效。

    如果它看起来有效,那么可能发生的情况是编译器的优化器内联了 family(),然后组合了 main(){p}family(){p} 的存储以避免复制返回的对象。即使没有内联,这种优化也很可能,但几乎可以肯定。

    很容易理解为什么在这种情况下允许它,因为您的 Parent 类没有自定义复制构造函数,但实际上无论如何都允许它。 C++ 标准特别引用 return value optimization ,并允许编译器假装复制构造函数没有副作用,即使它不能证明这一点。

    为了解决这个问题,需要在堆上分配 Parent,并且需要做一些其他的准备来释放它。假设不涉及时间旅行(因此没有对象可以成为它自己的祖先),这可以通过使用 tr1::shared_ptr(或 boost::shared_pointer 用于 pre-TR1 编译器)作为每个子级指向其父级的指针来轻松实现。

    关于c++ - 在堆栈上创建类对象,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/4894413/

    10-11 22:44
    查看更多