• 类型转换C11


    类型转换

    C++类型转换分为显式类型转换和隐式类型转换 ,隐式类型转换由编译器自动完成,这里只讨论显式类型转换。

    旧式风格的类型转换

    type(expr); // 函数形式的强制类型转换
    (type)expr; // C语言风格的强制类型转换
    
    • 1
    • 2

    现代C++风格的类型转换

    cast-name<type>(expression)
    
    • 1

    type是转换的目标类型。

    expression是被转换的值。

    cast-name有static_cast,dynamic_cast,const_cast和reinterpret_cast四种,表示转换的方式。

    static_cast

    格式:

    static_cast<type>(expression)
    
    • 1

    任何编写程序时能够明确的类型转换都可以使用static_cast(static_cast不能转换掉底层const,volatile和__unaligned属性)。由于不提供运行时的检查,所以叫static_cast,因此,需要在编写程序时确认转换的安全性。

    主要在以下几种场合中使用:

    1. 用于类层次结构中,父类和子类之间指针和引用的转换;进行上行转换,把子类对象的指针/引用转换为父类指针/引用,这种转换是安全的;进行下行转换,把父类对象的指针/引用转换成子类指针/引用,这种转换是不安全的,需要编写程序时来确认;
    2. 用于基本数据类型之间的转换,例如把int转char,int转enum等,需要编写程序时来确认安全性;
    3. 把void指针转换成目标类型的指针(这是极其不安全的);

    示例:

    int i, j;
    double slope = static_cast<double>(j) / i;
    void *p = &d;
    double *dp = static_cast<double*>(p);
    
    • 1
    • 2
    • 3
    • 4

    dynamic_cast

    格式:

    dynamic_cast<type>(expression)
    
    • 1

    相比static_cast,dynamic_cast会在运行时检查类型转换是否合法,具有一定的安全性。由于运行时的检查,所以会额外消耗一些性能。dynamic_cast使用场景与static相似,在类层次结构中使用时,上行转换和static_cast没有区别,都是安全的;下行转换时,dynamic_cast会检查转换的类型,相比static_cast更安全。

    dynamic_cast转换仅适用于指针或引用。

    在转换可能发生的前提下,dynamic_cast会尝试转换,若指针转换失败,则返回空指针,若引用转换失败,则抛出异常。

    1.继承中的转换

    (1)上行转换:

    在继承关系中 ,dynamic_cast由子类向父类的转换与static_cast和隐式转换一样,都是非常安全的。

    (2)下行转换

    class A { virtual void f(){}; };
    class B : public A{ };
    void main()
    {
         A* pA = new B;
         B* pB = dynamic_cast<B*>(pA); 
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    注意类A和类B中定义了一个虚函数,这是不可缺少的。因为类中存在虚函数,说明它可能有子类,这样才有类型转换的情况发生,由于运行时类型检查需要运行时类型信息,而这个信息存储在类的虚函数表中,只有定义了虚函数的类才有虚函数表。

    2.void*的转换

    一些情况下,我们需要将指针转换为void*,然后再合适的时候重新将void*转换为目标类型指针。

    class A { virtual void f(){} };
    int main()
    {
         A *pA = new A;
         void *pV = dynamic_cast<void *>(pA); 
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    3.菱形继承中的上行转换

    首先,定义一组菱形继承的类:

    class A { virtual void f() {}; };
    class B :public A { void f() {}; };
    class C :public A { void f() {}; };
    class D :public B, public C { void f() {}; };
    
    • 1
    • 2
    • 3
    • 4

    继承关系如图:

    image-20221117225713058

    B继承A,C继承A。

    D继承B和C。

    考虑这样的情况:D对象指针能否安全的转换为A类型指针?

    直觉来说是可以的,因为从子类向父类转化,无论如何都是安全的。

    class A { virtual void f() {}; };
    class B :public A { void f() {}; };
    class C :public A { void f() {}; };
    class D :public B, public C { virtual void f() {}; };
    
    void main()
    {
        D *pD = new D;
        A *pA = dynamic_cast<A *>(pD); // pA = NULL
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    但实际上,如果尝试这样的转换,只能得到一个空指针。因为B和C都继承了A,并且都实现了虚函数f(),导致在进行转换时,无法选择一条转换路径。

    一种可行的方法是,自行指定一条转换路径:

    class A { virtual void f() {}; };
    class B :public A { void f() {}; };
    class C :public A { void f() {}; };
    class D :public B, public C { void f() {}; };
    
    void main()
    {
        D *pD = new D;
        B *pB = dynamic_cast<B *>(pD);
        A *pA = dynamic_cast<A *>(pB);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    const_cast

    格式:

    const_cast<type>(expression)
    
    • 1

    const_cast用于移除类型的const、volatile和__unaligned属性。

    常量指针被转换成非常量指针,并且仍然指向原来的对象;常量引用被转换成非常量引用,并且仍然引用原来的对象。

    const char *pc;
    char *p = const_cast<char*>(pc);
    
    • 1
    • 2

    reinterpret_cast

    格式:

    reinterpret_cast<type>(expression)
    
    • 1

    非常激进的指针类型转换,在编译期完成,可以转换任何类型的指针,所以极不安全。非极端情况不要使用。

    int *ip;
    char *pc = reinterpret_cast<char*>(ip);
    erpret_cast**
    
    格式:
    
    ```cpp
    reinterpret_cast<type>(expression)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    非常激进的指针类型转换,在编译期完成,可以转换任何类型的指针,所以极不安全。非极端情况不要使用。

    int *ip;
    char *pc = reinterpret_cast<char*>(ip);
    
    • 1
    • 2
  • 相关阅读:
    我的Mysql突然挂了(Communications link failure)
    lv7 嵌入式开发-网络编程开发 12 IP协议与ethernet协议
    【大数据面试题】024 Spark 3 升级了些什么?
    将Servlet项目改为SSM项目
    git远端协同开发、解决冲突、分支合并、gitlab使用、远程仓库回滚、为开源项目贡献代码、git工作流,git pull和git fetch,变基
    VP视频结构化框架
    统一网关Gateway
    外设篇:串口通信
    书籍Java8 实战 笔记
    【web开发】10、数据统计(echarts)--柱状图、折线图、饼图
  • 原文地址:https://blog.csdn.net/qq_55125921/article/details/127944066