std::enable_shared_from_this
序言
最近在看C++很少用到的知识,学到这遇到和大家一样的问题,std::enable_shared_from_this
到底是怎么指向自己的?看别人说的云里雾里的,我来简单通俗解释下。
.
为什么要用std::enable_shared_from_this
?
看个例子
void main()
{
AA *a = new AA;
std::shared_ptr< AA > ptr1(a);
std::shared_ptr< AA > ptr2(a);
std::cout << ptr1.use_count() << " " << ptr2.use_count() << std::endl; // 1 1
return 0;
}
a是个裸指针,给ptr1和ptr2两个智能指针赋值时,智能指针是不知道裸指针被别的指向过,所以这两个的use_count()都会是1。
a被释放了两次!所以,为了避免这个情况,就可以用std::enable_shared_from_this
。
.
memory部分源码
这是我电脑上std::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<_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() {
// 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 _Yty>
friend class shared_ptr;
mutable weak_ptr<_Ty> _Wptr;
};
我想大家看到这里时都很迷惑,_Wptr
不是个弱类型智能指针吗?它也没有指向任何东西呀。
我也陷入了和大家一样的境地,但是这时,我将_Wptr
搜索看看,发现上边std::shared_ptr
的类里用到了!
template <class _Ux,
enable_if_t<conjunction_v<conditional_t<is_array_v<_Ty>, _Can_array_delete<_Ux>, _Can_scalar_delete<_Ux>>,
_SP_convertible<_Ux, _Ty>>,
int> = 0>
explicit shared_ptr(_Ux* _Px) { // construct shared_ptr object that owns _Px
if constexpr (is_array_v<_Ty>) {
_Setpd(_Px, default_delete<_Ux[]>{});
} else {
_Temporary_owner<_Ux> _Owner(_Px);
_Set_ptr_rep_and_enable_shared(_Owner._Ptr, new _Ref_count<_Ux>(_Owner._Ptr));
_Owner._Ptr = nullptr;
}
}
...
template <class _Ux>
void _Set_ptr_rep_and_enable_shared(_Ux* const _Px, _Ref_count_base* const _Rx) noexcept { // take ownership of _Px
this->_Ptr = _Px;
this->_Rep = _Rx;
if constexpr (conjunction_v<negation<is_array<_Ty>>, negation<is_volatile<_Ux>>, _Can_enable_shared<_Ux>>) {
if (_Px && _Px->_Wptr.expired()) {
_Px->_Wptr = shared_ptr<remove_cv_t<_Ux>>(*this, const_cast<remove_cv_t<_Ux>*>(_Px));
}
}
}
void _Set_ptr_rep_and_enable_shared(nullptr_t, _Ref_count_base* const _Rx) noexcept { // take ownership of nullptr
this->_Ptr = nullptr;
this->_Rep = _Rx;
}
看上边_Set_ptr_rep_and_enable_shared
这个是关键,这个函数里,_Can_enable_shared
会判断是否是继承std::enable_shared_from_this
的,是则可直接调用其的私有成员_Wptr
。
然后将传进来的这个实例,让自己的成员_Wptr
指向自己这个实例,完成了指向自己。
enable_shared_from_this
拥有代码friend class shared_ptr;
,会允许shared_ptr
任意调用。
说到这想必大家都差不多明白了
这就是通过std::shared_ptr< AA > (a);
指向的第一个实例,也自己指向了自己。
要注意的是,因为if的第二个判断_Px->_Wptr.expired()
,所以会只有第一次实例未被智能指针指向时可以使用,第二次就会正常如一个std::shared_ptr
给另一个std::shared_ptr
赋值的效果,计数器正常递增。
.
示例
给个例子会更容易接受
class AA : public std::enabled_shared_from_this< AA >
{
public:
...
std::shared_ptr< AA > GetThis()
{
return shared_from_this(); // 也可以用weak_from_this()弱类型的
}
...
}
...
AA *a = new AA;
std::shared_ptr< AA > ptr1(a); // shared_ptr新建的同时,调用了上边源码写出来的shared_ptr构造,new AA的成员_Wptr
// 指向new AA,该实例的_Wptr完成闭环指向
std::shared_ptr< AA > ptr2 = a->GetThis(); //获取的是实例的this智能指针给第二个智能指针
std::cout << ptr1.use_count() << " " << ptr2.use_count() << std::endl; // 2 2
...
.
后续可能性疑问
有些人虽然看完了,知道这个怎么用,原理是什么,可是还是有很多疑问,可以看看下面,下面是我了解之后仍然会产生的疑问,和自我解答:
为什么不用std::shared_ptr而是用的std::wear_ptr指向自己?
因为如果用std::shared_ptr指向自己的话,就会导致被管理对象持有对象自身,这会导致什么?
1.释放自身
2.释放成员std::shared_ptr
3.std::shared_ptr释放持有的自身
4.释放自身
5.释放成员std::shared_ptr
6.std::shared_ptr释放持有的自身
…
懂了吧?
那么又可能有疑问,那不指向自己,指向新的实例不就好了?
std::shared_ptr不指向自己,指向新的实例
要实现这个疑问,就有些问题,你怎么new新的呢?
首先排除在构造里new,这会导致重复构造,构造循环。
其他的诸如放函数里,放静态函数里,或者说自己可以规范一点不出现开头的问题。
都可以。
只是,如果需要避免这种情况,std::enabled_shared_from_this
就是目前知道最简便,性能开销最少,抽象最少的方法了。
(截止参考自C++11及之前的版本)