【C++】类型转换 ④ ( 子类 和 父类 之间的类型转换 - 动态类型转换 dynamic_cast )

news2024/11/27 6:37:33

文章目录

  • 一、子类 和 父类 之间的类型转换 - 动态类型转换 dynamic_cast
    • 1、构造父类和子类
    • 2、子类 和 父类 之间的类型转换 - 隐式类型转换
    • 3、子类 和 父类 之间的类型转换 - 静态类型转换 static_cast
    • 4、子类 和 父类 之间的类型转换 - 重新解释类型转换 reinterpret_cast
    • 5、子类 和 父类 之间的类型转换 - 动态类型转换 dynamic_cast
  • 二、完整代码示例
    • 1、代码示例
    • 2、执行结果


在之前写过一篇 C++ 类型转换的博客 【C++ 语言】类型转换 ( 转换操作符 | const_cast | static_cast | dynamic_cast | reinterpret_cast | 字符串转换 ) , 简单介绍了 C++ 类型转换 ;

在 博客 【C++】类型转换 ① ( C 中的类型转换 | C++ 类型转换操作符 | const_cast | static_cast | dynamic_cast | reinterpret_cast ) 将 C 语言 和 C++ 中的类型转换进行了对比 ;

在 博客 【C++】类型转换 ② ( C++ 静态类型转换 static_cast | C 语言隐式转换弊端 | 代码示例 ) 中 , 主要分析了 静态类型转换 static_cast , 可以解决 C 语言隐式转换的弊端 ;

在博客 【C++】类型转换 ③ ( 重新解释类型转换 reinterpret_cast | 指针类型数据转换 ) 分析了 指针数据类型的转换 , 在 C 语言环境下 , 可以使用显示强制类型转换 , 在 C++ 环境中只能使用 重新解释类型转换 reinterpret_cast ;


本篇博客中 , 分析 C++ 环境下 使用 各种方式 进行 父类 和 子类 类型之间的转换 , 推荐使用 动态类型转换 dynamic_cast ;





一、子类 和 父类 之间的类型转换 - 动态类型转换 dynamic_cast



C++ 面向对象 应用场景中 , 涉及到 父类 和 子类 之间的转换 ;

很明显 C 语言的 强制类型转换 , 不管是 隐式 还是 显示 转换 , 都无法转换 C++ 对象的类型 ;

动态类型转换 dynamic_cast 一般用于 父类 ( 对象 / 指针 / 引用 ) 和 子类 ( 对象 / 指针 / 引用 ) 之间的转换 , 是 C++ 语言特有的 , C 语言中没有该转换类型 ;


1、构造父类和子类


编写一个 父类 , 其中定义一个纯虚函数 ;

再编写两个 子类 , 重写 父类的 纯虚函数 , 每个子类再 各自定义一个 特有的函数 ;

// 父类
class Father
{
public:
	virtual void say() = 0;
};

// 子类
class Son : public Father
{
public:
	void say()
	{
		cout << "Son" << endl;
	}

	// Son 子类的特有函数
	void son_say()
	{
		cout << "son_say" << endl;
	}
};

// 子类2
class Son2 : public Father
{
public:
	void say()
	{
		cout << "Son2" << endl;
	}

	// Son2 子类的特有函数
	void son2_say()
	{
		cout << "son2_say" << endl;
	}
};

2、子类 和 父类 之间的类型转换 - 隐式类型转换


先创建 子类对象 ,

将子类对象的 地址赋值给 父类指针 , 其中包含了 隐式转换 ;

在下面的代码中 , 使用取地址符获取 Son 类型 子类对象的地址 , 指针类型是 Son* 类型 , 将该类型值 赋值给 Father* 指针 , 其中进行了 隐式类型转换 ;

	Son son;

	// 创建父类指针 , 直接让父类指针指向子类对象
	// 不会报错 , 但是这么做有一定的风险
	Father* pFather = NULL;
	// 隐式类型转换
	pFather = &son;

此外 , 函数接收 父类指针形参 作为参数 , 如果调用该函数 , 传入子类指针 , 此时涉及到将 子类指针 Son* 隐式转为 父类指针 Father* ;

// 函数接收 父类对象 作为参数, 可传入子类对象
void objSay(Father* obj)
{
}

// 调用函数, 传入子类对象指针
objSay(&son);

3、子类 和 父类 之间的类型转换 - 静态类型转换 static_cast


