• 浅谈static_cast、dynamic_cast、const_cast、reinterpret_cast用法


    C++中的类型转换分两种:隐式类型转换和显式类型转换。

    隐式转换,是标准的转换,很多时候是默认情况下由编译器进行转换;

    显式转换,在C++中有四个类型的转换符:static_cast、dynamic_cast、const_cast、reinterpret_cast。

    1. static_cast用法

          转换格式:

    1. 将expression转换为type_id类型,主要用于非多态类型之间的转换
    2. static_cast转换,不能转换掉expression的const、volatile、和_unaligned属性

    static_cast(expression)

    • 场景1,用于类层次结构中,基类和子类之间的指针或引用的转换

            【1】上行转换,子类指针或引用转父类,这种转换是安全的。

             多态类型之间转换:使用static_cast和使用dynamic_cast效果一样;

             非多态类型之间转换:只能使用static_cast,使用dynamic_cast会编译报错

    1. //先定义一个父类A,和子类B
    2. class A
    3. {
    4. public:
    5. virtual void Func() { cout << "A::Func()" << endl; }
    6. };
    7. class B : public A
    8. {
    9. public:
    10. void Func() override { cout << "B::Func()" << endl; }
    11. };
    12. int main()
    13. {
    14. A* pa = new B;
    15. A* a = static_cast(pb);//ok,转换是安全的
    16. B* pb = new B;
    17. A* a = static_cast(pb);//ok,转换是安全的
    18. return 0;
    19. }

            【2】下行转换,父类指针或引用转子类,这种转换是不安全的

    1. int main()
    2. {
    3. //B* pb = new A; //error,编译报错,无法从A*转换为B*
    4. A* pa = new A;
    5. B* b = static_cast(pa); //ok,编译无误,b != nullptr,这是不安全的,没有做类型检查
    6. return 0;
    7. }
    • 场景2,用于基本数据类型之间的转换,如把int转成char,把int转换成enum等等,这种转换是不安全的

            char转int,是安全的

    1. int iVar1 = 10;
    2. char cVar1 = 'a';
    3. iVar1 = static_cast<int>(cVar1);
    4. cout << iVar1 << endl;

            int转char,是不安全的,由ASSIC表得出,只有int[32,126]区间,转换才是安全的

    1. int iVar2 = 10;
    2. char cVar2 = 'a';
    3. cVar2 = static_cast<char>(iVar2);
    4. cout << cVar2 << endl;
    •  场景3,把void指针转换成目标类型,这种转换是及其不安全的,编译报错
    1. int a = 10;
    2. void* pa = &a;
    3. int b = static_cast<int>(pa);
    4. cout << b << endl;
    5. float c = static_cast<float>(pa);
    6. cout << c << endl;

    •  另外,static_cast转换,不能转换掉expression的const、volatile、和_unaligned属性
    1. const int iVar = 110;
    2. int* pVar1 = static_cast<int*>(&iVar);//编译报错
    3. const int* pVar2 = static_cast<const int*>(&iVar);//编译无误,通过

    2. dynamic_cast用法

            转换格式:

    1. 将expression转换为type-id类型,type-id必须是类的指针或引用或void*;
    2. 如果type-id是指针类型,那么expression也必须是一个指针;
    3. 如果type-id是引用类型,那么expression也必须是一个引用;
    4. dynamic_cast主要用于类层次间的上行转换和下行转换,还可以用于类之间的交叉转换;其中进行上行转换时,dynamic_cast和static_cast的效果一样;进行下行转换时,dynamic_cast具有类型检查功能,比static_cast更安全;
    5. 在多态类型之间转换主要使用dynamic_cast,因为类型提供了运行时信息。

    dynamic_cast(expression)

    • 场景1,用于类层次结构中,基类和子类之间的指针或引用的转换

            【1】上行转换(单继承),子类指针或引用转父类,这种转换是安全的。

             多态类型之间转换:使用static_cast和使用dynamic_cast效果一样;

             非多态类型之间转换:只能使用static_cast,使用dynamic_cast会编译报错

    1. class A
    2. {
    3. public:
    4. virtual void Func(){cout << "A::Func()" << endl;}
    5. };
    6. class B: public A
    7. {
    8. public:
    9. virtual void Func() override{cout << "B::Func()" << endl;}
    10. };
    11. int main()
    12. {
    13. B* pb = new B;
    14. A* a1 = dynamic_cast(pb); //OK
    15. a1->Func();
    16. A* pa = new B;
    17. A* a2 = dynamic_cast(pa); //OK
    18. a2->Func();
    19. return 0;
    20. }

            如果Func为virtual,输出结果:        

            如果Func为非virtual,输出结果:    


           【2】上行转换(多重继承),子类指针或引用转父类,这种转换是安全的 

    1. class A
    2. {
    3. public:
    4. virtual void Func(){}
    5. };
    6. class B: public A
    7. {
    8. public:
    9. virtual void Func() override{}
    10. };
    11. class C : public B
    12. {
    13. public:
    14. virtual void Func() override{}
    15. };
    16. int main()
    17. {
    18. C* c = new C;
    19. B* b = dynamic_cast(c); //OK
    20. A* a = dynamic_cast(c); //OK
    21. }
    • 场景2,将类转换为void*,A类和B类中必须包含虚函数

            原因,类中存在虚函数,说明有想让基类指针或引用指向派生类对象的情况,此时转换才有意义;由于运行时类型检查需要运行时类型信息(该运行信息存储在类的虚函数表中),所以只有定义了虚函数的类才有虚函数表。

    1. class A
    2. {
    3. public:
    4. virtual void Func() {} //注意关键字virtual
    5. // ......
    6. };
    7. class B : public A
    8. {
    9. public:
    10. virtual void Func() {} //注意关键字virtual
    11. // ......
    12. };
    13. int main()
    14. {
    15. A *pA = new A;
    16. B *pB = new B;
    17. void *pV = dynamic_cast<void *>(pA); // pV points to an object of A
    18. pV = dynamic_cast<void *>(pB); // pV points to an object of B
    19. system("pause");
    20. return 0;
    21. }

              去掉virtual关键字,会编译报错 

    • 场景3,如果expression类型是type-id的基类,使用dynamic_cast进行转换时,在运行时会检查expression是否真正的指向一个type-id类型的对象。如果是,则能进行正确的转换,得到真正的值;否则返回NULL;如果是引用,则在运行时就会抛出异常。

            【1】下行转换(单继承),父类指针或引用转子类,会做类型安全检查,这种转换是安全的

    1. class A
    2. {
    3. public:
    4. virtual void Func(){cout << "A::Func()" << endl;}
    5. };
    6. class B: public A
    7. {
    8. public:
    9. virtual void Func() override{cout << "B::Func()" << endl;}
    10. };
    11. int main()
    12. {
    13. //B* pb = new A; //error,编译报错,无法从父类A*转换为子类B*
    14. /*
    15. * 由父类A*转换子类B*,使用dynamic_cast能做到安全检查,转换失败会返回null;
    16. * 使用static_cast不安全
    17. */
    18. A* pa1 = new B;
    19. B* b1 = dynamic_cast(pa1); //ok,编译无误,b != nullptr
    20. if (nullptr != b1)
    21. {
    22. b1->Func();
    23. }
    24. A* pa2 = new A;
    25. B* b2 = dynamic_cast(pa2); //ok,编译无误,b = nullptr
    26. if (nullptr != b2)
    27. {
    28. b2->Func();
    29. }
    30. return 0;
    31. }

            【2】 对于一些复杂继承关系,使用dynamic_cast进行转换是存在一些陷阱的

            如下做法存在陷阱,

    1. class A
    2. {
    3. virtual void Func() = 0;
    4. };
    5. class B : public A
    6. {
    7. void Func() { cout << "B::Func()" << endl; }
    8. };
    9. class C : public A
    10. {
    11. void Func() { cout << "C::Func()" << endl; };
    12. };
    13. class D : public B, public C
    14. {
    15. void Func() { cout << "D::Func()" << endl; }
    16. };
    17. int main()
    18. {
    19. D *pD = new D;
    20. /*
    21. * B和C都继承了A,并且都实现了虚函数Func,导致在进行转换时,无法进行抉择应该向哪个A进行转换
    22. */
    23. A *pA = dynamic_cast(pD); // pA = NULL

             正确的做法是,像多重继承关系这种情况,需要把子类逐步转换到父类

    1. int main()
    2. {
    3. D *pD = new D;
    4. B *pB = dynamic_cast(pD);

    3. const_cast用法

           转换格式:const_cast用来将类型的const、volatile和_unaligned属性移除。

    1. 常量指针被转换为非常量指针,并且仍然指向原来的对象;
    2. 常量引用被转换为非常量引用,并且仍然引用原来的对象;
    3. 不能直接对非指针和非引用的变量使用const_cast操作符去直接移除它的const、volatile和__unaligned属性。

    const_cast (expression)

    • 场景1,常量指针转非常量指针
    1. class A
    2. {
    3. public:
    4. A() :m_iA(10) {}
    5. int m_iA;
    6. };
    7. int main()
    8. {
    9. //常量指针转非常量指针
    10. const A *pA = new A;
    11. //pA->m_iA = 100; //error,pA常量指针,指向的内容不能修改
    12. cout << pA->m_iA << endl;
    13. A *pB = const_cast(pA); //去const属性
    14. pB->m_iA = 100; //通过指针,修改值
    15. //指针pA和pB指向同一个对象
    16. cout << pA->m_iA << endl;
    17. cout << pB->m_iA << endl;
    18. return 0;
    19. }
    • 场景2,指针常量转非指针常量
    1. class A
    2. {
    3. public:
    4. A() :m_iA(10) {}
    5. int m_iA;
    6. };
    7. int main()
    8. {
    9. //常量指针转非常量指针
    10. A * const pA1 = new A;
    11. pA1->m_iA = 100; //ok,pA是指针常量,指向的内容可以修改
    12. cout << pA1->m_iA << endl;
    13. A *pA2 = new A;
    14. //pA1 = pA2; //error,pA是指针常量,指向的地址不能修改
    15. A *pB = const_cast(pA1); //去const属性
    16. pB = pA2; //通过指针,修改值
    17. pB->m_iA = 111;
    18. //指针pA和pB指向同一个对象
    19. cout << pA1->m_iA << endl;
    20. cout << pA2->m_iA << endl;
    21. cout << pB->m_iA << endl;
    22. return 0;
    23. }
    • 场景3,常量引用转非常量引用
    1. int main()
    2. {
    3. const int &num = 10;
    4. //num = 100; //error,不能给常量赋值
    5. int &num2 = const_cast<int&>(num);
    6. num2 = 100;
    7. return 0;
    8. }
    •  场景4,不能给非常量指针和引用使用const_cast
    1. int main()
    2. {
    3. const int num1 = 10;
    4. //int num2 = const_cast<int>(num1); //error,编译报错,无法从“const int”转换为“int
    5. return 0;
    6. }

    4. reinterpret_cast用法

            转换格式:

    reinterpret_cast (expression)

            reinterpret_cast后的尖括号中的type-id类型必须是一个指针、引用、算术类型、函数指针或者成员指针。它可以把一个指针转换成一个整数,也可以把一个整数转换成一个指针。 

  • 相关阅读:
    存储器的性能指标以及层次化存储器
    Llama 2: 深入探讨ChatGPT的开源挑战者
    Skywalking UI使用攻略
    RK3568+鸿蒙工控板工业网关视频网关解决方案
    JavaEE——spring MVC请求处理
    猴子吃桃问题--c语言
    SCHNOKA施努卡:电池模组回收铣削多功能一体机
    FreeSWITCH dtmf事件测试
    Insertion or Heap Sort(堆排序解释)(PAT甲级)
    如何从异步调用中返回响应
  • 原文地址:https://blog.csdn.net/weixin_43712770/article/details/125893843