boost::enable_shared_from_this分析

本文详细解释了boost::enable_shared_from_this的作用,如何使用它来管理多个shared_ptr共享同一个对象,并避免多次删除的风险。通过实例展示了在特定场景下其带来的便利。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

为什么需要boost::enable_shared_from_this? 

C++代码  
  1. class cat{};  
  2.   
  3. shared_ptr<cat> p(new cat);  
  4. shared_ptr<cat> p2(p);  
  5.   
  6.   
  7.   
  8. class cat:public boost::enable_shared_frome_this{};  
  9.   
  10. shared_ptr<cat> p(new cat);  
  11. shared_ptr<cat> p2=p->shared_from_this();  


这两段代码看似没什么区别。但是如果你在程序里,比如在cat中的成员方法中,有的只是cat的raw pointer,但你依然想利用shared_ptr的自动delete特性,又不想自己防止由于存在多个shared_ptr<cat>对同一个cat对象delete多次的危险。即多个shared_ptr<cat>共用同一个计数器。这时候,boost::enable_shared_frome_this就变得很方便了。 

C++代码  
  1. struct A;  
  2.   
  3. void f(shared_ptr<A> const&) {...}  
  4.   
  5. struct A: public enable_shared_from_this<A>  
  6. {  
  7.     void a_f()  
  8.     {  
  9.         // you need to call f() here passing itself as a parameter  
  10.         f(shared_from_this());  
  11.     }  
  12. };  
  13.   
  14. shared_ptr<A> a_ptr(new A());  
  15. a_ptr->a_f(); // in inner call to f() will be used temporary shared_ptr<A>   
  16. // that uses (shares) the same reference counter as a_ptr   


boost::enable_shared_from_this原理 

其实继承了boost::enable_shared_from_this的类,真正的magic是发生在创建它的第一个shared_ptr实例时。shared_ptr会反调boost::enable_shared_from_this的_internal_accept_owner方法。 
C++代码  
  1. template<class T> class enable_shared_from_this  
  2. {  
  3. protected:  
  4.   
  5.     enable_shared_from_this()  
  6.     {  
  7.     }  
  8.   
  9.     enable_shared_from_this(enable_shared_from_this const &)  
  10.     {  
  11.     }  
  12.   
  13.     enable_shared_from_this & operator=(enable_shared_from_this const &)  
  14.     {  
  15.         return *this;  
  16.     }  
  17.   
  18.     ~enable_shared_from_this()  
  19.     {  
  20.     }  
  21.   
  22. public:  
  23.   
  24.     shared_ptr<T> shared_from_this()  
  25.     {  
  26.         shared_ptr<T> p( weak_this_ );  
  27.         BOOST_ASSERT( p.get() == this );  
  28.         return p;  
  29.     }  
  30.   
  31.     shared_ptr<T const> shared_from_this() const  
  32.     {  
  33.         shared_ptr<T const> p( weak_this_ );  
  34.         BOOST_ASSERT( p.get() == this );  
  35.         return p;  
  36.     }  
  37.   
  38. public// actually private, but avoids compiler template friendship issues  
  39.   
  40.     // Note: invoked automatically by shared_ptr; do not call  
  41.     template<class X, class Y> void _internal_accept_owner( shared_ptr<X> const * ppx, Y * py ) const  
  42.     {  
  43.         if( weak_this_.expired() )  
  44.         {  
  45.             weak_this_ = shared_ptr<T>( *ppx, py );  
  46.         }  
  47.     }  
  48.   
  49. private:  
  50.   
  51.     mutable weak_ptr<T> weak_this_;  
  52. };  
  53.   
  54. //从源码中,可以看出,重点不在于初始化的时候。而是在于创建第一个该对象的shared_ptr的时候:  
  55.   
  56. template<class Y>  
  57. explicit shared_ptr( Y * p ): px( p ), pn( p ) // Y must be complete  
  58. {  
  59.     boost::detail::sp_enable_shared_from_this( this, p, p );  
  60. }  
  61.   
  62. templateclass X, class Y, class T > inline void sp_enable_shared_from_this( boost::shared_ptr<X> const * ppx, Y const * py, boost::enable_shared_from_this< T > const * pe )  
  63. {  
  64.     if( pe != 0 )  
  65.     {  
  66.         pe->_internal_accept_owner( ppx, const_cast< Y* >( py ) );  
  67.     }  
  68. }  
