【深入理解C++】RTTI、dynamic_cast、typeid()

news2024/12/28 20:23:59

文章目录

  • 1.RTTI
  • 2.dynamic_cast运算符
  • 3.typeid运算符
  • 4.RTTI与虚函数表

1.RTTI

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

RTTI 提供了两个非常有用的运算符:

  • dynamic_cast 运算符:将基类类型的指针或引用安全地转换为其派生类类型的指针或引用。

  • typeid 运算符:返回指针或引用所指对象的实际类型。

要想让 RTTI 的两个运算符能够正常工作,那么基类中必须至少要有一个虚函数,不然这两个运算符工作的结果可能跟预期结果不一样,因为只有虚函数的存在,这两个运算符才会使用指针或引用所绑定的对象的动态类型。

2.dynamic_cast运算符

如果 dynamic_cast 运算符能够转换成功,说明这个指针实际上就是要转换到的那个类型,也就是说 dynamic_cast 运算符能够做运行时安全检查。

#include <iostream>
using namespace std;

class Animal
{
public:
	virtual void run() { cout << "Animal::run()" << endl; }
};

class Dog : public Animal
{
public:
	void run() { cout << "Dog::run()" << endl; }
	void speak() { cout << "Dog::speak()" << endl; }
};

int main()
{
	Animal* pa = new Dog();

	//pa->speak(); // 报错

	Dog* pd = dynamic_cast<Dog*>(pa);

	if (pd != nullptr)
	{
		cout << "转换成功" << endl;
		pd->speak(); // 正确
	}
	else
	{
		cout << "转换失败" << endl;
	}

	return 0;
}

对于引用,如果用 dynamic_cast 转换失败,则系统会抛出一个 std::bad_cast 异常,如下代码所示。

#include <iostream>
using namespace std;

class Animal
{
public:
	virtual void run() { cout << "Animal::run()" << endl; }
};

class Dog : public Animal
{
public:
	void run() { cout << "Dog::run()" << endl; }
	void speak() { cout << "Dog::speak()" << endl; }
};

int main()
{
	Animal* pa = new Dog();
	Animal& refa = *pa;

	//refa.speak(); // 报错

	try
	{
		Dog& refd = dynamic_cast<Dog&>(refa); // 如果转换不成功,则流程直接进入到catch里边去;如果转换成功,则流程继续往下走
		cout << "转换成功" << endl;
		refd.speak(); // 正确
	}
	catch (std::bad_cast)
	{
		cout << "转换失败" << endl;
	}

	return 0;
}

3.typeid运算符

typeid() 的主要作用就是让用户知道当前的变量是什么类型的。

typeid() 返回的是一个 type_info 类型的常量对象的引用,即 const type_info&。type_info 类中的成员函数 name() 返回的是一个C语言风格的字符串。

#include <iostream>
using namespace std;

int main()
{
	int a = 180;
	cout << typeid(a).name() << endl;

	char b[] = "hello";
	cout << typeid(b).name() << endl;

	cout << typeid(19.6).name() << endl;

	cout << typeid("world").name() << endl;

	return 0;
}

输出结果如下:

在这里插入图片描述

#include <iostream>
using namespace std;

class Animal
{
public:
	virtual void run() { cout << "Animal::run()" << endl; }
};

class Dog : public Animal
{
public:
	void run() { cout << "Dog::run()" << endl; }
};

class Cat : public Animal
{
public:
	void run() { cout << "Cat::run()" << endl; }
};

int main()
{
	Animal* p1 = new Dog();

	cout << typeid(p1).name() << endl;

	cout << typeid(*p1).name() << endl;

	if (typeid(*p1) == typeid(Dog))
	{
		cout << "将Dog类型的对象动态绑定到p1指针上" << endl;
	}
	else
	{
		cout << "没有将Dog类型的对象动态绑定到p1指针上" << endl;
	}

	Animal* p2 = new Cat();

	cout << typeid(p2).name() << endl;

	cout << typeid(*p2).name() << endl;

	if (typeid(*p2) == typeid(Cat))
	{
		cout << "将Cat类型的对象动态绑定到p2指针上" << endl;
	}
	else
	{
		cout << "没有将Cat类型的对象动态绑定到p2指针上" << endl;
	}

	return 0;
}

输出结果如下:

在这里插入图片描述

如下代码所示,如果基类中不含有虚函数,则 typeid() 返回的是表达式的静态类型(即定义的类型),既然是定义的类型,那么编译器不需要对表达式求值也能知道表达式的静态类型。

#include <iostream>
using namespace std;

class Animal
{
public:
	void run() { cout << "Animal::run()" << endl; }
};

class Dog : public Animal
{
public:
	void run() { cout << "Dog::run()" << endl; }
};

