C++ Primer 类型转换

欢迎阅读我的 【C++Primer】专栏

专栏简介:本专栏主要面向C++初学者,解释C++的一些基本概念和基础语言特性,涉及C++标准库的用法,面向对象特性,泛型特性高级用法。通过使用标准库中定义的抽象设施,使你更加适应高级程序设计技术。希望对读者有帮助!

在这里插入图片描述
在这里插入图片描述

目录

  • 4.11类型转换
    • 何时发生隐式类型转换
      • 算术转换
      • 整型提升
    • 无符号类型的运算对象
    • 理解算术转换
    • 其他隐式类型转换运
    • 显式转换
    • 命名的强制类型转换
    • const_cast
    • reinterpret_cast
    • 旧式的强制类型转换

4.11类型转换

在C++语言中,某些类型之间有关联。如果两种类型有关联,那么当程序需要其中一种类型的运算对象时,可以用另一种关联类型的对象或值来替代。换句话说,如果两种类型可以相互转换(conversion),那么它们就是关联的。举个例子,考虑下面这条表达式,它的目的是将ival初始化为6:

int ival = 3.541 + 3;//编译器可能会警告该运算损失了精度

加法的两个运算对象类型不同:3.541的类型是double,3的类型是int。C++语言不会直接将两个不同类型的值相加,而是先根据类型转换规则设法将运算对象的类型统一后再求值。上述的类型转换是自动执行的,无须程序员的介入,有时甚至不需要程序员了解。因此,它们被称作隐式转换(implicit conversion)。

算术类型之间的隐式转换被设计得尽可能避免损失精度。很多时候,如果表达式中既有整数类型的运算对象也有浮点数类型的运算对象,整型会转换成浮点型。在上面的例子中,3转换成double类型,然后执行浮点数加法,所得结果的类型是double。

接下来就要完成初始化的任务了。在初始化过程中,因为被初始化的对象的类型无法改变,所以初始值被转换成该对象的类型。仍以这个例子说明,加法运算得到的double类型的结果转换成int类型的值,这个值被用来初始化ival。由double向int转换时忽略掉了小数部分,上面的表达式中,数值6被赋给了ival。

何时发生隐式类型转换

在下面这些情况下,编译器会自动地转换运算对象的类型:

在大多数表达式中,比int类型小的整型值首先提升为较大的整数类型。在条件中,非布尔值转换成布尔类型。初始化过程中,初始值转换成变量的类型:在赋值语句中,右侧运算对象转换成左侧运算对象的类型。如果算术运算或关系运算的运算对象有多种类型,需要转换成同一种类型。函数调用时也会发生类型转换。

算术转换

算术转换(arithmetic conversion)的含义是把一种算术类型转换成另外一种算术类型。算术转换的规则定义了一套类型转换的层次,其中运算符的运算对象将转换成最宽的类型。例如,如果一个运算对象的类型是long double,那么不论另外一个运算对象的类型是什么都会转换成long double。还有一种更普遍的情况,当表达式中既有浮点类型也有整数类型时,整数值将转换成相应的浮点类型。

整型提升

整型提升(integral promotion)负责把小整数类型转换成较大的整数类型。对于bool、char、signed char、unsigned char、short和unsigned short等类型来说,只要它们所有可能的值都能存在int里,它们就会提升成int类型,否则,提升成unsigned int类型。就如我们所熟知的,布尔值false提升成0、true提升成1。

较大的char类型(wchar_t、char16_t、vchar32_t)提升成int、unsigned int、long、unsigned long、long long和unsigned long long中最小的一种类型,前提是转换后的类型要能容纳原类型所有可能的值。

无符号类型的运算对象

如果树个运算符的运算对象类型不一致,这些运算对象将转换成同一种类型。但是如果树个运算对象的类型是无符号类型,那么转换的结果就要依赖于机器中各个整数类型的相对大小了。

像往常一样,首先执行整型提升。如果结果的类型匹配,无须进行进一步的转换。如果两个(提升后的运算对象的类型要么都是带符号的、要么都是无符号的,则小类型的运算对象转换成较大的类型。

如果一个运算对象是无符号类型、另外一个运算对象是带符号类型,而一其中的无符号类型不小于带符号类型,那么带符号的运算对象转换成无符号的。例如,假设两个类型分别是unsigned int和int,则int类型的运算对象转换成unsigned int类型。需要注意的是,如果int型的值恰好为负值。

剩下的一种情况是带符号类型大于无符号类型,此时转换的结果依赖于机器。如果无符号类型的所有值都能存在该带符号类型中,则无符号类型的运算对象转换成带符号类型。如果不能,那么带符号类型的运算对象转换成无符号类型。例如,如果两个运算对象的类型分别是long和unsigned int,并且int和long的大小相同,则long类型的运算对象转换成unsigned int类型;如果long类型占用的空间比int更多,则unsigned int类型的运算对象转换成long类型。

理解算术转换

要想理解算术转换,办法之一就是研究大量的例子:

bool flag;char val;
short sval; unsigned short usval;
int ival; unsigned int uival;
long lval;unsigned long ulval;
float fval; double dval;
3.14159L+ 'a'; //“a“提升成int,然后该int值转换成long double
dval+ival;//ival转换成double
dval+fval;//fval转换成double
ival=dval;//dval转握成(切除小数部分后)int
flag=dval;//如果dval是0,则flag是false,否则f1ag是Lrue
cval+fval;//cval提升成int,然后该int值转换成float
sval+cval;//sval和cval都提升成int
cval+lval;//cval转援成long
ival+ulval;//ival转换成unsigned long
usval+ival;//根据unsigned short和int所占空间的大小进行提升
uival+lval;//根据hnsigned int和long所占空间的大小进行转换

在第一个加法运算中,小写字母’a’是char型的字符常量,它其实能表示一个数字值。到底这个数字值是多少完全依赖于机器上的字符集,在我们的环境中,‘a’对应的数字值是97。当把’a’ 和一个long double类型的数相加时,char类型的值首先提升成int类型,然后int类型的值再转换成long double类型。最终我们把这个转换后的值与那个字面值相加。最后的两个含有无符号类型值的表达式也比较有趣,它们的结果依赖于机器。

其他隐式类型转换运

除了算术转换之外还有几种隐式类型转换,包括如下几种。数组转换成指针:在大多数用到数组的表达式中,数组自动转换成指向数组首元素的指针:

int ia[10];//含有10个整数的数组
int*ip = a;//ia转换成指向数组首元素的指针

当数组被用作 decltype 关键字的参数,或者作为取地址符(&)、sizeof及typeid等运算符的运算对象时,上述转换不会发生。同样的,如果用一个引用来初始化数组,上述转换也不会发生。指针的转换:C++还规定了几种其他的指针转换方式,包括常量整数值 0 或者字面值 nullptr 能转换成任意指针类型;指向任意非常量的指针能转换成void*;指向任意对象的指针能转换成const void*。

型间还有另外一种指针转换的方式。
转换成布尔类型:存在一种从算术类型或指针类型向布尔类型自动转换的机制。如果指针或算术类型的值为0,转换结果是false;否则转换结果是true:

char*cp=get_string();
if(cp) /*...*/ //如果指针cp不是0,条件为真
while(*cp)/*...*///如果*cp不是空字符,条件为真

转换成常量:允许将指向非常量类型的指针转换成指向相应的常量类型的指针,对于引用也是这样。也就是说,如果0是一种类型,我们就能将指向0的指针或引用分别转换成指向const的指针或引用:

int i;const int &j =i;  //非常量转换成const int的引用
const int*p=&i;   //非常量的地址转换成const的地址
int &r = j,*q= p; //错误:不允许const转换成非常量

相反的转换并不存在,因为它试图删除掉底层const。类类型定义的转换:类类型能定义由编译器自动执行的转换,不过编译器每次只能执行一种类类型的转换。我们将看到一个例子,如果同时提出多个转换请求,这些请求将被拒绝。

我们之前的程序已经使用过类类型转换:一处是在需要标准库string类型的地方使用C风格守符串;另一处是在条件部分读入istream:

string s, t= "a value";//字符串字面值转换成string类型
while(cin >> s)       // while 的条件部分把cin 转换成布尔值

条件( cin >> s )读入cin的内容并将cin作为其求值结果。条件部分本来需要一个布尔类型的值,但是这里实际检查的是istream类型的值。幸好,IO库定义了从istream向布尔值转换的规则,根据这一规则,cin自动地转换成布尔值。所得的布尔值到底是什么由输入流的状态决定,如果最后一次读入成功,转换得到的布尔值是true:相反,如果最后一次读入不成功,转换得到的布尔值是false。

显式转换

有时我们希望显式地将对象强制转换成另外一种类型。例如,如果想在下面的代码中执行浮点数除法:

int i,j;
double slope=i;

就要使用某种方法将i和/或j显式地转换成double,这种方法称作强制类型转换(cast)。

WARNING: 虽然有时不得不使用强制类型转换,但这种方法本质上是非常危险的。

命名的强制类型转换

一个命名的强制类型转换具有如下形式:

cast-name<type>(expression)

其中,type是转换的目标类型而 expression 是要转换的值。如果type是引用类型,则结果是左值。cast-name是 static_cast、dynamic_cast、const_cast 和reinterpret_cast中的一种。dynamic_cast支持运行时类型识别。

    static_cast

任何具有明确定义的类型转换,只要不包含底层const,都可以使用static_cast。例如,通过将一个运算对象强制转换成double类型就能使表达式执行浮点数除法:

//进行强制类型转换以便执行浮点数除法
double slope=static_cast<double>(j)/i; 

当需要把一个较大的算术类型赋值给较小的类型时,static_cast非常有用。此时,强制类型转换告诉程序的读者和编译器:我们知道并且不在乎潜在的精度损失。一般来说,如果编译器发现一个较大的算术类型试图赋值给较小的类型,就会给出警告信息;但是当我们执行了显式的类型转换后,警告信息就会被关闭了。

static_cast对于编详器无法自动执行的类型转换也非常有用。例如,我们可以使用static_cast找回存在于void*指针中的值:

void+p=&d;//正确:任何非常量对象的地址都能存入void*//正确:将void*转接回初始的指针类型
double*dp=static_cast<double*>(p);

当我们把指针存放在void*中,并且使用static_cast将其强制转换回原来的类型时,应该确保指针的值保持不变。也就是说,强制转换的结果将与原始的地址值相等,因此我们必须确保转换后所得的类型就是指针所指的类型。类型一旦不符,将产生未定义的后果。

const_cast

const_cast只能改变运算对象的底层const:

const char*pc;
char*p=const_cast<char*>(pc);//正确:但是通过p写值是未定义的行为

对于将常量对象转换成非常量对象的行为,我们一般称其为“去掉const性质(cast away the const)“。一旦我们去掉了树个对象的const性质,编译器就不再阻止我们对该对象进行写操作了。如果对象本身不是一个常量,使用强制类型转换获得写权限是合法的行为。然而如果对象是一个常量,再使用const_cast执行写操作就会产生未定义的后果。

只有const_cast能改变表达式的常量属性,使用其他形式的命名强制类型转换改变表达式的常量属性都将引发编译器错误。同样的,也不能用const_cast改变表达式的类型:

const char*cp;
//错误:static_cast不能转换据const性质
char*q = static_cast<char*>(cp);
static_cast<string>(cp);//正确:字符串字面值转换成string类型
const_cast<string>(cp);//错误:const_cast只改变常量属性

const_cast常常用于有函数重载的上下文中

reinterpret_cast

reinterpret_cast通常为运算对象的位模式提供较低层次上的重新解释。举个例子,假设有如下的转换

int *ip;
char*pc=reinterpret_cast<char*>(ip);

我们必须牢记pc所指的真实对象是一个int而非字符,如果把pc当成普通的字符指针使用就可能在运行时发生错误。例如:

string str(pc);

可能导致异常的运行时行为。

使用reinterpret_cast是非常危险的,用pc初始化str的例子很好地证明了这一点。其中的关键问题是类型改变了,但编译器没有给出任何警告或者错误的提示信息。当我们用一个int的地址初始化pc时,由于显式地声称这种转换合法,所以编译器不会发出任何警告或错误信息。接下来再使用pc时就会认定它的值是char*类型,编译器没法知道它实际存放的是指向int的指针。最终的结果就是,在上面的例子中虽然用pc初始化str没什么实际意义,甚至还可能引发更糟糕的后果,但仅从语法上而言这种操作无可指摘。查找这类问题的原因非常困难,如果将ip强制转换成pc的语句和用pc初始化string对象的语句分属不同文件就更是如此。

WRNING: reinterpret_cast本质上依赖于机器.要想安全地使用reinterpret_cast必须对涉及的类型和编译嚣实现转换的过程都非常了解。

建议:避免强制类型转换

强制类型转换干扰了正常的类型检查,因此我们强烈建议程序员避免使用强制类型转换。这个建议对于reinterpret_cast尤其适用,因为此类类型转换总是充满了风险。在有重载函数的上下文中使用const_cast无可厚非,关于这一点将在;但是在其他情况下使用const_cast也就意味着程序存在某种设计缺陷。其他强制类型转换,比如static_cast和dynamic_cast,都不应该频素使用。每次书写了一条强制类型转换语句,都应该反复斟酌能否以其他方式实现相同的目标。就算实在无法避免,也应该尽量限制类型转换值的作用域,并且记录对相关类型的所有假定,这样可以减少错误发生的机会。′

旧式的强制类型转换

在早期版本的C++语言中,显式地进行强制类型转换包含两种形式:

type(expr);//函数形式的强制类型转换
(type)expr;//C语言风格的强制类型转换

根据所涉及的类型不同,旧式的强制类型转换分别具有与const_cast、static_cast或reinterpret_cast相似的行为。当我们在某处执行旧式的强制类型转换时,如果换成const_cast和static_cast也合法,则其行为与对应的命名转换一致。如果替换后不合法,则旧式强制类型转换执行与reinterpret_cast类似的功能:

char *pc=(char*)ip;//ip是指向整数的指针

的效果与使用retnterpret_cast一样。

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

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

相关文章

Gitlab中如何进行仓库迁移

需求&#xff1a;之前有一个自己维护的新仓库A&#xff0c;现在需要将这个仓库提交并覆盖另一个旧的仓库B&#xff0c;需要保留A中所有的commit信息。 1.方法一&#xff1a;将原有仓库A导出后再导入到新的仓库B中 适用场景&#xff1a;新的仓库B是一个待建仓库&#xff0c;相当…

SpringCloud - Sentinel服务保护

前言 该博客为Sentinel学习笔记&#xff0c;主要目的是为了帮助后期快速复习使用 学习视频&#xff1a;7小快速通关SpringCloud 辅助文档&#xff1a;SpringCloud快速通关 源码地址&#xff1a;cloud-demo 一、简介 官网&#xff1a;https://sentinelguard.io/zh-cn/index.h…

文件和内容管理:非结构化数据的有序化

在数据管理的众多领域中&#xff0c;文件和内容管理专注于处理非结构化数据&#xff0c;如文档、图像、音频和视频等。这些数据虽然不像结构化数据那样易于管理和分析&#xff0c;但它们在组织的日常运营中扮演着不可或缺的角色。今天&#xff0c;让我们深入《DAMA数据管理知识…

集成右键的好用软件,支持多线程操作!

今天给大家分享一个超级实用的小工具&#xff0c;真的能帮上大忙呢&#xff01;这个软件是吾爱大神无知灰灰精心制作的&#xff0c;简直就是图片转换界的“小能手”。 它能一键把webp格式的图片转换成png格式&#xff0c;而且速度超快&#xff0c;完全不输那些付费的软件&#…

UPDATE 语句结合 REPLACE() 函数来批量修改 detail 字段中的 xxx 为 xxx

问题出现的背景&#xff0c;由于阿里云的oss服务器域名更换&#xff0c;所以我们需要修改数据库中detail字段中的域名&#xff0c;才能加载图片 您可以使用 SQL 的 UPDATE 语句结合 REPLACE() 函数来批量修改 detail 字段中的 oss.kxlist.com 为 www.crossbiog.com。 以下是 S…

【设计模式】【行为型模式】职责链模式(Chain of Responsibility)

&#x1f44b;hi&#xff0c;我不是一名外包公司的员工&#xff0c;也不会偷吃茶水间的零食&#xff0c;我的梦想是能写高端CRUD &#x1f525; 2025本人正在沉淀中… 博客更新速度 &#x1f4eb; 欢迎V&#xff1a; flzjcsg2&#xff0c;我们共同讨论Java深渊的奥秘 &#x1f…

Visual Studio踩过的坑

统计Unity项目代码行数 编辑-查找和替换-在文件中查找 查找内容输入 b*[^:b#/].*$ 勾选“使用正则表达式” 文件类型留空 也有网友做了指定&#xff0c;供参考 !*\bin\*;!*\obj\*;!*\.*\*!*.meta;!*.prefab;!*.unity 打开Unity的项目 注意&#xff1a;只是看&#xff0…

《深度学习》——pytorch框架及项目

文章目录 pytorch特点基本概念 项目项目实现导入所需库下载训练数据和测试数据对训练和测试样本进行分批次展示手写图片判断pytorch是否支持GPU定义神经网络模型定义训练函数定义测试函数创建交叉熵损失函数和优化器通过多轮训练降低损失值得到最终结果注意 pytorch PyTorch 是…

深入探索人工智能的未来:DeepSeek R1与蓝耘智算平台的完美结合

在当今数字化时代&#xff0c;人工智能&#xff08;AI&#xff09;和机器学习&#xff08;ML&#xff09;正以前所未有的速度改变着我们的生活和工作方式。从智能语音助手到自动驾驶汽车&#xff0c;从精准医疗到金融风险预测&#xff0c;AI的应用无处不在。深度学习作为AI的核…

基于uniapp vue3 的滑动抢单组件

通过在onMounted获取movable-area与movable-view实例&#xff0c;计算出可滑动的距离 效果图&#xff1a; 代码&#xff1a; <template><view class"slider-container"><movable-area class"movable-area" id"movableArea">…

亚博microros小车-原生ubuntu支持系列:26手势控制小车基础运动

背景知识 手指检测&#xff1a;亚博microros小车-原生ubuntu支持系列&#xff1a;4-手部检测-CSDN博客 程序功能说明 功能开启后&#xff0c;摄像头捕获图像&#xff0c;识别手势来控制小车移动。 手势 “5”小车前进拳头小车后退手势 “1”小车向左手势 “2”小车向右 运…

在人工智能领域 ⊕、⊗和 ⊙ 符号是什么含义?

我们经常在论文中看到 ⊕、⊗和 ⊙ 符号&#xff0c;那么有下面两个问题&#xff1a; 这三个符号有什么作用呢&#xff1f; 如何在论文中正确使用这三个数学符号 1. 两种符号的解释 1.1 逐元素相加&#xff1a;⊕ ⊕ 在论文中表示逐元素相加&#xff0c;如果用两个矩阵表示&a…

NineData云原生智能数据管理平台新功能发布|2025年1月版

本月发布 14 项更新&#xff0c;其中重点发布 6 项、功能优化 7 项、安全性更新 1 项。 重点发布 数据库 Devops - 数据导出功能增强 支持 AWS ElastiCache 数据源&#xff1a;现已支持通过 SQL 查询语句或直接通过库表导出 AWS ElastiCache 数据&#xff0c;方便用户快速提取…

蓝桥与力扣刷题(226 翻转二叉树)

题目&#xff1a;给你一棵二叉树的根节点 root &#xff0c;翻转这棵二叉树&#xff0c;并返回其根节点。 示例 1&#xff1a; 输入&#xff1a;root [4,2,7,1,3,6,9] 输出&#xff1a;[4,7,2,9,6,3,1]示例 2&#xff1a; 输入&#xff1a;root [2,1,3] 输出&#xff1a;[2,…

C# OpenCV机器视觉:OSTU算法实现背景差分的自适应分割

在一个热闹的科技公司里&#xff0c;阿强是一个负责图像分析的员工。他的日常工作就是从各种复杂的图像中提取出有用的信息&#xff0c;可这可不是一件轻松的事情哦 最近&#xff0c;阿强接到了一个艰巨的任务&#xff1a;要从一堆嘈杂的监控图像中分离出运动的物体&#xff0c…

amis组件crud使用踩坑

crud注意 过滤条件参数同步地址栏 默认 CRUD 会将过滤条件参数同步至浏览器地址栏中&#xff0c;比如搜索条件、当前页数&#xff0c;这也做的目的是刷新页面的时候还能进入之前的分页。 但也会导致地址栏中的参数数据合并到顶层的数据链中&#xff0c;例如&#xff1a;自动…

解决 ollama._types.ResponseError 问题

原因 在对问题进行分析后&#xff0c;我认为原因是之前为了在服务器上下载模型&#xff0c;我设置了启动时自动配置的网络代理。然而&#xff0c;ollama在运行时采用了该代理配置&#xff0c;而不是默认的API URL&#xff08;“http://localhost:11434”&#xff09;。因此&am…

用户认证实验

一&#xff0c;拓扑图: 第一步&#xff1a;先开启防火墙 第二步&#xff1a;sw2配置&#xff1a; [sw2] vlan batch 10 20 interface GigabitEthernet0/0/2 port link-type access port default vlan 10 interface GigabitEthernet0/0/3 port link-type access port defau…

活动预告 | 为 AI 新纪元做好准备:助力安全的业务转型

课程介绍 随着现代办公模式的不断演变和 AI 技术的迅速发展&#xff0c;企业在享受效率提升的同时&#xff0c;也面临着信息安全与数据保护的严峻挑战。在利用 AI 技术释放业务潜力的同时&#xff0c;如何确保数据质量与安全已成为企业发展的关键议题。 在本次线上课程中&…

【再谈设计模式】中介者模式 - 协调对象间交互的枢纽

一、引言 在软件工程&#xff0c;软件开发过程中&#xff0c;复杂的软件系统&#xff0c;对象之间的交互往往错综复杂。当众多对象相互依赖、频繁通信时&#xff0c;系统的耦合度会急剧上升&#xff0c;导致代码难以维护、扩展和理解。就像在一个大型社交聚会中&#xff0c;如果…