c++的typeid关键字

本文深入探讨了C++中typeid关键字的使用方法及其背后的原理。通过示例解释了如何利用typeid进行安全的类型转换,并分析了typeid返回的type_info类的内部结构。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. typeid关键字的引入

  回顾c++的赋值兼容性原则,子类的对象在使用上可以被当做基类的对象,反过来则不可。具体表现在:
  (1) 子类的对象可以被赋值给基本对象
  (2) 指向基类的指针可以指向子类
  (3) 子类的对象可以初始化基类对象的引用

class BaseCls
{
public:
    //...

    virtual ~BaseCls()
    {}
};

class SubCls : public BaseCls
{
public:
    //...
};

void test_func(BaseCls* p)
{
    //将指针p强制转换为SubCls类型的指针
    SubCls *ps = static_cast<SubCls* >(p);
}

int main(void)
{
    BaseCls *pb = new SubCls();

    test_func(pb);

    delete pb;

    return 0;
}

  如上test_func()函数,在该函数的实现中,将基类类型p指针强制转换为子类类型指针,这是一种极为危险的操作:
  因为BaseCls类型的p指针既可以接收BaseCls类对象的地址,还可以接收SubCls对象的地址,子类是从父类继承而来,子类包含了父类的所有成员,但是将父类强制转为子类,那就意味着转换后的“子类”将含有一部分未知的成员。

  基类指针能否强制类型转换为子类指针取决于基类指向的类型,因为这个类型是在编译期间不能确定的,所以也称为动态类型。

  要让test_func()函数中的强制类型转换是安全的,也就是要判断动态类型为子类指针后才进行强制类型转换,如何判断?根据前面所学的多态可以实现:

class BaseCls
{
public:
    virtual std::string get_type()
    {
        return "BaseCls";
    }

    virtual ~BaseCls()
    {}
};

class SubCls : public BaseCls
{
public:
    virtual std::string get_type()
    {
        return "SubCls";
    }

    void print()
    {
        printf("Hello, SubCls!!\n");    
    }

};

void test_func(BaseCls* p)
{
    if (p->get_type() == "SubCls")
    {
        SubCls* ps = static_cast<SubCls* >(p);
        ps->print();    
    }
    else
    {
        printf("transcription error!!\n");
    }
}

int main(void)
{
    SubCls *ps = new SubCls();
    BaseCls *pb = new BaseCls();

    test_func(ps);  //打印Hello, SubCls!!
    test_func(pb);  //打印transcription error!!

    delete ps;
    delete pb;

    return 0;
}

  编译运行:
这里写图片描述

  利用类的多态,看似完美解决地实现识别动态类型功能,但是存在问题
  (1) 基类必须提供返回类型的虚函数,继承类必须重写基类的虚函数,这显然有点别扭
  (2) 在实际开发中,继承类可能是由他人写的,他人有可能遗忘重写基类返回类型的虚函数,那么将无法实现动态类型识别

  动态类型识别是一个重要功能,c++自然提供了关键字来实现,即typeid:

#include <typeinfo>
//...
void test_func(BaseCls* p)
{
    const std::type_info& type = typeid(*p);
    printf("type.name = %s\n", type.name());
}

  编译运行:
这里写图片描述

  需要注意,不同的编译器返回的.name()可能会不一样。

2. typeid使用分析

  typeid()关键字返回对应参数的类型信息,类型信息用type_info类对象存储:

const std::type_info& type = typeid(*p);

  typeid关键字的返回值为const std::type_info的引用,type_info原型如下:

//摘自Linux系统的/usr/include/c++/4.5/typeinfo
class type_info 
{
public:
    virtual ~type_info();    //析构函数
    const char* name() const
    { 
        return __name[0] == '*' ? __name + 1 : __name;
    }

#if !__GXX_TYPEINFO_EQUALITY_INLINE
    bool before(const type_info& __arg) const;
    bool operator==(const type_info& __arg) const;

#else
    #if !__GXX_MERGED_TYPEINFO_NAMES
        bool before(const type_info& __arg) const
        { 
            return (__name[0] == '*' && __arg.__name[0] == '*')
                ? __name < __arg.__name
                : __builtin_strcmp (__name, __arg.__name) < 0; 
        }

