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

    }

};

 

最后需要注意两点:
1. 使用std::dynamic_pointer_cast<T>()需要基类中存在虚函数,这是由于这个转换函数使用输入的类型和目标类型中是否存在相同签名的虚函数作为转换能否成功的标识。最简单也是正确的解决方法是将基类中的析构函数声明为虚函数。
2. 不能在构造函数中使用shared_form_this()。这是由于std::enable_share_from_this在实现时使用了一个对象的weak_ptr,而这个weak_ptr需要对象的shared_ptr进行初始化。由于此时对象尚未构造完成,所以会抛出std::bad_weak_ptr的异常。关于这点目前没有较为完美的方案,可以尝试写一个init()函数,在对象构造后手动调用。或是手动写一个std::shared_ptr<Derived>(this)使用,但这种解决方案可能造成循环引用。更多方案请查阅StackOverFlow。

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值