shared_ptr and weak_ptr differences

61,356

Solution 1

A shared_ptr wraps a reference counting mechanism around a raw pointer. So for each instance of the shared_ptr the reference count is increased by one. If two share_ptr objects refer the eachother they will never get deleted because they will never end up with a reference count of zero.

weak_ptr points to a shared_ptr but does not increase its reference count.This means that the underying object can still be deleted even though there is a weak_ptr reference to it.

The way that this works is that the weak_ptr can be use to create a shared_ptr for whenever one wants to use the underlying object. If however the object has already been deleted then an empty instance of a shared_ptr is returned. Since the reference count on the underlying object is not increased with a weak_ptr reference, a circular reference will not result in the underlying object not being deleted.

Solution 2

Let me repeat your question: "My question, how cyclic data structures makes reference count above zero, kindly request to show with example in C++ program. How the problem is solved by weak_ptrs again with example please."

The problem occurs with C++ code like this (conceptually):

class A { shared_ptr<B> b; ... };
class B { shared_ptr<A> a; ... };
shared_ptr<A> x(new A);  // +1
x->b = new B;            // +1
x->b->a = x;             // +1
// Ref count of 'x' is 2.
// Ref count of 'x->b' is 1.
// When 'x' leaves the scope, there will be a memory leak:
// 2 is decremented to 1, and so both ref counts will be 1.
// (Memory is deallocated only when ref count drops to 0)

To answer the second part of your question: It is mathematically impossible for reference counting to deal with cycles. Therefore, a weak_ptr (which is basically just a stripped down version of shared_ptr) cannot be used to solve the cycle problem - the programmer is solving the cycle problem.

To solve it, the programmer needs to be aware of the ownership relationship among the objects, or needs to invent an ownership relationship if no such ownership exists naturally.

The above C++ code can be changed so that A owns B:

class A { shared_ptr<B> b; ... };
class B { weak_ptr<A>   a; ... };
shared_ptr<A> x(new A); // +1
x->b = new B;           // +1
x->b->a = x;            // No +1 here
// Ref count of 'x' is 1.
// Ref count of 'x->b' is 1.
// When 'x' leaves the scope, its ref count will drop to 0.
// While destroying it, ref count of 'x->b' will drop to 0.
// So both A and B will be deallocated.

A crucial question is: Can weak_ptr be used in case the programmer cannot tell the ownership relationship and cannot establish any static ownership because of lack of privilege or lack of information?

The answer is: If ownership among objects is unclear, weak_ptr cannot help. If there is a cycle, the programmer has to find it and break it. An alternative remedy is to use a programming language with full garbage collection (such as: Java, C#, Go, Haskell), or to use a conservative (=imperfect) garbage collector which works with C/C++ (such as: Boehm GC).

Solution 3

For future readers.
Just want to point out that explanation given by Atom is excellent, here is working code

#include <memory> // and others
using namespace std;

    class B; // forward declaration 
    // for clarity, add explicit destructor to see that they are not called
    class A { public: shared_ptr<B> b; ~A() {cout << "~A()" << endl; } };  
    class B { public: shared_ptr<A> a; ~B() {cout << "~B()" << endl; } };     
    shared_ptr<A> x(new A);  //x->b share_ptr is default initialized
    x->b = make_shared<B>(); // you can't do "= new B" on shared_ptr                      
    x->b->a = x;
    cout << x.use_count() << endl;  

Solution 4

Weak pointers just "observe" the managed object; they don't "keep it alive" or affect its lifetime. Unlike shared_ptr, when the last weak_ptr goes out of scope or disappears, the pointed-to object can still exist because the weak_ptr does not affect the lifetime of the object - it has no ownership rights. The weak_ptr can be used to determine whether the object exists, and to provide a shared_ptr that can be used to refer to it.

The definition of weak_ptr is designed to make it relatively foolproof, so as a result there is very little you can do directly with a weak_ptr. For example, you can't dereference it; neither operator* nor operator-> is defined for a weak_ptr. You can't access the pointer to the object with it - there is no get() function. There is a comparison function defined so that you can store weak_ptrs in an ordered container, but that's all.

Share:
61,356

Related videos on Youtube

venkysmarty
Author by

venkysmarty

Updated on July 19, 2020

Comments

  • venkysmarty
    venkysmarty almost 4 years

    I am reading Scott Meyers "Effective C++" book. It was mentioned that there are tr1::shared_ptr and tr1::weak_ptr act like built-in pointers, but they keep track of how many tr1::shared_ptrs point to an object.

    This is known as reference counting. This works well in preventing resource leaks in acyclic data structures, but if two or more objects contain tr1::shared_ptrs such that a cycle is formed, the cycle may keep each other's reference count above zero, even when all external pointers to the cycle have been destroyed.

    That's where tr1::weak_ptrs come in.

    My question is how cyclic data structures make the reference count above zero. I kindly request an example C++ program. How is the problem solved by weak_ptrs? (again, with example please).

    • curiousguy
      curiousguy over 7 years
      "How is the problem solved by weak_ptrs?" It is not. It is solved by proper design, where no ownership cycle exists.
  • curiousguy
    curiousguy over 12 years
    But all uses of B::a must now be prepared the weak reference to be dead. If this must not be the case, then it means weak_ptr is not an adequat tool.
  • rich.e
    rich.e about 12 years
    If B::a is a weak_ptr, then nothing should depend on it's existence - as it does not own a. A::b is the dependable guy here.
  • Ben L
    Ben L over 11 years
    I believe that the reference control object keeps a count of both "Uses"(shared_ptrs) and "Weaks" (weak_ptrs + (Uses > 0 ? 1 : 0 )). But this might be an implimentation detail.
  • Paul
    Paul about 11 years
    I would have chosen yours as best answer. But hey.. not my choice :) +1 btw
  • curiousguy
    curiousguy about 8 years
    "weak_ptr is NOT used to break cyclic references" +1 "they have other purpose." but that answer doesn't explain the release purpose, so -1
  • g10guang
    g10guang over 5 years
    Is weak_ptr just for cyclic reference or Cache?
  • jwm
    jwm over 5 years
    "if all shared_ptr(s) were created by make_shared()" then you don't need shared_ptr (unique_ptr is more appropriate). The value of reference counting is when multiple objects hold references. This happens through assignments, like the very fine example.