Clases base virtuales
Dado que una clase puede ser una clase base indirecta de una clase derivada más de una vez, C++ proporciona una manera de optimizar el funcionamiento de esas clases base. Las clases base virtuales proporcionan una manera de ahorrar espacio y evitar la ambigüedad en las jerarquías de clases que usan la herencia múltiple.
Cada objeto no virtual contiene una copia de los miembros de datos definidos en la clase base. Esta duplicación desperdicia espacio y requiere especificar qué copia de los miembros de la clase base se desea siempre que se accede a ellos.
Cuando una clase base se especifica como base virtual, puede actuar como base indirecta más de una vez sin la duplicación de sus miembros de datos. Todas las clases base que utilizan una clase base como base virtual comparten una única copia de sus miembros de datos.
Al declarar una clase base virtual, la palabra clave de virtual aparece en las listas base de las clases derivadas.
Considere la jerarquía de clases de la ilustración siguiente, que muestra un gráfico Lunch-Line simulado.
Gráfico de línea de comida simulada
En la ilustración, Queue es la clase base de CashierQueue y LunchQueue. Sin embargo, cuando ambas clases se combinan para formar LunchCashierQueue, surge el siguiente problema: la nueva clase contiene dos subobjetos de tipo Queue, uno de CashierQueue y otro de LunchQueue. La ilustración siguiente muestra el diseño de memoria conceptual (el diseño de memoria real se podría optimizar).
Objeto de línea de comida simulada
Observe que hay dos subobjetos Queue en el objeto LunchCashierQueue. El código siguiente declara Queue como clase base virtual:
// deriv_VirtualBaseClasses.cpp
// compile with: /LD
class Queue {};
class CashierQueue : virtual public Queue {};
class LunchQueue : virtual public Queue {};
class LunchCashierQueue : public LunchQueue, public CashierQueue {};
La palabra clave virtual garantiza que solo se incluye una copia del subobjeto Queue (consulte la ilustración siguiente).
Objeto de línea de comida simulada con clases base virtuales
Una clase puede tener un componente virtual y un componente no virtual de un tipo determinado. Esto sucede en las condiciones que se muestran en la siguiente ilustración.
Componente virtuales y no virtuales de la misma clase
En la ilustración, CashierQueue y LunchQueue usan Queue como clase base virtual. Sin embargo, TakeoutQueue especifica Queue como clase base, no como una clase base virtual. Por consiguiente, LunchTakeoutCashierQueue tiene dos subobjetos de tipo Queue: uno en la ruta de herencia que incluye LunchCashierQueue y otro en la ruta que incluye TakeoutQueue. Esto se muestra en la ilustración siguiente.
Diseño de objeto con herencia virtual y no virtual
Nota
La herencia virtual supone una importante ventaja con respecto al tamaño si se compara con la herencia no virtual.Sin embargo, puede agregar una sobrecarga de procesamiento.
Si una clase derivada reemplaza una función virtual que hereda de una clase base virtual y si un constructor o destructor para la clase derivada llama a esa función con un puntero a la clase base virtual, el compilador puede incluir campos "vtordisp" ocultos adicionales en clases con bases virtuales. La opción del compilador /vd0 suprime la adición del miembro oculto de desplazamiento del constructor/destructor de vtordisp. La opción del compilador /vd1, la predeterminada, los habilita cuando es necesario. Desactive los vtordisp solo si está seguro de que todos los constructores y destructores de clase llaman a funciones virtuales virtualmente.
La opción del compilador /vd afecta a un módulo de compilación completo. Utilice la directiva pragma de vtordisp para suprimir y después volver a habilitar los campos de vtordisp clase por clase:
#pragma vtordisp( off )
class GetReal : virtual public { ... };
#pragma vtordisp( on )