C++进阶——浅谈隐式转化

在代码里我们或多或少都会依赖c++的隐式类型转换。

然而不幸的是隐式类型转换也是c++的一大坑点,稍不注意很容易写出各种奇妙的bug。

因此我梳理一遍c++的隐式类型转换

一、什么是隐式类型转换

概念:就是当你只有一个类型T1,但是当前表达式需要类型为T2的值,如果这时候T1自动转换为了T2,那么这就是隐式类型转换。

接下来看两个例子,首先是最常见的混用数值类型

int a = 0;
long b = a + 1; // int 转换为 long
if(a == b)
{
    // 默认的operator==需要a的类型和b相同,因此也发生转换
}

int转成long是向上转换,通常不会有太大问题,而long到int则很可能导致数据丢失,因此要尽量避免后者。

第二个例子是自定义类型到标量类型的转换

std::shared_ptr<int> ptr = func();
if(ptr) // 这里会从shared_ptr转换成bool
{ 
    // 处理数据
}

因为提供了用户自定义的隐式类型转换规则,所以我们可以很简单地去判断智能指针是否为空。在这里if表达式里需要bool,因此ptr转换为了bool,这又被叫做语境转换。

由此可见隐式类型转换转换可以简化代码的书写。不过简化不是没有代价的,我们细细说来。

二、基础回顾

在正式介绍隐式类型转换之前,我们先要回顾一下基础知识,放轻松。

直接初始化

首先是类的直接初始化。

顾名思义,就是显式调用类型的构造函数进行初始化。举个例子:

structA{
A() = default;
A(constA&) = default;
A(int) {}
};
// 这是默认初始化: A a; 注意区分
A a1{}; // c++11的列表初始化
// 不能写出A a2(),因为这会被认为是函数声明
A a2(1);
A a3(a2); // 没错,显式调用复制构造函数也是直接初始化
autoa4 = static_cast<A>(1);

需要注意的是a4,用static_cast转换成类型T的这一步也是直接初始化。

这种初始化方式有什么用呢?直接初始化会考虑全部的构造函数,而不会忽略explicit修饰的构造函数。

显式地调用构造函数进行直接初始化实际上是显式类型转换的一种。

复制初始化

除去默认初始化和直接初始化,剩下的会导致复制的基本都是复制初始化,典型的如下:

A func(){
returnA{}; // 返回值会被复制初始化
}
A a5 = 1; // 先隐式转换,再复制初始化
voidfunc2(A a){} // 非引用的参数传递也会进行复制构造

然而类似A a6 = {1}的表达式却不是复制初始化,这是复制列表初始化,会直接选择合适的非explicit构造函数进行初始化,而不用创建临时量再进行复制。

复制初始化又起到什么作用呢?

首先想到的是这样可以创造某个对象的副本,没错,不过还有一个更重要的作用:

如果想要某个类型T1的value能进行到T2的隐式转换,两个类型必须满足这个表达式的调用T2 v2 = value

而这个形式的表达式正是复制初始化表达式。至于具体的原因,我们马上就会在下一节看到。

类型构造时的隐式转换

我们看一道经典的面试题:

std::strings = "hello c++";

请问创建了几个string呢?如果你脱口而出1个,那么面试官八成会狡黠一笑,让你回家等通知去了。

那么答案是什么呢?是1个或者2个。什么,你逗我呢?

先别急,我们分情况讨论。首先是c++11之前。

在c++11前题目里的表达式实际上会导致下面的行为:

  1. 首先"hello c++"const char[N]类型的,不过它在表达式中于是退化成const char *
  2. 然后因为s实际上是处于“声明即定义”的表达式中,因此适用的只有复制构造函数,而不是重载的=
  3. 因此等号的右半边必须也是string类型
  4. 因为正好有从const char *string的转换规则,因此把它转换成合适的类型
  5. 转换完会返回一个新的string的临时量,它会作为参数调用复制构造函数
  6. 复制构造函数调用完成后s也就创建完毕了。

在这里我们暂且忽略了string的写时复制等黑科技,整个过程创建了s和一个临时量,一共两个string。

很快c++11就出现了,同时还带来了移动语义,然而结果并没有改变:

  1. 前面步骤相同,字符串字面量隐式转换成string,创建了一个临时量
  2. 临时量是个右值,所以绑定给右值引用,因此移动构造函数被选择
  3. 临时量里的数据移动到s里,s创建完成

移动语义减少了不必要的内部数据的复制,但是临时量还是会被创建的。

有进捣鼓编译器的朋友可能要说了,编译器是不生成这个临时量的。是这样的,编译器会用复制省略(copy elision)优化这段代码。

是的,复制省略在c++11里就已经被提到了,不过那时候它是可选的,并不强制编译器支持这一优化。因此你在GCC和clang上观察到的不一定能代表全部的c++编译器的情况,所以我们仍以标准为基础推演了理论上的行为。

