初识C++ · 类型转换

news2024/9/19 22:31:26

目录

前言:

1 C++中的类型转换

1.1 static_cast

1.2 reinterpret_cast

1.3 const_cast

1.4 dynamic_cast


前言:

C++可以说是恨死了隐式类型转换,你可能会疑问了,为什么?不是单参数隐式类型转换为自定义类型的时候人们还是津津乐道的吗?但是当我们模拟首先插入的某种情况的时候,可能会写这种代码:

int main()
{
	size_t a = 100;
	while (a >= 0)
	{
		//...
		a--;
	}

	return 0;
}

这段代码就是个死循环,因为a最后变成了-1,然后就隐式类型转为为size_t类型,最后一直重复,直到系统崩溃。C++委员会呢,可能是深受其害,所以打算在类型转换上动点东西。

在此之前,我们先看简单看看C语言的类型转换,因为很简单,这里看一段代码咱们就过:

int main()
{
	char ch = 'a';
	int a = ch;

	int* pa = &a;
	int b = (int)pa;

	return 0;
}

C语言里面类型转换分为隐式的类型转换,指类型关联性有点强的,比如int 和 char,可以类型转换,那么指针和int之间的关联性就没那么强了,但是还是有,所以只能强制转换,不能隐式类型转换,这是C语言里面的类型转换,现在我们来看C++里面的类型转换。


1 C++中的类型转换

C语言为了兼容C语言,所以C语言中的类型转换也可以使用,但是呢,C++为了有自己的一套体系,引入了四个关键字,分别表示为不同的类型转换。

1.1 static_cast

static_cast用于非多态变量之间的隐式类型转换,即同C语言的int转为char类型一样,使用隐式类型转换的时候可以加上这个关键字:

int main()
{
	int a = 2;
	double b = 1.1;
	a = static_cast<int>(b);
	cout << a << " " << b << endl;
	return 0;
}

只要是C语言里面能够隐式类型转换的,它都可以使用,同样的,强转什么的就不可以了。

1.2 reinterpret_cast

reinterpret_cast的第一个英文单词的意思是重新解释,也就是这个关键字用于C++里面的强制类型转换,比如指针转为int:

int main()
{
	int* pa = &a;
	int pb = reinterpret_cast<int>(pa);
	printf("%X\n", pb);
	return 0;
}

这里直接cout打印是不行的,因为默认十进制打印,打印出来就是负数了,这里使用static_cast就会报错了,因为条件不符合。

1.3 const_cast

const_cast就很显然已然了,取消对象的const属性,比如非const对象赋值给const对象可以,const对象赋值给非const对象就不可以了。

int main()
{
	int c = 0;
	const int cc = 1;
	c = cc;
	c = const_cast<int>(cc);

	const int d = 0;
	int* pd = const_cast<int*>(&d);

	return 0;
}

这里有一个要注意的是const_cast里面的类型只能是指针或引用,其他内置类型不可以。

那么这里提问,const修饰的对象真的不能被修改吗?

虽然不能直接被修改,但是可以间接的修改:

int main()
{
	const int a = 0;
	int* pa = const_cast<int*>(&a);
	*pa = 1;
	cout << *pa << endl;
	cout << a << endl;
	return 0;
}

这里就可以进行打印了,但是你会发现,打印*pa的时候还是正常的,打印a就不对了,这时候你带有疑惑的打开了监视窗口:

你又疑惑的发现a已经被修改了,于是你又疑惑的打开了汇编代码:

你发现编译器很暴力的给了a一个0,这时候你明白了,这时编译器的行为,但是怎么样才能做到a打印出来也是0呢?

只需要一个关键字:

int main()
{
	volatile const int a = 0;
	int* pa = const_cast<int*>(&a);
	*pa = 1;
	cout << *pa << endl;
	cout << a << endl;

	return 0;
}

 volatile,英文意思是不稳定的,所以即便被const修饰,间接修改就被允许了,编译器看到关键字就直到,哦,你要修改啊!但是即便被volatile修饰了,也不能直接修改a,只能间接的修改。

1.4 dynamic_cast

在了解这个关键字之前,我们先来想一个问题:

既然单参数可以隐式类型转换为自定义类型,那么自定义类型能不能转换为内置类型呢?

class A
{
public:
	A(int a, int b = 0)
		:_a1(a)
		,_b1(b)
	{}
	operator int()
	{
		return _a1 + _b1;
	}

private:
	int _a1 = 2;
	int _b1 = 3;
};
int main()
{
	A a1 = 1;
	int a = a1;
	cout << a1 << endl;

	return 0;
}

这里的语法类型也是有点怪的,operator + 返回值,这样就可以实现自定义类型往内置类型走了,有了内置类型隐式转换为自定义类型,也有自定义类型转为内置类型,那么,有没有自定义类型转为自定义类型呢?