静态类型转换 static_cast , 可以在 C++ 编译器 编译时 对类型转换 进行检查 ;

如果 转换的类型不匹配 , 就会在编译时报错 , 避免出现更大的错误 ;


下面的代码中 , 使用取地址运算符 &son 获取 的 Son* 类型的 指针 , 将其使用 静态类型转换 static_cast 转为 Father* 类型的指针 ,

在 C++ 编译器编译阶段 , 会对类型进行检测 , 如果通过检测 , 则可以编译成功 , 如果类型错误 , 则会出现编译时报错的情况 ;

	Son son;

	// 创建父类指针 , 直接让父类指针指向子类对象
	// 不会报错 , 但是这么做有一定的风险
	Father* pFather = NULL;

	// 静态类型转换 static_cast,  可以在编译时 对类型转换 进行检查
	pFather = static_cast<Father*>(&son);

下面的代码就是 执行静态类型转换 检查出错的情况 ,

Son 和 Son2 都是 Father 的子类 , 二者之间不能相互转化 , 只能是 父类 和 子类 之间进行相互转换 ;

类型转换错误报错 : error C2440: “static_cast”: 无法从“Son *”转换为“Son2 *” ;

	Son son;
	Son2 son2;

	// 创建父类指针 , 直接让父类指针指向子类对象
	// 不会报错 , 但是这么做有一定的风险
	Father* pFather = NULL;

	// 静态类型转换 static_cast,  可以在编译时 对类型转换 进行检查
	pFather = static_cast<Father*>(&son);

	// 类型转换错误报错 : error C2440: “static_cast”: 无法从“Son *”转换为“Son2 *”
	// message : 与指向的类型无关;
	//		强制转换要求 reinterpret_cast、C 样式强制转换或函数样式强制转换
	Son2* pSon2 = static_cast<Son2*>(&son);

执行后 , 出现如下报错 :

已启动生成…
1>------ 已启动生成: 项目: HelloWorld, 配置: Debug Win32 ------
1>Test.cpp
1>D:\002_Project\006_Visual_Studio\HelloWorld\HelloWorld\Test.cpp(92,39): error C2440:static_cast: 无法从“Son *”转换为“Son2 *1>D:\002_Project\006_Visual_Studio\HelloWorld\HelloWorld\Test.cpp(92,16): message : 与指向的类型无关;强制转换要求 reinterpret_cast、C 样式强制转换或函数样式强制转换
1>已完成生成项目“HelloWorld.vcxproj”的操作 - 失败。
========== 生成: 成功 0 个,失败 1 个,最新 0 个,跳过 0==========

完整代码示例 :

#include "iostream"
using namespace std;

// 父类
class Father
{
public:
	virtual void say() = 0;
};

// 子类
class Son : public Father
{
public:
	void say()
	{
		cout << "Son" << endl;
	}

	// Son 子类的特有函数
	void son_say()
	{
		cout << "son_say" << endl;
	}
};

// 子类2
class Son2 : public Father
{
public:
	void say()
	{
		cout << "Son2" << endl;
	}

	// Son2 子类的特有函数
	void son2_say()
	{
		cout << "son2_say" << endl;
	}
};

// 函数接收 父类对象 作为参数, 可传入子类对象
void objSay(Father* obj)
{
	// 调用 父类 纯虚函数 可发生多态调用
	// 传入不同的子类 调用的是不同的函数
	obj->say();
}

int main() {

	Son son;
	Son2 son2;

	// 创建父类指针 , 直接让父类指针指向子类对象
	// 不会报错 , 但是这么做有一定的风险
	Father* pFather = NULL;

	// 静态类型转换 static_cast,  可以在编译时 对类型转换 进行检查
	pFather = static_cast<Father*>(&son);

	// 类型转换错误报错 : error C2440: “static_cast”: 无法从“Son *”转换为“Son2 *”
	// message : 与指向的类型无关;
	//		强制转换要求 reinterpret_cast、C 样式强制转换或函数样式强制转换
	Son2* pSon2 = static_cast<Son2*>(&son);

	// 控制台暂停 , 按任意键继续向后执行
	system("pause");

	return 0;
};

执行结果 :

