C 语言

关注公众号 jb51net

关闭
首页 > 软件编程 > C 语言 > C++ 状态机

C++实现状态机的四种方法

作者:筠筠喵呜喵

本文主要介绍了状态机实现的四种方法,包括Switch、stdstd::variant&std::visit、查表法和状态模式,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

1.Switch

#include <iostream>

enum class State { Idle, Running, Paused, Error };
enum class Event { Start, Pause, Resume, Stop, ErrorOccur, Reset };

struct SM {
    State s = State::Idle;
    void dispatch(Event ev) {
        switch (s) {
            case State::Idle:
                if (ev == Event::Start) { enter(State::Running); }
                else if (ev == Event::ErrorOccur) { enter(State::Error); }
                break;
            case State::Running:
                if (ev == Event::Pause) { enter(State::Paused); }
                else if (ev == Event::Stop) { enter(State::Idle); }
                else if (ev == Event::ErrorOccur) { enter(State::Error); }
                break;
            case State::Paused:
                if (ev == Event::Resume) { enter(State::Running); }
                else if (ev == Event::Stop) { enter(State::Idle); }
                else if (ev == Event::ErrorOccur) { enter(State::Error); }
                break;
            case State::Error:
                if (ev == Event::Reset) { enter(State::Idle); }
                break;
        }
    }
    void enter(State ns) {
        onExit(s);
        s = ns;
        onEnter(s);
    }
    void onEnter(State st) {
        std::cout << "enter " << toString(st) << "\n";
    }
    void onExit(State st) {
        std::cout << "exit " << toString(st) << "\n";
    }
    static const char* toString(State st) {
        switch (st) {
            case State::Idle: return "Idle";
            case State::Running: return "Running";
            case State::Paused: return "Paused";
            case State::Error: return "Error";
        }
        return "Unknown";
    }
};

int main() {
    SM sm;
    sm.dispatch(Event::Start);
    sm.dispatch(Event::Pause);
    sm.dispatch(Event::Resume);
    sm.dispatch(Event::ErrorOccur);
    sm.dispatch(Event::Reset);
    return 0;
}

2. std::variant + std::visit

#include <iostream>
#include <variant>
#include <memory>

struct Context;

// 各状态类型封装行为
struct Idle {
    static void onEnter(Context&);
    static void onExit(Context&);
    static void handle(Context&, int ev);
    static constexpr const char* name = "Idle";
};
struct Running {
    static void onEnter(Context&);
    static void onExit(Context&);
    static void handle(Context&, int ev);
    static constexpr const char* name = "Running";
};
struct Paused {
    static void onEnter(Context&);
    static void onExit(Context&);
    static void handle(Context&, int ev);
    static constexpr const char* name = "Paused";
};
struct ErrorSt {
    static void onEnter(Context&);
    static void onExit(Context&);
    static void handle(Context&, int ev);
    static constexpr const char* name = "Error";
};

using StateVar = std::variant<Idle, Running, Paused, ErrorSt>;

struct Context {
    StateVar state{Idle{}};
    void dispatch(int ev) {
        std::visit([&](auto &st){
            using T = std::decay_t<decltype(st)>;
            T::handle(*this, ev);
        }, state);
    }
    void setState(StateVar ns) {
        std::visit([&](auto &st){ using T = std::decay_t<decltype(st)>; T::onExit(*this); }, state);
        state = std::move(ns);
        std::visit([&](auto &st){ using T = std::decay_t<decltype(st)>; T::onEnter(*this); }, state);
    }
    void log(const char* msg) { std::cout << msg << "\n"; }
};

void Idle::onEnter(Context& c){ c.log("enter Idle"); }
void Idle::onExit(Context& c){ c.log("exit Idle"); }
void Idle::handle(Context& c, int ev){
    if(ev==1) c.setState(Running{});
    else if(ev==99) c.setState(ErrorSt{});
    else c.log("Idle ignore");
}

void Running::onEnter(Context& c){ c.log("enter Running"); }
void Running::onExit(Context& c){ c.log("exit Running"); }
void Running::handle(Context& c, int ev){
    if(ev==2) c.setState(Paused{});
    else if(ev==0) c.setState(Idle{});
    else if(ev==99) c.setState(ErrorSt{});
    else c.log("Running ignore");
}

