virtuales sencillos puro poo polimorfismo herencia funciones ejemplos ejemplo codigo c++ pointers reference virtual

sencillos - llamada de función virtual c++ sin puntero ni referencia



polimorfismo puro c++ (3)

Por lo que yo sé, la llamada de función virtual generalmente requiere puntero o referencia. Así que estoy muy sorprendido por los siguientes códigos.

#include <iostream> using namespace std; class B{ public: void runB(){ call(); } virtual void call(){ cout<<"B/n"; }; }; class D: public B{ public: void runD(){ runB(); } void call(){ cout<<"D/n"; } }; int main(){ D d; d.runD(); }

El resultado es

re

¿Podría alguien comentar por qué funciona esta llamada de función virtual? Gracias.


Dentro de una función miembro, cualquier referencia a otras funciones miembro o variables se resuelven implícitamente a través de this puntero. Entonces, en la definición de runB() , la call() realmente significa this->call() . La llamada a la función virtual se realiza utilizando la tabla virtual del objeto actual.


La diferencia entre virtual y no virtual es:

no virtual - siempre va por el tipo de llamador / referencia / tipo de puntero.

virtual - reference / pointer - pasa por el tipo de objeto creado.

virtual - object - pasa por la persona que llama.

por ejemplo:

class A{ public: virtual void f(){ cout <<"A/n"; } }; class B: public A{ public: virtual void f(){ cout <<"B/n"; } }; B b; A a,*pa=&b; a.f(); //A: caller type = created type - same for not virtual b.f(); //B: caller type = created type - same for not virtual ((A)b).f(); //A: object goes by the caller type - same for not virtual pa->f(); // B: pointer goes by the created type - it would be A if it was not virtual!!


En primer lugar, la llamada de función virtual no requiere un puntero o una referencia. En lo que respecta al lenguaje, cualquier llamada a la función virtual es una llamada virtual, a menos que se suprima explícitamente el mecanismo de despacho virtual utilizando un nombre de función calificado . Por ejemplo, estos

d.D::call(); // calls `D::call()` directly d.B::call(); // calls `B::call()` directly

son llamadas que fueron explícitamente forzadas a ser no virtuales. Sin embargo, esto

d.call(); // calls `D::call()` virtually

es una llamada virtual. En este caso, es inmediatamente obvio para el compilador que la función de destino es D::call() , por lo que el compilador normalmente optimiza esta llamada virtual en una llamada directa regular. Sin embargo, conceptualmente, d.call() sigue siendo una llamada virtual.

En segundo lugar, la llamada a call() realizada dentro de B::runB() se realiza a través de un puntero. El puntero está presente allí implícitamente. Escribir call() dentro de B::runB() es solo una abreviatura de (*this).call() . this es un puntero Entonces esa llamada se hace a través de un puntero.

En tercer lugar, la propiedad clave de la llamada virtual es que la función objetivo se elige de acuerdo con el tipo dinámico del objeto utilizado en la llamada. En su caso, incluso cuando se encuentre dentro de B::runB() el tipo dinámico del objeto *this es D Por eso llama a D::call() , como debería.

En cuarto lugar, para lo que realmente necesita un puntero o una referencia es para observar el polimorfismo real. El polimorfismo propiamente dicho se produce cuando el tipo estático de la expresión del objeto utilizado en la llamada es diferente de su tipo dinámico. Para eso, sí necesitas un puntero o una referencia. Y eso es exactamente lo que observa en esa llamada (*this).call() dentro de B::runB() . Aunque el tipo estático de *this es B , su tipo dinámico es D y la llamada se envía a D::call() .