c++ 类型的转换

news2024/10/5 20:25:25

文章目录

    • 1. C语言中的类型转换
      • 1.1 隐式转换
      • 1.2 显示转换
    • 2. C++的类型转换
      • 2.1 static_cast
      • 2.2 reinterpret_cast
      • 2.3 const_cast
      • 2.4 dynamic_cast
    • 3. 常见面试题

前言: C++给出了四种类型转换,这是做出的一些规范,为了减少隐式转换。隐式转换的问题,考虑清楚还行,就怕你考虑不清楚,然后悄咪咪的发生了隐式转换,让人头疼。所以 如果要进行类型转换,最好用 C++给出的方式,这样代码更清晰易懂。


1. C语言中的类型转换

先讲讲C语言中的类型转换,因为C++兼容C嘛。其实 要想规避 隐式转换的发生,最简单的方式 就是 将隐式转换 这一机制删除掉。但是 这样做 相当于大改,这样的事情 也就是python敢做。所以 C++还是 支持了C中的类型转换方式。

需要类型转换的情况:

  • 赋值,比如 float a = 2; 2是int整型,它会发生类型转换成float 再赋值给 a(隐式)。
  • 算术运算符两边的类型不同,不能比较,发生隐式转换。
  • 函数传参,形参和实参类型不同。
  • 函数返回值,被接收的类型与返回的类型不同,这种情况。

总共有两种 类型转换方式:

  • 隐式转换
  • 显示转换

1.1 隐式转换

发生隐式转换的都是类型相接近的,就是一个隐式的类型提升。

隐式转换的目的是 让两个类型 变为同一个类型,方便赋值,比较之类的事;赋值只能是同类型之间的赋值,如果是不同类型,那么就得是 发生隐式转换。

隐式转换的原则是:低的 转化 高的。

我画一幅图,帮助理解:

在这里插入图片描述
我写一个代码:可能 坑过初学C语言的我们

int main()
{
    int a = 10;
    size_t i = 0;

    while (a >= i)
    {
        printf("%d\n", a);
        a--;
    }

	return 0;
}

它是像要打印 10 到 0。 如果 i是一个整型int,那么这个代码是没问题的;关键 i是一个 size_t 。i和a进行比较时,a发生隐式转换为 size_t 。而且我们知道 size_t 是永远大于 0的。所以造成了死循环。

a本来减到 -1 就该停止,但是发生隐式类型转换,-1 转换为 size_t 变成了很大的整数,于是乎 永远大于0嘛。

看看结果:

在这里插入图片描述

还有赋值的时候,也会存在隐式转换:

float a = 1;

同类型之间才能相互赋值,因为 1是一个整型,所以先发生隐式类型转换为浮点型,再复制给a。

在这里插入图片描述


1.2 显示转换

这就是强制类型转换,在C语言中很常用。有很多 骚操作,都利用的这个显示转换。但是也会造成不少的bug。

就是显示的发生转换:

    int a = 10;
	int* p = &a;
	int m = (int)p;

当然上面这种玩法比较的挫,很少这样用。

比如:Linux中thread_create()函数的最后一个参数是 void* 它可以接收任意类型的指针。然后进入 线程执行的函数里,通过强转 可以转为 自己想用的类型。

我简单的写段代码:

void run(void* v)
{
	printf("%s\n", (char*)v);
}

int main()
{
   
	run("good");
	
	return 0;
}

这就有点意思了,通过显示类型转换 还有很多骚操作,还有那个system V方案里对 共享内存,消息队列,信号量 用同一个 结构体指针接收。这个也有意思,这是我了解最早的切片操作。

我还是给段代码,让大家 看看 大神 是如何利用 一种结构体指针 ,管理三个不同结构体:

struct con
{
	int m;
};

struct A
{
	struct  con m;
	int a;
};

struct B
{
	struct  con m;
	int b;
};

struct C
{
	struct  con m;
	int c;
};

int main()
{
    // 定义三个结构体
	struct A ma;
	struct B mb;
	struct C mc;
	// 用同一种类型的结构体 指向它们
	struct con* p1 = &ma;
	struct con* p2 = &mb;
	struct con* p3 = &mc;
	// 如果具体要用某个结构体,可以通过强转,转回原来类型
	struct A* pa=(struct A*)p1;
	
	return 0;
}

为什么能这样操作?很明显 每个结构体 的开头都是 struct con类型的结构体。然而指针指向结构体的开头位置,所以能用 struct con类型指针 指向 那三个结构体。

有用嘛?这个东西? 肯定是有用,比如 要统一化接口。因为之前 C语言没有函数重载,也没有模板。对吧,所以 这个东西 还是 不错的。有人这里可能还不太理解,我简单给个代码:

