状态模式的核心是将状态转移的控制权移交当前State对象,通过虚函数和指针重绑定实现自动路由,Context仅持std::unique_ptr并暴露request()接口,State子类无状态、通过handle(Context&)接收上下文。

状态模式的核心不是类图,是状态转移的控制权移交
状态模式在 C++ 里真正起作用的地方,不是定义一堆 State 子类,而是让上下文(Context)把「当前该做什么」的决策权,彻底交给当前持有的 State* 对象。一旦转移发生,后续所有行为调用都自动路由到新状态——这个“自动路由”靠的是虚函数 + 指针重绑定,不是 if-else 判断。
常见错误是把状态逻辑写在 Context 里,只用 enum 区分分支,那只是状态机模拟,不是状态模式;或者每个 State 子类还去访问 Context 的私有成员,破坏封装。
-
Context只暴露必要的接口(如request()),内部仅持有一个State*,且不关心具体类型 - 每个
State子类实现自己的handle(),并在需要时调用context->set_state(new_state) - 必须确保
State生命周期安全:推荐用std::unique_ptr<state></state>管理,避免裸指针悬空
怎么避免状态对象被意外析构或重复 delete
最常踩的坑是 Context 持有裸 State*,而状态对象在栈上创建或由其他模块管理,导致 Context 调用已销毁对象的虚函数——直接 crash,错误信息类似 pure virtual method called 或 segmentation fault。
正确做法是让 Context 完全拥有状态对象:
立即学习“C++免费学习笔记(深入)”;
- 用
std::unique_ptr<state></state>替代裸指针,转移所有权时用std::move() - 所有状态子类设计为无状态(stateless)且可构造,比如
ConcreteStateA{},不保存上下文引用 - 如果状态需要访问
Context数据,通过handle(Context& ctx)参数传入,而非在构造时捕获指针
示例转移逻辑:
void ConcreteStateA::handle(Context& ctx) {
// ... 执行 A 状态逻辑
if (should_switch_to_b) {
ctx.set_state(std::make_unique<ConcreteStateB>()); // 自动释放旧 state
}
}
什么时候该用状态模式,而不是 switch(enum) 或策略模式
当你的「状态」之间存在明确的、非对称的转移关系,且每个状态的行为差异大、逻辑复杂,并可能随时间演化出新状态时,状态模式才值得引入。否则就是杀鸡用牛刀。
对比判断点:
- 用
switch:状态数少(≤4)、转移规则简单、各分支逻辑短( - 用策略模式:行为可互换但无内在状态依赖,比如不同排序算法,不记录“当前处于第几步”
- 用状态模式:状态有生命周期(如 “未初始化 → 连接中 → 已连接 → 断开中”),转移条件分散在各状态内部,且某些操作在非法状态下应静默/报错(如断开中再调用
send())
性能上,虚函数调用开销极小,现代编译器常能 devirtualize;但若每帧调用数百次 handle(),且状态极少变化,可考虑用 std::variant<statea stateb></statea> + std::visit 替代(C++17 起)。
继承体系里怎么处理状态共享数据和回调通知
状态对象本身不该持有上下文数据副本,但有时多个状态需读写同一份配置或缓存。强行让每个 State 通过 Context& 参数访问,会导致接口膨胀(每个 handle() 都要传一堆引用)。
更干净的做法是:
- 在
Context中定义受保护的访问器,如protected: Config& config(); DataCache& cache(); - 让
State基类持有一个Context*(非拥有),子类通过context_->config()读取——注意这里Context必须比所有State活得久 - 若需异步通知(如状态切换后触发 UI 更新),在
Context::set_state()内部调用on_state_changed(const State&)回调,由用户继承重写
关键约束:任何 State 子类都不能缓存 Context* 到成员变量里,因为 Context 可能被移动或销毁;所有访问必须通过参数传入或实时调用 Context 的方法。
状态模式最难的部分从来不是写几个虚函数,而是厘清哪些逻辑属于状态自身,哪些必须回传给上下文——边界模糊时,代码会迅速退化成带指针的巨型 switch。


















