Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

Example Analysis of Intelligent pointers shared_ptr and weak_ptr in Category 11

2025-03-26 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/03 Report--

The purpose of this article is to share with you the content of the sample analysis of smart pointers shared_ptr and weak_ptr in Category 11. The editor thinks it is very practical, so share it with you as a reference and follow the editor to have a look.

1. Preface

This article only analyzes the shared_ptr and weak_ptr source code of C++ smart pointers, which requires readers to have a certain C++ foundation and have some understanding of smart pointers. This article does not elaborate and analyze the use, scene and efficiency of smart pointers, and these knowledge should be understood by consulting relevant books.

2. Source code preparation

This article is based on the analysis of the source code of gcc-4.9.0. Shared_ptr and weak_ptr are added to the standard only by Clear11, so there is no shared_ptr and weak_ptr in the lower version of gcc source code. It is recommended to choose 4.9.0 or newer version to learn. Different versions of gcc source code should be different, but the principle and design idea are the same. The source code download address is given below.

Http://ftp.gnu.org/gnu/gcc

3. The concept of intelligent pointer

Smart pointers (Smart pointers) are objects that store pointers to dynamically allocated objects (on the heap). In other words, a smart pointer is actually an object. However, it behaves much like C++ 's built-in pointers, except that they can automatically delete the objects they point to when appropriate. Smart pointers play a very significant role in the face of exceptions, and they can ensure the complete deconstruction of dynamically allocated objects. They can also be used to track dynamically allocated objects shared by multiple owners. Conceptually, a smart pointer can be seen as owning the object it points to and is therefore responsible for deleting the object when it is no longer needed.

4. Source code parsing 4.1, shared_ptr parsing 4.1.1, shared_ptr

Shared_ptr is located in libstdc++-v3\ include\ bits\ shared_ptr.h

Templateclass shared_ptr: public _ _ shared_ptr {public:... / / constructor template explicit shared_ptr (_ Tp1* _ p): _ _ shared_ptr (_ _ p) {}.}

Because the source code is too long, only a part of it is posted here for analysis:

This class has no class members

This class inherits from _ _ shared_ptr, and the constructor simply calls the constructor of _ _ shared_ptr, passing the takeover normal pointer to _ _ shared_ptr

This class does not overload the * and-> operators. From this point, shared_ptr does not seem to be able to implement the function of ordinary pointers. It is inferred that the overloading of these two operators is implemented in the parent class _ _ shared_ptr

There is no destructor in this class. Judging from the fact that the smart pointer will eventually release memory automatically, the release work is certainly not carried out in this class. Next, the implementation of the parent class _ _ shared_ptr is analyzed.

4.1.2 、 _ _ shared_ptr

_ _ shared_ptr is located in libstdc++-v3\ include\ bits\ shared_ptr_base.h

Templateclass _ _ shared_ptr {public: typedef _ Tp element_type / / Constructor template explicit _ _ shared_ptr (_ Tp1* _ p): _ M_ptr (_ _ p), _ M_refcount (_ _ p) {_ _ glibcxx_function_requires (_ ConvertibleConcept) static_assert (! is_void::value, "incomplete type") Static_assert (sizeof (_ Tp1) > 0, "incomplete type"); _ _ enable_shared_from_this_helper (_ M_refcount, _ _ p, _ _ p);} / / destructor ~ _ shared_ptr () = default Typename std::add_lvalue_reference::type operator* () const noexcept {_ GLIBCXX_DEBUG_ASSERT (_ M_ptr! = 0); return * _ Tp* operator-;} _ Tp* operator- > () const noexcept {_ GLIBCXX_DEBUG_ASSERT (_ M_ptr! = 0) Return _ Contained pointer;}. Private: _ Tp* _ Macroptra; / / Contained pointer. _ _ shared_count _ Macrorefract; / / Reference counter.}

Similarly, the source code is long and is not the focus of the analysis, so only part of it is posted for analysis:

You can see that there are two class members: _ M_ptr (a normal pointer taken over by a smart pointer) and _ M_refcount (reference counter of type _ _ shared_count)

From the point of view of the constructor, _ M_ptr gets the value of the normal pointer to take over, and this value is also required for the construction of _ M_refcount

The * and-> operators are overloaded and inherited by shared_ptr so that smart pointers end up behaving like ordinary pointers, even though smart pointers are essentially an object

