如何使用`typeid`判断指针或引用所指对象的实际类型?

核心前提:typeid判断实际类型的条件

typeid能否识别指针/引用指向的实际类型,唯一的关键是:被判断的类是否是多态类(包含至少一个虚函数,通常是虚析构函数)

  • 非多态类:typeid只能识别编译期的声明类型(静态类型)。
  • 多态类:typeid会解析运行时的实际对象类型(动态类型)。

一、判断指针指向对象的实际类型

关键注意点

使用typeid判断指针时,必须解引用指针typeid(*ptr)),如果直接写typeid(ptr),得到的只是“指针类型”本身(如Base*),而非指向对象的类型。

代码示例(对比多态/非多态类)

#include <iostream>
#include <typeinfo>

// 1. 非多态基类(无虚函数)
class NonPolymorphicBase {
public:
    void func() {} // 普通成员函数
};
class NonPolymorphicDerived : public NonPolymorphicBase {};

// 2. 多态基类(有虚函数)
class PolymorphicBase {
public:
    virtual ~PolymorphicBase() = default; // 虚析构,使类多态
    virtual void func() {}
};
class PolymorphicDerived : public PolymorphicBase {};

int main() {
    // ---------------------- 非多态类测试 ----------------------
    NonPolymorphicBase* np_base_ptr = new NonPolymorphicDerived();
    // 错误:直接判断指针类型(得到的是指针本身的类型)
    std::cout << "非多态-直接判断指针: " << typeid(np_base_ptr).name() << std::endl; 
    // 结果:NonPolymorphicBase*(编译器可能简写为P19NonPolymorphicBase)
    
    // 非多态类:即使解引用,仍返回编译期声明类型(Base)
    std::cout << "非多态-解引用指针: " << typeid(*np_base_ptr).name() << std::endl; 
    // 结果:NonPolymorphicBase(而非Derived)

    // ---------------------- 多态类测试 ----------------------
    PolymorphicBase* p_base_ptr = new PolymorphicDerived();
    // 错误:直接判断指针类型(得到的是指针本身的类型)
    std::cout << "多态-直接判断指针: " << typeid(p_base_ptr).name() << std::endl; 
    // 结果:PolymorphicBase*(编译器可能简写为P16PolymorphicBase)
    
    // 多态类:解引用后,返回运行时实际类型(Derived)
    std::cout << "多态-解引用指针: " << typeid(*p_base_ptr).name() << std::endl; 
    // 结果:PolymorphicDerived

    // ---------------------- 类型比较 ----------------------
    // 判断指针指向的对象是否是指定的实际类型
    if (typeid(*p_base_ptr) == typeid(PolymorphicDerived)) {
        std::cout << "指针指向的是PolymorphicDerived类型对象" << std::endl;
    }

    // 释放内存
    delete np_base_ptr;
    delete p_base_ptr;
    return 0;
}

二、判断引用所指对象的实际类型

引用的特性是“别名”,不存在空引用,因此使用typeid判断引用时:

  • 无需解引用(引用本身就是对象的别名);
  • 同样遵循“多态类识别实际类型,非多态类识别声明类型”的规则。

代码示例

#include <iostream>
#include <typeinfo>

// 多态基类
class Base {
public:
    virtual ~Base() = default;
};
class Derived1 : public Base {};
class Derived2 : public Base {};

void check_ref_type(Base& ref) {
    // 直接判断引用,多态类会返回实际类型
    if (typeid(ref) == typeid(Derived1)) {
        std::cout << "引用指向Derived1对象" << std::endl;
    } else if (typeid(ref) == typeid(Derived2)) {
        std::cout << "引用指向Derived2对象" << std::endl;
    } else {
        std::cout << "引用指向Base对象" << std::endl;
    }
}

int main() {
    Derived1 d1;
    Derived2 d2;
    Base b;

    Base& ref1 = d1;
    Base& ref2 = d2;
    Base& ref3 = b;

    check_ref_type(ref1); // 输出:引用指向Derived1对象
    check_ref_type(ref2); // 输出:引用指向Derived2对象
    check_ref_type(ref3); // 输出:引用指向Base对象

    return 0;
}

三、常见坑点与注意事项

  1. 不要直接判断指针类型typeid(ptr) 得到的是“指针类型”(如Base*),而非指向对象的类型,必须写typeid(*ptr)
  2. 空指针处理:如果指针是nullptr,对非多态类使用typeid(*ptr)会直接崩溃;对多态类,typeid(*ptr)仍会返回指针的声明类型(不会崩溃)。
    PolymorphicBase* null_ptr = nullptr;
    std::cout << typeid(*null_ptr).name() << std::endl; // 输出PolymorphicBase(不会崩溃)
    
    NonPolymorphicBase* null_np_ptr = nullptr;
    std::cout << typeid(*null_np_ptr).name() << std::endl; // 未定义行为,大概率崩溃
    
  3. 类型名的可读性typeid(...).name() 的返回值是编译器相关的(比如GCC会简写类型名,MSVC会返回完整名),不要直接依赖字符串内容判断类型,优先用typeid(A) == typeid(B)的方式。
  4. 运行时开销:对多态类使用typeid会查询虚函数表(vtable),有轻微运行时开销,频繁使用会影响性能。

总结

  1. 核心条件:只有对多态类(含虚函数)的指针/引用,typeid才能识别指向对象的实际类型;非多态类仅能识别编译期声明类型。
  2. 指针判断:必须解引用(typeid(*ptr)),否则得到的是指针类型本身。
  3. 引用判断:直接使用typeid(ref)即可,多态类会返回实际对象类型。
  4. 安全建议:判断类型时优先用typeid(A) == typeid(B),而非解析name()字符串;空指针场景需先判空再使用typeid(*ptr)
posted @ 2026-01-14 11:08  C++大哥来也  阅读(6)  评论(0)    收藏  举报