责任链模式属于行为型设计模式,目的是让多个对象都有机会处理请求,从而避免请求发送者与接收者之间的强耦合。在C++中,我们可以通过抽象接口定义统一的处理契约,再利用指针将各个处理器串成一条链。每个处理器在收到请求时自行判断能否处理,不能处理就转交给后继节点。这种写法没有第三方库依赖,头文件加上几十行实现就能用在生产环境。

一、接口抽象的设计思路
轻量级实现的第一步是抽出公共接口。我们定义一个名为 Handler 的抽象基类,里面包含一个纯虚函数 handle,以及用于设置下一个节点的 setNext 方法。由于 C++ 没有像 Java 那样的接口关键字,用纯虚类即可表达契约。为了资源管理安全,链中保存的后续节点指针建议使用 shared_ptr,而在节点内部反向引用时可用 weak_ptr 防止循环引用导致内存泄漏。
为什么强调接口抽象而不是具体类互调?假如每个处理逻辑都直接调用下一个具体类,新增一种处理器就要改旧类的代码,违反开闭原则。抽象之后,链条的组装方只依赖 Handler 类型,具体节点可随时替换或增减。下面的代码展示了基础接口与两个具体实现。
#include <iostream>
#include <memory>
#include <string>
class Handler {
public:
virtual ~Handler() = default;
virtual bool handle(const std::string& request) = 0;
void setNext(std::shared_ptr<Handler> next) {
next_ = next;
}
std::shared_ptr<Handler> getNext() const {
return next_;
}
private:
std::shared_ptr<Handler> next_;
};
class AuthHandler : public Handler {
public:
bool handle(const std::string& request) override {
if (request == "auth") {
std::cout << "AuthHandler processed" << std::endl;
return true;
}
if (auto n = getNext()) return n->handle(request);
return false;
}
};
class LogHandler : public Handler {
public:
bool handle(const std::string& request) override {
std::cout << "LogHandler logged: " << request << std::endl;
if (auto n = getNext()) return n->handle(request);
return false;
}
};
二、指针链式调用的组装方式
有了接口和节点,下一步是把它们连起来。所谓指针链式调用,是指用对象本身的指针返回来实现连续设置,比如 obj.setNext(a).setNext(b) 这样的语法。在 C++ 里只要让 setNext 返回自身引用即可。这样写出来的组装代码读起来像一条流水线,也方便在配置阶段动态拼装不同顺序的处理器。
需要注意的是,如果返回的是裸指针容易发生空指针问题,返回 shared_ptr 的引用又可能延长生命周期。实践中返回 Handler& 或 shared_ptr<Handler> 都可以,前者更轻量。下面例子演示了如何让 setNext 支持链式写法,并给出主函数中的调用示范。
#include <memory>
class ChainHandler : public Handler {
public:
// 返回自身引用以支持链式调用
ChainHandler& setNext(std::shared_ptr<Handler> next) {
Handler::setNext(next);
return *this;
}
bool handle(const std::string& request) override {
std::cout << "ChainHandler got: " << request << std::endl;
if (auto n = getNext()) return n->handle(request);
return false;
}
};
int main() {
auto auth = std::make_shared<AuthHandler>();
auto log = std::make_shared<LogHandler>();
auto chain = std::make_shared<ChainHandler>();
// 指针链式调用组装
chain->setNext(auth).setNext(log);
chain->handle("auth");
chain->handle("other");
return 0;
}
三、避免循环引用与资源释放
当责任链节点互相持有 shared_ptr 时,若某个节点又指回前面的节点,引用计数永远降不到零,会造成内存泄漏。解决办法是链条只单向持有 next 的 shared_ptr,如果业务需要反向通知,用 weak_ptr 观察即可。由于我们的轻量设计是单向传递,正常组装不会成环,但用户在运行时动态重设 next 时要避免把已有前驱节点设为自己的后继。
另一个细节是析构顺序。shared_ptr 管理的 Handler 析构时会自动释放后续节点,无需手动 delete。如果处理器内部含有文件句柄或网络连接,应在析构函数中关闭。下面的表格对比了裸指针与智能指针在责任链中的差异。
| 方式 | 内存安全 | 组装便利性 | 适用场景 |
|---|---|---|---|
| 裸指针 | 易泄漏 | 需手动管理 | 极简Demo |
| shared_ptr | 自动释放 | 支持链式 | 生产环境 |
| weak_ptr配合 | 防环引用 | 稍复杂 | 双向需求 |
四、实际应用场景与扩展
轻量责任链特别适合请求需经多道关卡的场景,例如 Web 框架里的鉴权、限流、参数校验。每增加一种检查只需新建 Handler 子类并插入链中,原有逻辑不受影响。如果希望链中途截断,让某个节点处理完不再往下传,只需在 handle 中返回 true 且不调用 getNext 即可。
进一步扩展时,可给 Handler 增加优先级字段,组装前按优先级排序;或者把链本身做成一个容器,用 vector 存储处理器,循环调用。但那就偏离了经典指针链式的结构。保持指针链接的好处是运行时可动态拆装,比如热更新时替换某个节点而不影响整条链。以下代码展示了一个带优先级的简单扩展思路。
#include <vector>
#include <algorithm>
class PriorityHandler : public Handler {
public:
PriorityHandler(int p) : priority_(p) {}
int priority() const { return priority_; }
bool handle(const std::string& request) override {
std::cout << "Priority " << priority_ << " handled" << std::endl;
if (auto n = getNext()) return n->handle(request);
return true;
}
private:
int priority_;
};
void buildChain(std::vector<std::shared_ptr<PriorityHandler>>& vec) {
std::sort(vec.begin(), vec.end(),
[](auto& a, auto& b){ return a->priority() < b->priority(); });
for (size_t i = 0; i + 1 < vec.size(); ++i) {
vec[i]->setNext(vec[i+1]);
}
}
通过上述方式,我们用极少的代码在 C++ 中落地了责任链模式。接口抽象保证了扩展性,指针链式调用让组装直观,智能指针解决了资源管理痛点。读者可直接将示例源码复制到工程里,根据业务填充 handle 逻辑即可。