博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C++中的类型转换
阅读量:6096 次
发布时间:2019-06-20

本文共 5873 字,大约阅读时间需要 19 分钟。

       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_cast、dynamic_cast、reinterpret_cast和const_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
(a);char c = static_cast
(b);type = static_cast
(b);char* pa = NULL;int *pb = (int*)pa;//int *pb = static_cast
(pa); //errorchar *pc = (char*)pb;//char *pc = static_cast
(pb); //errorvoid *p = static_cast
(pa);pb = static_cast
(p);pc = static_cast
(p);

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

class A{   public:       void print(){cout<<"A"<
(&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();	      a =b;//正确。     a=static_cast(b);     a = static_cast
(b);        pa=pb;     pa = static_cast
(pb);   //right 基类指针指向子类 b=a;//错误 b=static_cast
(a);//错误 b=static_cast
(a);//正确 pb=pa;//错误 pb=static_cast
(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(b);//错误     a = dynamic_cast
(b); //正确 pa=pb;//正确 pa = dynamic_cast
(pb); //right 基类指针指向子类 b=a;//错误 b=dynamic_cast
(a);//错误 b=dynamic_cast
(a);//正确,需要具备虚函数机制 pb=pa;//错误 pb=dynamic_cast
(pa);//正确,需要具备虚函数机制 b=c;//错误 b=dynamic_cast
(c);//错误 b=dynamic_cast
(c);//正确,不需要具备虚函数机制 pb=pc;//错误 pb=dynamic_cast
(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
(pb); Derived *pd2 = dynamic_cast
(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"<
func1(); pa->func2(); B*pb=dynamic_cast
(pa); if(pb) pb->func1(); pb->func2(); pb->func3(); delete pa; cout<<"------------------------"<
func1(); pa->func2(); pb=dynamic_cast
(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(b1);    b2. m_iNum = 200; //fine}
         代码编译时会报错,因为b1是一个常量对象,不能对它进行改变;使用const_cast把它转换成一个常量对象,就可以对它的数据成员任意改变。注意:b1和b2是两个不同的对象。

    

参考:

     1.

     2.

转载于:https://www.cnblogs.com/engineerLF/p/5393008.html

你可能感兴趣的文章
原创经验:微信小程序开发总结
查看>>
创建swap文件与删除swap分区
查看>>
Linux Shell实现多进程并发执行
查看>>
MySQL的timeout相关配置参数小结一把
查看>>
我的友情链接
查看>>
Apache Spark探秘:Spark Shuffle实现
查看>>
dubbo
查看>>
linux高级文件管理系统
查看>>
下一代云计算平台Apache Mesos定制自己的PaaS-1(Apache Mesos安装部署)
查看>>
四层和七层负载均衡的区别
查看>>
Heritrix maven
查看>>
解决Lync联盟用户之间只能IM聊天不能进行A/V呼叫问题
查看>>
java Log日志规范
查看>>
Linux系统日常管理
查看>>
深入理解PHP中的Session和Cookie
查看>>
Apache 日志分析
查看>>
第一期openstack远程培训10月10日开课(来晚就没有了)
查看>>
【Git入门之四】操作项目
查看>>
老男孩教育每日一题-第107天-简述你对***的理解,常见的有哪几种?
查看>>
Python学习--time
查看>>