The role and implementation of the enable_shared_from_this class,
Example
In practice, you often need to obtain your own shared_ptr within an object managed by shared_ptr. For example:
- Construct a shared_ptr using the this pointer, as shown below:
1 struct Bad 2 {3 void fun () 4 {5 shared_ptr <Bad> sp {this}; 6 cout <sp-> count () <endl; 7} 8}; 9 shared_ptr <Bad> sp {make_shared <Bad> ()}; 10 sp-> fun (); // The output is 1
However, note that the shared_ptr constructed in this way does not share a counter with other shared_ptr. In this case, the object will be released repeatedly during the analysis, leading to an error.
The correct method is to inherit the enable_shared_from_this class and call the sahred_from_this () function to generate shared_ptr. The usage is as follows:
1 struct Good: public std: enable_shared_from_this <Good> 2 {3 void fun () 4 {5 shared_ptr <Good> sp {shared_from_this ()}; 6 cout <sp-> count () <endl; 7} 8}; 9 shared_ptr <Good> sp {make_shared <Good> ()}; // -------------- * 1*10 sp-> fun (); // The output is 2.
In the class, use the shared_from_this () function defined by enable_shared_from_this to construct a shared_ptr <Good> object, which can share a Good object with other shared_ptr objects.
Enable_shared_from_this Implementation Analysis (gcc-7.2.0-based source code)
Most implementations are implemented through weak_ptr. first, use the pointer of the object to be managed (obj) and the existing shared_ptr (sp1 ,..., the number of SPNs (spi-> use_count () to initialize a weak_ptr <Obj> (& obj, spi-> use_count (), and then construct a shared_ptr using this weak_ptr.
1 // enable_shared_from_this implementation 2 // based on (/usr/include/c ++/7.3.0/bits/shared_ptr.h) 3 // This code is a simplified version of gcc implementation, it is used only to describe the principle. 4 template <typename T> 5 class enable_shared_from_this 6 {7 public: 8 shared_ptr <T> shared_from_this () 9 {10 return shared_ptr <T> (this-> weak_this ); 11} 12 shared_ptr <const T> shared_from_this () const13 {14 return shared_ptr <const T> (this-> weak_this); 15} 16 private: 17 template <typename> 18 friend class shared_ptr; 19 20 template <typename T1> 21 void _ M_weak_assign (T1 * p, const shared_count <> & n) 22 {23 weak_this. _ M_assign (p, n); 24} 25 26 mutable weak_ptr <T> weak_this; 27 };
The enable_shared_from_this <T> class defines a weak_ptr <T>, which serves to generate a shared_ptr <T> object from the obj pointer. according to the previous principle, we may think that we initialize weak_this at the same time during obj initialization, but it is clear that weak_this is not initialized in this Code (not in the original code. Why doesn't gcc implement it like this ?).
Gcc processes weak_ptr in the shared_ptr constructor <T>. from the perspective of the Good class, weak_ptr <Good> weak_this in the Good object is processed at * 1 *, so that it points to a valid Good object and modifies use_count. the usage of the above Good class for enable_shared_from_this is a few valid methods, which must be guaranteed. If shared_from_this () is called for an object, this object must be held by shared_ptr <T>. from the above principle, we can understand the reason for this: the first shared_ptr holding the Good object g_obj <T> sp1 will process weak_this of g_obj to make it valid. without this step, when shared_from_this () is called, weak_this is an invalid value, that is, weak_this.expire () = true, an exception is thrown.
So how do we deal with weak_ptr in the shared_ptr constructor?
Shared_ptr defines such a function (from class _ shared_ptr in/usr/include/c ++/7.3.0/bits/shared_ptr_base.h ):
1 template<typename _Yp, typename _Yp2 = typename remove_cv<_Yp>::type> 2 typename enable_if<__has_esft_base<_Yp2>::value>::type 3 _M_enable_shared_from_this_with(_Yp* __p) noexcept 4 { 5 if(auto __base = __enable_shared_from_this_base(_M_refcount, __p)) 6 __base->_M_weak_assign(const_cast<_Yp2*>(__p), _M_refcount); 7 } 8 9 template<typename _Yp, typename _Yp2 = typename remove_cv<_Yp>::type>10 typename enable_if<!__has_esft_base<_Yp2>::value>::type11 _M_enable_shared_from_this_with(_Yp*) noexcept { }
_ Yp is the type of objects managed by shared_ptr. The two template functions are represented as follows:
When _ Yp is a subclass of make_shared_from_this, the first function is generated. Its function is to call the _ M_weak_assign function through the pointer of the _ Yp object to modify the weak_this Member of the _ Yp object, in fact, _ M_weak_assign calls the _ M_assign function.
Otherwise, the second function is generated, which does not work.
1 // from shared_ptr_base.h class __weak_ptr, derived by weak_ptr 2 3 void _M_assign(_Tp* __ptr, const __shared_count<_Lp>& __refcount) noexcept 4 { 5 if (use_count() == 0) 6 { 7 _M_ptr = __ptr; 8 _M_refcount = __refcount; 9 }10 }
The _ M_enable_shared_from_this_with function is called in the shared_ptr <_ Yp> constructor to check whether _ Yp inherits from make_shared_from_this and perform corresponding processing. here, _ M_refcount is a member of shared_ptr, which is used to record the number of shared_ptr management operations on _ Yp. in this way, the weak_ptr processing is completed to make it a valid value. when you call the shared_from_this () function later, you can use weak_this to call the shared_ptr constructor to generate a shared_ptr that shares the same object.
The above code references the instance code on cppreference and the source code of gcc.