已启动生成…
1>------ 已启动生成: 项目: HelloWorld, 配置: Debug Win32 ------
1>Test.cpp
1>D:\002_Project\006_Visual_Studio\HelloWorld\HelloWorld\Test.cpp(92,39): error C2440:static_cast: 无法从“Son *”转换为“Son2 *1>D:\002_Project\006_Visual_Studio\HelloWorld\HelloWorld\Test.cpp(92,16): message : 与指向的类型无关;强制转换要求 reinterpret_cast、C 样式强制转换或函数样式强制转换
1>已完成生成项目“HelloWorld.vcxproj”的操作 - 失败。
========== 生成: 成功 0 个,失败 1 个,最新 0 个,跳过 0==========

在这里插入图片描述


4、子类 和 父类 之间的类型转换 - 重新解释类型转换 reinterpret_cast


C++ 中 父类 和 子类 之间类型转换 , 还可以使用 重新解释类型转换 reinterpret_cast ;

下面的代码中 , 将 Son* 指针类型 重新解释为 Father* 指针类型 ;

	// C++ 强制类型转换 , 重新解释类型转换 reinterpret_cast
	pFather = reinterpret_cast<Father*>(&son);
	pFather->say();

5、子类 和 父类 之间的类型转换 - 动态类型转换 dynamic_cast


动态类型转换 dynamic_cast , 一般用于 子类 和 父类 之间的类型转换 ,

  • 运行时 , 如果类型转换成功 , 则进行转换 ;
  • 运行时 , 如果类型转换失败 , 则返回转换结果 NULL ;

借助上述特性 , 动态类型转换 dynamic_cast 可用于在 运行时 识别对象类型 ;

将 对象 强转为 指定类型对象, 如果失败了, 转换结果为 NULL , 说明被转换的对象 不是 指定类型的对象 ;


下面代码的作用是 : 将Father* obj 父类对象 强转为 Son* 子类对象 ,

  • 如果转换成功, 说明 obj 对象就是 Son 子类对象 , 则执行 Son 子类对象特有的函数 ;
  • 如果转换失败, 说明不是 Son 子类对象, 转换结果是 NULL , 也就是 0 , 后续不再处理 ;
	// 将Father* obj 父类对象 强转为 Son* 子类对象
	// 如果转换成功, 说明 obj 对象就是 Son 子类对象
	// 如果转换失败, 说明不是 Son 子类对象, 转换结果是 NULL , 也就是 0
	Son* son = dynamic_cast<Son*>(obj);
	if (son != NULL)
	{
		// 转换成功
		// 执行 Son 特有工作
		son->son_say();
	}

完整代码 , 参考下面章节的 完整代码示例 ;





二、完整代码示例




1、代码示例


#include "iostream"
using namespace std;

// 父类
class Father
{
public:
	virtual void say() = 0;
};

// 子类
class Son : public Father
{
public:
	void say()
	{
		cout << "Son" << endl;
	}

	// Son 子类的特有函数
	void son_say()
	{
		cout << "son_say" << endl;
	}
};

// 子类2
class Son2 : public Father
{
public:
	void say()
	{
		cout << "Son2" << endl;
	}

	// Son2 子类的特有函数
	void son2_say()
	{
		cout << "son2_say" << endl;
	}
};

// 函数接收 父类对象 作为参数, 可传入子类对象
void objSay(Father* obj)
{
	// 调用 父类 纯虚函数 可发生多态调用
	// 传入不同的子类 调用的是不同的函数
	obj->say();

	// 动态类型转换 dynamic_cast
	// 可用于在 运行时 识别对象类型
	// 将 对象 强转为 指定类型对象, 如果失败了, 转换结果为 NULL

	// 将Father* obj 父类对象 强转为 Son* 子类对象
	// 如果转换成功, 说明 obj 对象就是 Son 子类对象
	// 如果转换失败, 说明不是 Son 子类对象, 转换结果是 NULL , 也就是 0
	Son* son = dynamic_cast<Son*>(obj);
	if (son != NULL)
	{
		// 转换成功
		// 执行 Son 特有工作
		son->son_say();
	}

	// 将Father* obj 父类对象 强转为 Son2* 子类对象
	// 如果转换成功, 说明 obj 对象就是 Son2 子类对象
	// 如果转换失败, 说明不是 Son2 子类对象, 转换结果是 NULL , 也就是 0
	Son2* son2 = dynamic_cast<Son2*>(obj);
	if (son2 != NULL)
	{
		// 转换成功
		// 执行 Son2 特有工作
		son2->son2_say();
	}
}

