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

文章目录

  • 一、子类 和 父类 之间的类型转换 - 动态类型转换 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_castpFather = 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 , 也就是 0Son* 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 , 也就是 0Son* son = dynamic_cast<Son*>(obj);if (son != NULL){// 转换成功// 执行 Son 特有工作son->son_say();}// 将Father* obj 父类对象 强转为 Son2* 子类对象// 如果转换成功, 说明 obj 对象就是 Son2 子类对象// 如果转换失败, 说明不是 Son2 子类对象, 转换结果是 NULL , 也就是 0Son2* 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_castpFather = 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.mzph.cn/news/174348.shtml

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

相关文章

【Java Spring】SpringBoot Bean详解

文章目录 1、Bean方法注解简介2、Bean注解重命名3、对象装配&#xff08;获取Bean对象&#xff09;3.1 对象装配之属性注入3.2 对象装配之Set 注入3.3 对象装配之构造方法注入 4、Resource VS Autowired5、Bean对象的作用域5.1 验证Bean对象的默认作用域5.2 Bean对象的六大作用…

论文解读:《数据增强:通过强化学习引导的条件生成进行文本数据扩充》

Title:<Data Boost: Text Data Augmentation Through Reinforcement Learning Guided Conditional Generation> 期刊&#xff1a;EMNLP &#xff08;顶级国际会议&#xff09; 作者 Ruibo Liu; Guangxuan Xu; Chenyan Jia; Weicheng Ma; Lili Wang; et al 出版日期 20…

基于Springboot的墙绘产品展示交易平台(有报告),Javaee项目,springboot项目。

演示视频&#xff1a; 基于Springboot的墙绘产品展示交易平台&#xff08;有报告&#xff09;&#xff0c;Javaee项目&#xff0c;springboot项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff0…

手势监听类GestureDetector Listener源码解析

手势监听类GestureDetector 前言一、GestureDetector是什么&#xff1f;二、Listener源码解析1.OnGestureListener2.OnDoubleTapListener3.OnContextClickListener4.SimpleOnGestureListener 总结 前言 在写自定义view的时候&#xff0c;涉及到了手势监听这块的知识&#xff0…

FLV 文件格式分析

前言 flv 是 flash video 的缩写&#xff0c;是 Adobe Flash payler 支持的一种流媒体播放格式。flv 是一种层级格式&#xff0c;除了一个 flv header 外&#xff0c;剩下全是由 一个个 tag 组成。tag 是由 tag 头和 tag 数据组成。tag 类型分为音频、视频、脚本&#xff0c;一…

交换机的VRRP主备配置例子

拓朴如下&#xff1a; 主要配置如下&#xff1a; [S1] vlan batch 10 20 # interface Vlanif10ip address 10.1.1.1 255.255.255.0vrrp vrid 1 virtual-ip 10.1.1.254vrrp vrid 1 priority 200vrrp vrid 1 preempt-mode timer delay 20 # interface Vlanif20ip address 13.1.1…

IDEA的安装与删除插件

不小心安装了一个英文转中文的插件&#xff0c;看不习惯&#xff0c;决定重新变回英文 先点击这个settings的安装 然后就看到这个下面这张图了 如果是安装就点install&#xff0c;不用了就和我一样把这个勾给去掉

MUI框架从新手入门【webapp开发教程】

文章目录 MUI -最接近原生APP体验的高性能前端框架APP开发3.25 开发记录miu框架介绍头部/搜索框&#xff1a;身体>轮播图轮播图设置数据自动跳转&#xff1a;九宫格图片九宫格图文列表底部选项卡按钮选择器手机模拟器 心得与总结&#xff1a;MUI框架在移动应用开发中的应用M…

openGauss学习笔记-133 openGauss 数据库运维-例行维护-日维护检查项

文章目录 openGauss学习笔记-133 openGauss 数据库运维-例行维护-日维护检查项133.1 检查openGauss状态133.2 检查锁信息133.3 统计事件数据133.4 对象检查133.5 SQL报告检查133.6 备份133.7 基本信息检查 openGauss学习笔记-133 openGauss 数据库运维-例行维护-日维护检查项 …

数据结构——利用堆进行对数组的排序

今天文章的内容是关于我们如何利用堆的特性对我们的数组进行排序&#xff0c;还有就是我们的TopK的问题&#xff0c;这次我们放在的是文件种&#xff0c;我们放入一亿个数字&#xff0c;然后我们取出一亿个数字中最大的十个数&#xff0c;利用上章堆的问题进行解决。 首先就是我…

【SQL Server2019SSMS】安装 | 卸载手册

目录 &#x1f4cb;前言 ⛳️【SQL Serverssms】安装 1. SQL Server自定义安装 2. SSMS安装 ⛳️【SQL Server】卸载 &#x1f4cb;前言 &#x1f308;个人主页&#xff1a;Sarapines Programmer &#x1f525; 系列专栏&#xff1a;本期文章收录在《宝藏工具使用手册》&am…

区块链介绍

区块链提供了比特币的公共账本&#xff0c;这是一个有序的、带有时间戳的交易记录。这个系统用于防止重复消费和修改之前的交易记录。 Introduction 比特币网络中的每个完全节点都独立存储只包含该节点验证的块的区块链。当多个节点在他们的区块链中都有相同的块时&#xff0…

00TDI 这件红色大衣也太适合过年穿了

分享女儿的时尚穿搭—红色大衣 这款大衣非常厚实 摸起来很软糯的触感 复合了660-700g绵羊绒 厚实度堪比一件厚实的羽绒服 门禁处做了立体的爱心装饰 精致又可爱&#xff01;&#xff01;&#xff01;

java--单继承、Object

java是单继承的&#xff0c;java中的类不支持多继承&#xff0c;但是支持多层继承。 反证法&#xff1a; 如果一个类同时继承两个类&#xff0c;然后两个类中都有同样的一个方法&#xff0c;哪当我创建这个类里的方法&#xff0c;是调用哪父类的方法 所以java中的类不支持多继…

【Linux】:信号的产生

信号 一.前台进程和后台进程1.前台进程2。后台进程3.总结 二.自定义信号动作接口三.信号的产生1.键盘组合键2.kill信号进程pid3.系统调用1.kill函数2.raise函数3.abort函数 四.异常五.软件条件六.通过终端按键产生信号 一.前台进程和后台进程 1.前台进程 一个简单的代码演示 …

【云备份】数据管理模块

文章目录 1. 数据管理模块要管理什么数据&#xff1f;2. 数据管理模块如何管理数据&#xff1f;3. 数据管理模块的具体实现BackupInfo 数据信息类NewBackupInfo —— 获取各项属性信息 DataManager 数据管理类构造函数析构函数insert —— 新增update —— 修改GetOneByURL——…

数据结构之时间复杂度与空间复杂度

1.算法效率 1.1 如何衡量一个算法的好坏&#xff1f; 比方说我们非常熟悉的斐波拉契数列&#xff1a; long long Fib(int N) {if(N < 3)return 1;return Fib(N-1) Fib(N-2); } 递归实现方式非常简洁&#xff0c;但一定好吗&#xff1f;如何衡量其好与坏&#xff1f; 1…

JVM——垃圾回收器(Serial,SerialOld,ParNew,CMS,Parallel Scavenge,Parallel Old)

目录 1.垃圾回收器的组合关系1.年轻代-Serial垃圾回收器2.老年代-SerialOld垃圾回收器3.年轻代-ParNew垃圾回收器4.老年代- CMS(Concurrent Mark Sweep)垃圾回收器CMS执行步骤&#xff1a;CMS垃圾回收器存在的问题缺点&#xff1a;CMS垃圾回收器存在的问题 – 线程资源争抢问题…

机器学习——支持向量机(SVM)

1.线性支持向量机 1.1数学模型 机器学习最终都是求解目标函数的最优问题&#xff1b; 一般都是讲问题转化为最小值来求解。 数学模型获得是一个不等式约束的最小化问题&#xff0c;求解时可通过构建拉格朗日函数求解。 1.2 拉格朗日函数及对偶问题求解 1.3 SMO算法求解 SMO算…

鸿蒙应用开发-初见:ArkTS

作者&#xff1a;HarderCoder ArkTS ArkTS围绕应用开发在 TypeScript &#xff08;简称TS&#xff09;生态基础上做了进一步扩展&#xff0c;继承了TS的所有特性&#xff0c;是TS的超集 ArkTS在TS的基础上扩展了struct和很多的装饰器以达到描述UI和状态管理的目的 基本语法 …