class Cat : public Animal
{
public:
	void run() { cout << "Cat::run()" << endl; }
};

int main()
{
	Animal* p1 = new Dog();

	cout << typeid(p1).name() << endl;

	cout << typeid(*p1).name() << endl;

	if (typeid(*p1) == typeid(Dog))
	{
		cout << "将Dog类型的对象动态绑定到p1指针上" << endl;
	}
	else
	{
		cout << "没有将Dog类型的对象动态绑定到p1指针上" << endl;
	}

	Animal* p2 = new Cat();

	cout << typeid(p2).name() << endl;

	cout << typeid(*p2).name() << endl;

	if (typeid(*p2) == typeid(Cat))
	{
		cout << "将Cat类型的对象动态绑定到p2指针上" << endl;
	}
	else
	{
		cout << "没有将Cat类型的对象动态绑定到p2指针上" << endl;
	}

	return 0;
}

输出结果如下:

在这里插入图片描述

4.RTTI与虚函数表

#include <iostream>
using namespace std;

class Animal
{
public:
	virtual void run() { cout << "Animal::run()" << endl; }
};

class Dog : public Animal
{
public:
	void run() { cout << "Dog::run()" << endl; }
};

int main()
{
	Animal* pa = new Dog();

	const type_info& tf = typeid(*pa);

	cout << tf.name() << endl; // class Dog

	return 0;
}

在 C++ 中,如果类中含有虚函数,那么编译器就会对该类产生一个虚函数表。

在上面代码中,pa 指向一个 Dog 类型的对象,该对象里有一个指针,称为虚函数表指针,虚函数表指针指向的是该对象所在的类 Dog 的虚函数表。

虚函数表里有很多项,每一项都是一个指针,每个指针指向的是这个类中的各个虚函数的入口地址。

虚函数表中的第一个表项很特殊,它指向的不是虚函数的入口地址,它指向的实际上是这个类所关联的 type_info 对象。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/11721.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据库命名规范

1、mysql 规范 1.1 表名规范 模块_功能点 示例&#xff1a;alllive_log alllive_category。采用26个英文字母(区分大小写)和0-9的自然数(经常不需要)加上下划线_组成&#xff0c;命名简洁明确&#xff0c;多个单词用下划线_分隔,一个项目一个数据库。全部小写命名&#xff0c…

嵌入式分享合集103

一、EEPROM和Flash 存储器分为两大类&#xff1a;RAM和ROM&#xff0c;本文主要讨论ROM。ROM最初不能编程&#xff0c;出厂什么内容就永远什么内容&#xff0c;不灵活。 后来出现了PROM&#xff0c;可以自己写入一次&#xff0c;要是写错了&#xff0c;只能换一片&#xff0c;自…

React中的生命周期函数

生命周期的三个阶段&#xff1a; 1.创建时&#xff08;挂载阶段&#xff09; ①执行时机&#xff1a;组件创建时&#xff08;页面加载时&#xff09; ②执行顺序&#xff1a;constructor()->render()->componentDidMount&#xff08;&#xff09; import React, { Com…

nodejs+vue+elementui共享充电宝管理系统express

目 录 摘 要 I ABSTRACT II 目 录 II 第1章 绪论 1 1.1背景及意义 1 1.2 国内外研究概况 1 1.3 研究的内容 1 第2章 相关技术 2 前端技术&#xff1a;nodejsvueelementui 前端&#xff1a;HTML5,CSS3、JavaScript、VUE 功能介绍 管理员&#xff1a;…

H5基本开发2——(HTML文档基本结构)

现实生活中&#xff0c;任何一个文档都是具有一定的格式&#xff0c;不同的文档&#xff0c;基本格式不同&#xff0c;例如&#xff1a;请假条、调休单、剧本、年中总结、十九大报告、等等&#xff0c;而我们所编写等html文档也是具有一定的编写基本格式的 事实上W3C组织一直致…

[附源码]java毕业设计教学辅助系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

TwoModalBERT进行角色分类

你是否遇到过数据集中有多个文本特性的情况?例如&#xff0c;根据消息的上下文正确地对消息进行分类&#xff0c;即理解前面的消息。比如说我们有下面的数据集&#xff0c;需要对其进行分类。 当只考虑message时&#xff0c;你可以看到它的情绪是积极的&#xff0c;因为“incr…

关于电影的HTML网页设计—— 电影小黄人6页 HTML+CSS+JavaScript

HTML实例网页代码, 本实例适合于初学HTML的同学。该实例里面有设置了css的样式设置&#xff0c;有div的样式格局&#xff0c;这个实例比较全面&#xff0c;有助于同学的学习,本文将介绍如何通过从头开始设计个人网站并将其转换为代码的过程来实践设计。 精彩专栏推荐&#x1f4…

【Pytorch with fastai】第 10 章 :NLP 深入探讨 RNN

