类型身份证 · RTTI

在 C++ 的世界里,每个类型都有自己的"身份证"。当你拿到一个基类指针时,如何知道它实际指向的是什么类型的对象?这就是 RTTI(Run-Time Type Information,运行时类型信息)的用武之地。


typeid 运算符

typeid 是查询类型信息的基本工具:

#include <iostream>
#include <typeinfo>

class Animal
{
public:
    virtual ~Animal() = default;
    virtual void speak() = 0;
};

class Dog : public Animal
{
public:
    void speak() override { std::cout << "Woof!" << std::endl; }
};

class Cat : public Animal
{
public:
    void speak() override { std::cout << "Meow!" << std::endl; }
};

int main()
{
    // 基本类型
    int x = 42;
    std::cout << "Type of x: " << typeid(x).name() << std::endl;
    std::cout << "Type of 3.14: " << typeid(3.14).name() << std::endl;
    
    // 类类型
    Dog dog;
    Cat cat;
    std::cout << "Type of dog: " << typeid(dog).name() << std::endl;
    std::cout << "Type of cat: " << typeid(cat).name() << std::endl;
    
    // 多态类型(通过基类指针)
    Animal* animal = &dog;
    std::cout << "Type of *animal: " << typeid(*animal).name() << std::endl;
    
    animal = &cat;
    std::cout << "Type of *animal: " << typeid(*animal).name() << std::endl;
    
    return 0;
}

注意:typeid().name() 的输出是编译器相关的,可能是修饰过的名称。


type_info 类

typeid 返回 std::type_info 对象的引用:


dynamic_cast:安全的向下转型

dynamic_cast 用于在继承层次中安全地转换指针或引用:

引用的 dynamic_cast

对引用使用 dynamic_cast 时,失败会抛出异常:


RTTI 的性能考量

RTTI 有一定的运行时开销。在性能敏感的代码中,有几种替代方案:

1. 虚函数代替 dynamic_cast

2. 枚举类型标记


访问者模式:类型安全的替代方案


std::any 与 std::variant(C++17)

现代 C++ 提供了类型安全的动态类型容器:


实现简单的反射系统


禁用 RTTI

在某些嵌入式系统或对代码大小敏感的环境中,可以禁用 RTTI:


总结

特性
用途
性能

typeid

获取类型信息

中等

dynamic_cast

安全向下转型

较慢

虚函数

多态行为

枚举标记

类型识别

最快

访问者模式

类型安全的双分派

std::any

类型擦除容器

中等

std::variant

类型安全的联合

RTTI 是 C++ 的强大特性,但要明智地使用。在性能关键的代码中,考虑使用虚函数或其他替代方案。记住:每个类型都有自己的"身份证",但查验身份证是要付出代价的!

Last updated