void run(struct con* p);

这个函数 是不是就可以 接收 struct con* 类型的指针,然后 struct con* 又可以指向 A,B,C 三种类型的结构体。就相当于 可以接收不同类型的指针。然后 具体 要怎么操作,进了 函数 自己去控制,比如 具体强转 成 那种类型呀之类的。


2. C++的类型转换

C++是兼容C的,所以上面的类型转换,C++都有。但是 发现隐式转换 有点 悄咪咪的。不好被发现。要么就明着来嘛,不是还可以显示的强制类型转换嘛?但是 显示的强制类型转换在某些情况下,也不能瞎转换,有可能转换不成功。或者转换成功了,但是不安全。

于是 C++ 就 对类型转换 做出一定的规范。

C++给出四种类型转换的方式:

  • static_cast、
  • reinterpret_cast、
  • const_cast、
  • dynamic_cast

2.1 static_cast

这是用于相近类型的转换,也就是之前所有的隐式转换都可以用 staic_cast 转换。

	int a = 10;
	float f = static_cast<int>(a);

使用起来也很简单对吧。之前那个死循环问题,一般情况不容易被发现,但是 你如果 用这个进行转换,就很明了的 看出问题所在。

int a =10;
size_t i=0;
while(static_cast<size_t> a >= i)
{
;
}

嗯,size_t 类型永远大于 0 ,所以死循环。


2.2 reinterpret_cast

这是不相近类型的转换,这个转换 很强势。无视种族,可以随便转换,就类似于强制转换。多用于不同类型指针之间的转换。这是它的主要用途。

比如:

int add(int x, int y)
{
	return x + y;
}

int main()
{

int (*p)(int, int) = &add;
int m = reinterpret_cast<int>(p); // 等价于 int m =(int)p;

return 0;
}

看到了吧,把一个函数指针,赋值给了 整型。但是这样的操作没有意义。

它主要用于 指针之间的相互转换,就是 我之前给大家演示的那个代码:

void run(void* v)
{
	printf("%s\n", (char*)v);
}

int main()
{
   
	run("good");
	
	return 0;
}

上面是利用的强制转换嘛,如果在c++中,我们推荐使用reinterpret_cast 进行转换。

void run(void* v)
{
    char* s = reinterpret_cast<char*> v;
	printf("%s\n", s);
}

错误使用reinterpret_cast 会使得 程序变得不安全。

正确的使用方式:

利用reinterpret_cast 转换成可以接收的类型;再使用 reinterpret_cast 将其转换回 原来的类型。
当然 如果是 void* 接收,那么 就省略第一步。


2.3 const_cast

这是用于取出const属性的。const属性可以通过指针去除掉,用这个注意是起到了一个提示作用,表示 我要去除 某某的 const属性。

比如:

这是C语言的去除const属性方式,但是 在C++中加入了检查机制,const 变量只能赋值给const属性的指针。

const int a = 10;
int* p = &a;
*p = 2;

我们再来看c++去除const属性的方式:

    const int a = 10;
	int* p = const_cast<int*>(&a);

	*p = 2;
	cout << a << endl;

我们来看一下结果:

在这里插入图片描述
发现a 还是 10,这是打印结果,其实底层a 已经变为 2。打印结果还是10的原因来自于 编译器的优化,因为是const属性 ,编译器默认它不会变了。如果不想让编译器优化可以加上 关键字 volatile修饰变量。

我先通过调试 看一下 a到底变没:

在这里插入图片描述
在这里插入图片描述
很明显变了,再加上volatile修饰变量,阻止编译器优化。

volatile const int a = 10;

看打印结果:

在这里插入图片描述


2.4 dynamic_cast

它是用于检查 父类子类之间的赋值的。

  • 父类 可以 赋值给父类;
  • 子类 可以 赋值给子类;
  • 父类 不可以 赋值给子类;
  • 子类 可以 赋值给父类,这是切片嘛;

使用条件:

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

第一个条件表明立场:处理多态时,指针的转换,用它更安全,以及它的参数都是类指针或类地址
第二个条件 是 它的返回机制。

我举个例子:

class A
{
	virtual void f() {}
public:
};

class B : public A
{
public:
	int _b = 0;
};

void func(A* pa)
{

	B* pb1 = dynamic_cast<B*>(pa);
	if (pb1 == nullptr)
	{
		cout << "转换失败" << endl;
	}
	else
	{
		cout << "pb1:" << pb1 << endl;
		pb1->_b++;
	}
}
int main()
{
    A aa;
	B bb;
	func(&aa);
	func(&bb);
	return 0;
}

