【C++从0到王者】第四十二站:类型转换

文章目录

  • 一、 C语言中的类型转换
    • 1. C语言中的类型转换
    • 2.一个常见的坑
  • 二、为什么C++需要四种类型转换
  • 三、C++强制类型转换
    • 1.static_cast
    • 2.reinterpret_cast
    • 3.const_cast
    • 4.dynamic_cast
  • 四、RTTI

一、 C语言中的类型转换

1. C语言中的类型转换

在C语言中,如果赋值运算符左右两侧类型不同,或者形参与实参类型不匹配,或者返回值类型与接收返回值类型不一致时,就需要发生类型转化,C语言中总共有两种形式的类型转换:隐式类型转换和显式类型转换

  1. 隐式类型转化:编译器在编译阶段自动进行,能转就转,不能转就编译失败
  2. 显式类型转化:需要用户自己处理

如下所示代码中。

void Test()
{int i = 1;// 隐式类型转换double d = i;printf("%d, %.2f\n", i, d);int* p = &i;// 显示的强制类型转换int address = (int)p;printf("%p, %d\n", p, address);
}

如果要进行类型转换,必须要有一些关系

对于整数家族、浮点数,指针都是可以相互进行转换的。

但是像一些不相关的类型就不可以了。比如vector不可以转换为string。

还有一些是:单参数的构造函数支持隐式类型转换。如果是自定义类型的转换,则要有单参数的对应的构造函数

image-20240206003213903

如果我们不想要去进行对应的隐式类型转换,我们可以加上explicit关键字。同时我们可以注意到,虽然无法显示类型转换了,但是因为有对应的构造函数,所以可以进行显示类型转换

image-20240206003549089

缺陷转换的可视性比较差,所有的转换形式都是以一种相同形式书写,难以跟踪错误的转换

2.一个常见的坑

int main()
{const int n = 10;int* p = (int*)&n;(*p)++;cout << n << endl;cout << *p << endl;return 0;
}

上面的代码运行结果如下

image-20240206004334037

我们可以注意到,监视窗口和我们实际打印的效果是不一样的。

这其实是因为编译器的一个优化。因为它发现这个n是一个const的,不会变化的。所以直接把它放到寄存器上了。取出的时候直接从寄存器中取出来。不会直接从内存中取,虽然内存中已经被改变了。

其次要注意的是,const的常变量,编译器优化后会直接将其当作宏来处理。所以虽然vs2022不支持c99的变长数组,但是如果是const修饰后的,还是可以编译通过的。

所以,这里的转换是有安全隐患的

如果我们不想让他取进行上述的优化,即直接从寄存器中去取。而是每次都从内存中取,我们只需要加上volatile关键字

int main()
{volatile const int n = 10;int* p = (int*)&n;(*p)++;cout << n << endl;cout << *p << endl;return 0;
}

image-20240206012145713

二、为什么C++需要四种类型转换

C风格的转换格式很简单,但是有不少缺点的:

  1. 隐式类型转化有些情况下可能会出问题:比如数据精度丢失
  2. 显式类型转换将所有情况混合在一起,代码不够清晰

因此C++提出了自己的类型转化风格,注意因为C++要兼容C语言,所以C++中还可以使用C语言的转化风格。

三、C++强制类型转换

标准C++为了加强类型转换的可视性,引入了四种命名的强制类型转换操作符:

static_castreinterpret_castconst_castdynamic_cast

1.static_cast

如下代码所示,是一个简单的使用。注意这里是对变量加上括号的

int main()
{double d = 3.14;int a = static_cast<int>(d);cout << a << endl;return 0;
}

image-20240206012851412

static_cast用于非多态类型的转换(静态转换),编译器隐式执行的任何类型转换都可用

static_cast,但它不能用于两个不相关的类型进行转换

比如地址和整型就不可以进行转换

下面代码就是错误的

int main()
{int a = 3;int* p = &a;int address = static_cast<int>(p);return 0;
}

image-20240206013226315

所以它必须要是相关相近类型的转化

2.reinterpret_cast

reinterpret_cast操作符通常为操作数的位模式提供较低层次的重新解释,用于将一种类型转换为另一种不同的类型

它可以进行不相关类型的转化

我们还是前面的代码,如果我们使用reinterpret_cast就正确了

int main()
{int a = 3;int* p = &a;//int address = static_cast<int>(p);int address = reinterpret_cast<int>(p);return 0;
}

如果我们这样做的话,即下面代码,它会进行报错

int main()
{const int n = 10;int* p = reinterpret_cast<int*>(&n);return 0;
}

image-20240206013822979

因为这个reinterpret_cast不支持把const int\*转化为int\*

于是我们就可以使用const_cast

3.const_cast

const_cast最常用的用途就是删除变量的const属性,方便赋值

下面代码就是正确了

int main()
{const int n = 10;//int* p = reinterpret_cast<int*>(&n);int* p = const_cast<int*>(&n);return 0;
}

这时候我们一看,这个const要变为普通的,是有风险的,所以我们就会自觉的加上volatile关键字