void Paused::onEnter(Context& c){ c.log("enter Paused"); }
void Paused::onExit(Context& c){ c.log("exit Paused"); }
void Paused::handle(Context& c, int ev){
    if(ev==3) c.setState(Running{});
    else if(ev==0) c.setState(Idle{});
    else if(ev==99) c.setState(ErrorSt{});
    else c.log("Paused ignore");
}

void ErrorSt::onEnter(Context& c){ c.log("enter Error"); }
void ErrorSt::onExit(Context& c){ c.log("exit Error"); }
void ErrorSt::handle(Context& c, int ev){
    if(ev==4) c.setState(Idle{});
    else c.log("Error ignore");
}

int main(){
    Context ctx;
    ctx.dispatch(1);   // Start -> Running
    ctx.dispatch(2);   // Pause -> Paused
    ctx.dispatch(3);   // Resume -> Running
    ctx.dispatch(99);  // Error -> Error
    ctx.dispatch(4);   // Reset -> Idle
    return 0;
}

3.查表法

#include <iostream>
#include <map>
#include <functional>
#include <tuple>

enum class State { Idle, Running, Paused, Error };
enum class Event { Start, Pause, Resume, Stop, ErrorOccur, Reset };

// 表项:(State,Event) -> (new State, action)
using Key = std::pair<State, Event>;
struct Action { State next; std::function<void()> act; };

struct SM {
    std::map<Key, Action> table;
    State s = State::Idle;
    SM() {
        table[{State::Idle, Event::Start}] = {State::Running, [this](){ log("Idle->Running"); }};
        table[{State::Idle, Event::ErrorOccur}] = {State::Error, [this](){ log("Idle->Error"); }};
        table[{State::Running, Event::Pause}] = {State::Paused, [this](){ log("Running->Paused"); }};
        table[{State::Running, Event::Stop}] = {State::Idle, [this](){ log("Running->Idle"); }};
        table[{State::Running, Event::ErrorOccur}] = {State::Error, [this](){ log("Running->Error"); }};
        table[{State::Paused, Event::Resume}] = {State::Running, [this](){ log("Paused->Running"); }};
        table[{State::Paused, Event::Stop}] = {State::Idle, [this](){ log("Paused->Idle"); }};
        table[{State::Paused, Event::ErrorOccur}] = {State::Error, [this](){ log("Paused->Error"); }};
        table[{State::Error, Event::Reset}] = {State::Idle, [this](){ log("Error->Idle"); }};
    }

    void dispatch(Event ev) {
        auto it = table.find({s, ev});
        if (it != table.end()) {
            onExit(s);
            it->second.act();
            s = it->second.next;
            onEnter(s);
        } else {
            log("no transition");
        }
    }

    void onEnter(State st){ std::cout << "enter " << name(st) << "\n"; }
    void onExit(State st){ std::cout << "exit " << name(st) << "\n"; }
    void log(const char* m){ std::cout << m << "\n"; }
    static const char* name(State st){
        switch(st){ case State::Idle: return "Idle"; case State::Running: return "Running"; case State::Paused: return "Paused"; case State::Error: return "Error"; }
        return "Unknown";
    }
};

int main(){
    SM sm;
    sm.dispatch(Event::Start);
    sm.dispatch(Event::Pause);
    sm.dispatch(Event::Resume);
    sm.dispatch(Event::ErrorOccur);
    sm.dispatch(Event::Reset);
    return 0;
}

4.状态模式(虚函数实现)

#include <iostream>
#include <memory>
#include <string>

enum class Event { Start, Pause, Resume, Stop, ErrorOccur, Reset };

class Context;
struct State {
    virtual ~State() = default;
    virtual void onEnter(Context&) {}
    virtual void onExit(Context&) {}
    virtual void handle(Context&, Event) = 0;
    virtual std::string name() const = 0;
};

class Context {
public:
    void setState(std::unique_ptr<State> s) {
        if (state) state->onExit(*this);
        state = std::move(s);
        if (state) state->onEnter(*this);
    }
    void handle(Event ev) {
        if (state) state->handle(*this, ev);
    }
    void log(const std::string &m) { std::cout << m << "\n"; }
private:
    std::unique_ptr<State> state;
};

