C++ RTTI 简介

代码编译运行平台:VS2017+Debug+Win32


RTTI(Runtime Type Identification)是“运行时类型识别”的意思。面向对象的编程语言,像 C++、Java、Delphi 都提供了对 RTTI 的支持。 本文将简略介绍 RTTI 的一些背景知识、描述 RTTI 的概念,并通过具体例子和代码介绍什么时候使用以及如何使用 RTTI。

本文还将详细描述 C++ 两个重要的 RTTI 运算符的使用方法,它们是 typeid 和 dynamic_cast。

1.typeid 的用法

1.1 type_info 类

typeid 的结果是const type_info&。所以下面先对type_info类做下介绍:

class type_info {
public:
    virtual ~type_info();
    size_t hash_code() const
    _CRTIMP_PURE bool operator==(const type_info& rhs) const;
    _CRTIMP_PURE bool operator!=(const type_info& rhs) const;
    _CRTIMP_PURE int before(const type_info& rhs) const;
    _CRTIMP_PURE const char* name() const;
    _CRTIMP_PURE const char* raw_name() const;
};

我们不能直接实例化 type_info 类的对象,因为该类只有一个私有复制构造函数。 构造 type_info 对象的唯一方式是使用 typeid 运算符。 由于赋值运算符也是私有的,因此不能复制或分配类 type_info 的对象。

1.2 typeid 应用实例

1.2.1 typeid 静态类型判断

typeid 可以静态地确定操作数的类型,也可以动态地确定操作数的类型,这取决于操作数本身是否拥有虚函数。当 typeid 的操作数是一个基本类型的变量,或者是一个不带虚函数的对象时,typeid 的运行结果是在编译阶段决定的,所以是一种静态的类型判断。

#include <iostream>
using namespace std;

class A  {
	int i;
public:
	void show() {
		cout<<"In class A"<<endl;
	}
};

class B {
	int i;
public:
	void show() {
		cout<<"In class B"<<endl;
	}
};

template <typename T>void func(T& a) {
	if(typeid(A)==typeid(T))
		cout<<"is A"<<endl;
	if(typeid(B)==typeid(T))
		cout<<"is B"<<endl;
}

int main() {
	if(typeid(B)==typeid(A))
		cout<<"A equal to B"<<endl;
	else
		cout<<"A not equal to B"<<endl;

	A a;
	B b;
	func<A>(a);
	func<B>(b);
	cout<<typeid(B).name()<<endl;
}

程序输出结果:

A not equal to B
is A
is B
class B

在上面的程序中,函数模板func()被实例化为class A和class B时,typeid(T)是在编译阶段静态确定的。在函数模板内部,可以通过typeid操作决定在模板参数被实例化为不同数据类型的时要采取不同的行动。

1.2.2 typeid 动态类型判断

typeid 更多的时候是在运行时用来动态地确定指针或引用所指向对象的类型,这时要求 typeid 所操作的对象一定要拥有虚函数。见下面的程序。

#include <iostream>
using namespace std;

class A {
	virtual void func(){}
};

class B:public A {};

void reportA(A* pa) {
	if(typeid(*pa)==typeid(A))
		cout<<"Type of *pb is A"<<endl;
	else if(typeid(*pa)==typeid(B))
		cout<<"Type of *pb is B"<<endl;
}

void reportB(B* pb) {
	if(typeid(*pb)==typeid(A))
		cout<<"Type of *pb is A"<<endl;
	else if(typeid(*pb)==typeid(B))
		cout<<"Type of *pb is B"<<endl;
}

int main() {
	A a,*pa;
	B b,*pb;
	pa=&a;
	reportA(pa);
	pa=&b;
	reportA(pa);
	pb=static_cast<B*>(&a);
	reportB(pb);
	pb=&b;
	reportB(pb);
}

程序输出结果:

Type of *pb is A
Type of *pb is B
Type of *pb is A
Type of *pb is B

从上面的运行结果可以看出,使用typeid确实能够在程序运行期间动态地判断指针所指对象的实际类型。使用引用可以到达同样的效果,因为引用的底层实现就是指针。

要注意的是:
(1)如果在 Class A 的定义中,将函数func()定义为普通函数(即将前面的 virtual 关键字去掉),那么typeid(*pa)的结果永远是typeid(A),而typeid(*pb)的结果永远是 typeid(B)。也就是说,由于 pa 和 pb 所指向的对象中没有虚函数,该对象就没有虚函数表存放运行时信息,typeid 实际就变成了一种静态运算符。

(2)C++ 中的一切动态机制,包括虚函数、RTTI 等,都必须通过指针或引用来实现。换句话说,指针所指的对象或引用所绑定的对象,在运行阶段可能与声明指针或引用时的类型不一致。如果不使用指针或引用,而是直接通过对象名访问对象,那么即使对象拥有动态信息(虚函数表),对象的动态信息与静态申明对象时的信息必然一致,就没有必要访问虚函数表;而如果对象不拥有虚函数,就没有虚函数表存放动态信息,也就无法在运行时动态判断指针所指向对象(或引用所绑定对象)的实际类型。

2.dynamic_cast 用法

详见我的另一篇 blog C++ 数据类型转换详解之终极无惑


参考文献

[1] 百度百科.RTTI
[2] 陈刚.C++高级进阶教程[M].武汉:武汉大学出版社,2008.C8.6,P288-P290
[3] typeid 运算符
[4] C++中的RTTI(转)

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值