int main()
{volatile const int n = 10;//int* p = reinterpret_cast<int*>(&n);int* p = const_cast<int*>(&n);return 0;
}

4.dynamic_cast

dynamic_cast用于将一个父类对象的指针/引用转换为子类对象的指针或引用(动态转换)
向上转型:子类对象指针/引用->父类指针/引用(不需要转换,赋值兼容规则)
向下转型:父类对象指针/引用->子类指针/引用(用dynamic_cast转型是安全的)
注意:

  1. dynamic_cast只能用于父类含有虚函数的类
  2. dynamic_cast会先检查是否能转换成功,能成功则转换,不能则返回0

比如如下代码所示,就是一个不安全的,出错的代码

class A
{
public:virtual void f() {}int _x = 0;
};
class B:public A
{
public:int _y = 0;
};void fun(A* a)
{B* pb = (B*)a;pb->_x++;pb->_y++;
}
int main()
{A a;fun(&a);return 0;
}

image-20240206015630369

因为A对象可以被转化为B对象指针,但是原来的A里面并没有B的_y内容, 所以出错了,出现了越界错误。

所以这里要用到dynamic_cast

比如在下面的代码中,如果pa指向的是子类对象B,那么转换可以成功,正常返回地址

如果是指向父类对象A的,转换失败,返回空指针

class A
{
public:virtual void f() {}int _x = 0;
};
class B:public A
{
public:int _y = 0;
};void fun(A* pa)
{//B* pb = (B*)pa;B* pb = dynamic_cast<B*>(pa);if (pb){cout << "转换成功" << endl;pb->_x++;pb->_y++;}else{cout << "转换失败" << endl;}
}
int main()
{A a;fun(&a);B b;fun(&b);return 0;
}

运行结果为

image-20240206020414910

它的原理其实就是做了一个标记,来进行辨认的

四、RTTI

RTTI:Run-time Type identification的简称,即:运行时类型识别。

C++通过以下方式来支持RTTI:

  1. typeid运算符 (打印类型字符串,不能用)
  2. dynamic_cast运算符
  3. decltype (可以用的类型)

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

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

相关文章

免费软件推荐-开源免费批量离线图文识别(OCR)

近期要批量处理图片转电子化&#xff0c;为了解决这个世纪难题&#xff0c;试了很多软件&#xff08;华为手机自带OCR识别、 PandaOCR、天若OCR、Free OCR&#xff09;等软件&#xff0c;还是选择了这一款&#xff0c;方便简单 一、什么是OCR? 光学字符识别&#xff08;Opt…

大模型学习 一

https://www.bilibili.com/video/BV1Kz4y1x7AK/?spm_id_from333.337.search-card.all.click GPU 计算单元多 并行计算能力强 指数更重要 A100 80G V100 A100 海外 100元/时 单卡 多卡并行&#xff1a; 单机多卡 模型并行 有资源的浪费 反向传播 反向传播&#xff08;B…

C++11新特性(一)

目录 C11简介 统一的列表初始化 变量类型推导 std::initializer_list 声明 auto decltype nullptr STL的一些变化 右值引用 右值引用和左值引用 右值引用适用场景 移动构造和移动语义 对类的影响 可变参数模板 递归函数方式展开参数包 STL容器中的empalce相…

使用Launch4j将jar包转成.exe可执行文件

Launch4j官网:Launch4j - Cross-platform Java executable wrapper 然后点击上面按钮 随便写个文件名

2024-02-08(Flume)

1.Flume 的架构和MQ消息队列有点类似 2.Flume也可以做数据的持久化操作 在Channel部分选择使用File channel组件 3.Flume进行日志文件监控 场景&#xff1a;企业中应用程序部署后会将日志写入到文件中&#xff0c;我们可以使用Flume从各个日志文件将日志收集到日志中心以便…

数据结构(C语言)代码实现(八)——顺序栈实现数值转换行编辑程序括号分配汉诺塔

目录 参考资料 顺序栈的实现 头文件SqStack.h&#xff08;顺序栈函数声明&#xff09; 源文件SqStack.cpp&#xff08;顺序栈函数实现&#xff09; 顺序栈的三个应用 数值转换 行编辑程序 顺序栈的实现测试 栈与递归的实现&#xff08;以汉诺塔为例&#xff09; 参考资…

【Leetcode】236. 二叉树的最近公共祖先

文章目录 题目思路代码结果 题目 题目链接 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。 百度百科中最近公共祖先的定义为&#xff1a;“对于有根树 T 的两个节点 p、q&#xff0c;最近公共祖先表示为一个节点 x&#xff0c;满足 x 是 p、q 的祖先且 x 的深度尽可…

[算法前沿]--058- LangChain 构建 LLM 应用详细教程

什么是LLMs? LLM,即大型语言模型,是指经过大量文本数据训练的最先进的语言模型。它利用深度学习技术来理解和生成类似人类的文本,使其成为各种应用程序的强大工具,例如文本完成、语言翻译、情感分析等。LLMs最著名的例子之一是 OpenAI 的 GPT-3,它因其语言生成能力而受到…

