Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does shared_ptr needs to hold reference counting for weak_ptr?

Quoted from C++ Primer $12.1.6:

A weak_ptr (Table 12.5) is a smart pointer that does not control the lifetime of the object to which it points. Instead, a weak_ptr points to an object that is managed by a shared_ptr. Binding a weak_ptr to a shared_ptr does not change the reference count of that shared_ptr. Once the last shared_ptr pointing to the object goes away, the object itself will be deleted. That object will be deleted even if there are weak_ptrs pointing to it—hence the name weak_ptr, which captures the idea that a weak_ptr shares its object “weakly.”

However,I've read an article says:

using make_shared is more efficient. The shared_ptr implementation has to maintain housekeeping information in a control block shared by all shared_ptrs and weak_ptrs referring to a given object. In particular, that housekeeping information has to include not just one but two reference counts:

  1. A “strong reference” count to track the number of shared_ptrs currently keeping the object alive. The shared object is destroyed (and possibly deallocated) when the last strong reference goes away.

  2. A “weak reference” count to track the number of weak_ptrs currently observing the object. The shared housekeeping control block is destroyed and deallocated (and the shared object is deallocated if it was not already) when the last weak reference goes away.

As far as I know,the shared_ptr created by make_shared is in the same control block with those ref countings.So the object will not be released until the last weak_ptr expires.

Question:

  1. Is the Primer wrong? Because weak_ptr will actually affects the lifetime of that object.
  2. Why does the shared_ptr need to track its weak refs?The weak_ptr can tell if the object exists by checking the strong refs in control blocks,so I think the control block does not need to track the weak refs.
  3. Just for curiosity,what does the control block created by shared_ptr look like?Is it something like:

    template<typename T>
    class control_block
    {
       T object;
       size_t strong_refs;
       size_t weak_refs;
       void incre();
       void decre();
       //other member functions...
    };
    //And in shared_ptr:
    template<typename T>
    class shared_ptr
    {
       control_block<T> block;//Is it like this?So that the object and refs are in the same block?
       //member functions...
    };
    
like image 672
scottxiao Avatar asked Mar 31 '18 08:03

scottxiao


People also ask

Is shared_ptr reference counting?

It is a reference counting ownership model i.e. it maintains the reference count of its contained pointer in cooperation with all copies of the std::shared_ptr. So, the counter is incremented each time a new pointer points to the resource and decremented when destructor of the object is called.

What is the difference between shared_ptr and weak_ptr?

The only difference between weak_ptr and shared_ptr is that the weak_ptr allows the reference counter object to be kept after the actual object was freed. As a result, if you keep a lot of shared_ptr in a std::set the actual objects will occupy a lot of memory if they are big enough.

Why would you choose shared_ptr instead of Unique_ptr?

Use unique_ptr when you want to have single ownership(Exclusive) of the resource. Only one unique_ptr can point to one resource. Since there can be one unique_ptr for single resource its not possible to copy one unique_ptr to another. A shared_ptr is a container for raw pointers.

What is the purpose of the shared_ptr <> template?

std::shared_ptr is a smart pointer that retains shared ownership of an object through a pointer.


1 Answers

The reference count controls the lifetime of the pointed-to-object. The weak count does not, but does control (or participate in control of) the lifetime of the control block.

If the reference count goes to 0, the object is destroyed, but not necessarily deallocated. When the weak count goes to 0 (or when the reference count goes to 0, if there are no weak_ptrs when that happens), the control block is destroyed and deallocated, and the storage for the object is deallocated if it wasn't already.

The separation between destroying and deallocating the pointed-to-object is an implementation detail you don't need to care about, but it is caused by using make_shared.

If you do

shared_ptr<int> myPtr(new int{10});

you allocate the storage for the int, then pass that into the shared_ptr constructor, which allocates storage for the control block separately. In this case, the storage for the int can be deallocated as early as possible: as soon as the reference count hits 0, even if there is still a weak count.

If you do

auto myPtr = make_shared<int>(10);

then make_shared might perform an optimisation where it allocates the storage for the int and the control block in one go. This means that the storage for the int can't be deallocated until the storage for the control block can also be deallocated. The lifetime of the int ends when the reference count hits 0, but the storage for it is not deallocated until the weak count hits 0.

Is that clear now?

like image 200
BoBTFish Avatar answered Oct 06 '22 22:10

BoBTFish