typeid()和dynamic_cast<>()

本文深入探讨了C++中的RTTI(运行时类型信息)概念及其应用,包括typeid和dynamic_cast操作符的功能与限制,并对比了使用RTTI与虚函数实现多态的不同方式。

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

一旦提到typeid()函数,就不得不提RTTI了。

RTTI(Run-Time Type Information 运行时类型信息),指程序有能力识别 基类的指针或引用 在运行时实际指向的对象的类型信息。

C++中与RTTI相关的操作符有两个:
(1)typeid 操作符:返回指针或引用所指对象的实际类型。
(2)dynamic_cast 操作符:将基类类型的指针或引用安全地转换为派生类型的指针和引用。

注意:这两个操作符均 只为带有一个或多个虚函数的类返回动态类型信息----即在运行时执行RTTI操作符;对于其他类型则返回静态类型的信息---即在编译时计算RTTI操作符。

再强调一下,如果表达式的类型是类类型且至少包含有一个虚函数,则typeid操作符返回表达式的动态类型,需要在运行时计算;否则,typeid操作符返回表达式的静态类型,在编译时就可以计算。

因为对于编译器来说,如果类中没有声明虚函数,则表明你不会用到类的多态特性,也就无需为你额外提供一个你不需要的RTTI了。这也是C++的设计原则之一,即:不会让你为你不需要的东西有所付出。

typeid()

头文件:# include <typeinfo>
语法--两种形式:typeid (type) 、typeid (expression)即任意表达式或类型名。
常见的用途:比较两个表达式的类型,或者将表达式的类型与特定类型相比较。
typeid返回类型:const type_info&;

type_info是一个定义在std中的类,具体定义如下:

class type_info {
public:
	/** Destructor first. Being the first non-inline virtual function, this
	 *  controls in which translation unit the vtable is emitted. The
	 *  compiler makes use of that information to know where to emit
	 *  the runtime-mandated type_info structures in the new-abi.  */
	virtual ~type_info();

	/** Returns an @e implementation-defined byte string; this is not
	 *  portable between compilers!  */
	const char* name() const 
	{ return __name[0] == '*' ? __name + 1 : __name; }


	bool before(const type_info& __arg) const;
	bool operator==(const type_info& __arg) const;
	bool operator!=(const type_info& __arg) const
	{ return !operator==(__arg); }
	
	//......
	
protected:
	const char *__name;
	explicit type_info(const char *__n): __name(__n) { }

private:
	/// Assigning type_info is not supported.
	type_info& operator=(const type_info&);
	type_info(const type_info&);
};

 ISO C++标准并没有确切定义type_info,它的确切定义编译器相关的,但是标准却规定了其实现必需提供如下四种操作:
 t1 == t2 如果两个对象t1和t2类型相同,则返回true;否则返回false
 t1 != t2 如果两个对象t1和t2类型不同,则返回true;否则返回false
 t.name() 返回类型的C-style字符串,类型名字用系统相关的方法产生
 t1.before(t2) 返回指出t1是否出现在t2之前的bool值

 type_info类没有默认构造函数,它的拷贝构造函数和赋值操作符都定义为private,同时提供protected的显示C风格字符串初始化构造函数,以及public的虚析构函数,这表明它期望用户能够使用它作为基类。

总之,我们可以使用typeid操作符来创建type_info对象。


dynamic_cast<>()

将基类类型的指针或引用安全地转换为派生类型的指针和引用。这句话清楚地表明了,dynamic_cast<>()只用于类的指针或引用在继承体系中的子父类转换。同时,名字中的dynamic表时基类中必须存在虚函数,否则编译器会认为你写的类不需要多态,故而对于dynamic_cast操作符,在编译的时候就会报错。

因为涉及到指针或引用的类运行多态,所以显然继承一定是public继承。当然,如果是在类的成员函数中使用多态,那可以放宽到protected继承。

如果可以安全地转换,则返回实际对象的地址,否则返回一个空指针。


RTTI与虚函数

如果不使用虚函数而只用RTTI的话:

 Circle和Square皆是由Figure所衍生出来的子类别﹐它们各有自己的draw()函数。如果使用typeid(),则代码如下:

void drawing( Figure *p ) { 
	if(typeid(*p).name() == "Circle") 
		static_cast<Circle*>(p)->draw(); 
	else if(typeid(*p).name() == "Rectangle") 
		static_cast<Rectangle*>(p)->draw(); 
}

当Figure类 再派生出新子类的时候,就还要修改上面的函数,再多加一个else if。这太坑了。


但是如果使用虚函数,将draw()声明为虚拟函数,则代码如下,一劳永逸。

void drawing(Figure *p) {
	p->draw();
}


最后,如果发现在扩展的if else语句系列中使用了typeid(),则就要考虑是否应该使用虚函数和dynamic_cast了。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值