【问题标题】:Pointers to C++ class methods指向 C++ 类方法的指针
【发布时间】:2010-09-08 12:54:32
【问题描述】:

在重构一些遗留 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" );

【问题讨论】:

    标签: c++


    【解决方案1】:

    您当然可以这样做,尽管 CaseMethod 调用不正确(它是一个指向成员函数的指针,因此您必须指定应该调用该方法的对象)。正确的调用如下所示:

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

    另一方面,我推荐boost::mem_fn - 你搞砸的机会更少。 ;)

    【讨论】:

      【解决方案2】:

      你有一个指向成员函数的指针。它会解决你的问题。我对您的“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);
      

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

      【讨论】:

        【解决方案3】:

        您在此处给出的本地化示例本质上没有任何问题,但是如果您在更广泛的上下文中使用类方法指针,例如在类之外它们是指针,则通常很难保持“安全” ,或与复杂的继承树结合使用。编译器通常管理方法指针的方式与“普通”指针不同(因为除了代码入口点之外还有额外的信息),因此对您可以使用它们做什么有很多限制。

        如果您只是按照您描述的方式保留简单的指针,那么您会没事的,但在更复杂的用途之前,您可能想看看更通用的函子系统,例如boost::bind。这些可以接受指向几乎任何可调用代码指针的指针,并且如果需要还可以绑定实例化函数参数。

        【讨论】:

          【解决方案4】:

          我看不出您的调用与仅在 switch 语句中调用方法之间的区别。

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

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

          【讨论】:

            【解决方案5】:

            您还可以构建一个查找(如果您的键范围是合理的),以便您最终编写:

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

            这也移除了开关,让清理工作更有价值。

            【讨论】:

              【解决方案6】:

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

              • 保持正常的 switch 语句,无需执行任何操作。这是最可能的解决方案

              • 如@Simon 所说,将指向成员函数的指针与数组结合使用,或者与映射结合使用。对于包含大量 case 的 case 语句,这可能会更快。

              • 将类拆分为多个类,每个类携带一个要调用的函数,并使用虚函数。这可能是最好的解决方案,购买它需要一些认真的重构。考虑 GoF 模式,例如 State 或 Visitor 等。

              【讨论】:

                【解决方案7】:

                还有其他可用的方法,例如使用抽象基类或专门的模板函数。

                我将描述基类的想法。

                你可以定义一个抽象基类

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

                然后把你的每一个case写成一个子类,比如

                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”这样的内置类型 - 但它没有。

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2010-12-05
                  • 1970-01-01
                  • 2015-04-18
                  • 2017-04-13
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多