int main() {

	Son son;
	Son2 son2;

	// 创建父类指针 , 直接让父类指针指向子类对象
	// 不会报错 , 但是这么做有一定的风险
	Father* pFather = NULL;

	// 静态类型转换 static_cast,  可以在编译时 对类型转换 进行检查
	pFather = static_cast<Father*>(&son);

	// 类型转换错误报错 : error C2440: “static_cast”: 无法从“Son *”转换为“Son2 *”
	// message : 与指向的类型无关;
	//		强制转换要求 reinterpret_cast、C 样式强制转换或函数样式强制转换
	//Son2* pSon2 = static_cast<Son2*>(&son);

	// C++ 强制类型转换 , 重新解释类型转换 reinterpret_cast
	pFather = reinterpret_cast<Father*>(&son);
	pFather->say();

	// 动态类型转换示例
	objSay(&son);
	objSay(&son2);


	// 控制台暂停 , 按任意键继续向后执行
	system("pause");

	return 0;
};

2、执行结果


执行结果 :

Son
Son
son_say
Son2
son2_say
Press any key to continue . . .

在这里插入图片描述

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

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

相关文章

时间序列预测实战(二十)自研注意力机制Attention-LSTM进行多元预测(结果可视化,自研结构)

一、本文介绍 本文给大家带来的是我利用我自研的结构进行Attention-LSTM进行时间序列预测&#xff0c;该结构是我专门为新手和刚入门的读者设计&#xff0c;包括结果可视化、支持单元预测、多元预测、模型拟合效果检测、预测未知数据、以及滚动长期预测&#xff0c;大家不仅可…

2023-2024-1-高级语言程序设计-字符数组

7-1 凯撒密码 为了防止信息被别人轻易窃取&#xff0c;需要把电码明文通过加密方式变换成为密文。输入一个以回车符为结束标志的字符串&#xff08;少于80个字符&#xff09;&#xff0c;再输入一个整数offset&#xff0c;用凯撒密码将其加密后输出。恺撒密码是一种简单的替换…

DIO算法

歌声和语音声带振动周期的快速可靠F0估计方法 原文题目&#xff1a; Fast and reliable F0 estimation method based on the period extraction of vocal fold vibration of singing voice and speech 发表在&#xff1a; AES 35TH INTERNATIONAL CONFERENCE, London, UK, 200…

【RLChina2023】CCF 苏州 记录

目录 RLChina介绍主旨报告专题报告智能体学习理论(专题一)智能体决策与规划(专题二)智能体框架、体系结构与训练系统(专题六)基于大语言模型的具身智能体与机器人研究 (专题八)教学报告——强化学习入门特别论坛——智能体和多智能体艺术的探索会议照片RLChina介绍 RLC…

什么是迁移学习

1 迁移学习概述 迁移学习&#xff08;Transfer Learning&#xff09;是机器学习中的一种方法&#xff0c;它允许模型将从一个任务中学到的知识应用到另一个相关的任务中。这种方法在数据稀缺的情况下尤为有用&#xff0c;因为它减少了对大量标记数据的需求。迁移学习已成为深度…

Failed to load resource: the server responded with a status of 404 ()

路径问题&#xff1a; 路径省略前面的http://localhost:8080/ 就行了。

OSG粒子系统与阴影 - ​​​​​​​阴影shadow(7)

OSG阴影 在虚拟现实仿真中&#xff0c;为了真实地模拟自然效果&#xff0c;阴影效果是不可缺少的&#xff0c;它对一个场景的真实性是非常重要的。在游戏或仿真中&#xff0c;一个高效的阴影往往能够提供非常强悍的视觉真实感。 osgShadow库 在OSG中专门定义了一个名字空间osg…

ShowWeb-浏览器插件:可视化元素路径查看器

ShowWeb&#x1f47b;&#xff1a;可视化元素路径查看器适配【谷歌】【Edge】 每次写前端最烦的就是一层一层找元素&#xff0c;又臭又长。所以我开发了一个小插件来缓解这个问题&#xff0c;这个插件可以输出整个路径&#xff0c;并把最后元素的内容输出方便查看&#xff0c;…

docker基础快速入门:基础命令、网络、docker compose工具

docker基础命令快速入门 目录 docker基本命令docker 网络docker compose Docker介绍 Docker是一个虚拟环境容器&#xff0c;可以将你的开发环境、代码、配置文件等一并打包到这个容器中&#xff0c;并发布和应用到任意平台中。 Docker的三个概念 镜像 Docker镜像是一个特…

Java飞翔的小鸟

