关于 enable_shared_from_this
目录
一. 引入
简单地说: 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)); //...do somthing 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)); //...do somthing 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(); //print 1 std::cout << ptr_c.use_count() << std::endl; //print 1 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 |
// CLASS TEMPLATE enable_shared_from_this template<class _Ty> class enable_shared_from_this { // provide member functions that create shared_ptr to this public: using _Esft_type = enable_shared_from_this; _NODISCARD shared_ptr<_Ty> shared_from_this() { // return shared_ptr return (shared_ptr<_Ty>(_Wptr)); } _NODISCARD shared_ptr<const _Ty> shared_from_this() const { // return shared_ptr return (shared_ptr<const _Ty>(_Wptr)); } _NODISCARD weak_ptr<_Ty> weak_from_this() noexcept { // return weak_ptr return (_Wptr); } _NODISCARD weak_ptr<const _Ty> weak_from_this() const noexcept { // return weak_ptr return (_Wptr); } protected: constexpr enable_shared_from_this() noexcept : _Wptr() { // construct } enable_shared_from_this(const enable_shared_from_this&) noexcept : _Wptr() { // construct (must value-initialize _Wptr) } enable_shared_from_this& operator=(const enable_shared_from_this&) noexcept { // assign (must not change _Wptr) 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()); } }; |