当前位置: 首页 > news >正文

【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 . . .

在这里插入图片描述

相关文章:

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

文章目录 一、子类 和 父类 之间的类型转换 - 动态类型转换 dynamic_cast1、构造父类和子类2、子类 和 父类 之间的类型转换 - 隐式类型转换3、子类 和 父类 之间的类型转换 - 静态类型转换 static_cast4、子类 和 父类 之间的类型转换 - 重新解释类型转换 reinterpret_cast5、…...

在CentOS 7.9上搭建高性能的FastDFS+Nginx文件服务器集群并实现外部远程访问

文章目录 引言第一部分&#xff1a;FastDFS介绍与安装1.1 FastDFS简介1.2 FastDFS安装1.2.1 安装Tracker Server1.2.2 安装Storage Server 1.3 FastDFS配置1.3.1 配置Tracker Server1.3.2 配置Storage Server1.3.3 启动FastDFS服务 第二部分&#xff1a;Nginx配置2.1 Nginx安装…...

YOLOv8独家原创改进: AKConv(可改变核卷积),即插即用的卷积,效果秒杀DSConv | 2023年11月最新发表

💡💡💡本文全网首发独家改进:可改变核卷积(AKConv),赋予卷积核任意数量的参数和任意采样形状,为网络开销和性能之间的权衡提供更丰富的选择,解决具有固定样本形状和正方形的卷积核不能很好地适应不断变化的目标的问题点,效果秒殺DSConv 1)AKConv替代标准卷积进行…...

Docker pause/unpause命令

docker pause &#xff1a;暂停容器中所有的进程。 docker unpause &#xff1a;恢复容器中所有的进程。 语法 docker pause CONTAINER [CONTAINER...]docker unpause CONTAINER [CONTAINER...]实例 暂停数据库容器db01提供服务。 docker pause db01恢复数据库容器db01提供…...

PostgreSQL create or replace view和重建视图 有什么区别?

一、 replace vs 重建 遇到开发提了个问题&#xff0c;create or replace view和重建视图&#xff08;dropcreate&#xff09;有什么区别&#xff0c;查询资料整理了一下。 1. create or replace 当存在同名视图时&#xff0c;尝试将其替换新视图语句必须与现有视图查询具有相…...

Selenium 连接到现有的 Firefox 示例

当前环境&#xff1a; python 3.7 selenium 3.14.1 urllib3 1.26.8 Frefox 115.1.0esr(32位) geckodriver.exe 0.33.0 1 下载 Firefox 浏览器&#xff0c;根据自己的需要选择。 下载 Firefox 浏览器&#xff0c;这里有简体中文及其他 90 多种语言版本…...

小程序如何进行版本回退

当商家决定回退小程序版本时&#xff0c;可能是因为新版本出现了一些问题或者不符合预期&#xff0c;需要恢复到之前的稳定版本。下面具体介绍怎么回退小程序的版本。 在小程序管理员后台->版本设置处&#xff0c;点击版本回退。确认后&#xff0c;小程序会回退到上一次的版…...

15:00面试,15:06就出来了,问的问题有点变态。。。

从小厂出来&#xff0c;没想到在另一家公司又寄了。 到这家公司开始上班&#xff0c;加班是每天必不可少的&#xff0c;看在钱给的比较多的份上&#xff0c;就不太计较了。没想到8月一纸通知&#xff0c;所有人不准加班&#xff0c;加班费不仅没有了&#xff0c;薪资还要降40%…...

大数据-之LibrA数据库系统告警处理(ALM-37008 MPPDB服务不可用)

告警解释 告警模块每30秒周期性检测MPPDB服务健康状态&#xff0c;当检测到MPPDB健康状态为“故障”时产生告警。 当检测到MPPDB健康状态为“良好”时告警恢复。 告警属性 告警ID 告警级别 可自动清除 37008 致命 是 告警参数 参数名称 参数含义 ServiceName 产生…...

Pytorch-gpu环境篇

