C++ typeid关键字详解

typeid关键字


注意:typeid是操作符,不是函数。这点与sizeof类似)

运行时获知变量类型名称,可以使用 typeid(变量).name()

需要注意不是所有编译器都输出”int”、”float”等之类的名称,对于这类的编译器可以这样使用

int ia = 3;
if(typeid(ia) == typeid(int))
{
    cout <<"int" <<endl;
}

RTTI(Run-Time Type Identification)-运行时类型识别


在揭开typeid神秘面纱之前,我们先来了解一下RTTI(Run-Time Type Identification,运行时类型识别),它使程序能够获取由基指针或引用所指向的对象的实际派生类型,即允许“用指向基类的指针或引用来操作对象”的程序能够获取到“这些指针或引用所指对象”的实际派生类型。

在C++中,为了支持RTTI提供了两个操作符:dynamic_casttypeid

  • dynamic_cast允许运行时刻进行类型转换,从而使程序能够在一个类层次结构中安全地转化类型,与之相对应的还有一个非安全的转换操作符static_cast,因为这不是本文的讨论重点,所以这里不再详述,感兴趣的可以自行查阅资料。

  • typeid是C++的关键字之一,等同于sizeof这类的操作符。typeid操作符的返回结果是名为type_info的标准库类型的对象的引用(在头文件typeinfo中定义,稍后我们看一下vs和gcc库里面的源码),它的表达式有下图两种形式。

实现机制与使用技巧


type_info类对象类别判别


对象类别判别分析


如果表达式的类型是类类型且至少包含有一个虚函数,则typeid操作符返回表达式的动态类型,需要在运行时计算;
否则,typeid操作符返回表达式的静态类型,在编译时就可以计算。

ISO C++标准并没有确切定义type_info,它的确切定义编译器相关的,但是标准却规定了其实现必需提供如下四种操作(在之后的章节中我会来分析type_info类文件的源码)

运算描述
t1 == t2如果两个对象t1和t2类型相同,则返回true;否则返回false
t1 != t2如果两个对象t1和t2类型不同,则返回true;否则返回false
t.name()返回类型的C-style字符串,类型名字用系统相关的方法产生1
t1.before(t2)返回指出t1是否出现在t2之前的bool值

type_info类提供了public虚 析构函数,以使用户能够用其作为基类。它的默认构造函数和拷贝构造函数及赋值操作符都定义为private,所以不能定义或复制type_info类型的对象。程序中创建type_info对象的唯一方法是使用typeid操作符(由此可见,如果把typeid看作函数的话,其应该是type_info的 友元)。type_info的name成员函数返回C-style的字符串,用来表示相应的类型名,但务必注意这个返回的类型名与程序中使用的相应类型名并不一定一致(往往如此,见后面的程序),这具体由编译器的实现所决定的,标准只要求实现为每个类型返回唯一的字符串。

type_info类源代码


使用sudo find / -name typeinfo.h来查找源码
这里写图片描述

#ifndef _TYPEINFO
#define _TYPEINFO

#include <exception>

namespace std 
{

  class type_info 
  {
  public:

    virtual ~type_info();
    { return __name[0] == '*' ? __name + 1 : __name; }


    bool before(const type_info& __arg) const
    { return __name < __arg.__name; }

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

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

    virtual bool __is_pointer_p() const;

    virtual bool __is_function_p() const;

  protected:
    const char *__name;

    explicit type_info(const char *__n): __name(__n) { }

  private:
    type_info& operator=(const type_info&);
    type_info(const type_info&);
  };

} // extern "C++"
#endif

示例1-基本数据类型


下表列出了使用typeid操作符的表达式的值

int a;
double b;
char * c;
long d;
运算描述
typeid(a) == typeid(int)true
typeid(a) == typeid(float)false
typeid(a) == typeid(int *)false
typeid(b) == typeid(double)true
typeid(b) == typeid(float)false
typeid(b) == typeid(long double)false
typeid(c) == typeid(char *)true
typeid(c) == typeid(char)false
typeid(c) == typeid(string)false
typeid(d) == typeid(long)true
typeid(d) == typeid(int)false

操作符typeid返回的是一个type_info类(用于描述数据类型的一个系统类)对象的引用。这个操作符可以用于表达式和类型名(包括自定的数据类型,比如类)。

示例2-类对象


class base
{
public :
    void m(){cout<<"base"<<endl;}
};
class derived : public base
{
public:
    void m(){cout<<"derived"<<endl;}
};

假设我们根据例2中定义的两个类来定义如下指针:

base * p = new derived;

下表将给出使用typeid操作符的结果。

运算描述
typeid(p) == typeid(base*)true
typeid(p) == typeid(derived*)false
typeid(*p) == typeid(base)true
typeid(*p) == typeid(derived)false