当然可以,比如,构造函数用另一个类型构造即可:

class A
{
public:
	A(int a, int b = 0)
		:_a1(a)
		,_b1(b)
	{}
	operator int()
	{
		return _a1 + _b1;
	}
	int get() const
	{
		return _a1 + _b1;
	}
private:
	int _a1 = 2;
	int _b1 = 3;
};
class B
{
public:
	B(const A& aa)
		:_b2(aa.get())
	{}
private:
	int _b2;
};
int main()
{
	A a1 = 1;
	B bb = a1;
	return 0;
}

现在进入整体,什么是dyniamic_cast,这个关键字是多态对象特有的,并且要保证虚函数已经重写,一般应用的情况是父子类关系指针之间的转换

一般派生类指针赋值给基类指针是没有问题的,但是基类指针赋值给派生类指针就会有问题了,比如:

class A
{
public:
	virtual void Func()
	{
		cout << "1" << endl;
	}

private:
	int _a;
};
class B : public A
{
public:
	virtual void Func()
	{
		cout << "2" << endl;
	}
private:
	int _b;
};

void Fun(A* aa)
{
	B* pb = (B*)aa;
	pb->Func();
}

父子类之间赋值分为两种,一种是子给父,这时没问题的,一种是父给子,这个是有问题的,比如我们现在的代码里面,调用是没有问题的,但是一旦涉及访问私有的成员变量就会出错了:


void Fun(A* aa)
{
	B* pb = (B*)aa;
	pb->Func();
	pb->_b++;
}

解决方案就是使用这个关键字,一旦发现aa指向的对象是子类,就会返回空:

void Func(A* aa)
{
	B* pb = dynamic_cast<B*>(aa);
	if (pb)
	{
		pb->Func();
		pb->_b++;
	}
	else
	{
		cout << "失败" << endl;
	}
}

简单了解一下RTTI:

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

C++通过以下方式来支持RTTI: 1. typeid运算符 2. dynamic_cast运算符 3. decltype


感谢阅读!

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

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

相关文章

苹果笔记本电脑可以玩steam游戏吗 MacBook支持玩steam游戏吗 在Steam上玩黑神话悟空3A大作 苹果Mac怎么下载steam

游戏是生活的润滑剂&#xff0c;越来越多的用户开始关注Mac平台上可玩的游戏。幸运的是&#xff0c;Steam作为最大的数字发行平台之一&#xff0c;提供了大量适用于Mac操作系统的游戏。无论你是喜欢策略、冒险还是射击类游戏&#xff0c;都能在Steam上找到适合自己Mac设备玩耍的…

ESP32CAM人工智能教学18

ESP32CAM人工智能教学18 获取数据并显示 如果我们给ESP32Cam外挂一些传感器&#xff08;比如温湿度传感器、超声波测距传感器、红外人体传感器等&#xff09;&#xff0c;我们怎么把ESP32Cam捕获到的数据&#xff0c;传递到客户端的浏览器&#xff0c;并在网页index.html中显示…

WordPress网站速度优化

提升网站速度对用户体验和搜索引擎排名至关重要。无论你是新手博主&#xff0c;还是经验丰富的网站开发人员&#xff0c;要想优化WordPress网站&#xff0c;需要长时间的努力和坚持。以下是按入门、中级和专家级别介绍的12个实用方法&#xff0c;帮助你提升WordPress网站的整体…

zabbix监控进程,日志,主从状态和主从延迟

自定义监控进程 使用httpd服务为例&#xff0c;监控httpd的进程 在zabbix-agent上安装httpd yum -y install httpd 重启httpd systemctl restart httpd systemtctl enable httpd 查看httpd的进程 [rootzabbix-agent ~]# ps -ef | grep httpd root 2407458 1 0 16:…

soapui调用接口参数传递嵌套xml,多层CDATA表达形式验证

1.环境信息 开发工具&#xff1a;idea 接口测试工具&#xff1a;soapui 编程语言&#xff1a;java 项目环境&#xff1a;jdk1.8 webservice&#xff1a;jdk自带的jws 处理xml&#xff1a;jdk自带的jaxb 2.涉及代码 package org.example.webdemo;import javax.jws.WebMethod; i…

Python,Spire.Doc模块,处理word、docx文件,极致丝滑

Python处理word文件&#xff0c;一般都是推荐的Python-docx&#xff0c;但是只写出一个&#xff0c;一句话的文件&#xff0c;也没有什么样式&#xff0c;就是36K。 再打开word在另存一下&#xff0c;就可以到7-8k&#xff0c;我想一定是python-docx的问题&#xff0c;但一直没…

汽车免拆诊断案例 | DAF(达富)汽油尾气处理液故障警示

