面试cast:reinterpret_cast/const_cast/static_cast/dynamic_cast
目录
1. cast
2. reinterpret_cast
3. const_cast
3.1 加上const的情况
3.2 去掉const的情况
4. static_cast
4.1 基本类型之间的转换
4.2 void指针转换为任意基本类型的指针
4.3 子类和父类之间的转换
5. dynamic_cast
5.1 RTTI(Run-time Type Identification)
1. cast
英 /kɑːst/ 美 /kæst/
v. 铸造;投(钓线);投票;投射(光、影子等);扔;使人怀疑;向…投以(视线、笑容等);分配角色;(蛇)蜕(皮);造谣中伤;踢落;把某人描写成
n. 铸件;铸模;特性;模子;铸造品;(一出戏剧或一部电影的)全体演员
在C++程序里是一种转型机制,跟物理铸造差不多,有一个模子(如int),然后根据这个模子生成一个铸件。
double a = 1.1;
char* b = reinterpret_cast<char*>(&a);
// char*就是一个新的模具,double指针是原料,通过铸造case变成一个新的铸件b
2. reinterpret_cast
reinterpret
英 /ˌriːɪnˈtɜːprət/, 美 /ˌriːɪnˈtɜːrprət/
vt. 重新解释;重新诠释
reinterpret_cast是四种强制转换中功能最为强大的(最暴力,最底层,最不安全),跟它的英文释义一样重新诠释。它的本质是编译器的指令。
它的作用:它可以把一个指针转换成一个整数,也可以把一个整数转换成一个指针。或者不同类型的指针的相互替换
代码示例:
#include <iostream>
int main()
{double a = 1.1;char* b = reinterpret_cast<char*>(&a);double* c = reinterpret_cast<double*>(b);printf("a:%lf, %p\n", a, &a);printf("b:%c, %p\n", *b, b);printf("c:%lf, %p\n", *c, c);
}
输出:

我尝试打断点进入reinterpret_cast内部,发现并不行(它的本质是编译器的指令)。
通过中间的 char*来转double*,但是没有出现精度问题。事实上reinterpret_cast只是在编译器进行的予以转化(并未拷贝,只是重新诠释),只要是个地址就可以转(二进制拷贝)。
3. const_cast
有两个功能,去掉const和加上const。
3.1 加上const的情况
#include <iostream>
int main()
{int* a = new int(1);const int* b = const_cast<const int*>(a);*a = 2;//*b=2;,常量不能修改printf("0x%p,%d\n", a,*a);printf("0x%p,%d\n", b,*b);return 0;
}
输出:

