聊聊 C++ 中的四种类型转换符

一:背景

在玩 C 的时候,经常会用 void* 来指向一段内存地址开端,然后再将其强转成尺度更小的 char*int* 来丈量一段内存,参考如下代码:

int main()
{void* ptr = malloc(sizeof(int) * 10);int* int_ptr = (int*)ptr;char* char_ptr = (char*)ptr;
}

由于 C 的自由度比较大,想怎么玩就怎么玩,带来的弊端就是容易隐藏着一些不易发现的bug,归根到底还是程序员的功底不扎实,C++ 设计者觉得不能把程序员想的太厉害,应该要力所能及的帮助程序员避掉一些不必要的潜在 bug,并且还要尽最大努力的避免对性能有过多的伤害,所以就出现了 4 个强制类型转换运算符。

  1. const_cast

  2. reinterpret_cast

  3. dynamic_cast

  4. static_cast

既然 C++ 做了归类,必然就有其各自用途,接下来我们逐一和大家聊一下。

二:理解四大运算符

1. const_cast

这是四个运算符中最好理解的,玩过 C++ 的都知道,默认情况下是不能修改一个 const 变量,比如下面这样:

int main()
{const int i = 10;i = 12;
}

这段代码肯定是要报错的,那如果我一定要实现这个功能,如何做呢?这就需要用到 const_cast 去掉它的常量符号,然后对 i 进行操作即可,所以修改代码如下:

int main()
{const int i = 10;auto j = const_cast<int*>(&i);*(j) = 12;
}
d53640bebbada567e5c100b755b2f6e6.png

2. reinterpret_cast

从名字上看就是一个 重新解释转换,很显然这个非常底层,如果大家玩过 windbg ,应该知道用 dt 命令可以将指定的内存地址按照某一个结构体丈量出来,比如说 C# 的 CLR 在触发 GC 时,会有 gc_mechanisms 结构,参考代码如下:

0:000> dt WKS::gc_mechanisms 0x7ffb6ba96e60
coreclr!WKS::gc_mechanisms+0x000 gc_index         : 1+0x008 condemned_generation : 0n0+0x00c promotion        : 0n0+0x010 compaction       : 0n1+0x014 loh_compaction   : 0n0+0x018 heap_expansion   : 0n0+0x01c concurrent       : 0+0x020 demotion         : 0n0+0x024 card_bundles     : 0n1+0x028 gen0_reduction_count : 0n0+0x02c should_lock_elevation : 0n0+0x030 elevation_locked_count : 0n0+0x034 elevation_reduced : 0n0+0x038 minimal_gc       : 0n0+0x03c reason           : 0 ( reason_alloc_soh )+0x040 pause_mode       : 1 ( pause_interactive )+0x044 found_finalizers : 0n0+0x048 background_p     : 0n0+0x04c b_state          : 0 ( bgc_not_in_process )+0x050 allocations_allowed : 0n1+0x054 stress_induced   : 0n0+0x058 entry_memory_load : 0+0x05c exit_memory_load : 0

其实 reinterpret_cast 大概也是干这个事的,参考代码如下:

typedef struct _Point {int x;int y;
} Point;int main()
{Point point = { 10,11 };//内存地址void* ptr = &point;//根据内存地址 丈量出  PointPoint* ptr_point = reinterpret_cast<Point*>(ptr);printf("x=%d", ptr_point->x);
}

从代码看,我直接根据 ptr 地址丈量出了 Point 结构,说实话这个和 C 玩法就比较类似了。

3. dynamic_cast

在多态场景下,有时候会遇到这样的一个问题,一个父类有多个子类,我现在手拥一个父类,我不知道能不能将它转换为其中一个子类,要试探一下看看,那怎么去试探呢?类似 C# 中的 as 运算符,在 C++ 中就需要用 dynamic_cast  来做这件事情,参考如下:

