Alex
Alex

Reputation: 876

Use of weak_ptr with cyclic references

So I'm having quite a hard time understanding why we have to use weak_ptr especially with cyclic reference problems, consider this code:

class B; //forward declaration

class A {
    shared_ptr<B> b_ptr;
public:
    void set_B(shared_ptr<B>& b)
    {
        b_ptr = b;
    }
    A() { cout << "A constructor" << endl; }
    ~A() { cout << "A destructor" << endl; }
};

class B {
    shared_ptr<A> a_ptr;
public:
    void set_A(shared_ptr<A>& a)
    {
        a_ptr = a;
    }
    B() { cout << "B constructor" << endl; }
    ~B() { cout << "B destructor" << endl; }
};

int main() {
   shared_ptr<A> a = make_shared<A>();
    shared_ptr<B> b = make_shared<B>();
    a->set_B(b);
    b->set_A(a);
}

Now from what I heard, when both a and b go out of scope and their reference count is 0 they try to deallocate and destroy the pointed to memory, but in this case they can't do it because both pointed to objects have shared_ptr's that have an reference count of 1 which makes them undeletable, now is that true?

Then, it says to fix this problem I have to make the shared_ptr in class B a weak_ptr, now why is that? it still has an reference count of 1, doesn't it? And even with the weak_ptr there is still the shared_ptr<B> b_ptr; which remains with a reference count of 1, so how can it be deleted?

Also it mentions that a weak_ptr breaks strong ownership reference but how can a weak_ptr have no ownership, how will it access the object?

Thanks in advance.

Upvotes: 2

Views: 181

Answers (1)

Jarod42
Jarod42

Reputation: 217283

With all std::shared_ptr, you have:

int main() {
    std::shared_ptr<A> a = std::make_shared<A>(); // ref_count_a = 1
    std::shared_ptr<B> b = std::make_shared<B>(); // ref_count_b = 1
    a->set_B(b); // ref_count_b = 2
    b->set_A(a); // ref_count_a = 2
} // ref_count_a = 1 && ref_count_b = 1
// memleak due to the cycle

With

class B {
    std::weak_ptr<A> a_ptr;
// ...
};

it becomes:

int main() {
    std::shared_ptr<A> a = std::make_shared<A>(); // ref_count_a = 1
    std::shared_ptr<B> b = std::make_shared<B>(); // ref_count_b = 1
    a->set_B(b); // ref_count_b = 2
    b->set_A(a); // ref_count_a = 1 , weak_ref_a = 1
} // ref_count_a = 0 && ref_count_b = 1
// -> release a -> ref_count_b = 0
// -> release b (+ control block) -> weak_ref_a = 0
// -> release control block of a

Also it mentions that a weak_ptr breaks strong ownership reference but how can a weak_ptr have no ownership, how will it access the object?

The control maintains a counter for the shared_ptr (to release the object) and a counter for weak_ptr to release the control block. weak_ptr retrieves the shared_ptr thanks to control block.

Finally I heard that a weak_ptr is a smart pointer which can use methods such as lock() or expired() to manage a shared_ptr, again is this correct?

Yes

Upvotes: 3

Related Questions