不能直接用 raw 指针管理 session,因其无生命周期语义,易导致 double free、use-after-free 或静默连接中断;必须用带自定义删除器的 std::shared_ptr,并配合 std::weak_ptr 破解循环引用,回调中需捕获 shared_ptr 确保对象存活。

为什么不能直接用 raw 指针管理 Session
裸指针 Session* 本身不带生命周期语义,一旦 delete 忘了、重复 delete、或提前释放底层 socket,就会触发 double free、use-after-free 或连接中断静默失败。网络 Session 的典型生命周期由 I/O 就绪(如 epoll/kqueue 回调)、超时器、业务逻辑多处触发,靠人工 new/delete 几乎必然出错。
std::shared_ptr 是起点,但必须配合自定义删除器
Session 通常持有 socket fd、buffer、定时器句柄等需显式释放的资源,仅靠 shared_ptr 默认析构无法关闭 fd 或 cancel timer。必须传入自定义删除器:
struct SessionDeleter {
void operator()(Session* s) const {
if (s) {
::close(s->sock_fd); // 确保 fd 关闭
if (s->timer_id) cancel_timer(s->timer_id);
delete s;
}
}
};
using SessionPtr = std::shared_ptr<session>;
SessionPtr sp(new Session(fd), SessionDeleter{});
</session>
- 删除器必须是可调用对象(函数对象、lambda 或函数指针),不能是普通函数 —— 否则
shared_ptr无法捕获其地址 - 避免在删除器里抛异常:C++ 标准规定删除器异常会导致
std::terminate - 不要把
shared_ptr存进全局容器后就丢弃所有引用 —— 这会延迟资源释放,导致 fd 耗尽
如何避免循环引用导致 Session 泄漏
Session 常需持有 shared_ptr<eventloop></eventloop> 来 post 任务,而 EventLoop 又可能存着 shared_ptr<session></session>(如 pending connections 列表),形成循环引用。解决方式只有一种:在「被持有方」改用 std::weak_ptr:
批量分析录音转写,输出多维度拓客报告。触发词:录音分析、总结、音频总结、拜访记录总结。当用户提及「分析录音」「看看录音数据」「最近的录音」「通话记录」且意图为批量统计/分析时触发。仅出现「录音」或「拜访」时需结合上下文,若仅查看单条详情则不触发。
class Session {
std::weak_ptr<eventloop> loop_; // 不增加引用计数
public:
void on_read() {
auto locked = loop_.lock(); // 成功则返回 shared_ptr,否则为空
if (locked) locked->post([this_ptr = shared_from_this()] {
// 安全访问 this_ptr
});
}
};
</eventloop>
-
shared_from_this()要求类继承std::enable_shared_from_this<session></session>,且只能在对象已被shared_ptr管理后调用 - 不要在构造函数里调用
shared_from_this()—— 此时对象尚未被任何shared_ptr持有,会抛std::bad_weak_ptr -
weak_ptr::lock()返回空shared_ptr表示 EventLoop 已销毁,此时应立即清理 Session 自身
异步操作中传递 Session 的安全姿势
在回调(如 libuv 的 uv_read_cb、asio 的 async_read handler)中,必须确保 Session 在回调执行期间仍存活。常见错误是仅传裸指针或引用:
// ❌ 危险:cb 执行时 Session 可能已被析构
uv_tcp_t* handle = ...;
handle->data = session_ptr.get(); // 丢失所有权信息
<p>// ✅ 正确:把 shared_ptr 捕获进 lambda 或绑定到 handler
session_ptr->socket.async_read_some(
boost::asio::buffer(buf),
[session_ptr](const boost::system::error_code& ec, size_t n) {
if (!ec) session_ptr->on_data(buf.data(), n);
}
);
</p>
- asio 的 handler 自动延长
session_ptr生命周期,直到 handler 执行完毕 - libuv 等 C 接口库无法自动管理,必须手动把
shared_ptr存为 handle->data 的 wrapper 结构体,或使用std::shared_ptr<void></void>+reinterpret_cast(需谨慎) - 别在回调里直接用
this—— 如果该对象不是由shared_ptr构造的,shared_from_this()会崩溃
真正棘手的从来不是怎么写 shared_ptr,而是判断哪里该用 weak_ptr、哪些回调必须延长生命周期、以及自定义删除器里要不要加日志或断言 —— 这些细节一漏,问题往往在线上压测时才爆发。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










