c++ - programacion - Mezcla de herencia virtual y no virtual de una clase base
objetos en c++ (3)
Este es el código:
struct Biology
{
Biology() { cout << "Biology CTOR" << endl; }
};
struct Human : Biology
{
Human() { cout << "Human CTOR" << endl; }
};
struct Animal : virtual Biology
{
Animal() { cout << "Animal CTOR" << endl; }
};
struct Centaur : Human, Animal
{
Centaur() { cout << "Centaur CTOR" << endl; }
};
int main()
{
Centaur c;
return 0;
}
Este código imprime:
Biology CTOR
Biology CTOR
Human CTOR
Animal CTOR
Centaur CTOR
¿Por qué?
Ya que creamos un objeto Centaur
, comenzamos a construir el Centaur
construyendo Human
, Animal
y finalmente Centaur
(comenzamos desde lo menos derivado hasta lo más derivado).
Empecemos por Human
: Human
hereda de Biology
, por lo que llamamos primero al constructor de Biology
. Ahora que Human
clase base del Human
está construida, finalmente podemos construir al Human
mismo. Pero en cambio, ¡la Biology
se construye de nuevo!
¿Por qué? ¿Qué está pasando detrás de las escenas?
Tenga en cuenta que fue completamente intencional dejar a Animal
heredado virtualmente de la Biology
y, al mismo tiempo, también fue intencional dejar al Human
no virtualmente heredado de la Biology
.
Estamos resolviendo el Dreaded Diamond de una manera incorrecta: tanto el humano como el animal deben heredar virtualmente la biología para que esto funcione.
Tengo curiosidad.
También, vea este código:
struct Biology
{
Biology() { cout << "Biology CTOR" << endl; }
};
struct Human : virtual Biology
{
Human() { cout << "Human CTOR" << endl; }
};
struct Animal : Biology
{
Animal() { cout << "Animal CTOR" << endl; }
};
struct Centaur : Human, Animal
{
Centaur() { cout << "Centaur CTOR" << endl; }
};
int main()
{
Centaur c;
return 0;
}
Aquí tenemos Human
herencia Human
virtualmente de la Biology
, mientras que el Animal
va a heredar de la "manera clásica".
Pero esta vez, la salida es diferente:
Biology CTOR
Human CTOR
Biology CTOR
Animal CTOR
Centaur CTOR
Esto se debe a que Centaur
hereda primero de Human
y luego de Animal
.
Si el orden hubiera sido el inverso, habríamos logrado el mismo resultado que antes, en el primer ejemplo: dos instancias de Biology
construidas en una fila.
¿Cuál es la lógica de esto?
Por favor, trate de explicar su camino, ya he revisado toneladas de sitios web que hablan sobre esto. Pero ninguno parece satisfacer mi petición.
- Todas las clases base que heredan virtualmente de la
Biology
comparten una instancia de la base deBiology
entre ellas. - Todas las clases base que heredan no virtualmente de la
Biology
tienen una instancia de cadaBiology
.
Tiene una base en cada categoría, por lo tanto, tiene una instancia de Biology
presentada por Human
(y en principio compartida con otras) y una instancia presentada por Animal
(nunca compartida con ninguna otra clase base).
Está claro a partir de la salida que se crea una instancia de dos objetos de Biology
. Eso es porque solo has hecho una herencia virtual
. Dos instancias de clase base son la causa de la ambigüedad en el temido problema de los diamantes y la solución es hacer (como sabemos) ambas herencias de la Biology
virtual
.
Resumen de la jerarquía:
Biology Biology
| | # one and only one inheritance virtual
Human Animal
/ /
Centaur
Ok, leamos la salida nuevamente con estas reglas en mente:
- Las clases base se construyen antes que las clases derivadas.
- Las clases base se construyen en el orden en que aparecen en la lista-especificador base .
- Las clases base virtuales se construyen antes que las no virtuales por la clase más derivada , vea esto .
1ra salida - Animal
virtual
ly hereda de la Biology
:
Biology CTOR # virtual base class inherited from Animal
Biology CTOR # non-virtual base class of Human
Human CTOR # Human itself
Animal CTOR # Animal''s virtual base class already constructed
Centaur CTOR
2da salida - Human
virtual
ly hereda de la Biology
:
Biology CTOR # virtual base class inherited from Human
Human CTOR # Human''s virtual base class already constructed
Biology CTOR # non-virtual base class of Animal
Animal CTOR # Animal itself
Centaur CTOR
Párrafo estándar más informativo ( [class.base.init]/10
) :
En un constructor no delegante, la inicialización se realiza en el siguiente orden:
- Primero, y solo para el constructor de la clase más derivada (1.8), las clases de base virtual se inicializan en el orden en que aparecen en el primer recorrido de izquierda a derecha del gráfico acíclico dirigido de clases de base, donde "izquierda -a-derecha ”es el orden de aparición de las clases base en la lista derivada -especificador-base de la clase.
- Luego, las clases base directas se inicializan en orden de declaración tal como aparecen en la lista de especificadores de base (independientemente del orden de los inicializadores de memoria ).
...
La herencia no virtual es una relación exclusiva, como la membresía. Una clase puede ser la clase base no virtual de otra clase en un objeto completo dado.
Esto implica que una clase puede anular las funciones virtuales de una clase base no virtual sin causar conflictos o problemas.
Un constructor también puede inicializar bases no virtuales de manera confiable.
Solo las bases virtuales pueden ser clases de base directa de muchas bases indirectas de un objeto completo. Debido a que una clase base virtual se puede compartir, los anuladores pueden entrar en conflicto.
Un constructor puede intentar inicializar un subobjeto de base virtual en ctor-init-list, pero si la clase se deriva aún más, esa parte de ctor-init-list se ignorará.