C++虚函数实战,实现一个状态机

状态机解释​​​​

状态机适用范围,同一个动作,有不同的反应操作,举个简单的例子
现在你有一个玩偶,按钮有前进,后退等等指令
但是你总得有方向吧,比如说面朝北,才是前进,和后退
也就说,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. **后期扩展性**:
- 只需添加新状态类并注册
- 新事件类型易于扩展
- 分层设计使功能模块化
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值