将普通类成员转换为静态类成员

时间:2010-11-19 16:53:13

标签: c++ static

我正在构建一个“系统”,以便稍后我可以在“基于动作”的环境中编写。我想要的是给我的实例(即“汽车”)一个特定的状态(即停车,驾驶,开始)。然后在某些事件中,它应该根据状态执行代码。

我不想使用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

3 个答案:

答案 0 :(得分:3)

只要您声明map静态,就需要在实现文件中添加以下行:

std::map<State,car::MemFn> car::posFunctions;

以下是相关C++ FAQ项目的链接。

答案 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;
}