c++ RTTI 运行时类型识别

c++ RTTI 运行时类型识别,第1张

文章摘自 C++从入门到精通(第四版)清华大学出版社

目录

一.什么是RTTI

二.RTTI与引用

三.RTTI与多重继承

四.RTTI映射语法

dynamic_cast:用于安全的向下映射

const_cast:用于映射常量和变量

static_cast:为了行为良好和行为较好使用的映射,如向上转型和类型自动转换。

reinterpret_cast:将某一类型映射回原有类型时使用 

运行时类型识别(Run-time Type Identification,RTTI)是在只有一个指向基类的指针或引用时所确定的一个对象的类型。

在编写程序的过程中,往往只提供了一个对象的指针,但通常在使用时需要明确这个指针的确切类型。利用RTTI就可以方便的获取某个对象指针的确切类型并进行控制。

一.什么是RTTI

RTTI可以在程序运行时通过某一对象的指针确定该对象的类型。许多程序设计人员都使用过虚基类编写面向对象的功能。通常在基类中定义了所有子类的通用属性或行为。但有些时候子类会存在属于自己的一些公有的属性或行为,这时通过基类对象的指针如何调用子类特有的属性或行为呢?首先需要确定的是这个基类对象属于哪个子类,然后将该对象转换成子类对象再进行调用。

下图展示了具有特有功能的类。

由上图中可以看出CBint类和CBstring类都继承于CBase,这三个类存在于一个公共方GetName(),CBint类有自己的方法GetInt,CBString类有自己的方法GetString().如果想通CBase类指针调用CBint类或CBString类的特有方法就必须确定指针的具体类。下面代码完成了这样的功能。

//RTTI
#include
using namespace std;
class CBase
{
	public:
		virtual char *GetName()=0;
};
class CBint:public CBase
{
	public:
		char *GetName()
		{
			return "CBint";
		}
	    int GetInt()
	    {
	    	return 1;
		}
};
class CBString:public CBase
{
	public:
		char *GetName()
		{
			return "CBString";
		}
		char *GetString()
		{
			return "Hello";
		}
};
int main()
{
	CBase *B1=(CBase*)new CBint();
	printf(B1->GetName());
	CBint *B2=static_cast(B1);//静态转换 
	if(B2)
	{
		printf("%d",B2->GetInt());
	}
	CBase *C1=(CBase*)new CBString();
	printf(C1->GetName());
	CBString *C2=static_cast(C1);
	if(C2)
	{
		printf(C2->GetString());
	}
	return 0;
}

从上面的代码可以看出,基类CBase的指针B1和C1分别指向了CBint类和CBString类的对象,并且在程序运行时基类通过ststic_cast进行了转换,这样就形成了一个运行时类型识别的过程。

二.RTTI与引用

RTTI必须能与引用一起工作。指针与引用存在明显不同,因为引用总是由编译器逆向引用,而一个指针的类型或他指向的类型可能要检测,例如,下面代码定义了一个子类和一个基类。

class CB
{
	public:
		int GetInt(){return 1;}
};
class Cl:public CB
{
};

通过下面的代码可以看出,typeid()获取的指针是基类类型,而不是子类类型或派生类类型,typeid()获取的引用是子类类型。

class CB
{
	public:
		int GetInt(){return 1;}
};
class Cl:public CB
{
};
int main()
{
	CB *p=new Cl();
	CB &t=*p;
	if(typeid(p)=typeid(CB*))
	{
		printf("指针类型是基类类型\n"); 
	}
	if(typeid(p)!=typeid(Cl*))
	{
		printf("指针类型不是子类类型\n");
	} 
	if(typeid(t)==typeid(CB))
	{
		printf("引用类型是基类类型\n"); 
	}
	return 0;
}

指针指向的类型在typeid()看来是派生类而不是基类,而用一个引用的地址产生的是基类而不是派生类。

三.RTTI与多重继承

RTTI具有非常强大的功能,对于面向对象的编程方法,如果在类继承时使用了virtual虚基类,RTTI仍可以准确的获取对象在运行时的信息。

例如,下面的代码通过虚基类的形式继承了父类,通过RTTI获取对象指针对象的信息。

class CB()
{
	virtual void dowork();
};
class CD1:virtual public CB
{
};
class CD2:virtual public CB
{
};
class CD3:public CD1,public CD2
{
	public:
		char *Print(){
			return "Hello";
		}
};
int main()
{
	CB *p=new CD3();//向上转型
	cout<(p);
	if(pd3)
	cout<Print()<

即使只提供一个virtual基类指针,typeid()也能准确地检测出实际对象的名字。用动态映射同样也会工作得很好,但编译器不允许试图用原来的方法强制映射:

CD3 *pd3=(CD3*)p //错误转换

编译器知道这不可能正确所以它要求用户使用动态映射。

四.RTTI映射语法

 无论什么时候使用类型映射,都是在打破类型系统。这实际上是在告诉编译器,即使知道一个对象的确切类型,还可以假定它是另外一种类型,这本身就是一件很危险的事情,也是一个容易发生错误的地方。

为了解决这种问题,C++用保留关键字dynamic_cast,const_cast,static_cast,reinterpret_cast提供了一个统一的映射语法。为需要进行动态映射时提供了可能。这意味着那些已有的映射语法已经被重载的太多,不能再支持任何其他的功能了。

dynamic_cast:用于安全的向下映射

例如,通过dynamic_cast实现基类指针的向下转型。

#include
using namespace std;
class CBase
{
	public:
		virtual void Print(){
			cout<<"CBase"<Print();
	CChild *d=dynamic_cast(p);
	d->Print();
	return 0;
}
const_cast:用于映射常量和变量

如果想把一个const转换为非const,就要用到const-cast。这是可以用const-cast的唯一转换,如果还有其他的转换牵扯进来,它必须分开来指定,否则会有一个编译错误。

例如,在常方法中修改成员变量和常量的值。

#include
using namespace std;
class CX
{
	protected:
		int m_count;
	public:
		CX(){
			m_count=10;
		}	
		void f() const
		{
			{
				const_cast(this)->m_count=8;
				cout<f();
	const int i=10;
	int *n=const_cast(&i);
	*n=5;
	cout<<*n<
static_cast:为了行为良好和行为较好使用的映射,如向上转型和类型自动转换。

例如,通过static_cast将子类指针向上转成基类指针。

#include
using namespace std;
class CB
{
	public:
		 void print()
		{
			cout<<"class CB"<print();
	CB *b=static_cast(p);//向上转型
	b->print();
	return 0; 
}
reinterpret_cast:将某一类型映射回原有类型时使用 

例如,将整型转成字符型,再由reinterpret_cast转换回原类型。

#include
using namespace std;
int main()
{
	int n=97;
	char p[4]={0};//定义与整型大小相同的字符数组
	p[0]=(char)n;
	cout<(&p);
	cout<<*f<

欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/langs/1325363.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2022-06-12
下一篇2022-06-12

发表评论

登录后才能评论

评论列表(0条)