状态机解释
状态机适用范围,同一个动作,有不同的反应操作,举个简单的例子
现在你有一个玩偶,按钮有前进,后退等等指令
但是你总得有方向吧,比如说面朝北,才是前进,和后退
也就说,w控制前进,但是方向不同,前进后的坐标就不同,这就是一个指令有不同的操作
这个玩偶就是你的状态机,负责在当前状态执行对应的指令事件,而你作为管理者,可以改变当前状态
看截图(我这里用1,2,3控制转向,0代表退出)
解释C++虚函数 virtual
普通虚函数
一旦一个成员函数添加了virtual这个关键字,那么这个类就会产生一个虚函数表,这个表里面存的是虚函数地址
这个类没有涉及到继承,此时虚函数表中记录的就是当前类的虚函数的地址
有继承时,分为两种情况:
1、子类不重写虚函数 (重写指的是 函数返回值,函数名,参数列表都相同)
那么不会发生覆盖,表中还是父类的虚函数地址
2、子类重写虚函数,由于发生了继承,那么虚函数表也会被继承,之前存的是父类的虚函数,但是由于子类重写了这个函数,这个函数也要被放入虚函数表,所以就发生了覆盖,这个表存的是子类的虚函数
父类指针调虚函数,通过虚函数指针找到虚函数表,而虚函数已经被子类重写的覆盖了,所以调用的是子函数重写的虚函数。也就说被virtual修饰以后,这个成员函数就变成了一个函数指针,只要不涉及继承,那么这个指针就指向自己这个函数
纯虚函数
该类一定会被继承,该类的虚函数一定会被重写,那么父类就没有必要写这个函数了,因为一定会被覆盖,这时候就叫纯虚函数,也就是虚函数不实现函数体
virtual void speak(void)=0; =0的意思是告诉你没有函数体,没有这个部分则是函数声明
一但类中有纯虚函数,那么这个类就是抽象类
抽象类,不能被实例化!换句话说,抽象类必须被继承,且子类必须重写父类中的所有纯虚函数
虚析构
在的继承一章节中,已经知道了继承过程中的构造和析构调用的顺序了
又可以由虚函数知道,只要加了virtual修饰,那么有虚函数表记录了这个虚函数,所以这个虚析构函数也被继承了
而子类有自己的析构函数,一但发生继承,虽然函数名不同,但是这个析构函数也是虚函数了,也发生了上述的覆盖行为
所以在delete父类的时候,虚函数指针找到虚函数表,而子函数的虚析构函数进行了覆盖,就释放了子类,一但子类的析构函数被调用,父类的析构函数也随之调用
代码部分(不懂概念没关系,代码来解释)
要实现状态机,首先来枚举事件类型
//枚举事件类型,这里假设我有四个事件,前进,后退,左移,右移
//不同的朝向指令,向左转,向右转,向后转
enum EventType{
None,
Forward,
Back,
Left,
Right,
LeftTurn,
RightTurn,
BackTurn
};
事件类 Event
//
// Created by Administrator on 2025/4/13.
//
#ifndef STATEMACHINE_EVENT_H
#define STATEMACHINE_EVENT_H
//枚举事件类型,这里假设我有四个事件,前进,后退,左移,右移
//不同的朝向指令,向左转,向右转,向后转
enum EventType{
None,
Forward,
Back,
Left,
Right,
LeftTurn,
RightTurn,
BackTurn
};
//基本事件类
class Event{
public:
//构造一个事件,事件类型默认为None
Event(EventType t = EventType::None);
virtual ~Event();
//设置事件类型
void setEventType(EventType t);
//获取事件类型
EventType getEventType() const;
private:
EventType _type;
};
#endif //STATEMACHINE_EVENT_H
//cpp
//
// Created by Administrator on 2025/4/13.
//
#include "Event.h"
Event::Event(EventType t)
:_type(t)
{
}
Event::~Event() {
}
void Event::setEventType(EventType t) {
_type = t;
}
EventType Event::getEventType() const {
return _type;
}
状态基类 BaseState
在前面说了,我们要用父类去调用子类的函数,父类等于是只提供接口,具体的逻辑由继承的子类来实现,在这里的注释中,你可以直接了解到虚函数和纯虚函数的区别
//
// Created by Administrator on 2025/4/13.
//
#ifndef STATEMACHINE_BASESTATE_H
#define STATEMACHINE_BASESTATE_H
#include <iostream>
#include "Event.h"
//先声明有这个类,因为这里要用到,等会再实现它
class StateMachine;
//基类状态
class BaseState{
public:
virtual ~BaseState(){};//虚析构,通过父类指针释放子类空间
//进入状态时调用
virtual void onEntry(){};//虚函数,不设置为纯虚函数,代表这个函数可以不重写,不重写则调用基类虚函数
//离开状态时调用
virtual void onExit(){};//虚函数,不设置为纯虚函数,代表这个函数可以不重写,不重写则调用基类虚函数
//处理事件
virtual bool handleEvent(StateMachine* machine,const Event* event) = 0;//纯虚函数,强制子类必须要重写这个方法
//获取ID,可以根据ID区分不同状态
virtual int getID() const = 0;//纯虚函数,强制子类必须要重写这个方法
//获取状态名称,主要用于调试
virtual std::string getStateName() const = 0;//纯虚函数,强制子类必须要重写这个方法
private:
};
#endif //STATEMACHINE_BASESTATE_H
状态机类 StateMachine
注意我没打错,上一个是状态 基类,这个是状态机 类,这个就是开头提到的玩偶
//
// Created by Administrator on 2025/4/13.
//
#ifndef STATEMACHINE_STATEMACHINE_H
#define STATEMACHINE_STATEMACHINE_H
#include <iostream>
#include "BaseState.h"
/*
* 把这个想象为一个受你把控的机器,机器有不同的朝向(东,南,西,北),这就是不同的状态
* 当你发出指令的时候,机器就有不同的动作
* 如果说发生了错误,那么机器就罢工了......
* */
//定义错误回调
using ErrorHandler = void(*)(int errID,std::string errMsg);
//状态机类
class StateMachine{
public:
StateMachine();
~StateMachine();
//初始化状态机,这个要有正常状态和错误状态
bool initStateMachine(BaseState* initialState,BaseState* errorState);
//核心!!!!!!
//处理事件,注意区别与 BaseState 中的函数
//这里是捕获到某个事件,由状态机调用 BaseState 中的handleEvent方法
//BaseState作为父类即可通过虚函数表调用子类,进行逻辑处理
bool handleEvent(const Event* event);
//切换状态
bool changeState(BaseState* newState);
//回到前一个状态
bool transitionToPreState();
//设置错误处理器
void setErrorHandler(ErrorHandler errorHandler);
//触发错误处理
void handleError(int errID,std::string errMsg);
//获取当前状态
BaseState* getCurrentState() const;
private:
BaseState* _preState;//存储上一个状态,如果不需要回到上一个状态可以不写
BaseState* _curState;//存储当前状态
BaseState* _errState;//错误状态,如果不需要可以不写
ErrorHandler _errorHandler;//定义错误处理方法,如果不需要可以不写
bool _transitionInProgress;//用来标志一个事件是否被"使用"
};
#endif //STATEMACHINE_STATEMACHINE_H
//CPP
//
// Created by Administrator on 2025/4/13.
//
#include "StateMachine.h"
StateMachine::StateMachine()
:_curState(nullptr)
,_preState(nullptr)
,_errState(nullptr)
,_errorHandler(nullptr)
,_transitionInProgress(false)
{
}
StateMachine::~StateMachine() {
//无论是什么状态
//这里都没有所有权,所以不负责删除
//看能不能理解,这只是一个机器,机器有不同的状态,有事件,就处理
//举个例子
//一个机器朝 "东、南、西、北" (有不同的状态)
//当有前进指令(事件),就前进(处理)
//朝向不同,状态不同,而东南西北并不是机器管理,只是拥有这个状态,所以不负责删除状态
}
bool StateMachine::initStateMachine(BaseState* initialState,BaseState* errorState) {
if(initialState == nullptr || errorState == nullptr ){
return false;
}
_curState = initialState;
_errState = errorState;
try{
//进入初始状态
_curState->onEntry();
return true;
}catch(...) {
handleError(1, "进入初始状态失败!");
return false;
}
}
bool StateMachine::handleEvent(const Event *event) {
if(event == nullptr){
return false;
}
//确保不会让同一事件出发两次
if(_transitionInProgress == true){
return false;
}
try{
//标记该事件得到处理
_transitionInProgress = true;
//本章虚函数核心,调用基类方法,基类去调用子类方法
bool ok = _curState->handleEvent(this,event);
//处理过了
_transitionInProgress = false;
return ok;
}catch (...){
_transitionInProgress = false;
handleError(2, "事件处理异常");
return false;
}
}
bool StateMachine::changeState(BaseState* newState) {
if(newState == nullptr){
return false;
}
try{
if(_curState != nullptr){
//调用当前状态离开方法
_curState->onExit();
}
//调用当前状态离开方法
_preState = _curState;
_curState = newState;
//进入下一个状态
_curState->onEntry();
return true;
}catch (...){
handleError(3, "切换状态失败");
return false;
}
}
bool StateMachine::transitionToPreState() {
if(_preState == nullptr)
return false;
return changeState(_preState);
}
void StateMachine::setErrorHandler(ErrorHandler errorHandler) {
//用户设置回调函数
_errorHandler = errorHandler;
}
void StateMachine::handleError(int errID, std::string errMsg) {
if(_errorHandler != nullptr){
_errorHandler(errID,errMsg);
}
// 如果有错误状态,切换到错误状态
if (_errState && _curState != _errState) {
changeState(_errState);
}
}
BaseState *StateMachine::getCurrentState() const {
if(_curState != nullptr){
return _curState;
}
return nullptr;
}
状态管理类 StateManager
上面那个是你的玩偶,所以你就是这个管理者
//
// Created by Administrator on 2025/4/13.
//
#ifndef STATEMACHINE_STATEMANAGER_H
#define STATEMACHINE_STATEMANAGER_H
/*
* 前一个类说了,它只是一个受你把控的机器
* 现在来行驶你作为一个"管理者所拥有的特权!"
* */
#include <iostream>
#include <vector>
#include "BaseState.h"
#include "NorthMode.h"
#include "EastMode.h"
#include "SouthMode.h"
#include "WestMode.h"
#include "ErrorMode.h"
#define STATE_SIZE 5 //定义状态数量
class StateManager {
public:
//单例模式
static StateManager* getInstance();
~StateManager();
//注册状态
bool registState(BaseState* state);
//获取状态
BaseState* getState(int stateIndex) const;
//获取状态数量
int getStateCount() const;
private:
//构造私有,为了实现单例模式。可以理解为管理者只能有一个!
StateManager();
private:
//使用vector容器存储状态指针
std::vector<BaseState*> _stateVec;
//嵌入式不支持容器则使用数组
// BaseState* _stateArr[STATE_SIZE];
};
#endif //STATEMACHINE_STATEMANAGER_H
//cpp
//
// Created by Administrator on 2025/4/13.
//
#include "StateManager.h"
StateManager* StateManager::getInstance() {
static StateManager* instance = new StateManager();
return instance;
}
StateManager::StateManager() {
//初始化状态容器
for(int i = 0 ;i < STATE_SIZE ; ++i ){
_stateVec.push_back(nullptr);
}
}
StateManager::~StateManager() {
// 遍历并删除所有注册的状态对象,避免内存泄漏
for (BaseState* state : _stateVec) {
delete state; // 释放每个状态对象的内存
}
}
bool StateManager::registState(BaseState* state) {
if(state == nullptr){
return false;
}
int id = state->getID();
if (id <= 0 || id > STATE_SIZE) {
return false;
}
// 如果该位置已经有状态对象,就要进行替换
if (_stateVec[id-1] != nullptr) {
delete _stateVec[id-1];
}
// 注册状态,插入到指定位置
_stateVec[id-1] = state;
std::cout<<"成功注册状态模式: "<<_stateVec[id-1]->getStateName()<<std::endl;
// 成功注册,返回true
return true;
}
BaseState* StateManager::getState(int stateIndex) const {
if(stateIndex <=0 || stateIndex > STATE_SIZE)
return nullptr;
return _stateVec[stateIndex - 1];
}
int StateManager::getStateCount() const {
int count = 0;
for (BaseState* state : _stateVec) {
if(state != nullptr){
count++;
}
}
return count;
}
基础框架已经搭建完成,接下来就是具体逻辑了,这里我不把所有的全贴出来了,完整代码看文章末尾
朝北状态模式 NorthMode
我们所有的模式都是继承基础模式,(也可以叫基础状态 BaseState)而来的,我们可以定义很多的模式(这里我只定义东南西北四个模式),那只要重写父类的虚函数,就可以实现由父类调子类了。看看给出的两个示例代码,理解一下是怎么操作的,这里只给出一个示例,完整代码我会放出来。
//
// Created by Administrator on 2025/4/13.
//
#ifndef STATEMACHINE_NORTHMODE_H
#define STATEMACHINE_NORTHMODE_H
#include "BaseState.h"
#include "StateManager.h"
class NorthMode: public BaseState {
public:
enum {ID = 1};
NorthMode();
//重写基类虚函数
virtual void onExit();
virtual void onEntry();
virtual bool handleEvent(StateMachine* machine,const Event* event);
virtual int getID() const;
virtual std::string getStateName() const;
public:
//静态成员变量,不依赖对象直接使用
static int _x;
static int _y;
};
#endif //STATEMACHINE_NORTHMODE_H
//CPP
//
// Created by Administrator on 2025/4/13.
//
#include "NorthMode.h"
int NorthMode::_x = 0;
int NorthMode::_y = 0;
NorthMode::NorthMode() {
}
void NorthMode::onExit() {
std::cout<<"离开朝向 ↑"<<std::endl;
}
void NorthMode::onEntry() {
std::cout<<"当前朝向 ↑"<<std::endl;
}
bool NorthMode::handleEvent(StateMachine *machine, const Event *event) {
if(machine == nullptr || event == nullptr){
return false;
}
switch (event->getEventType()) {
case EventType::Forward:{
NorthMode::_y++;
std::cout<<"前进一步,当前位置: ("<<NorthMode::_x<<" , "<<NorthMode::_y<<" )"<<std::endl;
return true;
}
case EventType::Back:{
NorthMode::_y--;
std::cout<<"后退一步,当前位置: ("<<NorthMode::_x<<" , "<<NorthMode::_y<<" )"<<std::endl;
return true;
}
case EventType::Left:{
NorthMode::_x--;
std::cout<<"左移一步,当前位置: ("<<NorthMode::_x<<" , "<<NorthMode::_y<<" )"<<std::endl;
return true;
}
case EventType::Right:{
NorthMode::_x++;
std::cout<<"右移一步,当前位置: ("<<NorthMode::_x<<" , "<<NorthMode::_y<<" )"<<std::endl;
return true;
}
case EventType::LeftTurn:{
std::cout<<"向左转"<<std::endl;
BaseState* state = StateManager::getInstance()->getState(WestMode::ID);
if(state != nullptr){
machine->changeState(state);
return true;
}
return false;
}
case EventType::RightTurn:{
std::cout<<"向右转"<<std::endl;
BaseState* state = StateManager::getInstance()->getState(EastMode::ID);
if(state != nullptr){
machine->changeState(state);
return true;
}
return false;
}
case EventType::BackTurn:{
std::cout<<" 向后转 "<<std::endl;
BaseState* state = StateManager::getInstance()->getState(SouthMode::ID);
if(state != nullptr){
machine->changeState(state);
return true;
}
return false;
}
default:
return false;
}
}
int NorthMode::getID() const {
return NorthMode::ID;
}
std::string NorthMode::getStateName() const {
return std::string("NorthMode");
}
错误状态 ErrorMode
//
// Created by Administrator on 2025/4/13.
//
#ifndef STATEMACHINE_ERRORMODE_H
#define STATEMACHINE_ERRORMODE_H
#include "BaseState.h"
#include "StateManager.h"
#include "StateMachine.h"
#include "NorthMode.h"
class ErrorMode: public BaseState{
public:
enum {ID = 5};
ErrorMode();
//重写基类虚函数
virtual void onEntry();
virtual bool handleEvent(StateMachine* machine,const Event* event);
virtual int getID() const;
virtual std::string getStateName() const;
private:
int _errCode;
std::string _errMsg;
};
#endif //STATEMACHINE_ERRORMODE_H
//CPP
//
// Created by Administrator on 2025/4/13.
//
#include "ErrorMode.h"
ErrorMode::ErrorMode()
:_errCode(0)
, _errMsg("")
{
}
void ErrorMode::onEntry() {
std::cout<<"发生错误,错误ID: "<<_errCode<<" ,errMsg: "<<_errMsg<<std::endl;
}
bool ErrorMode::handleEvent(StateMachine *machine, const Event *event) {
if(machine == nullptr || event == nullptr){
return false;
}
//任何指令都回到(0,0),↑
if(event->getEventType()>EventType::None){
auto a = StateManager::getInstance()->getState(NorthMode::ID);
if(a != nullptr){
machine->changeState(a);
return true;
}
}
return false;
}
int ErrorMode::getID() const {
return ErrorMode::ID;
}
std::string ErrorMode::getStateName() const {
return std::string("Error");
}
主函数 main.cpp
#include <iostream>
#include "StateManager.h"
#include "StateMachine.h"
// 错误处理函数
void errorHandler(int errorCode, std::string errMsg) {
// std::cout<<"错误"<<std::endl;
// 可以记录错误日志等、对应的错误逻辑
}
Event* getNextEvent();
bool isRun = true;//用来控制是否结束程序
int main() {
// 注册状态
StateManager* manager = StateManager::getInstance();
//注册功能状态
manager->registState(new NorthMode());
//注册错误状态,初始化也要用错误状态
ErrorMode* errorMode = new ErrorMode();
manager->registState(errorMode);
// 创建状态机
StateMachine stateMachine;
// 设置错误处理器
stateMachine.setErrorHandler(errorHandler);
//初始化状态机
bool ok = stateMachine.initStateMachine(manager->getState(NorthMode::ID),errorMode);
if(!ok){
std::cout<<"初始化状态机失败"<<std::endl;
return -1;
}
while (isRun){
Event* e = getNextEvent();
if(e != nullptr){
stateMachine.handleEvent(e);
delete e;
}
// 可以添加退出条件,例如:
// if (shouldExit()) {
// running = false;
// }
}
return 0;
}
Event* getNextEvent()
{
//这里获取对应的事件,只是简单的演示一下功能
//我就不做太多的修改了 比如
// Event* e = new Event()
// return e;
return nullptr;
}
完整代码及演示
通过网盘分享的文件:StateMachine.zip
链接: https://pan.baidu.com/s/1hwEIfoM7CbraiHdYIQ_vHg?pwd=yuan 提取码: yuan
--来自百度网盘超级会员v5的分享
StateMachine: C++虚函数实战,实现一个状态机
这里用w a s d控制往前、往左等等,1对应左转,3对应右转,2对应后转,0代表退出,没有做界面了,就用控制台吧
架构特点
## 架构特点 | ||
1. **灵活性**: | ||
- 状态、事件可以方便扩展 | ||
- 新功能只需创建新的状态类并注册 | ||
- 事件处理和状态转换分离 | ||
2. **异常处理**: | ||
- 错误状态处理系统崩溃 | ||
- try-catch 机制捕获异常 | ||
- 错误回调函数提供自定义处理 | ||
3. **后期扩展性**: | ||
- 只需添加新状态类并注册 | ||
- 新事件类型易于扩展 | ||
- 分层设计使功能模块化 |