How does weak_ptr work?

I understand how to use weak_ptr and shared_ptr . I understand how shared_ptr works by counting the number of links in its object. How does weak_ptr work? I tried reading through the source code of boost, and I'm not knowledgeable enough to understand everything that it uses.

Thank.

+50
c ++ boost weak-ptr weak-references tr1
Apr 15 2018-11-11T00:
source share
2 answers

shared_ptr uses an additional counter object (for example, a shared counter or control unit) to store a reference counter. (BTW: this counter object also saves the debiter.)

Each shared_ptr and weak_ptr contains a pointer to the actual pointer and a second pointer to the counter object.

To implement weak_ptr , the counter object stores two different counters:

  • "Use count" - the number of shared_ptr instances pointing to the object.
  • "weak count" is the number of weak_ptr instances pointing to the object, plus one if the "usage counter" is still> 0.

The pointer is deleted when the "usage counter" reaches zero.

The helper counter object is deleted when the weak counter reaches zero (which means that the usage counter must also be zero, see above).

When you try to get shared_ptr from weak_ptr , the library atomically checks the "usage count", and if it> 0 increases it. If it succeeds, you will get your shared_ptr . If the "usage counter" is already zero, you will get an empty shared_ptr instance instead.




EDIT : why do they add one to the weaker score instead of just letting go of the counter object when both values ​​fall to zero? Good question.

An alternative would be to delete the counter object when the usage counter and weak count drop to zero. Here is the first reason: checking two (indicative sizes) counters is atomically not possible on each platform, and even where it is, it is more complicated than checking only one counter.

Another reason is that the plaintiff must remain valid until it completes the execution. Since the remote object is stored in the counter object, this means that the counter object must remain valid. Think about what might happen if one object is shared_ptr and one weak_ptr for some object, and they are reset simultaneously in parallel threads. Let say that shared_ptr first. It reduces the "usage count" to zero and begins to execute the debiter. Now weak_ptr reduces the "weak count" to zero and finds that the "usage counter" is also zero. Thus, it deletes the object "counter", and with it the debiter. While the debiter is still running.

Of course, there would be different ways to ensure that the counter object remains alive, but I think that increasing the "weak count" on one is a very elegant and intuitive solution. A weak count becomes the reference count for the counter object. And since shared_ptr refers to a counter object, they must also increase the "weak count".

Probably an even more intuitive solution would be to increase the "weak score" for each individual shared_ptr , since each single shared_ptr contains a reference to the "counter" object.

Adding one for all shared_ptr instances is just an optimization (it saves one atomic increment / decrement when copying / assigning shared_ptr instances).

+82
Apr 15 2018-11-11T00:
source share

Basically, "weak_ptr" is a regular "T *" pointer, which allows you to RESTORE a strong link, that is, "shared_ptr", later in the code.

Like regular T *, weak_ptr does not do any reference counting. Internally, to support reference counting on an arbitrary type T, STL (or any other library that implements this type of logic) creates a wrapper object, which we will call Anchor. An “anchor” exists only in order to implement a reference counter and “when the number of null values ​​caused by a call” is needed.

In a strong link, shared_ptr implements its copy instance, operator =, constructor, destructor and other relevant APIs to update the "Anchor" link counter. This is how shared_ptr ensures that your T lives exactly as long as someone uses it. In "weak_ptr", the same APIs simply copy the actual anchor binding. They DO NOT update the number of links.

This is why the most important weak_ptr APIs have expired and the poorly called lock. "Expired" tells you if the base object is still located, i.e. "Has he already retired because all the strong links are out of scope?" A “lock” (if possible) converts weak_ptr to a strong shared_ptr link, restoring link counts.

By the way, "lock" is a terrible name for this API. You do not (just) call the mutex, you create a strong link from the weak, so that the "Anchor" acts. The biggest drawback of both patterns is that they did not implement the-> operator, so in order to do something with your object, you must restore the original "T *". They did this mainly to support things like "shared_ptr", because primitive types do not support the "->" operator.

-5
Jul 31 '14 at 18:59
source share



All Articles