From the point of view of the destructor, nothing is done in it, indicating that the ordinary pointer to take over is not released here, so it is possible that the work of releasing memory is done by _ M_refcount. Let's analyze the implementation of _ _ shared_count.

4.1.3 、 _ _ shared_count

_ _ shared_count is located in libstdc++-v3\ include\ bits\ shared_ptr_base.h

Templateclass _ _ shared_count {public: constexpr _ shared_count () noexcept: _ M_pi (0) {} template explicit _ shared_count (_ Ptr _ p): _ M_pi (0) {_ try {_ M_pi = new _ Sp_counted_ptr (_ _ p) } _ _ catch (...) {delete _ _ p; _ _ throw_exception_again }} template _ shared_count (_ Ptr _ p, _ Deleter _ d): _ _ shared_count (_ _ p, std::move (_ d), allocator ()) {} template _ shared_count (_ Ptr _ p, _ Deleter _ d, _ Alloc _ a): _ M_pi (0) {typedef _ Sp_counted_deleter _ Sp_cd_type Typedef typename allocator_traits::template rebind_traits _ Alloc_traits; typename _ Alloc_traits::allocator_type _ a2 (_ _ a); _ Sp_cd_type* _ _ mem = 0; _ _ try {_ _ mem = _ Alloc_traits::allocate (_ _ a2,1) _ Alloc_traits::construct (_ _ a2, _ _ mem, _ _ p, std::move (_ d), std::move (_ a)); _ M_pi = _ _ mem;} _ _ catch (...) {_ d (_ p) / / Call _ Deleter on_ _ p. If (_ _ mem) _ Alloc_traits::deallocate (_ a2, _ _ mem, 1); _ _ throw_exception_again }} template _ _ shared_count (_ Sp_make_shared_tag, _ Tp*, const _ Alloc& _ a, _ Args&&... _ _ args): _ M_pi (0) {typedef _ Sp_counted_ptr_inplace _ Sp_cp_type; typedef typename allocator_traits::template rebind_traits _ Alloc_traits Typename _ Alloc_traits::allocator_type _ a2 (_ a); _ Sp_cp_type* _ mem = _ Alloc_traits::allocate (_ _ a2,1); _ _ try {_ Alloc_traits::construct (_ _ a2, _ _ mem, std::move (_ _ a), std::forward (_ _ args)...) _ M_pi = _ _ mem;} _ _ catch (...) {_ Alloc_traits::deallocate (_ a2, _ _ mem, 1); _ _ throw_exception_again }} template explicit _ shared_count (std::unique_ptr&& _ r): _ M_pi (0) {using _ Ptr = typename unique_ptr::pointer; using _ Del2 = typename conditional::type; using _ Sp_cd_type = _ Sp_counted_deleter; using _ Alloc = allocator; using _ Alloc_traits = allocator_traits; _ Alloc_ _ a _ Sp_cd_type* _ _ mem = _ Alloc_traits::allocate (_ _ a, 1); _ Alloc_traits::construct (_ _ a, _ _ mem, _ _ r.release (), _ _ r.get_deleter ()); / / non-throwing _ M_pi = _ _ mem;} explicit _ _ shared_count (const _ _ weak_count& _ r) Explicit _ _ shared_count (const _ _ weak_count& _ r, std::nothrow_t); ~ _ shared_count () noexcept {if (_ M_pi! = nullptr) _ maspih-> _ M_release () } _ _ shared_count (const _ _ shared_count& _ r) noexcept: _ M_pi (_ _ r._M_pi) {if (_ M_pi! = 0) _ M_add_ref_copy-> _ M_add_ref_copy () } _ _ shared_count& operator= (const _ _ shared_count& _ r) noexcept {_ Sp_counted_base* _ _ tmp = _ _ r _ M_pi; if (_ _ tmp! = _ M_pi) {if (_ _ tmp! = 0) _ _ tmp- > _ M_add_ref_copy () If (_ M_pi! = 0) _ noexcept-> _ M_release (); _ M_pi = _ _ tmp;} return * this;} void _ M_swap (_ _ shared_count& _ r) noexcept {_ Sp_counted_base* _ tmp = _ _ r._M_pi _ _ r._M_pi = _ M_get_use_count; _ M_pi = _ _ tmp;} long _ M_get_use_count () const noexcept {return _ M_pi! = 0? _ M_get_use_count-> _ M_get_use_count (): 0;} bool _ M_unique () const noexcept {return this- > _ M_get_use_count () = = 1 } void* _ M_get_deleter (const std::type_info& _ ti) const noexcept {return _ M_pi? _ ti-> _ M_get_deleter (_ _ ti): nullptr;} bool _ M_less (const _ _ shared_count& _ rhs) const noexcept {return std::less () (this- > _ M_pi, _ _ rhs._M_pi) } bool _ M_less (const _ _ weak_count& _ rhs) const noexcept {return std::less () (this- > _ M_pi, _ _ rhs._M_pi);} friend inline bool operator== (const _ _ shared_count& _ a, const _ shared_count& _ b) noexcept {return _ a._M_pi = = _ _ b.roomMipi;} private: friend class _ weak_count _ Sp_counted_base* _ makeshift;}

