如何安全地更改状态对象之间的状态?

时间:2013-10-30 15:42:05

标签: c++ state

我最近发了一个问题,有人向我指出我的情况非常糟糕!

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

3 个答案:

答案 0 :(得分:4)

我认为你遇到的问题是你将状态行为与状态机的行为混为一谈;即执行状态行为,管理状态变化。这些应该分开。

您可以拥有一个代表您的状态机整体的类。它内部没有任何特定于州的功能。相反,它将包含指向当前状态对象的指针。 (我假设你所有的个别州级派生都来自一个共同的基类。)

当您想要更改状态时,状态机类将删除旧状态并创建和/或存储新状态。各个州的类不应该直接这样做,尽管他们可以调用状态机上的函数来启动更改。

作为旁注,最好避免使用delete this。它在技术上有效,但通常不是一个好主意。

答案 1 :(得分:1)

在你的情况下,你不应该

delete this;
_state = new_state;

您应该通过以下方式将new_state的值分配给:

*this = *new_state

但我不认为这是一个好的设计。请查看此example

答案 2 :(得分:0)

我也在尝试实现它,我也有一些疑问,但是据我了解,您犯了一些设计错误。

此设计模式定义了以下参与者

  • 上下文定义客户端感兴趣的界面并维护一个实例 定义当前状态的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 ):

void Context::setState(InputState* new_state)
{
  state = new_state;
}

更改状态的功能的实现如下,它将从State类的具体实现中调用:

state

构造函数必须初始化this成员:

Context :: Context() {   状态=新状态(此); }

我们正在传递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; 初始化的指针。

您的代码中还有一个问题是您在做

T

此操作将删除对象,但随后您为其成员之一分配了一个值。我认为这行不通。

我试图简明扼要,但是如果不清楚,请告诉我。

相关问题