Todos sabemos que cuando se usa una herencia simple simple, la dirección de una clase derivada es la misma que la dirección de la clase base. La herencia múltiple hace que eso sea falso.
¿La herencia virtual también hace que eso sea falso? En otras palabras, ¿es correcto el siguiente código?
struct A {};
struct B : virtual A
{
int i;
};
int main()
{
A* a = new B; // implicit upcast
B* b = reinterpret_cast<B*>(a); // fishy?
b->i = 0;
return 0;
}
c++
virtual-inheritance
usuario1610015
fuente
fuente
reinterpret_cast
with classes es siempre sospechoso (excepto de clase avoid*
, y de regreso a la misma clase).Respuestas:
Creo que la afirmación no es cierta. En el siguiente código, tenemos una herencia simple (no virtual) simple (no múltiple), pero las direcciones son diferentes.
y la salida para VS2015 es:
Si cambia la herencia en el código anterior a virtual, el resultado será el mismo. así, incluso en el caso de la herencia virtual, las direcciones de los objetos base y derivados pueden ser diferentes.
fuente
void main()
es aceptable incluso en los compiladores modernos de MSVS. Por cierto, gracias por el comentario. He actualizado el código.void main()
no es aceptable Tiene que estar deint main()
acuerdo con el estándar. Y elimine esodynamic_cast
del código, no es necesario allí y causa confusión.El resultado de
reinterpret_cast<B*>(a);
solamente está garantizada a punto a la envolventeB
objeto dea
si ela
subobjeto y la envolventeB
objeto son puntero-interconvertibles , ver [expr.static.cast] / 3 de la C ++ 17 estándar.El objeto de clase derivado es interconvertible con el puntero con el objeto de clase base solo si el objeto derivado es un diseño estándar , no tiene miembros de datos directos no estáticos y el objeto de clase base es su primer subobjeto de clase base. [basic.compound] /4.3
Tener una
virtual
clase base descalifica a una clase de ser un diseño estándar . [clase] /7.2 .Por lo tanto, debido a que
B
tiene una clase base virtual y un miembro de datos no estático,b
no apuntará alB
objeto que lo encierra , sino queb
el valor del puntero permanecerá sin cambios con respectoa
al de.Acceder al
i
miembro como si estuviera apuntando alB
objeto tiene un comportamiento indefinido.Cualquier otra garantía provendría de su ABI específico u otra especificación.
fuente
Eso no es del todo correcto. Considere este ejemplo:
Al crear una instancia de
D
,B
yC
se instancian cada uno con su instancia respectiva deA
. Sin embargo, no habría ningún problema si la instancia deD
tuviera la misma dirección de su instancia deB
y su respectiva instancia deA
. Aunque no es obligatorio, esto es exactamente lo que sucede al compilarclang 11
ygcc 10
:Consideremos una versión modificada del ejemplo anterior:
El uso del
virtual
especificador de funciones se usa generalmente para evitar llamadas ambiguas a funciones. Por lo tanto, cuando se usa lavirtual
herencia, ambosB
y lasC
instancias deben crear unaA
instancia común . Al crear instanciasD
, obtenemos las siguientes direcciones:Aquí no hay ninguna razón para usar
reinterpret_cast
, aún más, resulta en un comportamiento indefinido. Use en sustatic_cast
lugar:Ambos lanzamientos se comportan de manera diferente en este ejemplo. La
reinterpret_cast
reinterpretarápB
como un puntero aA
, pero el punteropA
puede apuntar a una dirección diferente, como en el ejemplo anterior (C vs A). El puntero se subirá correctamente si lo usastatic_cast
.fuente
La razón
a
yb
son diferentes en su caso es porque, dadoA
que no tiene ningún método virtual,A
no está manteniendo avtable
. Por otro lado,B
mantiene avtable
.Cuando se sube a
A
, el compilador es lo suficientemente inteligente como para omitir lovtable
previstoB
. Y de ahí la diferencia en las direcciones. No deberíasreinterpret_cast
volver aB
hacerlo, no funcionaría.Para verificar mi reclamo, intente agregar un
virtual
método, digamosvirtual void foo() {}
enclass A
. AhoraA
también mantendrá avtable
. Por lo tanto, downcast (reinterpret_cast
) a B le devolverá el originalb
.fuente
Subclass address equal to virtual base class address?
tiene que ver con la herencia virtual. Y la herencia virtual tiene que ver con vtables.A
garantice que las direcciones coincidan y que el elenco funcione, ya sea en teoría o en la práctica. No puedo eliminar mi voto negativo sin una edición de publicación, porque se ha bloqueado.