跳到正文
孔乙己

C++ 强制类型转换

C 风格强制转换的坑,以及 C++ 四种具名转换 static_cast、dynamic_cast、const_cast、reinterpret_cast 的适用场景与区别。

编程,C++1分钟阅读

在C语言中,类型转换主要有两种:隐式类型转换和显式类型转换。

  1. 隐式类型转换(Implicit Type Conversion) 在表达式中自动进行的类型转换,不需要使用特定的语法或运算法,比如常见的整数和浮点数之间的转换。
    char c ='A';
    int i =c; //隐式类型转换
  2. 显示类型转换(Explicit Type Conversion) 在表达式中使用特定的语法或运算符进行的类型转换,也称为强制类型转换。
    float f =3.15;
    int i =(int)f;

在C语言中。类型转换存在一些潜在的问题和不足之处,其中包括:

  1. 信息丢失:当一个较大的数据类型转换为一个较小的数据类型时,可能会发生信息丢失。例如,将一个浮点数转换为整数时,小数部分会被截断,导致精度丢失。
  2. 未定义行为:某些情况下,进行不适当的类型转换可能导致未定义的行为,这使得代码的行为不可预测。例如,在指针类型之间进行不正确的转换导致内存访问错误。
    #include <stdio.h>
    int main() {
    int arr[3] = {1, 2, 3};
    int *ptr = arr; // ptr指向数组arr的第一个元素
    printf("%d\n", ptr[0]);
    // 错误的指针类型转换
    float *floatPtr = (float *)ptr; // 将int指针转换为float指针
    // 尝试使用错误类型的指针访问数组元素
    printf("%f\n", floatPtr[0]); // 未定义行为,可能导致内存访问错误
    return 0;
    }
  3. 数据损坏:类型转换可能会导致数据损坏或失真。例如,将一个整数转换为一个较小的整数类型时,如果超出了目标类型的表示范围,结果可能会不正确。
  4. 可读性和可维护性:使用类型转换可能会降低代码的可读性和可维护性。在代码中使用大量的类型转换可能会使代码难以理解,并且增加了代码维护的复杂性。
  5. 难以排查错误:当类型转换被滥用或错误使用时,可能会导致代码中隐藏的错误。这些错误可能很难排查和修复,因为它们可能会在运行时产生不可预测的结果。

在C++中,常见的类型转换方法包括隐式类型转换和显式类型转换,而显示类型转换又可以进一步细分为四种:static_cast、dynamic_cast、const_cast和reinterpret_cast。

  1. 静态转换(static_cast)

    • 用于基本内置数据类型之间的转换
    float data = static_cast<float>(1)
    • 用于指针之间的类型转换
    #include <iostream>
    using namespace std;
    int main()
    {
    int type_int =10;
    //float* float_ptr1= &type_int; //隐式转换无效 error: cannot convert ‘int*’ to ‘float*’ in initialization
    //float* float_ptr2= static_cast<float*>(&type_int); //error: invalid static_cast from type ‘int*’ to type ‘float*’
    float* float_ptr3= (float*)&type_int;
    cout <<"float_ptr3:"<<*float_ptr3<<endl; //编译器运行通过,但是输出不对float_ptr3:1.4013e-44
    void* void_ptr=&type_int; //任何指针都可以隐士转换为void*
    //float* float_ptr4 = void_ptr; //error: invalid conversion from ‘void*’ to ‘float*
    float* float_ptr5 = static_cast<float*>(void_ptr);
    cout <<"float_ptr5:"<<*float_ptr5<<endl; //编译器运行通过,但是输出不对float_ptr5:1.4013e-44
    float * float_ptr6 = (float *) void_ptr;
    cout <<"float_ptr6:"<<*float_ptr6<<endl; //编译器运行通过,但是输出不对float_ptr6:1.4013e-44
    return 0;
    }
    • 用于类之间的类型转换
      #include <iostream>
      using namespace std;
      class A
      {
      public:
      int a{1};
      };
      class B
      {
      public:
      int b{2};
      };
      class C: public A, public B
      {
      public:
      int c{3};
      };
      int main()
      {
      C c;
      A* a_ptr =static_cast<A*>(&c);//上行指针转换正常
      cout<<"A a: "<< a_ptr->a<<endl;
      B* b_ptr =static_cast<B*>(&c);//上行指针转换正常
      cout<<"B b: "<< b_ptr->b<<endl;
      A& a_ref =static_cast<A&>(c);//上行引用转换正常
      cout<<"A a: "<< a_ref.a<<endl;
      B& b_ref =static_cast<B&>(c); //上行引用转换正常
      cout<<"B b: "<< b_ref.b<<endl;
      A a;
      B b;
      C* c_ptr1 =static_cast<C*>(&a);
      C* c_ptr2 =static_cast<C*>(&b);
      cout<<"c_ptr1 c: "<<c_ptr1->c<<endl; //2 下行转换指针无效,编译通过,结果不对
      cout<<"c_ptr2 c: "<<c_ptr2->c<<endl; //1 下行转换指针无效,编译通过,结果不对
      C& c_ref1 =static_cast<C&>(a);
      C& c_ref2 =static_cast<C&>(b);
      cout<<"c_ref1 c: "<<c_ref1.c<<endl; //2 下行转换引用无效,编译通过,结果不对
      cout<<"c_ref2 c: "<<c_ref2.c<<endl; //1 下行转换引用无效,编译通过,结果不对
      return 0;
      }
    - 用于具多态的类实例指针或引用之间的转换
  2. 动态转换(dynamic_cast)

    • 用于多态类型的安全转换,通常在基类和派生类之间转换时使用。
    • 运行时执行类型检查,只能用于指针或引用类型,并且要求目标类型必须有虚函数。
    • 如果转换不安全,返回空指针(在指针转换中),或抛出std::bad_cast异常(在引用转换中)。
    Base* basePtr = new Derived();
    Derived* derivedPtr = dynamic_cast<Derived*>(basePtr); // 动态转换
  3. 常量转换(const_cast)

    • 用于添加或删除’const’、’volatile’修饰符。
    • 主要用于修改常量对象的常量性或者将常量引用转换为非常量引用,以允许对其进行修改。
    const int x = 5;
    int& y = const_cast<int&>(x); // 取消常量性
    y = 10; // 合法
  4. 重解释转换(reinterpret_cast)

    • 用于不同类型之间的位模式重新解释。
    • 是一种较低级别的转换,可以将任何指针类型转换为其他指针类型,也可以将任何整数类型转换为指针类型,反之亦然
    • 非常危险,可能导致未定义的行为,应谨慎使用。
    int* ptr = reinterpret_cast<int*>(somePointer);

评论