C++:类型转换

目录

一、C语言中的类型转换

二、为什么C++要新的转换格式

三、 C++强制类型转换

1.static_cast

2.reinterpret_cast 

3.const_cast

4.dynamic_cast 


一、C语言中的类型转换

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

  1.  隐式类型转化:编译器在编译阶段自动进行,能转就转,不能转就编译失败
  2. 显式类型转化:需要用户自己处理
    void Test ()
    {int i = 1;int* p = &i;// 隐式类型转换double d = i;printf("%d, %.2f\n" , i, d);// 显示的强制类型转换int address = (int) p;printf("%x, %d\n" , p, address);
    }

注意:一般关联性强,表示的意义相近的变量可以隐式转换,如int转换成double;一般关联性不强的一些内置类型可以显示转换,比如(void*)int转换成void*;没有关联性的几乎不能转换。

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

二、为什么C++要新的转换格式

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

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

我们举个例子。

void insert(size_t pos, char ch)
{int end = 10;while (end >= pos){cout << end << endl;--end;}
}int main()
{insert(5, 'x');return 0;
}

这段代码正常插入没有问题,但当pos是0时,就会陷入“死循环”。

因为size_t是无符号整形,--变成-1,其实是size_t表示的最大无符号整数。

针对这些问题,C++提出了自己的类型转化风格,因为C++要兼容C语言,所以C++中还可以使用C语言的转化风格。

三、 C++强制类型转换

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

1.static_cast

 static_cast用于非多态类型的转换(静态转换),编译器隐式执行的任何类型转换都可用static_cast,但它不能用于两个不相关的类型进行转换。

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

2.reinterpret_cast 

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

int main()
{double d = 12.34;int a = static_cast<int>(d);cout << a << endl;// 这里使用static_cast会报错,应该使用reinterpret_cast//int *p = static_cast<int*>(a);int *p = reinterpret_cast<int*>(a);return 0;
}

3.const_cast

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

const_cast转换中有一个细节需要注意, 请看下面代码,

int main()
{const int a = 2;int* p = const_cast<int*>(&a);*p = 3;cout << a << endl;cout << *p << endl;cout << &a << endl;cout << p << endl;return 0;
}

执行,

我们发现a的地址和p的值一样,也就是p指向的空间就是a的值,可为什么p指向空间的值是3,a的值还是2呢,我们修改了*p,a的值也应该被修改了呀,a为什么没有变?

因为const修饰的变量,编译器会进行优化。正常const修饰的变量不会被修改,不会被修改就不用经常去内存中取,所以编译器会把const修饰的值拷贝到寄存器,甚至用常量去替代,所以我们正常修改const_cast强制类型转换“去const属性的指针”,不会修改到内存中的const变量。

要想禁止编译器对const变量的优化,可以在const前加一个关键字——volatile。

cv (const and volatile) type qualifiers - cppreference.com

 这样我们运行,就可以修改到内存中a的值了,那么为什么a的地址是1呢?这是流插入(<<)的bug。

注意:流插入<<是指将程序中的数据插入到流中,输出到屏幕或文件中或者其他流中;流提取>>是从流中提取数据到程序中。

我们可以用printf打印看一下,

printf打印就没有问题,难道是类型识别错误,我们打印一下&a的类型。

 发现&a的类型是int const valatile*,

ostream::operator<< - C++ Reference (cplusplus.com)

可能编译器类型匹配错了,应该要匹配成指针类型的模板参数。我们强制转换成指针类型看一下,

int* 和void*都可以,看来就是模板匹配错误。

 其实流插入还有一个缺点,就是当我们想打印字符的地址时,operator会匹配到字符类型的参数,从而打印的是字符而不是字符地址。

所以这里也必须强转一下, 以上三种转换都是C++对C语言转换的规范。

4.dynamic_cast 

dynamic_cast用于将一个父类对象的指针/引用转换为子类对象的指针或引用(动态转换)

  • 向上转型:子类对象指针/引用->父类指针/引用(不需要转换,赋值兼容规则)
class A
{
public:virtual void f() {}int _a = 0;
};
class B : public A
{
public:int _b = 1;
};
int main()
{B objb;A obja = objb;A& ra = objb;//直接将objb继承的部分切割给ra,中间没有类型转换double d = 1.1;const int& i = d;//隐式类型转换,中间产生临时变量,要const修饰return 0;
}
  • 向下转型:父类对象指针/引用->子类指针/引用(用dynamic_cast转型是安全的)

父类的指针或引用,转换成子类的指针和引用,如果再用子类指针或引用访问子类独有的成员变量,就会造成越界访问,如下:

