20、执行期类型识别

目录

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

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

References并不是Pointers

Typeid运算符


执行期类型识别:Runtime Type Identification,RTTI

在cfront中,用以表现一个程序所谓“内部类型体系”,看起来像这样:

class node {...};

//root of the 'type' subtree:basic types,
//'derived' types:pointers,arrays,
// functions,classes,enums ...
class type : public node {...};

//two representations for functions
class fct : public type {...};
class gen : public type {...};

其中gen是 generic的简称,用来表现一个overloaded function。

于是只要你有一个变量,或是类型type *的成员(并知道它代表一个函数),你就必须决定其特定的derived type是否为fct或是gen。

class String {
public:
	operator char *();
	//...
};

在2.0 导入const member functions之前,conversion运算符不能够被overloaded,因为它们不使用参数。直到引进了const member functions,情况才有所变化。现在,像下面这样的声明就有可能了:

class String {
public:
	// ok with Release 2.0
	operator char *();
	operator char *() const;
	//...
};

也就是说,在2.0版本之前,以一个explicit cast来存取derived object总是安全(而且比较快速)的,像下面这样:

typedef type *ptype;
typedef fct *pfct;

simplify_conv_op(ptype pt)
{
	// ok: conversion operator can only be fcts
	pfct pf = pfct(pt);
	//...
}

在const member functions引入之前,这份代码是正确的。但是在const member functions引进之后,不论程序注释还是程序代码就都不对了。程序之所以失败,是因为String class声明的改变。因为char *conversion运算符现在被内部视为一个gen而不是一个fct。

下面这样的转换形式:

pfct pf = pfct(pt);//type class to fct class

被称为downcast(向下转换),因为它有效的把一个base class 转换至继承框架的末端,变成其derived classes中的某一个。Downcast有潜在性的危险,因为它遏制了类型系统的作用,不正确的使用可能带来错误的解释(如果它是一个read操作)或腐蚀掉程序内存(如果它是一个write操作)。在我们的例子中,一个指向gen object的指针被不正确地转换为一个指向fct object地指针pf。所有后续对pf地使用都是不正确的。

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

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

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

C++的RTTI机制提供了一个安全的downcast设备,但只对那些展示“多态(也就是使用继承和动态绑定)”的类型有效。

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

dynamic_cast运算符可以在执行期决定真正的类型。如果downcast是安全的(也就是说,如果base type pointer指向一个derived class object),这个运算符会被传回被适当转换过的指针。如果downcast不是安全的,这个运算符会传回0。下面就是我们重写我们原本的cfront downcast:

typedef type *ptype;
typedef fct *pfct;

simplify_conv_op(ptype pt)
{
	if(pfct pf = dynamic_cast<pfct>(pt)){
		//表示pf转换成功了
		//... process pf
	}else{...}
}

什么是dynamic_cast的真正成本呢?pfct的一个类型描述器会被编译出来。由pt所指向的class object类型描述器必须在执行期通过vptr取得。下面就是可能的转换:

//取得pt的类型描述器
((type_info *)(pt->vptr[0]))->_type_descriptor;

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

References并不是Pointers

程序执行中对一个class指针类型实施dynamic_cast运算符,会获得true或false:

  • 如果传回真正的地址,则表示这一object的动态类型被确认了,一些与类型有关的操作现在可以施行于其上了。
  • 如果传回0,则表示没有指向任何object,意味着应该以另一种逻辑施行于这个动态类型未确定的object身上。

dynamic_cast运算符也适用于reference身上。然而对于一个non-type-safe cast,其结果不会与施行于指针的情况相同。为什么?一个reference不可以像指针那样“把自己设置成0便代表了no object”。因此当dynamic_cast运算符施行于一个reference时候,不能提供像指针对等的那一组true/false。取代的是:

如果reference真正参考到适当的derived class,downcast会被执行而程序可以继续进行。

如果reference并不真正是某一种derived class,那么,由于不能传回0,因此抛出一个bad_cast exception。

下面是重新实现的simplify_conv_op函数,参数改为一个reference:

simplify_conv_op(const type rt)
{
	try {
		fct &rf = dynamic_cast<fct &>(rt);
	}
	catch(bad_cast){
		//...mumble ...
	}
}

Typeid运算符

使用typeid运算符,就有可以以一个reference达到相同的执行期代替路线(runtime ‘alternative pathway’):

simplify_conv_op(const type rt)
{
	if(typeid(rt) == typeid(fct))
	{
		fct &rf = static_cast<fct &>(rt);
		//...
	}else{...}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值