2008-09-11 110 views
3

虽然重构了一些传统的C++代码,但我发现我可能会通过某种方式删除一些代码重复,从而定义一个变量,该变量可指向任何共享相同签名的类方法。一个小挖之后,我发现我可以做类似如下:指向C++类方法的指针

class MyClass 
{ 
protected: 
    bool CaseMethod1(int abc, const std::string& str) 
    { 
     cout << "case 1:" << str; 
     return true; 
    } 

    bool CaseMethod2(int abc, const std::string& str) 
    { 
     cout << "case 2:" << str; 
     return true; 
    } 

    bool CaseMethod3(int abc, const std::string& str) 
    { 
     cout << "case 3:" << str; 
     return true; 
    } 

public: 
    bool TestSwitch(int num) 
    { 
     bool (MyClass::*CaseMethod)(int, const std::string&); 

     switch (num) 
     { 
      case 1: CaseMethod = &MyClass::CaseMethod1; 
        break; 
      case 2: CaseMethod = &MyClass::CaseMethod2; 
        break; 
      case 3: CaseMethod = &MyClass::CaseMethod3; 
        break; 
     } 

     ... 

     bool res = CaseMethod(999, "hello world"); 

     ... 

     reurn res; 
    } 
}; 

我的问题是 - 这是正确的方式去吗?我应该考虑Boost必须提供的任何东西吗?

编辑...

好了,我的错 - 我要调用该方法,像这样:

bool res = ((*this).*CaseMethod)(999, "Hello World"); 

回答

8

你在那里有一个指向成员函数的指针。它会解决你的问题。我很惊讶你的“TestSwitch”函数编译,因为调用语法与你所期望的略有不同。它应该是:

bool res = (this->*CaseMethod)(999, "hello world"); 

然而,你可能会发现的boost ::功能的组合和boost ::绑定使事情变得更容易一些,因为可以避开奇异的调用语法。

boost::function<bool(int,std::string)> f= 
    boost::bind(&MyClass::CaseMethod1,this,_1,_2); 

当然,这将其绑定到当前this指针:可以使this指针的成员函数明确的第三个参数,如果你喜欢:

boost::function<bool(MyClass*,int,std::string)> f= 
    boost::bind(&MyClass::CaseMethod1,_1,_2,_3); 

另一种方法是,以使用虚函数和派生类,但这可能需要对代码进行重大更改。

1

你当然可以做到这一点,虽然案例教学法调用不正确(这是一个指向成员函数的指针,所以你必须指定方法应该被调用的对象)。正确的调用是这样的:

bool res = this->*CaseMethod(999, "hello world"); 

在另一方面,我建议boost::mem_fn - 你可以少机会搞砸了。 ;)

0

这里给出的本地化示例没有任何内在错误,但是如果您在更广泛的上下文中使用类方法指针,那么保持“安全”通常会非常棘手,例如在类外部,它们是指针或与复杂的继承树一起使用。编译器通常管理方法指针的方式与“普通”指针不同(因为除了代码入口点外,还有额外的信息),因此对可以用它们执行的操作有很多限制。

如果你只是按照你描述的方式保持简单的指针,那么你会好起来的,但是前面更复杂的用途你可能想看看更广义的仿函数系统,如boost::bind。这些可以将指针指向任何可调用的代码指针,并且还可以根据需要绑定实例化的函数参数。

1

我没有看到你的调用和简单地调用switch语句中的方法之间的区别。

不,没有语义或可读性差异。

我看到的唯一区别是您正在使用指向某个方法的指针,因此禁止编译器将其内联或优化对该方法的任何调用。

3

你也可以建立一个查询(如果您的键程合理),这样写出来:

this->*Methods[num](999, "hello world"); 

这消除了开关,以及,使得清理更有价值一点。

1

如果没有更广泛的背景下,很难找出正确的答案,但我在这里缝三种可能性:

  • 住宿与正常switch语句,没有必要做任何事情。这是最可能的解决方案

  • 使用指向与数组结合使用的成员函数指针,如@Simon所说,或者可能带有映射。对于有大量案例的案例陈述,这可能会更快。

  • 将他的类拆分成多个类,每个类携带一个函数来调用,并使用虚函数。这可能是最好的解决方案,购买它需要一些严肃的反驳。考虑GoF模式,如国家或访问者等。

0

有可用的其它方法,如使用一个抽象基类,或专门的模板函数。

我将描述基类的想法。

您可以定义一个抽象基类

class Base { virtual bool Method(int i, const string& s) = 0; }; 

然后写出你的每一个案件作为一个子类,如

class Case1 : public Base { virtual bool Method(..) { /* implement */; } }; 

在某些时候,你会得到你的“民”变量指示要执行的测试。你可以编写一个带有这个num的工厂函数(我将其称为which_case),并返回一个指向Base的指针,然后从该指针调用Method。

Base* CreateBase(int which_num) { /* metacode: return new Case[which_num]; */ } 
// ... later, when you want to actually call your method ... 
Base* base = CreateBase(23); 
base->Method(999, "hello world!"); 
delete base; // Or use a scoped pointer. 

顺便说一句,这个应用程序使我希望C++支持静态虚拟函数,或像“类型”为内建类型 - 但事实并非如此。