我想实现对某个类的访问:
class A { some properties and methods };
问题是A可以存在多个状态,并且方法需要相应地运行。一种方法是:
class A
{
void Method1() {
if (A is in state 1) { do something }
else if (A is in state 2) { do something else }
...
}
};
如果方法被多次调用,那显然不是最优的。因此,一个易于实现的解决方案是为不同的状态创建几个类:
class A
{
class State1 {
virtual void Method1(A& a) { do something; }
...
} State1Instance;
class State2 { ... }
...
};
然后根据当前状态(例如State1Instance)管理指向对象的指针,并调用此对象的方法。这避免了CPU消耗情况。
但是国家#方法也收到了完全无用的#34;这个"指向State对象的指针。有办法避免这种情况吗?我知道差别很小,但我试图尽可能地使其最佳,并且使用CPU寄存器获得完全无意义的值并不理想。这实际上很适合用于虚拟静态"但是这是禁止的。
答案 0 :(得分:1)
如果您真的关心重复的分支,请使用好的旧函数指针,通常您不应该这样做。
struct A
{
using StateFn = void (*)(A&);
static void State1(A& a) { a.i = 42; }
static void State2(A& a) { a.i = 420; }
void Method1() { s(*this); }
StateFn s = State1;
int i;
};
如果您有多个与每个州相关联的方法,则可以构建一个方法表
struct A
{
static void State1M1(A& a) { a.i = 42; }
static void State2M1(A& a) { a.i = 420; }
static int State1M2(A& a) { return a.i * 42; }
static int State2M2(A& a) { return a.i * 420; }
// The naming sucks, you should find something better
static constexpr struct {
void (*Method1)(A&);
int (*Method2)(A&);
} State[] = {{State1M1, State1M2}, {State2M1, State2M2}};
void Method1() { State[s].Method1(*this); }
int Method2() { return State[s].Method2(*this); }
int s, i;
};
我很好奇,如果这甚至超过了switch
声明,请在采用它之前做基准测试。当你开始像第二种情况那样开始构建一个方法表时,你真的不会以一种相当不优化的方式做一些与多态不同的事情。
答案 1 :(得分:1)
如果您真的想要使用它,请使用自由或静态函数,而不是多态,并使用here封装它们。你甚至可以在这里使用lambdas。
Sub Driver()
Dim Driver As String
uiDriver = Application.InputBox("Filter out Driver", Type:=2)
If uiDriver = "False" Then Exit Sub: Rem Cancel pressed
Range("A1").Select
Selection.AutoFilter
Selection.AutoFilter field:=2, Criteria1:="<>*" & uiDriver & "*", Operator:=xlAnd
End Sub
但是,在大多数情况下,class A {
public:
::std::function<void(A*)> state = func1;
static void func1(A* that) {
::std::cout << "func1\n";
that->state = func2;
}
static void func2(A* that) {
::std::cout << "func2\n";
that->state = [](A* that) { ::std::cout << "lambda\n"; that->state = func1; };
}
public:
void method() {
state(this);
}
};
或switch
块会更好,因为它可以由编译器优化,可以将其转换为跳转表。如有疑问,请以此为基准!
答案 2 :(得分:0)
c ++ 17中开箱即用的最通用的解决方案之一,并且提升优先级是variant
类型和static_visitor
的概念。
使用c ++ 14和boost::variant
我创建了一个非常简单的状态机,它使用基于类型的代码路径切换以及自动捕获未计入的状态/事件组合。
对于更全面的解决方案,我会将您推荐给boost fsm仅限头文件库。
#include <boost/variant.hpp>
#include <iostream>
#include <typeinfo>
struct event1 {
};
struct event2 {
};
struct state_machine {
struct state1 {
};
struct state2 {
};
struct state3 {
};
using state_type = boost::variant<state1, state2, state3>;
struct handle_event {
// default case for event/state combinations we have not coded for
template<class SM, class State, class Event>
void operator()(SM &sm, State &state, Event const&event) const {
std::cout << "unhandled event "
"state=" << typeid(state).name() << " "
"event=" << typeid(event).name() << std::endl;
}
template<class SM>
void operator()(SM &sm, state1 &state, event1 const&event) const {
std::cout << "received event1 while in state1 - switching to state 2" << std::endl;
sm.change_state(state2());
}
template<class SM>
void operator()(SM &sm, state2 &state, event2 const&event) const {
std::cout << "received event2 while in state2 - switching to state 1" << std::endl;
sm.change_state(state1());
}
template<class SM>
void operator()(SM &sm, state1 &state, event2 const&event) const {
std::cout << "received event2 while in state1 - switching to state 3" << std::endl;
sm.change_state(state3());
}
};
template<class Event>
auto notify_event(Event const&evt) {
return boost::apply_visitor([this, &evt](auto& state)
{
handle_event()(*this, state, evt);
}, state_);
}
template<class NewState>
void change_state(NewState&& ns) {
state_ = std::forward<NewState>(ns);
}
private:
state_type state_ = state1{};
};
int main()
{
state_machine sm {};
sm.notify_event(event1());
sm.notify_event(event2());
sm.notify_event(event2());
// we have not coded for this one
sm.notify_event(event2());
}
示例输出(确切输出将取决于编译器ABI):
received event1 while in state1 - switching to state 2
received event2 while in state2 - switching to state 1
received event2 while in state1 - switching to state 3
unhandled event state=N13state_machine6state3E event=6event2