        bool operator==(const type_info& __arg) const
        {
          return ((__name == __arg.__name)
              || (__name[0] != '*' &&
              __builtin_strcmp (__name, __arg.__name) == 0));
        }
    #else
        bool before(const type_info& __arg) const
        {
            return __name < __arg.__name;
        }

        bool operator==(const type_info& __arg) const
        {
            return __name == __arg.__name;
        }
    #endif

#endif
    bool operator!=(const type_info& __arg) const
    {
        return !operator==(__arg);
    }

    virtual bool __is_pointer_p() const;
    virtual bool __is_function_p() const;   
    virtual bool __do_catch(const type_info *__thr_type, void **__thr_obj,
                unsigned __outer) const;

    virtual bool __do_upcast(const __cxxabiv1::__class_type_info *__target,
                 void **__obj_ptr) const;

protected:
    const char *__name;    
    explicit type_info(const char *__n): __name(__n) { }    //explicit用于杜绝隐式类型转换,构造函数

private:
    //私有化赋值操作符和拷贝构造函数
    type_info& operator=(const type_info&);     
    type_info(const type_info&);
};

  可见:
  (1) 公有成员函数都是const属性的,只有const对象才能使用const属性的成员方法,这也就是为什么typeid()关键字返回的type_info是const的原因
  (2) 看到type_info的拷贝构造函数和赋值操作符都是私有的,这也就意味着type_info对象不能初始化、赋值另一个type_info对象
  (3) 构造函数是protected属性,即type_info不能在外部实例化(只能在继承类的内部实例化)

  typeid()的参数可以是类型名,还可以是变量:

    int a = 6;
    const type_info& t1 = typeid(a);
    printf("type.name = %s\n", t1.name());

    const type_info& t2 = typeid(int);
    printf("type.name = %s\n", t2.name());

    double d = 0.63;
    const type_info& t3 = typeid(d);
    printf("type.name = %s\n", t3.name());

    const type_info& t4 = typeid("hello");
    printf("type.name = %s\n", t4.name());

  编译运行:
这里写图片描述

  当typeid()的参数为类型名时,返回的是静态类型信息。
  当typeid()的参数为变量时,分两种情况:
  (1) 变量不存在虚函数表,返回的是静态类型信息
  (2) 变量存在虚函数表,返回动态类型信息

  注意,同样的代码,在windows上的visual studio2010编译运行:
这里写图片描述
  跟Linux上g++的打印结果是不一样的,所以在利用typeid()判断数据类型的时候,要以两个typeid()的返回值来判断,以int类型为例:

int a;
if (typeid(int) == typeid(a))
    printf("type is int for a\n");

  所以在一开始的动态类型识别函数test_func()的实现为:

void test_func(BaseCls* p)
{
    const std::type_info& type = typeid(*p);
    if (typeid(*p) == typeid(SubCls))
    {
        printf("进行强制类型\n");
        SubCls* ps = static_cast<SubCls* >(p);
        //...
    }
    else
        printf("不能进行强制类型转换\n");
}
//...

int main(void)
{
    SubCls *ps = new SubCls();
    BaseCls *pb = new BaseCls();

    test_func(ps);
    test_func(pb);

    delete ps;
    delete pb;
    return 0;
}

  编译运行:
这里写图片描述

