Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

c++ weak_ptr expiring after dereference?

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?

like image 626
silentwf Avatar asked Dec 19 '22 14:12

silentwf


1 Answers

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.

like image 132
Cheers and hth. - Alf Avatar answered Dec 24 '22 01:12

Cheers and hth. - Alf