A veces es más fácil de entender con un ejemplo:
class PureVirtual {
public:
virtual void methodA() = 0;
virtual void methodB() = 0;
};
class Base : public PureVirtual {
public:
virtual void methodA();
void methodC();
private:
int x;
};
class Derived : public Base {
public:
virtual void methodB();
private:
int y;
};
Así, dado un objeto de tipo derivado puede ser que parezca:
------------
Known offset for vtable | 0xblah | -------> [Vtable for type "Derived"]
------------
Known offset for x | 3 |
------------
Known offset for y | 2 |
------------
Con la viable para el tipo "Derivado" buscando algo como:
------------
Known offset for "methodA" | 0xblah1 | ------> methodA from Base
-------------
Known offset for "methodB" | 0xblah2 | ------> methodB from Derived
-------------
Tenga en cuenta que ya que "methodC" no era virtual, no está en el vtable en absoluto. También tenga en cuenta que todas las instancias de la clase derivada tendrán un puntero vtable al mismo objeto vtable compartido (ya que tienen el mismo tipo).
Aunque las implementaciones para C++ y Java son ligeramente diferentes, las ideas no son incompatibles. La diferencia clave, desde un punto de vista conceptual, es que los métodos de Java son "virtuales" a menos que se declare "final". En C++, la palabra clave "virtual" debe darse explícitamente para que la función esté en el vtable. Todo lo que no esté en vtable se enviará utilizando los tipos de tiempo de compilación en lugar del tipo de tiempo de ejecución del objeto.
Nota: no todas las clases en C++ tienen tablas virtuales. Solo los que tienen funciones virtuales tienen. Cuando no hay funciones virtuales, todos los métodos pueden vincularse estáticamente (en tiempo de compilación) y el mecanismo de envío no es necesario en tiempo de ejecución. – eran