C++编译器优化问题-程序员宅基地

技术标签: C++  c++  开发语言  

在这里插入图片描述

编译器优化问题

首先在之前我们提到过编译器优化,就是下面这段代码。

int main()
{
    
	string s = "hello";
	return 0;
}

这句代码理论上应该是先使用hello创建一个临时对象,然后用临时对象拷贝构造s对象,但是在现代大多数编译器下,都会优化掉临时对象的创建,直接用这个字符串构造对象s。

在谈编译器优化的时候要说明,编译器的优化并不是绝对的,有的编译器会做出优化但是有的编译器又没有优化。因为这个是C++标准未定义的。
以下的测试环境是VS2019;

编译器的优化行为一般发生在一个表达式中,如果产生了一个临时对象,然后又用这个临时对象去构造一个对象,可能优化成直接去构造这个对象,就不会产生临时对象了。
总结说就是:在一个表达式中,连续多个构造函数(构造和拷贝构造)可能会被优化成一次,优化掉的一般都是临时对象或者匿名对象的创建。注意如果是构造函数和赋值,这是不能优化的(例如,拷贝构造+赋值这是不可优化的),必须是多个构造函数。

下面我们使用几段代码来测试编译器优化,首先定义这个类,每调用一次构造函数或者是拷贝构造或者是析构和赋值运算符重载就会打印一次。

class A
{
    
public:
	A()
	{
    
		cout << "A()" << endl;
	}
	A(const A& a)
	{
    
		cout << "A(const A& a)" << endl;
	}

	~A()
	{
    
		cout << "~A()" << endl;
	}
	A& operator=(const A& a)
	{
    
		cout << "A& operator=(const A& a)" << endl;
	}
private:

};

来看第一段代码

A test1()
{
    
	static A aa;
	return aa;
}

int main()
{
    
	A a1 = test1();
	return 0;
}

分析,首先,静态对象只会在第一次调用test1函数的时候调用一次构造函数。全局对象是在main函数调用之前构造的。

上面代码首先调用了test1函数构造了aa对象,然后使用值返回用aa对象拷贝构造了一个临时对象,然后调用拷贝构造函数构造出来了a1对象,所以一共调用了一次构造两次拷贝构造。最后应该还有三次析构。

来看答案:在这里插入图片描述

这里只调用了一次构造函数和一次析构函数。所以编译器进行了优化,那么优化的地方在哪呢?少了一次拷贝构造说明返回值的时候没有拷贝构造临时对象,而是直接拷贝构造出来了a1对象。但是上面是static对象,出来函数作用域不会销毁所以可以使用传引用返回。

在这里插入图片描述

这里再看如果使用赋值,因为赋值和上段代码的拷贝构造是不同的,这里的赋值是不可以优化的。所以就会出现两次构造,a1和aa然后使用aa拷贝构造临时对象,最后调用了赋值运算符重载函数。

当然如果使用传引用,返回那么就可以减少一次拷贝构造
在这里插入图片描述

所以如果对象出了函数作用域是不会销毁的话,尽量使用传引用返回。

所以可以看到,在一个表达式中,连续多个构造可能会被编译器优化成一次

再来看最后一个例题:

A test(A aa)
{
    
	A copy1(aa);
	A copy2 = copy1;
	return copy2;
}

int main()
{
    
	A a;
	A ret = test(test(a));
	return 0;
}

来看这段代码,调用了几次拷贝构造函数呢?
在这里插入图片描述

这里是图解,如果假设编译器没有优化,是总共调用了9次拷贝构造,两个方框代表了两个临时对象。
首先使用a拷贝构造了形参aa,然后aa拷贝构造了copy1,copy1拷贝构造了copy2(注意这里虽然是等号也是拷贝构造,因为这里的copy2还是对象创建初始化阶段,只有对象创建出来之后再给对象赋值才是赋值)copy2拷贝构造了一个临时对象然后传参给test,使用这个临时对象1拷贝构造了aa然后aa重复上面再函数中的拷贝构造,返回值,因为是值返回所以先再main函数的栈帧内开辟一个临时对象2,调用了一次拷贝构造,然后再用哦这个临时对象2,拷贝构造了ret对象。至此总共调用了9次拷贝构造函数。

下面来看一下结果

在这里插入图片描述

结果是7次拷贝构造,想必优化了那两个拷贝构造应该很明了了,就是优化了两个临时对象的拷贝构造,直接使用了返回值去拷贝构造目标对象。
因为这里的返回值和传参可以看作是一个表达式,并且有多个构造函数,所以就发生了优化。

其次关于传值返回,如果是对象比较小话一般是使用寄存器保存返回值然后返回。如果对象比较大就会拷贝这个对象一次,然后再用拷贝出来的临时对象作为返回值,所以临时对象一定不在test栈帧中,一般是在需要接受test返回值的栈帧之中,临时对象具有常属性,只读不可修改。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_62745420/article/details/125837087

智能推荐

ffmpeg获取设备支持的分辨率_FFmpeg 基础知识-程序员宅基地

文章浏览阅读632次。FFmpeg 是一套可以用来记录、转换数字音频、视频,并能将其转化为流的开源计算机程序。采用 LGPL 或 GPL 许可证。它提供了录制、转换以及流化音视频的完整解决方案。它包含了非常先进的音频/视频编解码库 libavcodec。官方网站:http://www.ffmpeg.org/项目组成libavformat封装模块,封装了Protocol层和Demuxer、Muxer层,使得协议和格式对于..._ffmpeg读取分辨的库

OpenWRT编译 -- 静态编译驱动到内核中的方法_openwrt中添加驱动编译进内核-程序员宅基地

