C++中shared_from_this()与getptr()的实际使用场景
Best::getptr() 的设计意图与使用场景 首先明确核心:getptr() 本质是对 shared_from_this() 的安全封装,它的作用从来不是创建新实例,而是解决一个非常具体的问题:当你在类的成员函数内部,仅持有当前对象的this指针/引用时,安全获取到和外部已有shared_ptr共享同一块控制块的智能指针,避免生命周期错误。
为什么不直接用this构造shared_ptr?
如果在成员函数里直接写std::shared_ptr<T>(this),会生成一个全新的独立控制块,和外部原本持有当前对象的shared_ptr完全不共享引用计数,最终会导致同一个对象被析构两次,触发double free崩溃,属于典型的未定义行为。std::enable_shared_from_this提供的shared_from_this()就是为了解决这个问题:它会从对象内部存储的弱引用,生成和现有shared_ptr共享控制块的新智能指针,只会让引用计数正常+1,不会重复创建控制块。
为什么要额外封装一层getptr()?
工业界普遍采用的Best实现中,封装getptr()的价值有两点:
- 从接口层面保证调用安全:类把构造函数设为私有,仅能通过静态
create()工厂方法返回托管在shared_ptr上的实例,从根源上杜绝了栈上分配对象、直接new生成非托管对象的可能——这类非托管对象调用shared_from_this()会直接触发未定义行为,而Best的设计让这类错误代码根本无法通过编译。 - 降低使用成本:不需要让使用者记住必须继承
enable_shared_from_this、必须正确调用shared_from_this()的各种约束,只要调用getptr()就能拿到合法的共享智能指针。
实际开发中的典型使用场景
你觉得这个方法没用,是因为测试代码没有涉及需要共享所有权、延长对象生命周期的场景,这类场景在异步、事件驱动的代码中极其常见:
- 异步IO/定时器回调:比如网络连接、文件IO的异步操作,需要在回调触发时保证当前对象还存活,必须在注册回调时通过
getptr()拿到持有当前对象的shared_ptr存到回调捕获列表里,把对象生命周期延长到异步操作完成,避免野指针。 - 观察者/事件总线注册:当对象需要把自己注册为某个事件的监听者时,注册接口通常要求传入
shared_ptr来统一管理监听者生命周期,这时候在成员函数内部注册自身就不能传裸this,必须传getptr()返回的共享指针。 - 跨模块传参:当你需要把当前对象传给其他模块,且对方接口要求传入
shared_ptr<T>来明确共享所有权语义时,用getptr()生成的指针是唯一安全的选择。
代码演示
错误写法(直接用this构造shared_ptr)
#include <iostream> #include <memory> #include <functional> // 错误实现:会触发double free class BadConnection { public: void async_read(std::function<void()>& io_task) { // 错误:从this构造独立的shared_ptr,生成新控制块 auto self = std::shared_ptr<BadConnection>(this); io_task = [self]() { std::cout << "handle read\n"; }; } }; int main() { std::function<void()> task; auto conn = std::make_shared<BadConnection>(); conn->async_read(task); // 作用域结束conn引用计数归零,第一次析构对象 task(); // task执行完self引用计数归零,第二次析构同一个对象,直接崩溃 return 0; }
正确写法(用getptr()获取共享指针)
#include <iostream> #include <memory> #include <functional> struct GoodConnection : std::enable_shared_from_this<GoodConnection> { // 封装安全的获取方法 std::shared_ptr<GoodConnection> getptr() { return shared_from_this(); } // 工厂方法,保证所有实例都被shared_ptr托管 static std::shared_ptr<GoodConnection> create() { return std::shared_ptr<GoodConnection>(new GoodConnection()); } void async_read(std::function<void()>& io_task) { // 拿到共享所有权的指针,引用计数+1 auto self = getptr(); io_task = [self]() { std::cout << "handle read, connection alive\n"; // 回调执行完self销毁,引用计数-1 }; } private: // 私有构造,禁止生成非托管对象 GoodConnection() = default; }; int main() { std::function<void()> task; { auto conn = GoodConnection::create(); conn->async_read(task); // 这里出作用域,conn被销毁,但因为task里持有self,引用计数还是1,对象不会析构 } std::cout << "left inner scope, connection still alive\n"; task(); // 回调执行完,引用计数归零,对象安全析构 return 0; }
运行这段正确代码可以看到,就算外部持有conn的shared_ptr已经出作用域销毁,对象依然会活到异步任务执行完成,不会出现野指针或重复析构问题。
内容的提问来源于stack exchange,提问作者John
相关产品推荐
相关产品推荐

