本博客将记录:类的相关知识点的第10节的笔记!

今天 总结的这5个知识点都是之前我coding中接触得非常少的,因此务必要重视这一章!

今天总结的知识分为以下5个点:

一、RTTI是什么?
二、dynamic_cast运算符
三、typeid运算符
四、type_info类
五、RTTI与虚函数表

一、RTTI是什么?

所谓的RTTI(Run Time Type Identification):运行时类型识别。

具体一点就是:在程序运行时,程序能够使用基类的指针or引用检查这些指针or引用所指向的对象的实际派生类型。我们也可以把RTTI这种称呼看成啥一种系统提供给我们的一种能力,或者说是一种功能。这种功能是通过2个运算符来体现到:

①dynamic_cast运算符:能够将基类的指针or引用安全地转换为派生类的指针or引用

②typeid运算符:返回指针or引用所指向对象实际的类型

补充:

要想让上述两个运算符能够正常地工作,那么基类中必须至少要有一个虚函数,不然这2个运算符工作的结果就可能与我们预想的有天壤之别。因为,只有存在虚函数时,这2个运算符才会使用指针or引用所绑定的对象的动态类型。

        结论:基类中必须有至少一个虚函数,才能让RTTI的这2个运算符发挥正确的作用!

(更加深层次的原理可能得到我学习深度探索对象模型的第三章才会总结,但是其实我们不用这么折腾地把这种概念性的东西理解地很深奥,说白了你当前阶段会用虚函数来写重写子类中与基类同名的函数就可以了)

二、dynamic_cast运算符

格式:

类名 * 指针名 = dynamic_cast<另一个类名* >(对应的指针);

注意:

在上面我其实已经提及到了,在我们程序运行时,dynamic_cast关键字的操作数必须要包含多态类的类型(什么叫多态?什么叫多态类的类型呢?其实我们并不需要这么学术地去称呼这些听起来这么深奥的概念,就是一句话:要使用dynamic_cast关键字对基类的指针or引用do类型转换的话就必须要让基类的函数中至少有一个是虚函数,否则你根本就没法通过dynamic_cast关键字do类型转换进而简化你写的多态代码

为什么我说用dynamic_cast关键字可以简化我们写的多态的代码呢?其实很简单,你用基类指针指向子类的对象时,要是想让子类重写其与基类中同名的函数的话,就必须要用虚函数(有时我们甚至直接把基类用do是一个抽象类,直接用写为纯虚函数呢),那么你每一个同名函数就都需要给它在基类的声明中加上virtual关键字,有时这样的操作就会是复杂的繁琐的操作。因此我们才有dynamic_cast关键字来给我们do简化的事情!

请看以下代码:(以上讲解了这么多文字,那么下面我就直接通过代码来带你学习一下哈!)

1)用dynamic_cast指针类型:

Human.h

#ifndef __HUMAN_H__
#define __HUMAN_H__
#include<iostream>
using namespace std;
class Human {
public:int m_Age;Human():m_Age(0) {}virtual void eat() { cout << "Human要吃饭!" << endl; }
};
#endif __HUMAN_H__

Man.h

#ifndef __MAN_H__
#define __MAN_H__
#include<iostream>
#include"Human.h"
using namespace std;
class Man :public Human{
public:Man()  {}virtual void eat() { cout << "Man要吃饭!" << endl; }void manfunc() { cout << "调用了manfunc()!" << endl; }
};
#endif __MAN_H__

main.cpp

#include <iostream>
#include"Human.h"
#include"Man.h"
using namespace std;
void test() {Human* pHuman = new Man;Man* pman = dynamic_cast<Man*>(pHuman);if (pman != nullptr) {//类型转换成功!cout << "pHuman实际上是指向一个Man类型的指针!" << endl;//在这里因为用dynamic_cast动态的把pHuman指针指向Man类的对象了,因此//在这里边去操作Man类的成员函数、成员变量就是非常安全的了!pman->manfunc(); }else {//类型转换失败!cout << "pHuman实际上并不是指向一个Man类型的指针!" << endl;//pHuman->manfunc();//do不了!因为dynamic_cast类型转换失败了!}
}
int main(){test();return 0;
}

2)用dynamic_cast引用类型:

