puras - polimorfismo puro c++
llamada de funciĆ³n virtual de la clase base (8)
Digamos que tenemos:
Class Base
{
virtual void f(){g();};
virtual void g(){//Do some Base related code;}
};
Class Derived : public Base
{
virtual void f(){Base::f();};
virtual void g(){//Do some Derived related code};
};
int main()
{
Base *pBase = new Derived;
pBase->f();
return 0;
}
¿Qué g()
se llamará desde Base::f()
? Base::g()
o Derived::g()
?
Gracias...
Bueno ... No estoy seguro de que esto deba compilarse. El seguimiento,
Base *pBase = new Derived;
no es válido a menos que tenga:
Class Derived : public Base
¿Quieres que quisieras decir? Si esto es querer, querías decir,
pBase->f();
Entonces la pila de llamadas sería así:
Derived::f()
Base::f()
Derived::g()
Como ha definido que g () es virtual, la g () más derivada se buscará en el vtable de la clase y se invocará independientemente del tipo en que su código esté accediendo actualmente a ella.
Consulte las preguntas frecuentes de C ++ sobre funciones virtuales .
Creo que intentas inventar el patrón de método de plantilla
En realidad, ejecutar su código muestra que se llama Derived :: g ().
pBase es un puntero a una base. pBase = new Derived devuelve un puntero a Derived - Derived is-a Base.
Entonces pBase = new Derived es válido.
pBase hace referencia a una Base, por lo que verá Derivada como si fuera una Base.
pBase-> f () llamará Derive :: f ();
Entonces vemos en el código que:
Derive :: f () -> Base :: f () -> g () - pero ¿qué g?
Bueno, llama Derive :: g () porque ese es el g al que pBase "apunta".
Respuesta: Derive :: g ()
Es Derived :: g, a menos que llame a g en el constructor de Base. Como se llama al constructor Base antes de que se construya el objeto Derivado, Derived :: g no se puede llamar lógicamente porque podría manipular las variables que aún no se han construido, por lo que se llamará a Base :: g.
Se llamará al método de la clase derivada.
Esto se debe a la inclusión de tablas virtuales dentro de las clases que tienen funciones virtuales y clases que anulan esas funciones. (Esto también se conoce como despacho dinámico). Esto es lo que realmente está pasando: se crea un vtable para Base
y se crea un vtable para Derived
, porque solo hay un vtable por clase. Como pBase
llama a una función que es virtual y se pBase
se llama a un puntero al vtable para Derived
. Llámalo d_ptr
, también conocido como vpointer:
int main()
{
Base *pBase = new Derived;
pBase->d_ptr->f();
return 0;
}
Ahora el d_ptr llama a Derived::f()
, que llama a Base::f()
, que luego mira el vtable para ver qué g()
usar. Debido a que vpointer solo conoce g()
en Derived
, ese es el que usamos. Por lo tanto, se llama Derived::g()
.
Se llamará la g de la clase derivada. Si desea llamar a la función en la base, llame
Base::g();
en lugar. Si desea llamar al derivado, pero aún desea que se llame a la versión base, disponga que la versión derivada de g llame a la versión base en su primera declaración:
virtual void g() {
Base::g();
// some work related to derived
}
El hecho de que una función de la base puede llamar a un método virtual y el control se transfiere a la clase derivada se usa en el patrón de diseño del método de la plantilla. Para C ++, es mejor conocido como Non-Virtual-Interface . Se usa ampliamente también en la biblioteca estándar de C ++ (los búferes de flujo C ++, por ejemplo, tienen funciones pub...
que llaman funciones virtuales que hacen el trabajo real. Por ejemplo, pubseekoff
llama a la seekoff
protegida). Escribí un ejemplo de eso en esta respuesta: ¿cómo se valida el estado interno de un objeto?