class A
{
public:virtual void f() {}int _a = 0;
};
class B : public A
{
public:int _b = 1;
};
void fun(A* pa)
{//  向下转换:直接转换是不安全的// 如果pa是指向父类A对象,存在越界问题B* ptr = (B*)pa;ptr->_a++;ptr->_b++;//越界访问
}int main()
{// 向下转换规则:父类对象不能转换成子类对象,但是父类指针和引用可以转换子类指针和引用A a;B b;fun(&a);fun(&b);return 0;
}

为了防止这类越界,C++推出了dynamic_cast用于将一个父类指针或引用转换。

dynamic_cast conversion - cppreference.com

class A
{
public:virtual void f() {}int _a = 0;
};class B : public A
{public:int _b = 1;
};void fun(A* pa)
{//  向下转换:直接转换是不安全的// 如果pa是指向父类A对象,存在越界问题B* ptr = dynamic_cast<B*>(pa);if (ptr){ptr->_a++;ptr->_b++;}else{cout << "转换失败" << endl;}
}int main()
{// 向下转换规则:父类对象不能转换成子类对象,但是父类指针和引用可以转换子类指针和引用A a;B b;fun(&a);fun(&b);return 0;
}

调试一下可以看到,向上转换时,程序输出,

对于指针类型向上转换会返回空指针,对于引用类型向上转换dynamic_cast会抛异常std::bad_cast 。

当然,这有一个前提,“表达式是对多态类型 Base 的指针或引用,并且 target-type 是对 Derived 类型的指针或引用,则执行运行时检查”。

四、RTTI

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

五、参考

ttps://blog.csdn.net/hp_cpp/article/details/104095700

RTTI、虚函数和虚基类的实现方式、开销分析及使用指导 (baiy.cn)

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

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

相关文章

【高阶数据结构】B-数、B+树、B*树的原理

文章目录 B树的概念及其特点解析B树的基本操作插入数据插入数据模拟 分析分裂如何维护平衡性分析B树的性能 B树和B*树B树B树的分裂B树的优势 B*B*树的分裂 总结 B树的概念及其特点 B树是一颗多叉的平衡搜索树&#xff0c;广泛应用于数据库和 文件系统中&#xff0c;以保持数据…

等保2.0的具体技术要求有哪些重点?

在数字化浪潮汹涌澎湃的当下&#xff0c;网络安全犹如一座守护智慧之城的巍峨城墙&#xff0c;不可或缺。等级保护制度&#xff08;等保&#xff09;作为我国网络安全战略的基石&#xff0c;历经岁月沉淀&#xff0c;已演进至2.0时代&#xff0c;即《网络安全等级保护基本要求》…

算法思想总结:优先级队列

一、最后一块石头的重量 . - 力扣&#xff08;LeetCode&#xff09; 我们每次都要快速找到前两个最大的石头进行抵消&#xff0c;这个时候用优先级队列&#xff08;建大堆&#xff09;,不断取堆顶元素是最好的&#xff01;每次删除堆顶元素后&#xff0c;可以自动调整&#xf…

HarmonyOS - 通过.p7b文件获取fingerprint

1、查询工程所对应的 .p7b 文件 通常新工程运行按照需要通过 DevEco Studio 的 Project Structure 勾选 Automatically generate signature 自动生成签名文件&#xff0c;自动生成的 .p7b 文件通常默认在系统用户目录下. 如&#xff1a;C:/Users/zhangsan/.ohos/config/default…

JavaSE面试题(二)

目录 一.为什么会有Java内存模型&#xff1f; 二.什么样的情况下finally不会执行 三.钩子是什么&#xff1f; 四.编译时期的多态性和运行时期的多态性 五.谈谈反射机制 六.Java管道 本专栏全是博主自己收集的面试题&#xff0c;仅可参考&#xff0c;不能相信面试官就出这…

TCP报文校验和(checksum)计算

一. 原理 将TCP相关内容&#xff08;TCP伪头部TCP头部TCP内容&#xff09;转换成16比特的字符&#xff0c;然后进行累加&#xff0c;最后结果进行取反。TCP伪头部是固定的&#xff0c;下文有相关代码展示。 二. 源码 源码 #include <stdio.h> #include <stdlib.h&…

3D鸡哥又上开源项目!单图即可生成,在线可玩

大家好&#xff0c;今天和大家分享几篇最新的工作 1、Unique3D Unique3D从单视图图像高效生成高质量3D网格&#xff0c;具有SOTA水平的保真度和强大的通用性。 如下图所示 Unique3D 在 30 秒内从单视图野生图像生成高保真且多样化的纹理网格。 例如属于一张鸡哥的打球写真照 等…

js 递归调用 相同对象--数组递归调用

