5

以下のサンプル メソッドは、派生クラスでオーバーライドされているかどうかを検出するためのものです。MSVC から得られるエラーは、「バインドされた」メンバーへの関数ポインターを取得しようとするのは単に間違っていることを意味しますが、これが問題になる論理的な理由はわかりません (結局のところ、これはthis->vtableにあります) )。このコードを修正するハックではない方法はありますか?

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

基本クラスとしてAandBBusesの 2 つのクラスがあります。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 に答える