C语言笔试题之求出二叉树的最大深度(递归解决)

实例要求&#xff1a; 1、给定一个二叉树 root &#xff0c;返回其最大深度&#xff1b;2、二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数&#xff1b; 案例展示&#xff1a; 实例分析&#xff1a; 1、判断根节点是否为空&#xff1b;2、分别递归处理左…

containerd中文翻译系列(十九)cri插件

cri插件包含的内容比较多&#xff0c;阅读之前请深呼吸三次、三次、三次。 CRI 插件的架构 本小节介绍了 containerd 的 cri 插件的架构。 该插件是 Kubernetes 容器运行时接口&#xff08;CRI&#xff09; 的实现。Containerd与Kubelet在同一个节点上运行。containerd内部的…

1987-2022年各省进出口总额数据整理(含进口和出口)(无缺失)

1987-2022年各省进出口总额数据整理&#xff08;含进口和出口&#xff09;&#xff08;无缺失&#xff09; 1、时间&#xff1a;1987-2022年 2、来源&#xff1a;各省年鉴、统计公报 3、指标&#xff1a;进出口总额&#xff08;万美元&#xff09;、进口总额&#xff08;万美…

Vuex介绍和使用

1. 什么是Vuex Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式和库。它解决了在大型 Vue.js 应用程序中共享和管理状态的问题&#xff0c;使得状态管理变得更加简单、可预测和可维护。 在 Vue.js 应用中&#xff0c;组件之间的通信可以通过 props 和事件进行&#xff0c…

SCI 1区论文:Segment anything in medical images(MedSAM)[文献阅读]

基本信息 标题&#xff1a;Segment anything in medical images中文标题&#xff1a;分割一切医学图像发表年份: 2024年1月期刊/会议: Nature Communications分区&#xff1a; SCI 1区IF&#xff1a;16.6作者: Jun Ma; Bo Wang(一作&#xff1b;通讯)单位&#xff1a;加拿大多…

文件绕过-Unsafe Fileuoload

文件上传基础 什么是文件上传 将客户端数据以文件形式封装通过网络协议发送到服务器端&#xff0c;在服务器端解析数据&#xff0c;最终在服务端硬盘上作为真实的文件保存。 通常一个文件以HTTP协议进行上传时&#xff0c;将以POST请求发送至Web服务器&#xff0c;Web服务器…

【初中生讲机器学习】6. 分类算法中常用的模型评价指标有哪些?here!

创建时间&#xff1a;2024-02-07 最后编辑时间&#xff1a;2024-02-09 作者&#xff1a;Geeker_LStar 你好呀~这里是 Geeker_LStar 的人工智能学习专栏&#xff0c;很高兴遇见你~ 我是 Geeker_LStar&#xff0c;一名初三学生&#xff0c;热爱计算机和数学&#xff0c;我们一起加…

【原创 附源码】Flutter海外登录--Google登录最详细流程

最近接触了几个海外登录的平台&#xff0c;踩了很多坑&#xff0c;也总结了很多东西&#xff0c;决定记录下来给路过的兄弟坐个参考&#xff0c;也留着以后留着回顾。更新时间为2024年2月8日&#xff0c;后续集成方式可能会有变动&#xff0c;所以目前的集成流程仅供参考&#…

[UI5 常用控件] 08.Wizard,NavContainer

文章目录 前言1. Wizard1.1 基本结构1.2 属性1.2.1 Wizard&#xff1a;complete1.2.2 Wizard&#xff1a;finishButtonText1.2.3 Wizard&#xff1a;currentStep1.2.4 Wizard&#xff1a;backgroundDesign1.2.5 Wizard&#xff1a;enableBranching1.2.6 WizardStep&#xff1a;…

PKI - 03 密钥管理(如何进行安全的公钥交换)

文章目录 Pre密钥管理面临的挑战安全密钥管理的几种方式手动密钥交换与确认受信任的介绍 Pre PKI - 02 对称与非对称密钥算法 密钥管理面临的挑战 密钥管理面临的挑战主要包括以下几点&#xff1a; 安全的公钥交换&#xff1a;在使用基于非对称密钥算法的服务之前&#xff0c…

清理神器CleanMyMac X 空间透镜——可视化您的磁盘空间 空间透镜有什么用

不久前&#xff0c;CleanMyMac X 发布了一个新功能&#xff1a; 空间透镜 相信有非常多的小伙伴和小编一样&#xff0c; 对这个功能一脸问号 这啥玩意儿&#xff1f;&#xff1f;&#xff1f; 今天就让我们深入了解一下&#xff0c; CleanMyMac X 的空间透镜功能。 - 更好…

嵌入式单片机中晶振的工作原理

晶振在单片机中是必不可少的元器件&#xff0c;只要用到CPU的地方就必定有晶振的存在&#xff0c;那么晶振是如何工作的呢&#xff1f; 什么是晶振 晶振一般指晶体振荡器&#xff0c;晶体振荡器是指从一块石英晶体上按一定方位角切下的薄片&#xff0c;简称为晶片。 石英晶体谐…