The following information can be obtained from the source code:

There is a class member: _ M_pi (counter, type _ Sp_counted_base)

Only the constructor allocates memory for _ M_pi, and the class does not directly hold the normal pointer that has been passed all the time, but continues to pass it to _ M_pi, so memory is not freed directly in this class.

The copy constructor does not assign content, but takes the _ M_pi of the copy object directly, which is similar to a shallow copy, and then calls the _ M_add_ref_copy method of _ M_pi (described later), increasing the reference count. The same is true for the assignment function, but because of the particularity of the assignment function (call the assignment function when the assignment object already exists, otherwise call the copy constructor), you should first call the _ M_release method of _ M_pi (later) to release the memory you hold, and the rest of the operation is the same as copying the constructor.

As you can see from the destructor, the memory allocated for _ M_pi is not released directly, but the _ M_release method of _ M_pi is called. It can be guessed that the memory of _ M_pi is freed through the _ M_release method (delete this pointer, which will be discussed later).

Since all the methods in _ _ shared_count are implemented with _ M_pi, and we haven't seen the code to release that ordinary pointer so far, we have to continue to see what _ M_pi has done, and then move on to the implementation of _ Sp_counted_base.

4.1.4 、 _ Sp_counted_base

_ Sp_counted_base is located in libstdc++-v3\ include\ bits\ shared_ptr_base.h

