En C++, ¿qué es una clase base virtual?
Quiero saber qué es una " clase base virtual " y qué significa.
Déjame mostrarte un ejemplo:
class Foo
{
public:
void DoSomething() { /* ... */ }
};
class Bar : public virtual Foo
{
public:
void DoSpecific() { /* ... */ }
};
Las clases base virtuales, utilizadas en la herencia virtual, son una forma de evitar que aparezcan múltiples "instancias" de una clase determinada en una jerarquía de herencia cuando se utiliza la herencia múltiple.
Considere el siguiente escenario:
class A { public: void Foo() {} };
class B : public A {};
class C : public A {};
class D : public B, public C {};
La jerarquía de clases anterior da como resultado el "temido diamante" que se parece a esto:
A
/ \
B C
\ /
D
Una instancia de D estará formada por B, que incluye A, y C, que también incluye A. Entonces tienes dos "instancias" (a falta de una mejor expresión) de A.
Cuando tienes este escenario, tienes la posibilidad de ambigüedad. ¿Qué sucede cuando haces esto?
D d;
d.Foo(); // is this B's Foo() or C's Foo() ??
La herencia virtual está ahí para resolver este problema. Cuando especifica virtual al heredar sus clases, le está diciendo al compilador que solo desea una instancia única.
class A { public: void Foo() {} };
class B : public virtual A {};
class C : public virtual A {};
class D : public B, public C {};
Esto significa que sólo hay una "instancia" de A incluida en la jerarquía. Por eso
D d;
d.Foo(); // no longer ambiguous
Este es un mini resumen. Para obtener más información, lea esto y esto . Un buen ejemplo también está disponible aquí .
Acerca del diseño de la memoria
Como nota al margen, el problema con el Dreaded Diamond es que la clase base está presente varias veces. Entonces, con la herencia regular, crees que tienes:
A
/ \
B C
\ /
D
Pero en el diseño de la memoria, tienes:
A A
| |
B C
\ /
D
Esto explica por qué cuando llama D::foo()
, tiene un problema de ambigüedad. Pero el verdadero problema surge cuando quieres utilizar un miembro de datos de A
. Por ejemplo, digamos que tenemos:
class A
{
public :
foo() ;
int m_iValue ;
} ;
Cuando intente acceder m_iValue
desde D
, el compilador protestará, porque en la jerarquía verá dos m_iValue
, no uno. Y si modifica uno, digamos, B::m_iValue
(que es el A::m_iValue
padre de B
), C::m_iValue
no se modificará (que es el A::m_iValue
padre de C
).
Aquí es donde la herencia virtual resulta útil, ya que con ella volverá a un verdadero diseño de diamante, no solo con un foo()
método, sino también con uno y solo uno m_iValue
.
¿Qué puede salir mal?
Imaginar:
A
tiene alguna característica básica.B
le agrega algún tipo de conjunto interesante de datos (por ejemplo)C
le agrega alguna característica interesante como un patrón de observador (por ejemplo, enm_iValue
).D
hereda deB
yC
, y por tanto deA
.
Con herencia normal, modificar m_iValue
desde D
es ambiguo y esto debe resolverse. Incluso si lo es, hay dos m_iValues
dentro D
, así que será mejor que lo recuerdes y actualices los dos al mismo tiempo.
Con la herencia virtual, modificar m_iValue
desde D
está bien... Pero... Digamos que tienes D
. A través de su C
interfaz, adjuntó un observador. Y a través de su B
interfaz, actualizas la genial matriz, que tiene el efecto secundario de cambiar directamente m_iValue
...
Como el cambio se realiza directamente (sin utilizar un método de acceso virtual), no se llamará m_iValue
al observador que "escucha" porque el código que implementa la escucha está en y no lo sabe...C
C
B
Conclusión
Si tienes un diamante en tu jerarquía, significa que tienes un 95% de probabilidad de haber hecho algo mal con dicha jerarquía.
Explicar la herencia múltiple con bases virtuales requiere conocimientos del modelo de objetos de C++. Y es mejor explicar el tema con claridad en un artículo y no en un cuadro de comentarios.
La mejor explicación legible que encontré y que resolvió todas mis dudas sobre este tema fue este artículo: http://www.phpcompiler.org/articles/virtualinheritance.html
Realmente no necesitarás leer nada más sobre el tema (a menos que seas un escritor compilador) después de leer eso...
Una clase base virtual es una clase de la que no se pueden crear instancias: no se puede crear un objeto directo a partir de ella.
Creo que estás confundiendo dos cosas muy diferentes. La herencia virtual no es lo mismo que una clase abstracta. La herencia virtual modifica el comportamiento de las llamadas a funciones; a veces resuelve llamadas a funciones que de otro modo serían ambiguas, a veces difiere el manejo de llamadas a funciones a una clase distinta a la que uno esperaría en una herencia no virtual.