1

我有一个基类Base,它有方法runUserMethod(void * pointer_to_method)

class Base {
    int runUserMethod(METHTYPE pointer_to_method) {
        // do some stuff before
        int retval = pointer_to_method();
        // do some stuff after
        return retval;
    }
};

现在,我想以runUserMethod一种能够运行由 的子类实现的任何方法的方式实现Base,前提是这种方法遵循一些标准签名,例如int <methodname> (void)

例如,假设用户创建的类UserClass如下:

class UserClass : public Base {
    int user_method (void);
};

应该METHTYPE是什么,以便我可以执行以下操作?

Base   * b = new UserClass();
int retval = b->runUserMethod(&UserClass::user_method);

还有,它会起作用吗?有没有更好的方法来做到这一点?基本上,我想要做的是以对用户或多或少透明的方式拦截对子类方法调用的调用。

4

3 回答 3

2

这可以使用std::functionand来完成std::bind

struct Base
{
    typedef std::function<int()> METHTYPE;

    int runUserMethod(METHTYPE pointer_to_method)
    {
        return pointer_to_method();
    }
};

UserObject b;
auto func_object = std::bind(&UserObject::user_method, std::ref(b));

int retval = b.runUserMethod(func_object);
于 2013-02-25T16:53:28.557 回答
1

您可以尝试将runUserMethod 设为模板方法。此外,为了安全地将从基类转换为派生类,您应该使用 dynamic_cast 所以类必须是多态的(例如通过添加虚拟析构函数):

class Base {
public:

    template <class T>
    int runUserMethod( int (T::* pointer_to_method)()) {
        T* p = dynamic_cast<T*> (this);
        if (p == NULL) throw std::runtime_error("cast error");


        // do some stuff before
        int retval = (p->*pointer_to_method)();
        // do some stuff after
        return retval;
    }

    virtual ~Base() {};
};

函数用法与您的代码示例中的相同:

Base* b = new UserClass;
int r = b->runUserMethod(&User::user_method);

函数参数和返回类型也可以作为模板参数,以便 runUserMethod 接受其他函数签名:

class Base {
public:

    // 0 arguments
    template <class T, class R>
    int runUserMethod( R (T::* pointer_to_method)()) {
        T* p = dynamic_cast<T*> (this);
        if (p == NULL) throw std::runtime_error("cast error");

        // do some stuff before
        R retval = (p->*pointer_to_method)();
        // do some stuff after
        return retval;
    }

    // 1 argument
    template <class T, class R, class ARG1>
    int runUserMethod( R (T::* pointer_to_method)(ARG1), ARG1 arg1) {
        T* p = dynamic_cast<T*> (this);
        if (p == NULL) throw std::runtime_error("cast error");

        // do some stuff before
        R retval = (p->*pointer_to_method)(arg1);
        // do some stuff after
        return retval;
    }

    // 2 arguments
    // [...]

    virtual ~Base() {};
};

这样下面的代码也可以工作:

class UserClass : public Base {
public:
     int   user_method1 (void)  {return 0;}
     float user_method2 (void)  {return 0.0f;}
     int   user_method3 (int x) {return 0;}
};

int main(int argc, char* argv[]) {
    Base* b = new UserClass;
    int   r1 = b->runUserMethod(&UserClass::user_method1);
    float r2 = b->runUserMethod(&UserClass::user_method2);
    int   r3 = b->runUserMethod(&UserClass::user_method3, 2);
}
于 2013-02-25T19:30:17.223 回答
0

如果您将示例更改为更实际可行的状态,您会看到以下问题:

int retval       = (this->*methptr)();

撇开丑陋的语法不谈,问题是您需要将具有 Base * 类型的“this”指针传递给方法调用,但您假设将指针传递给派生类的方法
所以您要么需要让 runUserMethod 知道派生类(可能通过模板) 或将派生方法的指针类型转换为指向有问题风险的基方法的指针:

class Derived1 : public Base ...
class Derived2 : public Base ...
Derived2 d2;
d2.runUserMethod( static_cast<METHTYPE>( &Derived1::func ) ); // problem Derived2 * will be passed to Deriived1 method and there is no easy way to detect that

所以使用 std::bind 会更安全。而且您不必从 Base 继承(我不知道您的情况是否好)

于 2013-02-25T17:10:55.033 回答