Pregunta

He estado escribiendo código C y C++ durante casi veinte años, pero hay un aspecto de estos lenguajes que nunca entendí realmente.Obviamente he usado yesos regulares, es decir.

MyClass *m = (MyClass *)ptr;

por todos lados, pero parece haber otros dos tipos de yesos, 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);
¿Fue útil?

Solución

transmisión_estática

static_cast se utiliza 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 debe usarse si sabe que se refiere a un objeto de un tipo específico y, por lo tanto, sería innecesaria una verificación.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, sabes que pasaste un MyClass objeto y, por lo tanto, no es necesario realizar una verificación en tiempo de ejecución para garantizar esto.

transmisión_dinámica

dynamic_cast Es útil cuando no se sabe 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 conviertes a una referencia, un bad_cast en ese caso se lanza una excepción).

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

No puede utilizar dynamic_cast si abate (transmite 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
}

Un "up-cast" (convertido a la clase base) siempre es válido con ambos static_cast y dynamic_cast, y también sin ningún tipo de conversión, ya que una "conversión ascendente" es una conversión implícita.

Reparto regular

Estos yesos también se denominan yeso estilo C.Una conversión de estilo C es básicamente idéntica a probar una variedad de secuencias de conversiones de C++ y tomar la primera conversión de C++ que funcione, sin siquiera considerar dynamic_cast.No hace falta decir que esto es mucho más poderoso ya que combina todos const_cast, static_cast y reinterpret_cast, pero también es inseguro, porque no utiliza dynamic_cast.

Además, las conversiones estilo C no sólo te permiten hacer esto, sino que también te permiten transmitir de forma segura a una clase base privada, mientras que el "equivalente" static_cast La secuencia le daría un error en tiempo de compilación para eso.

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

Otros consejos

reparto estático

La conversión estática realiza conversiones entre tipos compatibles.Es similar al elenco 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 memoria asignada, escribir en este puntero provocará un error de tiempo de ejecución o sobrescribirá parte de la memoria adyacente.

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

A diferencia de la conversión de estilo C, la conversión estática permitirá al compilador verificar que los tipos de datos de puntero y punta sean compatibles, lo que permite al programador detectar esta asignación incorrecta de puntero 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 manera que lo hace la conversión de estilo C en segundo plano, se usaría la conversión de reinterpretación.

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

Esta conversión maneja 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 usarse con precaución si no se puede evitar por completo.

reparto dinámico

Este solo se usa 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 garantiza que el objeto al que apunta se pueda convertir, realizando 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 MyChild se convierte en un puntero MyBase mediante una conversión dinámica.Esta conversión de derivado 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 MyBase en un puntero 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 forma cómoda de comprobar si una conversión se ha realizado correctamente 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 generar una excepción bad_cast.Esto debe manejarse mediante 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 utilizar una conversión dinámica es que permite al programador comprobar si una conversión se ha realizado correctamente durante el tiempo de ejecución.La desventaja es que existe una sobrecarga de rendimiento asociada con esta verificación.Por esta razón, habría sido preferible utilizar una conversión estática en el primer ejemplo, porque una conversión de 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 fracasar.Fallará si el objeto MyBase contiene una instancia de MyBase y tendrá éxito si contiene una instancia de MyChild.En algunas situaciones, es posible que esto no se sepa hasta el tiempo de ejecución.Cuando este es el caso, la transmisión dinámica es una mejor opción que la transmisión estática.

// 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 hacía referencia a un objeto incompleto.Eliminar la referencia a dicho puntero puede provocar errores en tiempo de ejecución.

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

// Incomplete MyChild object dereferenced
(*child);

reparto constante

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

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

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

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

En cambio, la conversión constante se usa principalmente cuando hay una función que toma un argumento de puntero no constante, aunque no modifica la punta.

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

Luego, a la función se le puede pasar una variable constante mediante una conversión constante.

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

print(nonConst); // allowed

Fuente y más explicaciones

Deberías mirar el artículo. Programación C++/Conversión de tipos.

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

const_cast

const_cast (expresión) El const_cast <> () se usa para agregar/eliminar const (ness) (o volátiles) de una variable.

transmisión_estática

static_cast (expresión) El static_cast <> () se usa para emitir entre los tipos enteros.'Eg' char-> long, int-> corto etc.

El elenco estático también se usa para emitir punteros a los tipos relacionados, por ejemplo, eliminar el vacío* al tipo apropiado.

transmisión_dinámica

Dynamic Cast se usa para convertir punteros y referencias en tiempo de ejecución, generalmente con el fin de lanzar un puntero o referencia hacia arriba o hacia abajo de una cadena de herencia (jerarquía de herencia).

emisión_dinámica(expresión)

El tipo de destino debe ser un puntero o tipo de referencia, y la expresión debe evaluar en un puntero o referencia.El reparto dinámico 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 de miembro virtual.Si no, y el tipo de expresión que se proyecta es un puntero, se devuelve NULL, si un reparto dinámico en una referencia falla, se lanza una excepción BAD_Cast.Cuando no falla, Dynamic Cast devuelve un puntero o referencia del tipo de destino al objeto al que se refería la expresión.

reinterpretar_cast

Reinterpret cast simplemente convierte un tipo bit a otro.Cualquier puntero o tipo integral se puede lanzar a cualquier otro con el elenco de reinterpret, lo que permite fácilmente un mal uso.Por ejemplo, con Reinterpret Cast One podría, inseguro, lanzar un puntero entero a un puntero de cadena.

Para su información, creo que se cita a Bjarne Stroustrup diciendo que se deben evitar las conversiones de estilo C y que se debe usar static_cast odynamic_cast si es posible.

Preguntas frecuentes sobre el estilo C++ de Barne Stroustrup

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

Evite el uso de yesos estilo C.

Las conversiones estilo C son una combinación de conversión constante y reinterpretación, y es difícil encontrarlas y reemplazarlas en su código.Un programador de aplicaciones C++ debería evitar la conversión al estilo C.

Las conversiones de estilo C combinan const_cast, static_cast y reinterpret_cast.

Desearía que C ++ no tuviera versiones de estilo C.Las conversiones de C++ se destacan adecuadamente (como deberían;Las conversiones normalmente indican que se está haciendo algo malo) y distinguir adecuadamente entre los diferentes tipos de conversión que realizan las conversiones.También permiten escribir funciones de aspecto similar, p.boost::lexical_cast, lo cual es bastante bueno desde una perspectiva de coherencia.

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

dynamic_cast solo admite tipos de puntero y referencia.Vuelve NULL si la conversión es imposible si el tipo es un puntero o arroja una excepción si el tipo es un tipo de referencia.Por eso, dynamic_cast se puede utilizar para comprobar si un objeto es de un tipo determinado, static_cast no puede (simplemente terminará con un valor no válido).

Los moldes de estilo C (y otros) se han cubierto en las otras respuestas.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top