<div class="save-cl"> <a-button @click="saveCl" >保存为常用策略</a-button> </div> saveCl(){ console.log(this.form.filterList[0],--------常用策略)// 此对象为上图对象 console.log(this.allElementsHaveValue(thi…

Windows的管理工具

任务计划程序&#xff1a;这是一个用来安排任务自动运行的工具。你可以在这里创建新的任务&#xff0c;设定触发条件&#xff0c;并指定任务的操作。 事件查看器&#xff1a;这是一套日志记录和分析工具&#xff0c;&#xff0c;你可以了解到系统的工作状况&#xff0c;帮助诊…

损失函数篇

损失函数 1、边界框损失函数/回归损失函数bbox_loss 2、分类损失函数cls_loss 3、置信度损失函数obj_loss YOLOv8损失函数 1、概述 通过YOLOv8-训练流程-正负样本分配的介绍&#xff0c;我们可以知道&#xff0c;经过预处理与筛选的过程得到最终的训练数据&#xff1a; a…

2024 年最佳 Figma 字体

字体不仅仅是文本字符&#xff0c;它们还塑造了用户体验。从引导用户浏览界面到传达品牌个性&#xff0c;字体对于设计​​至关重要。然而&#xff0c;找到适合您的网站或应用风格的完美字体可能具有挑战性。 但不要害怕&#xff0c;我们会帮助您&#xff01;请继续关注&#x…

C语言 指针和数组——指针的算术运算

目录 指针的算术运算 指针加上一个整数 指针减去一个整数 指针相减 指针的关系比较运算 小结 指针的算术运算 指针加上一个整数 指针减去一个整数 指针相减 指针的关系比较运算 小结  指针变量 – 指针类型的变量&#xff0c;保存地址型数据  指针变量与其他类型…

负载均衡(服务器)

vi /etc/sysconfig/network-scripts/ifcfg-ens33 systemctl restart network 防火墙 systemctl stop firewalld systemctl disable firewalld vi /etc/selinux/config setenforce 0 yum install gcc gcc-c mkdir /lnmp cd /lnmp/ tar -zxvf zlib-1.2.12.tar.gz tar -zxv…

[C++][CMake][CMake基础]详细讲解

目录 1.CMake简介2.大小写&#xff1f;3.注释1.注释行2.注释块 4.日志 1.CMake简介 CMake是一个项目构建工具&#xff0c;并且是跨平台的 问题 – 解决 如果自己动手写Makefile&#xff0c;会发现&#xff0c;Makefile通常依赖于当前的编译平台&#xff0c;而且编写Makefile的…

vue的学习--day3

1、尝试使用json文件模拟增删改查 json server:准备一份自己的数据&#xff08;这里我用的是老师给的&#xff09;。 转到d盘&#xff0c;然后打开json文件&#xff1a; 下面模拟增删改查&#xff1a; 借助工具postman或apifox或apipost&#xff1a; 这里我下载了apifox&…

产品公告 | MemFire Cloud 现已支持微信授权登录,为移动应用带来更便捷的认证服务

MemFire Cloud推出的“开箱即用”的后端服务&#xff0c;提供了云数据库、身份验证与授权、云存储、静态托管、实时realtime、自动生成API等功能&#xff0c;本次升级新增/优化功能如下&#xff1a; 标题微信授权登录&#xff08;移动应用&#xff09; 为了顺应国内用户的使用…

Elasticsearch:Ingest architectures - 摄取架构

我们提供各种采集架构&#xff0c;以满足各种用例和网络配置的需求。 要将数据采集到 Elasticsearch&#xff0c;请使用最符合你的需求和用例的选项。对于许多用户和用例来说&#xff0c;最简单的方法是使用 Elastic Agent 采集数据并将其发送到 Elasticsearch。Elastic Agent…

深度学习——深度学习中感受野的计算

感受野 在卷积神经网络&#xff08;CNN&#xff09;中&#xff0c;感受野&#xff08;Receptive Field&#xff09; 是一个非常重要的概念。它描述了网络中某一层的输出&#xff08;通常是特征图上的一个像素点&#xff09;所对应的输入图像上的空间范围。这个范围代表了该输出…

SARscape——地理编码与辐射定标

目录 一、算法原理1、概述2、参考文献 二、软件操作三、结果展示1、原始图像2、处理结果 一、算法原理 1、概述 SAR系统观测到的是电磁波入射地球表面后反射&#xff08;后向散射&#xff09;的雷达脉冲的强度和相位信息。这个信息编码到雷达坐标系统下&#xff0c;即斜距坐标…

数据结构之二叉树概念

数据结构之二叉树 二叉树简介分类普通二叉树平衡二叉树满二叉树二叉搜索树&#xff08;二叉排序树、二叉查找树&#xff09;&#xff0c;平衡二叉树红黑树 B树类型B树&#xff08;B-树、B_树&#xff09;B树B*树 二叉树 简介 二叉树(Binary Tree) &#xff1a;是一种非常重要…