我正在构建一个“系统”,以便稍后我可以在“基于动作”的环境中编写。我想要的是给我的实例(即“汽车”)一个特定的状态(即停车,驾驶,开始)。然后在某些事件中,它应该根据状态执行代码。
我不想使用switch / if-then-else语句,因为这是非常错误的&很难延伸。 (允许更多的州)。相反,我想使用函数指针。
我拥有的代码(忽略函数的愚蠢,他们要演示):
#define CALL_MEMBER_FN(object,ptrToMember) ((object).*(ptrToMember))
enum State {Set, Add, Mul};
class car {
public:
typedef void (car::*MemFn)(int v);
car(int v, State _s) : val(v), s(_s) {
posFunctions.insert(std::make_pair(State(Set),&car::SetVal));
posFunctions.insert(std::make_pair(State(Add),&car::AddVal));
}
void DoIt(int v) {
//MemFn t = &car::SetVal;
MemFn t = posFunctions.find(s)->second;
CALL_MEMBER_FN(*this,t)(v);
}
int val;
State s;
protected:
std::map<State,car::MemFn> posFunctions;
void SetVal(int v) {
val = v;
}
void AddVal(int v) {
val += v;
}
void MulVal(int v) {
val *= v;
}
};
这正如我所料。 (我可以调用创建一个汽车对象,给它一个特定的状态然后调用“doit - 这将是由事件触发的函数”来执行操作)。
然而,有一件非常讨厌的事情:我必须为每个汽车对象独立创建功能图(用映射状态和要执行的功能)。这实际上不是“实际”(实际上,当状态和事件相同时,每辆车总是执行相同的操作),因此容易出错/丑陋。
我尝试将“posFunctions”设为静态映射,并对构造函数中的2行使用静态初始化函数。
main.obj:错误LNK2001:未解析的外部符号“protected:static class std :: map,class std :: allocator&gt;&gt; car :: posFunctions”(?posFunctions @ car @@ 1V?$ map @ W4State @@ P8car @@ @ AEXH ZU?$ @少@@@ W4State STD @@ V'$分配器@ U&$对@ $$ CBW4State @@ P8car @@ AEXH @ Z @ STD @@@ 4 @@ @性病@一个) 我想这是因为我访问一个非静态成员函数 - 即使它是指向的?
是否可以将地图设为静态(或全局)?
谢谢你的帮助, paul23
答案 0 :(得分:3)
答案 1 :(得分:2)
您忘记在全球范围内定义地图,即
std::map<State,car::MemFn> car::posFunctions;
在您定义汽车的.cpp文件中。
一般来说,当你制作这样的结构时,问问自己 - 你能用多态来做到吗?
答案 2 :(得分:1)
这是可能的 - 你必须在一个地方定义静态变量(最有可能是这个类的CPP文件)。
std::map<State,car::MemFn> car::posFunctions;
头文件中的内容只是声明。
这类似于您必须在.CPP文件中为标题中声明的每个函数提供函数定义的方式。如果您错过了任何函数体,链接器将在每个缺少的函数上给出相同的错误。
要按需初始化此结构,请提供从类构造函数调用的静态成员函数,但检查(如果需要,以线程安全的方式)是否已在之前调用它。
void car::initFunctions()
{
static bool done(false);
if (done)
return;
// first pass, set up the map
done = true;
}