以前说过:C++临时对象的创建和销毁对程序性能有一定的影响,尤其当该对象的类处于一个复杂继承体系的末端,或者该对象包含
很多成员变量对象时,对程序的影响尤为明显。
避免创建不必要的对象,不仅意味着在编程时,主要减少显示出现在源码中的对象创建,还有在编程过程中,编译器在某些特定情况下
生成的隐士的临时对象,这些对象创建并不出现在源码中,而是由编译器在编译过程中“悄悄生成”,并在适当的时期进行销毁。
临时变量的概念:
voidswap(int*px,int*py)
{
inttemp; //这时的temp临时变量(并不是这里说的“临时对象”)
temp= *px;
*px= *py;
*py= temp;
}
我们现在讨论的临时对象并不会出现在源码中:
首先我们先看如下的代码:
#include"stdafx.h"
#include<iostream>
usingnamespacestd;
const intsize= 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;
const intsize= 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是否还是非法的!!(声明周期)
临时对象!!!