Reputation: 153
I'm new to smart pointers and I'm trying to wrap around my head why a weak_ptr would expire after a dereference operator. The code I used to test is here:
#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;
}
The program outputs weak ptr in d has expired.
I don't understand why when d
uses the dereference operator, it expires. In regards to this, is there anyway to prevent it (other than not dereferencing it)?
I tried as mrtnj suggested by changing the weak_ptr
in node to shared_ptr
but I think I have a memory leak. I changed the node
class to
struct node
{
shared_ptr<node> parent;
shared_ptr<node> child;
int val;
};
and then modified the source code to add a tryCreate
function.
void tryCreate()
{
node d = *foo();
}
and then called it in my main
such that my main looks like
int main()
{
tryCreate();
return 0;
}
I used Visual Studio 2015's memory profiling and noticed that there were only allocations and no deallocations. I changed parent
into a weak_ptr
and I see deallocations. Am I doing something wrong or is it indeed a requirement to use weak_ptr
in these cyclic condition?
Upvotes: 4
Views: 960
Reputation: 50026
Here:
node d = *foo();
you dereference shared_ptr, so d
contains a copy of node
which was created in foo in line:
shared_ptr<node> a = make_shared<node>();
this a
will be destroyed just after node d = *foo();
. This is because parent is only a weak_ptr inside node.
In regards to this, is there anyway to prevent it (other than not dereferencing it)?
Not dereferencing seems to be good aproach.
You could switch from Other solution would be to keep a global weak_tr<node> parent;
to shared_ptr<node> parent;
.shared_ptr<node> root;
which would keep a reference to your a
. But It depends on what your code really is going todo.
Upvotes: 2
Reputation: 145419
A weak_ptr
expires when the last shared_ptr
that refers to the object, is destroyed.
In your code that happens in the statement
node d = *foo();
Here foo()
returns a shared_ptr
, which is the last shared_ptr
that refers to that object (the parent object of the two created by foo
). And this shared_ptr
is a temporary that's destroyed right there, after the derefencing. That reduces the reference count to 0 and the weak_ptr
expires.
Since the shared_ptr
was the last one, the object is destroyed, which causes its child object to also be destroyed. Thus the later code that delves into these objects has undefined behavior.
Since d
contains a shared_ptr
to the child node, the child node is not destroyed at this point, as noted by Miles Budnek in a comment.
Upvotes: 6
Reputation: 24269
node d = *foo();
foo
returns a shared_ptr
which is keeping the parent node allocated in foo
alive.
You then copy the contents of that into d
but do not store the shared_ptr, so that is destroyed at the end of the statement. There are now no shared_ptr instances referencing the node instance dynamically allocated in foo, so the weak pointer references to it are now expired.
The dereference isn't the problem: the problem is the failure to capture the shared_ptr
being returned.
Upvotes: 3