责任链模式在C++中可用裸指针实现轻量级链式调用,BaseHandler无虚函数和成员变量以避免多态开销,next_手动管理生命周期,需循环调用防栈溢出,禁用shared_ptr以保性能。

责任链模式在 C++ 里不用虚函数和动态分配也能跑起来,但必须手动管理生命周期;用裸指针链式调用最轻量,前提是别让链表断在析构时。
如何定义抽象处理类(不带虚析构的 BaseHandler)
轻量级的核心是避免运行时多态开销,所以 BaseHandler 不需要虚函数表 —— 只提供统一接口签名,靠模板或显式类型转换来衔接。但要注意:如果后续想用 std::unique_ptr<basehandler></basehandler> 管理,就必须加虚析构;裸指针链则完全绕过这点。
- 推荐只声明纯接口函数,如
handle(Request&),返回bool表示是否终结链路 - 不放虚析构,也不放成员变量,保持结构体级轻量(sizeof == 1 或 0,取决于编译器)
- 若需共享状态,用外部传入的引用或全局/静态对象,而非在基类里塞数据成员
用裸指针实现 next 指针链(next_ 成员必须手动置空)
链表节点之间用 BaseHandler* 连接是最省的,没有智能指针的引用计数、没有 std::optional 的空间开销。但风险也集中在这里:析构顺序错乱会导致悬空指针。
- 每个派生类持有一个
BaseHandler* next_ = nullptr成员 - 构造时由用户显式调用
setNext(),禁止在构造函数里自动连链(易引发未定义行为) - 析构前必须确保
next_所指对象生命周期长于当前对象,否则调用next_->handle()就崩 - 示例:
struct AuthHandler : BaseHandler { BaseHandler* next_ = nullptr; void setNext(BaseHandler* h) { next_ = h; } bool handle(Request& r) override { if (!r.token) return false; return next_ ? next_->handle(r) : true; } };
链式调用时怎么避免递归爆栈(尤其嵌套深时)
责任链天然有递归倾向,C++ 默认栈空间有限(Linux 通常 8MB,Windows 更小),百层以上链就可能触发 stack overflow。这不是设计缺陷,而是裸指针链的物理限制。
立即学习“C++免费学习笔记(深入)”;
- 不要在
handle()里直接递归调用next_->handle(),改用循环跳转 - 把“当前处理器”指针存在局部变量里,while 循环中更新:
BaseHandler* curr = head; while (curr && !curr->handle(req)) { curr = curr->next_; } - 若业务逻辑本身必须递归(比如树形权限检查),那就得换结构——用显式栈容器(
std::stack<basehandler></basehandler>)替代函数调用栈 - Clang/GCC 可加
-Wstack-protector提醒潜在风险,但不会自动优化掉这种递归
为什么不能直接用 std::shared_ptr 替代裸指针
能用,但违背“轻量级”初衷:std::shared_ptr 每次拷贝要原子增减引用计数,每次析构要查计数并可能 delete;而裸指针链只要求开发者明确谁 new、谁 delete、谁先死谁后死。
- shared_ptr 会让每个节点额外占用 16 字节(x64 上控制块指针 + 引用计数)
- 循环引用风险真实存在:A→B→A 链会锁死内存,裸指针则根本不会尝试管理所有权
- 如果链是静态生命周期(如全局 handler 实例),shared_ptr 完全多余;只有动态创建+销毁场景才值得权衡
- 真要自动管理,
std::unique_ptr更合适,但它要求链尾必须是唯一所有者,且无法在运行时重连
裸指针链的复杂点不在写法,而在对象生存期图谱 —— 画不出这张图,代码跑三天后必 crash。别指望编译器帮你检查 next_ 是否 dangling,它连 warning 都不会给。


















