我正在创建一个状态机,其中几个状态正在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
的新对象(参见示例代码)。
您可以尝试添加中间步骤:
在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
的成员,并检查机器的状态是否为空。