время жизни объекта состояния unique_ptr в конечном автомате при нажатии нового состояния в методе ввода - PullRequest
0 голосов
/ 10 апреля 2019

Я создаю конечный автомат, в нескольких состояниях которого он переключается в новое (следующее) состояние в методе enter().Состояния - это unique_ptr объекты, которые создаются по мере необходимости и передаются машине.

Поскольку некоторые состояния переключаются на следующее состояние в методе enter() (таким образом, при использовании), я обеспокоен возможными проблемами здесь - когда текущее состояние вызывает set_state(), конечный автомат назначает новое состояниеего член state, таким образом, теряет единственный указатель на состояние, из которого был сделан этот вызов.

Ниже приведен пример моего беспокойства - единственный unique_ptr для obj A и B установлен на Cв то время как рекурсивно вызывает это действие от A, а затем B. Является ли это надежным?Это вызовет какие-либо проблемы?

#include <memory>
#include <iostream>

class IState;
class IStateMachine {
public:
    virtual ~IStateMachine() = default;
    virtual void set_state(std::unique_ptr<IState> new_state) = 0;
};

class IState {
public:
    virtual ~IState() = default;
    virtual void enter(IStateMachine&) = 0;
};

class StateC : public IState {
public:
    void enter(IStateMachine&) override {
        std::cout <<  __func__ << ": StateC - start" << std::endl;
        std::cout <<  __func__ << ": StateC - end" << std::endl;
    }
};

class StateB : public IState {
public:
    void enter(IStateMachine& sm) override {
        std::cout <<  __func__ << ": StateB - start" << std::endl;
        sm.set_state(std::make_unique<StateC>());
        std::cout <<  __func__ << ": StateB - end" << std::endl;
    }
};

class StateA : public IState {
public:
    void enter(IStateMachine& sm) override {
        std::cout <<  __func__ << ": StateA - start" << std::endl;
        sm.set_state(std::make_unique<StateB>());
        std::cout <<  __func__ << ": StateA - end" << std::endl;
    }
};

class StateMachine : public IStateMachine {
public:
    void start() {
        set_state(std::make_unique<StateA>());
    }

    void set_state(std::unique_ptr<IState> new_state) {
        state_ = std::move(new_state);
        state_->enter(*this);
    }

    std::unique_ptr<IState> state_;
};

int main()
{
    StateMachine sm;
    sm.start();
}

Вывод кода:

enter: StateA - start
enter: StateB - start
enter: StateC - start
enter: StateC - end
enter: StateB - end
enter: StateA - end

Редактировать 1:

Основная идея заключается в следующемчто состояния создаются по мере необходимости и автоматически уничтожаются после того, как они больше не нужны.Ограничение состоит в том, что состояние может выполнять некоторую работу в своем методе enter(), а затем в конце этого метода переключать конечный автомат в следующее состояние.

Редактировать 2:

Я не удаляю объект явно.Мой вопрос больше касается времени жизни объекта, на который указывает unique_ptr в случае, когда новый объект назначен этому unique_ptr из собственного метода объекта (рекурсивно) (см. Пример кода).

1 Ответ

0 голосов
/ 11 апреля 2019

Вы можете попробовать добавить промежуточный шаг:

В методах enter() вместо прямой отправки нового состояния на ваш конечный автомат (который удаляет текущее состояние, которое фактически вызывает ваш компьютер для изменения состояния), вы можете указать компьютеру, какое состояние вы хотите толкать. Это сделало бы своего рода ожидающий толчок в ожидании сброса.

Для этого у вас может быть перечисление и карта, связывающая каждого члена перечисления с фактическим состоянием. Затем вы могли бы справиться с ожидающим push, посмотрев, в каком состоянии вы хотите нажать непосредственно в конечном автомате.

Но для этого необходимо снова вернуться к машине, чтобы государства не могли выполнять всю работу самостоятельно.

Это будет выглядеть примерно так:

enum States
{
    State_A,
    //...
};

class StateMachine : public IStateMachine {
public:
    StateMachine () {
        fabric[State_A] = [] () { return std::unique_ptr<IState>(new StateA()); }
       // ....
    }

    void push(States state) { 
        pendingState_ = std::move(state); 
    }

    void update()
    {
        auto found = fabric_.find(pendingState_);
        assert(found != fabric_.end());
        state_ = found->second();
        state_->enter(*this);
    }

   // ... same as before ...
private:
    States                  pendingState_;
    std::map<States, std::function<std::unique_ptr<IState>()> > fabric_;
};

Теперь в методах ввода это будет:

sm.push(/*a state*/);

и в основном вы заставляете его свернуться в петлю. Это означает, что вам также необходимо реализовать способ проверки выполнения задания, например, Вы могли бы сделать член enum None и проверить, является ли состояние машины нулевым или нет.

...