一、项目分析 创建一个窗口和画板&#xff0c;把画板放到窗口上&#xff0c;在画板上绘画图片 &#xff08;2&#xff09;让小鸟在画面中动起来&#xff0c;可以上下飞 &#xff08;3&#xff09;让地面和管道动起来 &#xff08;4&#xff09;碰撞检测 &#xff08;5&#xf…

2023信息技术应用创新论坛|云轴科技ZStack分享云原生超融合在智慧交通的应用

11月25日&#xff0c;2023信息技术应用创新论坛在常州开幕。江苏省工业和信息化厅副厅长池宇、中国电子工业标准化技术协会理事长胡燕、常州市常务副市长李林等领导出席论坛并致辞。中国工程院院士郑纬民出席并作主题报告。来自产学研用金等各界的千余名代表参加本次论坛。 在“…

UE5 - 虚幻引擎各模块流程图

来自虚幻官方的一些资料&#xff0c;分享一下&#xff1b; 一些模块的流程图&#xff0c;比如动画模块&#xff1a; 或角色相关流程&#xff1a; 由于图片比较大&#xff0c;上传到了网络&#xff0c;可自取&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1BQ2KiuP08c…

MATLAB的rvctools工具箱熟悉运动学【机械臂机器人示例】

1、rvctools下载安装 rvctools下载地址&#xff1a;rvctools下载 截图如下&#xff0c;点击红色箭头指示的“Download Shared Folder” 即可下载 下载之后进行解压&#xff0c;解压到D:\MATLAB\toolbox这个工具箱目录&#xff0c;这个安装路径根据自己的情况来选择&#xff0c…

【华为OD】统一考试C卷真题 100%通过: 传递悄悄话 二叉树遍历 C/C++实现

目录 题目描述&#xff1a; 示例1 解题思路&#xff1a; 代码实现&#xff1a; 题目描述&#xff1a; 给定一个二叉树&#xff0c;每个节点上站着一个人&#xff0c;节点数字表示父节点到该节点传递悄悄话需要花费的时间。 初始时&#xff0c;根节点所在位置的人有一个悄悄…

中通快递查询入口,根据物流更新量筛选出需要的单号记录

批量中通快递单号的物流信息&#xff0c;根据物流更新量将需要的单号记录筛选出来。 所需工具&#xff1a; 一个【快递批量查询高手】软件 中通快递单号若干 操作步骤&#xff1a; 步骤1&#xff1a;运行【快递批量查询高手】软件&#xff0c;并登录 步骤2&#xff1a;点击主…

代码随想录算法训练营第六十天|84. 柱状图中最大的矩形

84. 柱状图中最大的矩形 给定 n 个非负整数&#xff0c;用来表示柱状图中各个柱子的高度。每个柱子彼此相邻&#xff0c;且宽度为 1 。 求在该柱状图中&#xff0c;能够勾勒出来的矩形的最大面积。 示例 1: 输入&#xff1a;heights [2,1,5,6,2,3] 输出&#xff1a;10 解释…

PT里如何针对某个模块设置false path

我正在「拾陆楼」和朋友们讨论有趣的话题&#xff0c;你⼀起来吧&#xff1f; 拾陆楼知识星球入口 如题&#xff0c;这个问题实际上讲的是get_cells的用法&#xff0c;我们要抓取某个模块内的全部cell&#xff0c;在ICC2里可以get_flat_cells xx/xx/module_name*&#xff0c;但…

Linux 基本语句_12_信号

用途&#xff1a; 信号可以直接进行用户进程与内核进程之间的交互 特性&#xff1a; 对于一个进程&#xff0c;其可以注册或者不注册信号&#xff0c;不注册的信号&#xff0c;进程接受后会按默认功能处理&#xff0c;对于注册后的信号&#xff0c;进程会按自定义处理 自定义…

Mysql之局域网内不同ip互登陆mysql

1 navicat修改mysql表中user> host改为% 2 重新加载mysql服务 3登陆mysql -h 192.168.x.xxx&#xff08;计算机ip&#xff09; -P 3306 -uroot -p123456&#xff08;密码&#xff09;

AI大模型相关产品的数据飞轮如何建设?

1、背景 数据飞轮&#xff0c;是今年大模型带火的一个典型词汇&#xff0c;通过客户在应用程序中输入的提示词这样的数据反馈&#xff0c;使大模型快速迭代。简单说&#xff1a;好的产品 -> 更多的用户数据 -> 更好的模型质量 -> 更好的产品就进入了一个正向循环。一…