C++临时对象以及针对其进行的优化
C++临时对象以及针对其进行的优化
C++中真正的临时对象是看不见的,它们不出现在你的源代码中。
那么什么时候回产生临时对象呢?主要是三个时刻:
产生临时对象的三个时刻:
用构造函数作为隐式类型转换函数时,会创建临时对象
看个例子:
#include <iostream>
using namespace std; #include <iostream>
#include <stdlib.h>
#include <string.h>
using namespace std; class A{
public:
int a;
A(int x)
{
a = x;
}
A(const A &re )
{
a = re.a;
}
}; int main()
{ A ca = ; return ;
}
在主函数中,我们直接用一个整型量10对对象ca进行初始化,这个时候实际上应该有如下步骤:
1、 调用构造函数构造一个临时对象
2、 调用拷贝构造函数,用临时对象对ca进行初始化。
建立一个没有命名的非堆(non-heap)对象,也就是无名对象时,会产生临时对象。
这种情况,我们和下面一种情况一起说。
函数返回一个对象值时,会产生临时对象,函数中的返回值会以值拷贝的形式拷贝到被调函数栈中的一个临时对象。
看一个例子:
#include <iostream>
using namespace std; #include <iostream>
#include <stdlib.h>
#include <string.h>
using namespace std; class A{
public:
int a;
A(int x)
{
a = x;
}
A(const A &re )
{
a = re.a;
}
}; A getA()
{
return A();
} int main()
{
A ba = getA();
return ;
}
其中在getA函数中,return语句应该有如下过程
1、 A(200)会产生一个匿名的临时对象(临时对象位于函数的栈区)
2、 用拷贝构造函数,将匿名的临时对象拷贝到返回区的一个临时对象中,用于返回
函数调用时,还应该有一工程就是,调用拷贝构造函数用函数返回区的临时对象初始化ba对象。
上面就是产生临时对象的三种情况,并且我们也对相应的语句,执行的过程进行了简单的分析。
在分析的过程中,我们注意一个问题,就是,临时对象,实际上仅仅是一个桥梁作用,比如第一种情况中A ca = 10这条语句,我们的本意就是将ca对象中的成员变量初始化为10,临时变量这个东西,我们是不关注的。再比如说第二个例子中A ba = getA(),我们的目的就是用200初始化ba对象的数据成员,但这个过程中却必须要多生成两个临时对象,一个是匿名的位于函数栈区的临时对象,另一个是函数返回去的临时对象,这显然很浪费时间和空间。但是,以前的C++标准中,必须产生一个这样的临时对象。我们都知道,对象的构造和析构要涉及空间的分配和释放等,是很影响效率的。所以临时对象这东西一直被认为是影响C++效率的一个诟病。
新的C++标准中都允许对涉及临时对象的部分进行优化。当然不同的编译器有不同的优化实现方法。这次实验我用的g++
下面就来看一下。
针对临时变量进行的优化
我们先看针对第一种情况的优化。
还是看例子吧:
#include <iostream>
using namespace std; #include <iostream>
#include <stdlib.h>
#include <string.h>
using namespace std; class A{
public:
int a;
A(int x)
{
cout<<"构造函数被调用!this = "<<this<<endl;
a = x;
}
A(const A &re )
{
cout<<"拷贝构造函数被调用!this = "<<this<<endl;
a = re.a;
}
~A()
{
cout<<"析构函数被调用!\n";
}
}; int main()
{ A ca = ;
cout<<"ca = "<<ca.a<<"地址为:"<<&ca<<endl;
cout<<"---------------------------------------------\n"; return ;
}
结果为:
我们发现,和我们之前分析的不一样,如果根据我们的分析,会调用一次普通构造函数和一次拷贝构造函数,分别用于构造临时对象和用临时对象初始化ca对象。但实际结果却只调用了一次普通的构造函数。这就是编译器进行优化后的结果,而且根据两次输出的地址信息,我们可以确定,优化后是直接对ca对象进行了构造。这种优化方法叫做Copy Elision(复制的省略)
g++中可以用选项-fno-elide-constructors禁止这种优化
用该选项后,输出的结果为:
可以看到,进制优化后,和我们之前的分析就对上了。
在来看针对第二、三种情况的优化
#include <iostream>
using namespace std; #include <iostream>
#include <stdlib.h>
#include <string.h>
using namespace std; class A{
public:
int a;
A(int x)
{
cout<<"构造函数被调用!this = "<<this<<endl;
a = x;
}
A(const A &re )
{
cout<<"拷贝构造函数被调用!this = "<<this<<endl;
a = re.a;
}
~A()
{
cout<<"析构函数被调用!\n";
}
}; A getA()
{
return A();
} A getA2()
{
A temp();
cout<<"getA2即将返回!\n";
return temp;//这里的temp就是一个将亡值
} int main()
{
A ba = getA();
cout<<"ba.a = "<<ba.a<<"地址为:"<<&ba<<endl;
cout<<"------------------------------------\n"; A da = getA2();
cout<<"da.a = "<<da.a<<"地址为:"<<&da<<endl;
cout<<"------------------------------------\n";
return ;
}
代码中有两个函数,区别是一个直接返回一个匿名的对象,另一个则是返回一个命了名的对象。
看结果:
我们可以看到,这两个函数的过程是一样的,都是直接对对象进行了构造(这一点可以根据输出的地址信息确定),而没有临时对象什么事儿。这也是编译器优化的结果,这种优化方法叫做RVO(return value optimization返回值优化),特别地,对第二个函数的返回值的优化叫做NRVO(命名返回值优化)。需要说明的是,在g++中,两个函数的优化方式相同,但是在VS中却不同(debug不同,release和g++中相同),这里就略过了。
如果禁止编译器优化,是不是结果和我们之前分析的过程相对应呢?答案是肯定的。
可以看到,都调用了两次拷贝构造函数,一次是从函数栈区的临时对象到返回区的临时对象,领一次是从返回去的临时对象到主函数中的对象。
这里还要捎带提一下,构造函数中的参数用了const修饰,这是因为,当用临时对象初始化时,会调用拷贝构造函数,而临时对象只能用const引用,这就意味着,我们不能对临时对象进行什么操作(但是在vs中竟然可以不用const修饰,不知道为什么,但是无论如何,在C++11没有引入右值引用之前,都是不能对临时对象进行修改的)。
关于右值引用,我下一篇随笔中会简要介绍一下
补充:
最近又遇到了这样的面试题,补充一下:
有
情况1---------------------------------------------------------------------------------------------------------------------------------
class A
A getA()
{
A temp(100);
return temp;
}
A a = getA();
对于VS,存在优化,
对于debug:在函数中直接用普通构造返回区临时对象,然后调用拷贝构造函数构造a;
对于release:在函数中直接用普通构造函数构造a。
对于g++,存在优化:
和release一样。
情况2----------------------------------------------------------------------------------------------------------------------------------------------------
class A
A getA()
{
return A(100);
}
A a = getA();
对于VS,存在优化:
对于debug和release都是直接构造a
对于g++,存在优化:
和VS相同
情况3---------------------------------------------------------------------------------------------------------------------------
class A
A getA(A temp)
{
return temp;
}
A a(100);
A b = getA(a);
对于VS,存在优化:
对于debug,release,都是:函数形实结合的时候,实参不和temp结合,而是直接和返回区临时对象结合,然后再用返回区临时对象构造b
对于g++,存在优化:
和vs一样
情况4--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
class A
A getA()
{
A temp(100);
return temp;
}
A a(100);
a = getA();
对于vs,存在优化:
对于debug,不存在优化,会构造temp,存在temp到返回区临时对象的拷贝构造。
对于release,存在优化,会构直接构造返回区临时对象。
对于g++,存在优化:
和release一样
情况5----------------------------------------------------------------------------------------------------------------------------------------------------------------
class A
A getA()
{
return A(100);
}
A a;
a = getA();
对于vs,存在优化
debug和release都是:在函数中,直接构造返回区临时对象
情况6------------------------------------------------------------------------------------------------------------------------------------------------------------------
class A
A getA(A temp)
{
return temp;
}
A a(100);
A b;
b = getA(a);
对于VS和debug都没有优化,都存在:形实结合时拷贝构造temp,temp到临时对象的拷贝构造。
如果你觉得对你有用,请赞一个吧~~~
C++临时对象以及针对其进行的优化的更多相关文章
- 与临时对象的斗争(上)ZZ
C++ 是一门以效率见长的语言(虽然近来越来越多的人“不齿”谈及效率,我深以为不然,在某一次的程序编写中不对效率锱铢必较并不意味意味着我们就不应该追求更多的更好的做法).总之吧,相比起其它语言,程序员 ...
- STL学习笔记--临时对象的产生与运用
所谓的临时对象,就是一种无名对象(unnamed objects).它的出现如果不在程序员的预期之下,往往造成效率上的负担.但有时刻意制造一些临时对象,却又是使程序干净清爽的技巧.刻意制造临时对象的方 ...
- 【编程篇】C++11系列之——临时对象分析
/*C++中返回一个对象时的实现及传说中的右值——临时对象*/ 如下代码: /**********************************************/ class CStuden ...
- SQL Server 内置函数、临时对象、流程控制
SQL Server 内置函数 日期时间函数 --返回当前系统日期时间 select getdate() as [datetime],sysdatetime() as [datetime2] getd ...
- 认识C++中的临时对象temporary object 分类: C/C++ 2015-05-11 23:20 137人阅读 评论(0) 收藏
C++中临时对象又称无名对象.临时对象主要出现在如下场景. 1.建立一个没有命名的非堆(non-heap)对象,也就是无名对象时,会产生临时对象. Integer inte= Integer(5); ...
- C++中临时对象的学习笔记
http://www.cppblog.com/besterChen/category/9573.html 所属分类: C/C++/STL/boost 在函数调用的时候,无论是参数为对象还是返回一个对 ...
- C++ 临时对象
1.什么是临时对象? swap方法中,常常定义一个temp对象,这个temp对象不是临时对象,而是局部对象.这里所说的临时对象是不可见的,在原代码中是看不到的. 2.为什么会产生临时对象? a.客户期 ...
- 【M19】了解临时对象的来源
1.首先,确认什么是临时对象.在swap方法中,建立一个对象temp,程序员往往把temp称为临时对象.实际上,temp是个局部对象.C++中所谓的临时对象是不可见的,产生一个non-heap对象,并 ...
- STL——临时对象的产生与运用
所谓临时对象,就是一种无名对象.它的出现如果不在程序员的预期之下(例如任何pass by value操作都会引发copy操作,于是形成一个临时对象),往往造成效率上的负担.但有时候刻意制造一些临时对象 ...
随机推荐
- 关于IE浏览器的一些思路
首先说说我对IE的看法: 第一感觉倔强.孤僻特立独行.(总是和别人不一样,是初学者的噩梦) 第二感觉个性(每个版本都需要你去用不同的代码去适配她) 虽然现在IE已经升级到了11相对于以前的IE8,IE ...
- SSH:dataSource配置问题
applicationContext.xml <?xml version="1.0" encoding="UTF-8"?> <beans xm ...
- [Spring面试] 问题整理
1.谈谈你对spring IOC和DI的理解,它们有什么区别? IoC:Inverse of Control 反转控制的概念,就是将原本在程序中手动创建UserService对象的控制权,交由Spri ...
- 第三篇--Jmeter测试数据库Mysql
Jmeter模拟100用户访问Mysql数据库 1.将Mysql数据库的驱动[mysql-connector-java-5.1.15-bin.jar]放到jmeter的lib目录下,新建线程组100[ ...
- 深度学习在 CTR 中应用
欢迎大家前往腾讯云技术社区,获取更多腾讯海量技术实践干货哦~ 作者:高航 一. Wide&&Deep 模型 首先给出Wide && Deep [1] 网络结构: 本质上 ...
- Redis介绍和环境安装
-------------------Redis环境安装------------------- 1.安装 1.卸载软件 sudo apt-get remove redis-se ...
- Haproxy基于ACL做访问控制
author:JevonWei 版权声明:原创作品 haproxy配置文档 https://cbonte.github.io/haproxy-dconv/ 基于ACL做访问控制(四层代理) 网络拓扑 ...
- LVS之-LAMP搭建wordpress
author:JevonWei 版权声明:原创作品 LVS搭建wordpress,涉及的知识点有DNS,LAMP,NFS及LVS 网络拓扑图 网络环境 NFS 192.168.198.130 mysq ...
- C#将Excel数据表导入SQL数据库的两种方法(转)
最近用写个winform程序想用excel 文件导入数据库中,网上寻求办法,找到了这个经过尝试可以使用. 方法一: 实现在c#中可高效的将excel数据导入到sqlserver数据库中,很多人通过循环 ...
- Java学习记录 : 画板的实现
接触java不满一个月,看厚厚的java入门简直要醉,故利用实例来巩固所学知识. 画板的实现其实从原理来说超级简单,可能一会儿就完成了. 但作为一名强迫症患者,要实现和win下面的画板一样的功能还是需 ...