(c++)如何指向子类中的方法

(C++) How to point to a method in a subclass?

本文关键字:子类 方法 何指 c++      更新时间:2023-10-16

我有一个基类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);

同样,它会工作吗?有更好的方法吗?基本上,我要做的是拦截子类方法调用的调用,以一种或多或少对用户透明的方式。

可以使用std::functionstd::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);

你可以尝试让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);
}

如果你改变你的例子在更真实可行的状态,你会看到这个问题:

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继承(我不知道这对你是否合适)