zoukankan      html  css  js  c++  java
  • c++的类型转换(转)

    类型转换机制可以分为:隐式类型转换 和 显示类型转换(强制类型转换)

    C中的类型转换:

      事情要从头说起,这个头就是C语言.我们已经习惯了使用C-like类型转换,因为它强大而且简单.

    主要有以下两种形式:

    • (new-type) expression
    • new-type (expression)

    C++中的类型转换:

    隐式类型转换比较常见,在混合类型表达式中经常发生。比如在表达式中存在short和int,那么就过会发生整型提升.四种强制类型转换操作符:static_cast、dynamic_cast、const_cast、reinterpret_cast。

    1. static_cast<>与dynamic_cast<>:

      把这两个放在一起比较容易记忆,"一静一动".从字面上也可以看出,前者提供的是编译时期的静态类型检测,后者提供的是

    运行时检测.

    • static_cast: 1)完成基础数据类型,2)同一个继承体系中类型的转换 3)任意类型与空指针类型void*之间的转换。
    • dynamic_cast:使用多态的场景,增加了一层对真实调用对象类型的检查
         char c  = 65;
         int *p = (int *)&c;//c like强制类型转换临时转变为int *,系统中变量c的类型未变
         cout<<(char)*p<<endl;//'A',同理强制类型转换,int to char
         *p = 5;
         int *q = static_cast<int *>(&c); //编译报错:error: invalid static_cast from type ‘char*’ to type ‘int*’

      在上面的例子中,C like可以运行,然而使用c++方式将会出现错误,static_cast可以将错误在编译时期检查出来。

     

    在不同继承体系的自定义类型中:

    复制代码
    class A
    {
    public:
      A(){}
      ~A(){}
    
    private:
      int i, j;
    };
    
    class C
    {
    public:
      C(){}
      ~C(){}
    
      void printC()
      {
        std::cout <<"call printC() in class C" <<std::endl;
      }
    private:
      char c1, c2;
    };     
    
        A *ptrA = new A();
         C *ptrC = (C *)(ptrA);//强制类型转换
         ptrC->printC(); //"call printC() in class C"
         //ptrC = static_cast<C*>(ptrA); //编译报错:error: invalid static_cast from type 'A*’ to type C*’
    delete ptrA;
    复制代码

      上面A和C是两个无关的类,不存在继承关系,然而使用C-like可以实现这种类型的临时强制转换,这是十分危险的! 那么使用static_cast可以将这种潜在的危险在编译器中找出来。那如何使用static_cast呢?

    在同一继承体系中,存在

      upcast(向上转换即子类转成父类、派生类转为基类):没有问题,因为父类(基类)的行为都包含在子类(派生类)中;

      downcast(向下转换即基类转成派生类):有可能会出现问题,编译时可能不会发现。

    以下举一个带有多态的继承例子说明该问题:

    复制代码
    #include <iostream>
    #include <cstdio>
    
    using namespace std;
    class A
    {
    public:
      A():i(1), j(1){}
      ~A(){}
    
      void printA()
      {
        std::cout <<"call printA() in class A" <<std::endl;
      }
    
      void printSum()
      {
        std::cout <<"sum = " <<i+j <<std::endl;
      }
    
    private:
      int i, j;
    };
    
    class B : public A//公有继承
    {
    public:
      B():a(2), b(2) {}
      ~B(){}
    
      void printB()
      {
        std::cout <<"call printB() in class B" <<std::endl;
      }
    
      void printSum()
      {
        std::cout <<"sum = " <<a+b <<std::endl;
      }
    
      void Add()
      {
        a++;
        b++;
      }
    
    private:
      double a, b;
    };
    int main()
    {
         B *ptrB = new B;
         ptrB -> printSum();//sum=4
         A *ptrA = static_cast<A *>(ptrB);//upcast,派生类转化为基类,也可以写成A *ptrA = static_cast<B *>(ptrB)
    ptrA -> printA();
    ptrA -> printSum(); //打印结果:sum = 2
    //在进行upcast的时候,指针指向的对象的行为与指针的类型相关。

    ptrA = new A;
    ptrB = static_cast<B *>(ptrA); //downcast,基类转化为派生类
    ptrB -> printB();
    ptrB -> printSum(); //打印结果:sum等于未知的数
    //在进行downcast的时候,其行为是“undefined”。

    B b;
    B &rB = b;
    rB.printSum(); //打印结果: sum = 4
    A &rA = static_cast<A &>(b);
    rA.printA();
    rA.printSum(); //打印结果: sum = 2
    //在进行upcast的时候,指针指向的对象的行为与引用类型相关.

    A a;
    A &rA1 = a;
    rA.printSum(); //打印结果:sum=2
    B &rB1 = static_cast<B &>(a);
    rB1.printB();  
    rB1.printSum();//打印结果sum未知
    //在进行downcast的时候,其行为是“undefined”。
         return 0;
    }
    复制代码

      这里其实很明显,一个类的行为和自身的类型相关,也就是一个A类型的指针总会优先调用自己A类内的函数(编译器看到的是指针的类型或引用的类型,这是早绑定,即静态多态的例子),当然发生继承中的重写(虚继承等)(编译器看到的是指针指向的内容或引用的变量,这是晚绑定,即动态多态的例子)例外。在downcast转换的时候,会出现一些跟指针或者引用类型相关的函数调用,但是因为指针或者引用(基类)没有定义这些行为,因为调用到了这些行为导致出现了未定义的行为,这是在继承关系中存在的转换关系。

      而解决这个问题的办法就是,虚函数! 如果声明A类中的printSum为虚函数,那么子类B就会有一个虚表,虚表中的第一个函数就是printSum函数,也就是B类的该函数(即晚绑定)。所以A类指针调用该函数就会调用B类中的该函数,显示结果sum= 4。在未定义之前sum = 2(A类中的该函数)。另外声明一个引用,不是新定义了一个变量,它只表示该引用名是目标变量名的一个别名,它本身不是一种数据类型,因此引用本身不占存储单元,系统也不给引用分配存储单元。故:对引用操作就是对目标变量操作。指针和引用最明显的区别是引用必须要初始化,且从始至终只能指向定义的那个目标变量,其值只能通过目标变量本身的改变而改变,试图改变引用名的行为是非法的,反之指针的值可变。

      总之,就是尽可能不要使用downcast,也就是使用派生类的指针指向基类。

    dynamic_cast<>
    1.dynamic_cast是在运行时检查的,用于在集成体系中进行安全的向下转换downcast(当然也可以向上转换,但没必要,因为可以用虚函数实现)

       即:基类指针/引用 -> 派生类指针/引用

       如果源和目标没有继承/被继承关系,编译器会报错!
    2.dynamic_cast是4个转换中唯一的RTTI操作符,提供运行时类型检查。
    3.dynamic_cast不是强制转换,而是带有某种”咨询“性质的,如果不能转换,返回NULL。这是强制转换做不到的。

    4.源类中必须要有虚函数,保证多态,才能使用dynamic_cast<source>(expression)

    static_cast<>

    用法:static_cast < type-id > ( expression )

    该运算符把expression转换为type-id类型,在编译时使用类型信息执行转换,在转换执行必要的检测(指针越界,类型检查),其操作数相对是安全的。

    但没有运行时类型检查来保证转换的安全性。

    reinterpret_cast<>

    通常为操作数的位模式提供较低层的重新解释。

    const_cast<>

     在进行类型转换时用来修改类型的const或volatile属性,除了const或volatile修饰之外,原来的数据值和数据类型都是不变的。

    我们都在通往真理的路上。
  • 相关阅读:
    Mysql存储引擎概念特点介绍及不同业务场景选用依据
    python
    nginx使用keepalived实现高可用
    python-文件操作(1)
    浏览器缓存知识小结及应用
    cookie 和 session
    tcp三次握手
    TCP/IP系列——长连接与短连接的区别
    python
    python- 迭代器与生成器
  • 原文地址:https://www.cnblogs.com/cvtoEyes/p/8488395.html
Copyright © 2011-2022 走看看