看运行结果:

在这里插入图片描述

也就是说 dynamic_cast是有检查机制,它可以判断出 能不能转换成功。如果我们这里用
static_cast 直接就转换了,它不会做检查的。

本来 父类是不能够 赋值给子类的,但是用 static_cast ,reinterpret_cast 或者是直接强转 都是可以完成 父类 赋值给 子类。肯定这样做 是不太安全的。

    A aa;
	B* bb;
	///
	bb = (B*)&aa;
	B* pb1 = static_cast<B*>(&aa);
	B* pd2 = reinterpret_cast<B*>(&aa);

所以为了 规范这一点,给出了 dynamic_cast。它发现 不能赋值过去,就直接返回 0,而且也不会做 赋值操作。非常安全。


3. 常见面试题

  1. C++中的4中类型转化分别是:
  2. 说说4中类型转化的应用场景。

答案就不给了,大家自由发挥。


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

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

相关文章

JDK19都出来了~是时候梳理清楚JDK的各个版本的特性了【JDK14特性讲解】

JDK各个版本特性讲解-JDK14特性 一、Java14概述 Oracle在2020年3月17日宣布JAVA14 全面上市,JAVA14通过每六个月发布一次新功能,为企业和开发人员社区提供增强功能,继续了Oracle加快创新的承诺. 最新的JAVA开发工具包提供了新功能,其中包括两项备受期待的新预览功能,实例匹配的…

【Redis】分布式限流与Redis实现限流的四种方式(Redis专栏启动)

&#x1f4eb;作者简介&#xff1a;小明java问道之路&#xff0c;专注于研究 Java/ Liunx内核/ C及汇编/计算机底层原理/源码&#xff0c;就职于大型金融公司后端高级工程师&#xff0c;擅长交易领域的高安全/可用/并发/性能的架构设计与演进、系统优化与稳定性建设。 &#x1…

MHA高可用及故障切换

目录 一、MHA的概述 1、 MHA的概念 2&#xff0e;MHA 的组成 3&#xff0e;MHA 的特点 二、搭建 MySQL MHA的操作步骤 1、实验思路&#xff1a; 1.1、MHA架构 1.2、故障模拟 2、环境准备 3、修改 Master、Slave1、Slave2 节点的主机名 4、修改 Master、Slave1、Slav…

基于java+springboot+mybatis+vue+mysql的农产品销售商城网站

项目介绍 农产品销售成为了很多农产品商家的一项重要的创收项目。传统的销售方式采用了摆摊销售和市场的农产品的批发&#xff0c;在销售农产品方面不是很方便&#xff0c;因此采用了网络的方式进行销售。也是电子商务发展今天的一种趋势&#xff0c;建立了网站&#xff0c;前…

WebSocket——SpringBoot+Vue3+TS+SockJS+STOMP简单连接使用

WebSocket——SpringBootVue3TSSockJSSTOMP简单连接使用本文视频以及相关资源关于WebSocket文档什么时候使用WebSocketWebSocket连接头服务器返回状态码客户端使用技术Github地址npm安装为什么要安装sockjs的.d.ts文件常用方法1.连接2.关闭连接3.发送消息4.设置订阅构建1.创建w…

【Redis】Redis布隆过滤器工作原理与实战(Redis专栏启动)

&#x1f4eb;作者简介&#xff1a;小明java问道之路&#xff0c;专注于研究 Java/ Liunx内核/ C及汇编/计算机底层原理/源码&#xff0c;就职于大型金融公司后端高级工程师&#xff0c;擅长交易领域的高安全/可用/并发/性能的架构设计与演进、系统优化与稳定性建设。 &#x1…

判断质数复习与分解质因数

TIPS 1. 之前我们讲到比方说要得到一个整数&#xff08;十进制形式&#xff09;的每一位数字&#xff0c;就需要把这个整数先%10得到最低位的数字&#xff0c;然后呢再把这个整数/10更新一下&#xff0c;然后呢就这么循环往复直到最终这个不断更新的整数为0跳出循环。那如果这…

深度学习-神经网络(Pytorch应用)

文章目录简介卷积层池化层激活层线性层前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。 简介 上图摘自网络 受生物神经网络的启发&#xff0c;每个神经元与其他神经元相连&#xff0c;当…

InfluxDB时序数据库-笔记-更新中

本笔记由在Bilibili学习的尚硅谷教学视频后整理得来 文章目录一、InfluxDB基础概念1.1InfluxDB是什么1.2为什么选择InfluxDB而不选择关系型数据库1.3下载安装1.4Promethus数据格式1.5时序数据中的数据模型1.6与时间标准相关的知识二、Flux语言2.1初识Flux语言2.2Flux基本语法三…

