责任链模式在C++中通过抽象基类RequestHandler统一接口,各处理器持有基类指针指向下一节点,避免依赖具体类型;使用std::unique_ptr管理所有权,基类handle()统一空检查并控制链式调用,子类专注实现do_handle()返回布尔值决定是否继续。

责任链模式在C++里怎么组织处理器类
核心是让每个处理器持有下一个处理器的指针,且不依赖具体类型——用抽象基类统一接口。别直接用 std::shared_ptr 包裹具体子类,否则破坏开闭原则;要用基类指针或智能指针指向抽象接口。
典型错误是把 next_handler 设为具体类型(比如 AuthHandler*),导致后续加新处理器(如 RateLimitHandler)时必须改前面所有类的成员声明。
- 定义纯虚基类
RequestHandler,含虚析构函数和虚函数handle() - 每个具体处理器(如
AuthHandler、ValidationHandler)继承它,并在构造时接收std::unique_ptr<requesthandler></requesthandler>或RequestHandler*作为下一环 - 链的起点由用户手动拼接:
auto chain = std::make_unique<authhandler>(std::make_unique<validationhandler>(std::make_unique<logginghandler>(nullptr)))</logginghandler></validationhandler></authhandler>
handle() 函数里什么时候调用 next_handler
不是“处理完就一定往下传”,而是由业务逻辑决定是否继续。比如认证失败就直接返回 false,不调用 next_handler->handle();校验通过才放行。
常见坑是忘了判空:如果 next_handler 是 nullptr(链尾),还硬调用会崩溃。别写 if (next_handler) next_handler->handle(req); 这种冗余判断——应该在基类 handle() 里统一做空检查,子类只专注本职逻辑。
立即学习“C++免费学习笔记(深入)”;
- 基类
handle()做两件事:1)调用纯虚do_handle();2)若do_handle()返回 true 且next_handler非空,再递归调用next_handler->handle() -
do_handle()由子类实现,返回true表示“已处理且允许继续”,false表示“终止链” - 这样避免每个子类重复写空指针检查,也防止漏掉链尾判断
如何避免循环引用和内存泄漏
用 std::unique_ptr 管理链式所有权最安全,但要注意:如果某个处理器内部又持有对链中上游节点的引用(比如回调里要访问 AuthHandler 的 token cache),就会形成循环引用——这时得换用 std::weak_ptr。
另一个高频问题是 handler 被提前析构:比如你在某处把 chain 智能指针赋值给局部变量,之后没保存好,导致整个链被销毁。责任链必须有明确的所有者(通常是 service 类或 main 函数里的长生命周期变量)。
- 链的构建必须一次性完成,不要中途用
reset()替换中间节点——这会断链 - 禁止在
do_handle()中捕获this到异步回调里,除非用weak_ptr包装 - 调试时可加日志输出每个 handler 的地址,确认析构顺序是否符合预期(应从尾到头)
要不要支持运行时动态插入处理器
静态链(编译期确定)足够多数场景,比如 HTTP 请求处理链固定为 auth → validate → route → execute。真需要动态插拔(比如插件系统),就得放弃 unique_ptr,改用 shared_ptr + 弱引用管理,但代价是性能下降和生命周期更难控。
容易被忽略的是线程安全:动态增删链节点时,若多个线程同时调用 handle(),可能读到半截断裂的链。除非你用读写锁或原子指针(C++20 std::atomic<:shared_ptr>></:shared_ptr>),否则默认按静态链设计更稳妥。
- 动态链需暴露
set_next()和get_next()接口,但基类不能暴露next_handler成员为 public - 插入新节点时,原
next_handler要先 detach 再 attach 到新节点后,否则丢失下游 - 如果只是偶尔开关某个 handler(如灰度开关),比动态插入更轻量的做法是让
do_handle()根据配置返回 false 直接中断
do_handle() 里,但语义必须一致。有人用返回枚举(CONTINUE/STOP/ERROR),有人用布尔值,只要团队约定清楚就行。别为了“灵活”在每层加一堆条件分支,责任链的价值恰恰在于把判断权下放到各环节。


















