09 - 运行时类型工具RTTI

C++以编译期为主,但有些特性提供了对象的运行时视角。这些特性通常归属于一个名为运行时类型信息(Run Time Type Information,RTTI)的特征集。

简介

RTTI主要有两个特性,一个是dynamic_cast(),另一个是typeid

通过RTTI,程序能够通过基类的指针或引用来检查这些指针或引用所指向的对象的实际派生类。RTTI 是 C++判断指针或引用实际类型的唯一方式

RTTI主要用于异常处理和IO操作中。

使用

需要注意的是,使用下边这两个函数时,父类和子类必须有虚函数,父类显式默认生成的虚析构函数就可以了。

typeid函数

typeid 函数返回的一个叫做 type_info 的结构体,该结构体包括了所指向对象的实际信息,其中的 name()函数就可以返回类的真实名称。

Spear* pSpear = new FireSpear();
std::cout << typeid(*pSpear).name();
delete pSpear;

// 输出
"class FireSpear"

dynamic_cast函数

该函数可以将父类指针转化为子类指针。

Spear* pSpear = new FireSpear();
// 使用dynamic_cast,将父类指针转化为子类指针/
FireSpear* pFs = dynamic_cast<FireSpear*>(pSpear);
if (pFs != nullptr)
{
    // 转换成功
}

可以将这两个函数一起用,例如判断为子类,就进行dynamic_cast

Spear* pSpear = new FireSpear();
if (std::string(typeid(*pSpear).name() == "class FireSpear")
{
    FireSpear* pFs = dynamic_cast<FireSpear*>(pSpear);
    if (pFs != nullptr)
	{
    	// 转换成功
	}
}

注意上边字符串是怎么比较的,由于name()返回值和右边的字符串字面量类型都是const char*,它们相等只有地址相等,因此得把左边用std::string初始化。

或者也能这样写:

if (typeid(*pSpear) == typeid(FireSpear))

个人认为这样在不同编译器环境下不易出bug,例如MSVC编译器的结果为class name,GCC编译器的结果为数字name

参考资料

  • 飘零的落花 - 现代C++详解
  • C++20高级编程