Aprendizaje de C++: cuatro tipos de conversiones obligatorias en C++ static_cast, dynamic_cast, const_cast, reinterpret_cast

1. Reparto de C y reparto de C++

La conversión de tipo obligatorio en lenguaje C se utiliza principalmente para la conversión entre tipos de datos básicos, la sintaxis es:

(type-id) expression;  //转换格式1
type-id (expression);  //转换格式2

Además de la conversión de tipo obligatoria del lenguaje c, c++ ha agregado cuatro nuevos tipos de conversión de tipo obligatoria: static_cast, dynamic_cast, const_cast, reinterpret_cast, que se utilizan principalmente para la conversión obligatoria entre clases de relación de herencia. La sintaxis es:

static_cast<new_type>      (expression)
dynamic_cast<new_type>     (expression) 
const_cast<new_type>       (expression) 
reinterpret_cast<new_type> (expression)

Nota: new_type es el tipo de datos de destino y expresión es la variable o expresión del tipo de datos original.

En "Effective C++", la conversión del lenguaje c se denomina conversión de estilo antiguo y la conversión de c++ se denomina nueva conversión .

二, static_cast, dynamic_cast, const_cast, reinterpret_cast

     1: const_cast

const_cast se utiliza para modificar un atributo constante o volátil de un tipo. Este operador se usa para modificar una constante (el único operador de conversión de estilo C++ capaz de hacerlo) o un atributo volátil de un tipo. Excepto por la decoración const o volátil, new_type y expression son del mismo tipo.

①El puntero constante se convierte en un puntero no constante y sigue apuntando al objeto original;

②La referencia constante se convierte en una referencia no constante y aún apunta al objeto original;

③const_cast generalmente se usa para modificar el puntero inferior. Como la forma const char *p.

Un ejemplo de conversión es el siguiente:

const int g = 20;
int *h = const_cast<int*>(&g);     //去掉const常量const属性

const int g = 20;
int &h = const_cast<int &>(g);     //去掉const引用const属性

 const char *g = "hello";
char *h = const_cast<char *>(g);   //去掉const指针const属性

2: transmisión_dinámica

dynamic_cast<type*>(e)
dynamic_cast<type&>(e)
dynamic_cast<type&&>(e)

type debe ser un tipo de clase, en el primer formulario type debe ser un puntero válido, en el segundo formulario type debe ser un lvalue, en el tercer formulario type debe ser un rvalue. En todas las formas anteriores, el tipo de e debe cumplir cualquiera de las tres condiciones siguientes: el tipo de e es una clase pública derivada del tipo de destino type, el tipo de e es una clase base común del tipo de destino o el tipo de e es el tipo del tipo de destino. Si una declaración de transmisión dinámica se convierte en un tipo de puntero y falla, el resultado es 0. Si el objetivo de conversión es un tipo de referencia y falla, el operador de transmisión dinámica generará una excepción std::bad_cast (la excepción se define en el archivo de encabezado de la biblioteca estándar typeinfo). e también puede ser un puntero nulo y el resultado es un puntero nulo del tipo deseado.

dynamic_cast se usa principalmente para la conversión ascendente y descendente entre jerarquías de clases, y también se puede usar para la conversión cruzada entre clases. 

El efecto de dynamic_cast y static_cast es el mismo cuando se realiza una conversión ascendente entre jerarquías de clases;

Durante el downcasting, dynamic_cast tiene la función de verificación de tipos y es más seguro que static_cast. dynamic_cast es la única acción que no se puede realizar con la sintaxis antigua y la única acción de conversión que puede tener un costo de tiempo de ejecución significativo.

(1) Tipo de puntero

Por ejemplo, Base es una clase base que contiene al menos una función virtual, Derived es una clase derivada común de Base, si hay un puntero bp que apunta a Base, podemos convertirlo en un puntero que apunta a Derived en tiempo de ejecución, el código es el siguiente:

if(Derived *dp = dynamic_cast<Derived *>(bp)){
  //使用dp指向的Derived对象  
}
else{
  //使用bp指向的Base对象  
}

Vale la pena señalar que en el código anterior, dp se define en la declaración If. La ventaja de esto es que se pueden completar dos tareas de conversión de tipo y verificación de condición en una sola operación.

(2) Tipo de referencia

Debido a que no existe la llamada referencia nula, la conversión de transmisión dinámica del tipo de referencia es diferente del tipo de puntero. Cuando la conversión de referencia falla, se lanzará una excepción std::bad_cast, que se define en el archivo de encabezado typeinfo.

void f(const Base &b){
 try{
   const Derived &d = dynamic_cast<const Base &>(b);  
   //使用b引用的Derived对象
 }
 catch(std::bad_cast){
   //处理类型转换失败的情况
 }
}

3:static_cast

static_cast es equivalente a la conversión forzada en el lenguaje C tradicional. Este operador convierte la expresión al tipo new_type y se usa para forzar la conversión implícita, como convertir un objeto no constante en un objeto constante. La verificación en tiempo de compilación se usa para la conversión no polimórfica. Puede convertir punteros y otros, pero no hay una verificación de tipo en tiempo de ejecución para garantizar la seguridad de la conversión . Tiene principalmente los siguientes usos:

①Utilizado para la conversión de punteros o referencias entre la clase base (clase principal) y la clase derivada (subclase) en la jerarquía de clases.

