static_cast reinterpret_cast dynamic_cast c++ pointers casting

c++ static_cast vs dynamic_cast vs reinterpret_cast



Reparto regular vs. static_cast vs. dynamic_cast (8)

Reparto estático

El reparto estático realiza conversiones entre tipos compatibles. Es similar al elenco de estilo C, pero es más restrictivo. Por ejemplo, la conversión de estilo C permitiría que un puntero entero apunte a un carácter.

char c = 10; // 1 byte int *p = (int*)&c; // 4 bytes

Dado que esto da como resultado un puntero de 4 bytes que apunta a 1 byte de la memoria asignada, escribir en este puntero causará un error en tiempo de ejecución o sobrescribirá alguna memoria adyacente.

*p = 5; // run-time error: stack corruption

En contraste con la conversión de estilo C, la conversión estática permitirá que el compilador compruebe que los tipos de datos de puntero y de punta son compatibles, lo que permite al programador detectar esta asignación de puntero incorrecta durante la compilación.

int *q = static_cast<int*>(&c); // compile-time error

Reinterpretar el elenco

Para forzar la conversión del puntero, de la misma forma que lo hace la conversión de estilo C en el fondo, se utilizará la conversión de reinterpretación en su lugar.

int *r = reinterpret_cast<int*>(&c); // forced conversion

Esta conversión maneja las conversiones entre ciertos tipos no relacionados, como de un tipo de puntero a otro tipo de puntero incompatible. Simplemente realizará una copia binaria de los datos sin alterar el patrón de bits subyacente. Tenga en cuenta que el resultado de una operación de tan bajo nivel es específico del sistema y, por lo tanto, no es portátil. Debe utilizarse con precaución si no se puede evitar por completo.

Reparto dinámico

Este solo se utiliza para convertir punteros de objetos y referencias de objetos en otros tipos de punteros o referencias en la jerarquía de herencia. Es la única conversión que se asegura de que el objeto al que se apunta se puede convertir, al realizar una verificación en tiempo de ejecución de que el puntero se refiere a un objeto completo del tipo de destino. Para que esta verificación en tiempo de ejecución sea posible, el objeto debe ser polimórfico. Es decir, la clase debe definir o heredar al menos una función virtual. Esto se debe a que el compilador solo generará la información de tipo de tiempo de ejecución necesaria para dichos objetos.

Ejemplos de reparto dinámico

En el siguiente ejemplo, un puntero de MyChild se convierte en un puntero de MyBase usando una conversión dinámica. Esta conversión derivada a base se realiza correctamente, porque el objeto secundario incluye un objeto Base completo.

