7.3 执行期类型识别(Runtime Type Identification,RTTI)

探讨C++运行时类型识别(RTTI)机制,包括类型安全的向下转换、dynamic_cast运算符的使用及成本,以及typeid运算符的功能。分析了在多态和非多态类型中RTTI的作用及其对程序效率的影响。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Type-Safe Downcast(保证安全的向下转换操作)

C++被吹毛求疵的一点,它缺乏一个保证安全的downcast(向下转换操作)。只有在“类型真的可以被适当转换”的情况下,你才能够执行downcast。一个type-safe downcast必须在执行期对指针所有查询,看看它是否指向它所展现之object真正的类型。因此,欲支持type-safe downcast,在object空间和执行时间上都需要一些额外的负担:

  • 额外的空间存储类型信息(type information),通常是一个指针,指向某个类型信息节点。
  • 额外的时间以决定执行期的类型(runtime tpye),这需要在执行期才能决定。

这种机制对于下面的C结构,会如何影响:

char* winnie_tbl[] = {"rumbly in my tummy","on,bother"};

会导致空间和效率上的不良后果:

  1. 程序员大量使用多态,并因此需要大量的downcast。
  2. 程序员使用内建数据类型以及非多态,就不会需要额外的负担所带来的不良结果。

C++的RTTI提供了一个安全的downcast,但只是针对那些展现(多态)的类型有效。分辨一个class的定义就是觉得这个class用以表现一个独立的ADT还是一个支持多态的可继承子类型(subtype)。策略之一就是导入一个新的关键词,优点可以清楚的识别出新特质的类型,缺点就是必须翻新久程序。

另一策略就是通过声明一个或多个virtual function来区别class的声明。其优点是透明化的将旧程序转换过来,只要重新编译好;缺点这是可能会将一个非必要的virtual function强迫导入继承体系的base class身上。目前C++的RTTI就是这个策略。

从编译器角度来看,这个策略还是有其他优点:就是大量降低了额外的负担。所有动态类的objects都维护了一个指针(vptr),指向vtbl。只要我们把该class的RTTI object地址放进virtual table,那么额外的负担就降低为:每一个class object 只要多花费一个指针。这指针只被设定一次,它是被编译器静态编译的,而非执行期由class constructor设定(vptr才是这么设定的)。

Type-Safe Dynamic Cast(保证安全的动态转换)

dynamic_cast运算符可以在执行期决定真正的类型。如果downcast是安全的,这个运算符会传回被被转换过的指针,如果downcast不安全,会传回0。

动态转换的成本:对于一个基类指针转化成子类的指针,必须通过class object类型描述符在执行期通过vptr取得的:

((type_info*)(pt->vptr[0]))->_type_descriptor;

type_info是C++ Standard所定义的类型描述器的class名称,该class 中放着待索求的类型信息。virtual table的第一个slot内含type_info object的地址;此type_info object与pt所指的class type有关。这两个类型描述器被交给一个runtime library函数,比较之后告诉我们是否吻合。这个比static cast昂贵的多,但却安全得多。

References并不是Pointers

dynamic_cast运算符也使用于reference身上。但是引用不能为0。对于向下转换不成功,指针传回0;引用是抛出异常。

Typeid运算符

使用typeid运算符,就有可能以一个reference达到相同的执行期替代路线:

typeid运算符传回一个const reference,类型为type_info。

对于typeid的测试等的运算符,是一个被overload的函数(typeid(a) == typeid(b)):

bool type_info::operator == (const type_info&) const;

type_info object定义如下:

class type_info
{
public:
		virtual ~type_info();
		bool operator==(const type_info&) const;
		bool operator!=(const type_info&) const;
		bool before(const type_info&) const;
		const char* name() const;
private:
	type_info(const type_info&);
	type_info& operator == (const type_info&);
};

虽然RTTI提供了type_info对于exception handing的支持来说必要的,但对于exception handing的完全支持而言,还不够。如果再加上额外的一些type_info derived classes,就可以在exception发生时提供关于指针、函数、类等等的更详细的信息。

虽然RTTI适合于多态类,事实上type_info objects也适合于内建类型,以及fei多态的使用者的自定义类型。这对exception handing的支持是有必要的。例如:

int ex_errno;
...
throw ex_errno;

//int类型也有自己的type_info object :
int* ptr;
...
if(typeid(ptr) == typeid(int*))
...

//在程序中使用typeid(expression)
int ival;
...
typeid(ival)...;

//或使用typeid(type);
typeid(double)...;

上述使用typeid会传回一个const type_info&,这是静态取得的,非执行期。一般实现策略是在需要时才产生type_info object,而非程序一开头就产生。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值