Question

I'm experimenting with using a finite state machine as a model for managing the flow of a simple game. Enter into a Main Menu state, from which you select say starting a game or modifying options, etc.

The way I'm doing it is by creating a base State class, from which each state inherits. I have an application class that manages the program loop, and add a pointer to a current state. The application itself has a changeState method that exits the current states and enters the next one. When entering a state I give the state a pointer to the application class, and each state holds the logic to switch to the next state.

This causes a situation where I'm not sure what is happening, or rather what should happen.

In particular: The program polls for events. The event is handed to the current state for processing. If the input dictates a switch to the next state, I call the changeState function. The change state function deletes the current state and loads the next one.

My confusion is, if I'm deleting the state from which I called the change state function, what happens when I return from the change state function? A bit of simplified code to show what I mean more clearly:

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;
}

Tried to put only the important bits in there. Anyway, what I see happening then is: I instance the Application. I then call the public run() method, which calls Initialize() to set the m_running variable to true and calls changeState to a new BaseState. changeState gives the state a pointer to this, so the event can access the information of the application.

The run method then polls for events, and when it detects one it sends it to the current state for processing.

If an event calls for a change in state, it calls m_Application->changeState( new StateBase );

And here is where I'm confused. changeState() calls delete on m_currentState, which is the instance of StateBase making the call. When control returns from changeState() it goes to an event that should have been deleted. However, I tested it and it doesn't crash. Of course, I didn't try to modify any of the states members either.

Anyway, I'd like to know if anyone can explain to me what's going on here. I'm still trying to figure out a better way to manage this, such as using singletons for Application and the different States, which would eliminate the need of handing pointers around and deleting states when I'm done with them. But this particular knot caught my attention.

Was it helpful?

Solution

If you did not try to do anything with the given state after changing to next state - then nothing wrong could happen. In short it is like called delete from member function - it is allowed if this is the last thing which happens to the object:

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.
}

In C-world which is simpler to understand (in opposite to C++ world) the equivalent is as follows:

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.
} 
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top