c ++ weak_ptr在取消引用后到期了吗?

时间:2016-07-26 04:50:19

标签: c++ smart-pointers weak-ptr

我是智能指针的新手,我试图绕过我的脑袋,为什么在取消引用运算符之后weak_ptr会过期。我以前测试的代码在这里:

#include <memory>
#include <iostream>
#include <vector>

using namespace std;

struct node
{
    weak_ptr<node> parent;
    shared_ptr<node> child;
    int val;
};

shared_ptr<node> foo()
{
    shared_ptr<node> a = make_shared<node>();
    shared_ptr<node> b = make_shared<node>();

    a->val = 30;
    b->val = 20;

    b->parent = a;
    a->child = b;

    return a;
}

int main()
{
    shared_ptr<node> c = foo();
    node d = *foo();

    if (c->child->parent.expired())
    {
        cout << "weak ptr in c has expired." << endl;
    }

    if (d.child->parent.expired())
    {
        cout << "weak ptr in d has expired." << endl;
    }

    return 0;
}

该计划输出weak ptr in d has expired.

我不明白为什么当d使用取消引用运算符时,它会过期。关于这一点,无论如何都要阻止它(除了不解除它之外)?

我通过将节点中的weak_ptr更改为shared_ptr来尝试as mrtnj suggested,但我认为我有内存泄漏。我将node类更改为

struct node
{
    shared_ptr<node> parent;
    shared_ptr<node> child;
    int val;
};

然后修改源代码以添加tryCreate函数。

void tryCreate()
{
    node d = *foo();
}

然后在我的main中调用它,使我的主要看起来像

int main()
{
    tryCreate();
    return 0;
}

我使用了Visual Studio 2015的内存分析,并注意到只有分配而没有解除分配。我将parent更改为weak_ptr,我看到了解除分配。我是做错了还是确实要求在这些循环条件下使用weak_ptr

3 个答案:

答案 0 :(得分:6)

当引用该对象的最后一个weak_ptr被销毁时,shared_ptr到期。

在语句

中发生的代码中
node d = *foo();

此处foo()返回shared_ptr,这是引用该对象的最后一个shared_ptr(由foo创建的两个父对象)。这个shared_ptr是一个临时的,在derefencing之后就在那里被摧毁了。这会将引用计数减少到0并且weak_ptr到期。

由于shared_ptr是最后一个,因此对象被销毁,导致其子对象也被销毁。因此,深入研究这些对象的后续代码具有未定义的行为。

由于d包含shared_ptr子节点,因此此时子节点不会被销毁,正如Miles Budnek在评论中所述。

答案 1 :(得分:3)

node d = *foo();

foo返回一个shared_ptr,它将foo中分配的父节点保持活动状态。

然后将其内容复制到d但不存储shared_ptr,以便在语句结束时销毁。现在没有引用在foo中动态分配的节点实例的shared_ptr实例,因此对它的弱指针引用现在已过期。

取消引用不是问题:问题是未能捕获被返回的shared_ptr

答案 2 :(得分:2)

下面:

node d = *foo();

您取消引用shared_ptr,因此d包含{fn}在foo中创建的node副本:

shared_ptr<node> a = make_shared<node>();

a将在node d = *foo();之后销毁。这是因为parent只是一个weak_ptr内部节点。

  

关于这一点,无论如何都要阻止它(除了不解除它之外)?

不解除引用似乎是好事。

您可以从weak_tr<node> parent;切换到shared_ptr<node> parent;其他解决方案是保留全局shared_ptr<node> root;,以保留对a的引用}。但这取决于你的代码到底要做什么。