从源码角度解读 enable_shared_from_this
我们在使用 C++ 的时候,有时会需要在类的内部获取自身的 shared_ptr,这就会用到 std::enable_shared_from_this
。在实际使用过程中,std::enable_shared_from_this
有三个陷阱需要注意:
- 不能在构造函数中使用 shared_from_this(), 否则会抛出 std::bad_weak_ptr 异常。对应下面情况 1。
- 创建的对象必须由 shared_ptr 管理,shared_from_this() 才能生效,否则也会报 std::bad_weak_ptr 异常。对应下面情况 2。
- 对应类必须 public 继承 std::enable_shared_from_this, 不能是 protected 或 private 继承,否则也会报 std::bad_weak_ptr 异常。对应下面情况 3。
以上 case 均可以通过 wandbox 复现。
那么为什么会有这些限制呢?本文将从 std::enable_shared_from_this 的源码角度解读其原因。(本文基于 clang libc++ 的源码实现进行解读, 代码地址:shared_ptr.h#L1433)
1 |
|
我把 enable_shared_from_this 的源码摘录下来,删掉了一些不太重要的逻辑以方便理解。代码如下:
1 | template <class _Tp> |
从代码可以看出 enable_shared_from_this 核心的就是一个 weak_ptr 属性 __weak_this_
。而 shared_from_this 其实就是把 weak_ptr 转换成 shared_ptr。
那么问题来了,__weak_this_
是在什么时候设置呢?答案是:在创建 shared_ptr 对象的时候。
以下是 shared_ptr 中创建对象的逻辑,其中在 __enable_weak_this
中设置了 enable_shared_from_this 的 __weak_this_
属性。
1 | template <class _Yp, class _CntrlBlk> |
在 __enable_weak_this
的实现中,因为 enable_shared_from_this 类里面将 shared_ptr<T>
设置为了 friend class。因此 shared_ptr 可以直接访问并设置 enable_shared_from_this 的 __weak_this_
属性。
同时,__enable_weak_this
使用 SFINAE 实现了一个模板匹配,即:只有当满足 __enable_if_t<is_convertible<_OrigPtr*, const enable_shared_from_this<_Yp>*>::value, int> = 0
时(即对应类可以转换成 enable_shared_from_this,也就是类 public 继承了 enable_shared_from_this), 才会设置 __weak_this_
。 否则会匹配到一个空实现。
1 | // 匹配到 enable_shared_from_this |
解读完源码之后,一切情况非常明了。我们再回头看下文章刚开始提到的三个陷阱:
- 情况 1:不能在构造函数中使用 shared_from_this()。这是因为整个过程是:先创建好了原始对象,再去设置
__weak_this_
属性,最终才能得到一个 shared_ptr 对象。所以在执行原始对象的构造函数时,__weak_this_
属性尚未设置,当然不能用 shared_from_this。 - 情况 2:创建的对象必须由 shared_ptr 管理,shared_from_this() 才能生效。这是因为,只有在 shared_ptr 里面才会设置
__weak_this_
。 - 情况 3:对应类必须 public 继承 std::enable_shared_from_this。因为只有 public 继承,才能正确匹配到对应的
__enable_weak_this
,从而设置__weak_this_
。