Templateclass _ Sp_counted_base: public _ Mutex_base {public: _ Sp_counted_base () noexcept: _ M_use_count (1), _ M_weak_count (1) {} virtual ~ _ Sp_counted_base () noexcept {} virtual void _ M_dispose () noexcept = 0; virtual void _ M_destroy () noexcept {delete this } virtual void* _ M_get_deleter (const std::type_info&) noexcept = 0; void _ M_add_ref_copy () {_ _ gnu_cxx::__atomic_add_dispatch (& _ M_use_count, 1);} void _ M_add_ref_lock (); bool _ M_add_ref_lock_nothrow () Void _ M_release () noexcept {_ GLIBCXX_SYNCHRONIZATION_HAPPENS_BEFORE (& _ M_use_count); if (_ _ gnu_cxx::__exchange_and_add_dispatch (& _ M_use_count,-1) = = 1) {_ GLIBCXX_SYNCHRONIZATION_HAPPENS_AFTER (& _ M_use_count); _ M_dispose () If (_ Mutex_base::_S_need_barriers) {_ GLIBCXX_READ_MEM_BARRIER; _ GLIBCXX_WRITE_MEM_BARRIER;} _ GLIBCXX_SYNCHRONIZATION_HAPPENS_BEFORE (& _ M_weak_count) If (_ _ gnu_cxx::__exchange_and_add_dispatch (& _ M_weak_count,-1) = = 1) {_ GLIBCXX_SYNCHRONIZATION_HAPPENS_AFTER (& _ M_weak_count); _ M_destroy () } void _ M_weak_add_ref () noexcept {_ _ gnu_cxx::__atomic_add_dispatch (& _ M_weak_count, 1);} void _ M_weak_release () noexcept {_ GLIBCXX_SYNCHRONIZATION_HAPPENS_BEFORE (& _ M_weak_count) If (_ _ gnu_cxx::__exchange_and_add_dispatch (& _ M_weak_count,-1) = = 1) {_ GLIBCXX_SYNCHRONIZATION_HAPPENS_AFTER (& _ M_weak_count); if (_ Mutex_base::_S_need_barriers) {_ GLIBCXX_READ_MEM_BARRIER; _ GLIBCXX_WRITE_MEM_BARRIER } _ M_destroy ();}} long _ M_get_use_count () const noexcept {return _ atomic_load_n (& _ M_use_count, _ _ ATOMIC_RELAXED);} private: _ Sp_counted_base (_ Sp_counted_base const&) = delete; _ Sp_counted_base& operator= (_ Sp_counted_base const&) = delete _ Atomic_word _ Atomic_word _ shared; / / # weak + (# shared! = 0)}

The following information can be obtained from the source code:

There are two class members: _ M_use_count (reference count) and _ M_weak_count (weak reference count). Operations on these two numbers need to be atomic

The _ M_release method is the key to this class. You can see that you subtract the _ M_use_count by 1, and then determine whether the value of the _ M_use_count before the subtraction is 1 (no other references). If it is 1, the _ M_dispose method is called (the virtual function, implemented by the derived class, is estimated to release the normal pointer that has been taken over by the smart pointer). Next, subtract _ M_weak_count from 1, and then determine whether the value of _ M_weak_count before subtraction is 1 (no other references). If so, the _ M_destroy method is called, and the this pointer is released in the _ M_destroy method, which is consistent with the previous guess.

As can be seen from the _ M_release, the memory release work of the pointer taken over by the smart pointer is only related to _ M_use_count. When the _ M_use_count is reduced, it will be released, and the _ M_weak_count is also useful. It is responsible for releasing the _ Sp_counted_base itself, which is why weak_ptr can ensure that the smart pointer is valid. But the reason why there is no guarantee that the pointer referenced by the smart pointer is valid (this is completely consistent with the definition of shared_ptr and weak_ptr)

Other methods are very simple, for example, the _ M_add_ref_copy method adds the reference count _ M_use_count to one, and the _ M_weak_add_ref method adds the weak reference count _ M_weak_count to one. This self-increment process is atomic, so we will not repeat it here. You can take a look at the specific implementation for yourself.

4.1.5 、 _ Sp_counted_ptr

_ Sp_counted_ptr is located in libstdc++-v3\ include\ bits\ shared_ptr_base.h

Templateclass _ Sp_counted_ptr final: public _ Sp_counted_base {public: explicit _ Sp_counted_ptr (_ Ptr _ p) noexcept: _ M_ptr (_ p) {} virtual void _ M_dispose () noexcept {delete _ M_destroy () noexcept {delete this;} virtual void* _ M_get_deleter (const std::type_info&) noexcept {return nullptr } _ Sp_counted_ptr (const _ Sp_counted_ptr&) = delete; _ Sp_counted_ptr& operator= (const _ Sp_counted_ptr&) = delete;private: _ Ptr _ Mirrptra;}

You can see from the source code that _ Sp_counted_ptr is a derivative of _ Sp_counted_base, and _ _ shared_count also uses _ Sp_counted_ptr when initializing _ M_pi.

Let's take a look at the implementation of the _ M_dispose method, which does delete the pointer that shared_ptr took over at the beginning, and the _ M_destroy method is used to free its own memory (called by _ _ shared_count), which is consistent with the previous guess.

4.1.6, shared_ptr summary

Looking back at the previous analysis, the _ M_add_ref_copy method of _ Sp_counted_base is the key to the entire process, which implements the increase of the reference counter, so when to call it is the key. By retrieving it in the code, you can find that the assignment constructor and copy constructor of _ _ shared_count called it (in fact, it can only be here, because only its class member has _ Sp_counted_base), so that the whole process is explained:

_ M_pi, a member of _ _ shared_count, is initialized only once (initialized by allocating memory in the constructor)

When the copy constructor is called later (this behavior is triggered by _ _ shared_ptr, both the copy constructor and the assignment function of _ _ shared_ptr call the copy constructor of _ _ shared_count), _ _ shared_count simply copies _ M_pi without reallocating memory, and then calls _ M_add_ref_copy to increase the reference count again. This implements the feature of increasing the reference count for each additional copy of shared_ptr.

Each _ _ shared_count is destructed to subtract the reference count by one, and then release the resources held by the smart pointer. This has been analyzed before, so I won't go into detail here.

4.2. weak_ptr parsing 4.2.1, weak_ptr

Weak_ptr is located in libstdc++-v3\ include\ bits\ shared_ptr.h

Templateclass weak_ptr: public _ weak_ptr {public: constexpr weak_ptr () noexcept: _ _ weak_ptr () {} template weak_ptr (const weak_ptr& _ r) noexcept: _ _ weak_ptr (_ r) {} template weak_ptr (const shared_ptr&) _ r) noexcept: _ _ weak_ptr (_ r) {} template weak_ptr& operator= (const weak_ptr& _ r) noexcept {this- > _ _ weak_ptr::operator= (_ r) Return * this;} template weak_ptr& operator= (const shared_ptr& _ r) noexcept {this- > _ _ weak_ptr::operator= (_ r); return * this;} shared_ptr lock () const noexcept {return shared_ptr (* this, std::nothrow) }}

You can see the following points from the source code:

This class has no class members

Judging from the parameters of the constructor (except for the no-parameter constructor), you can only use shared_ptr or weak_ptr to construct a weak_ptr object, including the assignment function, which is very different from shared_ptr. You can see from section 4.1.1 that shared_ptr can be constructed using ordinary pointers.

You can call the lock method to get an implementation of the shared_ptr,lock method, which will be discussed later.

This class does not overload the * and-> operators, and then analyzes the implementation of its parent class _ _ weak_ptr

4.2.2 、 _ _ weak_ptr

_ _ weak_ptr is located in libstdc++-v3\ include\ bits\ shared_ptr_base.h

Templateclass _ _ weak_ptr {public: typedef _ Tp element_type; constexpr _ _ weak_ptr () noexcept: _ M_ptr (0), _ M_refcount () {} _ weak_ptr (const _ weak_ptr&) noexcept = default; _ weak_ptr& operator= (const _ weak_ptr&) noexcept = default ~ _ weak_ptr () = default; template _ weak_ptr (const _ _ weak_ptr& _ r) noexcept: _ M_refcount (_ _ r._M_refcount) {_ M_ptr = _ _ r.lock (). Get () } template _ _ weak_ptr (const _ _ shared_ptr& _ r) noexcept: _ M_ptr (_ _ r._M_ptr) _ M_refcount (_ _ r._M_refcount) {} template _ _ weak_ptr& operator= (const _ _ weak_ptr& _ r) noexcept {_ M_ptr = _ _ r.lock () .get () _ M_refcount = _ _ r.refract; return * this;} template _ _ weak_ptr& operator= (const _ _ shared_ptr& _ _ r) noexcept {_ M_ptr = _ _ r.refract; _ M_refcount = _ _ r.refract; return * this } _ shared_ptr lock () const noexcept {return _ shared_ptr (* this, std::nothrow);} long use_count () const noexcept {return _ M_refcount._M_get_use_count ();} bool expired () const noexcept {return _ M_refcount._M_get_use_count () = = 0 } template bool owner_before (const _ shared_ptr& _ rhs) const {return _ M_refcount._M_less (_ _ rhs._M_refcount);} template bool owner_before (const _ weak_ptr& _ rhs) const {return _ M_refcount._M_less (_ _ rhs._M_refcount) } void reset () noexcept {_ _ weak_ptr (). Swap (* this);} void swap (_ _ weak_ptr& _ s) noexcept {std::swap (_ M_ptr, _ _ s._M_ptr); _ M_refcount._M_swap (_ _ s._M_refcount) } private: / / Used by _ _ enable_shared_from_this. Void _ M_assign (_ Tp* _ _ ptr, const _ _ shared_count& _ refcount) noexcept {_ M_ptr = _ _ ptr; _ M_refcount = _ _ refcount;} template friend class _ _ shared_ptr; template friend class _ _ weak_ptr; friend class _ _ enable_shared_from_this; friend class enable_shared_from_this _ Tp* _ Mitteptra; / / Contained pointer. _ _ weak_count _ Macrorefract; / / Reference counter.}

The following information can be seen from the source code:

There are two class members: _ M_ptr (a normal pointer taken over by a smart pointer) and _ M_refcount (weak reference counter of type _ _ weak_count)

From the point of view of the constructor, _ M_ptr obtains the value of the normal pointer to take over, and the construction of _ M_refcount does not need this value (which is different from _ _ shared_ptr). _ M_refcount can only be constructed with the help of other _ _ shared_ptr 's _ M_refcount or _ _ weak_ptr 's _ M_refcount (note that the two have different _ M_refcount types, indicating that _ _ weak_count supports multiple types of construction)

The implementation of copy constructor and assignment function is the same as above

This class still does not overload the * and-> operators, and since there is no inheritance relationship, weak_ptr does not have the characteristics of ordinary pointers and cannot use resources directly, which is in line with the definition of weak_ptr.

Since weak_ptr cannot use resources directly, what is his intention in designing the member _ M_ptr? The answer is that when the lock method converts weak_ptr to shared_ptr, you need to pass this pointer, otherwise the pointers of the connection pipe will lose the meaning of conversion.

The destructor does nothing because weak_ptr does not hold resources and does not affect the release of resources. Next, analyze _ _ weak_count

4.2.3 、 _ _ weak_count

The implementation of _ _ weak_count is located in libstdc++-v3\ include\ bits\ shared_ptr_base.h

Templateclass _ _ weak_count {public: constexpr _ _ weak_count () noexcept: _ M_pi (0) {} _ weak_count (const _ shared_count& _ r) noexcept: _ M_pi (_ _ r._M_pi) {if (_ M_pi! = 0) _ masculpi-> _ M_weak_add_ref () } _ _ weak_count (const _ _ weak_count& _ r) noexcept: _ M_pi (_ _ r._M_pi) {if (_ M_pi! = 0) _ M_weak_add_ref-> _ M_weak_add_ref () } ~ _ weak_count () noexcept {if (_ M_pi! = 0) _ M_weak_release-> _ M_weak_release () } _ _ weak_count& operator= (const _ _ shared_count& _ r) noexcept {_ Sp_counted_base* _ _ tmp = _ _ r _ r; if (_ _ tmp! = 0) _ _ tmp- > _ M_weak_add_ref () If (_ M_pi! = 0) _ tmp; return-> _ M_weak_release (); _ M_pi = _ _ tmp; return * this } _ _ weak_count& operator= (const _ _ weak_count& _ r) noexcept {_ Sp_counted_base* _ _ tmp = _ _ r _ r; if (_ _ tmp! = 0) _ _ tmp- > _ M_weak_add_ref () If (_ M_pi! = 0) _ tmp; return-> _ M_weak_release (); _ M_pi = _ _ tmp; return * this } void _ M_swap (_ _ weak_count& _ r) noexcept {_ Sp_counted_base* _ _ tmp = _ _ r.roomMappi; _ _ r._M_pi = _ maspih; _ M_pi = _ _ tmp } long _ M_get_use_count () const noexcept {return _ M_pi! = 0? _ M_less-> _ M_get_use_count (): 0;} bool _ M_less (const _ _ weak_count& _ rhs) const noexcept {return std::less () (this- > _ M_pi, _ _ rhs._M_pi) } bool _ M_less (const _ _ shared_count& _ rhs) const noexcept {return std::less () (this- > _ M_pi, _ _ rhs._M_pi);} friend inline bool operator== (const _ _ weak_count& _ a, const _ weak_count& _ b) noexcept {return _ a._M_pi = = _ _ b._M_pi } private: friend class _ _ shared_count; _ Sp_counted_base* _ masking;}

The following information can be obtained from the source code:

There is a class member: _ M_pi (counter, type _ Sp_counted_base)

If you take a closer look at _ _ shared_count, you also hold this member, and the type is exactly the same, which explains why _ _ shared_count and _ _ weak_count can be converted to each other in a simple way:

The process of converting _ _ shared_count to _ _ weak_count is:

Copy _ M_pi, and then call the _ M_weak_add_ref method to add a weak reference count. The process of converting _ _ weak_count to _ _ shared_count is:

Copy _ M_pi, and then call the _ M_add_ref_copy method to increase the reference count

Constructor, copy constructor and assignment function do not allocate memory for _ M_pi. From this point, we can see that weak_ptr is indeed an accessory of shared_ptr. It does not hold resources and does not control resources.

The _ M_weak_release method of _ M_pi is called in the destructor, freeing the memory of _ M_pi (only if the conditions are met)

The next content is the same as section 3.1.4 and section 3.1.5, so I won't repeat it here.

4.2.4. Looking back at the implementation of lock method in weak_ptr

The lock method of weak_ptr calls the constructor of shared_ptr as follows:

Shared_ptr (const weak_ptr& _ r, std::nothrow_t): _ _ shared_ptr (_ _ r, std::nothrow) {}

From the above code, you can see that the constructor of _ _ shared_ptr is called, as follows:

_ _ shared_ptr (const _ _ weak_ptr& _ r, std::nothrow_t): _ M_refcount (_ _ r._M_refcount, std::nothrow) {_ M_ptr = _ M_refcount._M_get_use_count ()? _ r._M_ptr: nullptr;}

You can see that at this time, the _ M_refcount member of _ _ weak_ptr (type _ _ weak_count) is used to construct the _ M_refcount member of _ _ shared_ptr (type _ _ shared_count), and then determine whether the reference counter is 0. If it is zero, the _ M_ptr member of _ _ shared_ptr is set to nullptr, that is, the lock function fails; if it is not zero, a shared_ptr will be built normally.

The method of constructing _ M_refcount described above is as follows:

Templateinline _ _ shared_count::__shared_count (const _ _ weak_count& _ r, std::nothrow_t): _ M_pi (_ _ r._M_pi) {if (_ M_pi! = nullptr) if (! _ M_pi-> _ M_add_ref_lock_nothrow ()) _ M_pi = nullptr } templateinline bool _ Sp_counted_base::_M_add_ref_lock_nothrow () {if (_ M_use_count = = 0) return false; + + _ return true;; return true;}

As we can see from the above code, first _ _ shared_count uses _ _ weak_count 's _ M_pi to build its own _ M_pi. From the previous analysis, we can know that the memory of _ M_pi will not be released until all shared_ptr and weak_ptr die, so even if the previous shared_ptr is dead (that is, resources have been released) _ M_pi is still valid (because weak_ptr is not dead yet). By judging the return value of _ M_add_ref_lock_nothrow to determine whether to set _ M_pi to nullptr, you can see that the condition for judging is whether _ M_use_count is 0 (that is, to determine whether the resource has been released).

Next, take a look at the _ M_get_use_count method of _ _ shared_count. The code is as follows:

Long _ M_get_use_count () const noexcept {return _ M_pi! = 0? _ M_get_use_count-> _ M_get_use_count (): 0;}

The code is relatively simple, which means that if the resource has been released (the corresponding value of _ Mpi is nullptr), 0 will be returned, and then back to point 2 above, _ M_ptr will be set to nullptr, that is, the resource is invalid and the execution of the lock function fails.

At this point, the implementation principle of weak_ptr 's lock method is fully explained.

4.3. enable_shared_from_this parsing 4.3.1, understanding the shortcomings of smart pointers from a typical example

Sometimes we need to get our own shared_ptr inside an object managed by shared_ptr, such as the following example:

Class Ptr {public: void fun () {std::shared_ptr p (this); std::cout use_count () fun (); / / output is 1

As you can see from the simple example above, the output of fun is 1 instead of 2. Why? Going back to section 4.1.2, you can see that when you use a normal pointer (the this above) to construct a shared_ptr, the constructed shared_ptr must be independent and not shared with others. In this way, there will be a very serious problem, that is, when the object is destructed, it will cause the object to be repeatedly released, thus causing an error.

4.3.2. Improve the method

Now let's be clear: when constructing the shared_ptr of an object inside an object, even if the object is already managed by shared_ptr, it will not cause the object to be managed by two independent smart pointers. This requires that when we construct the smart pointer of the object in the object, we must be able to identify whether the object has been managed by other smart pointers, the number of smart pointers, and we can also make the previous smart pointers aware after we create the smart pointers. Of course, the standard has also given a solution to this problem, that is, the use of the next mentioned enable_shared_from_this

4.3.3. Enable_shared_from_this parsing

The implementation of enable_shared_from_this is located in libstdc++-v3\ include\ bits\ shared_ptr.h

Templateclass enable_shared_from_this {protected: constexpr enable_shared_from_this () noexcept {} enable_shared_from_this (const enable_shared_from_this&) noexcept {} enable_shared_from_this& operator= (const enable_shared_from_this&) noexcept {return * this } ~ enable_shared_from_this () {} public: shared_ptr shared_from_this () {return shared_ptr (this- > _ M_weak_this);} shared_ptr shared_from_this () const {return shared_ptr (this- > _ M_weak_this) } private: template void _ M_weak_assign (_ Tp1* _ p, const _ shared_count& _ n) const noexcept {_ M_weak_this._M_assign (_ p, _ n) } template friend void _ enable_shared_from_this_helper (const_ _ shared_count& _ pn, const enable_shared_from_this* _ pe, const_ Tp1* _ px) noexcept {if (_ _ pe! = 0) _ _ pe- > _ M_weak_assign (const_cast (_ _ px), _ _ pn) } mutable weak_ptr _ recently scheduled events;}

The following information can be obtained from the source code:

There is a class member: _ M_weak_this

This class needs to be inherited and inherited by objects that need to be managed with smart pointers.

We usually use the shared_from_this method of this class, and we can see that its implementation is to construct a shared_ptr object using _ M_weak_this.

This class does not initialize _ M_weak_this directly, but provides a _ M_weak_assign method to construct _ M_weak_this. Its implementation is relatively simple by calling the _ M_assign method of weak_ptr.

So the question is, who calls the _ M_weak_assign method? From later on, we can see that it is called by a global function _ _ enable_shared_from_this_helper, which is overloaded as a friend function of enable_shared_from_this. As you can see from the above code, the only friend function is _ _ enable_shared_from_this_helper, which calls the _ M_weak_assign method of enable_shared_from_this.

At what point in time should the _ _ enable_shared_from_this_helper function be used to achieve the desired results? The answer, of course, is to call in the constructor of _ _ shared_ptr. The partial constructor of _ _ shared_ptr is listed below. You can see that _ _ enable_shared_from_this_helper is indeed called, confirming the previous conjecture.

Templateclass _ _ shared_ptr {public:... Template explicit _ _ shared_ptr (_ Tp1* _ p): _ M_ptr (_ _ p), _ M_refcount (_ p) {_ _ glibcxx_function_requires (_ ConvertibleConcept) static_assert (! is_void::value, "incomplete type") Static_assert (sizeof (_ Tp1) > 0, "incomplete type"); _ _ enable_shared_from_this_helper (_ M_refcount, _ p, _ p) } template _ shared_ptr (_ Tp1* _ p, _ Deleter _ d): _ M_ptr (_ _ p), _ M_refcount (_ _ p, _ d) {_ _ glibcxx_function_requires (_ ConvertibleConcept) _ enable_shared_from_this_helper (_ M_refcount, _ _ p _ p) } template _ _ shared_ptr (_ Tp1* _ p, _ Deleter _ d, _ Alloc _ a): _ M_ptr (_ _ p), _ M_refcount (_ _ p, _ d) Std::move (_ a)) {_ glibcxx_function_requires (_ ConvertibleConcept) _ enable_shared_from_this_helper (_ M_refcount, _ p, _ p) }.}; 4.3.4, _ _ enable_shared_from_this_helper resolution

The implementation of _ _ enable_shared_from_this_helper is located in libstdc++-v3\ include\ bits\ shared_ptr_base.h

/ / Friend of enable_shared_from_this.templatevoid _ _ enable_shared_from_this_helper (const _ shared_count&, const enable_shared_from_this*, const _ Tp2*) noexcept;templateinline void _ _ enable_shared_from_this_helper (const _ _ shared_count&,...) Noexcept {}

It is necessary to look at the implementation of the _ _ enable_shared_from_this_helper function again. There are two forms, the first is the friend function of the enable_shared_from_this mentioned above, and the second overloaded form does nothing. Why do you need to overload these two functions? The answer is simple: when one of our classes inherits enable_shared_from_this, the class can definitely be converted to enable_shared_from_this, and the _ _ enable_shared_from_this_helper called in _ _ shared_ptr is the first case above, in which case the shared_from_this function can be used. Conversely, when the class does not inherit enable_shared_from_this, the second form of _ _ enable_shared_from_this_helper is called, and the shared_from_this function cannot be used at this time.

At this point, it's all clear why the corresponding class needs to inherit enable_shared_from_this before using shared_from_this.

Thank you for reading! This is the end of this article on "sample Analysis of Smart pointers shared_ptr and weak_ptr in Craft 11". I hope the above content can be of some help to you, so that you can learn more knowledge. if you think the article is good, you can share it for more people to see!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report