// Concrete states
struct Idle : State {
    void onEnter(Context& c) override { c.log("enter Idle"); }
    void onExit(Context& c) override { c.log("exit Idle"); }
    void handle(Context& c, Event ev) override;
    std::string name() const override { return "Idle"; }
};
struct Running : State {
    void onEnter(Context& c) override { c.log("enter Running"); }
    void onExit(Context& c) override { c.log("exit Running"); }
    void handle(Context& c, Event ev) override;
    std::string name() const override { return "Running"; }
};
struct Paused : State {
    void onEnter(Context& c) override { c.log("enter Paused"); }
    void onExit(Context& c) override { c.log("exit Paused"); }
    void handle(Context& c, Event ev) override;
    std::string name() const override { return "Paused"; }
};
struct ErrorState : State {
    void onEnter(Context& c) override { c.log("enter Error"); }
    void onExit(Context& c) override { c.log("exit Error"); }
    void handle(Context& c, Event ev) override;
    std::string name() const override { return "Error"; }
};

void Idle::handle(Context& c, Event ev){
    if(ev==Event::Start) c.setState(std::make_unique<Running>());
    else if(ev==Event::ErrorOccur) c.setState(std::make_unique<ErrorState>());
    else c.log("Idle ignore");
}
void Running::handle(Context& c, Event ev){
    if(ev==Event::Pause) c.setState(std::make_unique<Paused>());
    else if(ev==Event::Stop) c.setState(std::make_unique<Idle>());
    else if(ev==Event::ErrorOccur) c.setState(std::make_unique<ErrorState>());
    else c.log("Running ignore");
}
void Paused::handle(Context& c, Event ev){
    if(ev==Event::Resume) c.setState(std::make_unique<Running>());
    else if(ev==Event::Stop) c.setState(std::make_unique<Idle>());
    else if(ev==Event::ErrorOccur) c.setState(std::make_unique<ErrorState>());
    else c.log("Paused ignore");
}
void ErrorState::handle(Context& c, Event ev){
    if(ev==Event::Reset) c.setState(std::make_unique<Idle>());
    else c.log("Error ignore");
}

int main(){
    Context ctx;
    ctx.setState(std::make_unique<Idle>());
    ctx.handle(Event::Start);
    ctx.handle(Event::Pause);
    ctx.handle(Event::Resume);
    ctx.handle(Event::ErrorOccur);
    ctx.handle(Event::Reset);
    return 0;
}

5. 方案对比总结

方案优点缺点适用场景
switch1. 最简单直观
2. 零额外抽象,性能最好(编译期确定)
3. 代码集中,便于理解
1. 状态/事件增多时,switch-case 臃肿
2. 逻辑分散,可维护性差
3. 难以扩展(添加新状态需修改多处)
状态机规模小(<10 个状态),性能要求极高,且不预期频繁变更的场景
std::variant + std::visit1. 类型安全,每个状态行为封装为独立类型
2. 无虚函数调用,可内联优化
3. 编译期检查,避免遗漏状态处理
1. 类型数量增加,代码量较大
2. 添加新状态需修改 variant 列表和工厂
3. 对 C++17 及以上版本有要求
状态集固定,希望将行为与类型绑定,且追求高性能、类型安全的场景
查表法1. 高度配置化、数据驱动
2. 易于维护和测试(表格/文件可外部化)
3. 性能极高(O(1) 查表)
4. 状态转换逻辑集中
1. 运行时查表有轻微开销
2. 复杂行为(guard 条件、enter/exit)仍需回调函数
3. 动作与状态分离,扩展性较差
状态转换规则稳定、希望外部配置、便于测试和动态调整的场景
状态模式(虚函数)1. 面向对象,符合开闭原则
2. 易于管理复杂的 enter/exit 行为
3. 运行时灵活,状态可动态替换
4. 结构清晰,职责分离
1. 虚函数调用开销(运行时多态)
2. 类数量多,可能涉及堆分配
3. 若状态对象非单例,会有对象创建开销
状态行为复杂、需要封装大量状态专属逻辑、且预期会频繁扩展的场景

到此这篇关于C++实现状态机的四种方法的文章就介绍到这了,更多相关C++ 状态机内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文