在标准State模式的实现中,有几个地方让人觉得不爽。首先,所有状态对象要处理的消息都必须在抽象状态类中申明;其次由于所有消息处理函数都是虚拟函数,一方面虚拟函数表占用内存,另一方面增加了调用的间接性,导致效率有所降低(当然这是相对的,也可能不是什么大问题)。
在实际运用当中,有一种公认的比较高效的状态机实现,其核心就是:把状态当成成员函数(指针)!此话怎讲?还是看看代码来得直接。
在:《Practical Statecharts in C/C++ Quantum Programming for Embedded Systems》一书中,作者Miro Samek博士提供了一个实现,我这里简单列一下:
class QFsm {
public:
typedef void (QFsm::*QFsmState)(QEvent const *);
QFsm(QFsmState initial) {
myState = initial;
}
virtual ~QFsm() {
}
void init(QEvent const *e = 0) {
(this->*myState)(e);
}
void dispatch(QEvent const *e) {
(this->*myState)(e);
}
QFsmState getState() const {
return myState;
}
static char const *getVersion();
protected:
void tran(QFsmState target) {
myState = target;
}
#define QFSM_TRAN(target_) (tran(Q_STATIC_CAST(QFsmState, target_)))
private:
QFsmState myState;
};
我们看到,QFsm代表一个平面状态机基类,它保存了当前的状态myState,而这个当前状态的类型是QFsmState,查看定义可以知道它是一个类成员函数指针。当这个状态机对象初始化时,它给用户提供一个Hook,让用户决定转换到什么初始状态(用户必须提供initial初始状态转换函数)。以后当有消息进来时,状态机直接委托给当前状态处理(通过dispatch函数)。状态的跃迁是非常轻量级的,只是一个类成员函数指针的赋值。
我们来看看要怎样使用这个状态机基类:
class MyFsm : public QFsm {
public:
MyFsm () : QFsm((QFsmState)initial){}
protected:
void initial(QEvent const *e);
void State1(QEvent const *e);
void State2(QEvent const *e);
void State3(QEvent const *e);
//...
private:
//private date and method
};
void BasicComponent::initial (CMessage *pMsg)
{
//...
QFSM_TRAN(&MyFsm::State1);
}
用户在写自己的状态机时,必须提供initial函数,并且在里面调用QFSM_TRAN跃迁到初始状态,接下来所有发给状态机处理的事件都交由这个状态处理,在里面可以跃迁到其他状态,实现状态机的状态转换图。
可以看到,没有庞大的虚函数表从而节省内存,并且状态转换非常便捷,同时这种实现也省略了枚举状态的开销,简直是居家旅行之必备良药。
在实际运用当中,有一种公认的比较高效的状态机实现,其核心就是:把状态当成成员函数(指针)!此话怎讲?还是看看代码来得直接。
在:《Practical Statecharts in C/C++ Quantum Programming for Embedded Systems》一书中,作者Miro Samek博士提供了一个实现,我这里简单列一下:
class QFsm {
public:
typedef void (QFsm::*QFsmState)(QEvent const *);
QFsm(QFsmState initial) {
myState = initial;
}
virtual ~QFsm() {
}
void init(QEvent const *e = 0) {
(this->*myState)(e);
}
void dispatch(QEvent const *e) {
(this->*myState)(e);
}
QFsmState getState() const {
return myState;
}
static char const *getVersion();
protected:
void tran(QFsmState target) {
myState = target;
}
#define QFSM_TRAN(target_) (tran(Q_STATIC_CAST(QFsmState, target_)))
private:
QFsmState myState;
};
我们看到,QFsm代表一个平面状态机基类,它保存了当前的状态myState,而这个当前状态的类型是QFsmState,查看定义可以知道它是一个类成员函数指针。当这个状态机对象初始化时,它给用户提供一个Hook,让用户决定转换到什么初始状态(用户必须提供initial初始状态转换函数)。以后当有消息进来时,状态机直接委托给当前状态处理(通过dispatch函数)。状态的跃迁是非常轻量级的,只是一个类成员函数指针的赋值。
我们来看看要怎样使用这个状态机基类:
class MyFsm : public QFsm {
public:
MyFsm () : QFsm((QFsmState)initial){}
protected:
void initial(QEvent const *e);
void State1(QEvent const *e);
void State2(QEvent const *e);
void State3(QEvent const *e);
//...
private:
//private date and method
};
void BasicComponent::initial (CMessage *pMsg)
{
//...
QFSM_TRAN(&MyFsm::State1);
}
用户在写自己的状态机时,必须提供initial函数,并且在里面调用QFSM_TRAN跃迁到初始状态,接下来所有发给状态机处理的事件都交由这个状态处理,在里面可以跃迁到其他状态,实现状态机的状态转换图。
可以看到,没有庞大的虚函数表从而节省内存,并且状态转换非常便捷,同时这种实现也省略了枚举状态的开销,简直是居家旅行之必备良药。