Differences between shared_ptr and weak_ptr

I'm reading Scott Meyers' book Effective C ++. It was mentioned that tr1::shared_ptr and tr1::weak_ptr act as inline pointers, but they keep track of how many tr1::shared_ptrs points to an object.

This is called reference counting. This works well in preventing resource leaks in acyclic data structures, but if two or more objects contain tr1::shared_ptrs so that the loop is formed, the loop can maintain a reference count above zero, even if all external pointers to the loop have been destroyed.

What is where tr1::weak_ptrs .

My question is how circular data structures make the number of links above zero. I am asking for an example program in C ++. How to solve the problem with weak_ptrs ? (again, please, an example).

+51
c ++ c ++ 11 shared-ptr weak-ptr cyclic-reference
Feb 13 '11 at 13:26
source share
5 answers

A shared_ptr completes the reference counting mechanism around the raw pointer. Therefore, for each instance of shared_ptr the reference count is incremented by one. If two share_ptr reference each other, they will never be deleted because they will never have a reference count with zero.

weak_ptr points to shared_ptr but does not increment the reference count. This means that the fake object can be deleted even if there is a weak_ptr link.

The way this works is that weak_ptr can be used to create shared_ptr for whenever you want to use a base object. If, however, the object is already deleted, an empty instance of shared_ptr returned. Since the reference count of the base object is not incremented using the weak_ptr link, the circular link will not cause the main object to not be deleted.

+36
Feb 13 2018-11-11T00:
source share

Let me repeat your question: "My question is, how do circular data structures make the reference count higher than zero, please kindly ask for an example using a C ++ program. How can the problem be solved with weak_ptrs again with an example please."

The problem arises with such C ++ code (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 to count links for loop processing. Thus, a weak_ptr (which is basically just a stripped down version of shared_ptr ) cannot be used to solve the loop problem - the programmer solves the loop problem.

To solve this problem, the programmer must know the relationship of ownership between objects or must invent a relationship of ownership if such ownership does not exist 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. 

The critical question is: can weak_ptr be used if the programmer cannot determine the ownership relationship and cannot establish any static ownership due to lack of privilege or lack of information?

Answer: If ownership among objects is fuzzy, weak_ptr cannot help. If there is a loop, the programmer must find it and break it. An alternative is to use a programming language with full garbage collection (e.g. Java, C #, Go, Haskell) or use a conservative garbage collector (= imperfect) that works with C / C ++ (e.g. Boehm GC).

+93
Sep 19 '11 at 15:48
source share

For future readers.
I just want to point out that the explanation given by Atom is excellent, here is the 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; 
+12
Sep 20 '13 at 3:56
source share

Weak pointers simply "observe" the managed entity; they do not โ€œsupport himโ€ or influence his life. Unlike shared_ptr , when the last weak_ptr goes out of scope or disappears, an object pointing to the object can still exist, because weak_ptr does not affect the lifetime of the object - it does not have ownership rights. weak_ptr can be used to determine if an object exists and provide shared_ptr , which can be used to reference it.

The definition of weak_ptr intended to make it relatively reliable, so as a result, very little can be done directly with weak_ptr . For example, you cannot play it; neither operator* nor operator-> is defined for a weak_ptr . You cannot access a pointer to an object with it - there is no get() function. There is a comparison function that allows you to store weak_ptrs in an ordered container, but thatโ€™s it.

+4
Mar 28 '14 at 18:33
source share

All of the above answers are WRONG. weak_ptr NOT used to break circular links, they have another purpose.

Basically, if all shared_ptr(s) were created by make_shared() or allocate_shared() , you will never need weak_ptr unless you have a resource other than memory to manage. These functions create a shared_ptr counter object with the object itself, and the memory will be freed at the same time.

The only difference between weak_ptr and shared_ptr is that weak_ptr allows the object reference count to be stored after the real object has been freed. As a result, if you save a lot of shared_ptr in std::set , the actual objects will take up a lot of memory if they are large enough. This problem can be solved with weak_ptr . In this case, you must make sure that the weak_ptr stored in the container has not expired before using it.

-four
Nov 10 '11 at 4:00
source share



All Articles