Herencia virtual

Para la herencia de métodos virtuales, consulte método virtual .

La herencia virtual ( ing.  herencia virtual ) en el lenguaje de programación C ++  es una de las opciones de herencia que se necesita para resolver algunos de los problemas generados por la presencia de la posibilidad de herencia múltiple (especialmente " herencia en forma de diamante "), resolviendo la ambigüedad de qué métodos son de superclases(clases de antecesores inmediatos). Se utiliza en los casos en que la herencia múltiple, en lugar de la composición completa prevista de las propiedades de las clases antepasadas, da como resultado una limitación de las propiedades heredadas disponibles debido a la ambigüedad. Una clase base que es de herencia múltiple se define como virtual mediante la palabra clave virtual.

La esencia del problema

Considere la siguiente jerarquía de clases:

clase Animal { público : comer vacío virtual (); // El método se define para la clase dada ... }; clase Mamífero : público Animal { público : Color getHairColor (); ... }; clase WingedAnimal : public Animal { público : aleta vacía (); ... }; // Un murciélago es un mamífero alado class Bat : public Mammal , public WingedAnimal {}; //<--- tenga en cuenta que el método eat() no se anula en Bat batbat ; _

Para el código anterior, la llamada bat.eat()es ambigua. Puede referirse Bat::WingedAnimal::Animal::eat()tanto a como a Bat::Mammal::Animal::eat(). Para cada sucesor intermedio ( WingedAnimal, Mammal), el método eat()se puede anular (esto no cambia la esencia del problema desde el punto de vista del lenguaje). El problema es que la semántica de la herencia múltiple tradicional no coincide con la realidad que modela. En cierto sentido, la esencia Animales única en esencia; Bat - esto es Mammaly WingedAnimal, pero la propiedad de animalidad ( Animalness) de un murciélago ( Bat), es también la propiedad de animalidad de un mamífero ( Mammal) y es la misma propiedad de animalidad WingedAnimal - de hecho, esta es una y la misma propiedad .

Esta situación se conoce comúnmente como " herencia de diamantes " y es un problema que la herencia virtual está diseñada para resolver.

Representación de clase

Antes de continuar, es útil revisar cómo se representan las clases en C++. En particular, durante la herencia, las clases del antepasado y el sucesor simplemente se colocan en la memoria una tras otra. Por lo tanto, un objeto de clase Murciélago es en realidad una secuencia de objetos de clase (Animal, Mamífero, Animal, Animal alado, Murciélago) colocados secuencialmente en la memoria, mientras que Animal se repite dos veces, lo que genera ambigüedad.

Solución

Podemos anular nuestras clases de esta manera:

clase Animal { público : comer vacío virtual (); ... }; // Dos clases que heredan virtualmente Animal: class Mammal : public virtual Animal // <--- observe la palabra clave virtual { público : Color getHairColor (); ... }; class WingedAnimal : public virtual Animal // <--- observe la palabra clave virtual { público : aleta vacía (); ... }; // Un murciélago sigue siendo un mamífero alado class Bat : public Mammal , public WingedAnimal {};

Ahora, la parte Animaldel objeto de clase Bat::WingedAnimal es la misma que la parte Animalque se usa en Bat::Mammal, y se puede decir que Battiene solo una parte en su representación Animal, y la llamada Bat::eat()se vuelve inequívoca.

La herencia virtual se implementa agregando punteros a Mammaly WingedAnimal. Así Bataparece como (ptr, Mammal, ptr, WingedAnimal, Bat, Animal). *ptr contiene información sobre el desplazamiento en la memoria entre el comienzo de Mammal/ WingedAnimaly su Animal. Debido a esto, no solo se elimina la duplicación de la Animalparte común para Mammaly WingedAnimal, sino que también se presenta un mecanismo simple para convertir un puntero (referencia) a un objeto de la clase heredera en un puntero (referencia) a un objeto de cualquier clase base. previsto. Obviamente, el uso excesivo de la herencia virtual dará cierta degradación del rendimiento (similar a la de las funciones virtuales, debido a una operación de lectura adicional).

Ejemplo

Para comprender la esencia de la herencia virtual sin demasiado "ruido", considere el siguiente ejemplo:

#incluir <iostream> clase A { público : foo int virtual () { devolver 1 ; } }; clase B : público virtual A {}; clase C : público virtual A {}; clase D : pública B , pública C {}; int principal () { Dd ; _ std :: cout << d . foo (); devolver 0 ; }

Si se elimina la palabra clave virtual , el método foo() no se puede definir de forma única y, como resultado, no estará disponible, al igual que un objeto de clase D : el código no se compilará.

Véase también

Literatura

  • Podbelsky VV Capítulo 10.2 Herencia múltiple y clases base virtuales // Lenguaje C++ / rec. Dadaev Yu. G. - 4. - M. : Finanzas y estadísticas , 2003. - S. 336-359. — 560 págs. - ISBN 5-279-02204-7 , UDC 004.438Si (075.8) LBC 32.973.26-018 1ya173.