简体   繁体   English

如果方法被覆盖,C ++基类如何在运行时确定?

[英]How can a C++ base class determine at runtime if a method has been overridden?

The sample method below is intended to detect whether or not it has been overridden in a derived class. 下面的示例方法旨在检测它是否已在派生类中被覆盖。 The error I get from MSVC implies that it is simply wrong to try to get the function pointer to a "bound" member, but I see no logical reason why this should be a problem (after all, it will be in this->vtable ). 我从MSVC得到的错误意味着尝试将函数指针指向“绑定”成员是完全错误的,但我认为没有合理的理由说明为什么这应该是一个问题(毕竟,它将在this-> vtable中 )。 Is there any non-hacky way of fixing this code? 是否有任何非hacky方法来修复此代码?

class MyClass
{
public:
    typedef void (MyClass::*MethodPtr)();  

    virtual void Method()
    {
        MethodPtr a = &MyClass::Method; // legal
        MethodPtr b = &Method;  // <<< error C2276: ‘&’ : illegal operation on bound member function expression

        if (a == b)     // this method has not been overridden?
            throw “Not overridden”;
    }
};

There is no way to determine if a method has been overridden, except for pure virtual methods: they must be overridden and non-pure in a derived class. 除了纯虚方法之外,无法确定是否已重写方法:它们必须在派生类中被重写和非纯。 (Otherwise you can't instantiate an object, as the type is still "abstract".) (否则你无法实例化一个对象,因为该类型仍然是“抽象的”。)

struct A {
  virtual ~A() {} // abstract bases should have a virtual dtor
  virtual void f() = 0; // must be overridden
}

You can still provide a definition of the pure virtual method, if derived classes may or must call it: 如果派生类可能或必须调用它,您仍然可以提供纯虚方法的定义:

void A::f() {}

Per your comment, "If the method had not been overridden it would mean it is safe to try mapping the call to the other method instead." 根据你的评论,“如果方法没有被覆盖,那就意味着尝试将调用映射到另一种方法是安全的。”

struct Base {
  void method() {
    do_method();
  }

private:
  virtual void do_method() {
    call_legacy_method_instead();
  }
};

struct Legacy : Base {
};

struct NonLegacy : Base {
private:
  virtual void do_method() {
    my_own_thing();
  }
};

Now, any derived class may provide their own behavior, or the legacy will be used as a fallback if they don't. 现在,任何派生类都可以提供自己的行为,否则遗产将被用作后备,如果他们不这样做。 The do_method virtual is private because derived classes must not call it. do_method虚拟是私有的,因为派生类不能调用它。 (NonLegacy may make it protected or public as appropriate, but defaulting to the same accessibility as its base class is a good idea.) (NonLegacy可能会使其受到保护或公开适当,但默认为与其基类相同的可访问性是一个好主意。)

You can actually find this out. 你实际上可以找到它。 We encountered the same problem and we found a hack to do this. 我们遇到了同样的问题,我们找到了一个黑客来做到这一点。

#include<iostream>
#include<cstdio>
#include<stdint.h>

using namespace std;

class A {
public:
    virtual void hi(int i) {}
    virtual void an(int i) {}
};

class B : public A {
public:
    void hi(int i) {
        cout << i << " Hello World!" << endl;
    }
};

We have two classes A and B and B uses A as base class. 我们有两个类ABB使用A作为基类。

The following functions can be used to test if the B has overridden something in A 以下函数可用于测试B是否覆盖了A某些内容

int function_address(void *obj, int n) {
    int *vptr = *(int **)&obj;
    uintptr_t vtbl = (uintptr_t)*vptr;

    // It should be 8 for 64-bit, 4 for 32-bit 
    for (int i=0; i<n; i++) vtbl+=8;

    uintptr_t p = (uintptr_t) vtbl;
    return *reinterpret_cast<int*>(p);
}

bool overridden(void *base, void* super, int n) {
    return (function_address(super, n) != function_address(base, n));
}

The int n is the number given to method as they are stored in vtable. int n是方法,因为它们存储在vtable中。 Generally, it's the order you define the methods. 通常,它是您定义方法的顺序。

int main() {
    A *a = new A();
    A *b = new B();

    for (int i=0; i<2; i++) {
        if (overridden(a, b, i)) {
            cout << "Function " << i << " is overridden" << endl;
        }
    }

    return 0;
}

The output will be 输出将是

Function 0 is overridden

EDIT: We get the pointers to vtables for each class instance and then compare the pointer to the methods. 编辑:我们得到每个类实例的vtables指针,然后比较指向方法的指针。 Whenever a function is overridden, there will be a different value for super object. 每当覆盖一个函数时,超级对象都会有不同的值。

There's no portable way of doing that. 没有便携式的方法。 If your intention is to have a method that is not pure virtual, but needs to be overridden for every class it will be called on you can just insert an assert( false ) statement into the base class method implementation. 如果你的目的是让一个非纯虚方法,但需要为每个调用它的类重写,你只需要在基类方法实现中插入一个assert( false )语句。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM