C++第五十弹---类型转换全解析:从静态到动态,一网打尽

news2024/11/14 13:27:11

个人主页: 熬夜学编程的小林

💗系列专栏: 【C语言详解】 【数据结构详解】【C++详解】

目录

1. C语言中的类型转换

2. 为什么C++需要四种类型转换

2.1、内置类型   -> 自定义类型

2.2、自定义类型 -> 内置类型 

2.3、自定义类型 -> 自定义类型

2.4、隐式类型转换的坑

3. C++强制类型转换

3.1 static_cast

3.2 reinterpret_cast

3.3 const_cast

3.4 dynamic_cast

4. RTTI


1. C语言中的类型转换


在C语言中,如果赋值运算符左右两侧类型不同,或者形参与实参类型不匹配,或者返回值类型与
接收返回值类型不一致时,就需要发生类型转化,C语言中总共有两种形式的类型转换隐式类型
转换(整型之间 整型浮点数之间)
显式类型转换(指针之间 整型指针之间)没有关联的类型不支持转换。

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

代码演示

#include<iostream>
using namespace std;

int main()
{
	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);
	// “类型强制转换”: 无法从“int *”转换为“double”
	// double x = (double)p;// 没有关联的类型不支持转换
	return 0;
}

 运行结果

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

2. 为什么C++需要四种类型转换


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

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

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

C++除了兼容C的转换用法之外还有三种转换方式:内置类型   -> 自定义类型  ;自定义类型 -> 内置类型  ; 自定义类型 -> 自定义类型。
 

2.1、内置类型   -> 自定义类型

内置类型转换为自定义类型通过 隐式类型转换 + 对应的构造函数 就能够实现。 

代码演示

class A
{
public:
	A(int a1)
		:_a1(a1)
	{}

	A(int a1, int a2)
		:_a1(a1)
		, _a2(a2)
	{}
private:
	int _a1 = 1;
	int _a2 = 1;
};
int main()
{
	// 内置类型 -> 自定义类型
	A aa1 = 1;// 单参数隐式类型转换
	A aa2 = { 1,2 };// 多参数隐式类型转换,使用{}
	// A aa3 = { 1,2,3 };// 没有实现3个参数的构造函数或者initializer_list,就不能隐式类型转换
	return 0;
}

运行结果 

2.2、自定义类型 -> 内置类型 

自定义类型转化为内置类型,直接写不支持,需要写重载函数。

代码演示

class A
{
public:
	A(int a1)
		:_a1(a1)
	{}
	// 自定义类 -> 内置类型 重载函数
	operator int()
	{
		return _a1 + _a2;
	}
private:
	int _a1 = 1;
	int _a2 = 1;
};
int main()
{
	// 内置类型 -> 自定义类型
	A aa1 = 1;

	// 自定义类型 -> 内置类型 直接写不支持,需要重载
	int x = aa1;
	cout << x << endl;
	return 0;
}

运行结果 

2.3、自定义类型 -> 自定义类型

自定义类型转换为自定义类型,直接写不支持,需要加拷贝构造。

代码演示

class A
{
public:
	A(int a1)
		:_a1(a1)
	{}
	// 成员变量私有,类外不能获取,const对象需要加const
	int get() const
	{
		return _a1;
	}
private:
	int _a1 = 1;
	int _a2 = 1;
};
class B
{
public:
	B(const A& aa)
		:_b1(aa.get())
	{}
private:
	int _b1;
};
int main()
{
	// 内置类型 -> 自定义类型
	A aa1 = 1;
	// 自定义类型 -> 自定义类型 直接写不支持,需要加拷贝构造
	B bb = aa1;
	return 0;
}

运行结果 

2.4、隐式类型转换的坑

代码演示

// 隐式类型转换的坑
void Insert(size_t pos)
{
	int end = 10;
	while (end >= pos)
	{
		cout << end << endl;
		--end;
	}
}
int main()
{
	Insert(5);
	// 插入0就有坑,-1转换为无符号整数为最大值
	Insert(0);
	return 0;
}

运行结果 


3. C++强制类型转换


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

  • static_cast、reinterpret_cast、const_cast、dynamic_cast

3.1 static_cast


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

代码演示(内置类型)

int main()
{
	int i = 1;
	// 隐式类型转换 : static_cast
	double d = static_cast<double>(i);
	cout << d << endl;

	// 强制类型转换
	int* p = &i;
	//int address = static_cast<int>(p);

	//printf("%p %d\n", p, address);
	return 0;
}

运行结果 

代码演示(自定义类型)

class A
{
public:
	A(int a1)
		:_a1(a1)
	{}
private:
	int _a1 = 1;
	int _a2 = 1;
};
int main()
{
	// 隐式类型转换,使用static
	A aa1 = 1;
	A aa2 = static_cast<int>(1);
    return 0;
}

运行结果 

3.2 reinterpret_cast


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

代码演示

int main()
{
	int i = 1;
	// 强制类型转换 : reinterpret_cast
	int* p = &i;
	int address = reinterpret_cast<int>(p);

	printf("%p %d\n", p, address);
	return 0;
}

运行结果 

3.3 const_cast


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

先看一个代码

代码演示

int main()
{
	const int i = 1;// 存放到寄存器中
	int* ptr = (int*)&i;
	(*ptr)++;
	cout << *ptr << endl;
	cout << i << endl;

	return 0;
}

运行结果 

我们能够看到一个很奇怪的现象,我们将 i 地址处的数据++,该地址处的值修改了,但是  i 的值却没有修改,这是为什么呢? 

实际是在C++程序中,使用const 修饰的变量是存放在寄存器中的,最终打印该值是直接打印寄存器存储的值。

汇编代码

如果想要 i 的值能够修改,需要加  volatile 修饰。

代码演示

int main()
{
	volatile const int i = 1;
	int* ptr = (int*)&i;
	(*ptr)++;
	cout << *ptr << endl;
	// 不加volatile,打印的值就是1,不会修改,监视窗口是查看对应地址的值
	// 加上volatile表示i 是不稳定的,可能被修改,因此打印的值是修改之后值
	cout << i << endl;

	return 0;
}

 运行结果 

汇编代码

 删除变量的const属性就可以使用const_cast。

代码演示

// const_cast -> 删除变量的const属性
int main()
{
	const int i = 1;
	int* ptr = const_cast<int*>(&i);
	(*ptr)++;
	cout << *ptr << endl;
	// 不加volatile,打印的值就是1,不会修改,监视窗口是查看对应地址的值
	// 加上volatile表示i 是不稳定的,可能被修改,因此打印的值是修改之后值
	cout << i << endl;

	return 0;
}

运行结果 

3.4 dynamic_cast


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

  • 向上转型:子类对象指针/引用->父类指针/引用(不需要转换,赋值兼容规则)
  • 向下转型:父类对象指针/引用->子类指针/引用(用dynamic_cast转型是安全的)

注意:
1. dynamic_cast只能用于父类含有虚函数的类 。

2. dynamic_cast会先检查是否能转换成功,能成功则转换,不能则返回0。

直接将父类对象指针/引用->子类指针/引用

 代码演示

class A
{
public:
	virtual void f(){}
	int _a1 = 1;
};

class B : public A
{
public:

	int _b1 = 1;
};

void func(A* pa)
{
	// 不差别转换,存在风险
	// 传A对象的地址,没有_b1成员,修改_b1成员则程序崩溃
	B* pb = (B*)pa;
	cout << pb << endl;
	pb->_b1++;
}
int main()
{
	A a;
	B b;
	func(&a);
	func(&b);
	return 0;
}

运行结果 

使用dynamic_cast

代码演示

class A
{
public:
	virtual void f(){}
	int _a1 = 1;
};

class B : public A
{
public:

	int _b1 = 1;
};
// 使用dynamic_cast,父类必须有虚函数
void func(A* pa)
{
	// pa指向a对象,转换成功
	// pa指向b对象,转换失败,返回nullptr
	B* pb = dynamic_cast<B*>(pa);
	if (pb)
	{
		cout << pb << endl;
		pb->_b1++;
	}
	else
	{
		cout << "转换失败" << endl;
	}
}
int main()
{
	A a;
	B b;
	func(&a);
	func(&b);
	return 0;
}

运行结果 

注意
强制类型转换关闭或挂起了正常的类型检查,每次使用强制类型转换前,程序员应该仔细考虑是否还有其他不同的方法达到同一目的,如果非强制类型转换不可,则应限制强制转换值的作用域,以减少发生错误的机会。强烈建议:避免使用强制类型转换 。

4. RTTI


RTTI:Run-time Type identification的简称,即:运行时类型识别
C++通过以下方式来支持RTTI:

  • 1. typeid运算符
  • 2. dynamic_cast运算符
  • 3. decltype

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

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

相关文章

Suno新上线Covers翻唱新 - 实现音频风格任意转换

历史文章 Suno AI如何解决中文多音字的问题&#xff1f;耗费500积分&#xff0c;亲测有效 &#xff0c;V4版本会不会直接支持呢&#xff1f; 上传音频&#xff0c;打造贴合您喜好的风格歌曲创作&#xff0c;这一波新玩法我打8分 Suno AI Noisee AI 做抖音冥想账号实操 音乐…

网络安全-利用 Apache Mod CGI

一、环境 蚁剑官网拉取 二、开始操作 蚁剑连接 一样终端命令不能执行 可以看到putenv已经禁用 我们开始一下&#xff0c;跳入一个新终端且可以执行命令 我们具体看一下干了什么事情 上传了一个htaccess这个文件的作用是让以后所有ant文件都以cgi去执行 三、总结 cgi文件可以…

【C++】C++的多态

目录 多态的使用 多态的概念 多态的定义和实现 虚函数 构成多态的条件 特殊情况&#xff1a;协变 析构函数的重写 怎么实现 为什么实现 override和final关键字 override final 重载/重写/隐藏的对比 纯虚函数和抽象类 纯虚函数 抽象类 多态的实现 虚函数表指针…

魔方财务安装指南

本文将详细介绍魔方财务的安装、升级和迁移过程&#xff0c;确保您能够顺利地部署和使用魔方财务系统。 服务器配置一览表 以下是魔方财务1.0.0及更高版本的最低和推荐系统要求&#xff1a; 需求名称推荐配置最低要求OSCentOS/Debian/UbuntuLinux&#xff08;不要使用window…

IP协议及相关特性

IP协议负责地址管理和路由选择。它的组成为&#xff1a; 接下来我们将对其中较重要的部分进行介绍。 4位版本&#xff1a;这里的四位版本只有两个取值 分别为IPv4和IPv6&#xff0c;这两个额分别为不同的IP协议&#xff0c;但是现在主流的还是IPv4但是近年来IPv6在中国的普及率…

2022高教社杯全国大学生数学建模竞赛C题 问题一(1) Python代码演示

目录 问题 11.1 对这些玻璃文物的表面风化与其玻璃类型、纹饰和颜色的关系进行分析数据探索 -- 单个分类变量的绘图树形图条形图扇形图雷达图Cramer’s V 相关分析统计检验列联表分析卡方检验Fisher检验绘图堆积条形图分组条形图分类模型Logistic回归随机森林import matplotlib…

中秋之际,唱响工体!玛丽亚·凯莉2024演唱会北京站璀璨上演

续写传奇华章 启幕音乐盛典 中秋之际&#xff0c;全国数万乐迷翘首以待的音乐盛典如约而至。时隔多年&#xff0c;传奇天后玛丽亚凯莉惊艳开唱工体&#xff01; 夜幕降临&#xff0c;圆月高悬&#xff0c;在不绝于耳的欢呼声中&#xff0c;玛丽亚凯莉以一袭流光溢彩的礼服优雅…

【LIO】FAST-LIO论文详解

FAST-LIO论文详解 1. 摘要2. 简介1. 相关工作A. LiDAR 里程计和地图绘制 2. 实现方法A. 基础知识1. 连续模型在这里插入图片描述 B. 激光雷达测量的预处理C. 状态估计1) 前向传播&#xff1a;2) 反向传播与运动补偿&#xff1a;3) 残差计算&#xff1a; 1. 摘要 提出了一种计算…

简单题21 - 合并两个有序链表(Java)20240917

问题描述&#xff1a; java代码&#xff1a; /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode() {}* ListNode(int val) { this.val val; }* ListNode(int val, ListNode next) { this.val val…

Java 技巧 如何在IDEA2024 中快速打出System.out.println();

1.基本用法 键入sout回车 回车后变成&#xff1a; 2.打印变量 快速打印变量,以打印变量名为set为例&#xff0c;set.sout回车&#xff0c; 回车后变成

简单题26 - 删除有序数组中的重复项(Java)20240917

问题描述&#xff1a; java代码&#xff1a; class Solution {public int removeDuplicates(int[] nums) {if (nums.length 0) return 0; // 处理空数组情况int i 0; // 指向新数组中的最后一个不重复元素for (int j 1; j < nums.length; j) {if (nums[j] ! nums[i]) { …

室内灯具检测系统源码分享

室内灯具检测检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer Vis…

从kaggle竞赛零基础上手CV实战(Deepfake检测)

关注B站可以观看更多实战教学视频&#xff1a;hallo128的个人空间 从kaggle竞赛零基础上手CV实战 从kaggle竞赛零基础上手CV实战&#xff08;Deepfake检测&#xff09; 目录 从kaggle竞赛零基础上手CV实战&#xff08;Deepfake检测&#xff09;背景介绍学习地址课程大纲课程特色…

方法引用(Java)

把已经有的方法拿过来用&#xff0c;当做函数式接口中抽象方法的方法体 1.引用处必须是函数式接口 2.被引用的方法必须已经存在 3.被引用的方法形参的返回值需要跟抽象方法保持一致 4.被引用方法的功能要满足当前需求 package function;import java.util.Arrays;public cl…

网络高级项目( 基于webserver的工业数据采集和控制项目)

目录 一、项目要求&#xff1a; 二、演示效果&#xff1a; 设备端&#xff1a; Modbus用户控制端&#xff1a; 服务器端&#xff1a; 网页端&#xff1a; 三、 项目代码&#xff1a; Modbus用户控制端代码&#xff1a; 服务器端代码&#xff1a; 网页端代码&#xff1…

C++3D迷宫

目录 开头程序程序的流程图程序游玩的效果下一篇博客要说的东西 开头 大家好&#xff0c;我叫这是我58。 程序 #include <iostream> using namespace std; void printmaze(char strmaze[5][5][5]) {cout << "-----" << endl;int i 0;int ia 0…

pdf去水印怎么去掉免费?6个pdf去除水印的方法快码住,超级好用!

pdf去水印怎么去掉免费&#xff1f;您是否有一些带有水印的pdf文档&#xff0c;让您感觉到头疼&#xff1f;您又是否希望能够去除这些水印&#xff0c;或者想用其他水印来替换现有的水印&#xff1f;如果是这样的话&#xff0c;我非常推荐您继续阅读本篇文章。本文将为您提供一…

如何在Linux下升级R版本和RStudio

一、升级R版本 在Linux上&#xff0c;R的安装通常通过包管理器完成。不同的Linux发行版&#xff08;如Ubuntu、Debian、Fedora等&#xff09;可能略有不同。下面以Ubuntu为例&#xff0c;介绍如何升级R版本。如果你使用其他发行版&#xff0c;步骤可能类似。 二.更新步骤 2.…

Git:版本控制工具介绍

目录 全文概要版本控制工具介绍版本控制系统的概念**版本控制系统的历史****版本控制系统的分类****本地版本控制系统****集中式版本控制****分布式版本控制系统** Git 介绍Git 概念Git 与 SVN 对比**SVN的记录方式****Git 的记录快照** Git 安装Git 安装Bash、CMD与GUIGit 的配…

黑神话悟空黄凤岭

黑神话悟空黄风岭支线任务大全 第二关“黄风寨”的难度可以说是飙升&#xff1a; 虎先锋从名字来看只是一般妖王&#xff0c;但是他的天命人“猴头下酒”怕是吃到撑也吃不完&#xff0c;关底的黄风大圣二阶段放法宝之后的飓风更是重量级&#xff0c;从视线和移动能力对玩家会造…