<think>嗯,用户想了解C++中的typeid关键字的用法和功能。首先,我需要回忆typeid的基本作用。记得typeid属于运行时类型信息(RTTI),主要用于获取对象的类型信息。应该和头文件<typeinfo>有关,使用前需要包含这个头文件。 接下来,用户需要知道具体的使用方法。比如,如何用typeid获取变量或类型的名称,判断类型是否相同。这时候需要举几个例子,比如基本类型、类对象、多态情况下的应用。要注意typeid在处理多态时,需要基类有虚函数,否则可能无法正确获取派生类的类型信息。 然后,功能特点方面,要强调typeid在运行时确定类型,返回的是type_info对象的引用,可以比较类型是否相等,以及获取类型名称。需要注意的是类型名称的字符串形式可能因编译器而异,不是可移植的。 还要考虑用户可能的疑问,比如typeid和auto、decltype的区别。auto用于自动类型推断,decltype在编译时获取表达式类型,而typeid在运行时获取类型信息,这些需要分清楚。引用已有的资料,比如引用[1]和[2],可以增强回答的可信度。 可能用户还会想知道typeid的应用场景,比如调试日志、类型检查、动态类型处理等。这时候需要举一些实际的例子,比如在异常处理中捕获bad_typeid异常,或者在模板编程中结合typeid使用。 需要注意的陷阱部分,比如多态情况下没有虚函数会导致typeid不准确,type_info对象的拷贝问题,以及类型名称的不可移植性。这些都是用户在实际使用中容易出错的地方,需要特别提醒。 最后,生成相关问题时,要考虑用户可能深入的问题,比如与dynamic_cast的对比,type_info的成员函数,或者typeid在模板元编程中的应用。这些问题可以帮助用户进一步探索相关知识。</think>### C++ `typeid`关键字 使用方法与功能介绍 `typeid`是C++中用于**运行时类型识别(RTTI)**的关键字,通过获取对象的类型信息,支持动态类型检查和类型名称查询。需包含头文件`<typeinfo>`。 --- #### 一、基本用法 1. **获取类型信息** ```cpp #include <typeinfo> #include <iostream> int main() { int num = 10; std::cout << typeid(num).name(); // 输出类型名称(如"i"表示int) return 0; } ``` - `typeid(表达式或类型)`返回`const std::type_info&`对象,包含类型信息[^1]。 - `.name()`返回类型名称(编译器相关,如GCC返回简写,MSVC返回可读名称)[^3]。 2. **类型比较** ```cpp if (typeid(a) == typeid(b)) { // 类型相同 } ``` 3. **多态类型判断** ```cpp class Base { virtual void foo() {} }; // 必须含虚函数 class Derived : public Base {}; Base* ptr = new Derived(); if (typeid(*ptr) == typeid(Derived)) { // 判断实际类型 // 执行派生类逻辑 } ``` --- #### 二、核心功能特点 1. **运行时类型识别** 动态判断对象的实际类型(需基类有虚函数)[^4]。 2. **类型名称查询** 通过`.name()`获取类型字符串(格式依赖编译器)。 3. **类型一致性验证** 直接比较两个`type_info`对象是否指向同一类型。 --- #### 三、典型应用场景 1. **调试与日志** 输出变量类型辅助调试: ```cpp std::cout << "Variable type: " << typeid(var).name(); ``` 2. **动态类型检查** 处理多态对象时验证类型: ```cpp void process(Base* obj) { if (typeid(*obj) == typeid(Derived)) { // 安全转换为派生类 dynamic_cast<Derived*>(obj)->method(); } } ``` 3. **异常处理** 捕获`std::bad_typeid`异常(当对空指针使用`typeid`时): ```cpp try { Base* ptr = nullptr; typeid(*ptr); // 抛出std::bad_typeid } catch (const std::bad_typeid& e) { std::cerr << e.what(); } ``` --- #### 四、注意事项 1. **多态类型限制** 基类必须至少有一个虚函数才能正确识别派生类类型。 2. **类型名称不可移植** `.name()`的输出因编译器而异,不可用于逻辑判断。 3. **类型信息对象** `type_info`对象禁止拷贝,需通过引用或指针使用。 --- #### 五、对比其他类型工具 | 工具 | 阶段 | 功能 | |-------------|----------|---------------------------| | `typeid` | 运行时 | 获取动态类型信息 | | `decltype` | 编译时 | 推断表达式类型(引用[^1])| | `auto` | 编译时 | 自动类型推断(引用[^2]) | ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值