¿Es legal este código?
class Base1 {
};
class Base2 {
public:
virtual ~Base2() {
if (!dynamic_cast<Base1*>(this))
std::cout << "aaaa" << std::endl;
}
Base2() {
}
};
class MyClass: public Base1, public Base2 {
public:
MyClass() {
}
virtual ~MyClass() {
std::cout << "bbb" << std::endl;
}
};
int main() {
MyClass s;
return 0;
}
Veo ambas impresiones pero debería ver solo una. Supongo que el elenco dinámico está mal. ¿Es posible hacer un control de este tipo?
c++
polymorphism
destructor
multiple-inheritance
dynamic-cast
greywolf82
fuente
fuente
!
dynamic_cast
comporta de manera diferente en constructores y destructores?Respuestas:
Tal vez encontré la solución yo mismo, la respuesta es no, no es posible:
De la viñeta 6 de la documentación de cppreference.com :
Ver también [class.cdtor] / 6 del estándar.
Como estoy enviando a Base1 en el destructor de Base2, este comportamiento no está definido.
fuente
[class.cdtor]/6
, para referencia. Lo sentimos, no 5. Era 5 en C ++ 17 (borrador N4659), parece que es/6
ahora.this
es del tipo del destructor, por lo que no veo que UB se aplique. Sin embargo, el mismo párrafo estándar dice que el tipo más derivado del objeto bajo destrucción se considerará la clase del destructor, de modo que se observa el comportamiento explicado en la otra respuesta.Estoy de acuerdo con la respuesta de @ j6t, pero aquí hay un razonamiento ampliado con referencias estándar.
El comportamiento especial de los
dynamic_cast
objetos en construcción y destrucción se describe en [class.cdtor] / 5 del estándar C ++ 17 (borrador final) y de manera equivalente en las versiones estándar anteriores.En particular dice:
El comportamiento indefinido no se aplica aquí, ya que el operando es la expresión
this
, que trivialmente tiene el tipo de un puntero a la propia clase del destructor, ya que aparece en el destructor mismo.Sin embargo, la primera oración establece que
dynamic_cast
se comportará como si*this
fuera un objeto de tipo más derivadoBase2
y, por lo tanto, la conversión aBase1
nunca puede tener éxito, yaBase2
que no se deriva deBase1
, ydynamic_cast<Base1*>(this)
siempre devolverá un puntero nulo, resultando en el comportamiento que está viendo.cppreference.com afirma que el comportamiento indefinido ocurre si el tipo de destino del molde no es el tipo de la clase del destructor o una de sus bases, en lugar de que esto se aplique al tipo de operandos. Creo que eso es solo un error. Probablemente, la mención de " nuevo tipo " en el punto 6 se suponía que debía decir " expresión ", lo que haría que coincidiera con mi interpretación anterior.
fuente
El
dynamic_cast
está bien definido en esta situación. Es correcto que observe ambas líneas de salida.Se equivoca al suponer que en el destructor de
Base2
this
es una clase derivada. En este momento, la parte de la clase derivada ya se ha destruido, por lo que ya no puede ser una clase derivada. De hecho, en el momento en que seBase2
ejecuta el destructor de , el objeto señalado porthis
es solo unBase2
objeto. ComoBase2
no está relacionado deBase1
ninguna manera,dynamic_cast
devuelve un puntero nulo y el condicional se ingresa en consecuencia.Editar: El estándar dice :
El operando se
this
refiere al objeto bajo destrucción. Por lo tanto, la clase del destructor (Base2
) se considera la clase más derivada, y esa es la razón por la cual el objeto no está relacionado con el tipo de destino (Base1*
) de ninguna manera. Además, el tipo estático del operandothis
esBase2* const
, que claramente es un puntero a la propia clase del destructor. Por lo tanto, la regla sobre el comportamiento indefinido no se aplica. En resumen, tenemos un comportamiento bien definido.fuente
[class.cdtor]/6
mencioné en la otra respuesta que dice lo mismo que cppref: "Si el operando de dynamic_cast se refiere al objeto en construcción o destrucción y el tipo estático del operando no es un puntero u objeto del constructor o propia clase de destructor o una de sus bases, el resultado de dynamic_cast en un comportamiento indefinido ".