ChainHandler基类必须声明virtual析构函数,否则多态删除时子类资源不释放导致泄漏;next_指针推荐std::unique_ptr管理;handle()返回bool适合纯转发,std::optional适合需透出结果的场景。

责任链模式在 C++ 里不用虚函数纯接口也能做,但不加智能指针或裸指针管理不当,八成会崩在析构或循环引用上。
为什么 ChainHandler 的基类必须带 virtual 析构函数
哪怕你只用 std::unique_ptr 管理后续 handler,只要存在多态删除(比如链表尾部 handler 被父类指针接管),没 virtual ~ChainHandler() = default; 就会触发未定义行为——析构只走基类、子类资源不释放,常见于 std::vector<:unique_ptr>> 清空时。
- 不加
virtual:子类的析构函数不会被调用,std::string成员或动态分配内存直接泄漏 - 加了
virtual但没写 =default 或 {}:编译器可能生成非内联析构,影响性能(不过对轻量链影响极小) - 如果确定只用 final 类型且不通过基类指针销毁,可省略,但链式结构天然需要运行时多态,不推荐绕过
next_ 指针该用 std::unique_ptr 还是 raw pointer
用 std::unique_ptr 是安全默认选择;用裸指针(ChainHandler*)仅适用于「链生命周期完全由外部控制」的场景,比如全局单例 handler 链或栈上临时链。
-
std::unique_ptr:自动管理内存,setNext()可直接 move 赋值,避免浅拷贝误传 - 裸指针:适合嵌入式或实时系统中禁止动态内存分配的场合,但必须确保
next_所指对象寿命 ≥ 当前 handler 寿命,否则handle()中调用next_->handle()就是野指针 - 别用
std::shared_ptr:容易因循环引用卡住整个链(A→B→C→A),轻量链首要目标就是零引用计数开销
handle() 返回 bool 还是 std::optional
返回 bool 表示「是否继续传递」,最轻量;返回 std::optional 表示「是否处理成功并给出结果」,更实用但增加模板依赖和拷贝成本。
- 纯转发链(如日志级别过滤):用
bool,return next_ ? next_->handle(req) : false; - 需透出处理结果(如解析请求后返回 status/code/data):用
std::optional,当前 handler 处理完直接 return,不调 next_;未处理则 returnnext_ ? next_->handle(req) : std::nullopt; - C++17 前可用
std::pair替代,但语义不如optional清晰
如何避免 handler 链构造时的悬挂指针
常见错误是栈上创建 handler 后,用裸指针连成链,然后函数返回——栈对象销毁,链中指针全变悬垂。
- 正确做法:所有 handler 实例统一由
std::make_unique创建,链头用std::unique_ptr持有 - 若需共享某个 handler(如通用鉴权 handler),用
std::shared_ptr包裹它,但只用于「被多个链引用」,不要让它成为链内部节点 - 调试技巧:在
ChainHandler构造/析构中打日志,配合 AddressSanitizer 检查 use-after-free
真正难的不是写出能跑的链,而是让每个 handler 不知道自己在第几环、不依赖链长度、也不假设 next_ 一定存在——这些约束一旦松动,测试时好好的代码,上线后并发一压就暴露竞态或析构顺序问题。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