Human* pHuman = new Man;
Human& q = *pHuman;
//用try-catch语句来接受转换失败时可能抛出的bad_cast的异常!
try {Man& manbm = dynamic_cast<Man&>(q);//if转换不成功,则流程直接进入到catch里边去。如果转换成功,则流程继续走下去!//走到这里,表示转换成功cout << "pHuman实际上是一个Man类型!" << endl;manbm.manfunc();
}
catch (std::bad_cast) {cout << "pHuman实际上不是一个Man类型!" << endl;
}

三、typeid运算符

typeid的作用:拿到对象类型信息,并返回一个 常量const对象的引用。这个常量对象 其实就是一个标准库中定义的type_info类的类型。(我们如果想输出这个常对象的引用,就可以调用该type_info类的成员函数.name()来do输出!)

格式:

typeid(内置/自定义类型);or typeid(表达式)

请看以下代码:(直接通过代码来学习)

当在Human类和Man类中不声明任何的虚函数时

Human.h

#ifndef __HUMAN_H__
#define __HUMAN_H__
#include<iostream>
using namespace std;
class Human {
public:int m_Age;Human():m_Age(0) {}void eat() { cout << "Human要吃饭!" << endl; }
};
#endif __HUMAN_H__

Man.h

#ifndef __MAN_H__
#define __MAN_H__
#include<iostream>
#include"Human.h"
using namespace std;
class Man :public Human{
public:Man()  {}void eat() { cout << "Man要吃饭!" << endl; }void manfunc() { cout << "调用了manfunc()!" << endl; }
};
#endif __MAN_H__

main.cpp

#include <iostream>
#include"Human.h"
#include"Man.h"
#include"Woman.h"
using namespace std;
void test() {Human* pHuman = new Man;cout << typeid(*pHuman).name() << endl;delete pHuman;
}
int main(){test();return 0;
}

运行结果:

当在Human类中声明一个虚函数时(只需要在Human.h中将eat函数声明为virtual)

Human.h

#ifndef __HUMAN_H__
#define __HUMAN_H__
#include<iostream>
using namespace std;
class Human {
public:int m_Age;Human():m_Age(0) {}virtual void eat() { cout << "Human要吃饭!" << endl; }
};
#endif __HUMAN_H__

运行结果:

通过上述学习typeid的使用的代码,这也侧面地说明了如果你在写多态代码时,如果写了virtual虚函数时,就可以让基类指针动态绑定到子类的对象上;而如果你没写virtual虚函数的话,那么你就算do了多态(也即让基类指针指向子类对象),你也没办法让基类指针动态绑定到子类的对象上,也即该基类指针实际上还只是指向基类的对象而已。上面的运行结果很好地说明了这一点。

typeid可以用于查看内置的数据类型的类型:(此时typeid就是简单地把定义该变量/对象时的数据类型返回回来而已)

int arr[10]{ 5,1 };
int b = 120;
cout << typeid(arr).name() << endl;//int [10]
cout << typeid(b).name() << endl;//int
cout << typeid(19.68).name() << endl;//double
cout << typeid(pair<int,int>(19, 68)).name() << endl;//std::pair<int,int>
cout << typeid("MyNameIs").name() << endl;//const char [9] or char const [9]

但是,typeid多用于比较两个指针是否都指向同一种类型的对象

case①若两个指针定义的类型相同,则不管他们new的是啥,其指针的typeid都相同

//还是以上述写的类作为代码的例子:
Human* phuman = new Man;
Human* phuman2 = new Human;
if (typeid(phuman).name() == typeid(phuman2).name()) {cout<<"phuman和phuman2是同一种类型[看指针定义的类型是啥"<<"那么typeid(pointer_Name)就是啥指针类型]"<<endl;
}

运行结果:

case②若两个指针所指向的对象的类型相同,其*指针(解引用)的typeid都相同

Human* phuman = new Man;
Human* phuman2 = new Man;
Man* pman3 = new Man;
if (typeid(*phuman).name() == typeid(*phuman2).name()) {cout << "*phuman和*phuman2所指向的对象是同一种类型[看指针所指向的实际对象的类型是啥"<< "那么typeid(pointer_Name)就是啥指针类型]" << endl;
}
if (typeid(*pman3).name() == typeid(*phuman2).name()) {cout << "*pman3和*phuman2所指向的对象是同一种类型[看指针所指向的实际对象的类型是啥"<< "那么typeid(*pointer_Name)就是啥对象类型]" << endl;
}

