c++怎么实现简单的状态模式_c++有限状态机写法【实战】

1次阅读

状态类必须用指针或引用传递,否则值传递引发对象切片致虚函数退化为基类实现;State 必须是纯虚类,Context 持有 std::unique_ptr<State>,状态转移逻辑统一收口于 Context::handleEvent(),禁止在状态类内 new/delete,推荐工厂函数创建状态并实现 name() 便于调试。

c++ 怎么实现简单的状态模式_c++ 有限状态机写法【实战】

状态类必须用指针或引用传递,否则对象切片

直接传值会导致派生状态对象被截断成基类,所有虚函数调用都退化为基类实现。这是 C++ 状态模式最隐蔽的崩溃源头。

  • 状态接口 State 必须是纯虚类,禁止定义非虚成员变量
  • 上下文类(如 Context)中保存的是 State*std::unique_ptr<state></state>,绝不能是 State 值类型
  • 切换状态时用 std::make_unique<concretestatea>()</concretestatea>,别写 state = ConcreteStateA()

示例错误:state = IdleState(); → 切片;正确:state = std::make_unique<idlestate>();</idlestate>

状态转移逻辑不能写在状态类内部

把「什么条件下该切到哪个状态」的判断逻辑塞进 handleEvent() 里,会快速导致状态类膨胀、耦合失控。状态类只负责响应,不负责决策。

  • 转移规则统一收口到 Context::handleEvent()
  • 每个状态类的 handleEvent() 只做两件事:执行当前行为、返回建议的新状态 ID(比如枚举 StateId::RUNNING)或者 void
  • 如果要用 ID 驱动转移,Context 内部维护一个 std::map<stateid std::unique_ptr>></stateid> 映射表

常见错误现象:IdleState::handleEvent() 里 new 一个 RunningState 并赋给 context->state —— 这会让 Context 失去对状态生命周期的控制权。

立即学习 C++ 免费学习笔记(深入)”;

避免裸 new / delete,优先用智能指针 + 工厂函数

手动管理状态对象内存,在异常路径或频繁切换场景下极易泄漏或 double-delete。

  • std::unique_ptr<state></state> 替代 State*,构造时用 std::make_unique()
  • 所有状态创建封装进工厂函数,例如 createState(StateId id),便于后续加日志、计数或替换策略
  • 如果状态需共享数据,通过 Context& 引用传入,不要在状态类里存 Context*(容易悬空)

性能影响:std::unique_ptr 零开销抽象,但每次 reset() 会有一次小内存分配;若状态极少切换,可预分配几个对象用对象池优化。

调试时打印状态名比打印地址有用得多

运行时看到 0x7ffee21a8b30 完全无法定位问题,而 "IdleState" 能立刻对应到代码分支。

  • 在基类 State 加一个纯虚函数 virtual const char* name() const = 0;
  • 每个派生类实现它,返回字符串字面量(无内存分配)
  • Context::handleEvent() 开头加日志:std::cout name()

容易被忽略的点:没重写 name() 就编译不过,强制每个状态自我标识;调试信息不依赖 RTTI,兼容 -fno-rtti 环境。

text=ZqhQzanResources