`
844604778
  • 浏览: 550403 次
文章分类
社区版块
存档分类
最新评论

C++中RTTI机制剖析

 
阅读更多

C++中要想在运行时获取类型信息,可没有Java中那么方便,Java中任何一个类都可以通过反射机制来获取类的基本信息(接口、父类、方法、属性、Annotation等),而且Java中还提供了一个关键字,可以在运行时判断一个类是不是另一个类的子类或者是该类的对象,但C++却没有这么多功能,C++中获得类信息只能通过RTTI机制,而且功能还是很有限的,因为C++中最终生成的代码是直接与机器相关的,而Java中会生成字节码文件,再由JVM加载运行,字节码文件中可以含有类的信息。

C++中RTTI的简单源程序示例:

class A{
private:
	int a;
};
class B{
public: 
	//加一个虚函数
	virtual void f(){}
};
class C:public B
{
public :
	void f(){};
};
class D:public A
{
public:
	void f(){}
};
int main()
{
	int a=2;
	//打印出int
	cout<<typeid(a).name()<<endl;
	A objA;
	//打印出class A
	cout<<typeid(objA).name()<<endl;
	B objB;
	//打印出class B
	cout<<typeid(objB).name()<<endl;
	C objC;
	//打印出class C
	cout<<typeid(objC).name()<<endl;
	/*
	//以下是多态在VC 6.0编译器不支持,但是在GCC以及微软更高版本的编译器却都是
    //支持的,且是在运行时候来确定类型的,而不是在编译器,会打印出class c
	B *ptr=new C();
	cout<<typeid(*ptr).name()<<endl;
	*/
	A *ptr=new D();
	//打印出class A而不是class D
	cout<<typeid(*ptr).name()<<endl;
	return 0;
}

要想理解上述代码:我们需要明白以下几个事实

1:typeid是一个关键字

2:typeid的结果有时候在编译期确定有时间会在执行期确定

3:typeid运行时,会将判断的结果存储在一个consttypeinfo&对象中

4:不同的编译器对typeid运算的结果差异很大,例如在VC 6.0与G++编译器中,G++编译器支持运行时动态确定类型,而VC 6.0则不支持。

1:typeid是一个关键字,可以在任意一本C++入门书中看到,typeid是一个关键字,像Sizeof一样,要是函数的话,函数传参你有见过这样的吗typeid(int),直接传int,而不是传一个整型值的,我是没见过:)

2:看看上述的程序,你会发现上述程序中除了多态的那一部份(在VC 6.0中是无法编译通过的),其他的均是在编译期运行,多态的会在执行期去运行,为了更具说服务力,看看下面的代码,是上面程序的部分汇编代码:

30:       //打印出int
31:       const type_info &t=typeid(a);//从下面的汇编代码中可以看出类型在编译期就已经确定了
004011C4   mov         dword ptr [ebp-14h],offset int `RTTI Type Descriptor' (00441e08)
32:       cout<<t.name()<<endl;
004011CB   push        offset @ILT+35(std::endl) (00401028)
004011D0   mov         ecx,dword ptr [ebp-14h]
从上面的程序,可以看出对于不是多态类型的,直接在编译器就解决了类型的确定,这样有利于减少程序的运行时间

对于多态类型(看看上面程序中注释掉部分代码在VS 2010中的反汇编代码):

	cout<<typeid(*ptr1).name()<<endl;
00A451B4  mov         esi,esp  
00A451B6  mov         eax,dword ptr ds:[00A5132Ch]  
00A451BB  push        eax  
00A451BC  mov         edi,esp  
00A451BE  push        0A5027Ch  
00A451C3  mov         ecx,dword ptr [ptr1]  
00A451C6  push        ecx  
;可以看出的是在这里调用了__RTtypeid函数,运行的时候来确定指针所指对象的真实类型
00A451C7  call        ___RTtypeid (0A414BAh)  
00A451CC  add         esp,4  
	cout<<typeid(*ptr1).name()<<endl;

从上面的汇编代码中可以看出的是对于类中有虚函数(多态)会在运行时决定类的类型

看看RTtypeid的实现吧

template<typename T>
const TypeDescriptor *__RTtypeid(const T *ptr)
{
    if (!ptr) throw new std::bad_typeid("Attempted a typeid of NULL pointer!");
    //获取指针把指对象的描述符,这里说明了一个问题,是对于多态类,里面会有一个指针指向这个描述符
    const _s_RTTICompleteObjectLocator *pCompleteLocator=GetCompleteObjectLocator(ptr);
    //获取其描述信息
    TypeDescriptor *pTypeDescriptor=pCompleteLocator->pTypeDescriptor;
    //如果未获取到,或者指针为空时,执行下面的逻辑
    if (!pTypeDescriptor) {
        throw std::__non_rtti_object("Bad read pointer - no RTTI data!");
    }
    return pTypeDescriptor;
}
这里的实现还是挺简单的,不是嘛:),其真实的实现原理是,每一个函数数类均有一个虚函数表,编译器会将类的vftable(包括它自己的和从基类继承的)的第一个函数指针前面插入一个指向_s_RTTICompleteObjectLocator结构的指针(描述类信息的指针),这个结构中会存放该类的TypeDescriptor(上面的GetCompleteObjectLocator函数就是用来从vftable获得s_RTTICompleteObjectLocator结构的),因此,即使你将派生类的指针赋给基类的指针,你仍然可以利用上面的算法得到派生类的类型.

typeid关键字将类型信息存放在一个const type_info类中,看看这个类的具体源代码吧

