【问题标题】:c++ weak_ptr expiring after dereference?c++ weak_ptr 在取消引用后过期?
【发布时间】:2016-07-26 04:50:18
【问题描述】:

我是智能指针的新手,我想弄清楚为什么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 中调用它,这样我的 main 看起来像

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

我使用了 Visual Studio 2015 的内存分析,发现只有分配,没有释放。我将parent 更改为weak_ptr,我看到了释放。我做错了什么还是确实需要在这些循环条件下使用weak_ptr

【问题讨论】:

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


    【解决方案1】:

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

    在语句中发生的代码中

    node d = *foo();
    

    这里foo() 返回一个shared_ptr,这是最后一个引用该对象的shared_ptrfoo 创建的两个的父对象)。而这个shared_ptr 是一个临时的,在解除引用之后就在那里被破坏了。这会将引用计数减少到 0 并且 weak_ptr 过期。

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

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

    【讨论】:

    • 我很确定子节点不会被破坏,因为在foo 中分配的父节点的child 指针在原始父节点被销毁之前被复制到d语句结束。
    • @MilesBudnek:你是对的。谢谢!我非常需要咖啡(和睡眠)。 ;-)
    【解决方案2】:
    node d = *foo();
    

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

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

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

    【讨论】:

      【解决方案3】:

      这里:

      node d = *foo();
      

      您取消引用 shared_ptr,因此 d 包含 node 的副本,该副本是在 foo 行中创建的:

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

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

      关于这一点,有没有办法阻止它(除了不取消引用它)?

      不取消引用似乎是个好方法。

      您可以从weak_tr&lt;node&gt; parent; 切换到shared_ptr&lt;node&gt; parent; 其他解决方案是保留一个全局shared_ptr&lt;node&gt; root;,它会保留对您的a 的引用。但这取决于您的代码真正要做什么。

      【讨论】:

      • 当 d 析构函数被调用时,你的内存将被释放,在你的例子中这是 main 的结尾。这是经典的 RAII。当您需要将 shared_ptr 存储在不应阻止其破坏的对象中以及消除指针的循环引用时,weak_ptr-s 最有用。
      • @mrtnj 我尝试了你所说的,将父级设置为shared_ptr。但是,我注意到有内存泄漏?也许我没有让我的概念正确。查看我对您的建议所做的修改。
      • @silentwf 你是对的,这是一个糟糕的建议,我会将其从答案中删除(删除)。您是否考虑过使用根引用?参见示例:coliru.stacked-crooked.com/a/24a40dcc0178d75b
      猜你喜欢
      • 1970-01-01
      • 2017-11-11
      • 2010-09-17
      • 1970-01-01
      • 2016-07-06
      • 1970-01-01
      • 2011-06-13
      • 1970-01-01
      • 2010-12-04
      相关资源
      最近更新 更多