发现值是一样的,但是地址也是一样,说白了加const就是加了一个修饰和限定。
3.2 去掉const的情况
#include <iostream>
class A
{
public:int num;A(int val = 100) :num(val) {}~A() {}
};
int main()
{//1.const 修饰指针对象,指向原对象const A* pa1 = new A(200);A* cast_pa1 = const_cast<A*>(pa1);printf("1.const 修饰指针指向对象,指向原对象\n");printf("%p\n", pa1);printf("%p\n", cast_pa1);//2.const 修饰指向指针对象的值,指向原对象A* const pa2 = new A(200);A* cast_pa2 = const_cast<A*>(pa2);printf("2.const 修饰指向对象的值,指向原对象\n");printf("%p\n", pa2);printf("%p\n", cast_pa2);//3.const 同时修饰指针对象和指针对象的值,指向原对象const A* const pa3 = new A(200);A* cast_pa3_1 = const_cast<A*>(pa3);const A* cast_pa3_2 = const_cast<A*>(pa3);A* const cast_pa3_3 = const_cast<A*>(pa3);printf("3.const 同时修饰指针对象和指针对象的值,指向原对象\n");printf("%p\n", pa3);printf("%p\n", cast_pa3_1);printf("%p\n", cast_pa3_2);printf("%p\n", cast_pa3_3);//4.const 修饰普通对象,并且赋值给一般对象,不指向原对象const A pa4;A cast_pa4 = const_cast<A&>(pa4);printf("4.const 修饰普通对象,并且赋值给一般对象\n");printf("%p\n", &pa4);printf("%p\n", &cast_pa4);//5.const 修饰普通对象,并且赋值给引用对象,指向原对象const A pa5;A& cast_pa5 = const_cast<A&>(pa5);printf("5.const 修饰普通对象,并且赋值给引用对象\n");printf("%p\n", &pa5);printf("%p\n", &cast_pa5);// 6. const 修饰对象,对象指针去 const 属性后赋给指针,指向原对象const A pa6;A* cast_pa6 = const_cast<A*>(&pa6);printf("6. const 修饰对象,对象指针去 const 属性后赋给指针\n");printf("%p\n", &pa6);printf("%p\n", cast_pa6);//7.const修饰局部变量,不指向原对象const int pa7 = 1;int cast_pa7_1 = const_cast<int&>(pa7);int& cast_pa7_2 = const_cast<int&>(pa7);int* cast_pa7_3 = const_cast<int*>(&pa7);printf("6. const 修饰对象,对象指针去 const 属性后赋给指针\n");printf("地址, pa7:%p\n", &pa7);printf("cast_pa7_1:%p\n", &cast_pa7_1);printf("cast_pa7_2:%p\n", &cast_pa7_2);printf("cast_pa7_3:%p\n", cast_pa7_3);cast_pa7_1 = 10;printf("pa7:%d,未修改\n", pa7);printf("cast_pa7_1:%d\n", cast_pa7_1);cast_pa7_2 = 100;printf("pa7:%d,未修改\n", pa7);printf("cast_pa7_1:%d\n", cast_pa7_1);printf("cast_pa7_2:%d\n", cast_pa7_2);*cast_pa7_3 = 1000;printf("pa7:%d,未修改\n", pa7);printf("cast_pa7_1:%d\n", cast_pa7_1);printf("cast_pa7_2:%d\n", cast_pa7_2);printf("cast_pa7_3:%d\n", *cast_pa7_3);return 0;
}
输出
1.const 修饰指针指向对象,指向原对象
016E7820
016E7820
2.const 修饰指向对象的值,指向原对象
016E77C0
016E77C0
3.const 同时修饰指针对象和指针对象的值,指向原对象
016E7850
016E7850
016E7850
016E7850
4.const 修饰普通对象,并且赋值给一般对象
012FFD24
012FFD18
5.const 修饰普通对象,并且赋值给引用对象
012FFD0C
012FFD0C
6. const 修饰对象,对象指针去 const 属性后赋给指针
012FFCF4
012FFCF4
6. const 修饰对象,对象指针去 const 属性后赋给指针
地址, pa7:012FFCDC
cast_pa7_1:012FFCD0
cast_pa7_2:012FFCDC
cast_pa7_3:012FFCDC
pa7:1,未修改
cast_pa7_1:10
pa7:1,未修改
cast_pa7_1:10
cast_pa7_2:100
pa7:1,未修改
cast_pa7_1:10
cast_pa7_2:1000
cast_pa7_3:1000
分析:
指针之间的转换无论怎样还是原地址
去掉一般对象的const,如果赋值给一般对象则是新对象(A cast_pa4 = const_cast<A &>(pa4);)
去掉内置类型(如int)变量的const,如果赋值给一般对象则是新对象,否则全是原来对象地址(虽然地址是一样的,但是值是不一样的)疑惑中。。。。。。
4. static_cast
三个作用:
1.基本类型之间的转换
2.void指针转换为任意基本类型的指针,基本类型指针之间无法使用
3.用于有继承关系的子类与父类之间的指针或引用的转换
4.1 基本类型之间的转换
#include <iostream>
int main()
{double i = 1.1;int a = static_cast<int>(i);double b = static_cast<double>(a);int c = static_cast<int>(b);printf("i:%lf, %p\n", i, &i);printf("a:%d, %p\n", a, &a);printf("b:%lf,%p\n", b, &b);printf("c:%d, %p\n", c, &c);
}
输出

可以进行基本类型的转化,但是会损失精度类似与C语言的强制转化。转换过程实际上是有内存拷贝的,每次地址都不一样。跟reinterpret_cast不太一样reinterpret_cast是底层二进制的强制拷贝和语义转换不会损失精度。
注:reinterpret_cast不能进行基本类型之间的转换,只能做指针转换。
4.2 void指针转换为任意基本类型的指针
#include <iostream>
int main()
{double a = 1.1;void* b = static_cast<void*>(&a);double* c = static_cast<double*>(b);//int* d = static_cast<int*>(c); // 类型转换无效(基本类型指针之间无法使用)printf("a:%lf, %p\n", a, &a);printf("b:%p\n", b);printf("c:%lf, %p\n", *c, c);
}