class type_info {
public:
		//析构函数
    _CRTIMP virtual ~type_info();
    //重载的==操作符
    _CRTIMP int operator==(const type_info& rhs) const;
    //重载的!=操作符
    _CRTIMP int operator!=(const type_info& rhs) const;
    _CRTIMP int before(const type_info& rhs) const;//用于type_info对象之间的排序算法
    //返回类的名字
    _CRTIMP const char* name() const;
    _CRTIMP 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);
};

分享到:
评论

相关推荐

    C++虚函数表解析

    在这篇文章中,我只想从虚函数的实现机制上面为大家 一个清晰的剖析。当然,相同的文章在网上也出现过一些了,但我总感觉这些文章不是很容易阅读,大段大段的代码,没有图片,没有详细的说明,没有比较,没有...

    C++ 虚函数表解析

    在这篇文章中,我只想从虚函数的实现机制上面为大家 一个清晰的剖析。当然,相同的文章在网上也出现过一些了,但我总感觉这些文章不是很容易阅读,大段大段的代码,没有图片,没有详细的说明,没有比较,没有...

    逆向c++【pdf】

    I.引言和必要性 II.手工方法 A. 识别类及其构造函数 B....1) 识别构造函数和析构函数 2) 利用RTTI识别多态类 C....1.通过分析构造函数来分析类与类之间的关系 2.通过RTTI分析类与类之间的关系 D.辨别类的成员

    Ghidra-Cpp-Class-Analyzer:Ghidra C++ 类和运行时类型信息分析器

    Ghidra C++ 类和运行时类型信息分析器 API 文档 完整构建和链接的文档版本可在。 建造 在您选择的终端中运行以下命令。 gradle buildExtension 完成后,输出将位于 dist 文件夹中。 安装 将存档解压缩到您选择的...

    Android C++高级编程:使用NDK_Onur Cinar, 于红PDF电子书下载 带书签目录 完整版

    11.5 C++ RTTI支持 248 11.6 C++标准库入门 249 11.6.1 容器 249 11.6.2 迭代器 250 11.6.3 算法 251 11.7 C++运行库的线程安全 251 11.8 C++运行库调试模式 251 11.8.1 GNU STL调试模式 251 11.8.2 STLport...

    C++虚函数及虚函数表简析

    C++中的虚函数的作用主要是实现了多态的...在这篇文章中,我只想从虚函数的实现机制上面为大家 一个清晰的剖析。 当然,相同的文章在网上也出现过一些了,但我总感觉这些文章不是很容易阅读,大段大段的代码,没有图片

    深入理解Java类型信息(Class对象)与反射机制

    本篇主要是深入对Java中的Class对象进行分析,这对后续深入理解反射技术非常重要,主要内容如下:认识Class对象之前,先来了解一个概念,RTTI(Run-TimeTypeIdentification)运行时类型识别,对于这个词一直是C++中...

    pro_android_cpp_with_the_ndk.pdf

    第1章 Android平台上的C++入门 1.1 Microsoft Windows 1.1.1 在Windows平台上下载并安装JDK开发包 1.1.2 在Windows平台上下载并安装ApacheANT 1.1.3 在Windows平台上下载并安装Android...第14章 程序概要分析和NEON优化

    深入浅出MFC【侯捷】

    可卷动的窗口:CScrollView 大窗口中的小窗口:Splitter 切分窗口的功能 切分窗口的程序概念 切分窗口的实现 本章回顾 第12章 打印与预览 概述 打印操作的后台原理 MFC默认的打印机制 Scribble打印机制的增强 打印机...

    深入浅出MFC 2e

    剖析AppWizard Components Dialog Templates和Dialog classes Macros Directives 动手修改Top Studio AppWizard 利用资源编辑器修改IDD_CUSTOM1对话框画面 利用ClassWizard修改IDD_CUSTOM1对话框的对应类CCustomlDlg...

    侯捷- -深入浅出MFC

    剖析AppWizard Components Dialog Templates和Dialog classes Macros Directives 动手修改Top Studio AppWizard 利用资源编辑器修改IDD_CUSTOM1对话框画面 利用ClassWizard修改IDD_CUSTOM1对话框的对应类CCustomlDlg...

    超爽的自学课件(java)

    尽管这些与C和C++中见到的有一定的共通性,但多少存在一些区别。除此以外,所有示例都是完整的Java示例,能使大家很快地熟悉Java的外观。 &lt;br&gt;(4) 第4章:初始化和清除 本章开始介绍构建器,它的作用是担保...

    编程新手真言......

    8.5 大中型软件和复用与逻辑达成 177 8.6 通用设计与专门设计 178 8.7 具象与抽象 178 8.7 架构与应用 179 8.8 应用与设计 179 8.9 与软件有关的哲学 联系 180 8.10 与软工有关的哲学 唯物主义 180 8.11 真正的设计...

    java联想(中文)

    1.12 分析和设计 1.12.1 不要迷失 1.12.2 阶段0:拟出一个计划 1.12.3 阶段1:要制作什么? 1.12.4 阶段2:开始构建? 1.12.5 阶段3:正式创建 1.12.6 阶段4:校订 1.12.7 计划的回报 1.13 Java还是C++? 第2章 ...

    Thinking in Java简体中文(全)

    1.12 分析和设计 1.12.1 不要迷失 1.12.2 阶段0:拟出一个计划 1.12.3 阶段1:要制作什么? 1.12.4 阶段2:开始构建? 1.12.5 阶段3:正式创建 1.12.6 阶段4:校订 1.12.7 计划的回报 1.13 Java还是C++? 第2章 ...

Global site tag (gtag.js) - Google Analytics