Pregunta ¿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
  • Molde de estilo C (type)value
  • Estilo funcional type(value)

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


2048
2017-12-01 20:11


origen


Respuestas:


static_cast es el primer elenco que debes intentar usar. Hace cosas como conversiones implícitas entre tipos (como int a float, o puntero a 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 T(something) la sintaxis es equivalente a (T)something y debe evitarse (más sobre esto más adelante). UN T(something, something_else) es seguro, sin embargo, y garantiza llamar al constructor.

static_cast también puede transmitir a través de jerarquías de herencia. No es necesario cuando se lanza hacia arriba (hacia una clase base), pero cuando se lanza hacia abajo se puede usar siempre que no se proyecte virtual herencia. Sin embargo, no se controla, y es un comportamiento indefinido para static_cast por 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 una antigua const el valor solo está indefinido si la variable original es const; si lo usa para tomar el const de una referencia a algo que no fue declarado con const, es seguro. Esto puede ser útil al sobrecargar funciones de miembros basadas en const, por ejemplo. También se puede usar para agregar const a un objeto, como 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 usa casi exclusivamente para manejar el polimorfismo. 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. los dynamic_cast buscará el objeto deseado y lo devolverá si es posible. Si no puede, regresará 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. También solo puede pasar por herencia pública; siempre dejará de viajar a través de protected o private herencia. Sin embargo, esto rara vez es un problema, ya que tales formas de herencia son raras.


reinterpret_cast es el yeso más peligroso, y debe usarse con moderación. Convierte un tipo directamente en otro, como lanzar el valor de un puntero a otro, o almacenar un puntero en un int, o todo tipo de otras cosas desagradables. En gran medida, la única garantía que obtienes con reinterpret_cast es que, normalmente, si devuelve el resultado al tipo original, obtendrá exactamente el mismo valor (pero no si el tipo intermedio es más pequeño que el tipo original). Hay una cantidad de conversiones que reinterpret_cast no puedo 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.


Molde de estilo C y estilo funcional 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 ignorando las restricciones de acceso)
  • static_cast (ver arriba), luego const_cast
  • reinterpret_cast
  • reinterpret_cast, entonces const_cast

Por lo tanto, puede utilizarse como reemplazo de otros moldes en algunos casos, pero puede ser extremadamente peligroso debido a la capacidad de convertirse en un reinterpret_cast, y este último debería ser preferido cuando se necesita una conversión explícita, a menos que esté seguro static_cast tendrá éxito o reinterpret_cast fallará. Incluso entonces, considere la opción más larga y más explícita.

Los moldes 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.


2207
2017-12-01 20:22



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

Utilizar static_cast para conversiones de tipo ordinarias.

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

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


283
2018-01-21 04:53



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

A continuación se encuentran algunos de los ejemplos prácticos cuando utilicé static_cast, dynamic_cast, const_cast, reinterpretar_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 e.g. 
  //  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<EventData*>(pData);
  .....
}

dynamic_cast:

void DebugLog::OnMessage(Message *msg)
{
    static DebugMsgData *debug;
    static XYZMsgData *xyz;

    if(debug = dynamic_cast<DebugMsgData*>(msg->pdata)){
        // debug message
    }
    else if(xyz = dynamic_cast<XYZMsgData*>(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<unsigned char*>(Passwd)

reinterpretar_cast:

typedef unsigned short uint16;

// Read Bytes returns that 2 bytes got read. 

bool ByteBuffer::ReadUInt16(uint16& val) {
  return ReadBytes(reinterpret_cast<char*>(&val), 2);
}

151
2017-12-11 02:05



Podría ayudar si conoces un poco de internos ...

static_cast

  • El compilador 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 compilación.
  • Echar 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 el error de compilación.
  • Gotcha: Si lanzas el puntero base al puntero derivado, pero si el objeto real es si no es un tipo derivado, entonces no lo hagas obtener 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& a B&.
  • Gotcha: ¡Transmitir desde 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 realmente del tipo derivado.
  • Esto significa que dynamic_cast es muy caro en comparación con static_cast.
  • por A* a B*, si el lanzamiento es inválido, dynamic_cast devolverá nullptr.
  • por A& a B& si el lanzamiento no es válido, dynamic_cast lanzará 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 algún contenedor como set<T> 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 quieres implementar T& foo() tanto 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.

51
2017-12-01 20:20



Hace esta ¿responde tu pregunta?

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


11
2018-05-31 14:16



Además de las otras respuestas hasta ahora, aquí hay un ejemplo obvio donde static_cast no es suficiente para que reinterpret_cast es 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() (ver 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 a su puntero en void** con static_cast: necesitas reinterpret_cast<void**>(&yourPointer).

En codigo:

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

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

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

9