//点
class Point {
public:Point(int x, int y) :x(x), y(y) {}virtual void show() {}
public:int x;int y;
};//矩形
class Rectangle :public Point {
public:Rectangle(int x, int y, int w, int h) : Point(x, y), w(w), h(h) {}
public:int w;int h;
};//三角形
class Triangle :public Point {
public:Triangle(int x, int y, int z) :Point(x, y), z(z) {}
public:int z;
};int main()
{Point* p1 = new Rectangle(10, 20, 100, 200);Point* p2 = new Triangle(4, 5, 6);//将  p1 转成 子类 Triangle 会报错的Triangle* t1 = dynamic_cast<Triangle*>(p1);if (t1 == nullptr) {printf("p1 不能转成 Triangle");}
}
baa802fa7c2772a38d231434c21387d7.png

对,场景就是这个,p1 其实是 Rectangle 转上去的, 这时候你肯定是不能将它向下转成 Triangle , 问题就在这里,很多时候你并不知道此时的 p1 是哪一个子类。

接下来的一个问题是,C++ 并不像C# 有元数据,那它是如何鉴别呢?其实这用了 RTTI 技术,哪里能看出来呢?哈哈,看汇编啦。

Triangle* t1 = dynamic_cast<Triangle*>(p1);
00831D57  push        0  
00831D59  push        offset Triangle `RTTI Type Descriptor' (083C150h)  
00831D5E  push        offset Point `RTTI Type Descriptor' (083C138h)  
00831D63  push        0  
00831D65  mov         eax,dword ptr [p1]  
00831D68  push        eax  
00831D69  call        ___RTDynamicCast (083104Bh)  
00831D6E  add         esp,14h  
00831D71  mov         dword ptr [t1],eax

从汇编可以看到编译器这是带夹私货了,在底层偷偷的调用了一个 ___RTDynamicCast 函数在运行时帮忙检测的,根据 cdcel 调用协定,参数是从右到左,恢复成代码大概是这样。

___RTDynamicCast(&p1, 0, &Point, &Triangle,0)

3. static_cast

从名字上就能看出,这个强转具有 static 语义,也就是 编译阶段 就生成好了,具体安全不安全,它就不管了,就拿上面的例子,将 dynamic_cast 改成 static_cast 看看有什么微妙的变化。

int main()
{Point* p1 = new Rectangle(10, 20, 100, 200);Point* p2 = new Triangle(4, 5, 6);Triangle* t1 = static_cast<Triangle*>(p1);printf("x=%d, y=%d,z=%d", t1->x, t1->y, t1->z);
}
9ddff7f3cbecff716f4a286e9b3cccc5.png

我们发现居然转成功了,而且 Triangle 的值也是莫名奇怪,直接取了 Rectangle 的前三个值,如果这是生产代码,肯定要挨批了。。。

接下来简单看下汇编代码:

Triangle* t1 = static_cast<Triangle*>(p1);
00DF5B17  mov         eax,dword ptr [p1]  
00DF5B1A  mov         dword ptr [t1],eax  printf("x=%d, y=%d,z=%d", t1->x, t1->y, t1->z);
00DF5B1D  mov         eax,dword ptr [t1]  
00DF5B20  mov         ecx,dword ptr [eax+0Ch]  
00DF5B23  push        ecx  
00DF5B24  mov         edx,dword ptr [t1]  
00DF5B27  mov         eax,dword ptr [edx+8]  
00DF5B2A  push        eax  
00DF5B2B  mov         ecx,dword ptr [t1]  
00DF5B2E  mov         edx,dword ptr [ecx+4]  
00DF5B31  push        edx  
00DF5B32  push        offset string "x=%d, y=%d,z=%d" (0DF8C80h)  
00DF5B37  call        _printf (0DF145Bh)  
00DF5B3C  add         esp,10h

从代码中看,它其实就是将 p1 的首地址给了 t1,然后依次把copy偏移值 +4,+8,+0C, 除了转换这个,还可以做一些 int ,long ,double 之间的强转,当然也是一样,编译时汇编代码就已经生成好了。

好了,本篇就说这么多,希望对你有帮助。

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

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

相关文章

【QGIS入门实战精品教程】3.1:QGIS如何连接SQL Server数据库?

文章目录 软件必备测试SQL Server服务是否启动QGIS与SQL Server建立连接软件必备 SQL Server 2008 R2QGIS 3.22.3测试SQL Server服务是否启动 打开SQL Server配置管理器,查看MSSQLSERVER服务的启动情况。

