运行时类型信息
在程式设计中,所谓的执行期型态讯息(Runtime type information,RTTI)指的是在程式执行时保存其物件的型态讯息的行为。某些语言实作仅保留有限的型态讯息,例如继承树资讯,而某些实作会保留较多资讯,例如物件的属性及方法讯息。
执行期型态讯息是一个电脑术语,用以标示一个电脑语言是否有能力在执行期保持或判别其物件或变数的型态讯息。
C++
虽然执行期型态讯息是一个通用的电脑术语,但是通常用来称呼C++的特质。为了让C++的指令达到动态指派(dynamic_cast
)、型态识别码(typeid
)操作与例外处理的能力,执行期型态讯息是必须的。
C++语言提供了dynamic_cast和typeid两种运算符,typeid用于在运行时识别类型信息,dynamic_cast具有运行时类型识别和类型转换匹配2个功能。实现方法为每个类型对应一个const type_info类型对象,存储了这个确切类型信息。在C++标准头文件<typeinfo>
中,type_info类重载了operator=()、operator!=()、name()等成员函数。
dynamic_cast的语法形如:
dynamic_cast<dest>(src);
dest和src都必须为指针或者引用。如果运行时src和dest所引用的对象,是相同类型,或者存在is-a关系(public继承),则转换成功;否则转换失败。dynamic_cast只能用来转换多态类型(即定义了虚函数)的对象的指针或引用。如果操作数是指针,成功则返回目标类型的指针,失败返回NULL。如果操作数是引用,成功则返回目标类型的引用,失败抛出std::bad_cast异常。
dynamic_cast的“运行时类型的转换匹配”,是通过维护一棵由type_info类型对象作为节点的类型继承关系的树,遍历这棵继承树来确定一个待转换的对象的类型和目标类型之间是否存在is-a关系。
下列是一C++的执行期型态讯息应用范例:
class base {
virtual ~base(){}
};
class derived : public base {
public:
virtual ~derived(){}
int compare (derived &ref);
};
int my_comparison_method_for_generic_sort (base &ref1, base &ref2)
{
derived & d = dynamic_cast<derived &>(ref1); // rtti used here
// rtti enables the process to throw a bad_cast exception
// if the cast is not successful
return d.compare (dynamic_cast<derived &>(ref2));
}
Visual C++
在类的虚表的前面存放RTTI数据块的指针。因此,类必须有虚函数,才会有RTTI。 数据结构如下:
struct TypeDescriptor
{
DWORD ptrToVTable;
DWORD spare;
char name[8];
};
struct PMD
{
int mdisp; //member displacement
int pdisp; //vbtable displacement
int vdisp; //displacement inside vbtable
};
struct RTTIBaseClassDescriptor
{
struct TypeDescriptor* pTypeDescriptor; //type descriptor of the class
DWORD numContainedBases; //number of nested classes following in the Base Class Array
struct PMD where; //pointer-to-member displacement info
DWORD attributes; //flags, usually 0
};
struct RTTIClassHierarchyDescriptor
{
DWORD signature; //always zero?
DWORD attributes; //bit 0 set = multiple inheritance, bit 1 set = virtual inheritance
DWORD numBaseClasses; //number of classes in pBaseClassArray
struct RTTIBaseClassArray* pBaseClassArray;
};
struct RTTICompleteObjectLocator
{
DWORD signature; //always zero ?
DWORD offset; //offset of this vtable in the complete class
DWORD cdOffset; //constructor displacement offset
struct TypeDescriptor* pTypeDescriptor; //TypeDescriptor of the complete class
struct RTTIClassHierarchyDescriptor* pClassDescriptor; //describes inheritance hierarchy
};