• [C++] C++风格的强制类型转换探讨


    C++风格的强制类型转换:const_caststatic_castdynamic_castreinterpret_cast

    使用:xxx_cast<type-id> (expression)

    const_cast

    • 说明:该运算符用来修改类型的const或volatile属性。
     1 class TestA
     2 {
     3 public:
     4     TestA() { }
     5 public:
     6     int m_data;
     7 };
     8 
     9 void foo()
    10 {
    11     const TestA ta;
    12     //ta1.m_data = 100;    //编译错误
    13 
    14     TestA *pTa = const_cast<TestA*>(&ta);
    15     pTa->m_data = 200;     //OK
    16     
    17     TestA &taTmp = const_cast<TestA&>(ta);
    18     taTmp.m_data = 300;    //OK
    19 }
    20 
    21 int main( int argc, char * argv[] )
    22 {
    23     foo();
    24     return 0;
    25 }

     

    static_cast

    • 说明:无条件强制转换,没有在运行时进行类型检查来保证转换的安全性,static_cast也不能去掉类型的const、volitale属性。
    • 场景:
    1. 基类和子类之间转换:进行上行转换(把派生类的指针或引用转换成基类表示)是安全的;进行下行转换(把基类指针或引用转换成派生类表示)时,由于没有动态类型检查,所以是不安全的。
    2. 用于基本数据类型之间的转换,如把int转换成char,把int转换成enum等。
    3. 把空指针转换成目标类型的空指针。
    4. 把任何类型的表达式转换成void类型。
     1 void foo()
     2 {
     3     int n = 6;
     4     char c = static_cast<char>(n);          // 基本类型转换
     5     double d = static_cast<double>(n);
     6     float f = static_cast<float>(n);
     7     
     8     int *pn = &n;
     9     //double *d = static_cast<double*>(&n)  //无关类型指针转换,编译错误
    10     void *p = static_cast<void*>(pn);       //任意类型转换成void类型
    11 }
    12 
    13 int main( int argc, char * argv[] )
    14 {
    15     foo();
    16     return 0;
    17 }

    dynamic_cast

    • 说明:type-id必须是类的指针、类的引用或者void*,运行时会进行类型安全检查(转换失败返回NULL)。
    • 场景:
    1. 基类和子类之间转换,上行和下行转换都是安全的。
    2. 基类必须要有虚函数。
    3. 交叉转换(相同基类的不同子类之间的转换),但结果是NULL。
     1 #include <string.h>
     2 #include <iostream>
     3 
     4 using namespace std;
     5 
     6 class BaseClass 
     7 {
     8 public:
     9     int m_data;
    10     virtual void btest(){}; //基类必须有虚函数
    11 };
    12 
    13 class DerivedClass : public BaseClass 
    14 {
    15 public:
    16     char m_arr[100];
    17     void dtest(){};
    18 };
    19 
    20 void foo()
    21 {
    22     BaseClass* pb = new DerivedClass();
    23     DerivedClass *pd1 = static_cast<DerivedClass *>(pb);   //子类->父类,静态类型转换,正确但不推荐
    24     DerivedClass *pd2 = dynamic_cast<DerivedClass *>(pb);  //子类->父类,动态类型转换,正确
    25 
    26     BaseClass* pb2 = new BaseClass();
    27     DerivedClass *pd3 = static_cast<DerivedClass *>(pb2);  //父类->子类,静态类型转换,危险!
    28     //strcpy(pd3->m_arr, "0123456789");                    //访问子类m_arr成员越界,造成运行时错误。
    29 
    30     DerivedClass *pd4 = dynamic_cast<DerivedClass *>(pb2); //父类->子类,动态类型转换,安全。结果为NULL。
    31     if (NULL == pd4)
    32     {
    33         cout << "null == pd4" << endl;
    34     }
    35 }
    36 
    37 int main( int argc, char * argv[] )
    38 {
    39     foo();
    40     return 0;
    41 }

    reinterpret_cast

    • 说明:仅仅重新解释类型,但没有做字节对齐。
    • 场景:最普通的用途就是在函数指针类型之间进行转换,但是很难保证移植性。
     1 #include<iostream>
     2 
     3 using namespace std;
     4 
     5 class TestA
     6 {
     7 public:
     8     int m_a;
     9 };
    10 
    11 class TestB 
    12 {
    13 public:
    14     int m_b;
    15 };
    16 
    17 class TestC : public TestA, public TestB 
    18 {};
    19 
    20 void foo()
    21 {
    22     TestC tc;
    23 
    24     cout << "&tc = 0x" << &tc << endl;
    25     cout << "reinterpret_cast<TestB*>(&tc) = 0x" << reinterpret_cast<TestB*>(&tc) << endl;
    26     cout << "static_cast<TestB*>(&tc) = 0x" << static_cast<TestB*>(&tc) << endl;
    27 }
    28 
    29 int main( int argc, char * argv[] )
    30 {
    31     foo();
    32     return 0;
    33 }

    结果:

    static_cast转换之后结果 - reinterpret_cast转换之后的结果 = 4(即该测试类中的字节对齐长度)。

    说明:static_cast在进行上行转换是安全的,static_cast计算了父子类指针转换的偏移量,并将之转换到正确的地址,而reinterpret_cast则不会做这层转换。

    总结

    • const_cast:去const或volatile属性时用到。
    • static_cast:基本类型转换(不包括指针类型)时用到。
    • daynamic_cast:基类与子类的指针(或引用)互相转换时用到。
    • reinterpret_cast不同类型的指针类型转换(如:函数指针类型之间进行转换),慎用。

    参考

    (完)

  • 相关阅读:
    package.json文件
    Node.js中模块加载机制
    第三方模块
    系统模块
    Node.js快速入门及模块化开发
    String 的扩展方法
    ES6 的内置对象扩展
    箭头函数
    解构赋值
    let、const、var 的区别
  • 原文地址:https://www.cnblogs.com/helloamigo/p/3506304.html
Copyright © 2020-2023  润新知