<think>我们正在实现一个继承自std::enable_shared_from_this的Fiber类。Fiber(纤程)是一种轻量级的线程,由用户自己进行调度。在C++中,我们通常使用第三方库(如Boost.Fiber)来实现纤程,但这里我们尝试自己实现一个简单的版本。由于Fiber对象需要被多个地方共享(例如在调度器中、在等待的纤程中等),因此我们使用std::shared_ptr来管理其生命周期。为了让Fiber对象能够安全地获取自身的shared_ptr,我们让它继承自std::enable_shared_from_this。注意:使用std::enable_shared_from_this时,必须确保对象已经被std::shared_ptr管理。也就是说,不能直接在栈上创建该对象,而应该通过std::make_shared等函数创建。以下是一个简单的Fiber类的实现框架:```cpp#include<memory>#include<functional>#include<coroutine>//假设使用C++20的协程(这里仅作为示例,实际上Fiber可以使用协程,但也可以用其他方式实现)//我们使用C++20的协程特性来实现纤程的执行体(需要编译器支持)//注意:这里仅是一个简化示例,实际实现需要考虑上下文切换、调度等复杂问题classFiber:publicstd::enable_shared_from_this<Fiber>{public://纤程的状态enumclassState{Ready,Running,Suspended,Terminated};//构造函数,传入一个可调用对象(纤程要执行的函数)template<typenameFn>explicitFiber(Fn&&fn):func_(std::forward<Fn>(fn)){}//启动纤程voidstart(){state_=State::Running;//这里我们假设使用协程的resume来启动(简化)//实际中可能需要保存/恢复上下文func_();}//暂停当前纤程(让出执行权)voidyield(){state_=State::Suspended;//实际中需要切换到调度器上下文}//获取当前纤程的shared_ptrstd::shared_ptr<Fiber>get_shared(){returnshared_from_this();//从enable_shared_from_this继承而来}Statestate()const{returnstate_;}private:std::function<void()>func_;Statestate_=State::Ready;};//使用示例intmain(){//创建纤程,注意必须使用shared_ptr管理autofiber=std::make_shared<Fiber>([]{std::cout<<"Fiberstarted\n";//模拟工作//...//让出执行权//注意:在纤程内部如何获取自己的shared_ptr?可以通过全局调度器或捕获//但是,在纤程函数内部,我们不能直接使用shared_from_this(),因为this可能不是Fiber对象(如果lambda捕获了this,但这里没有捕获)//因此,我们需要设计使得纤程函数能够访问到它所属的Fiber对象。});//启动纤程fiber->start();//...其他代码}```然而,上面的示例中有一个问题:在纤程执行的函数(lambda)内部,我们无法直接调用yield(),因为yield()是Fiber的成员函数,而lambda没有捕获Fiber对象。为了解决这个问题,我们可以在构造Fiber时,将自身的shared_ptr传递给执行函数。修改如下:```cppclassFiber:publicstd::enable_shared_from_this<Fiber>{public://...状态枚举同上template<typenameFn>explicitFiber(Fn&&fn):func_(std::forward<Fn>(fn)){}voidstart(){state_=State::Running;//将当前Fiber的shared_ptr作为参数传递给执行函数func_(shared_from_this());}//...其他同上};//使用示例intmain(){autofiber=std::make_shared<Fiber>([](std::shared_ptr<Fiber>self){std::cout<<"Fiberstarted\n";//现在可以通过self来调用yieldself->yield();});fiber->start();}```注意:在构造函数中,我们不能调用shared_from_this(),因为此时对象还没有被shared_ptr管理(在make_shared之后才被管理)。所以我们在start()中调用shared_from_this(),并传递给执行函数。但是,上面的代码在构造Fiber时,我们传递的lambda捕获了一个shared_ptr(即self),这会导致循环引用:fiber对象持有lambda(通过func_),而lambda又持有fiber的shared_ptr(self)。这样即使外部不再持有fiber的shared_ptr,fiber也不会被释放。因此,我们需要小心地设计。为了避免循环引用,我们可以将执行函数中的self保存为weak_ptr,或者确保在不需要的时候打破循环。另外,我们可以考虑在Fiber执行结束时,将func_置空,从而打破循环。另一种设计是不传递shared_ptr给执行函数,而是提供一个全局的调度器接口,让执行函数通过调度器接口来让出,而调度器知道当前的Fiber。这样执行函数就不需要持有Fiber的shared_ptr。由于Fiber的实现是一个复杂的话题,这里我们只关注如何使用std::enable_shared_from_this。我们选择传递weak_ptr给执行函数,以避免循环引用:```cppclassFiber:publicstd::enable_shared_from_this<Fiber>{public:template<typenameFn>explicitFiber(Fn&&fn):func_(std::forward<Fn>(fn)){}voidstart(){state_=State::Running;//传递weak_ptrfunc_(weak_from_this());}voidyield(){state_=State::Suspended;//...}//提供一个从weak_ptr升级并调用yield的静态方法,方便在lambda中使用staticvoidyield(conststd::weak_ptr<Fiber>&weak_self){if(autoself=weak_self.lock()){self->yield();}}private:std::function<void(std::weak_ptr<Fiber>)>func_;Statestate_=State::Ready;};//使用示例intmain(){autofiber=std::make_shared<Fiber>([](std::weak_ptr<Fiber>weak_self){std::cout<<"Fiberstarted\n";Fiber::yield(weak_self);//通过静态方法让出});fiber->start();}```这样,执行函数中持有的是weak_ptr,不会增加引用计数,从而避免了循环引用。总结:1.Fiber类继承std::enable_shared_from_this,以便在成员函数中获取自身的shared_ptr。2.在Fiber的执行函数中,我们传递weak_ptr,以避免循环引用。3.提供静态工具函数来通过weak_ptr调用成员函数(如yield)。注意:以上代码仅为示例,并未实现真正的纤程上下文切换。实际中,上下文切换需要使用平台相关的API(如Windows的FiberAPI,或ucontext,或使用汇编代码保存寄存器等)或者使用C++20协程(协程有独立的栈,但需要编译器支持)。另外,关于std::enable_shared_from_this的使用,必须注意对象必须由shared_ptr管理,且在调用shared_from_this()之前,shared_ptr必须已经存在。否则会抛出std::bad_weak_ptr异常。引用说明:-在实现中,我们使用了std::enable_shared_from_this,其原理是在对象内部存储一个weak_ptr,该weak_ptr在shared_ptr构造时被初始化[^2][^3]。-因此,直接构造Fiber对象(不使用shared_ptr)或者在没有shared_ptr管理的情况下调用shared_from_this(),都会导致错误。最后,关于Fiber类的使用,我们还需要注意线程安全等问题,这里不再展开。§§相关问题§§1.如何避免使用std::enable_shared_from_this时的循环引用?2.在C++中实现纤程有哪些常用的方法?3.如何使用C++20协程来实现纤程?4.为什么在构造函数中不能调用shared_from_this()?</think>### 实现继承自 std::enable_shared_from_this 的 Fiber 类 在 C++ 中实现纤程(Fiber)并继承 `std::enable_shared_from_this` 需要解决两个核心问题: 1. **纤程的生命周期管理**(通过共享指针实现) 2. **安全的自身指针获取**(通过 `shared_from_this()` 实现) #### 基础实现代码 ```cpp #include <memory> #include <functional> class Fiber : public std::enable_shared_from_this<Fiber> { public: using Ptr = std::shared_ptr<Fiber>; // 构造函数必须私有化,强制使用create方法创建 template<typename Callable> static Ptr create(Callable&& func) { // 使用make_shared初始化enable_shared_from_this内部的weak_ptr auto fiber = std::make_shared<Fiber>(std::forward<Callable>(func)); fiber->init(); // 初始化后执行 return fiber; } void resume() { // 通过共享指针安全访问 if (auto self = weak_self_.lock()) { func_(self); } } private: template<typename Callable> Fiber(Callable&& func) : func_(std::forward<Callable>(func)) {} void init() { weak_self_ = weak_from_this(); // 初始化弱引用 } std::function<void(Ptr)> func_; std::weak_ptr<Fiber> weak_self_; // 关键:避免循环引用 }; ``` #### 关键设计要点 1. **安全创建模式** - 构造函数设为私有 - 通过静态工厂方法 `create()` 创建对象 - 确保对象始终由 `shared_ptr` 管理(满足 `enable_shared_from_this` 的前提条件)[^2][^3] 2. **循环引用处理** - 存储 `weak_ptr` 而非 `shared_ptr` 作为成员变量 - 在 `init()` 中初始化 `weak_self_ = weak_from_this()` - 使用前通过 `lock()` 升级为临时 `shared_ptr` 3. **执行流程** ```cpp auto fiber = Fiber::create([](Fiber::Ptr self) { std::cout << "Fiber running!"; // 可通过self访问纤程成员 }); fiber->resume(); // 执行纤程 ``` #### 典型使用场景 1. **纤程调度器** ```cpp class Scheduler { std::queue<Fiber::Ptr> ready_queue; void schedule(Fiber::Ptr fiber) { ready_queue.push(fiber); } void run() { while (!ready_queue.empty()) { auto fiber = ready_queue.front(); ready_queue.pop(); fiber->resume(); } } }; ``` 2. **纤程间通信** ```cpp void fiber_a(Fiber::Ptr self) { auto fiber_b = Fiber::create(...); // 将自身weak_ptr传递给其他纤程 fiber_b->set_caller(self->weak_from_this()); } ``` #### 注意事项 1. **禁止栈上对象** ```cpp Fiber fiber(...); // 错误!违反enable_shared_from_this约定 fiber.resume(); // 触发未定义行为 ``` 2. **首次指针获取时机** - 必须在 `std::make_shared` 完成后才能调用 `shared_from_this()` - 在构造函数中调用会导致崩溃(因内部 `weak_ptr` 未初始化)[^3] 3. **线程安全性** - `shared_from_this()` 本身线程安全 - 但纤程状态需额外同步机制 > **设计意义**:通过 `enable_shared_from_this` 实现纤程的自主指针管理,使得纤程能安全地传递自身引用或加入调度队列,同时避免手动内存管理错误[^2][^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值