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

问题描述 投票:0回答:1

我正在创建一个状态机,其中几个状态正在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的新对象(参见示例代码)。

c++ unique-ptr state-machine
1个回答
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*/);

在主要的,你让它循环滚动。这意味着您还需要实施一种方法来检查作业何时完成,例如你可以成为enum None的成员,并检查机器的状态是否为空。

© www.soinside.com 2019 - 2024. All rights reserved.