一. 引入
简单地说: enable_shared_from_this 是为了解决 在类的内部获取自己的 shared_ptr 这件事情而存在的。
众所周知, 每一个对象都能通过this 指针来访问自己的地址。this 指针也是所有成员函数的隐含参数。然而有些时候,我们需要的不仅是 this,而是一个 “this的智能指针”。
这里有一个常见的场景:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class A {public : A () :did_it_ (false ) {} ~A () { std::cout << "destoried" << std::endl; } void OnDo (bool did) { did_it_ = did; std::cout << "somthing did" << std::endl; } void DoSth_Async () { std::thread t ([this ]() { std::this_thread::sleep_for(std::chrono::seconds(5 )); OnDo(true ); }) ; t.detach (); } private : bool did_it_; };
代码如上:在异步方法 DoSth_Async() 中调用了成员方法 OnDo(bool) . 这里存在一个问题: 当 OnDo() 被调用的时候,该类的是否还在生存中:
1 2 3 4 5 6 7 8 int main () { { std::shared_ptr<A> ptr (new A()) ; ptr->DoSth_Async (); } std::this_thread::sleep_for (std::chrono::seconds (5 )); return 0 ; }
智能指针 ptr 在出作用域后立即被释放。所以当 OnDo() 被调用的时候,其所在的对象实际已经被释放了。如果确保在 OnDo() 被调用的时候,该对象仍然在生命周期内呢?一个方便的方法便上在构建线程的时候,将该对象的 shared_ptr 传入到线程。在该线程的生命周期内,该对象就会一直存在。这是一种利用 shared_ptr 的 保活机制 。
此时, enable_shared_from_this 就有存在的必要了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class A : public std::enable_shared_from_this<A> {public : A () :did_it_ (false ) {} ~A () { std::cout << "destoried" << std::endl; } void OnDo (bool did) { did_it_ = did; std::cout << "somthing did" << std::endl; } void DoSth_Async () { auto self = shared_from_this (); std::thread t ([this , self]() { std::this_thread::sleep_for(std::chrono::seconds(3 )); OnDo(true ); }) ; t.detach (); } private : bool did_it_; };
enable_shared_from_this 是一个模板类。它一般用作基类,它的成员 shared_from_this() weak_from_this() 可以使继承此类的类从当前对象获取其本身的 shared_ptr 或者 weak_ptr 并且增加引用计数.
我们直接使用 this 指针来构建自身的 shared_ptr 不可以吗,就像下面代码所表现的?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class C {public : std::shared_ptr<C> GetSelf () { return std::shared_ptr <C>(this ); } void DoSomthing () { auto ptr = GetSelf (); std::cout << ptr.use_count () << std::endl; } }; int main () { std::shared_ptr<C> ptr_c (new C()) ; ptr_c->DoSomthing (); std::cout << ptr_c.use_count () << std::endl; return 0 ; }
这种方法在使用的时候可能看不出问题,但是在对象析构的时候将会出现问题:一个对象将被释放两次,在 DoSomthing() 方法结束后它将释放一次,在 main() 函数完成后又将释放一次。究其原因,GetSelf() 构造智能指针时,其引用计数并没有自增。
二. 原理
enable_shared_from_this 位于 <memory> 头文件中,其实现非常简单:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 template <class _Ty > class enable_shared_from_this { public : using _Esft_type = enable_shared_from_this; _NODISCARD shared_ptr<_Ty> shared_from_this () { return (shared_ptr <_Ty>(_Wptr)); } _NODISCARD shared_ptr<const _Ty> shared_from_this () const { return (shared_ptr <const _Ty>(_Wptr)); } _NODISCARD weak_ptr<_Ty> weak_from_this () noexcept { return (_Wptr); } _NODISCARD weak_ptr<const _Ty> weak_from_this () const noexcept { return (_Wptr); } protected : constexpr enable_shared_from_this () noexcept : _Wptr() { } enable_shared_from_this (const enable_shared_from_this&) noexcept : _Wptr() { } enable_shared_from_this& operator =(const enable_shared_from_this&) noexcept { return (*this ); } ~enable_shared_from_this () = default ; private : template <class _Other , class _Yty > friend void _Enable_shared_from_this1(const shared_ptr<_Other>& _This, _Yty * _Ptr, true_type); mutable weak_ptr<_Ty> _Wptr; };
其中友元 _Enable_shared_from_this1() 将会被类 shared_ptr 调用。在这个友元里,会尝试着给私有成员 _Wptr 赋。当 shared_from_this() 被调用时,使用 _Wptr 构造一个 shared_ptr ,此时引用计数增 1 。
三. 陷阱
1. 在原生指针的对象里调用了 shared_from_this
如下代码:
1 2 A *a = new A (); a->DoSth_Async ();
由于没有使用 shared_ptr, 友元 _Enable_shared_from_this1() 不会被调用,此时 _Wptr 是 empty 的。如果强行调用 shared_from_this() 将会引发异常 exception : std::bad_weak_ptr
2. 过早调用 shared_from_this()
代码如下: 在构造函数中调用了 shared_from_this() 时, 还没有给 _Wptr 赋值。此时会引发 exception : std::bad_weak_ptr
1 2 3 4 5 6 class Sample : public std::enable_shared_from_this<Sample>{public : Sample () { auto ptr = shared_from_this (); } };
3. 关于继承
在一棵继承树里重复的继承 std::enable_shared_from_this 将引发编译错误。如果需要在子类中使用 shared_from_this可以这么写:
1 2 3 4 5 6 7 8 9 10 11 class Super : public std::enable_shared_from_this<Super> {public : virtual ~Super () {} }; class Sub : public Super {public : std::shared_ptr<Sub> shared_from_this () { return std::dynamic_pointer_cast <Sub>(Super::shared_from_this ()); } };