zoukankan      html  css  js  c++  java
  • C++中的类型转换

           C++类型转换分为:隐式类型转换和显式类型转换

    1.隐式转换

          1) 算术转换(Arithmetic conversion)。在混合类型的算术表达式中, 最宽的数据类型成为目标转换类型。

    int ival = 3;
    double dval = 3.14159;
    ival 
    + dval;//ival被提升为double类型

        

          2)赋值转换。一种类型表达式赋值给另一种类型的对象:目标类型是被赋值对象的类型

    int *pi = 0// 0被转化为int *类型
    ival = dval; // double->int

             例外:void指针赋值给其他指定类型指针时,不存在标准转换,编译出错。必须强制转换,例如使用malloc()函数
        

         3)将一个表达式作为实参传递给函数调用,此时形参和实参类型不一致:目标转换类型为形参的类型

    extern double sqrt(double);
    cout 
    << "The square root of 2 is " << sqrt(2<< endl;//2被提升为double类型:2.0

        

        4)从一个函数返回一个表达式,表达式类型与返回类型不一致:目标转换类型为函数的返回类型

    double difference(int ival1, int ival2)
    {
        
    return ival1 - ival2;//返回值被提升为double类型
    }

     

    2.显式转换

          C++继承了C中的隐式和显式转换的方式。但这种转换并不是安全和严格的,加上C++本身对象模型的复杂性,C++(C++是强类型语言)增加了四个显式转换的关键字:static_castdynamic_castreinterpret_castconst_cast。


    2.1 static_cast

        使用方法

            static_cast < type-id > ( expression )。该运算符把expression转换为type-id类型,但没有运行时类型检查来保证转换的安全性。

        使用说明

          (1)用于类层次结构中基类和子类之间指针或引用的转换。进行上行转换(把子类的指针或引用或者对象转换成基类表示)是安全的。进行下行转换(把基类指针或引用转换成子类指针或引用)时,由于没有动态类型检查,所以是不安全的。
          (2)用于基本数据类型之间的转换,如把int转换成char,把int转换成enum。这种转换的安全性也要开发人员来保证。
          (3)把void指针转换成目标类型的指针(不安全!),或者将目标类型指针转换为void指针。
          (4)把任何类型的表达式转换成void类型。static_cast不能去掉expression的const、volitale、或者__unaligned属性。


        例1.基本类型转换

    test_enum type = test_enum_1;
    
    char a ;
    int b = static_cast<int>(a);
    char c = static_cast<char>(b);
    type = static_cast<test_enum>(b);
    
    char* pa = NULL;
    int *pb = (int*)pa;
    //int *pb = static_cast<int*>(pa);        //error
    char *pc = (char*)pb;
    //char *pc = static_cast<char*>(pb);    //error
    
    void *p = static_cast<void*>(pa);
    pb = static_cast<int*>(p);
    pc = static_cast<char*>(p);


        例2.类层次中基类与子类成员函数指针的转换

    class A
    {
       public:
           void print(){cout<<"A"<<endl;}
    };
    
    class B:public A
    {
      public:
          void print(){cout<<"B"<<endl;}
    };
    
    void main(void)  
    {  
    
    	A a;
    	typedef void (A::*PS_MFunc)(); //函数指针指向类A的成员函数指针
            PS_MFunc func = &A::print;
    	(a.*func)();
    
            func = static_cast<PS_MFunc>(&B::print); //函数指针指向子类B成员函数,必须进行转换
    	(a.*func)();
    } 

        输出A,B


    例3:类层次结构中基类与子类指针或引用之间的转换  

       上行转换:子类(指针、引用、类型)转换成基类(指针、引用、类型)。不管是显式或者隐式,都可行。安全

       下行转换:基类指针转换成子类指针——危险(没有动态类型检查)

    class A{};
    class B:public A{};
    class C:public A{};
    class D{};
    void main(void)  
    {  
        A a;
        B b;
        A* pa= new A();
        B* pb = new B();
        C* pc = new C();
        D* pd = new D();
    <span style="white-space:pre">	</span> 
         a =b;//正确。
         a=static_cast<A>(b);
         a = static_cast<A&>(b);   
         pa=pb;
         pa = static_cast<A*>(pb);   //right 基类指针指向子类
    
    
         b=a;//错误
         b=static_cast<B>(a);//错误
         b=static_cast<B&>(a);//正确
         pb=pa;//错误
         pb=static_cast<B*>(pa);//正确
    }

        总结:

          上行转换皆可。下行转换,仅仅指针和引用转换合法,但是没有作安全检查。不能进行交叉转换和非继承关系之间的转换。


    2.2 dynamic_cast

           使用方法

                dynamic_cast < type-id > ( expression )。该运算符把expression转换成type-id类型的对象。Type-id必须是类的指针、类的引用或者void *;如果type-id是类指针类型,那么expression也必须是一个指针,如果type-id是一个引用,那么expression也必须是一个引用。

          使用说明

            (1)当无法使用virtual函数的时候,需要dynamic_cast强制转换。尤其是不知道基类原码,但是要在派生类中增加函数,且使用基类指针传入函数参数时。   
            (2)支持交叉转换。即继承机制中,兄弟之间的指针转换。
            (3)只有在基类指针转换为子类指针时才有意义。只有在存在虚函数机制的情况下,才容许下行转换。并且,只有基类指针/引用本身指向的是一个派生类的对象,然后将此基类指针、引用转换为对应的派生类指针、引用才是有意义的。否则即使能够转换,返回也为空,此时应该检查。


        例1 类继承中的转换

    class A{
    public :
    	virtual ~A(){}
    };
    class B:public A{};
    class C:public A{};
    class D{};
    void main(void)  
    {  
        A a;
        B b;
        A* pa= new A();
        B* pb = new B();
        C* pc = new C();
        D* pd = new D();
    	 
         a =b;//正确。
         a=dynamic_cast<A>(b);//错误
         a = dynamic_cast<A&>(b); //正确
         pa=pb;//正确
         pa = dynamic_cast<A*>(pb);   //right 基类指针指向子类
    
         b=a;//错误
         b=dynamic_cast<B>(a);//错误
         b=dynamic_cast<B&>(a);//正确,需要具备虚函数机制
         pb=pa;//错误
         pb=dynamic_cast<B*>(pa);//正确,需要具备虚函数机制
    
         b=c;//错误
         b=dynamic_cast<B>(c);//错误
         b=dynamic_cast<B&>(c);//正确,不需要具备虚函数机制
         pb=pc;//错误
         pb=dynamic_cast<B*>(pc);//正确,需要具备虚函数机制
    }


               总结:

                上行转换不能进行类型转换。下行转换仅仅在存在虚函数机制下,指针和引用的转换合法。交叉转换,指针转换必须在具备虚函数机制下合法,引用转换不需要。其他转换皆不合法。


           为何使用dynamic_cast下行转换类指针时,需要虚函数呢?

            Dynamic_cast转换是在运行时进行转换,运行时转换就需要知道类对象的信息(继承关系等)。如何在运行时获取到这个信息——虚函数表。C++对象模型中,对象实例最前面的就是虚函数表指针,通过这个指针可以获取到该类对象的所有虚函数,包括父类的。因为派生类会继承基类的虚函数表,所以通过这个虚函数表,我们就可以知道该类对象的父类,在转换的时候就可以用来判断对象有无继承关系。所以虚函数对于正确的基类指针转换为子类指针是非常重要的。


     例2 static_cast和dynamic_cast的区别
             dynamic_cast主要用于类层次间的上行转换和下行转换,还可以用于类之间的交叉转换。上行转换时,dynamic_cast和static_cast的效果是一样的(前者只能进行转换为引用和指针类型,后者还包括类型本身)。

            在进行下行转换时,dynamic_cast具有类型检查的功能,比static_cast更安全。但是dynamic_cast要求存在虚函数机制,而static_cast不需要。下行转换仅仅涉及到指针和引用转换。

    class Base
    {
    public:
        int m_iNum;
        virtual void foo();
    };
    
    class Derived:public Base
    {
    public:
        char *m_szName[100];
    };
    
    void func(Base *pb)
    {
        Derived *pd1 = static_cast<Derived *>(pb);
        Derived *pd2 = dynamic_cast<Derived *>(pb);
    }
            如果pb实际指向一个Derived类型的对象,pd1和pd2是一样的,并且对这两个指针执行Derived类型的任何操作都是安全的;如果pb实际指向的是一个Base类型的对象,那么pd1将是一个指向该对象的指针,对它进行Derived类型的操作将是不安全的(如访问m_szName),而pd2将是一个空指针(即0,因为dynamic_cast失败)。
             Base要有虚函数,否则会dynamic_cast下行转换编译出错;static_cast则没有这个限制。这是由于运行时类型检查需要运行时类型信息,而这个信息存储在类的虚函数表(关于虚函数表的概念,详细可见<Inside c++ object model>)中,只有定义了虚函数的类才有虚函数表,没有定义虚函数的类是没有虚函数表的。


      例3:dynamic_cast常见使用与虚函数机制

    class A{
    public :
    	virtual void func1(){cout<<"A---func1"<<endl;}
    	void func2(){cout<<"A-func2"<<endl;}
    	virtual ~A(){}
    };
    class B:public A{
    public:
    	void func1(){cout<<"B---func1"<<endl;}
    	void func2(){cout<<"B-func2"<<endl;}
    	void func3(){cout<<"B-func3"<<endl;}
    };
    void main(void)  
    {  
    	 A *pa=new A;
    	 pa->func1();
    	 pa->func2();
    
    	 B*pb=dynamic_cast<B*>(pa);
    	 if(pb)
    		 pb->func1();
    	 pb->func2();
    	 pb->func3();
    	 delete pa;
    
    	 cout<<"------------------------"<<endl;
    	 pa=new B;
    	 pa->func1();
    	 pa->func2();
    
    	 pb=dynamic_cast<B*>(pa);
    	 if(pb)
    		 pb->func1();
    	 pb->func2();
    	 pb->func3();
    	 delete pa;
    }
        输出:


             如图所示。基类指针体现了运行多态性。且当基类指针指向派生类对象时,重载函数调用基类的版本,且此转换结果为非空。若基类指针指向基类对象,转换结果为空。但是虽然为空,func3()和func2()依然能够正常调用,因为此时没有使用任何成员数据,也不是虚函数,不要this指针和动态绑定,可以正常运行。

           总结:

                dynamic_cast提供运行时安全检查,因此不能进行某些无理的转换。并不是强制转换(带有某种咨询性质),能转换则转换,不能则返回为空(可以用作检查条件)。


    2.3 const_cast

             const_cast<type_id> (expression)。该运算符用来修改类型的const或volatile属性。除了const 或volatile修饰之外, type_id和expression的类型是一样的。

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

    class B{
    public:
        int m_iNum;
    };
    
    void foo(){
        const B b1;
        b1.m_iNum = 100; //comile error
        B b2 = const_cast<B>(b1);
        b2. m_iNum = 200; //fine
    }
             代码编译时会报错,因为b1是一个常量对象,不能对它进行改变;使用const_cast把它转换成一个常量对象,就可以对它的数据成员任意改变。注意:b1和b2是两个不同的对象。

        


    参考:

         1.static_cast与dynamic_cast转换

         2.static_cast, dynamic_cast, const_cast探讨








  • 相关阅读:
    oracle 锁表查看与解锁
    oracle 监视索引是否使用
    oracle 查看数据库版本
    mybatis 中文做参数报错
    windows mysql绿色版配置
    maven使用本地jar包
    Java 使用Jedis连接Redis数据库(-)
    Linux 安装redis
    ELK之在CentOS7.5上使用rpm包安装配置ELK7版本
    Ubuntu-18.04更改安装源为国内源
  • 原文地址:https://www.cnblogs.com/engineerLF/p/5393008.html
Copyright © 2011-2022 走看看