最最最头疼的就是配环境了 包之间的版本匹配问题 INSTALLING PREVIOUS VERSIONS OF PYTORCH 要考虑到pytorch和torchvision之间的匹配关系 显卡版本匹配问题...

互联网上门洗鞋店小程序

上门洗鞋店小程序门店版是基于原平台版进行增强的&#xff0c;结合洗鞋行业的线下实际运营经验和需求&#xff0c;专为洗鞋人和洗鞋店打造的高效、实用、有价值的管理软件系统。 它能够帮助洗鞋人建立自己的私域流量&#xff0c;实现会员用户管理&#xff0c;实现用户与商家的点…...

【深度学习笔记】04 概率论基础

04 概率论基础 概率论公理联合概率条件概率贝叶斯定理边际化独立性期望和方差模拟投掷骰子的概率随投掷次数增加的变化 概率论公理 概率&#xff08;probability&#xff09;可以被认为是将集合映射到真实值的函数。 在给定的样本空间 S \mathcal{S} S中&#xff0c;事件 A \m…...

45.113.200.1搜索引擎蜘蛛抓取不到网站内容页面可能的原因

以下是搜索引擎蜘蛛抓取不到网站内容页面的一些主要原因总结&#xff1a; 网站的 robots.txt 文件中禁止了搜索引擎蜘蛛访问网站某些页面或目录&#xff0c;导致搜索引擎无法抓取到相关页面的内容。 网站的页面存在重定向或者跳转&#xff0c;搜索引擎蜘蛛无法直接抓取到需要的…...

VMware 系列:vSphere Client安装配置常见问题及解决方案

vSphere Client安装配置常见问题及解决方案 1.本地通过远程桌面复制文件或者文件夹到windows服务器时出错提示“未指定的错误”问题描述:原因分析:解决方案:方法 1方法 2方法 32.远程桌面连接出现身份验证错误要求的函数不受支持问题描述:原因分析:解决方案:3.Windows se…...

FLASK博客系列5——模板之从天而降

我们啰啰嗦嗦讲了4篇&#xff0c;都是在调接口&#xff0c;啥时候能看到漂亮的页面呢&#xff1f;别急&#xff0c;今天我们就来实现。 来我们先来实现一个简单的页面。不多说&#xff0c;上代码。 app.route(/) def index():user {username: clannadhh}return <html>&…...

6.一维数组——用冒泡法将10个整数由大到小排序

文章目录 前言一、题目描述 二、题目分析 三、解题 程序运行代码 前言 本系列为一维数组编程题&#xff0c;点滴成长&#xff0c;一起逆袭。 一、题目描述 用冒泡法将10个整数由大到小排序 二、题目分析 三、解题 程序运行代码 #include<stdio.h> int main() {int …...

Wireshark的捕获过滤器

Wireshark的过滤器&#xff0c;顾名思义&#xff0c;作用是对数据包进行过滤处理。具体过滤器包括捕获过滤器和显示过滤器。本文对捕获过滤器进行分析。 捕获过滤器&#xff1a;当进行数据包捕获时&#xff0c;只有那些满足给定的包含/排除表达式的数据包会被捕获。 捕获过滤器…...

安陆FPGA调试中遇到的问题总结

参考链接&#xff1a;安陆FPGA踩坑填坑记录&#xff08;梦里呓语回忆录&#xff09;_fpga开发是个大坑-CSDN博客 上海安陆FPGA设置重上电启动速度_安路 fpga 加载时间-CSDN博客...

Springboot2+WebSocket

一、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId> </dependency> 二、添加配置 新增配置文件 config/WebSocketConfig.java import org.springframewo…...

希尔伯特和包络变换

一、希尔伯特变换 Hilbert Transform&#xff0c;数学定义&#xff1a;在数学与信号处理的领域中&#xff0c;一个实值函数的希尔伯特变换是将信号x(t)与h(t)1/(πt)做卷积&#xff0c;以得到其希尔伯特变换。因此&#xff0c;希尔伯特变换结果可以理解为输入是x(t)的线性时不…...

Zustand 状态管理库:极简而强大的解决方案

