C++中真正的臨時對象是看不見的,它們不出現(xiàn)在你的源代碼中,臨時對象的產(chǎn)生在如下幾個時刻:
1. 用構(gòu)造函數(shù)作為隱式類型轉(zhuǎn)換函數(shù)時,會創(chuàng)建臨時對象。
例:
class Integer
{
public:
Integer(int i)
:m_val(i)
{}
~Integer()
{}
private:
int m_val;
};
void Calculate(Integer itgr)
{
// do something
}
那么語句: int i = 10;
Calculate(i);
會產(chǎn)生一個臨時對象,作為實參傳遞到Calculate 函數(shù)中。
2. 建立一個沒有命名的非堆(non-heap)對象,也就是無名對象時,會產(chǎn)生臨時對象。
如:
Integer& iref = Integer(5); //用無名臨時對象初始化一個引用,等價于
//Integer iref(5);
Integer itgr = Integer(5); //用一個無名臨時對象拷貝構(gòu)造另一個對象
按理說,C++應(yīng)先構(gòu)造一個無名的臨時對象,再用它來拷貝構(gòu)造itgr,由于
該臨時對象拷貝構(gòu)造 itgr 后,就失去了任何作用,所以對于這種類型(只起拷貝構(gòu)造另一個對象的作用)的臨時對象,c++特別將其看做: Integer itgr(5); 即直接以相同參數(shù)構(gòu)造目標(biāo)對象,省略了創(chuàng)建臨時對象這一步。
Calculate( Integer(5) ); //無名臨時對象作為實參傳遞給形參,函數(shù)調(diào)
//用表達(dá)式結(jié)束后,臨時對象生命期結(jié)束,被//析構(gòu).
3. 函數(shù)返回一個對象值時,會產(chǎn)生臨時對象,函數(shù)中的返回值會以值拷貝的形式拷貝到被調(diào)函數(shù)棧中的一個臨時對象。
如:
Integer Func()
{
Integer itgr;
return itgr;
}
void main()
{
Integer in;
in = Func();
}
表達(dá)式 Func() 處創(chuàng)建了一個臨時對象,用來存儲Func() 函數(shù)中返回的對象,臨時對象由 Func() 中返回的 itgr 對象拷貝構(gòu)造(值傳遞),臨時對象賦值給 in后,賦值表達(dá)式結(jié)束,臨時對象被析構(gòu)。見下圖:
看看如下語句:
Integer& iRef = Func();
該語句用一個臨時對象去初始化iRef 引用,一旦該表達(dá)式執(zhí)行結(jié)束,臨時對象的生命周期結(jié)束,便被結(jié)束,iRef引用的尸體已經(jīng)不存在,接下來任何對 iRef 的操作都是錯誤的。
下面,來看看實際的測試結(jié)果,代碼如下:
class VECTOR3
{
public:
VECTOR3()
:x(0.0f),y(0.0f),z(0.0f)
{
std::cout<<"VECTOR3 Default Constructor "
<<std::setiosflags(std::ios_base::hex)<<this
<<std::endl;
}
VECTOR3(float fx, float fy, float fz)
:x(0.0f),y(0.0f),z(0.0f)
{
std::cout<<"VECTOR3 Parameter Constructor "
<<std::setiosflags(std::ios_base::hex)<<this
<<std::endl;
}
VECTOR3(const VECTOR3& rht)
:x(rht.x), y(rht.y), z(rht.z)
{
std::cout<<"VECTOR3 Copy Constructor "
<<std::setiosflags(std::ios_base::hex)<<this
<<" from rht : "
<<std::setiosflags(std::ios_base::hex)<<&rht
<<std::endl;
}
~VECTOR3()
{
std::cout<<"VECTOR3 Destructor "
<<std::setiosflags(std::ios_base::hex)<<this
<<std::endl;
}
VECTOR3& operator = (const VECTOR3& rht)
{
if( &rht == this )
return *this;
x = rht.x;
y = rht.y;
z = rht.z;
std::cout<<"VECTOR3 operator = left oper : "
<<std::setiosflags(std::ios_base::hex)<<this
<<" right oper : "
<<std::setiosflags(std::ios_base::hex)<<&rht
<<std::endl;
return *this;
}
private:
float x;
float y;
float z;
};
VECTOR3 Func1()
{
return VECTOR3(1.0f, 1.0f, 1.0f);
}
VECTOR3 Func2()
{
VECTOR3 ret;
ret.x = 2.0f;
ret.y = 2.0f;
ret.z = 2.0f;
return ret;
}
void main()
{
VECTOR3 v1 = Func1();
v1 = Func1();
VECTOR3 v2 = Func2();
VECTOR3 v3;
v3 = Func2();
}
分析:
<1>.
VECTOR3 v1 = Func1();
該語句的執(zhí)行過程本該是:
1>. 在 Func1() 中構(gòu)造一個無名對象
2>. 由 Func1() 中的無名對象拷貝構(gòu)造調(diào)用表達(dá)式處的臨時對象
3>. 再由臨時對象拷貝構(gòu)造v1
4>. Func1() 返回,析構(gòu)無名對象
5>. 整個語句結(jié)束,析構(gòu)臨時對象
但是c++ 會優(yōu)化上述過程,省略了 1>. 2>. 處的臨時對象創(chuàng)建,直接以
1.0f, 1.0f, 1.0f 為參數(shù)構(gòu)造v1,這樣只會有一次構(gòu)造函數(shù)的調(diào)用。結(jié)果
如圖:
<2>.
v1 = Func1();
該語句的執(zhí)行過程本該是:
1>. 在 Func1() 中構(gòu)造一個無名對象
2>. 由 Func1() 中的無名對象拷貝構(gòu)造調(diào)用表達(dá)式處的臨時對象
3>. 再由臨時對象賦值給v1 (賦值運算符)
4>. Func1() 返回,析構(gòu)無名對象
5>. 整個語句結(jié)束,析構(gòu)臨時對象
但是c++ 會優(yōu)化上述過程,省略了 1>. 處的無名臨時對象創(chuàng)建,直接以
1.0f, 1.0f, 1.0f 為參數(shù)構(gòu)造調(diào)用表達(dá)式處的臨時對象,因為是賦值,所以這個臨時對象是無法被優(yōu)化的,賦值完畢后,表達(dá)式結(jié)束,臨時對象被析構(gòu)。結(jié)果如圖:
<3>.
VECTOR3 v2 = Func2();
該語句的執(zhí)行過程本該是:
1>. Func2() 中的 ret 拷貝構(gòu)造調(diào)用表達(dá)式處的臨時對象
2>. 該臨時對象拷貝構(gòu)造v2
3>. 析構(gòu)臨時對象
但是c++ 會優(yōu)化上述過程,省略了創(chuàng)建臨時對象這一步,直接由ret拷貝
構(gòu)造v2,就一次拷貝構(gòu)造函數(shù)的代價。
結(jié)果如圖:
<4>.
VECTOR3 v3;
v3 = Func2();
執(zhí)行過程如下:
1>. 構(gòu)造v3
2>. 進(jìn)入Func2(),構(gòu)造ret
3>. 返回ret,用ret拷貝構(gòu)造到調(diào)用表達(dá)式處的臨時對象
4>. Func2()結(jié)束,ret被析構(gòu)
5>. 臨時對象賦值給v3
6>. 賦值表達(dá)式結(jié)束,析構(gòu)臨時對象
結(jié)果如圖:
綜上所述,可得如下結(jié)論:
<1>. 在使用一個臨時對象( 可能是無名對象 或者 返回對象值時 ) 創(chuàng)建構(gòu)造另一個對象的過程的中,c++會優(yōu)化掉該臨時對象的產(chǎn)生,直接以相同參數(shù)調(diào)用相關(guān)構(gòu)造函數(shù)構(gòu)或者 直接調(diào)用拷貝構(gòu)造函數(shù) 到 目標(biāo)對象.
<2>. 若不是對象創(chuàng)建,而是對象賦值,則在賦值表達(dá)式的右值處的臨時對象
創(chuàng)建不能省略,臨時對象賦值給左值后,表達(dá)式結(jié)束,臨時對象被析構(gòu)。