2009-11-26 80 views
3

下面的示例方法旨在檢測它是否在派生類中被重寫。我從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」; 
    } 
}; 

回答

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可能會將其保護或公開爲適當的,但默認爲與其基類相同的可訪問性是個好主意。)

+0

不幸的是,在基類和可能實現新方法或舊方法的類之間有許多中間類。我希望用這種方式來儘可能少地改變代碼,但現在看起來不太可能。 – intepid 2009-11-26 07:49:16

0

沒有可移植的方法。如果你的意圖是要有一個不是純虛擬的方法,但是需要爲每個類重寫它,那麼你只需要在基類方法實現中插入一個assert(false)語句。

+0

爲什麼要有派生類必須重寫的非虛方法?如果它是虛擬的而不是純粹的,那麼它有什麼優勢使它變得純粹呢? – 2009-11-26 07:02:24

+0

在某些情況下,您可能需要幾種派生類。對於屬於某種類的類,只會調用一部分方法。這聽起來像是不完美的面向對象設計,但你有時在現實生活中需要這樣的設計。 – sharptooth 2009-11-26 07:10:23

+0

我的問題來自需要支持兩種不同接口方法(一種新的和一種傳統)之間的映射。如果該方法未被覆蓋,則意味着將該調用映射到另一個方法是安全的。 一些對象實現新方法,其他對象不這樣做,同樣一些調用代碼會調用新的和一些遺留的(我知道是混亂的,是逐步重構的一部分)。沒有這種能力,它只能在一個方向上映射。 – intepid 2009-11-26 07:16:15

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作爲基類。

下面的功能可以被用來測試是否BA

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

編輯:我們拿到的指向虛函數表爲每個類的實例,然後比較指針的方法。只要函數被覆蓋,超級對象就會有不同的值。

相關問題