&#x1f50e;大家好&#xff0c;我是Sonhhxg_柒&#xff0c;希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流&#x1f50e; &#x1f4dd;个人主页&#xff0d;Sonhhxg_柒的博客_CSDN博客 &#x1f4c3; &#x1f381;欢迎各位→点赞…

【JavaScript高级程序设计】重点-第五章笔记:Date、RegExp、原始值包装类、单例内置对象

文章目录基本引用类型1.Date1.1 继承的方法1.2 日期格式化方法1.3 日期/时间组件方法2.RegExp正则表达式2.1 RegExp 实例属性2.2 RegExp 实例方法2.3 RegExp 构造函数属性3.原始值包装类型3.1 Boolean3.2 Number3.3 String3.3.1 JavaScript 字符3.3.2 normalize()方法3.3.3 字符…

AI 实战篇 |十分钟学会【动物识别】,快去寻找身边的小动物试试看吧【送书】

&#x1f3ac; 博客主页&#xff1a;https://xiaoy.blog.csdn.net &#x1f3a5; 本文由 呆呆敲代码的小Y 原创&#xff0c;首发于 CSDN&#x1f649; &#x1f384; 学习专栏推荐&#xff1a;Unity系统学习专栏 &#x1f332; 游戏制作专栏推荐&#xff1a;游戏制作 &…

计算机毕业设计node.js+vue在线日程管理系统

项目介绍 我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,在线日程管理系统被用户普遍使用,为方便用户能够可以随时进行在线管理自己的日程的数据信息,特开发了基于在线日程管理…

【Pytorch with fastai】第 11 章 :使用 fastai 的中级 API 进行数据处理

&#x1f50e;大家好&#xff0c;我是Sonhhxg_柒&#xff0c;希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流&#x1f50e; &#x1f4dd;个人主页&#xff0d;Sonhhxg_柒的博客_CSDN博客 &#x1f4c3; &#x1f381;欢迎各位→点赞…

第2关:子节点创建、列出、删除

子节点创建、列出 首先&#xff0c;需要启动服务器&#xff0c;并使用zkCli.sh连接服务器&#xff0c;进入客户端命令行界面&#xff08;如第一关所述&#xff09;。 创建子节点类似于创建新的节点&#xff0c;子节点也具有四种类型的节点。唯一的区别是&#xff1a;子节点的…

redis学习4-list

基本的数据类型&#xff0c;列表,redis命令是不区分大小写的 在redis中&#xff0c;我们可以把list玩成&#xff0c;线&#xff0c;队列&#xff0c;阻塞队列&#xff01; 所有的list命令都是用l开头的 [rootcentos7964 bin]# redis-cli -p 6379 127.0.0.1:6379> LPUSH li…

Oracle Primavera Unifier计划管理器(Planning Manager)

目录 一、前言 二、介绍 一、前言 在计划管理器中&#xff0c;Oracle Primavera Unifier 用户可以计划新项目/外壳和提案&#xff0c;并为已在 Unifier 中运行的项目/外壳创建预测。他们不能像在 Unifier 中管理真实项目/外壳那样管理计划的项目/外壳;但是&#xff0c;他们可…

Observer

一些比较方便的 DOM 监测的 API。 一个 Observer 实例具备的实例方法&#xff1a; observe。向监听的目标集合添加一个元素。unobserve。停止对一个元素的观察。disconnect。终止对所有目标元素的观察。… 一、IntersectionObserver 提供了一种异步检测目标元素与祖先元素或…

图解LeetCode——792. 匹配子序列的单词数(难度:中等)

一、题目 给定字符串 s 和字符串数组 words, 返回 words[i] 中是s的子序列的单词个数 。 字符串的 子序列 是从原始字符串中生成的新字符串&#xff0c;可以从中删去一些字符(可以是none)&#xff0c;而不改变其余字符的相对顺序。 例如&#xff0c; “ace” 是 “abcde” 的…

css3对页面打印设置的一些特殊属性,如@page,target-counter等

公司内部应业务需求&#xff0c;需要将html生成pdf并能打印&#xff0c;前后台都各有方式&#xff0c;这里综合比较选择用java去生成&#xff0c;避免了前端生成带来的诸多问题&#xff0c;后台用的框架是 iTextPdf 但是在做的同时发现用iText实现的pdf生成和公司的业务需要生成…

Spring学习第6篇: 基于注解使用IOC

大家家好&#xff0c;我是一名网络怪咖&#xff0c;北漂五年。相信大家和我一样&#xff0c;都有一个大厂梦&#xff0c;作为一名资深Java选手&#xff0c;深知Spring重要性&#xff0c;现在普遍都使用SpringBoot来开发&#xff0c;面试的时候SpringBoot原理也是经常会问到&…