所以,从上面的代码我们()可以看出来,比较两个指针所指向的对象的类型是否一样时,一定要这样写:typeid(*指针1名).name() == typeid(*指针2名).name()

否则的话如果你把*星号拉下了的话,这只是比较一下两个指针定义时的类型而已,这样是不能达到我们的原始目的的。

四、type_info类:

在我们使用typeid时,其会返回一个常量对象的引用,而这个常量对象引用就是一个标准库中定义好了的类的类型,而这个类就是type_info类!

so下面就介绍一下该类的一些常用的成员变量/方法!

         1).name():返回typeid(里面的类型)的类型名!

请看以下代码:

int a = 1, arr[10]{ 1,2, };
float f = 1.1;
double b = 2.288;//常量 对象的引用
const type_info& tpi1 = typeid(a);
cout << tpi1.name() << endl;//int
cout << typeid(a).name() << endl;//int
cout << "-------------------------------" << endl;
const type_info& tpi2 = typeid(arr);
cout << tpi2.name() << endl;//int [10]
cout << typeid(arr).name() << endl;//int [10]
cout << "-------------------------------" << endl;
const type_info& tpi3 = typeid(f);
cout << tpi3.name() << endl;//float
cout << typeid(f).name() << endl;//float
cout << "-------------------------------" << endl;
const type_info& tpi4 = typeid(b);
cout << tpi4.name() << endl;//double
cout << typeid(b).name() << endl;//double

运行结果:

这里继续再次重复上述我强调过的重点:如果说在写多态的代码时,你的基类不加些任何的虚函数,那么你的就算用基类指针指向子类的对象,实际上这个基类的指针也还是指向的事基类的对象的!一旦你在基类中写了至少一个虚函数,那么此时你的基类指针实际上就回动态地绑定到子类的对象中,也即此时你的基类指针会指向子类对象了!

当Human基类中不含有任何虚函数时,运行下面这段代码:

Human* phuman1 = new Human;
Human* phuman2 = new Man;
const type_info& tpiHuman1 = typeid(*phuman1);
cout << tpiHuman1.name() << endl;const type_info& tpiHuman2 = typeid(*phuman2);
cout << tpiHuman2.name() << endl;

运行结果:

当Human基类中含有至少一个虚函数时,运行刚才那段代码:

运行结果:

         2).operator==()等号以及.operator!=()不等号。(这是标准库中给type_info对象重载的运算符函数)

请看以下代码:

int a1 = 1, a2 = 1;
double a3 = 2.1;
const type_info& tpia1 = typeid(a1);
const type_info& tpia2 = typeid(a2);
const type_info& tpia3 = typeid(a3);
if (tpia1 == tpia2)cout << "tpia1 == tpia2" << endl;
if (tpia1.operator==(tpia2))cout << "tpia1 == tpia2" << endl;
cout<<"--------------------------" << endl;
if (tpia1 != tpia3)cout << "tpia1 != tpia3" << endl;
if (tpia1.operator!=(tpia3)) cout << "tpia1 != tpia3" << endl;

运行结果:

         3).operator==()等号以及.operator!=()不等号。(这是标准库中给type_info对象重载的运算符函数)

五、RTTI与虚函数表(virtual-Table):

在C++中,如果类中有虚函数,那么编译器就会为该类产生一个虚函数表。而虚函数表中有很多项,每一项都是一个指针。每个指针指向的是这个类中的各个虚函数的入口地址。虚函数表的项中,第一个表项很特殊,它指向的不是虚函数的入口地址,它指向的实际上是咱们这个类所关联的type_info对象。现在我只是简要地介绍一下虚函数表的概念而已,具体学习的话还是要等学习到《深度探索对象模型》时我再深入地学习并总结!

        好,那么以上就是这一3.10小节我所回顾的内容的学习笔记,希望你能读懂并且消化完,也希望自己能牢记这些小小的细节知识点,加油吧,我们都在coding的路上~