文章浏览阅读9.6k次。申明:小弟openwrt初学,很多东西都不知道,在偶然的机会用到openwrt,想要将自己写的驱动程序编译进内核中,费了九牛二虎之力,最终还是直接在内核源码中进行增加才搞定,过程尽可能的详细,也是自己的一个学习过程,也是给自己做一个笔记后面遇到可以借鉴。另外,第一次写博客,希望大家不喜勿喷哦,当然文档中有任何问题都欢迎能指出来,而且在过程中遇到了一个问题到现在没有找出为什么,希望大家看到了..._openwrt中添加驱动编译进内核

Unity2D 面向目标方向-程序员宅基地

文章浏览阅读1.7k次。在2d空间上,假设角色的自身的y轴方向为正方向,如果要让角色随时面向一个目标点。这里假设(0,0)点为目标点第一种:Vector3 v = Vector3.zero - transform.position; //首先获得目标方向 v.z = 0; ..._unity 2d中哪个方向是forward

基于XDMA 的PCIE读写DDR_xdma ddr-程序员宅基地

文章浏览阅读7.4k次,点赞16次,收藏100次。基于XDMA 的PCIE读写DDR概述:  想实现基于FPGA的PCIe通信,查阅互联网各种转载…基本都是对PCIe的描述,所以想写一下基于XDMA的PCIe通信的实现(PCIe结构仅做简单的描述(笔记),了解详细结构移至互联网)。实现功能:PC通过PCIE读写DDR,同时用户通过逻辑代码可以读取被写入DDR内的数据(我是通过VIO实现DDR任意地址,任意数据大小的读取。)。实践实践!!!说明:参考文档:PCI Express Base Specification Revision 3.0P_xdma ddr

缓存穿透与布隆过滤器_缓存穿透 布隆过滤器-程序员宅基地

文章浏览阅读1.7k次。本文主要介绍在使用缓存过程中经常会遇到的几个问题:缓存击穿、缓存雪崩、缓存穿透,以及其解决方案。之后会对缓存穿透的解决方案之一布隆过滤器,进行详细讲解。_缓存穿透 布隆过滤器

pandas写入oracle,python pandas dataframe 读取和写入Oracle-程序员宅基地

文章浏览阅读1.4k次。1、代码:主要写入时表要为小写,否则报错Could not reflect: requested table(s) not available in Enginefrom sqlalchemy import create_engineconn_string='oracle+cx_oracle://admin:[email protected]:1521/ORCL?charset=utf8'..._pandas cx_oracle 写入

随便推点

python 基因序列提取_Python + 生物信息 05 : 提取 CDS 等其他特征序列-程序员宅基地

文章浏览阅读1.4k次。1 介绍在基因结构分析或其他生物功能分析中会时常用到 CDS 序列,以及其他诸如 mRNA 序列,misc RNA序列等具有生物意义的序列片段。而NCBI 的基因库中已经包含有这些的信息,但是只有一部分是整理可下载的。而剩下的一部分可以通过 genbank给出的位点信息来提取,个人能力有限,这里只做抛转之用。下面以提取 CDS 为例,记录提取序列过程,其他特征序列类似。2 结构目录3 Python..._提取dna中cds序列

php 获取 mysql多行数据 存在数组中_mysql 将多行数据添加到数组-程序员宅基地

文章浏览阅读753次。个人习惯使用面向对象的方式来操作mysql。首先定义一个二维数组,用来存储结果。结果集取得一行就是一个数组,因为有多行数据,所以是二维。$data=array();这条代码$result->fetch_assoc()是: 从结果集中取得一行作为关联数组:那么就有:while($row = $result->fetch_assoc()){ array_push($data,$row);}不断从结果集读取数据,并将每行数据加到二维数组data中,直到读取完毕_mysql 将多行数据添加到数组

Python学习系列之zip函数_python in zip-程序员宅基地

文章浏览阅读677次。目录一、zip函数(内建函数)1.1 定义基本语法:参数说明:返回值:示例:1.2 处理列表1.3 处理元组 1.4 处理字典 1.5 处理一个参数和空列表一、zip函数(内建函数)1.1 定义 Python 2:zip() 函数用于将可迭代的对象(字典,列表,元组,集合,字符串等)作为参数,将对象中..._python in zip

探索网络攻防技术:自学之道_网络攻防自学-程序员宅基地

文章浏览阅读383次。网络攻防技术在不断演进,新的威胁和安全措施不断出现。因此,持续学习和跟进是至关重要的。订阅安全博客、参加安全研讨会、阅读最新的安全报告等方式,可以帮助你保持对新技术和趋势的了解。_网络攻防自学

0-1字典树总结和经典例题(ing)_字典树例题 poj-程序员宅基地

文章浏览阅读1.1k次,点赞2次,收藏5次。Table of Contents0-1字典树例题1. CSU 1216:异或最大值:给定一些数,任意两个数的最大异或值例题2.HDU 4825Xor Sum:每次询问给出一个数,找出一个与它异或结果最大的数例题3.HDU 5536Chip Factory: 计算(s[i] + s[j]) ^ s[k] 的最大值例题4.POJ 3764The xor-longe..._字典树例题 poj

HEVD之栈溢出_通过fs寄存器解决栈溢出-程序员宅基地

文章浏览阅读881次。自学习CVE-2017-11882漏洞后,便开始希望学习更多的漏洞,提高一下对漏洞的理解。在github上找到一个很好的项目,基本涵盖了所有漏洞(https://github.com/hacksysteam/HackSysExtremeVulnerableDriver)。下载下来后,执行过后得到驱动文件HEVD.sys,以及漏洞利用程序HackSysEVDExploit.exe,然后开启双..._通过fs寄存器解决栈溢出

推荐文章

热门文章

相关标签