You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 12:18:23