状态设计模式 - 不想删除成员类中的此指针

State Design Pattern - Don't want delete this pointer in member class

本文关键字:指针 成员类 删除成员 设计模式 不想 删除 状态      更新时间:2023-10-16

我使用下面的例子来实现一个状态设计模式。

https://sourcemaking.com/design_patterns/state/cpp/1

我不想删除成员类中的*this指针,因为不安全(删除后我会调用其他成员函数)。

class ON: public State
{
  public:
    ON()
    {
        cout << "   ON-ctor ";
    };
    ~ON()
    {
        cout << "   dtor-ONn";
    };
    void off(Machine *m);
};
class OFF: public State
{
  public:
    OFF()
    {
        cout << "   OFF-ctor ";
    };
    ~OFF()
    {
        cout << "   dtor-OFFn";
    };
    void on(Machine *m)
    {
        cout << "   going from OFF to ON";
        m->setCurrent(new ON());
        delete this; // <<< This line looks suspect and unsafe
    }
};

void ON::off(Machine *m)
{
  cout << "   going from ON to OFF";
  m->setCurrent(new OFF());
  delete this; // <<< This line looks suspect and unsafe
}

有没有更好的方法来实现状态设计模式?我考虑过使用singleton,但是我想避免使用singleton。

问好,

链接的设计为:

  • 状态机跟踪指向当前状态的指针。
  • 当前状态负责在发生转换时设置新状态
  • 通过创建新状态并指示机器改变指针
  • 来实现。
  • 一旦完成,它就会通过删除自己来自杀。

如本文所述,如果格外小心,这是可以工作的。您的代码符合先决条件。

另一种选择是状态机删除setCurrent()中的当前状态。但更糟糕的是:一旦机器删除了对象,该对象就不再存在,因此当setCurrent()返回时,您实际上处于与之前(delete this)相同的(危险)情况,重要的区别是这不会很明显。

编辑:

如果您对这个结构感到不舒服,您可以选择一个变体:

class Machine
{
   class State *current;     // current state
   class State *nextstate;   // null, unless a state transition was requested
   void check_transition();  // organise switch to nextstate if transition is needed 
public:
    Machine();
    void setCurrent(State *s)
    {
        nextstate = s;  // only sets the info about nextstate 
    }
    void set_on();
    void set_off();
}; 

在这个场景中,需要稍微更新状态机函数:

void Machine::set_on()
{
   current->set_on(this);  // as before
   check_transition();     // organise the transition if the transition was requested
}

状态转换将由状态机管理:

void Machine::check_transition() { 
       if (nextstate) {
           swap (current, nextstate);
           delete nextstate;  // this contains the former current  
           nextstate = nullptr; 
       }
   }

机器组织删除未使用的状态。注意,这种方法允许使用shared_ptr而不是原始指针。

这里有一个小的在线概念证明。

顺便说一句:机器析构函数也应该删除当前和下一个状态,以避免泄漏。在任何情况下,状态析构函数都应该定义为virtual

"…但我想避免使用单例。"

这可能是它们罕见的有效用例之一,因为状态本身应该无状态(这并不意味着它们不需要实例),并且无论当前状态机的上下文如何,都可以并发访问。
这就意味着你每次只需要一个状态的实例。

上述网站上显示的示例也会给您带来不必要的性能损失,其代价是每当状态发生变化时,newdelete都要付出代价,可以通过为状态提供稳定的static实例来避免这种情况。

实际上你可以考虑像轻量级设计模式那样提供状态实例,它本质上归结为拥有单例状态实例。


但是,这要看情况。UML状态图实际上允许有非状态状态(作为具有历史属性的组合状态,或活动状态)。

看一下我的STTCL模板库概念文档,它解释了我开发模板库所使用的一些方面和设计决策,以及如何正确使用它。
请放心,我没有任何一个delete this;在那里;-)。


我想说这个网站目前给出的例子,设计得真的很糟糕,一般不推荐1
正如你所说,使用delete this是不合适的,危险的,不可接受的。

当使用单例时,如果你手头有它们的有效用例。


1) 遗憾地注意到这一点,因为我一直使用它作为许多设计模式相关问题的"参考"