3

我最近发布了一个问题,有人指出我错了!

main()我正在创建一个状态,然后将整数传递给processState()并基于整数它将更改状态(通过破坏状态并创建新状态的新实例),或保持相同的状态。

向我指出的主要问题是 function 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;
}
4

3 回答 3

4

我认为您遇到的问题是您将状态的行为与状态机的行为混淆了;即执行状态行为,与管理状态变化。这些应该分开保存。

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

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

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

于 2013-10-30T15:57:19.123 回答
1

在你的情况下,你不应该这样做

delete this;
_state = new_state;

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

*this = *new_state

但我不认为这是一个好的设计。请检查这个例子

于 2013-10-30T15:58:28.550 回答
0

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

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

  • context定义了客户感兴趣的接口,并维护了一个 ConcreteState定义当前状态的子类的实例。
  • State定义了一个接口,用于封装与特定状态相关的行为。
  • ConcreteState子类:每个子类实现与上下文状态相关的行为。

目标是上下文不必担心它处于哪个状态:它使用通用接口将特定操作委托给ConcreteState子类。此外,您可以通过添加新状态来扩展程序,而无需修改上下文和其余代码。

ContextC++ 中的状态由“机器”中的成员变量表示,该类型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成员:

上下文::上下文() { state = new State(this); }

我们通过this是因为所有State类都需要引用Context.

然后我们有了base的具体实现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;

这会删除该对象,但随后您正在为其成员之一分配一个值。我认为这行不通。

我尽量简洁,但如果有不清楚的地方请告诉我。

于 2018-12-14T16:54:19.903 回答