我是智能指针的新手,我试图绕过我的脑袋,为什么在取消引用运算符之后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
?
答案 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
的引用}。但这取决于你的代码到底要做什么。