RTTI---dynamic_cast

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/shanghx_123/article/details/82777436

什么是RTTI?

RTTI(Run-Time Type Identification),通过运行时类型信息程序能够使用基类的指针或引用来检查这些指针或引用所指的对象的实际派生类型。

RTTI提供了以下两个非常有用的操作符:
(1)typeid操作符,返回指针和引用所指的实际类型。
(2)dynamic_cast操作符,将基类类型的指针或引用安全地转换为派生类型的指针或引用。

typeid函数

该函数的主要作用就是让用户知道当前的变量是什么类型的,比如使用typeid(a).name()就能知道变量a是什么类型的。因为typeid()函数是一个返回类型为const typeid_info&类型的函数。

在这里我们主要针对上篇博客中的问题讨论,所以这里typeid函数不做过多讨论。
C++第二大特性—继承

dynamic_cast操作符

作用:该转换符用于将一个指向派生类的基类指针或引用转换为派生类的指针或引用,但需注意dynamic_cast转换符只能用于含有虚函数的类。
格式

dynamic_cast<类型>(表达式)			//其中的类型是指要将表达式转换成的目标类型

这里附上我们之前代码:

#include<iostream>
#include <stdlib.h>
#include<string>
using namespace std;
class Person
{
public:
	virtual void  Display()			//这里需要将基类的函数声明成虚函数,这样才可以用dynamic_cast从操作符
	{
		cout << _name << endl;
	}
protected:
	string _name; // 姓名
};

class Student : public Person
{
public:
	int _num;
};
void Test()
{
	Person p;
	Student s;
	Person *p1 = &s;						//用基类指针指向子类对象
	//p1->_num=10;				 			//不可访问 ,此时编译器会显示Person没有_num这个成员
	Student *c = dynamic_cast<Student*>(p1);		//将基类指针转化为子类指针
	if (c)													//对结果进行判断
	{
		c->_num = 10;
		cout << "success->" << c->_num<<endl;
	}
	else
	{
		cout << "this is a nullptr!" << endl;
	}
}
int main()
{
	Test();	
	system("pause");
	return 0;
}

运行结果:

在这里插入图片描述

从结果可以看出,我们是先用基类指针指向子类对象,也就是将子类的指针赋值给基类指针,此时p1这个指针Person* 类型,想要访问到子类Student的对象是不可以的,所以只能借助dynami_cast操作符来完成,它会将p1强转为Student*类型,这时p1就可以访问到子类Student的对象。

可能有人会质疑,1.为什么不直接用子类Student的指针指向其对象? 然后想怎么访问怎么访问,一都没事,干嘛要先转成基类指针,又转回来?或者又有问题 , 2.就是p1是基类指针指向子类对象时,我要访问子类对象,在强转回来就完事了,为什么要用dynamic_cast? 这两个问题有一个共同的答案就是:

解释: 在C++中存在虚函数,也就存在了多态性,对于多态性的对象,在程序编译时可能会出现无法确定对象的类型的情况。当类中含有虚函数时,其基类的指针就可以指向任何派生类的对象,这时就有可能不知道基类指针到底指向的是哪个对象的情况,类型的确定要在运行时利用运行时类型标识做出。这时为了获得一个对象的类型就可以使用typeid函数;要转换类型就可以用dynamic_cast操作符。因为dynamic_cast与运行时类型转换密切相关,也就是它会在程序运行时进行类型转换。这样当我们不知道基类指针指向哪个子类对象时,我们只需要dynamic_cast直接转化为我们想要的类型即可。

  • 如果我们将上面Test函数的代码改成如下:
void Test()
{
	Person p;
	Student s;
	Person *p3 = &p;
	//Student *p2 = (Student*)&p;
	//Student *c = dynamic_cast<Student*>(p2);
	Student *c = dynamic_cast<Student*>(p3);
	if (c)
	{
		c->_num = 10;
		cout << "success->" << c->_num << endl;
	}
	else
	{
		cout << "this is a nullptr!" << endl;
	}
}

在这里插入图片描述
可以看到结果,说明若用原本基类的指针指向基类对象的地址,用了dynamic_cast将基类指针强转为子类指针之后,是不可以转换成功的,这时,强转之后的子类指针c会是一个NULL值。原因也很简单,因为人家基类指针p3本来就指向基类的对象,强转之后可能会出现访问越界等问题,当然不可以了,在这里,用dynamic_cast的好处就是让程序自己判断,能否转换成功;成功则转,否则就返回空值。避免
了自己手动强转在不成功的情况下出现访问越界的问题。

  • 如果再将Test函数代码改下:
void Test()
{
	Person p;
	Student s;
	//Person *p3 = &p;
	Student *p2 = (Student*)&p;
	Student *c = dynamic_cast<Student*>(p2);
	//Student *c = dynamic_cast<Student*>(p3);
	if (c)
	{
		c->_num = 10;
		cout << "success->" << c->_num << endl;
	}
	else
	{
		cout << "this is a nullptr!" << endl;
	}
}

运行结果:

在这里插入图片描述
可以看到结果;程序崩溃,原因就是我们先手动将基类地址转换成了子类地址之后,这样做后dynamic_cast会认为此时的子类指针p2就是原本指向子类的对象(其实不然),重点来了:此时dynamic_cast就是将p3再次强转成子类指针c,也就是说此时dynamic_cast就没做什么事,此时的c指针其实就是p3指针的赋值,所以c指针此时的值不为空,所以就会执行success语句;可是p3人家本来就是指向基类的对象,让它去访问子类的独有成员_num很明显是不可以的,所以程序也就自然崩溃了。
总结一下,我们就可以把dynamic_cast的特点列举如下:

  1. dynamic_cast转换符将一个指向派生类的基类指针或引用转换为派生类的指针或引用,但需注意dynamic_cast转换符只能用于含有虚函数的类。这是它的作用,也是其特点。
  2. dynamic_cast与运行时类型转换密切相关,所以在无法确定基类指针指向那个子类对象时,可以用它,并且不要忘了用if语句判断是否强转成功,成功返回强转后的地址,失败返回0。
  3. 在使用dynamic_cast将基类指针强转为子类指针时,若要被强转的基类指针原本指向子类的对象,则是可以强转成功的;若要被强转的基类指针原本指向基类的对象,则是不可以强转成功的, 若把基类的对象的地址手动强转成子类指针后, 再用dynamic_cast强转成子类指针,表面是可以转换成功,但实在dynamic_cast没做什么事,转换后的指针还是不能访问子类的独有成员。
  4. 一般情况下不推荐这样使用dynamic_cast转换符,因为dynamic_cast的转换并不会总是成功的。

C++有四种强制类型转换符,分别是dynamic_cast,const_cast,static_cast,reinterpret_cast。其他操作符后面介绍

注:本文为个人学习和总结,如有不正之处,欢迎评论指正。

猜你喜欢

转载自blog.csdn.net/shanghx_123/article/details/82777436