如何安全地更改状态对象之间的状态
How can I safely change states between state objects?
我最近发布了一个问题,有人向我指出我大错特错了!
main()
我正在创建一个状态,然后将整数传递给processState()
,并根据整数更改状态(通过销毁状态并创建新状态的新实例)或保持相同的状态。
向我指出的主要问题是功能void state_t::changeState(state_t * new_state)
.我正在删除this
,然后将已删除的_state
指针指向新状态。 现在有人向我指出,这显然是一件坏事。
所以问题是:使用不同状态实现这种状态切换的最佳方法是什么? 也许_state
需要成为当前状态或类似状态的全局指针?
Main.cpp
:
#include <QCoreApplication>
#include <QDebug>
#include "statemachine.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
state_t *myState = new testState1();
myState = myState->processState(1);
myState = myState->processState(2);
myState = myState->processState(3);
myState = myState->processState(1);
return a.exec();
}
基本类型:
#include "state_t.h"
state_t::state_t(QByteArray stateName) :
name(stateName),
_state(this)
{
qDebug() << this->name << ": Creating state";
}
state_t::~state_t()
{
qDebug() << this->name << ": Deleting state";
qDebug() << endl;
}
void state_t::changeState(state_t * new_state)
{
// Check if the state has changed
if (this != new_state)
{
qDebug() << this->name << ": State changed to: " << new_state->name;
delete this;
_state = new_state;
}
else
{
qDebug() << this->name << ": State un-changed";
}
}
void state_t::unknownStateEventHandler(int event)
{
qWarning() << this->name << ": Unknown event " << event;
}
状态类(可以有很多):
#include "teststate1.h"
testState1::testState1() :
state_t("state1")
{
}
state_t *testState1::processState(int event)
{
qDebug() << name << ": event" << event;
switch (event)
{
case 2:
{
changeState(new testState2());
//changeState_t(testState2);
break;
}
default:
{
unknownStateEventHandler(event);
break;
}
}
return _state;
}
我认为你遇到的问题是你把状态的行为与状态机的行为混为一谈;即执行状态行为,而不是管理状态变化。这些应分开。
你可以有一个类来表示整个状态机。它内部没有任何特定于状态的功能。相反,它将包含指向当前状态对象的指针。(我假设您所有单独的状态类都派生自一个公共基类。
当您想要更改状态时,状态机类将删除旧状态并创建和/或存储新状态。各个状态类不应该直接执行此操作,尽管它们可以在状态机上调用函数来启动更改。
作为旁注,最好避免使用 delete this
.它在技术上有效,但通常不是一个好主意。
在您的情况下,您不应该这样做
delete this;
_state = new_state;
您应该通过以下方式将new_state的值分配给它:
*this = *new_state
但我不认为这是一个好的设计。请检查此示例。
我也在尝试实现它,我也有一些疑问,但据我所知,你犯了一些设计错误。
此设计模式定义了以下参与者:
-
上下文定义客户端感兴趣的接口,并维护
ConcreteState
定义当前状态的子类。 -
State
定义了一个接口,用于封装与特定状态关联的行为。 -
ConcreteState
子类:每个子类实现与上下文状态关联的行为。
目标是上下文不必担心它处于哪种状态:它使用通用接口将特定操作委托给ConcreteState
子类。此外,您可以通过添加新状态来扩展程序,而无需修改上下文和其余代码。
C++中的状态由Context
中的成员变量表示,您的"机器"类型为 State
,它是一个基类,可能是抽象的。定义父类类型的指针可利用多态性,允许您引用ConcreteState
子类。
例如,让我定义以下State
类(注意它引用了Context
类),定义了所有ConcreteState
子类的接口。(我将方法定义为虚拟的,这不是强制性的):
#ifndef INPUTSTATE_H
#define INPUTSTATE_H
#include "Context.hpp"
using namespace std;
class Context;
class State
{
public:
State(Context* context);
virtual void method1(int x, int y);
virtual void method2();
protected:
Context* _context;
};
#endif
然后Context
将是这样的(注State* state;
):
#ifndef INPUTESTATECONTEXT_H
#define INPUTESTATECONTEXT_H
#include "State.hpp"
class State;
class Context
{
private:
State* state;
public:
Context();
void setState(State* new_state);
};
#endif
更改状态的函数的实现如下,它将从 State 类的具体实现中调用:
void Context::setState(InputState* new_state)
{
state = new_state;
}
构造函数必须初始化state
成员:
:上下文(){ 状态 = 新状态(此);}
我们正在传递this
因为所有State
类都需要对Context
的引用。
然后我们有了基础State
的具体实施
#include "State.hpp"
class ConcreteState1 : public State
{
public:
ConcreteState1(Context* context);
void method1(int x, int y);
void method2();
};
#include "State.hpp"
class ConcreteState2 : public State
{
public:
ConcreteState1(Context* context);
void method1(int x, int y);
void method2();
};
这两个类在实现其成员函数时会有所不同。
在Context
类中,您将使用 state->method2()
调用此方法,而不必担心当前处于活动状态的状态(state
是指向当前状态的指针)。
最终回答您的问题假设在ConcreteState1::method1(int int)
中找到了一个需要将状态更改为 ConcreteState2
的条件。这可以通过以下方法实现:
_context->setState( new ConcreteState2(_context) );
更改状态后,您必须从ConcreteState
子类中清除内存,因为它不再需要。许多人用delete
来做到这一点(如果有更优雅的方式,有人纠正我):
_context->setState( new ConcreteState2(_context) );
delete this;
另请注意,所有类还需要一个适当的析构函数方法来删除使用 new
初始化的指针。
你的代码中的一个问题也是你正在做的事情:
delete this;
_state = new_state;
这将删除对象,但随后将值分配给其成员之一。我认为这行不通。
我尽量简洁,但如果有什么不清楚的地方,请告诉我。
- Constexpr替代了新的放置方式,可以让内存中的对象保持未初始化状态
- 在容器上移动分配:以前包含的对象的状态
- MATLAB:跟踪imufilter对象中的状态变化
- 是否很好地使用状态模式来维护当前选定的对象?
- "Extern"对象问题:错误:Id 返回 1 个退出状态
- 访问处于"移自"状态的对象
- 是 *this = Ctor();清除对象状态的合法和高效?
- 设计模式中对象中的过程(方法和操作)的状态
- 如何保持异步函数中使用成员的shared_ptr对象的活动状态?
- 无法访问/存储地图中的游戏状态对象
- 类的堆分配对象是否在其作用域之后但在 C++ 中调用其析构函数之前处于活动状态
- 当对象的当前状态不允许在其上进行操作时,要投掷哪个例外
- 单元测试 - 设置私人成员以获得所需的对象状态
- OpenCV 中 VideoCapture 对象的状态
- 过滤器抱怨对象保持活动状态
- 如何安全地更改状态对象之间的状态
- 在处理无状态对象时,将它们标记为const是否仍有性能优势
- boost::MPI 的 irecv() 返回未初始化的状态对象
- 如何从状态对象本身而不是外部改变状态
- 在DirectX 12中,切换管道状态对象的性能如何?