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);??
};??
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!