c++ - puras - ¿Por qué las clases base virtuales deben ser construidas por la clase más derivada?
funciones virtuales puras en java (3)
El siguiente código no compilará:
class A {
public:
A(int) {}
};
class B: virtual public A {
public:
B(): A(0) {}
};
// most derived class
class C: public B {
public:
C() {} // wrong!!!
};
Si llamo al constructor de A en la lista de inicialización del constructor de C
, es decir:
// most derived class
class C: public B {
public:
C(): A(0) {} // OK!!!
};
funciona
Aparentemente, la razón es que las clases base virtuales siempre deben ser construidas por la mayoría de las clases derivadas .
No entiendo la razón detrás de esta limitación.
Debido a que en la jerarquía de clases que tiene una clase base virtualmente heredada, la clase base podría ser compartida por varias clases (en la herencia de diamante, por ejemplo, donde la misma clase base es heredada por varias clases). Esto significa que solo habría una copia de la clase base virtualmente heredada. Esencialmente significa que la clase base debe construirse primero. Eventualmente significa que la clase derivada debe instanciar la clase base dada.
Por ejemplo:
class A;
class B1 : virtual A;
class B2 : virtual A;
class C: B1,B2 // A is shared, and would have one copy only.
Me parece que esta regla es propensa a errores y engorrosa (pero, ¿qué parte de la herencia múltiple no lo es?).
Pero el orden de construcción impuesto lógicamente debe diferir del caso de la herencia normal (no virtual). Considere el ejemplo de Ajay, menos virtual:
class A;
class B1 : A;
class B2 : A;
class C: B1,B2
En este caso, para cada C se construyen dos As, una como parte de la construcción de B1 y la otra como parte de la construcción de B2. El código de las clases B es responsable de eso, y puede hacerlo. El orden de los eventos es:
Start C ctor
Start B1 ctor
A ctor (in B''s ctor code)
End B1 ctor
Start B2 ctor
A ctor (in B''s ctor code)
End B2 ctor
End C ctor
Ahora consideremos la herencia virtual en
class A;
class B1 : virtual A;
class B2 : virtual A;
class C: B1,B2
Una orden de evento es
Start C ctor
Start B1 ctor
// NO A ctor
End B1 ctor
Start B2 ctor
// NO A ctor
End B2 ctor
A ctor // not B''s code!
End C ctor
Es posible que A se construya antes que B1 y B2, pero eso es irrelevante. Lo importante es que la construcción de A no está ocurriendo durante la construcción de las otras clases base. Ese código simplemente no se ejecuta, y no se puede ejecutar, porque el subobjeto de la clase base del tipo A, heredado virtualmente, forma parte y está bajo el control de la clase más derivada que no es accesible desde el código de B1 y B2; de hecho, la C ni siquiera está completamente construida en el momento en que B1 o B2 se están construyendo y podría potencialmente intentar crear una A en una C.
Porque evita esto:
class A {
public:
A(int) {}
};
class B0: virtual public A {
public:
B0(): A(0) {}
};
class B1: virtual public A {
public:
B1(): A(1) {}
};
class C: public B0, public B1 {
public:
C() {} // How is A constructed? A(0) from B0 or A(1) from B1?
};