开源重量级的流程引擎或UI引擎

关注这两个方面的同学个踊跃加粉了~~同时在下面回复期望开源哪一个&#xff0c;将先开源呼声高的一个。 好吧&#xff0c;先小秀一下UI&#xff1a; 增加下面的pom依赖&#xff0c;表示此工程需要org.tinygroup.aerowindow ?12345<dependency> <groupId>org.tinyg…

阿里云网盘内测开启 填写申请表获取邀请码 附最新申请地址

日前有相关报道显示&#xff0c;阿里方面推出了一款名为“阿里云网盘”的独立App&#xff0c;并且其是由阿里云团队开发&#xff0c;定位则是为C端用户提供可靠安全的存储备份及智能相册等功能。据了解&#xff0c;目前这款APP尚未正式上线&#xff0c;暂时还在邀请制测试阶段。…

跟我做⼀个⾼德地图的 iOS / Android MAUI控件(Android 原⽣库绑定)

我们已经介绍了如何通过 .NET 绑定 iOS 原⽣库 &#xff0c;本篇开始介绍⼀下如何通过 .NET 绑定 Android 原⽣库。Android的库Android 的库以 .jar 做打包&#xff0c; 通过⼯具你可以将多个 .jar 完成绑定&#xff0c;然后通过 C# 调⽤原⽣的 Java 库。对⽐起 iOS &#xff0…

【QGIS入门实战精品教程】4.6:QGIS实现栅格(影像、DEM)的拼接与掩膜提取

参考阅读:ArcGIS实验教程——实验十一:影像拼接与提取 加载实验数据 本实验所采用的栅格数据为两个dem数据和一个矢量范围数据,加载如下图所示: 栅格数据信息如下: 栅格拼接 点击下拉菜单【栅格】→【杂项】→【合并(merge)】,如下所示:

ReSharper 2020.2 补丁

ReSharper 是一个JetBrains公司出品的著名的代码生成工具。其能帮助Microsoft Visual Studio成为一个更佳的IDE&#xff0c;它包括一系列丰富的能大大增加C#和Visual Basic .net开发者生产力的特征。使用ReSharper&#xff0c;你可以进行深度代码分析&#xff0c;智能代码协助…

零中频接收机主要问题

直流偏差和本振泄漏问题基本不会影响超外差式接收机的性能&#xff0c;问题主要是镜频抑制。需要高Q值的带通滤波器。 零中频不存在镜频干扰&#xff0c;可以省掉镜像抑制滤波器和中频滤波器。零中频的主要问题是&#xff1a;1直流偏差 2本振泄漏 3 闪烁噪声。 1 本振泄漏 本振…

【QGIS入门实战精品教程】9.1:QGIS构建泰森多边形(Thiessen Polygon)实例精解

泰森多边形是进行快速插值和分析地理实体影响区域的常用工具。例如,用离散点的性质描述多边形区域的性质,用离散点的数据计算泰森多边形区域的数据。泰森多边形可用于定性分析、统计分析和临近分析等。 参考教程: ArcGIS构建泰森多边形(Thiessen Polygon)实例精解 【Glob…

“Visual Studio 启动不能打开上次打开的文件” 最正确的解决姿势

网上很多提供的方法&#xff0c;不是删除.vs目录&#xff0c;就是修改.suo文件。 删除有风险&#xff0c;操作需谨慎&#xff01;&#xff01; 其实最简单的方法就是&#xff1a;工具——选项——项目和解决方案——☑加载解决方案时重新打开文档(D)

【QGIS入门实战精品教程】5.1:QGIS地理坐标转火星坐标系(GCJ02)案例教程

本文以案例的形式,讲述WGS84(GPS)、火星坐标系(GCJ02)、百度地图(BD09)坐标系之间互相转换。 一、WGS转火星坐标系对比 文中将行政区的WGS坐标转为火星坐标系,局部效果对比: 二、火星坐标系简介 火星坐标系统是一种国家保密插件,也叫做加密插件或者加偏或者SM模组,其…