Upcasting (convertir un puntero o referencia de una clase derivada a una representación de clase base) es seguro ;

Downcasting (convertir punteros de clase base o referencias a representaciones de clases derivadas) no es seguro porque no hay verificación dinámica de tipos .

② Se utiliza para la conversión entre tipos de datos básicos, como convertir int a char y convertir int a enum. La seguridad de esta conversión también debe ser garantizada por el desarrollador.

③Convierta el puntero nulo en un puntero nulo del tipo de destino.

④ Convierta cualquier tipo de expresión en tipo vacío.

Nota: static_cast no puede convertir atributos de expresión constantes, volátiles o __no alineados.

Ejemplos de conversión de datos de tipo básico son los siguientes:

char a = 'a';
int b = static_cast<char>(a);//正确,将char型数据转换成int型数据

double *c = new double;
void *d = static_cast<void*>(c);//正确,将double指针转换成void指针

int e = 10;
const int f = static_cast<const int>(e);//正确,将int型数据转换成const int型数据

const int g = 20;
int *h = static_cast<int*>(&g);//编译错误,static_cast不能转换掉g的const属性

Conversión de enlace ascendente y descendente de clase:

if(Derived *dp = static_cast<Derived *>(bp)){//下行转换是不安全的
  //使用dp指向的Derived对象  
}
else{
  //使用bp指向的Base对象  
}

if(Base*bp = static_cast<Derived *>(dp)){//上行转换是安全的
  //使用bp指向的Derived对象  
}
else{
  //使用dp指向的Base对象  
}

4: reinterpretar_cast

new_type debe ser un puntero, una referencia, un tipo aritmético, un puntero de función o un puntero de miembro. Puede convertir un puntero en un entero, o convertir un entero en un puntero (primero convertir un puntero en un entero, luego convertir el entero en un puntero del tipo original y obtener el valor del puntero original).

reinterpret_cast está destinado a realizar una conversión de bajo nivel, la acción real (y el resultado) pueden depender del editor, lo que significa que no es portátil .

  Para dar un ejemplo del uso incorrecto de reintepret_cast, después de convertir el tipo entero en un puntero de función, vc++ informará un error de "excepción no controlada en 0xxxxxxxxx en...: 0xC0000005: Infracción de acceso" durante la ejecución:

#include <iostream>
using namespace std;
int output(int p){
    cout << p <<endl;  return 0;
}

typedef int (*test_func)(int );//定义函数指针test_func
int main(){
    int p = 10;
    test_func fun1 = output;
    fun1(p);//正确
    test_func fun2 = reinterpret_cast<test_func>(&p);
    fun2(p);//...处有未经处理的异常: 0xC0000005: Access violation
    return 0;
}

La guía C++ de IBM , la página de preguntas frecuentes de Bjarne Stroustrup, el padre de C++, y Visual C++ de MSDN también señalan que el uso incorrecto de reinterpret_cast puede conducir fácilmente a la inseguridad del programa, y ​​solo el valor del tipo convertido se vuelve a convertir a su tipo original, que es la forma correcta de usar reinterpret_cast.

  También se menciona en MSDN que, en la práctica, reinterpret_cast se puede aplicar a la función hash, de la siguiente manera (en sistemas de 64 bits, unsigned int debe cambiarse a unsigned long):

// expre_reinterpret_cast_Operator.cpp
// compile with: /EHsc
#include <iostream>

// Returns a hash code based on an address
unsigned short Hash( void *p ) {
   unsigned int val = reinterpret_cast<unsigned int>( p );
   return ( unsigned short )( val ^ (val >> 16));
}

using namespace std;
int main() {
   int a[20];
   for ( int i = 0; i < 20; i++ )
      cout << Hash( a + i ) << endl;
}

Además, la diferencia entre static_cast y reinterpret_cast está principalmente en la herencia múltiple, como

class A {
    public:
    int m_a;
};
 
class B {
    public:
    int m_b;
};
 
class C : public A, public B {};

ejemplo:

C c;
printf("%p, %p, %p", &c, reinterpret_cast<B*>(&c), static_cast <B*>(&c));

Los dos primeros valores de salida son los mismos y el último tendrá un desplazamiento de 4 bytes sobre la base original. Esto se debe a que static_cast calcula el desplazamiento de la conversión de puntero de clase padre-hijo y lo convierte a la dirección correcta (c tiene m_a, m_b, que se convierte en un puntero B * y apunta a m_b), mientras que reinterpret_cast no realizará este nivel de conversión.

 Por lo tanto, debe usar reinterpret_cast con precaución.

Precauciones:

  • Se prefieren las conversiones de estilo nuevo a las conversiones de estilo antiguo. Hay dos razones. Una es que las transformaciones de nuevo estilo son más fáciles de identificar, lo que puede simplificar el proceso de "descubrir dónde está roto el sistema de tipos". En segundo lugar, cuanto más estrecho sea el objetivo de cada acción de transformación, mejor podrá diagnosticar el compilador el uso incorrecto.
  • Trate de usar las operaciones de transformación lo menos posible, especialmente dynamic_cast, que lleva mucho tiempo y provocará una degradación del rendimiento. Trate de usar otros métodos en su lugar.

Supongo que te gusta

Origin blog.csdn.net/weiweiqiao/article/details/131328873
Recomendado
Clasificación