¿Cómo puede una clase base de C ++ determinar en tiempo de ejecución si un método ha sido anulado?

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

  •  05-07-2019
  •  | 
  •  

Pregunta

El siguiente método de ejemplo está destinado a detectar si se ha anulado o no en una clase derivada. El error que recibo de MSVC implica que es simplemente incorrecto intentar obtener el puntero de función a un " límite " miembro, pero no veo ninguna razón lógica por la que esto debería ser un problema (después de todo, estará en this- > vtable ). ¿Hay alguna forma no hacky de arreglar este código?

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”;
    }
};
¿Fue útil?

Solución

No hay forma de determinar si un método ha sido anulado, excepto los métodos virtuales puros: deben ser anulados y no puros en una clase derivada. (De lo contrario, no puede crear una instancia de un objeto, ya que el tipo sigue siendo " abstracto " ;.)

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

Todavía puede proporcionar una definición del método virtual puro, si las clases derivadas pueden o deben llamarlo:

void A::f() {}

Según su comentario, " Si el método no se hubiera anulado, significaría que es seguro intentar asignar la llamada al otro método en su lugar. "

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

Ahora, cualquier clase derivada puede proporcionar su propio comportamiento, o el legado se usará como respaldo si no lo hacen. El do_method virtual es privado porque las clases derivadas no deben llamarlo. (NonLegacy puede hacerlo protegido o público según corresponda, pero es una buena idea dejar de usar la misma accesibilidad que su clase base).

Otros consejos

Realmente puedes descubrir esto. Nos encontramos con el mismo problema y encontramos un truco para hacer esto.

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

Tenemos dos clases A y B y B utiliza A como clase base.

Las siguientes funciones se pueden usar para probar si B ha anulado algo en 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));
}

El int n es el número dado al método, ya que se almacenan en vtable. En general, es el orden en que se definen los métodos.

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

La salida será

Function 0 is overridden

EDITAR: Obtenemos los punteros a vtables para cada instancia de clase y luego comparamos el puntero con los métodos. Cada vez que se invalida una función, habrá un valor diferente para el súper objeto.

No hay una forma portátil de hacerlo. Si su intención es tener un método que no sea puramente virtual, pero que deba ser anulado para cada clase en la que se invocará, solo puede insertar una declaración assert (false) en la implementación del método de la clase base .

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top