Cuáles son los usos adecuados de:
static_cast
casting_dinámico
const_cast
reinterpretar_cast
(tipo)valor
tipo(valor)
¿Cómo se decide cuál utilizar en cada caso concreto?
static_cast
es la primera conversión que deberías intentar utilizar. Hace cosas como conversiones implícitas entre tipos (como int
a float
, o puntero a void*
), y también puede llamar a funciones de conversión explícitas (o implícitas). En muchos casos, no es necesario indicar explícitamente static_cast
, pero es importante tener en cuenta que la sintaxis T(algo)
es equivalente a (T)algo
y debe evitarse (más adelante se habla de ello). Sin embargo, un T(algo, algo_else
es seguro y garantiza la llamada al constructor.
El static_cast
también puede hacer un casting a través de las jerarquías de la herencia. No es necesario cuando se hace un casting hacia arriba (hacia una clase base), pero cuando se hace un casting hacia abajo se puede utilizar siempre que no se haga un casting a través de la herencia virtual
. Sin embargo, no hace comprobaciones, y es un comportamiento indefinido hacer un static_cast
hacia abajo en una jerarquía a un tipo que no es realmente el tipo del objeto.
const_cast
puede usarse para eliminar o añadir const
a una variable; ningún otro cast de C++ es capaz de eliminarlo (ni siquiera reinterpret_cast
). Es importante tener en cuenta que modificar un valor que antes era const
sólo es indefinido si la variable original es const
; si se utiliza para quitar el const
a una referencia a algo que no fue declarado con const
, es seguro. Esto puede ser útil cuando se sobrecargan funciones miembro basadas en const
, por ejemplo. También se puede utilizar para añadir const
a un objeto, como para llamar a una sobrecarga de funciones miembro.
const_cast
también funciona de forma similar en volatile
, aunque es menos común.
dynamic_cast
se utiliza exclusivamente para manejar el polimorfismo. Puedes lanzar un puntero o referencia a cualquier tipo polimórfico a cualquier otro tipo de clase (un tipo polimórfico tiene al menos una función virtual, declarada o heredada). Puedes usarlo para algo más que para hacer un casting hacia abajo - puedes hacer un casting lateral o incluso hacia otra cadena. El dynamic_cast
buscará el objeto deseado y lo devolverá si es posible. Si no puede, devolverá nullptr
en el caso de un puntero, o lanzará std::bad_cast
en el caso de una referencia.
Sin embargo, dynamic_cast
tiene algunas limitaciones. No funciona si hay varios objetos del mismo tipo en la jerarquía de la herencia (el llamado "temido diamante") y no se utiliza la herencia virtual. También sólo puede pasar por la herencia pública - siempre fallará al pasar por la herencia protegida
o privada
. Sin embargo, esto no suele ser un problema, ya que estas formas de herencia son poco frecuentes.
reinterpret_cast
es el cast más peligroso, y debería usarse con mucha moderación. Convierte un tipo directamente en otro, como por ejemplo al pasar el valor de un puntero a otro, o al almacenar un puntero en un int
, o todo tipo de cosas desagradables. En gran medida, la única garantía que se obtiene con reinterpret_cast
es que normalmente si se devuelve el resultado al tipo original, se obtendrá exactamente el mismo valor (pero no si el tipo intermedio es más pequeño que el tipo original). Hay un número de conversiones que reinterpret_cast
no puede hacer, también. Se utiliza principalmente para conversiones y manipulaciones de bits particularmente extrañas, como convertir un flujo de datos en bruto en datos reales, o almacenar datos en los bits bajos de un puntero a datos alineados.
El C-style cast y el function-style cast son conversiones que utilizan (tipo)objeto
o tipo(objeto)
, respectivamente, y son funcionalmente equivalentes. Se definen como el primero de los siguientes que tiene éxito:
const_cast
static_cast
(aunque ignorando las restricciones de acceso)static_cast
(véase más arriba), y luego const_cast
.reinterpret_cast
.reinterpret_cast
, y luego const_cast
.Por lo tanto, puede ser utilizado como un reemplazo de otros lanzamientos en algunos casos, pero puede ser extremadamente peligroso debido a la capacidad de convertirse en un reinterpret_cast
, y este último debe ser preferido cuando se necesita un lanzamiento explícito, a menos que esté seguro de que static_cast
tendrá éxito o reinterpret_cast
fallará. Incluso entonces, considere la opción más larga y explícita.
Los lanzamientos de estilo C también ignoran el control de acceso cuando realizan un static_cast
, lo que significa que tienen la capacidad de realizar una operación que ningún otro lanzamiento puede realizar. Sin embargo, esto es más que nada un truco, y en mi opinión es otra razón para evitar los lanzamientos de estilo C.
Utilice dynamic_cast
para convertir punteros/referencias dentro de una jerarquía de herencia.
Utilice static_cast
para las conversiones de tipo ordinarias.
Utilice reinterpret_cast
para la reinterpretación de bajo nivel de los patrones de bits. Utilizar con extrema precaución.
Utiliza const_cast
para la eliminación de const/volatile
. Evite esto a menos que esté atascado usando una API incorrecta.
¿Responde esto a su pregunta?
Nunca he utilizado reinterpret_cast
, y me pregunto si encontrarse con un caso que lo necesite no es un olor a mal diseño. En la base de código en la que trabajo dynamic_cast
se utiliza mucho. La diferencia con static_cast
es que un dynamic_cast
hace una comprobación en tiempo de ejecución que puede (más seguro) o no (más sobrecarga) ser lo que quieres (ver msdn).