¿Cuándo se deberían usar static_cast, dynamic_cast, const_cast y reinterpret_cast?

¿Cuáles son los usos apropiados de:

  • static_cast
  • dynamic_cast
  • const_cast
  • reinterpret_cast
  • (type)value conversión de (type)value C (type)value
  • type(value) conversión de estilo de función type(value)

¿Cómo se decide cuál usar en qué casos específicos?

static_cast es el primer elenco que debes intentar usar. Hace cosas como conversiones implícitas entre tipos (como int to float , o pointer to void* ), y también puede llamar funciones de conversión explícitas (o implícitas). En muchos casos, declarar explícitamente static_cast no es necesario, pero es importante tener en cuenta que la syntax T(something) es equivalente a (T)something y debe evitarse (más sobre esto más adelante). A T(something, something_else) es seguro, sin embargo, y garantiza llamar al constructor.

static_cast también puede static_cast través de jerarquías de herencia. No es necesario cuando se lanza hacia arriba (hacia una clase base), pero al lanzar hacia abajo se puede usar siempre que no se genere a través de virtual herencia virtual . Sin embargo, no se comprueba y el comportamiento no definido es static_cast en una jerarquía a un tipo que no es realmente el tipo del objeto.


const_cast se puede usar para eliminar o agregar const a una variable; ningún otro elenco de C ++ es capaz de eliminarlo (ni siquiera reinterpret_cast ). Es importante tener en cuenta que modificar un valor const anterior solo está indefinido si la variable original es const ; si lo usas para sacar una referencia a algo que no fue declarado con const , es seguro. Esto puede ser útil al sobrecargar funciones miembro basadas en const , por ejemplo. También se puede usar para agregar const a un objeto, como para llamar a una sobrecarga de función miembro.

const_cast también funciona de manera similar en volatile , aunque eso es menos común.


dynamic_cast se dynamic_cast casi exclusivamente para manejar el polymorphism. Puede convertir un puntero o referencia a cualquier tipo polimórfico en cualquier otro tipo de clase (un tipo polimórfico tiene al menos una función virtual, declarada o heredada). Puede usarlo para algo más que simplemente lanzar hacia abajo: puede lanzar hacia los lados o incluso hacia arriba en otra cadena. 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.

dynamic_cast tiene algunas limitaciones, sin embargo. No funciona si hay múltiples objetos del mismo tipo en la jerarquía de herencia (el llamado ‘diamante temido’) y no está usando virtual herencia virtual . También solo puede pasar a través de la herencia pública; siempre dejará de viajar a través de private herencia private o protected . Sin embargo, esto rara vez es un problema, ya que tales formas de herencia son raras.


reinterpret_cast es el elenco más peligroso, y debe usarse con moderación. Convierte un tipo directamente en otro, como emitir el valor de un puntero a otro, o almacenar un puntero en un int , o todo tipo de cosas desagradables. En general, la única garantía que obtienes con reinterpret_cast es que normalmente si vuelves a enviar el resultado al tipo original, obtendrás exactamente el mismo valor (pero no si el tipo intermedio es más pequeño que el original). Hay una cantidad de conversiones que reinterpret_cast no puede hacer, también. Se usa principalmente para conversiones y manipulaciones de bits particularmente extrañas, como convertir una secuencia de datos en datos reales o almacenar datos en los bits bajos de un puntero alineado.


El elenco de estilo C y el elenco de estilo de función son moldes usando (type)object o type(object) , respectivamente. Un lanzamiento de estilo C se define como el primero de los siguientes que tiene éxito:

  • const_cast
  • static_cast (aunque ignora las restricciones de acceso)
  • static_cast (ver arriba), luego const_cast
  • reinterpret_cast
  • reinterpret_cast , luego const_cast

Por lo tanto, puede utilizarse como reemplazo de otros lanzamientos en algunos casos, pero puede ser extremadamente peligroso debido a la capacidad de delegar en un reinterpret_cast , y el último debe preferirse 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 más explícita.

Los moldes de estilo C también ignoran el control de acceso cuando se realiza un static_cast , lo que significa que tienen la capacidad de realizar una operación que ningún otro elenco puede realizar. Sin embargo, esto es principalmente un desafío, y en mi opinión es solo otra razón para evitar los lanzamientos de estilo C.

Use dynamic_cast para convertir punteros / referencias dentro de una jerarquía de herencia.

