天天看点

dynamic_cast介绍

首先说到c++常用的四中转换类型,我们都很清楚,分别是下面四中

 

dynamic_cast介绍

1 const_cast 

const_cast<目标类型>(标识符):目标类型只能是指针或者引用

2 static_cast 

    类似C风格的强制转换,进行无条件转换,静态类型转换:

    1)基类和子类之间的转换:其中子类指针转换为父类指针是安全的,但父类指针转换为子类指针是不安全的(基类和子类之间的动态类型转换建议用dynamic_cast)。

    2)基本数据类型转换,enum,struct,int,char,float等。static_cast不能进行无关类型(如非基类和子类)指针之间的转换。

    3)把任何类型的表达式转换成void类型。

    4)static_cast不能去掉类型的const、volatile属性(用const_cast)。

3 reinterpret_cast

    仅重新解释类型,但没有进行二进制的转换:

    1) 转换的类型必须是一个指针,应用、算术类型、函数指针或者成员指针。

    2) 在比特级别上进行转换,可以把一个指针转换成一个整数,也可以把一个整数转换成一个指针(先把一个指针转换成一个整数,在把该整数转换成原类型的指针,还可以得到原先的指针值)。但不能将非32bit的实例转成指针。

    3) 最普通的用途就是在函数指针类型之间进行转换。

    4) 很难保证移植性。

4dynamic_cast

    有条件转换,动态类型转换,运行时检查类型安全(转换失败返回NULL):

    1 将一个基类对象指针(或引用)cast到继承类指针,dynamic_cast会根据基类指针是否真正指向继承类指针来做相应处理, 即会作出一定的判断。

    2 若对指针进行dynamic_cast,失败返回null,成功返回正常cast后的对象指针;

    3 若对引用进行dynamic_cast,失败抛出一个异常,成功返回正常cast后的对象引用。

    4 dynamic_cast在将父类cast到子类时,父类必须要有虚函数,否则编译器会报错。

    5 dynamic_cast主要用于类层次间的上行转换和下行转换,还可以用于类之间的交叉转换。

    6 在类层次间进行上行转换时,dynamic_cast和static_cast的效果是一样的;

    7 在进行下行转换时,dynamic_cast具有类型检查的功能,比static_cast更安全。

    因为这里主要说dynamic_cast,所以把dynamic_cast放最后一个说,怎么用我们都很清楚,但是有个关键点值得深究,为什么dynamic_cast在将父类cast到子类时,父类必须要有虚函数,否则编译器会报错,而当子类转换为父类的时候不需要呢? 

    要说明白这个问题得先说多态,多态分为两种一种是编译器的多态,还有一种是运行期的多态。然后说当子类转换为父类的时候为什么不需要,因为在类层次间进行上行转换时,dynamic_cast和static_cast的效果是一样的,所以这种转换关系在编译时期编译器就明确的直到父子之间的继承关系,这个工作编译时期就已经完成了,所以不需要,可为什么dynamic_cast在将父类cast到子类时,为什么又需要了呢?这种情况正好对应的是运行期的多态,为什么这种情况在编译期搞不定了呢,很简单你想想,一个父亲可以又多个孩子,但是所有的孩子只有一个父亲(不接受杠精的反驳),当向上转换的时候,因为所有的子类只有一个父亲编译器很明确目标直接就搞定了,可是向下就不一样了,那么多孩子,如果有一天老大过来说我是老二,又过了一天又说我是老四(对应程序中错误的转换行为),要怎么辨别真伪呢,如果是错的怎么般,关键的地方在这如果错了会发生什么问题。非常危险,先说为什么这种错误的转换很危险,要明白类型转换实际上是对内存的截取或者扩展,为什么这样说呢,假如类D1继承了类B,B有一个变量int i,D有一个int y,那么内存结构是这样的

 

dynamic_cast介绍

  

    当我们进行类型转换时,理论上发生了什么呢,当我们把子类强转换为父类时很简单,把D1中末尾属于自己的部分截掉,将父类强转为子类时只要把内存大小扩大子类大小字节就可以了,看到区别没有,当父类强转为子类时实际上时需要扩大内存区间映射到相应的子类对象上,如果转换的子类类型是错误的呢,如果两个子类的大小不一样,可能访问到非法内存,就算内存大小是一样的,但是内存和对象结构的对应关系是错误的,如果有指针变量,对象的指针变量也会变成野指针,直接乱掉,访问起来会造成灾难性的后果。

  那么如何确保转换的正确性呢,这也是dynamic_cast最重要的功能,这里就要说到才c++的RTTI机制,这里的RTTI指c++的运行时类型识别信息,我们知道只有带有虚函数的类才会生成虚函数表,虚函数指针指向了虚函数表,实际上在这张表里还有一个很重要的信息就是指向该类的typeinfo的指针,里面记录了该类的类型信息和继承关系,在进行动态类型转换时只要取虚函数表中的第-1个元素得到type_info类对象判断其真正的类型在进行操作(vptr实际上向后移动了一个位置,编译器取虚函数的时候vptr[0]是第一个虚函数指针,vptr[-1]就能取到指向type_info的指针,一般我们不会主动去取type_info指针).因此很简单运行时转换的时候只要取虚函数表中的typeinfo信息判断他的类型是否是你要转换的类型以及在继承关系的合法性就可以了,如果是非法的就转换失败返回空指针。这样就保证了转换的安全性,当然也有他的弊端,就是相比静态编译器的转换带来了运行时的开销,且集成关系越复杂开销越大,不过我们一般能通过虚函数的多态特性解决(这种虚函数的多态性是不需要遍历typeinfo的虚函数表已经进行了相应的替换)不会进行这种强制类型的转换,除非虚函数解决不了需要子类中特有的属性后者方法的时候才会进行这种运行时期的转换。

继续阅读