5

下面的示例方法旨在检测它是否已在派生类中被覆盖。我从 MSVC 得到的错误意味着尝试将函数指针指向“绑定”成员是完全错误的,但我认为这应该是一个问题没有任何合乎逻辑的理由(毕竟,它将在this->vtable中)。有没有修复此代码的非 hacky 方法?

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

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

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

3 回答 3

4

除了纯虚方法外,没有办法确定方法是否已被覆盖:它们必须在派生类中被覆盖且非纯。(否则你不能实例化一个对象,因为类型仍然是“抽象的”。)

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

如果派生类可能或必须调用它,您仍然可以提供纯虚方法的定义:

void A::f() {}

根据您的评论,“如果该方法未被覆盖,则意味着尝试将调用映射到其他方法是安全的。”

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();
  }
};

现在,任何派生类都可以提供自己的行为,或者如果他们不这样做,遗留类将用作后备。do_method virtual 是私有的,因为派生类不能调用它。(NonLegacy 可能会酌情使其受保护或公开,但默认为其基类提供相同的可访问性是个好主意。)

于 2009-11-26T06:52:53.343 回答
1

你实际上可以发现这一点。我们遇到了同样的问题,我们找到了一个黑客来做到这一点。

#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;
    }
};

我们有两个类ABB用作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));
}

int n是给方法的编号,因为它们存储在 vtable 中。通常,这是您定义方法的顺序。

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;
}

输出将是

函数 0 被覆盖

编辑:我们为每个类实例获取指向 vtables 的指针,然后将指针与方法进行比较。每当一个函数被覆盖时,超级对象就会有不同的值。

于 2014-04-28T11:56:39.507 回答
0

没有便携的方法可以做到这一点。如果您的意图是拥有一个不是纯虚拟的方法,但需要为每个将被调用的类重写,您只需assert( false )在基类方法实现中插入一条语句即可。

于 2009-11-26T06:55:10.687 回答