spark streaming 的 Job创建、调度、提交

2019独角兽企业重金招聘Python工程师标准>>> 上文已经从源码分析了Receiver接收的数据交由BlockManager管理&#xff0c;整个数据接收流都已经运转起来了&#xff0c;那么让我们回到分析JobScheduler的博客中。 // JobScheduler.scala line 62def start(): Unit sy…

官宣!微软发布 VS Code Server!

北京时间 2022 年 7 月 7 日&#xff0c;微软在 VS Code 官方博客中宣布了 Visual Studio Code Server&#xff01;远程开发的过去与未来2019 年&#xff0c;微软发布了 VS Code Remote&#xff0c;开启了远程开发的新时代&#xff01;2020 年&#xff0c;微软发布了 GitHub Co…

【QGIS入门实战精品教程】4.4:QGIS如何将点自动连成线、线生成多边形?

个人简介:刘一哥,多年研究地图学、地理信息系统、遥感、摄影测量和GPS等应用,精通ArcGIS等软件的应用,精通多门编程语言,擅长GIS二次开发和数据库系统开发,具有丰富的行业经验,致力于测绘、地信、数字城市、资源、环境、生态、国土空间规划、空间数字建模、无人机等领域…

.NET7之MiniAPI(特别篇) :Preview6 缓存和限流

前几在用MiniAPI时还想没有比较优雅的缓存&#xff0c;这不&#xff0c;Preivew6就带来了。使用起来很简单&#xff0c;注入Sevice&#xff0c;引用中间件&#xff0c;然后在Map方法的后面跟CacheOutput()就ok了&#xff0c;CacheOutpu也有不同的参数&#xff0c;可以根据每个方…

曾鸣:未来十年,将确定智能商业的格局|干货

2019独角兽企业重金招聘Python工程师标准>>> 20年来风云变幻&#xff0c;潮起潮涌&#xff0c;我自己最深的一个感受&#xff0c;是对“势”这个字的理解。 第一&#xff0c;敬畏。对于商业规律和对大势的把握&#xff0c;很容易在三五年内决定一个企业的命运。 第二…

Jedis 设置key的超时时间

一分钟之内只能发送一次短信, 若用户刷新页面,然后输入原来的手机号,则继续计时 方案:服务器端要记录时间戳 方法名:sMSWaitingTime 功能:返回倒计时剩余时间,单位秒 Java代码 /*** * 倒计时还剩余多长时间 * param mobile : 手机号 * return : second */…

【QGIS入门实战精品教程】4.1:QGIS栅格数据地理配准完整操作流程

推荐阅读:ArcGIS地理配准完整操作步骤 文章目录 一、安装地理配准插件二、准备实验数据三、配准操作流程1. 添加栅格数据2. 添加地面控制点3. 配准设置4. 开始配准5. 精度评价一、安装地理配准插件 点击下拉菜单【插件】→【管理并安装插件】,如下图所示: QGIS默认已经安装…

聊聊 C++ 中的几种智能指针 (上)

一&#xff1a;背景 我们知道 C 是手工管理内存的分配和释放&#xff0c;对应的操作符就是 new/delete 和 new[] / delete[], 这给了程序员极大的自由度也给了我们极高的门槛&#xff0c;弄不好就得内存泄露&#xff0c;比如下面的代码&#xff1a;void test() {int* i new i…

【Android 学习】深入理解Handler机制

版权声明&#xff1a;本文为博主原创文章&#xff0c;转载请注明出处http://blog.csdn.net/u013132758。 https://blog.csdn.net/u013132758/article/details/51355051 Android 提供了Handler和Looper来来满足线程间的通信&#xff0c;而前面我们所说的IPC指的是进程间的通信。…

【QGIS入门实战精品教程】4.3:QGIS属性表按字段链接外部属性数据

属性数据是GIS空格数据的重要组成部分。属性数据采集的基本操作由于地理实体(如建筑物) 位于地块之内成者与地块有关(如道路),因此,描述地理实体的属性数据和描述地块实体与地理实体之间关系的属性数强大多数都是土地信息的范畴土地空间数据库的属性教据主要是用来描述空间目…