献上SpringBoot核心笔记,简直就是血赚

市面上真正适合学习的SpringBoot资料太少&#xff0c;有的书或资料虽然讲得比较深入&#xff0c;但是语言晦涩难懂&#xff0c;大多数人看完这些书基本都是从入门到放弃。学透SpringBoot难道就真的就没有一种适合大多数同学的方法吗&#xff1f; 很多SpringBoot相关的知识点与…

Java+MySQL学生选课与成绩管理系统(数据库版)

一、项目要求 学生选课与成绩管理系统(数据库版) 1、配置MySQL数据库 2、利用java+MySQL实现系统各个功能 3、功能包括: (1)系统交互界面,如下图所示 (2)学生信息的增删改查。学生属性包括学号(唯一),姓名,专业。 (3)课程信息的增删改查。课程属性包括编号…

96-114-mysql-高级篇-架构篇

96-mysql-高级篇&#xff1a; 推荐网站 mysql学习网站&#xff1a;https://www.bilibili.com/video/BV1iq4y1u7vj?p109&vd_source39a1ba1654411bc9ab90f6f2926600b7 mysql&#xff1a;https://dev.mysql.com/doc/refman/8.0/en/ 算法&#xff1a;https://www.cs.usfca.…

JoyStick Shield连接Nokia 5110--Arduino

SpaceTrash游戏是一个简单的射击游戏&#xff0c;您可以在其中控制宇宙飞船&#xff0c;并通过移动或爆破&#xff08;使用激光&#xff09;来避免漂浮在周围的小行星的碰撞。该游戏是u8g2图形库附带的示例&#xff0c;该图形库通常用于连接具有SPI或I2C协议的各种单色8位显示器…

非零基础自学Golang 第1章 走进Go 1.4 常用软件及网站 1.5 小结

非零基础自学Golang 文章目录非零基础自学Golang第1章 走进Go1.4 常用软件及网站1.4.1 开发工具【IDE 或编辑器】1.4.2 代码管理工具1.4.3 其他工具1.4.4 网站资源1.5 小结第1章 走进Go 1.4 常用软件及网站 1.4.1 开发工具【IDE 或编辑器】 工欲善其事必先利其器。 Go开发工…

基于java+springboot+mybatis+vue+mysql的电动车辆充电桩管理系统

项目介绍 随着我国汽车行业的不断发展&#xff0c;电动汽车已经开始逐步的领导整个汽车行业&#xff0c;越来越多的人在追求环保和经济实惠的同时开始使用电动汽车&#xff0c;电动汽车和燃油汽车最大的而不同就是 需要充电&#xff0c;同时我国的基础充电桩也开始遍及了大多数…

机器人开发--SICK激光雷达系列

机器人开发--SICK激光雷达系列1 介绍2 TiM系列2.1 TiM510参数测量方法协议请求一次数据&#xff08;应答直接携带数据&#xff09;请求持续数据&#xff08;应答收到指令&#xff0c;数据持续发送&#xff09;数据内容参考1 介绍 SICK 是国际先进的工业用传感器应用程序解决方…

016 | 乡村振兴战略下农村宅基地有偿退出现状 | 大学生创新训练项目申请书 | 极致技术工厂

&#xff08;一&#xff09;研究目的 随着国家城市化进程的加快&#xff0c;城市和农村的具体情况都发生了巨大变化&#xff0c;大量的问题随之产生&#xff0c;在农村经济中&#xff0c;宅基地的有偿退出矛盾尤为突出。在当前的时代发展背景下&#xff0c;我国农村宅基地有偿…

[附源码]Python计算机毕业设计宠物寄养平台设计Django(程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程 项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等…

git cherry-pick命令用法详解

1、需求背景 想在某个稳定版本上&#xff0c;添加一个刚开发完成的版本中的功能。就可以使用 Cherry-pick 命令&#xff0c;将这个功能相关的 commit 提取出来&#xff0c;合入稳定版本的分支上。 对于多分支的代码库&#xff0c;将代码从一个分支转移到另一个分支是常见需求。…

VS2015+PCL1.8.1安装

VS2015+PCL1.8.1安装 一、下载PCL1.8.1二、PCL-1.8.1安装三、系统环境变量配置四、 vs2015配置PCL1.8.1一、下载PCL1.8.1 1.1 首先下载pcl1.8.1的库,地址在这:pcl1.8.1库链接。   1.2 打开网站,将页面拉到最下面,下载这个两个文件:PCL-1.8.1-AllInOne-msvc2015-win64.ex…