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
2
3
class base {}
class derived : public base {}
class last : public derived {}

然后,我们创建三个指针

1
2
3
base* pb = new base;
base* pd = new derived;
base* pl = new last;

基类指针指向派生类对象,这很合理

接着,我们来看转型

1
2
3
last* p1 = (last*)pl;    //1
last* p2 = (last*)pb; //2
derived* p3 = (last*)p1; //3

在讨论之前,我们需要明确一点:与讨论「指针指向的对象的类型」相比,讨论「指针转型是否安全」更有意义。

需要额外说明的是,只有那些指针类型与对象类型(或者对象的直接或间接基类的类型)相同的类型转换才是安全的

说人话就是,指针本身的类型要么与其指向对象的类型一致,要么其对象的类型是指针本身类型的派生类。除这两种情况以外的所有,转型都不安全

对于上面这个问题我们一个一个看

对于第一个,先将 pl 指针的类型从 base* 转成 last* ,然后再对 p1 进行赋值,而 p1 所指的对象就是原先 pl 所指的对象,即 last 类型,这与 p1 的类型相一致,这么做是安全的

对于第二个,同样是先将指针的类型进行转换,最后是 last* 类型的 p2 指针指向 derived 对象,出现了派生类指针指向基类对象,这么做是错误的

对于第三个,是先将 pl 指针转成 last* 类型,然后再对 derived* 类型的指针进行赋值。这么做并没有问题,因为派生类指针给基类指针赋值是正确的。最终的结果是 derived* 类型的指针指向一个 last 对象,这么做也是安全的

确认一个转型是否安全可以用 dynamic_cast 关键字,不过这东西速度很慢,最好不要用

这个关键字的语法是:

1
dynamic_cast<Type*>(pr);//这里的 pr 是指针

如果对象 *pr 的类型是 Type 或者从 Type 直接或间接派生过来,那么上面的语句将会把 pr 转成 Type* 类型,否则将会返回 nullptr

额外说明一点,这个语句是将指针的类型转换成某个特定的类型,往后的下一步往往是对指针进行赋值,所以应该这么写:

1
Type* pt = dynamic_cast<Type*>(pr);

除了指针,这个关键字还可以对引用使用,不过在引用里面没有类似于空指针的引用值,所以在转型失败的时候会抛出一个 bad_cast 异常。这个异常由 expression 类派生而来,它在头文件 typeinfo 当中定义

具体用法如下:

1
2
3
4
5
6
7
8
try
{
Type& pt = dynamic_cast<Type&>(pr);
}
catch (bad_cast&)
{
...
}

typeid 运算符和 type_info 类

typeid 运算符可以确定两个对象(不是指针)是否为同一种类型,它接收两种类型的参数:

  • 类名
  • 运算结果为对象的表达式

typeid 运算符会返回一个对 type_info 对象的引用,这个对象重载了 ==!= ,使得可以直接作比较,即

1
2
3
4
if(typeid(Type) == typeid(*pt))
{
...
}

如果 pt 是一个空指针,那么会引发 bad_typeid 异常,用 catch 捕获就行

typeid 当中还有一个方法 name ,可以用字符串来显示类型

RTTI 确实会有一些效率方面的问题,但如果用同类型的多个 if elsetypeid 来进行替代的话,效率会更低,所以还是建议使用 dynamic_cast


RTTI
https://nishikichisato.github.io/2022/09/03/C++ Primer Plus/RTTI/
Author
Nishiki Chisato
Posted on
September 3, 2022
Updated on
November 25, 2023
Licensed under