首页 > Note > 关于 enable_shared_from_this

关于 enable_shared_from_this

2019年5月16日 发表评论 阅读评论

一. 引入

简单地说: enable_shared_from_this 是为了解决 在类的内部获取自己的 shared_ptr 这件事情而存在的。

众所周知, 每一个对象都能通过this 指针来访问自己的地址。this 指针也是所有成员函数的隐含参数。然而有些时候,我们需要的不仅是 this,而是一个 “this的智能指针”。

这里有一个常见的场景:

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() 被调用的时候,该类的是否还在生存中:

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 就有存在的必要了:

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 不可以吗,就像下面代码所表现的?

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> 头文件中,其实现非常简单:

// 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

如下代码:

    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

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可以这么写:

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());
    }
};

 

  1. 本文目前尚无任何评论.
  1. 本文目前尚无任何 trackbacks 和 pingbacks.