typeid -- study
在揭开typeid神秘面纱之前,我们先来了解一下RTTI(Run-Time Type Identification,运行时类型识别),它使程序能够获取由基指针或引用所指向的对象的实际派生类型,即允许“用指向基类的指针或引用来操作对象”的程序能够获取到“这些指针或引用所指对象”的实际派生类型。在C++中,为了支持RTTI提供了两个操作符:dynamic_cast和typeid。
dynamic_cast允许运行时刻进行类型转换,从而使程序能够在一个类层次结构中安全地转化类型,与之相对应的还有一个非安全的转换操作符static_cast,因为这不是本文的讨论重点,所以这里不再详述,感兴趣的可以自行查阅资料。下面就开始今天我们的话题:typeid。
typeid是C++的关键字之一,等同于sizeof这类的操作符。typeid操作符的返回结果是名为type_info的标准库类型的对象的引用(在头文件typeinfo中定义,稍后我们看一下vs和gcc库里面的源码),它的表达式有下图两种形式。
如果表达式的类型是类类型且至少包含有一个虚函数,则typeid操作符返回表达式的动态类型,需要在运行时计算;否则,typeid操作符返回表达式的静态类型,在编译时就可以计算。
ISO C++标准并没有确切定义type_info,它的确切定义编译器相关的,但是标准却规定了其实现必需提供如下四种操作(在之后的章节中我会来分析type_info类文件的源码):
t1 == t2 | 如果两个对象t1和t2类型相同,则返回true;否则返回false |
t1 != t2 | 如果两个对象t1和t2类型不同,则返回true;否则返回false |
t.name() | 返回类型的C-style字符串,类型名字用系统相关的方法产生 |
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(类型信息):存储特定类型的有关信息,定义在<typeinfo>头文件中。type_info类的具体内容由编译器实现来决定,但是至少必须包含返回字符串的name()成员函数。下面是type_info类的VC05实现版本:
class type_info { // VC05中定义的简化
public:
virtual ~type_info();
bool operator==(const type_info& rhs) const;
bool operator!=(const type_info& rhs) const;
int before(const type_info& rhs) const;
const char* name() const;
const char* raw_name() const;
private:
void *_m_data;
char _m_d_name[1];
type_info(const type_info& rhs);
type_info& operator=(const type_info& rhs);
static const char *_Name_base(const type_info *prhs, __type_info_node* __ptype_info_node);
static void _Type_info_dtor(type_info *prhs);
};
上面的都是一些理论的东西,看不真切,下面将通过代码和图例来展示。
using namespace std;
class Base {};
class Derived: public Base {};
int main()
{
Base b, *pb;
pb = NULL;
Derived d;
cout << typeid(int).name() << endl
<< typeid(unsigned).name() << endl
<< typeid(long).name() << endl
<< typeid(unsigned long).name() << endl
<< typeid(char).name() << endl
<< typeid(unsigned char).name() << endl
<< typeid(float).name() << endl
<< typeid(double).name() << endl
<< typeid(string).name() << endl
<< typeid(Base).name() << endl
<< typeid(b).name()<<endl
<< typeid(pb).name()<<endl
<< typeid(Derived).name() << endl
<< typeid(d).name()<<endl
<< typeid(type_info).name() << endl;
return 0;
}
我分别用MS的V8和GUN的GCC编译该段代码并运行,结果分别为下面的左右二图。
对比代码以及上面的文字描述,不知道各位是否已经有所明了(这里需要注意的是Base类的对象b和对象指针pb,他们的输出)。
考虑到V8的输出很直观,所以我采用V8来做实验。下面对上面的代码稍微添加一点内容,如下:
Base &b2 = d;
Base *pb3 = &d;
cout << typeid(pb2).name() <<endl//输出Base *
<< typeid(b2).name()<<endl //输出Base
<< typeid(pb3).name()<<endl//输出Base *
<< typeid(*pb3).name()<<endl;//输出Base
因为Base不包含虚函数,所以typeid的结果指出,表达式的类型是Base或Base *型,尽管他们的底层对象是Derived。即:当typeid操作符的操作数是不带有虚函数的类类型时,typeid操作符会指出操作数的类型,而不是底层对象的类型。
下面在对Base函数做一个小小调整,为其加上一个虚函数,再看输出结果。
/*...*/
cout << typeid(pb2).name() <<endl//输出Base *
<< typeid(b2).name()<<endl //输出Derived
<< typeid(pb3).name()<<endl//输出Base *
<< typeid(*pb3).name()<<endl;//输出Derived
这次Base含有虚函数,注意看结果,指针仍然是Base*的,尽管他们指向的是底层对象Derived,而这些Base对象的类型却是Derived的。
因为指针pb3不是类类型,所以typeid就返回该指针pb3的指针类型Base *。而*pb3是一个类类型的表达式,而且该类带有虚函数,所以指出该pb3指向的底层对象的类型Derived。
如果typeid操作符的操作数是至少包含一个虚拟函数的类类型时,并且该表达式是一个基类的应用,则typeid操作符指出底层对象的派生类类型。
好了,文篇到此结束,留下几道小题目吧。
cout<< typeid(7.84).name() <<endl
<< typeid(Base*).name() <<endl
<< typeid(&pb3).name() <<endl;
typeid
Syntax:
typeid( object );
The typeid operator returns a reference to a type_info object that describes `object`.
If `object` is a null pointer, then the operation will throw a std::bad_typeid exception.
NOTE The C++98 standard requires that header file <typeinfo>
to be included before operator typeid is used within a compilation unit. Otherwise, the program is considered ill-formed.
typeid -- study相关推荐
- Oracle Study之--Oracle等待事件(3)
Oracle Study之--Oracle等待事件(3) Db file parallel read 这是一个很容易引起误导的等待事件,实际上这个等待事件和并行操作(比如并行查询,并行DML)没有关系 ...
- 读书笔记2013第10本:《学得少却考得好Learn More Study Less》
<学得少却考得好Learn More Study Less>这本书最早是从褪墨网站上看到的,crowncheng翻译了全文.这本书介绍了不少学习方法,非常适合在校的学生,原文的作者Scot ...
- Case study:在数据库网页中设计数据排序工具
一.目的 该笔记的目的是引导读者在已搭建的数据库网页的基础上,利用JS设计数据排序工具.其效果如图1所示."Order by"下拉列表框由一系列字段组成,如"Locati ...
- Case Study: 利用PHP获取关系型数据库中多张数据表的数据
一.目标 该笔记的目的是引导读者借助WampServer平台和MySQL数据库,利用HTML/CSS/JS/PHP设计一个多数据表关联的网页.在上一个案例(Case Study: 利用JS实现数据库网 ...
- Case Study: 利用JS实现数据库网页的数据分页、数据选择、数据详细信息查看功能
一.目标 该笔记的目的是引导读者借助WampServer平台和MySQL数据库,利用HTML/CSS/JS/PHP设计一个能够进行实现数据分页显示.数据选择.数据详细信息查看功能的数据库网页.该数据库 ...
- Case Study: 利用JS设计高级检索功能通过PHP获取MySQL数据
一.目标 该笔记的目的是引导读者借助WampServer平台和MySQL数据库,利用HTML/CSS/JS/PHP设计一个含有高级检索功能的数据库网页.该功能效果如图1所示.用户在文本框中输入相应内容 ...
- QIIME 2用户文档. 5粪菌移植分析练习Fecal microbiota transplant (FMT) study
文章目录 前情提要 QIIME 2用户文档. 5粪菌移植分析练习 启动QIIME2运行环境 实验数据下载 序列质控评估 生成特征表和代表性序列 查看去噪过程统计 合并不同批的代表序列和特征表 表1. ...
- 二值网络训练--A Empirical Study of Binary Neural Networks' Optimisation
A Empirical Study of Binary Neural Networks' Optimisation ICLR2019 https://github.com/mi-lad/studyin ...
- oracle顺序读等待,Oracle Study之--Oracle等待事件(4)
Oracle Study之--Oracle等待事件(4) Db file scattered read这个等待事件在实际生产库中经常可以看到,这是一个用户操作引起的等待事件,当用户发出每次I/O需要读 ...
最新文章
- latex常见编译错误和细节
- go语言csv包_golang 写入 csv 文件
- LDP传输地址配置——Vecloud
- BigData:绘制2018年福布斯中国富豪榜人名坐标地图(解决多个人名显示在同一个家乡地点)
- java对密码进行加密的方法_如何在JAVA中使用MD5加密对密码进行加密
- boost::incremental_components用法的测试程序
- 程序员经常看的开源月刊《HelloGitHub》第 57 期
- Python生成大量随机人员信息
- Tomcat服务器响应特别慢(服务器假死)的解决办法
- 【 理想的机器学习书】
- 通过Web.config中的configSections配置自己系统的全局常量
- php5.6/7.0,浅谈PHP5.6 与 PHP7.0 区别
- express 手册
- 中小企业固定资产管理办法哪种好?
- thinkpad T580加装内存条
- 2021-09-15如何把一个字符串的大小写取反(大写变小写小写变大写),例如 ’AbC‘ 变成 ‘aBc‘
- STM32F4_外部中断详解(EXTI)
- 【笔记】OpenSSL 使用
- ffmpeg 命令图片和视频相互转换
- JAVA简易贪吃蛇游戏实现