class MyBase { public: virtual void test() {} }; class MyChild : public MyBase {}; int main() { MyChild *child = new MyChild(); MyBase *base = dynamic_cast<MyBase*>(child); // ok }

El siguiente ejemplo intenta convertir un puntero de MyBase en un puntero de MyChild. Dado que el objeto Base no contiene un objeto secundario completo, esta conversión de puntero fallará. Para indicar esto, la conversión dinámica devuelve un puntero nulo. Esto proporciona una manera conveniente de verificar si una conversión ha tenido éxito durante el tiempo de ejecución.

MyBase *base = new MyBase(); MyChild *child = dynamic_cast<MyChild*>(base); if (child == 0) std::cout << "Null pointer returned";

Si se convierte una referencia en lugar de un puntero, la conversión dinámica fallará al lanzar una excepción bad_cast. Esto debe ser manejado usando una declaración try-catch.

#include <exception> // … try { MyChild &child = dynamic_cast<MyChild&>(*base); } catch(std::bad_cast &e) { std::cout << e.what(); // bad dynamic_cast }

Reparto dinámico o estático

La ventaja de usar una conversión dinámica es que le permite al programador verificar si una conversión ha tenido éxito durante el tiempo de ejecución. La desventaja es que hay una sobrecarga de rendimiento asociada con la realización de esta comprobación. Por esta razón, el uso de una conversión estática hubiera sido preferible en el primer ejemplo, porque una conversión derivada a base nunca fallará.

MyBase *base = static_cast<MyBase*>(child); // ok

Sin embargo, en el segundo ejemplo, la conversión puede tener éxito o fallar. Fallará si el objeto MyBase contiene una instancia de MyBase y tendrá éxito si contiene una instancia de MyChild. En algunas situaciones esto puede no ser conocido hasta el tiempo de ejecución. Cuando este es el caso, el lanzamiento dinámico es una mejor opción que el lanzamiento estático.

// Succeeds for a MyChild object MyChild *child = dynamic_cast<MyChild*>(base);

Si la conversión de base a derivada se hubiera realizado utilizando una conversión estática en lugar de una conversión dinámica, la conversión no habría fallado. Habría devuelto un puntero que se refería a un objeto incompleto. La anulación de la referencia de un puntero de este tipo puede provocar errores en tiempo de ejecución.

// Allowed, but invalid MyChild *child = static_cast<MyChild*>(base); // Incomplete MyChild object dereferenced (*child);

Const cast

Este se usa principalmente para agregar o eliminar el modificador const de una variable.

const int myConst = 5; int *nonConst = const_cast<int*>(&myConst); // removes const

Aunque la conversión constante permite cambiar el valor de una constante, hacerlo sigue siendo un código no válido que puede causar un error en el tiempo de ejecución. Esto podría ocurrir, por ejemplo, si la constante se encontraba en una sección de la memoria de solo lectura.

*nonConst = 10; // potential run-time error

En su lugar, Const cast se usa principalmente cuando hay una función que toma un argumento de puntero no constante, aunque no modifique el pointee.

void print(int *p) { std::cout << *p; }

La función puede entonces pasar una variable constante utilizando una conversión constante.

print(&myConst); // error: cannot convert // const int* to int* print(nonConst); // allowed

Fuente y más explicaciones

Esta pregunta ya tiene una respuesta aquí:

He estado escribiendo código C y C ++ durante casi veinte años, pero hay un aspecto de estos idiomas que nunca he entendido. Obviamente he usado moldes regulares, es decir

MyClass *m = (MyClass *)ptr;

Por todas partes, pero parece que hay otros dos tipos de moldes, y no sé la diferencia. ¿Cuál es la diferencia entre las siguientes líneas de código?

MyClass *m = (MyClass *)ptr; MyClass *m = static_cast<MyClass *>(ptr); MyClass *m = dynamic_cast<MyClass *>(ptr);


static_cast

static_cast se usa para casos en los que básicamente desea revertir una conversión implícita, con algunas restricciones y adiciones. static_cast no realiza comprobaciones de tiempo de ejecución. Esto debería usarse si sabe que se refiere a un objeto de un tipo específico, y por lo tanto, una verificación sería innecesaria. Ejemplo:

void func(void *data) { // Conversion from MyClass* -> void* is implicit MyClass *c = static_cast<MyClass*>(data); ... } int main() { MyClass c; start_thread(&func, &c) // func(&c) will be called .join(); }

En este ejemplo, sabe que pasó un objeto MyClass y, por lo tanto, no es necesario realizar una verificación en tiempo de ejecución para garantizar esto.

dynamic_cast

dynamic_cast es útil cuando no sabes cuál es el tipo dinámico del objeto. Devuelve un puntero nulo si el objeto al que se hace referencia no contiene el tipo convertido como clase base (cuando se bad_cast en una referencia, se bad_cast una excepción de bad_cast en ese caso).

if (JumpStm *j = dynamic_cast<JumpStm*>(&stm)) { ... } else if (ExprStm *e = dynamic_cast<ExprStm*>(&stm)) { ... }

No puede usar dynamic_cast si realiza una conversión descendente (conversión a una clase derivada) y el tipo de argumento no es polimórfico. Por ejemplo, el siguiente código no es válido, porque Base no contiene ninguna función virtual:

struct Base { }; struct Derived : Base { }; int main() { Derived d; Base *b = &d; dynamic_cast<Derived*>(b); // Invalid }

Una "conversión ascendente" (conversión a la clase base) siempre es válida con static_cast y dynamic_cast , y también sin ninguna conversión, ya que una "conversión ascendente" es una conversión implícita.

Reparto regular

Estos moldes también se llaman reparto de estilo C. Un reparto de estilo C es básicamente idéntico a probar una serie de secuencias de lanzamientos de C ++, y tomar el primer lanzamiento de C ++ que funcione, sin tener en cuenta nunca dynamic_cast . No hace falta decir que esto es mucho más poderoso, ya que combina todos los const_cast , static_cast y reinterpret_cast , pero también es inseguro, porque no usa dynamic_cast .

Además, los lanzamientos de estilo C no solo le permiten hacer esto, sino que también le permiten emitir de forma segura a una clase base privada, mientras que la secuencia "equivalente" de static_cast le daría un error de tiempo de compilación.

Algunas personas prefieren moldes de estilo C debido a su brevedad. Los uso solo para conversiones numéricas, y utilizo las conversiones de C ++ apropiadas cuando se trata de tipos definidos por el usuario, ya que proporcionan una comprobación más estricta.


Debes mirar el artículo Programación en C ++ / Casting de tipos .

Contiene una buena descripción de todos los diferentes tipos de reparto. Lo siguiente tomado del enlace de arriba:

const_cast

const_cast (expresión) const_cast <> () se utiliza para agregar / eliminar const (ness) (o volatile-ness) de una variable.

static_cast

static_cast (expresión) static_cast <> () se utiliza para convertir entre los tipos de enteros. ''eg'' char-> long, int-> short etc.

La conversión estática también se utiliza para emitir punteros a tipos relacionados, por ejemplo, la conversión de nulos * al tipo apropiado.

dynamic_cast

La conversión dinámica se utiliza para convertir los punteros y las referencias en tiempo de ejecución, generalmente con el propósito de convertir un puntero o referencia hacia arriba o hacia abajo en una cadena de herencia (jerarquía de herencia).

dynamic_cast (expresión)

El tipo de destino debe ser un puntero o un tipo de referencia, y la expresión debe evaluar un puntero o una referencia. La conversión dinámica funciona solo cuando el tipo de objeto al que se refiere la expresión es compatible con el tipo de destino y la clase base tiene al menos una función miembro virtual. Si no, y el tipo de expresión que se está emitiendo es un puntero, se devuelve NULL, si falla una conversión dinámica en una referencia, se lanza una excepción de bad_cast. Cuando no falla, la conversión dinámica devuelve un puntero o referencia del tipo de destino al objeto al que se refiere la expresión.

reinterpretar_cast

Reinterpretar el reparto simplemente convierte un tipo en modo bit a otro. Cualquier puntero o tipo integral se puede convertir en otro con reinterpretar el elenco, permitiendo fácilmente el mal uso. Por ejemplo, con la reinterpretación de la conversión, se puede convertir, de forma poco segura, un puntero de entero en un puntero de cadena.


Evite usar moldes estilo C

Los modelos de estilo C son una mezcla de modelos const y reinterpretados, y es difícil encontrar y reemplazar tu código. Un programador de aplicaciones C ++ debería evitar el reparto de estilo C


Los conversos de estilo C combinan const_cast, static_cast y reinterpret_cast.

Desearía que C ++ no tuviera modelos de estilo C Los lanzamientos de C ++ se destacan correctamente (como deberían; los lanzamientos son normalmente indicativos de hacer algo malo) y distinguen adecuadamente entre los diferentes tipos de conversión que realizan los lanzamientos. También permiten escribir funciones de aspecto similar, por ejemplo, boost :: lexical_cast, que es bastante bueno desde una perspectiva de coherencia.


Para su información, creo que Bjarne Stroustrup se cita diciendo que se deben evitar los lanzamientos de estilo C y que debería usar static_cast o dynamic_cast si es posible.

Preguntas frecuentes sobre el estilo C ++ de Barne Stroustrup

Toma ese consejo para lo que quieras. Estoy lejos de ser un gurú de C ++.


dynamic_cast solo admite punteros y tipos de referencia. Devuelve NULL si la conversión es imposible si el tipo es un puntero o lanza una excepción si el tipo es un tipo de referencia. Por lo tanto, dynamic_cast se puede usar para verificar si un objeto es de un tipo dado, static_cast no puede (simplemente terminará con un valor no válido).

Los modelos de estilo C (y otros) han sido cubiertos en las otras respuestas.


dynamic_cast tiene comprobación de tipo de tiempo de ejecución y solo funciona con referencias y punteros, mientras que static_cast no ofrece comprobación de tipo de tiempo de ejecución. Para obtener información completa, consulte el artículo static_cast Operator de MSDN.