到目前为止答案都是2,然而很快有意思的事情发生了——复制省略在c++17里成为了被标准化的行为。

在c++17里除非必要,否则临时量(现在叫做右值的结果对象,一个右值只有在实际需要存在一个临时变量的情况下才会创建一个临时变量,这个过程叫做实质化,创建出来的那个临时量就是该右值的结果对象)不会被创建,换而言之,T obj = expr这样的形式会以expr产生结果直接调用合适的构造函数,而不会进行临时量的创建和复制构造函数的调用,不过为了保证语义的完整性,复制构造函数仍然被要求是可访问的,毕竟类本身不允许复制构造的话复制初始化本身就是不正确的,不能因为复制省略而导致错误的代码被编译通过。

所以现在过程变成了下面这样子:

  1. 编译器发现表达式是string的复制初始化
  2. 右侧是表达式会隐式转换产生一个string的纯右值用于初始化同一类型的s
  3. 判断复制构造函数是否可用,然后发现符合复制省略的条件
  4. 寻找string里是否有符合要求的构造函数
  5. 找到了string::string(const char *),于是直接调用
  6. s初始化完成

因此,在c++17下只会创建一个string对象,这比移动语义更加高效。这也是为什么我说题目的答案既可以是1也可以是2的原因。

同时我们还发现,在复制构造时的类型转换不管复制有没有被省略都是存在的,只不过换了一个形式,这就是我们后面要讲的内容。

总结:尽量不要去依赖隐式类型转换,多用explicit和各种显式转换,少想当然。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

[leetcode] 68. 文本左右对齐

文章目录 题目描述解题方法贪心java代码复杂度分析 题目描述 给定一个单词数组 words 和一个长度 maxWidth &#xff0c;重新排版单词&#xff0c;使其成为每行恰好有 maxWidth 个字符&#xff0c;且左右两端对齐的文本。 你应该使用 “贪心算法” 来放置给定的单词&#xff…

运用远期交易防范外汇风险

随着全球化的深入&#xff0c;跨境贸易和投资愈加频繁&#xff0c;外汇风险成为各类企业和投资者必须面对的现实问题。汇率的波动可能导致交易和投资的成本大幅增加&#xff0c;甚至引发利润损失。在这种情况下&#xff0c;远期交易作为一种有效的外汇风险对冲工具&#xff0c;…

2023年全国职业院校技能大赛(高职组)“云计算应用”赛项赛卷1(私有云)

#需要资源&#xff08;软件包及镜像&#xff09;或有问题的&#xff0c;可私聊博主&#xff01;&#xff01;&#xff01; #需要资源&#xff08;软件包及镜像&#xff09;或有问题的&#xff0c;可私聊博主&#xff01;&#xff01;&#xff01; #需要资源&#xff08;软件包…

源代码加密

代码加密&#xff0c;特别是源代码加密&#xff0c;是一种安全措施&#xff0c;旨在保护软件的源代码不被未授权访问或泄露。源代码是软件应用程序的原始编程文本&#xff0c;它包含了程序的逻辑、算法和设计思想。由于源代码包含了软件的核心知识&#xff0c;因此它具有极高的…

数智算网,链启未来 | 算力网络子链诚邀各方加入

4月28日&#xff0c;在中国移动算力网络大会期间&#xff0c;由中国移动集团主办&#xff0c;中国移动研究院和云能力中心联合承办的“数智算网&#xff0c;链启未来”共链行动算力网络专场会议成功召开。中国移动研究院副院长段晓东&#xff0c;中国移动集团首席专家、云能力中…

MySQL·内置函数

目录 函数 日期函数 案例1&#xff1a;创建一张表&#xff0c;记录生日 案例2&#xff1a;创建一个留言表 案例3&#xff1a;请查询在2分钟内发布的帖子 字符串函数 案例1&#xff1a; 获取emp表的ename列的字符集 案例2&#xff1a;要求显示exam_result表中的信息&am…

Vinted店铺总被封号?如何有效养号?

Vinted是一家欧洲知名的二手时尚交易平台&#xff0c;致力于连接买家和卖家&#xff0c;让他们能够在平台上买卖二手时尚商品。用户可以在Vinted上销售和购买服装、鞋子、配饰等各种时尚物品&#xff0c;无论是品牌商品还是非品牌商品&#xff0c;都可以在平台上找到。Vinted的…

idea修改maven项目名称及子模块名称

一、修改目录名称 shift F6修改目录&#xff0c;选择“rename module and dictionary”。![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/43efd9c6af6e43ad9656455db94b37a2.png)二、修改子项目pom的 三、修改父项目pom的 四、刷新maven项目

JS笔试手撕题

