typeid详解
在揭开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的字符串,用来表示相应的类型名,但务必注意这个返回的类型名与程序中使用的相应类型名并不一定一致(往往如此,见后面的程序),这具体由编译器的实现所决定的,标准只要求实现为每个类型返回唯一的字符串。   
上面的都是一些理论的东西,看不真切,下面将通过代码和图例来展示。
#include <iostream>   
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 *pb2 = dynamic_cast<Base *>(new Derived);   
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函数做一个小小调整,为其加上一个虚函数,再看输出结果。
class Base {virtual void f(){}; };   
/*...*/    
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操作符指出底层对象的派生类类型。
typeid详解的更多相关文章
- typeid详解(转)
		
(http://www.cppblog.com/smagle/archive/2010/05/14/115286.html) 在揭开typeid神秘面纱之前,我们先来了解一下RTTI(Run-Time ...
 - 【转】gdb typeid 详解
		
在揭开typeid神秘面纱之前,我们先来了解一下RTTI(Run-Time Type Identification,运行时类型识别),它使程序能够获取由基指针或引用所指向的对象的实际派生类型, ...
 - 20160208.CCPP体系详解(0018天)
		
程序片段(01):main.c 内容概要:PointWithOutInit #include <stdio.h> #include <stdlib.h> //01.野指针详解: ...
 - 异常处理与MiniDump详解(2)  智能指针与C++异常
		
write by 九天雁翎(JTianLing) -- blog.csdn.net/vagrxie 讨论新闻组及文件 一. 综述 <异常处理与MiniDump详解(1) C++异常>稍 ...
 - Apache Rewrite 规则详解
		
在开篇之前: 我想说这篇文章其实是我刚刚接触Rewrite的时候学习的文档,应属转载,但是在这里我不想写明原地址,原因是文章中大多数给出的配置命令经实验都是错误的.需要原文的可以在谷歌上搜索一下&qu ...
 - ASP.NET MVC Filters 4种默认过滤器的使用【附示例】  数据库常见死锁原因及处理  .NET源码中的链表  多线程下C#如何保证线程安全?  .net实现支付宝在线支付  彻头彻尾理解单例模式与多线程  App.Config详解及读写操作  判断客户端是iOS还是Android,判断是不是在微信浏览器打开
		
ASP.NET MVC Filters 4种默认过滤器的使用[附示例] 过滤器(Filters)的出现使得我们可以在ASP.NET MVC程序里更好的控制浏览器请求过来的URL,不是每个请求都会响 ...
 - Net is as typeof 运行运算符详解     net 自定义泛型那点事
		
Net is as typeof 运行运算符详解 概述 在了解运行运算符的前提我们需要了解什么是RTTI ,在任何一门面向对象的语言中,都有RTTI这个概念(即 运行时). RTTI(Run-Ti ...
 - 详解 C++11 lambda表达式
		
详解 C++11 lambda表达式 lambda表达式是函数式编程的基础.咱对于函数式编程也没有足够的理解,因此这里不敢胡言乱语,有兴趣的可以自己查找相关资料看下.这里只是介绍C++11中的la ...
 - Oracle/PLSQL存储过程详解
		
原文链接:https://blog.csdn.net/zezezuiaiya/article/details/79557621 Oracle/PLSQL存储过程详解 2018-03-14 17:31: ...
 
随机推荐
- 【转】进程与CPU
			
声明:本博客转自:http://blog.chinaunix.net/uid-20737871-id-1881246.html 简单地说,CPU 亲和性(affinity) 就是进程要在某个给定的 C ...
 - __slots__ 属性绑定
			
s = Student() # 创建新的实例 s.name = 'Michael' # 绑定属性'name' s.age = 25 # 绑定属性'age' s.score = 99 # 绑定属性'sc ...
 - ios调用第三方程序打开文件,以及第三方调用自己的APP打开文件
			
1.自己的APP调用第三方打开文件 主要是使用 UIDocumentInteractionController 类 并实现 UIDocumentInteractionControllerDel ...
 - NSLog函数重写
			
跟C++的输出函数相比,NSlog函数有个很大的优势,就是它可以输出对象. 在实际使用过程中,我们可以通过实现description函数来实现对NSLog函数的重写 -(NSString*)descr ...
 - 公网IP映射修改后,原先的图片访问却不行了
			
描述:www与img在一个公网的nginx下时,www访问img没问题 ip:*.*.*.26 www与jimg同样还在同一组nginx,改另一个公网IP映射进来的时候,却访问不了 ip:*.* ...
 - iOS 开发者必知的 75 个工具(译文)
			
原文地址:http://benscheirman.com/2013/08/the-ios-developers-toolbelt (需FQ) 如果你去到一位熟练的木匠的工作室,你总是能发现他/她有 ...
 - International Conference in 2014
			
International Conference on Machine Learning (ICML2014, Beijing).(papers are available) Neural Infor ...
 - 如果我用C#来输出99表
			
题目:参见这个链接,简单点说就是在控制台输出一个99乘方表. 无聊想了个C#版本的解答: private static void Print(int n) { var s = Enumerable.R ...
 - C#设计模式(22)——访问者模式(Vistor Pattern)
			
一.引言 在上一篇博文中分享了责任链模式,责任链模式主要应用在系统中的某些功能需要多个对象参与才能完成的场景.在这篇博文中,我将为大家分享我对访问者模式的理解. 二.访问者模式介绍 2.1 访问者模式 ...
 - 深入理解javascript 中的 delete(转)
			
在这篇文章中作者从<JavaScript面向对象编程指南>一书中关于 delete 的错误讲起,详细讲述了关于 delete 操作的实现, 局限以及在不同浏览器和插件(这里指 firebu ...