在enter方法中推送新状态时,状态机中的unique_ptr状态对象生存期

时间:2019-04-10 19:43:52

标签: c++ unique-ptr state-machine

我正在创建一个状态机,几乎没有哪个状态可以通过enter()方法将其切换到新的状态。状态是unique_ptr对象,它们根据需要创建并提供给计算机。

随着某些状态以enter()方法切换到下一个状态(因此,在使用时),我担心这里可能存在的问题-当当前状态调用set_state()时,状态机会分配一个新状态指向其state成员的位置,因此丢失了指向进行此调用的状态的唯一指针。

以下是我关注的一个示例-obj A和B的唯一unique_ptr设置为指向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分配一个新对象(请参见示例代码)。 / p>

1 个答案:

答案 0 :(得分:0)

您可以尝试添加一个中间步骤:

enter()方法中,您可以告诉机器什么类型的状态,而不是直接将新状态推送到状态机(这会删除实际上要求您的机器更改状态的当前状态)。你想推动。这将使待处理的推送等待被删除。

要这样做,您可以有一个枚举和一个将枚举的每个成员与实际状态相关联的映射。然后,您可以处理待处理的推送,以查看要直接在状态机中推送的状态。

但是,这样做需要再次回到机器上,所以各州不能独自完成所有工作。

它看起来像这样:

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

现在在enter方法中,应该是:

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

并在主循环中使其滚动。因此,这意味着您还需要实现一种检查工作完成时间的方法,例如您可以成为枚举None的成员,并检查计算机的状态是否为无。