数据劫持 Vue2的Object.defineProperty() Vue2的响应式是通过Object.defineProperty()拦截数据&#xff0c;将数据转换成getter/setter的形式&#xff0c;在访问数据的时候调用getter函数&#xff0c;在修改数据的时候调用setter函数。然后利用发布-订阅模式&#xff0c;在数…

Windows下启动Tomcat显示乱码解决办法

1、Windows下启动Tomcat显示乱码 2、解决办法 找到 D:\apache-tomcat-9.0.89\conf下的logging.properties&#xff0c;找到java.util.logging.ConsoleHandler.encoding的值改为GBK&#xff0c;就可以了 完美解决&#xff01;显示正常的中文了

网络安全之二层局域网封装及广域网封装详解

局域网封装&#xff1a;Ethernet2&#xff08;TCP/IP&#xff09;&#xff0c;IEEE802.3&#xff08;OSI&#xff09;&#xff08;前面文章中讲解了TCP、IP和OSI本文就不继续讲解&#xff1a;可以查看&#xff1a;网络安全之OSI七层模型详解-CSDN博客&#xff09; 广域网封装&…

『ZJUBCA Collaboration』WTF Academy 赞助支持

非常荣幸宣布&#xff0c;浙江大学区块链协会收到WTF Academy的赞助与支持&#xff0c;未来将共同开展更多深度合作。 WTF Academy是开发者的Web3开源大学&#xff0c;旨在通过开源教育让100,000名开发者进入到Web3。截止目前&#xff0c;WTF开源教程在GitHub收获超15,000 ⭐&a…

环形链表问题详解

引言 环形链表的题大家都应该做过&#xff0c;如果没有做过可以去某扣上做一下 ,下面有传送门 141. 环形链表 - 力扣&#xff08;LeetCode&#xff09;https://leetcode.cn/problems/linked-list-cycle/submissions/530160081/ 正文 如果在面试的情况下出现了环形链表的题大…

QLineEdit 最右侧添加按钮

如果采用QLineEdit + QPushButton的方式的话,无法将按钮放到QLineEdit的输入框内部,所以下面的方法可以将按钮放到QLineEdit内部的最右侧,效果: 代码如下: QLineEdit* editor = new QLineEdit(parent); QToolButton* btn = new QToolButton; btn->setText("...&q…

【噪声学习】噪声标签的鲁棒点云分割

Robust Point Cloud Segmentation with Noisy Annotations 事实上,与二维图像标注[1]、[2]相比,三维数据的干净标签更难获得。这主要是因为1)需要标注的点数通常非常庞大,例如在 ScanNetV2 [3] 中标注一个典型的室内场景时,需要标注百万量级的点数;2)标注过程本身更加复…

使用SmartEDA电路仿真软件,让这五件事变得很简单

SmartEDA电路仿真软件&#xff1a;轻松实现电子设计五大突破 在电子设计领域&#xff0c;SmartEDA电路仿真软件以其强大的功能和用户友好的界面&#xff0c;成为了设计师们的得力助手。这款软件不仅简化了电路设计流程&#xff0c;还提高了设计效率&#xff0c;让以下五件事情…

【驱动】I2C读写时序

1、I2C总线 I2C使用两条线在主控制器和从机之间通信,SCL(串行时钟线)和SDA(串行数据线),这两条线需接5~10欧上拉电阻,总线空闲空闲时,SCL和SDA处于高电平,I2C总线标准模式速度可以达到100K/S,快速模式可以达到400K/S。 2、状态 I2C总线有四种状态:空闲、启动、忙碌、…

品质为王:高效溶解性鱼油胶囊的软胶囊弹性硬度测试解析

品质为王&#xff1a;高效溶解性鱼油胶囊的软胶囊弹性硬度测试解析 在当今的健康产品市场中&#xff0c;高效溶解性鱼油胶囊以其独特的营养价值和吸收效率赢得了众多消费者的青睐。然而&#xff0c;要想在激烈的市场竞争中脱颖而出&#xff0c;产品的品质保证至关重要。其中&a…

RabbitMQ-基础

RabbitMQ 同步调用 双方交互都是实时的&#xff0c;可以立即返回结果 问题 拓展性差&#xff1a;每次有新的需求&#xff0c;代码经常变动&#xff0c;不符合开闭原则性能下降&#xff1a;调用者需要等待服务提供者分别执行后才返回结果&#xff0c;服务提供者很多情况下会…

看完这个,你就懂了!IT审计到底是干什么的?如何做好IT审计?

01 大家应该都知道财务审计&#xff0c; 通俗讲&#xff0c;就是查账的。 看一下公司账上的数据是否准确&#xff0c; 每笔账是否都能合理溯源。 那IT审计到底是干什么的呢&#xff1f; 它和财务审计有什么关系吗&#xff1f; 这么跟你说吧&#xff0c; 现在很多公司都…
最新文章