Th3.10:RTTI、dynamic_cast、typeid简介相关推荐

  1. RTTI: dynamic_cast typeid

    dynamic_cast:将基类类型的指针向派生类指针安全转换.多用于下行转换.上行转换时,和static_cast是一样的.C++类型转换看这里.而const_cast用来修改类型的const或vo ...

  2. C++ TypeId简介与使用

    简介 TypeId 返回一个变量或数据类型的"类型". 使用场景 用法如下: cout<<typeid(int).name()<<endl; int a;c ...

  3. C++中dynamic_cast的简介

    概述 这里针对于dynamic_cast简单记录下其使用用中应该改注意的事项. 详情 功能 可以用于具有继承关系的类指针或引用之间的向上转换或向下转换.同时还可以用于交叉转换.即派生类的多个基类指针或 ...

  4. TOP 10 开源的推荐系统简介

    本文转自http://www.oschina.net/news/51297/top-10-open-source-recommendation-systems,所有权力归原作者所有. 最 近这两年推荐 ...

  5. 爬虫入门【10】Pyspider框架简介及安装说明

    Pyspider是python中的一个很流行的爬虫框架系统,它具有的特点如下: 1.可以在Python环境下写脚本 2.具有WebUI,脚本编辑器,并且有项目管理和任务监视器以及结果查看. 3.支持多 ...

  6. C++ Internals: VC RTTI - dynamic_cast (2)

    返回目录 下面进入正题,让我们见识一下 dynamic_cast到底是如何实现的.首先,在你调用 dynamic_cast之前,编译器会帮你进行语法检查.如果指针的静态类型和目标类型相同,那么就什么事 ...

  7. 【ASM9260T】【LINUX-3.10】asm9260t芯片简介

    紫芯官网如下: http://www.alphascale.com/index.asp?yfa.html asm9260t, 封装LQFP 176-14x14(21x21) 240MHz ARM926 ...

  8. 深入浅出数据结构C语言版(10)——树的简介

    到目前为止,我们一直在谈论的数据结构都是"线性结构",不论是普通链表.栈还是队列,其中的每个元素(除了第一个和最后一个)都只有一个前驱(排在前面的元素)和一个后继(排在后面的元素) ...

  9. 2.10 WXS ---uni-app框架简介【uni-app从入门到精通在线教程(黄菊华-跨平台开发系列教程)】

    WXS是微信小程序的一套脚本语言,规范详见. 它的特点是运行在渲染层.当需要避免逻辑层和渲染层交互通信折损时,可采用wxs. uni-app可以将wxs代码编译到微信小程序.QQ小程序.APP.H5上 ...

最新文章

  1. 微软开源 Malmo AI 平台,构建复杂机器人任务
  2. 初识Mysql(part10)--我需要知道的5种聚集函数
  3. root目录空间不够的问题
  4. 【遥感物候】Hants NDVI时间序列谐波分析法数据重构,植被生长季曲线效果可佳(附Hants软件下载)
  5. NLP应该如何学、如何教?斯坦福大学大牛Dan Jurafsky教授专访
  6. java折叠自行车x1-auto,java入门第三季 7-1 简易扑克练习
  7. vs2005配置OpenCv2.3.1
  8. iOS当期时间和日期选择器
  9. matlab微带带通滤波器,小型化宽阻带微带带通滤波器的设计方案
  10. 时钟转盘html源代码
  11. it人才计算机知识题,Excel试题_电脑基础知识_IT/计算机_专业资料
  12. 济南服务器管理系统方案,物品管理系统和物品管理服务器技术方案
  13. 男人必备!泡妞全攻略
  14. LWN:Linux audio plugin APIs综述!
  15. HTML 语法教学之连结标签
  16. 10年测试,告诉你常见的软件测试类型有哪些?
  17. python 爬虫 第一周:学会爬取网页信息
  18. CMMI认证是什么?为什么这些IT类企业都在申请?
  19. Java概述-Java技术体系标准:SE、EE、ME
  20. 虚拟机安装图形化界面

热门文章

  1. 7-3 抢红包 (25 分)
  2. 吉布斯采样和梅特罗波利斯-黑斯廷斯算法
  3. 人脸视频跟踪与检索系统
  4. 使用ssh tunnel + sock5 穿越防火墙
  5. IKAnalyzer
  6. 关于php的搞笑段子,搞笑段子
  7. gradle的几个实用技巧让你爽歪歪
  8. Dart语法篇之类型系统与泛型(七)
  9. FFmpeg音视频解码流程
  10. 【Visual Studio 2022】VS2022安装教程