这里是void指针和其他类型的指针进行的转化,结果是指向的是原地址,跟reinterpret_cast是一样的结果。说白了static_cast就是能做铁水变铁器和铁器变铁水,但是不能直接把铁镰刀变铁锄头。reinterpret_cast就是可以指鹿为马,能直接把铁镰刀变铁锄头,但是变成的铁锄头能不能刨地就不知道了。这里需要区分普通类型之间的转换和普通类型指针之间的转换(普通类型的转换不是)。
//int* d = static_cast<int*>(c); // 类型转换无效(基本类型指针之间无法使用)
4.3 子类和父类之间的转换
#include <iostream>
using namespace std;
class A
{public:A(){};void foo(){cout<<"A!"<<endl;}
};
class B:public A
{public:B(){} ;void foo(){cout<<"B!"<<endl;}
};
int main()
{A *a = new A();B * b = static_cast<B *>(a);b->foo();return 0;
}
输出:
![]()
这是向下转型,是不安全的,但是为什么没有报错呢,因为B中还没有B特有的(B的成员变量),A和B两者在内存中的结构是一致的。
举个不一致的例子:
#include <iostream>
using namespace std;
class A
{public:A(){}void foo(){cout<<"A!"<<endl;}
};
class B:public A
{char b='c';public:B(){}void foo(){cout<<b<<endl;}
};
int main()
{A* a = new A();a->foo();B* b = static_cast<B*>(a);b->foo();B* pb = new B();pb->foo();return 0;
}
输出

分析:这里就发生了错误了,B中特有的成员变量没有初始化(使用了不安全的向下转型)
5. dynamic_cast
dynamic_cast用于类继承层次间的指针或引用转换(主要用于向下的安全转换)
dynamic_cast向下转型的安全性主要体现在RTTI
5.1 RTTI(Run-time Type Identification)
运行时类型识别。程序能够使用基类的指针或引用来检查着这些指针或引用所指的对象的实际派生类型(判断指针原型)
RTTI提供了两个非常有用的操作符:typeid和dynamic_cast。(三个最主要的东西,dynamic_cast,typeid,type_info)
typeid:typeid函数(为type_info类的友元函数,为什么要这样呢?目的是防止创建type_info对象)的主要作用就是让用户知道当前的变量是什么类型的,它可以返回一个type_info的引用,可以获取类的名称和编码typeid重载了type_info中的==和!=可以用于判断两个类型是否相等
1)typeid识别静态类型
当typeid中的操作数是如下情况之一时,typeid运算符指出操作数的静态类型,即编译时的类型。
(1)类型名
(2)一个基本类型的变量
(3)一个具体的对象(非指针对象)
(4)一个指向 不含有virtual函数的类 对象的指针的解引用
(5)一个指向 不含有virtual函数的类 对象的引用
静态类型在程序的运行过程中并不会改变,所以并不需要在程序运行时计算类型,在编译时就能根据操作数的静态类型,推导出其类型信息。例如如下的代码片断,typeid中的操作数均为静态类型
#include <iostream>
#include <typeinfo>
using namespace std;
class X {
public:X() { };virtual void func() = 0;
};
class XX : public X {
public:XX() { };void func() { };
};
class Y {
public:Y() { };void func() { };
};
int main()
{int n = 0;XX xx;Y y;Y* py = &y;X* px = &xx;// int和XX都是类型名cout << typeid(int).name() << endl;cout << typeid(XX).name() << endl;// n为基本变量cout << typeid(n).name() << endl;// xx所属的类虽然存在virtual,但是xx为一个具体的对象cout << typeid(xx).name() << endl;// py为一个指针,属于基本类型cout << typeid(py).name() << endl;// py指向的Y的对象,但是类Y不存在virtual函数cout << typeid(*py).name() << endl;// pxcout << typeid(*px).name() << endl;cout << typeid(px).name() << endl;return 0;
}
输出:

对于px,px是 class X *,但是*px是class XX。
动态类型转换:
#include <iostream>
#include <typeinfo>
using namespace std;
class X
{
public:X() { mX = 101;}virtual ~X(){}
private:int mX;
};class XX : public X
{
public:XX() :X() { mXX = 1001; }virtual ~XX() { }
private:int mXX;
};class YX : public X
{
public:YX() { mYX = 1002; }virtual ~YX() { }
private:int mYX;
};
int main()
{X x;XX xx;YX yx;// 子类直接转父类指针,没问题X* px = &xx;cout << "px:\t" << px << endl;XX* pxx = dynamic_cast<XX*>(px); // 转换1,成功cout << "pxx:\t" << pxx << endl;YX* pyx = dynamic_cast<YX*>(px); // 转换2,失败cout << "pyx:\t" << pyx << endl;pyx = (YX*)px; // 转换3,成功cout << "pyx:\t" << pyx << endl;pyx = static_cast<YX*>(px); // 转换4,成功cout << "pyx:\t" << pyx << endl;return 0;
}
输出:

px是一个基类(X)的指针,但是它指向了派生类XX的一个对象。在转换1中,转换成功,因为px指向的对象确实为XX的对象。在转换2中,转换失败,因为px指向的对象并不是一个YX对象,此时dymanic_cast返回nullptr。转换3为C风格的类型转换而转换4使用的是C++中的静态类型转换,它们均能成功转换,但是这个对象实际上并不是一个YX的对象,所以在转换3和转换4中,若继续通过指针使用该对象必然会导致错误,所以这个转换是不安全的。
相关文章:
面试cast:reinterpret_cast/const_cast/static_cast/dynamic_cast
目录 1. cast 2. reinterpret_cast 3. const_cast 3.1 加上const的情况 3.2 去掉const的情况 4. static_cast 4.1 基本类型之间的转换 4.2 void指针转换为任意基本类型的指针 4.3 子类和父类之间的转换 5. dynamic_cast 5.1 RTTI(Run-time Type Identification) 1.…...
致远M3 反序列化RCE漏洞复现(XVE-2023-24878)
0x01 产品简介 M3移动办公是致远互联打造的一站式智能工作平台,提供全方位的企业移动业务管理,致力于构建以人为中心的智能化移动应用场景,促进人员工作积极性和创造力,提升企业效率和效能,是为企业量身定制的移动智慧…...
Ubuntu安装CUDA驱动
Ubuntu安装CUDA驱动 前言官网安装确认安装版本安装CUDA Toolkit 前言 CUDA驱动一般指CUDA Toolkit,可通过Nvidia官网下载安装。本文介绍安装方法。 官网 CUDA Toolkit 最新版:CUDA Toolkit Downloads | NVIDIA Developer CUDA Toolkit 最新版文档&…...
【MySQL】内连接和外连接
内连接和外连接 前言正式开始内连接外连接左外连接右外连接 前言 前一篇讲多表查询的时候讲过笛卡尔积,其实笛卡尔积就算一种连接,不过前一篇讲的时候并没有细说连接相关的内容,本篇就来详细说说表的连接有哪些。 本篇博客中主要用到的还是…...
U盘启动制作工具Rufus
U盘启动制作工具Rufus 下载U盘启动制作工具Rufus,进入Rufus官网:http://rufus.ie/en/,打开之后往后滑动,找到download即可点击下载。 需要插入U盘 首先需要插入U盘,如果U盘有重要文件一定要备份,然后右键…...
Ubuntu 22.04安装vscode
要在Ubuntu 22.04安装vscode,请完成这些步骤。 首先apt命令更新软件包索引并安装导入微软GPG密钥的依赖软件。 更新,近期内执行过可忽略 sudo apt update安装工具包 sudo apt install software-properties-common apt-transport-https curl当导入GPG后…...
计算机视觉的应用19-基于pytorch框架搭建卷积神经网络CNN的卫星地图分类问题实战应用
大家好,我是微学AI,今天给大家介绍一下计算机视觉的应用19-基于pytorch框架搭建卷积神经网络CNN的卫星地图分类问题实战应用。随着遥感技术和卫星图像获取能力的快速发展,卫星图像分类任务成为了计算机视觉研究中一个重要的挑战。为了促进这一…...
Java 获取本地ip网卡信息
工具类 public static Optional<Inet4Address> getLocalIp4Address() throws SocketException {final List<Inet4Address> inet4Addresses getLocalIp4AddressFromNetworkInterface();if (inet4Addresses.size() ! 1) {final Optional<Inet4Address> ipBySo…...
将kali系统放在U盘中插入电脑直接进入kali系统
首先准备一个空白的 U 盘。 Kali Linux | Penetration Testing and Ethical Hacking Linux Distribution 在 Windows 上制作 Kali 可启动 USB 驱动器 Making a Kali Bootable USB Drive on Windows | Kali Linux Documentation 1. 首先下载 .iso 镜像 Index of /kali-images…...
二十四、RestClient操作文档
目录 一、新增文档 1、编写测试代码 二、查询文档 1、编写测试代码 三、删除文档 1、编写测试代码 四、修改文档 1、编写测试代码 五、批量导入文档 批量查询 一、新增文档 1、编写测试代码 SpringBootTest public class HotelDocumentTest {private RestHighLevelC…...
【Docker】从零开始:9.Docker命令:Push推送仓库(Docker Hub,阿里云)
【Docker】从零开始:9.Docker命令:Push推送仓库 知识点1.Docker Push有什么作用?2.Docker仓库有哪几种2.1 公有仓库2.2 第三方仓库2.3 私有仓库2.4 搭建私有仓库的方法有哪几种 3.Docker公有仓库与私有仓库的优缺点对比 Docker Push 命令标准语法操作参数…...
Centos部署GitLab-备份恢复
1. 下载rpm包 wget https://mirrors.tuna.tsinghua.edu.cn/gitlab-ce/yum/el7/gitlab-ce-10.8.4-ce.0.el7.x86_64.rpm2. 安装依赖 yum -y install policycoreutils openssh-server openssh-clients postfix policycoreutils-python3. rpm安装 rpm -ivh gitlab-ce-10.8.4-ce.…...
CSV用EXCEL打开后为科学计数法(后几位丢失)解决方法
当在Excel中打开含有长数字(如订单号)的CSV文件时,Excel可能会默认将这些长数字格式化为科学计数法。 而当您尝试将它们转换为文本格式时,如果数字非常长,Excel可能无法正确处理其精度,导致数字的后几位变…...
flink sqlClient提交hiveIceberg
flink sqlClient提交hiveIceberg 环境准备sqlclient启动前准备启动sqlclientinit.sqlinsert.sql 环境准备 组件名版本flink客户端1.14.4-2.12hadoop集群3.1.4hive客户端3.1.2icebergiceberg-flink-runtime-1.14-0.13.2.jariceberg-hive依赖iceberg-hive-runtime-0.13.2.jar s…...
SpringBoot 导入其他配置文件
默认情况下,springboot 初始的项目中都有一个 application.yml 或者 application.properties 文件,如果我们希望再定义一个独立的配置文件用来配置特定业务数据,而不希望把这些配置内容都堆积在 application 配置文件中,实现这个需…...
景区智慧旅游智能化系统方案:PPT全文58页,附下载
关键词:智慧景区解决方案,智慧文旅解决方案,智慧旅游解决方案,智慧文旅综合运营平台 一、景区智慧旅游智能化系统建设背景 近年来,随着信息技术的快速发展和普及,以及旅游市场的不断扩大和升级࿰…...
Java特殊文件读取案例Properties
代码 package com.itheima.d1;import java.io.FileNotFoundException; import java.io.FileReader; import java.io.FileWriter; import java.util.Properties;public class Test3 {public static void main(String[] args) throws Exception {//目标:读取属性文件…...
搜维尔科技:Faceware面部捕捉最佳实践!
视频源和分辨率: 我们的软件针对 RGB 彩色素材进行了优化,不支持使用红外摄像机。 我们建议视频分辨率为 720p 和 1080p。低于 720p 的分辨率可能会对跟踪质量产生负面影响,而高于 1080p 的分辨率会导致存储要求和传输时间增加,而…...
如何使用ArcGIS Pro进行坐标转换
不同来源的数据坐标系可能是不同的,为了统一使用这些数据就需要进行坐标转换,ArcGIS Pro作为专业的GIS软件,坐标转换功能肯定也是包含的,这里为大家介绍一下ArcGIS Pro如何进行坐标转换,希望能对你有所帮助。 数据来源…...
Python----类对象和实例对象
目录 一.类和类的实例 二.类属性和实例属性 三.私有属性和公有属性 四.静态方法和类方法 五.__init__方法,__new__方法和__del__方法: 六.私有方法和公有方法 七.方法的重载 八.方法的继承 九.方法的重写 十.对象的特殊方法 十一.对象的引用&a…...
超越F8:解锁SAP ABAP调试器里那些被低估的‘神器’按钮(含ALV数据直接编辑)
超越F8:解锁SAP ABAP调试器里那些被低估的‘神器’按钮(含ALV数据直接编辑) 在SAP ABAP开发的世界里,调试器就像一把瑞士军刀——大多数人只用了其中的几个基本功能。每天重复着F5/F6/F7/F8的单步执行,却不知道调试器里…...
NCM音频解密终极指南:3步解锁网易云音乐加密格式,实现全平台自由播放
NCM音频解密终极指南:3步解锁网易云音乐加密格式,实现全平台自由播放 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 你是否曾经下载了网易云音乐的NCM格式文件,却发现只能在特定客户端播放&#…...
开源一个基于INA226的USB-C PD诱骗器功耗监测仪,硬件软件全公开
开源USB-C PD诱骗器功耗监测仪:从硬件设计到软件实现的全栈解析 最近在调试一个支持USB PD协议的移动电源时,我发现市面上大多数功率检测工具都无法准确捕捉快充协议握手过程中的动态功率变化。这促使我开发了一款基于INA226的高精度USB-C PD诱骗器功耗监…...
CompressO:免费开源的终极跨平台视频压缩工具完整指南
CompressO:免费开源的终极跨平台视频压缩工具完整指南 【免费下载链接】compressO Convert any video/image into a tiny size. 100% free & open-source. Available for Mac, Windows & Linux. 项目地址: https://gitcode.com/gh_mirrors/co/compressO …...
# CentOS 7 + 中国服务器 + Codex + 中转 API 完整教程
CentOS 7 中国服务器 Codex 中转 API 完整教程 适用场景: 国内云服务器无法直连 OpenAI系统为 CentOS 7希望稳定使用 Codex CLI 这篇文章把安装、配置、避坑和最终可用方案一次讲清楚,适合直接照着操作。 一、先说核心问题 很多人在 CentOS 7 上安装 …...
如何快速解决音乐标签乱码问题:Music Tag Web的完整繁简体转换指南
如何快速解决音乐标签乱码问题:Music Tag Web的完整繁简体转换指南 【免费下载链接】music-tag-web 音乐标签编辑器,可编辑本地音乐文件的元数据(Editable local music file metadata.) 项目地址: https://gitcode.com/gh_mirro…...
本地Cookie导出终极指南:5分钟掌握安全Cookie管理技巧
本地Cookie导出终极指南:5分钟掌握安全Cookie管理技巧 【免费下载链接】Get-cookies.txt-LOCALLY Get cookies.txt, NEVER send information outside. 项目地址: https://gitcode.com/gh_mirrors/ge/Get-cookies.txt-LOCALLY 你是否曾需要获取网站的Cookie数…...
Turing Pi 2集群主板:模块化设计与边缘计算实践
1. Turing Pi 2集群主板深度解析:从硬件架构到应用场景Turing Pi 2的出现在单板计算机领域掀起了一阵新风潮。这款采用mini-ITX规格的集群主板,最大的创新在于其模块化设计理念——通过四个SO-DIMM插槽,用户可以自由混搭不同架构的计算模块。…...
AI自动化非营利组织尽职调查:MCP服务器与七大数据源实战
1. 项目概述:当AI助手学会“尽职调查”如果你在基金会、企业社会责任部门或合规团队工作,那么“尽职调查”这个词对你来说一定不陌生。它意味着在批准一笔拨款、捐赠或建立合作关系前,你必须完成一系列繁琐但至关重要的核查工作:翻…...
告别LabVIEW!用Python+PyVISA搞定示波器自动化,保姆级代码解析
从LabVIEW到Python:PyVISA实现示波器自动化的工程实践 在电子测试测量领域,LabVIEW长期占据主导地位,但越来越多的工程师开始寻求更灵活、经济的替代方案。Python凭借其开源生态和丰富的科学计算库,正成为仪器自动化的新选择。本文…...
