Come può una classe base C ++ determinare in fase di esecuzione se un metodo è stato sovrascritto?

StackOverflow https://stackoverflow.com/questions/1801949

  •  05-07-2019
  •  | 
  •  

Domanda

Il seguente metodo di esempio ha lo scopo di rilevare se è stato ignorato o meno in una classe derivata. L'errore che ricevo da MSVC implica che è semplicemente sbagliato provare a portare il puntatore della funzione su un "limite". membro, ma non vedo alcun motivo logico per cui questo dovrebbe essere un problema (dopo tutto, sarà in this- > vtable ). Esiste un modo non confuso di correggere questo codice?

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”;
    }
};
È stato utile?

Soluzione

Non è possibile determinare se un metodo è stato sovrascritto, ad eccezione dei metodi virtuali puri: devono essere sovrascritti e non puri in una classe derivata. (Altrimenti non puoi istanziare un oggetto, poiché il tipo è ancora "astratto")

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

Puoi comunque fornire una definizione del metodo virtuale puro, se le classi derivate possono o devono chiamarlo:

void A::f() {}

Secondo il tuo commento, " Se il metodo non fosse stato ignorato, significherebbe che è sicuro provare a mappare la chiamata sull'altro metodo. "

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

Ora, qualsiasi classe derivata può fornire il proprio comportamento, altrimenti l'eredità verrà utilizzata come fallback in caso contrario. Do_method virtual è privato perché le classi derivate non devono chiamarlo. (NonLegacy può renderlo protetto o pubblico come appropriato, ma l'impostazione predefinita è la stessa accessibilità della sua classe di base.)

Altri suggerimenti

Puoi effettivamente scoprirlo. Abbiamo riscontrato lo stesso problema e abbiamo trovato un trucco per farlo.

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

Abbiamo due classi A e B e B utilizza A come classe base.

Le seguenti funzioni possono essere utilizzate per verificare se il B ha sovrascritto qualcosa in 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 è il numero assegnato al metodo in quanto sono memorizzati in vtable. Generalmente, è l'ordine in cui definisci i metodi.

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

L'output sarà

Function 0 is overridden

MODIFICA: otteniamo i puntatori su vtables per ogni istanza della classe e quindi confrontiamo il puntatore con i metodi. Ogni volta che una funzione viene ignorata, ci sarà un valore diverso per il super oggetto.

Non esiste un modo portatile per farlo. Se la tua intenzione è quella di avere un metodo che non sia puro virtuale, ma debba essere ignorato per ogni classe su cui verrà chiamato, puoi semplicemente inserire un'istruzione assert (false) nell'implementazione del metodo della classe base .

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top