samedi 10 juillet 2021

Segmentation fault when implementing state pattern in C++

I'm trying to implement State pattern in C++. Here an example code:

#include <cstdio>

class Context;

class State {
public:
    virtual ~State() = default;

    explicit State(Context *context) : context_(context) {}

    virtual void Print() = 0;

    virtual void ChangeState() = 0;

protected:
    Context *const context_;
};

class Context {
public:
    void SwitchState(State *state) {
        delete this->current_state_;
        this->current_state_ = state;
    }

    State *GetCurrentState() const {
        return this->current_state_;
    }

private:
    State *current_state_ = nullptr;
};

class FirstState : public State {
public:
    explicit FirstState(Context *context) : State(context) {}

    ~FirstState() override {
        printf("FirstState destructed. Address: %p\n", this);
    }

    void Print() override {
        printf("First state print..\n");
    }

    void ChangeState() override;
};

class SecondState : public State {
public:
    explicit SecondState(Context *context) : State(context) {}

    void Print() override {
        printf("Second state print..\n");
    }

    void ChangeState() override;
};

void FirstState::ChangeState() {
    printf("First state changed to second state\n");
    this->context_->SwitchState(new SecondState(this->context_));
    printf("First state changed to first state\n");
    this->context_->SwitchState(new FirstState(this->context_));
    printf("First state changed to second state\n");
    this->context_->SwitchState(new SecondState(this->context_));
}

void SecondState::ChangeState() {
    this->context_->SwitchState(new FirstState(this->context_));
    this->context_->SwitchState(new SecondState(this->context_));
    this->context_->SwitchState(new FirstState(this->context_));
}

int main() {
    Context context;

    context.SwitchState(new FirstState(&context));

    context.GetCurrentState()->Print();
    context.GetCurrentState()->ChangeState();
    context.GetCurrentState()->Print();
    context.GetCurrentState()->ChangeState();

    return 0;
}

I have a segmentation fault error because in this implementation after calling Context::SwitchState from the State, the current state is deleted and after returning from SwitchState method I can't use this, but sometimes I have to. I tried to use shared pointers, it helped a little, because I didn't get segmentation fault anymore, but for some reason destructor of the state is called anyway. I need to create new instances of states when switching it because I need to pass some arguments to the changed state. Also, I don't know how to solve this problem in general because all references to the state are lost after switching state, only remaining reference is this. I have some bad ideas that may solve that problem, but they are ugly, for example to make SwitchState just saving new state, and call some RealSwitchSavedState at the end of every method in state. I think it would solve the problem, because in this case a reference to the state remains until the end of the method, but the code will be ugly.

Hope for your help! Thanks in advance!

Aucun commentaire:

Enregistrer un commentaire