2011-03-16 9 views
8

Sé que es legal usar dynamic_cast para hacer un "cross-cast" en una jerarquía de clases. Por ejemplo, si tengo clases que tener este aspecto:Verificando si podría funcionar un cruce cruzado?

A B 
    \/
    C 

Si tengo un puntero A* que está apuntando a un objeto de tipo C, entonces puedo usar

A* aPtr = /* ... something that produces a C* ... */ 
B* bPtr = dynamic_cast<B*>(aPtr); 

para obtener un puntero a el objeto base B del C al que estoy apuntando.

La razón que mencionar es que en el momento en que escribo el código anterior, es posible que el compilador aún no ha visto la definición de C a pesar de que ha visto A y B. Esto significa que es posible que el compilador no detecte ningún tipo de conexión entre A y B, pero aún tiene que compilar el código porque es posible que exista una clase como C y que el dynamic_cast tenga éxito bajo alguna circunstancia.

El problema es que esto significa que puedo castear de forma accidental un objeto del tipo incorrecto. Supongamos que tengo clases que se ven así:

A B D 
\/ 
    C 

Aquí, D es alguna clase no relacionada al azar. Si trato de escribir algo como esto:

A* aPtr = /* ... get a C* pointer ... */ 
D* dPtr = dynamic_cast<D*>(aPtr); 

Entonces este dynamic_cast siempre se producirá un error en tiempo de ejecución, ya que no hay manera posible conectar A y D. Si estoy usando D accidentalmente porque quería usar B, el compilador no me dará ninguna indicación de que tengo un reparto sin sentido.

Mi pregunta es: ¿Hay alguna forma en que pueda hacer que el compilador me advierta que el elenco siempre fallará en el tiempo de ejecución? Estaría contento con una solución de nivel de idioma o alguna configuración de compilador para cualquier compilador importante que pudiera detectar esto. Si hay una herramienta externa, está bien también; Solo quiero saber si es posible detectar esta clase de errores.

+0

Esto debería ser seguro para el nivel del enlazador? En tiempo de compilación, ¿no sabe que una clase podría no crearse que se deriva de 'A' y' D'? – Keith

+0

@ Keith- Definitivamente, a menos que tenga un compilador omnisciente. Hombre, quiero uno de esos ... :-) – templatetypedef

+0

@Keith: Incluso el vinculador puede no tener información completa sobre todos los tipos, como se explica en mi respuesta. –

Respuesta

3

No es posible detectar esto en tiempo de compilación. La clase C que introduce la relación se puede encontrar en una biblioteca cargable dinámicamente que aún no se ha escrito, y el compilador no puede probar lo contrario.

Sin embargo, puede haber algunas excepciones.Si A tiene solo constructores privados (o un destructor privado), entonces el compilador puede estar seguro de que no habrá nuevas subclases que no sean nombradas como amigos por A.

+0

Eso es definitivamente cierto. Sin embargo, ¿sería posible de alguna manera hacer que el enlazador me advierta algo con el efecto de "hey, simplemente haciéndole saber que esto parece sospechoso"? incluso si no puede terminar siendo un problema? – templatetypedef

+1

@templatetypedef: Dado que 'dynamic_cast' se ve y se siente como una función de plantilla, quizás podrías introducir un contenedor, usar' #define dynamic_cast checked_dynamic_cast' para forzar el uso del contenedor, y de alguna manera obtener una lista de todas las versiones de la plantilla ? Obtener el gráfico de herencia es bastante fácil, hay una serie de herramientas que lo exponen, como por ejemplo la salida XML de doxygen. Combinar esa información y aplicar algunas heurísticas debería darle la advertencia que busca. –

-3

La verdad es que cuando se utiliza dynamic_cast puede lanzar cualquier tipo puntero polimórfico a cualquier tipo puntero polimórfico aun cuando las clases no están relacionados entre sí.

Si desea comprobar el tiempo de compilación, debe utilizar otras conversiones - static_cast o conversiones implícitas - que pueden no ser adecuadas por alguna otra razón, en muchos casos.

La solución que utilizamos cuando se necesita dynamic_cast es una función de plantilla que hace dynamic_cast y arroja una excepción si se obtiene un puntero nulo. Eso es, por supuesto, solo un control de tiempo de ejecución, pero es bastante confiable.

+1

-1: Puedes dynamic_cast una clase polimórfica a otra clase polimórfica solo si sus RTTI están relacionados. De lo contrario, obtienes un 0 ptr (si lanzas a ptr), o recibes una excepción bad_cast si lanzas al ref de la clase polimórfica no relacionada. – TrueY

0

Ese es todo el significado de la conversión dinámica: es dinámico, es decir, se comprueba en el tiempo de ejecución no en el tiempo de compilación.

El compilador solo comprueba si las clases fundidas son polimórficas. Si las clases no son polimórficas, entonces no habrá suficiente información para verificar el lanzamiento en el tiempo de ejecución.

Y finalmente, después del lanzamiento dinámico, el programa debería verificar si el puntero recibido no es nulo. Si seleccionas una referencia, deberías atrapar std :: bad_cast.

Cuestiones relacionadas