对于表达式typeid(p),同样,因为p是base*类型的指针,因此typeid(p) == typeid(base*)为真,而typeid(p) == typeid(derived*)为假。而对于表达式typeid(*p),由于此时的基类不具有多态性,因而*p将会采用编译期类型来计算,编译期*p是base对象,因此表达式typeid(*p) == typeid(derived)为假,typeid(*p) == typeid(base)为真。

示例3-带虚函数的基类


class base
{
public :
    virtual void m(){cout<<"base"<<endl;}
};
class derived : public base
{
public:
    void m(){cout<<"derived"<<endl;}
};

假设我们如本例所示定义了两个类base类和derived类,基于这两个类定义,我们定义指针如下:

base * p = new derived;

下表将给出使用typeid操作符的结果。

运算描述
typeid(p) == typeid(base*)true
typeid(p) == typeid(derived*)false
typeid(*p) == typeid(base)false
typeid(*p) == typeid(derived)true

对于表达式typeid(p),因为p是base*类型的指针,因此typeid(p) == typeid(base*)为真,而typeid(p) == typeid(derived*)为假。而对于表达式typeid(*p),因为base类具有多态性,因而在计算typeid(*p)时会根据运行时p所指向的实际类型去计算,而本例中p指向的是派生类对象,因此表达式typeid(*p) == typeid(derived)为真,typeid(*p) == typeid(base)为假。

异常处理bad_typeid


  class bad_typeid : public exception 
  {
  public:
    bad_typeid () throw() { }

    // This declaration is not useless:
    // http://gcc.gnu.org/onlinedocs/gcc-3.0.2/gcc_6.html#SEC118
    virtual ~bad_typeid() throw();

    // See comment in eh_exception.cc.
    virtual const char* what() const throw();
  };
} // namespace std

这里写图片描述

参照

【C++】typeinfo.h

C++中的typeid关键字

  • 72
    点赞
  • 212
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
以下是C++中的所有关键字及其作用: 1. `auto`: 自动类型推导。 2. `break`: 终止循环或 switch 语句。 3. `case`: switch 语句中的分支。 4. `catch`: 捕获异常。 5. `char`: 字符类型。 6. `class`: 定义类。 7. `const`: 声明常量,或者表示函数不会修改对象。 8. `continue`: 跳过当前循环中的剩余代码。 9. `default`: switch 语句中的默认分支。 10. `delete`: 删除对象或释放动态分配的内存。 11. `do`: 循环语句,至少执行一次。 12. `double`: 双精度浮点数类型。 13. `dynamic_cast`: 运行时类型转换。 14. `else`: if 语句的分支。 15. `enum`: 枚举类型。 16. `explicit`: 显式构造函数或类型转换。 17. `export`: 指定函数或变量可在其他文件中使用。 18. `extern`: 声明变量或函数在其他文件中定义。 19. `false`: 布尔值假。 20. `float`: 单精度浮点数类型。 21. `for`: 循环语句。 22. `friend`: 友元函数或类。 23. `goto`: 跳转到标记的位置。 24. `if`: 条件语句。 25. `inline`: 内联函数。 26. `int`: 整数类型。 27. `long`: 长整数类型。 28. `mutable`: 声明类成员变量可修改。 29. `namespace`: 命名空间。 30. `new`: 分配动态内存。 31. `operator`: 自定义运算符。 32. `private`: 类访问控制,只能在类内部访问。 33. `protected`: 类访问控制,派生类可访问。 34. `public`: 类访问控制,任何地方都可访问。 35. `register`: 声明寄存器变量。 36. `reinterpret_cast`: 指针类型转换。 37. `return`: 返回函数值。 38. `short`: 短整数类型。 39. `signed`: 带符号整数类型。 40. `sizeof`: 获取类型或变量的大小。 41. `static`: 静态变量或函数,只在当前文件中可见。 42. `static_cast`: 静态类型转换。 43. `struct`: 定义结构体。 44. `switch`: 多分支选择语句。 45. `template`: 模板类或函数。 46. `this`: 指向当前对象的指针。 47. `throw`: 抛出异常。 48. `true`: 布尔值真。 49. `try`: 异常处理语句。 50. `typedef`: 定义类型别名。 51. `typeid`: 获取对象类型信息。 52. `typename`: 指示后面的标识符是类型名。 53. `union`: 联合类型。 54. `unsigned`: 无符号整数类型。 55. `using`: 声明命名空间中的符号。 56. `virtual`: 虚函数。 57. `void`: 空类型。 58. `volatile`: 声明变量可能被意外修改。 59. `wchar_t`: 宽字符类型。 以上是C++中的所有关键字及其作用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值