C++實(shí)現(xiàn)狀態(tài)機(jī)的四種方法
1.Switch
- 優(yōu)點(diǎn):直觀、零額外抽象、性能最好
- 缺點(diǎn):case 越多越臃腫,邏輯分散。
#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
- 優(yōu)點(diǎn):每個(gè)狀態(tài)行為封裝成類(lèi)型,類(lèi)型安全且可內(nèi)聯(lián)(無(wú)虛調(diào)用)。
- 缺點(diǎn):增加類(lèi)型數(shù)量,添加新?tīng)顟B(tài)需改 variant 列表和工廠。
#include <iostream>
#include <variant>
#include <memory>
struct Context;
// 各狀態(tài)類(lèi)型封裝行為
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.查表法
- 優(yōu)點(diǎn):配置化、數(shù)據(jù)驅(qū)動(dòng)、容易以表格/文件維護(hù)和測(cè)試,性能極高。
- 缺點(diǎn):運(yùn)行時(shí)查表開(kāi)銷(xiāo)、復(fù)雜行為(guards/enter/exit)仍需回調(diào)。動(dòng)作(action)與狀態(tài)分離,擴(kuò)展性較差
#include <iostream>
#include <map>
#include <functional>
#include <tuple>
enum class State { Idle, Running, Paused, Error };
enum class Event { Start, Pause, Resume, Stop, ErrorOccur, Reset };
// 表項(xiàng):(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.狀態(tài)模式(虛函數(shù)實(shí)現(xiàn))
- 優(yōu)點(diǎn):面向?qū)ο?、可在狀態(tài)間封裝復(fù)雜 enter/exit 行為,運(yùn)行時(shí)靈活。
- 缺點(diǎn):類(lèi)數(shù)量與抽象開(kāi)銷(xiāo)(虛函數(shù)/堆分配),需謹(jǐn)慎優(yōu)化(對(duì)象復(fù)用或單例狀態(tài)可減開(kāi)銷(xiāo))。
#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. 方案對(duì)比總結(jié)
| 方案 | 優(yōu)點(diǎn) | 缺點(diǎn) | 適用場(chǎng)景 |
|---|---|---|---|
| switch | 1. 最簡(jiǎn)單直觀 2. 零額外抽象,性能最好(編譯期確定) 3. 代碼集中,便于理解 | 1. 狀態(tài)/事件增多時(shí),switch-case 臃腫 2. 邏輯分散,可維護(hù)性差 3. 難以擴(kuò)展(添加新?tīng)顟B(tài)需修改多處) | 狀態(tài)機(jī)規(guī)模?。?lt;10 個(gè)狀態(tài)),性能要求極高,且不預(yù)期頻繁變更的場(chǎng)景 |
| std::variant + std::visit | 1. 類(lèi)型安全,每個(gè)狀態(tài)行為封裝為獨(dú)立類(lèi)型 2. 無(wú)虛函數(shù)調(diào)用,可內(nèi)聯(lián)優(yōu)化 3. 編譯期檢查,避免遺漏狀態(tài)處理 | 1. 類(lèi)型數(shù)量增加,代碼量較大 2. 添加新?tīng)顟B(tài)需修改 variant 列表和工廠 3. 對(duì) C++17 及以上版本有要求 | 狀態(tài)集固定,希望將行為與類(lèi)型綁定,且追求高性能、類(lèi)型安全的場(chǎng)景 |
| 查表法 | 1. 高度配置化、數(shù)據(jù)驅(qū)動(dòng) 2. 易于維護(hù)和測(cè)試(表格/文件可外部化) 3. 性能極高(O(1) 查表) 4. 狀態(tài)轉(zhuǎn)換邏輯集中 | 1. 運(yùn)行時(shí)查表有輕微開(kāi)銷(xiāo) 2. 復(fù)雜行為(guard 條件、enter/exit)仍需回調(diào)函數(shù) 3. 動(dòng)作與狀態(tài)分離,擴(kuò)展性較差 | 狀態(tài)轉(zhuǎn)換規(guī)則穩(wěn)定、希望外部配置、便于測(cè)試和動(dòng)態(tài)調(diào)整的場(chǎng)景 |
| 狀態(tài)模式(虛函數(shù)) | 1. 面向?qū)ο?,符合開(kāi)閉原則 2. 易于管理復(fù)雜的 enter/exit 行為 3. 運(yùn)行時(shí)靈活,狀態(tài)可動(dòng)態(tài)替換 4. 結(jié)構(gòu)清晰,職責(zé)分離 | 1. 虛函數(shù)調(diào)用開(kāi)銷(xiāo)(運(yùn)行時(shí)多態(tài)) 2. 類(lèi)數(shù)量多,可能涉及堆分配 3. 若狀態(tài)對(duì)象非單例,會(huì)有對(duì)象創(chuàng)建開(kāi)銷(xiāo) | 狀態(tài)行為復(fù)雜、需要封裝大量狀態(tài)專(zhuān)屬邏輯、且預(yù)期會(huì)頻繁擴(kuò)展的場(chǎng)景 |
到此這篇關(guān)于C++實(shí)現(xiàn)狀態(tài)機(jī)的四種方法的文章就介紹到這了,更多相關(guān)C++ 狀態(tài)機(jī)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C語(yǔ)言實(shí)例講解四大循環(huán)語(yǔ)句的使用
C語(yǔ)言有四大循環(huán)語(yǔ)句,他們之間可以進(jìn)行任意轉(zhuǎn)換。本文將首先對(duì)其語(yǔ)法進(jìn)行講解,然后通過(guò)一個(gè)實(shí)例用四種循環(huán)來(lái)實(shí)現(xiàn)。相信通過(guò)本文的學(xué)習(xí),大家都能夠?qū)語(yǔ)言循環(huán)語(yǔ)句有著熟練的掌握2022-05-05
解決scanf_s輸入%d%c%d格式錯(cuò)誤的問(wèn)題
這篇文章主要介紹了解決scanf_s輸入%d%c%d格式錯(cuò)誤的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-12-12
淺析C語(yǔ)言中printf(),sprintf(),scanf(),sscanf()的用法和區(qū)別
以下是對(duì)C語(yǔ)言中printf(),sprintf(),scanf(),sscanf()的用法以及區(qū)別進(jìn)行了詳細(xì)的分析介紹,需要的朋友可以參考下2013-07-07
C++中vector迭代器失效問(wèn)題的原因及解決方案
迭代器(iterator)是一種用于遍歷數(shù)據(jù)集合的的對(duì)象,它提供了一種訪問(wèn)數(shù)據(jù)集合中元素的方式,而無(wú)需暴露數(shù)據(jù)集合內(nèi)部的細(xì)節(jié),使用迭代器,我們可以對(duì)數(shù)據(jù)集合中的每個(gè)元素進(jìn)行處理,本文介紹了C++中關(guān)于vector迭代器失效問(wèn)題的原因及解決方案,需要的朋友可以參考下2024-09-09
C++使用string的大數(shù)取模運(yùn)算(5)
這篇文章主要為大家詳細(xì)介紹了C++使用string的大數(shù)取模運(yùn)算,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-09-09
C語(yǔ)言詳解關(guān)鍵字sizeof與unsigned及signed的用法
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言關(guān)鍵字sizeof&&unsigned&&signed,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-06-06
C++利用libcurl獲取下載文件名稱(chēng)及大小
這篇文章主要為大家詳細(xì)介紹了C++如何利用libcurl獲取下載文件名稱(chēng)及大小的功能,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下2023-03-03

