c++中实现状态机有两种常用方法:状态模式通过类和多态封装状态行为,适合逻辑复杂、扩展需求高的场景;表驱动法用状态转移表定义“状态+事件→动作”,适用于状态事件固定、性能要求高的场合。状态模式扩展性强但类数量多;表驱动结构清晰、效率高但灵活性差。选择依据包括逻辑复杂度、状态可变性及团队习惯,也可结合使用以兼顾灵活性与性能。

在C++中实现状态机,常见的方式有两种:状态模式(State Pattern)和表驱动法(table-Driven approach)。两者各有优势,适用于不同场景。下面分别介绍其实现方法与使用建议。
状态模式:面向对象的经典设计
状态模式通过类和多态机制来管理对象的状态转换,适合状态行为差异大、逻辑复杂的系统。
核心思想是将每个状态封装成独立的类,对象的行为随状态改变而动态切换。
示例代码:
立即学习“C++免费学习笔记(深入)”;
定义一个抽象状态基类,再为每种状态提供具体实现。
#include <iostream> #include <memory> class Context; // 前向声明 // 抽象状态类 class State { public: virtual ~State() = default; virtual void handle(Context& ctx) = 0; }; // 具体状态类 class ConcreteStateA : public State { public: void handle(Context& ctx) override; }; class ConcreteStateB : public State { public: void handle(Context& ctx) override; }; // 上下文类,持有当前状态 class Context { private: std::shared_ptr<State> state_; public: Context(std::shared_ptr<State> state) : state_(state) {} void request() { state_->handle(*this); } void changeState(std::shared_ptr<State> new_state) { state_ = new_state; } }; // 实现具体状态的行为 void ConcreteStateA::handle(Context& ctx) { std::cout << "处理状态 A 的逻辑n"; // 可以触发状态转移 ctx.changeState(std::make_shared<ConcreteStateB>()); } void ConcreteStateB::handle(Context& ctx) { std::cout << "处理状态 B 的逻辑n"; ctx.changeState(std::make_shared<ConcreteStateA>()); }
使用方式:
“`cpp int main() { auto ctx = Context(std::make_shared
优点:扩展性强,新增状态只需添加新类;符合开闭原则。
缺点:类数量增多,结构略复杂。
表驱动法:简洁高效的状态转换
表驱动法用二维数组或映射结构描述“当前状态 + 事件 → 下一状态 + 动作”,适合状态和事件有限且明确的场景。
常用于协议解析、控制流程等对性能要求较高的场合。
示例代码:
立即学习“C++免费学习笔记(深入)”;
#include <iostream> #include <functional> #include <map> enum State { IDLE, RUNNING, PAUSED }; enum Event { START, STOP, PAUSE, RESUME }; struct Transition { State next_state; std::function<void()> action; }; class Statemachine { private: State current_state_; std::map<std::pair<State, Event>, Transition> table_; public: StateMachine() : current_state_(IDLE) { // 初始化状态转移表 table_[{IDLE, START}] = {RUNNING, []{ std::cout << "启动设备n"; }}; table_[{RUNNING, PAUSE}] = {PAUSED, []{ std::cout << "暂停运行n"; }}; table_[{PAUSED, RESUME}] = {RUNNING, []{ std::cout << "恢复运行n"; }}; table_[{RUNNING, STOP}] = {IDLE, []{ std::cout << "停止设备n"; }}; table_[{PAUSED, STOP}] = {IDLE, []{ std::cout << "强制停止n"; }}; } void dispatch(Event e) { auto key = std::make_pair(current_state_, e); auto it = table_.find(key); if (it != table_.end()) { if (it->second.action) { it->second.action(); } current_state_ = it->second.next_state; } else { std::cout << "无效的状态转移: "; // 简单打印状态和事件 printStateEvent(current_state_, e); } } void printCurrentState() const { switch (current_state_) { case IDLE: std::cout << "当前状态: IDLEn"; break; case RUNNING: std::cout << "当前状态: RUNNINGn"; break; case PAUSED: std::cout << "当前状态: PAUSEDn"; break; } } private: void printStateEvent(State s, Event e) { const char* s_str[] = {"IDLE", "RUNNING", "PAUSED"}; const char* e_str[] = {"START", "STOP", "PAUSE", "RESUME"}; std::cout << s_str[s] << " + " << e_str[e] << "n"; } };
使用方式:
“`cpp int main() { StateMachine sm; sm.printCurrentState(); // IDLE sm.dispatch(START); // 启动设备 sm.printCurrentState(); // RUNNING sm.dispatch(PAUSE); // 暂停运行 sm.dispatch(RESUME); // 恢复运行 sm.dispatch(STOP); // 停止设备 sm.printCurrentState(); // IDLE return 0; } “`
优点:结构清晰,易于维护和生成代码;性能好。
缺点:状态和事件必须提前确定;动作函数若复杂则不易管理。
如何选择合适的方法?
根据项目需求权衡:
- 业务逻辑复杂、状态行为差异明显 → 推荐使用状态模式
- 状态和事件固定、追求简洁高效 → 推荐使用表驱动法
- 需要运行时动态配置状态转移 → 表驱动更灵活
- 团队熟悉设计模式 → 状态模式更容易协作理解
也可以结合使用:用表驱动管理状态跳转,用函数对象绑定状态模式中的处理逻辑。
基本上就这些。两种方法都能有效管理对象状态,关键看场景是否匹配。