En C++, las jerarquías de clases pueden provocar el problema del rombo (diamond problem), cuando una clase base es heredada por dos descendientes, y luego se crea otra clase que hereda de estas dos. En este caso, el objeto de la clase heredada contendrá dos copias independientes de la clase base. Para resolver este problema, C++ implementó la herencia virtual.
Si se utiliza la herencia múltiple normal:
class A { public: int x; }; class B : public A {}; class C : public A {}; class D : public B, public C {};
El objeto D contendrá 2 copias de A: una a través de B, y otra a través de C. Esto provoca ambigüedades al acceder a A::x y un uso injustificado de memoria.
La herencia virtual elimina la duplicación de la clase base. Una única instancia de la clase base A será utilizada por todos los descendientes:
class A { public: int x; }; class B : public virtual A {}; class C : public virtual A {}; class D : public B, public C {};
Ahora D contiene solo una copia de A, y la ambigüedad en el acceso a A::x se elimina.
Características clave:
¿Por qué no se puede resolver el problema del rombo mediante la indicación explícita de la ruta a través de la resolución de ámbito?
La resolución de ámbito solo resuelve la ambigüedad del acceso a los miembros de la clase base, pero no elimina las copias adicionales de la propia clase base. El problema de la doble inicialización y el almacenamiento duplicado de datos permanece.
¿En qué orden se llaman los constructores en la herencia virtual?
Los constructores de las clases base virtuales se llaman primero y solo una vez, y precisamente por el constructor de la última clase en la jerarquía de herencia.
Ejemplo:
class A { public: A() { std::cout << "A "; } }; class B : public virtual A { public: B() { std::cout << "B "; } }; class C : public virtual A { public: C() { std::cout << "C "; } }; class D : public B, public C { public: D() { std::cout << "D "; } }; D d; // Salida: A B C D
¿Es obligatorio declarar la herencia virtual tanto en la declaración como en la definición de la clase?
Sí, la herencia virtual debe indicarse en todas partes donde se herede de la clase base correspondiente. De lo contrario, se producirá un error de compilación, y la herencia múltiple se convertirá en herencia normal.
Jerarquía complicada en la que no se ha aplicado la herencia virtual, por lo que en el objeto hay dos copias de la configuración:
Ventajas:
Desventajas:
Se utilizó la herencia virtual con un constructor coherente del descendiente más alto:
Ventajas:
Desventajas: