指向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" );
解决方案
回答
尽管CaseMethod调用不正确(它是指向成员函数的指针,所以我们必须指定应在其上调用该方法的对象),但是我们当然可以做到。正确的调用如下所示:
bool res = this->*CaseMethod( 999, "hello world" );
另一方面,我建议使用boost :: mem_fn,这样我们将有更少的机会搞砸它。 ;)
回答
我们所拥有的是一个指向成员函数的指针。它将解决问题。使" TestSwitch"函数编译,使我感到惊讶,因为调用语法与我们期望的语法略有不同。它应该是:
bool res = (this->*CaseMethod)( 999, "hello world" );
但是,我们可能会发现boost :: function和boost :: bind的组合使事情变得容易一些,因为可以避免使用奇怪的调用语法。
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);
另一种选择是使用虚函数和派生类,但这可能需要对代码进行重大更改。
回答
我们在这里给出的本地化示例没有本质上的错误,但是如果在更广泛的上下文中使用它们(例如,在类外部,它们是该类的指针)与复杂的继承树结合。编译器通常管理方法指针的方式与"普通"指针不同(因为除了代码入口点以外,还有其他信息),因此,我们对它们的操作有很多限制。
如果我们只是按照描述的方式保持简单的指针,那么我们会很好,但是在更复杂的用途上,我们可能希望看一下更通用的函子系统,例如boost :: bind。它们可以将指针指向几乎所有可调用的代码指针,并且在必要时还可以绑定实例化函数参数。
回答
我看不出调用与仅在switch语句中调用方法之间的区别。
不,没有语义或者可读性差异。
我看到的唯一区别是我们正在使用指向方法的指针,因此禁止编译器内联它或者优化对该方法的任何调用。
回答
我们还可以构建一个查找(如果键范围是合理的),以便最终编写:
this->*Methods[num]( 999, "hello world" );
这也将删除该开关,并使清理工作更有价值。
回答
没有更广阔的背景,很难找到正确的答案,但是我在这里缝了三种可能性:
- 保持正常的switch语句,无需执行任何操作。这是最可能的解决方案
- 如@Simon所说,可以将指向成员函数的指针与数组结合使用,也可以与地图一起使用。对于具有大量案例的案例陈述,这可能会更快。
- 将类分为多个类,每个类都带有一个要调用的函数,并使用虚函数。这可能是最好的解决方案,购买它需要认真考虑。考虑诸如州或者访客之类的GoF模式。
回答
还有其他可用的方法,例如使用抽象基类或者专用模板函数。
我将描述基类的想法。
我们可以定义一个抽象基类
class Base { virtual bool Method(int i, const string& s) = 0; };
然后将每个案例写为一个子类,例如
class Case1 : public Base { virtual bool Method(..) { /* implement */; } };
在某个时候,我们将获得" num"变量,该变量指示要执行的测试。我们可以编写一个使用此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 ++支持静态虚拟函数,或者像" type"这样的内置类型,但事实并非如此。