Use static_cast para las conversiones de tipo ordinario.

Utilice reinterpret_cast para la reinterpret_cast de bajo nivel de patrones de bits. Usar con extrema precaución.

Usa const_cast para descartar const/volatile . Evita esto a menos que estés atascado con una API incorrecta.

(Se ha dado una gran cantidad de explicación teórica y conceptual más arriba)

A continuación se muestran algunos de los ejemplos prácticos cuando utilicé static_cast , dynamic_cast , const_cast , reinterpret_cast .

(También hace referencia a esto para comprender la explicación: http://www.cplusplus.com/doc/tutorial/typecasting/ )

static_cast:

 OnEventData(void* pData) { ...... // pData is a void* pData, // EventData is a structure eg // typedef struct _EventData { // std::string id; // std:: string remote_id; // } EventData; // On Some Situation a void pointer *pData // has been static_casted as // EventData* pointer EventData *evtdata = static_cast(pData); ..... } 

dynamic_cast:

 void DebugLog::OnMessage(Message *msg) { static DebugMsgData *debug; static XYZMsgData *xyz; if(debug = dynamic_cast(msg->pdata)){ // debug message } else if(xyz = dynamic_cast(msg->pdata)){ // xyz message } else/* if( ... )*/{ // ... } } 

const_cast:

 // *Passwd declared as a const const unsigned char *Passwd // on some situation it require to remove its constness const_cast(Passwd) 

reinterpretar_cast:

 typedef unsigned short uint16; // Read Bytes returns that 2 bytes got read. bool ByteBuffer::ReadUInt16(uint16& val) { return ReadBytes(reinterpret_cast(&val), 2); } 

Podría ayudar si conoces un poco de internos …

static_cast

  • El comstackdor de C ++ ya sabe cómo convertir tipos de scaler como float a int. Usa static_cast para ellos.
  • En general, al convertir el tipo A a B, static_cast llamaría al constructor de B pasándolo A. Si B no tiene dicho constructor, entonces obtendrá un error de tiempo de comstackción.
  • Transmitir de A* a B* siempre tiene éxito si A y B están en la jerarquía de herencia (o nulo), de lo contrario se obtiene un error de comstackción.
  • Gotcha : Si lanzas el puntero base al puntero derivado, pero si el objeto real es si no es un tipo derivado, entonces no obtienes un error. Obtiene un puntero malo y, tan pronto como intenta acceder a los miembros del puntero derivado, obtiene segfault en el tiempo de ejecución.
  • Lo mismo vale para A& B& .
  • Gotcha : ¡Transmitido de Derivado a Base o viceversa crea una nueva copia! Para las personas que vienen de C # / Java, muchos de los anteriores pueden ser una gran sorpresa.

dynamic_cast

  • dynamic_cast usa información del tipo de tiempo de ejecución para determinar si el cast es válido. Por ejemplo, (Base*) a (Derived*) puede fallar si el puntero no es en realidad de tipo derivado.
  • Esto significa que dynamic_cast es muy caro en comparación con static_cast.
  • Para A* a B* , si el lanzamiento no es válido, dynamic_cast devolverá nullptr.
  • Para A& B& si el lanzamiento no es válido, dynamic_cast arrojará la excepción bad_cast.
  • A diferencia de otros lanzamientos, hay sobrecarga en tiempo de ejecución.

const_cast

  • Mientras static_cast puede hacer no-const para const no puede ir en sentido contrario. El const_cast puede hacer las dos cosas.
  • Un ejemplo donde esto es útil es iterar a través de un contenedor como set que solo devuelve sus elementos como const para asegurarse de no cambiar su clave. Sin embargo, si su intención es modificar los miembros no clave del objeto, entonces debería estar bien. Puede usar const_cast para eliminar la constness.
  • Otro ejemplo es cuando desea implementar T& foo() así como const T& foo() . Para evitar la duplicación de código, puede aplicar const_cast para devolver el valor de una función a otra.

reinterpretar_cast

  • Esto básicamente dice que tome estos bytes en esta ubicación de memoria y piense en ello como un objeto dado.
  • Por ejemplo, puede cargar 4 bytes de float a 4 bytes de int para ver cómo se ven los bits en float.
  • Obviamente, si los datos no son correctos para el tipo, puede obtener segfault.
  • No hay sobrecarga de tiempo de ejecución para este elenco.

¿Responde esto a tu pregunta?

Nunca he usado reinterpret_cast , y me pregunto si encontrar un caso que lo necesite no es un olor de mal diseño. En la base de código que trabajo en dynamic_cast se usa mucho. La diferencia con static_cast es que dynamic_cast hace una comprobación en tiempo de ejecución que puede (más seguro) o no (más sobrecarga) ser lo que desea (ver msdn ).

Además de las otras respuestas hasta ahora, este es un ejemplo obvio en el que static_cast no es suficiente para que reinterpret_cast sea ​​necesario. Supongamos que hay una función que en un parámetro de salida devuelve punteros a objetos de diferentes clases (que no comparten una clase base común). Un ejemplo real de tal función es CoCreateInstance() (vea el último parámetro, que de hecho es void** ). Supongamos que solicita una clase particular de objeto a partir de esta función, para que sepa de antemano el tipo de puntero (que a menudo hace para los objetos COM). En este caso, no puede convertir el puntero al puntero en void** con static_cast : necesita reinterpret_cast(&yourPointer) .

En codigo:

 #include  #include  ..... INetFwPolicy2* pNetFwPolicy2 = nullptr; HRESULT hr = CoCreateInstance(__uuidof(NetFwPolicy2), nullptr, CLSCTX_INPROC_SERVER, __uuidof(INetFwPolicy2), //static_cast(&pNetFwPolicy2) would give a compile error reinterpret_cast(&pNetFwPolicy2) ); 

Sin embargo, static_cast funciona para punteros simples (no punteros a punteros), por lo que el código anterior se puede reescribir para evitar reinterpret_cast (a un precio de una variable adicional) de la siguiente manera:

 #include  #include  ..... INetFwPolicy2* pNetFwPolicy2 = nullptr; void* tmp = nullptr; HRESULT hr = CoCreateInstance(__uuidof(NetFwPolicy2), nullptr, CLSCTX_INPROC_SERVER, __uuidof(INetFwPolicy2), &tmp ); pNetFwPolicy2 = static_cast(tmp); 

Mientras que otras respuestas describieron muy bien todas las diferencias entre los moldes de C ++, me gustaría agregar una breve nota sobre por qué no se deben usar moldes de (Type) var C (Type) var y Type(var) .

Para los principiantes de C ++ los moldes de estilo C parecen ser la operación de superconjunto sobre moldes de C ++ (static_cast <> (), dynamic_cast <> (), const_cast <> (), reinterpret_cast <> ()) y alguien podría preferirlos a los moldes de C ++ . De hecho, el elenco estilo C es el superconjunto y más corto para escribir.

El principal problema de los lanzamientos de estilo C es que ocultan la intención real del desarrollador del reparto. Los moldes de estilo C pueden hacer virtualmente todos los tipos de moldeo desde moldes normalmente seguros hechos por static_cast <> () y dynamic_cast <> () a moldes potencialmente peligrosos como const_cast <> (), donde el modificador const puede eliminarse para que las variables const se puede modificar y reinterpretar_cast <> () que incluso puede reinterpretar valores enteros a punteros.

Aquí está la muestra.

 int a=rand(); // Random number. int* pa1=reinterpret_cast(a); // OK. Here developer clearly expressed he wanted to do this potentially dangerous operation. int* pa2=static_cast(a); // Compiler error. int* pa3=dynamic_cast(a); // Compiler error. int* pa4=(int*) a; // OK. C-style cast can do such cast. The question is if it was intentional or developer just did some typo. *pa4=5; // Program crashes. 

La razón principal por la cual los moldes de C ++ se agregaron al lenguaje fue para permitir que un desarrollador aclare sus intenciones, por qué va a hacer ese yeso. Mediante el uso de moldes de estilo C, que son perfectamente válidos en C ++, hace que su código sea menos legible y más propenso a errores, especialmente para otros desarrolladores que no crearon su código. Entonces, para que su código sea más legible y explícito, siempre debe preferir los moldes de C ++ sobre los moldes de estilo C.

Aquí hay una breve cita del libro de Bjarne Stroustrup (el autor del libro en C ++) The C ++ Programming Language 4th edition – page 302.

Este modelo de estilo C es mucho más peligroso que los operadores de conversión nombrados porque la notación es más difícil de detectar en un progtwig grande y el tipo de conversión previsto por el progtwigdor no es explícito.