C++基类如何在运行时确定方法是否已被覆盖?
下面的示例方法旨在检测它是否在派生类中被重写。我从MSVC中得到的错误暗示着试图让函数指针指向一个“绑定”成员是完全错误的,但我没有看到为什么这会成为一个问题的合乎逻辑的原因(毕竟,它将在this-> vtable)。有没有修复此代码的非哈希方式?C++基类如何在运行时确定方法是否已被覆盖?
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”;
}
};
没有方法来确定是否一个方法已被覆盖,除了纯虚拟方法:它们必须被重写和在派生类非纯的。 (否则不能实例化对象,作为类型仍是“抽象”)
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可能会将其保护或公开为适当的,但默认为与其基类相同的可访问性是个好主意。)
没有可移植的方法。如果你的意图是要有一个不是纯虚拟的方法,但是需要为每个类重写它,那么你只需要在基类方法实现中插入一个assert(false)
语句。
为什么要有派生类必须重写的非虚方法?如果它是虚拟的而不是纯粹的,那么它有什么优势使它变得纯粹呢? – 2009-11-26 07:02:24
在某些情况下,您可能需要几种派生类。对于属于某种类的类,只会调用一部分方法。这听起来像是不完美的面向对象设计,但你有时在现实生活中需要这样的设计。 – sharptooth 2009-11-26 07:10:23
我的问题来自需要支持两种不同接口方法(一种新的和一种传统)之间的映射。如果该方法未被覆盖,则意味着将该调用映射到另一个方法是安全的。 一些对象实现新方法,其他对象不这样做,同样一些调用代码会调用新的和一些遗留的(我知道是混乱的,是逐步重构的一部分)。没有这种能力,它只能在一个方向上映射。 – intepid 2009-11-26 07:16:15
你实际上可以找到它。我们遇到了同样的问题,我们发现一个黑客要做到这一点。
#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
已重写的东西是赋予方法的数量,因为它们被存储在虚表。通常,这是您定义方法的顺序。
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
编辑:我们拿到的指向虚函数表为每个类的实例,然后比较指针的方法。只要函数被覆盖,超级对象就会有不同的值。
不幸的是,在基类和可能实现新方法或旧方法的类之间有许多中间类。我希望用这种方式来尽可能少地改变代码,但现在看起来不太可能。 – intepid 2009-11-26 07:49:16