您的位置:首页 > 编程语言 > C语言/C++

C++性能分析之临时对象

2014-08-31 22:01 381 查看
以前说过:C++临时对象的创建和销毁对程序性能有一定的影响,尤其当该对象的类处于一个复杂继承体系的末端,或者该对象包含
很多成员变量对象时,对程序的影响尤为明显。

避免创建不必要的对象,不仅意味着在编程时,主要减少显示出现在源码中的对象创建,还有在编程过程中,编译器在某些特定情况下
生成的隐士的临时对象,这些对象创建并不出现在源码中,而是由编译器在编译过程中“悄悄生成”,并在适当的时期进行销毁。
临时变量的概念:
voidswap(int*px,int*py)

{
inttemp;
//这时的temp临时变量(并不是这里说的“临时对象”)
temp=
*
px;
*px=
*
py;
*py=
temp;
}
我们现在讨论的临时对象并不会出现在源码中:
首先我们先看如下的代码:
#include"stdafx.h"
#include<iostream>
usingnamespacestd;
constintsize=
5;


classMatrix
{
private:
intmVal[size][size];
public:
//Matrix(void){}
Matrix(intm=1)
{
cout<<"Matrix::Matrix()"<<endl;
for(inti=0;i<size;i++)
for(intj=0;j<size;j++)
mVal[i][j]=
m;
}

Matrix(constMatrix&src)
{
cout<<"Matrix(constMatrix&
src)"<<endl;
memcpy(this,&src,sizeof(Matrix));
}

Matrix&operator=(constMatrix&src)
{
if(this==
&
src)
return*this;
cout<<"Matrix&operator=(const
Matrix& src)"<<endl;
memcpy(this,&src,sizeof(Matrix));
return*this;
}
~Matrix(){}
friendconstMatrixoperator+(constMatrix&,constMatrix&);
};

constMatrixoperator+(constMatrix&left,constMatrix&right)
{
Matrixsum;
for(inti=0;i<size;i++)
for(intj=0;j<size;j++)
sum.mVal[i][j]=
left.mVal[i][j]+
right.mVal[i][j];
returnsum;
}

int_tmain(intargc,_TCHAR*argv[])
{
Matrixa(1),b(2),c;
c=
a+b;
return0;
}

从控制台我们可以看出有四个构造函数,有一个拷贝函数,还有一个赋值运算。
我们分析下:首先主函数Matrixa,b,c等三个,第四个应该是Matrixsum的构造函数。
接下来是Matrix(constMatrix&src)的输出这时应该是a+b返回时通过Matrix拷贝函数构造
最后是Matrix&operator=(constMatrix&src)的输出,这时比较明显,是由于=的使用。
int_tmain(intargc,_TCHAR*argv[])
{
Matrixa(1),b(2);
Matrixc=
a+b;
return0;
}
当我们将主函数里面的代码写成这样的时候,会明显少一个输出,
此时并不会执行赋值运算符,

这时,我们发现在使用重载运算符+号的时候,明显出现了一个临时对象,那么我们如何规避呢?
这是一个很重要的问题,值得研究与讨论

产生临时对象的一般来说会有如下两种场合。
1>当实际调用函数时传入的参数与函数定义的声明的变量类型不匹配
2>当函数返回一个对象时

有时候因为类型不匹配而生成临时对象的情况,下面写一段代码:
#include"stdafx.h"
#include<iostream>
usingnamespacestd;
classTest
{
private:
intm;
intn;
public:
Test(intm1=0,intn1=1):m(m1),n(n1){}
~Test(){}
};

int_tmain(intargc,_TCHAR*argv[])
{
Testr;
r=
2;

return0;
}
在执行上段代码r=2时,编译器会在此处合成一个operator=(constTest&),并且执行逐位拷贝形式的赋值操作,
但是右边的一个整型常量并不是一个Test对象,乍看此处是无法编译成功的,但是C++编译器总是寻找合适的
转换路径,以满足编译的需要。
可能这时编译器会通过调用Test::Test(2,1)生成一个临时对象,进而使得编译能够通过。
Test(intm1=0,intn1=1):m(m1),n(n1){
cout<<m<<"
"<<n<<endl;
}
我们将构造函数的里面内容显示出来,可以看到的确显示了这个数据。
从上面的例子可以看见,C++编译器为了成功编译某些程序,往往会在私底下悄悄生成很多“不为人知”的函数或者对象。
那么我们如何阻止这种事情的发生呢?
我们在构造函数里面可以使用关键字explicit此时上序代码则会出现问题。

如果我们还想这样实现则可以重载=运算符。
但是这样做会让代码显得比较臃肿。

还有一种往往导致临时对象的产生,即当一个函数返回的是某个非内建类型的对象时,这时因为返回结果必须要有一个
地方存放。所以编译器会从调用该函数的函数对象堆栈中开辟空间,并用返回值作为参数调用该对象所属类型的拷贝
构造函数在此空间中生成该对象。在被调用函数结束并返回时,可以继续利用此对象。
#include"stdafx.h"
#include<iostream>
usingnamespacestd;
constintsize=
5;


classTest
{
private:
intmVal;
intnVal;
public:
Test(intm=1,intn=2):mVal(m),nVal(n)
{
cout<<"Test::Test()"<<endl;
}

Test(constTest&src):mVal(src.mVal),nVal(src.nVal)
{
cout<<"Test(constTest&
src)"<<endl;
}

Test&operator=(constTest&src)
{
if(this==
&
src)
return*this;
mVal=
src.mVal;
nVal=
src.nVal;
cout<<"Test&operator=(const
Test& src)"<<endl;
return*this;
}
~Test(){}
friendconstTestoperator+(constTest&,constTest&);
};

constTestoperator+(constTest&left,constTest&right)
{
cout<<"start"<<endl;
Testtemp;
temp.mVal=
left.mVal+right.mVal;
temp.nVal=
left.mVal+right.nVal;
cout<<"end"<<endl;
returntemp;
}

int_tmain(intargc,_TCHAR*argv[])
{
Testa(1),b(2);
Testc=
a+b;
return0;
}

当这样使用时会减少一个临时对象的产生(上面介绍过)
constTestoperator+(constTest&left,constTest&right)
{
cout<<"start"<<endl;
returnTest(left.mVal+right.mVal,right.nVal+left.nVal);
}
这是又会出现一个现象会省去临时temp计算的多余。
大家对于临时对象一定要慎重。例如以下的代码:
stringa="123",b="456789";
constchar*str;
if(strlen(str=(a+b).c_str())>5)
cout<<str<<endl;
return0;
以上代码会有一些问题,cout中的str是否还是非法的!!(声明周期)

临时对象!!!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: