下面的示例方法旨在检测是否已在派生类中重写它。我从MSVC得到的错误意味着尝试将函数指针指向“绑定”成员是完全错误的,但我认为没有合理的理由说明为什么这应该是一个问题(毕竟,它将在这个 - >虚表)。是否有任何非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”;
}
};
答案 0 :(得分: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虚拟是私有的,因为派生类不能调用它。 (NonLegacy可能会使其受到保护或公开适当,但默认为与其基类相同的可访问性是一个好主意。)
答案 1 :(得分: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;
}
};
我们有两个类A
和B
,B
使用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;
}
输出
Function 0 is overridden
编辑:我们获取每个类实例的vtables指针,然后将指针与方法进行比较。每当覆盖一个函数时,超级对象都会有不同的值。
答案 2 :(得分:0)
没有可移植的方法。如果你的目的是拥有一个非纯虚方法,但需要为每个调用它的类重写,你可以在基类方法实现中插入一个assert( false )
语句。