virtuales que puro puras polimorfismo objeto modificador herencia funciones c++ oop polymorphism

que - polimorfismo puro c++



¿Cómo sabe el compilador C++ qué implementación de una función virtual llamar? (7)

Aquí hay un ejemplo de polimorfismo de http://www.cplusplus.com/doc/tutorial/polymorphism.html (editado para facilitar la lectura):

// abstract base class #include <iostream> using namespace std; class Polygon { protected: int width; int height; public: void set_values(int a, int b) { width = a; height = b; } virtual int area(void) =0; }; class Rectangle: public Polygon { public: int area(void) { return width * height; } }; class Triangle: public Polygon { public: int area(void) { return width * height / 2; } }; int main () { Rectangle rect; Triangle trgl; Polygon * ppoly1 = &rect; Polygon * ppoly2 = &trgl; ppoly1->set_values (4,5); ppoly2->set_values (4,5); cout << ppoly1->area() << endl; // outputs 20 cout << ppoly2->area() << endl; // outputs 10 return 0; }

Mi pregunta es ¿cómo sabe el compilador que ppoly1 es un rectángulo y que ppoly2 es un triángulo, por lo que puede llamar a la función de área correcta ()? Podría descubrirlo mirando el "Polígono * ppoly1 = & rect;" línea y sabiendo que rect es un Rectángulo, pero eso no funcionaría en todos los casos, ¿verdad? ¿Qué pasa si hiciste algo como esto?

cout << ((Polygon *)0x12345678)->area() << endl;

Asumiendo que se te permite acceder a ese área aleatoria de memoria.

Lo probaría pero no puedo en la computadora en la que estoy en este momento.

(Espero no perderme algo obvio ...)


Cada objeto (que pertenece a una clase con al menos una función virtual) tiene un puntero, llamado vptr . Señala el vtbl de su clase real (que cada clase con funciones virtuales tiene al menos uno, posiblemente más de uno para algunos escenarios de herencia múltiple).

El vtbl contiene un grupo de punteros, uno para cada función virtual. Por lo tanto, en tiempo de ejecución, el código solo utiliza el vptr del objeto para localizar el vtbl , y desde allí la dirección de la función real anulada.

En su caso específico, Polygon , Rectangle y Triangle cada uno vtbl , cada uno con una entrada que apunta a su método de area relevante. Su ppoly1 tendrá un vptr apunta a la vptr de Rectangle , y ppoly2 manera similar con la vtbl Triangle . ¡Espero que esto ayude!


Para responder a la segunda parte de su pregunta: esa dirección probablemente no tendrá una tabla v en el lugar correcto, y la locura se producirá. Además, no está definido de acuerdo con el estándar.


Sin tener en cuenta los aspectos vinculantes, en realidad no es el compilador el que determina esto.

Es el tiempo de ejecución de C ++ que evalúa, a través de vtables y vpointers, qué es realmente el objeto derivado en tiempo de ejecución.

Recomiendo encarecidamente el libro de Scott Meyer Effective C ++ para obtener buenas descripciones sobre cómo se hace esto.

Incluso cubre cómo se ignoran los parámetros predeterminados en un método en una clase derivada y aún se toman los parámetros predeterminados en una clase base. Eso es vinculante.


Tablas de funciones virtuales. A saber, ambos objetos derivados de Polygon tienen una tabla de funciones virtuales que contiene indicadores de función para las implementaciones de todas sus funciones (no estáticas); y cuando instancia un Triangle, el puntero de función virtual para la función area () apunta a la función Triangle :: area (); cuando instancia un Rectángulo, la función de área () apunta a la función Rectángulo :: área (). Debido a que los punteros de funciones virtuales se almacenan junto con los datos de un objeto en la memoria, cada vez que haga referencia a ese objeto como un Polígono, se usará el área apropiada () para ese objeto.



cout << ((Polygon *)0x12345678)->area() << endl;

Este código es un desastre esperando a suceder. El compilador compilará todo correctamente, pero cuando se trata de tiempo de ejecución, no estará apuntando a una tabla v válida y si tiene suerte, el programa se bloqueará.

En C ++, no deberías usar moldes antiguos de estilo C como este, debes usar dynamic_cast como sigue :

Polygon *obj = dynamic_cast<Polygon *>(0x12345678)->area(); ASSERT(obj != NULL); cout << obj->area() << endl;

dynamic_cast devolverá NULL si el puntero dado no es un objeto Polygon válido, por lo que será atrapado por ASSERT.


Chris Jester-Young da la respuesta básica a esta pregunta.

Wikipedia tiene un tratamiento más en profundidad.

Si desea conocer todos los detalles sobre cómo funciona este tipo de cosas (y para todo tipo de herencia, incluida la herencia múltiple y virtual), uno de los mejores recursos es " Inside the C ++ Object Model " de Stan Lippman.