常州本地招聘网站,快速网站建设多少钱,wordpress分类图标列表,网站备案是自己可以做吗灵魂拷问std::enable_shared_from_this#xff0c;揭秘实现原理 引言 在C编程中#xff0c;使用智能指针是一种安全管理对象生命周期的方式。std::shared_ptr是一种允许多个指针共享对象所有权的智能指针。然而#xff0c;当一个对象需要获取对自身的shared_ptr时#xff0… 灵魂拷问std::enable_shared_from_this揭秘实现原理 引言 在C编程中使用智能指针是一种安全管理对象生命周期的方式。std::shared_ptr是一种允许多个指针共享对象所有权的智能指针。然而当一个对象需要获取对自身的shared_ptr时传统的方法可能导致未定义行为。为了解决这个问题C引入了std::enable_shared_from_this类本文将深入探讨其基础知识、使用案例以及内部实现。 首先抛出一些问题 enable_shared_from_this通常被用来做什么什么场景被使用enable_shared_from_this解决了什么问题enable_shared_from_this的public、private继承为何需要特别注意不然会引发什么问题enable_shared_from_this内部的实现细节你知道多少呢 std::shared_ptr基础知识 首先我们回顾一下std::shared_ptr的基础知识。它是一种智能指针通过共享控制块的方式安全地管理对象的生命周期。多个 shared_ptr 实例通过共享的 控制块 结构来控制对象的生命周期。 当使用原始指针构造或初始化 shared_ptr 时会创建一个新的控制块。为了确保对象仅由一个共享控制块管理对对象的任何额外的 shared_ptr 实例必须通过复制已经存在的指向该对象的 shared_ptr 来产生例如 void run() {auto p{new int(12)}; //p 是 int*std::shared_ptrint sp1{p}; auto sp2{sp1}; //OK sp2 与 sp1 共享控制块
} 使用原始指针初始化已经由 shared_ptr 管理的对象会创建另一个控制块来管理该对象这将导致未定义的行为。例如 void bad_run() {auto p{new int(12)}; std::shared_ptrint sp1{p};std::shared_ptrint sp2{p}; //! 未定义行为
} 从一个原始指针实例化多个 shared_ptr 是一种严重后果的编程失误。在可能的情况下尽量使用 std::make_shared或 std::allocate_shared来减少发生此错误的可能性。例如 auto sp1 std::make_sharedint();
std::shared_ptrint sp2{sp1.get()}; // 这会发生什么 然而有些情况下shared_ptr 托管的对象需要获得一个指向自己的 shared_ptr。但首先像下面这样尝试使用 this 指针创建 shared_ptr 不会起作用原因如上所述 struct Foo {std::shared_ptrFoo getSelfPtr() {return std::shared_ptrFoo(this); }//...
};void run() {auto sp1 std::make_sharedFoo();auto sp2 sp1-getSelfPtr(); //!! 未定义行为/*sp1 和 sp2 有两个不同的控制块 管理相同的 Foo*/
} 这就是 std::enable_shared_from_thisT 发挥作用的地方。公开继承 std::enable_shared_from_this 的类可以通过调用方法 shared_from_this() 获得指向自己的 shared_ptr。以下是它的一个基本示例 #include memorystruct Foo : std::enable_shared_from_thisFoo {std::shared_ptrFoo getSelfPtr() {return shared_from_this(); }//...
};void run() {auto sp1 std::make_sharedFoo();auto sp2 sp1-getSelfPtr(); // OK/*sp1 和 sp2 共享相同的控制块正确管理 Foo*/
} enable_shared_from_this类初识 std::enable_shared_from_this 的实现是一个类它只包含一个 weak_ptr 字段通常称为 _M_weak_this这里面有很多细节看看你知道吗 _M_weak_this成员是在何时被初始化的怎么初始化的friend class声明在这里起到了什么作用 template typename _Tp
class enable_shared_from_this
{
public:shared_ptr_Tpshared_from_this(){ return shared_ptr_Tp(this-_M_weak_this); }shared_ptrconst _Tpshared_from_this() const{ return shared_ptrconst _Tp(this-_M_weak_this); }
private:templatetypename _Tp1void_M_weak_assign(_Tp1* __p, const __shared_count __n) const noexcept{ _M_weak_this._M_assign(__p, __n); }// Found by ADL when this is an associated class.friend const enable_shared_from_this*__enable_shared_from_this_base(const __shared_count,const enable_shared_from_this* __p){ return __p; }templatetypename, _Lock_policyfriend class __shared_ptr;mutable weak_ptr_Tp _M_weak_this;
}; 这里的friend声明特别重要这样的话__shared_ptr便可以访问这个类的所有private成员因此__shared_ptr便可以访问_M_weak_assign、__enable_shared_from_this_base、_M_weak_this。 至于_M_weak_this 在什么地方被初始化见下方内容。 实现原理 假设此时Foo继承了enable_shared_from_this当我们编写这样一段代码到底放生了什么 于此同时我们要解决第一个问题为何enable_shared_from_this需要public继承私有继承会发生什么 auto sp1 std::make_sharedFoo(); make_shared会调用allocate_shared随后调用shared_ptr的构造函数再调用__shared_ptr的构造函数此时我们可以看到会调用_M_enable_shared_from_this_with它是一个模版函数此时会使用ADL从enable_shared_from_this类中查找enable_shared_from_this。 templatetypename _Alloc, typename... _Args
__shared_ptr(_Sp_alloc_shared_tag_Alloc __tag, _Args... __args)
: _M_ptr(), _M_refcount(_M_ptr, __tag, std::forward_Args(__args)...)
{ _M_enable_shared_from_this_with(_M_ptr); } 查找到了则拿到base通过访问私有函数_M_weak_assign来初始化_M_weak_this。如果没查找到则不会初始化_M_weak_this。 当我们通过public继承enable_shared_from_this时可以正常的初始化_M_weak_this而如果是private继承这里会走空实现_M_weak_this未被初始化。 templatetypename _Yp, typename _Yp2 typename remove_cv_Yp::type
typename enable_if__has_esft_base_Yp2::value::type
_M_enable_shared_from_this_with(_Yp* __p) noexcept
{if (auto __base __enable_shared_from_this_base(_M_refcount, __p))__base-_M_weak_assign(const_cast_Yp2*(__p), _M_refcount);
}templatetypename _Yp, typename _Yp2 typename remove_cv_Yp::type
typename enable_if!__has_esft_base_Yp2::value::type
_M_enable_shared_from_this_with(_Yp*) noexcept
{ } make_shared看起来一切正常那为啥我还要强调上面这些逻辑呢往下看。 当我们使用了shared_from_this()在private会报异常。 std::shared_ptrFoo getSelfPtr() { return shared_from_this(); } shared_from_this()会基于已有的_M_weak_this构造shared_ptr_M_refcount是一个__shared_count对象。 templatetypename _Yp, typename _Compatible_Yp
explicit __shared_ptr(const __weak_ptr_Yp, _Lp __r)
: _M_refcount(__r._M_refcount) // may throw
{// ...
} 这里会使用_M_weak_this的_M_refcount去初始化__shared_count而当私有继承时_M_weak_this并没有被初始化于是引发了bad_weak_ptr异常。 template_Lock_policy _Lpinline__shared_count_Lp::__shared_count(const __weak_count_Lp __r): _M_pi(__r._M_pi){if (_M_pi ! nullptr)
_M_pi-_M_add_ref_lock();else
__throw_bad_weak_ptr();} 更多干货文章发布于星球欢迎加入学习