Zustand 是一个轻量级、快速和可扩展的状态管理库&#xff0c;特别适合 React 应用。它以简洁的 API 和高效的性能解决了 Redux 等状态管理方案中的繁琐问题。 核心优势对比 基本使用指南 1. 创建 Store // store.js import create from zustandconst useStore create((set)…...

ios苹果系统,js 滑动屏幕、锚定无效

现象&#xff1a;window.addEventListener监听touch无效&#xff0c;划不动屏幕&#xff0c;但是代码逻辑都有执行到。 scrollIntoView也无效。 原因&#xff1a;这是因为 iOS 的触摸事件处理机制和 touch-action: none 的设置有关。ios有太多得交互动作&#xff0c;从而会影响…...

华硕a豆14 Air香氛版,美学与科技的馨香融合

在快节奏的现代生活中&#xff0c;我们渴望一个能激发创想、愉悦感官的工作与生活伙伴&#xff0c;它不仅是冰冷的科技工具&#xff0c;更能触动我们内心深处的细腻情感。正是在这样的期许下&#xff0c;华硕a豆14 Air香氛版翩然而至&#xff0c;它以一种前所未有的方式&#x…...

CSS设置元素的宽度根据其内容自动调整

width: fit-content 是 CSS 中的一个属性值&#xff0c;用于设置元素的宽度根据其内容自动调整&#xff0c;确保宽度刚好容纳内容而不会超出。 效果对比 默认情况&#xff08;width: auto&#xff09;&#xff1a; 块级元素&#xff08;如 <div>&#xff09;会占满父容器…...

[大语言模型]在个人电脑上部署ollama 并进行管理,最后配置AI程序开发助手.

ollama官网: 下载 https://ollama.com/ 安装 查看可以使用的模型 https://ollama.com/search 例如 https://ollama.com/library/deepseek-r1/tags # deepseek-r1:7bollama pull deepseek-r1:7b改token数量为409622 16384 ollama命令说明 ollama serve #&#xff1a…...

Caliper 负载(Workload)详细解析

Caliper 负载(Workload)详细解析 负载(Workload)是 Caliper 性能测试的核心部分,它定义了测试期间要执行的具体合约调用行为和交易模式。下面我将全面深入地讲解负载的各个方面。 一、负载模块基本结构 一个典型的负载模块(如 workload.js)包含以下基本结构: use strict;/…...

Vue ③-生命周期 || 脚手架

生命周期 思考&#xff1a;什么时候可以发送初始化渲染请求&#xff1f;&#xff08;越早越好&#xff09; 什么时候可以开始操作dom&#xff1f;&#xff08;至少dom得渲染出来&#xff09; Vue生命周期&#xff1a; 一个Vue实例从 创建 到 销毁 的整个过程。 生命周期四个…...

DiscuzX3.5发帖json api

参考文章&#xff1a;PHP实现独立Discuz站外发帖(直连操作数据库)_discuz 发帖api-CSDN博客 简单改造了一下&#xff0c;适配我自己的需求 有一个站点存在多个采集站&#xff0c;我想通过主站拿标题&#xff0c;采集站拿内容 使用到的sql如下 CREATE TABLE pre_forum_post_…...

[拓扑优化] 1.概述

常见的拓扑优化方法有&#xff1a;均匀化法、变密度法、渐进结构优化法、水平集法、移动可变形组件法等。 常见的数值计算方法有&#xff1a;有限元法、有限差分法、边界元法、离散元法、无网格法、扩展有限元法、等几何分析等。 将上述数值计算方法与拓扑优化方法结合&#…...

渗透实战PortSwigger Labs指南:自定义标签XSS和SVG XSS利用

阻止除自定义标签之外的所有标签 先输入一些标签测试&#xff0c;说是全部标签都被禁了 除了自定义的 自定义<my-tag onmouseoveralert(xss)> <my-tag idx onfocusalert(document.cookie) tabindex1> onfocus 当元素获得焦点时&#xff08;如通过点击或键盘导航&…...