RTTI
Last updated on 3 months ago
概述
RTTI 是运行阶段类型识别(Runtime Type Identification)的简称,其目的是为程序在运行阶段确定对象的类型提供一种标准
也就是说,RTTI 的用途就是为了在程序运行期间确定对象的类型
RTTI 只能用于包含虚函数的类层次结构,原因在于只有一个继承体系当中含义虚函数,将派生类对象赋值给基类指针这件事才有意义(话说回来,没有虚函数直接把派生类对象赋值给基类指针真的好蠢啊,怎么会有人写出这种代码的)
RTTI 工作原理
C++ 有 3 个支持 RTTI 的元素
dynamic_cast
运算符,用于确定两个指针之间的转型是否正确。正确的话返回指针地址,否则返回空指针typeid
运算符返回一个指出对象的类型的值type_info
结构存储了有关特定类型的信息
dynamic_cast 运算符
考虑下面这样一个类层次结构
1 |
|
然后,我们创建三个指针
1 |
|
基类指针指向派生类对象,这很合理
接着,我们来看转型
1 |
|
在讨论之前,我们需要明确一点:与讨论「指针指向的对象的类型」相比,讨论「指针转型是否安全」更有意义。
需要额外说明的是,只有那些指针类型与对象类型(或者对象的直接或间接基类的类型)相同的类型转换才是安全的
说人话就是,指针本身的类型要么与其指向对象的类型一致,要么其对象的类型是指针本身类型的派生类。除这两种情况以外的所有,转型都不安全
对于上面这个问题我们一个一个看
对于第一个,先将 pl
指针的类型从 base*
转成
last*
,然后再对 p1
进行赋值,而
p1
所指的对象就是原先 pl
所指的对象,即
last
类型,这与 p1
的类型相一致,这么做是安全的
对于第二个,同样是先将指针的类型进行转换,最后是 last*
类型的 p2
指针指向 derived
对象,出现了派生类指针指向基类对象,这么做是错误的
对于第三个,是先将 pl
指针转成 last*
类型,然后再对 derived*
类型的指针进行赋值。这么做并没有问题,因为派生类指针给基类指针赋值是正确的。最终的结果是
derived*
类型的指针指向一个 last
对象,这么做也是安全的
确认一个转型是否安全可以用 dynamic_cast
关键字,不过这东西速度很慢,最好不要用
这个关键字的语法是:
1 |
|
如果对象 *pr
的类型是 Type
或者从
Type
直接或间接派生过来,那么上面的语句将会把
pr
转成 Type*
类型,否则将会返回
nullptr
额外说明一点,这个语句是将指针的类型转换成某个特定的类型,往后的下一步往往是对指针进行赋值,所以应该这么写:
1 |
|
除了指针,这个关键字还可以对引用使用,不过在引用里面没有类似于空指针的引用值,所以在转型失败的时候会抛出一个
bad_cast
异常。这个异常由 expression
类派生而来,它在头文件 typeinfo
当中定义
具体用法如下:
1 |
|
typeid 运算符和 type_info 类
typeid
运算符可以确定两个对象(不是指针)是否为同一种类型,它接收两种类型的参数:
- 类名
- 运算结果为对象的表达式
typeid
运算符会返回一个对 type_info
对象的引用,这个对象重载了 ==
和 !=
,使得可以直接作比较,即
1 |
|
如果 pt
是一个空指针,那么会引发 bad_typeid
异常,用 catch
捕获就行
typeid
当中还有一个方法 name
,可以用字符串来显示类型
RTTI 确实会有一些效率方面的问题,但如果用同类型的多个
if else
和 typeid
来进行替代的话,效率会更低,所以还是建议使用
dynamic_cast