Переключение состояний в FSM

Я экспериментирую с использованием конечного автомата в качестве модели для управления потоком простой игры. Войдите в главное меню, в котором вы выбираете, например, запуск игры или изменение параметров и т. Д.

Я делаю это путем создания базового класса State, от которого наследуется каждое состояние. У меня есть класс приложения, который управляет программным циклом, и добавить указатель на текущее состояние. Само приложение имеет метод changeState, который выходит из текущих состояний и вводит следующее. При входе в состояние я даю ему указатель на класс приложения, и каждое состояние содержит логику для перехода в следующее состояние.

Это вызывает ситуацию, когда я не уверен, что происходит, или, скорее, что должно произойти.

В частности: Программа опрашивает события. Событие передается в текущее состояние для обработки. Если ввод диктует переключение в следующее состояние, я вызываю функцию changeState. Функция изменения состояния удаляет текущее состояние и загружает следующее.

Моя путаница заключается в том, что если я удаляю состояние, из которого я вызвал функцию изменения состояния, что произойдет, когда я вернусь из функции изменения состояния? Немного упрощенного кода, чтобы показать, что я имею в виду более четко:

class Application
{
    public:
        void run();
        void changeState( StateBase * nextState );

    protected:
        void Initialize(){m_running=false; changeState(new BaseState);};

    private:
        StateBase * m_currentState;
        bool m_running;
};

void Application::run()
{
    Initialize();
    while (m_running)
    {
        Event event;
        while ( pollEvent(event) ) // Process events until event queue is empty
        {
            m_currentState->handleEvent( event );
        }
    }
}

void Application::changeState( StateBase * nextState )
{
    if (m_currentState!= 0)
    {
        m_currentState->exit();
        delete m_currentState;
    }
    m_currentState = nextState;
    m_currentState->enter( this );
}

class StateBase()
{
    public:
        void enter(  Application * app ){ m_Application = app };
        void handleEvent( Event const& event );
        void exit(){};
    private:
        Application * m_Application;
}

void StateBase::handleEvent( Event const& event )
{
    if ( event )
        m_Application->changeState( new StateBase );
}

int main()
{
    Application App;
    App.run();
    return 0;
}

Пытался поставить только важные биты там. В любом случае, я вижу, что происходит: я запускаю приложение. Затем я вызываю публичный метод run(), который вызывает Initialize(), чтобы установить для переменной m_running значение true, и вызывает changeState для нового BaseState. changeState дает состояние указатель на это, так что событие может получить доступ к информации приложения.

Затем метод run опрашивает события и, обнаружив их, отправляет их в текущее состояние для обработки.

Если событие требует изменения состояния, оно вызывает m_Application->changeState( new StateBase);

И вот тут я запутался. changeState() вызывает delete для m_currentState, который является экземпляром StateBase, выполняющим вызов. Когда управление возвращается из changeState (), оно переходит к событию, которое должно было быть удалено. Тем не менее, я проверил его, и он не падает. Конечно, я не пытался изменить ни одного из государств-членов.

В любом случае, я хотел бы знать, может ли кто-нибудь объяснить мне, что здесь происходит. Я все еще пытаюсь найти лучший способ управлять этим, например, использовать синглтоны для Application и различных состояний, что избавило бы от необходимости использования указателей вокруг и удаления состояний, когда я закончил с ними. Но именно этот узел привлек мое внимание.

1 ответ

Решение

Если вы не пытались что-либо делать с данным состоянием после перехода в следующее состояние - тогда ничего плохого не могло произойти. Короче говоря, это похоже на вызов функции delete из функции-члена - это разрешено, если это последнее, что происходит с объектом:

void Test::deleteMe(int c)
{
   extern int b;
   int a;
   void f();
   delete this;
   // do not do this - do not touch/use this after delete
   // this->a = 7; 
   // this->f(); 
   // but you can use outer world and local variables
   a = 7;
   b = 8;
   c = 9;
   f(); 
   return; // just returns from function after delete this.
}

В мире C, который проще для понимания (в отличие от мира C++), эквивалент выглядит следующим образом:

Test* this;
....
void Test_deleteMe(Test* this)
{
   free(this);
   // this->a = 7; // do not do this
   // f(this); // and this 
   return; // just returns from function after delete this.
} 
Другие вопросы по тегам