故障现象 距离我上次在货卡上工作已经有一段时间了&#xff0c;让它们在道路上保持安全行驶是非常重要的。因此&#xff0c;当故障警示灯亮起时&#xff0c;我们需要迅速找到问题方向以及排除故障。 车辆的仪表板亮起多个故障灯以及警示灯&#xff0c;我们需要用解码器查找触…

数据分析与应用:微信-情人节红包流向探索分析

目录 0 需求描述 1 红包发送方用户的基本信息缺失率有多高?(即有多少红包发送方用户无法在用户基本信息表中匹配? 2 哪一组红包金额的拒收率最高? 3、最受二线城市欢迎的红包金额为?(即发出次数最多) 4 北上广深 4 大城市中,哪座城市的男性用户发出的 520 红包比例…

《密码编码学与网络安全原理与实践》第十四章第十五章密钥管理和用户认证

密钥管理与分发 密钥分发管理 传统上&#xff0c;对称加密被用于实现消息的保密性&#xff08;confidentiality&#xff09; 面临的攻击&#xff1a;局域网内监听、搭线窃听 传输媒介&#xff1a;线缆(双绞线、同轴电缆、光纤) (电磁波干扰)、微波链路、卫星信道 保密通信…

windows11-ubuntu-可爱的截图-启动时按F2可以选简体中文

windows11-ubuntu-可爱的截图-启动时按F2可以选简体中文

c++ 46 const

const伪命题 可以间接修改

Java-自定义注解中成员变量是Class<?>

在Java中,自定义注解可以包含各种类型的成员变量,包括 Class<?> 类型。这种类型的成员变量 通常用于表示某个类的类型信息。下面我将详细介绍如何定义一个包含 Class<?> 类型成员变量的 自定义注解,并给出一些示例代码。 1. 定义自定义注解 定义一个自定义…

jmeter使用while控制器时防止死循环

while 控制器&#xff1a;当前条件为true会一直循环&#xff0c;直到条件为false时退出循环 如果环境出现问题&#xff0c;可能导致死循环&#xff0c;需要进行设置 本次结合计时器进行组合配置&#xff0c;防止死循环 配置while控制器 条件&#xff1a;${__jexl3("${i…

libnl教程(2):发送请求

文章目录 前言示例示例代码构造请求创建套接字发送请求 简化示例 前言 前置阅读要求&#xff1a;libnl教程(1):订阅内核的netlink广播通知 本文介绍&#xff0c;libnl如何向内核发送请求。这包含三个部分&#xff1a;构建请求&#xff1b;创建套接字&#xff1b;发送请求。 …

milvus helm k8s开启监控

https://milvus.io/docs/monitor.md 文章写的很清晰 &#xff0c;我这边做一下个人补充&#xff0c;初版可能只是配置&#xff0c;具体的grafana 监控报表后期补一下。 架构如下&#xff1a; values.yaml 配置 enabled: true 改为true metrics:enabled: trueserviceMonitor:…

F.Enchanted

https://codeforces.com/gym/105139/problem/F24湖北省赛F 看了一下前面两种操作&#xff0c;做法不是很明显 后面两种操作&#xff0c;一看就是可持久化线段树&#xff0c;单点修改&#xff0c;版本复制 接下来解决前面的两种操作 第一个操作 两个相同的合成一个新的(33-&…

Linux基础-磁盘管理

基于Ubuntu 20.04环境测试验证。 一、磁盘空间查看 1.1 磁盘分区管理 - fdisk fdisk通常被用来查看系统磁盘的分区信息&#xff0c;同时fdisk也支持对磁盘空间进行分区&#xff08;下一章节介绍&#xff09;。 命令参数&#xff1a; Usage:fdisk [options] <disk> …

科普----Linux的前世今生

提到电脑的操作系统 相信大多数人会脱口而出&#xff1a;Windows&#xff01;MacOS&#xff01; 没错&#xff0c;这确实是目前市场上主流的两款操作系统。 但对于从事IT相关工作的人们来说&#xff0c;还有一种系统也必须拥有姓名 那就是Linux 看到这个词&#xff0c;大家…

unity游戏开发003:深入理解Unity中的坐标系

Unity游戏开发 “好读书&#xff0c;不求甚解&#xff1b;每有会意&#xff0c;便欣然忘食。” 本文目录&#xff1a; Unity游戏开发 Unity游戏开发深入理解Unity中的坐标系前言1. 坐标轴2. 左手坐标系3. 世界坐标系 vs. 局部坐标系4. 坐标变换5. 注意事项 总结 深入理解Unity中…

虚幻5|简单装备武器

一&#xff0c;首先我们要创建一个actor蓝图类 打开组件添加一个静态网格体&#xff0c;点击该组件&#xff0c;右侧细节找到网格体资产&#xff0c;选择需要的武器 三&#xff0c;打开角色的骨骼网格体&#xff0c;添加武器插槽 这个有些动作包里的骨骼网格体是加了骨骼&…