C++类型转换关键字的用法

424 阅读3分钟

c++中有很多类型转换的关键字,包括:

  • 兼容c的强制类型转换
  • static_cast
  • dynamic_cast
  • reinterpret_cast
  • const_cast 本文对这些转换方法进行一些总结

1. c强制类型转换

  int num1 = 5;
  double num2 = 134878357887823745980.0;
  char ch = 'c';

  int *pnum1 = &num1;
  double *pnum2 = &num2;

  // test c-format cast
  int test = (int)num2;
  int test1 = (int)ch;
  long test2 = (long)pnum1;
  int test4 = (int)pnum1;   // Error: 8bytes --> 4bytes
  int test4 = (long)pnum1;
  char ch2 = (char)num2;
  char ch3 = (char)3876;

可以看得到C风格的转换非常自由,doule<-->int, int<-->long, ptr<-->long, int/long<-->char都不会报错,只有在将ptr转换为int的时候由于占用空间不同出现报错。一般不应该使用c类型的转换方式

2. static_cast

    // test static_cast
    struct Base {
      int a1 = 23;
    };
    struct Derived: public Base {
      int b1 = 33;
    };
    Base b1;
    Derived b2;
    int test = static_cast<int>(num2);
    int test1 = static_cast<int>(ch);
    long test2 = static_cast<long>(pnum1); // Error: value to pointer
    int test4 = static_cast<int>(pnum1);   // Error: value to pointer
    int test5 = static_cast<long>(pnum1);  // Error: value to pointer
    char ch2 = static_cast<char>(num2);
    char ch3 = static_cast<char>(3876);
    int test6 = static_cast<int>(b1);      // Error: class to int
    Base c1 = static_cast<Base>(b2);
    int *p1 = static_cast<int*>(p2);       // Error: pointer size not fit
    Derived c2 = static_cast<Derived>(b1); // Error: up-side is invalid

static_cast在对值类型做转换(int,long,double,char)的时候几乎不做检查, 在对指针做转换的时候会检查指针指向的类型, 对象做转换的时候, 只能子类向父类转换

static_cast使用范围较广,主要在于显式标出了类型转换,表明作者能够接受转换的副作用

3. dynamic_cast

    // test dynamic_cast
    int test = dynamic_cast<int>(num2);
    int test1 = dynamic_cast<int>(ch);
    long test2 = dynamic_cast<long>(pnum1);
    int test4 = dynamic_cast<int>(pnum1);
    int* p1 = dynamic_cast<int*>(pnum1);
    Base b1;
    Derived b2;
    Base c1 = dynamic_cast<Base>(b2);
    Base* p2 = dynamic_cast<Base*>(&b2);  // Rright
    Derived* p3 = dynamic_cast<Derived*>(&b1);

dynamic_cast只能做类指针和引用之间的转换,适用范围较窄。并且也只能由子类向父类转换

4. reinterpret_cast

    // test reinterpret_cast
    double test0 = reinterpret_cast<double>(num1);  // Error: int to double
    int test = reinterpret_cast<int>(num2);         // Error: double to int
    int test1 = reinterpret_cast<int>(ch);          // Error: char to int
    long test2 = reinterpret_cast<long>(pnum1);     // Right: ptr to long -- bytes fit
    int test4 = reinterpret_cast<int>(pnum1);       // Error: ptr to int -- bytes not fit
    int* p1 = reinterpret_cast<int*>(pnum1);        // Right: ptr to ptr is always right
    Base b1;
    Derived b2;
    Base c1 = reinterpret_cast<Base>(b2);           // Right: to father class
    Base* p2 = reinterpret_cast<Base*>(&b2);        // Rright
    Derived* p3 = reinterpret_cast<Derived*>(&b1);  // Error: to son class

reinterpret_cast原理上的解释是,不改变目标的存储bits,只对其进行重新解释。从而可知,将double转化为int/long数值肯定没有意义,从而会失败;指针间进行变换都有意义--默认作者的变换全在掌控范围内;子类对象转换为父类对象也有意义;需要注意的是指针转换为相同bytes的整型默认也有意义

5. const_cast

const_cast用来移除指针或者引用的const性质

  {
    const int a1 = 33;
    int *b1 = const_cast<int*>(&a1);
    *b1 = 44;
    cout << a1 << " " << *b1 << " " << &a1 << " " << b1 << endl;
  }
  
  output:
  33 44 0x7ffee1409794 0x7ffee1409794

虽然const_cast能够转化const指针,但是用转换后的指针重新赋值常量,是一个undefined behavior, 上述代码中展示了虽然地址相同,但是a1输出仍保持33不变。原因可以猜测一下(喜欢研究的可以看下反编译代码),编译器在编译阶段认为a1是常量直接用33替换了a1出现的所有位置,所以cout作用域的a1实际上已经替换成了数值33

所以const_cast使用场景基本用于某个函数输入参数用到了常量指针,但是定义的时候忘记了加const关键字,函数体内并不会修改指针指向的内容,此时可以用const_cast转换做一下妥协

6. c风格类型转化与static_cast和reinterpret_cast的关系

c风格的类型转换本质上是先用static_cast进行转换,失败的话尝试用reinterpret_cast