C++中真正的臨時對象是看不見的,它們不出現在你的源代碼中,臨時對象的產生在如下幾個時刻:
1. 用構造函數作為隱式類型轉換函數時,會創建臨時對象。
例:
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);
會產生一個臨時對象,作為實參傳遞到Calculate 函數中。
2. 建立一個沒有命名的非堆(non-heap)對象,也就是無名對象時,會產生臨時對象。
如:
Integer& iref = Integer(5); //用無名臨時對象初始化一個引用,等價于
//Integer iref(5);
Integer itgr = Integer(5); //用一個無名臨時對象拷貝構造另一個對象
按理說,C++應先構造一個無名的臨時對象,再用它來拷貝構造itgr,由于
該臨時對象拷貝構造 itgr 后,就失去了任何作用,所以對于這種類型(只起拷貝構造另一個對象的作用)的臨時對象,c++特別將其看做: Integer itgr(5); 即直接以相同參數構造目標對象,省略了創建臨時對象這一步。
Calculate( Integer(5) ); //無名臨時對象作為實參傳遞給形參,函數調
//用表達式結束后,臨時對象生命期結束,被//析構.
3. 函數返回一個對象值時,會產生臨時對象,函數中的返回值會以值拷貝的形式拷貝到被調函數棧中的一個臨時對象。
如:
Integer Func()
{
Integer itgr;
return itgr;
}
void main()
{
Integer in;
in = Func();
}
表達式 Func() 處創建了一個臨時對象,用來存儲Func() 函數中返回的對象,臨時對象由 Func() 中返回的 itgr 對象拷貝構造(值傳遞),臨時對象賦值給 in后,賦值表達式結束,臨時對象被析構。見下圖:

看看如下語句:
Integer& iRef = Func();
該語句用一個臨時對象去初始化iRef 引用,一旦該表達式執行結束,臨時對象的生命周期結束,便被結束,iRef引用的尸體已經不存在,接下來任何對 iRef 的操作都是錯誤的。
下面,來看看實際的測試結果,代碼如下:
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();
該語句的執行過程本該是:
1>. 在 Func1() 中構造一個無名對象
2>. 由 Func1() 中的無名對象拷貝構造調用表達式處的臨時對象
3>. 再由臨時對象拷貝構造v1
4>. Func1() 返回,析構無名對象
5>. 整個語句結束,析構臨時對象
但是c++ 會優化上述過程,省略了 1>. 2>. 處的臨時對象創建,直接以
1.0f, 1.0f, 1.0f 為參數構造v1,這樣只會有一次構造函數的調用。結果
如圖:

<2>.
v1 = Func1();
該語句的執行過程本該是:
1>. 在 Func1() 中構造一個無名對象
2>. 由 Func1() 中的無名對象拷貝構造調用表達式處的臨時對象
3>. 再由臨時對象賦值給v1 (賦值運算符)
4>. Func1() 返回,析構無名對象
5>. 整個語句結束,析構臨時對象
但是c++ 會優化上述過程,省略了 1>. 處的無名臨時對象創建,直接以
1.0f, 1.0f, 1.0f 為參數構造調用表達式處的臨時對象,因為是賦值,所以這個臨時對象是無法被優化的,賦值完畢后,表達式結束,臨時對象被析構。結果如圖:

<3>.
VECTOR3 v2 = Func2();
該語句的執行過程本該是:
1>. Func2() 中的 ret 拷貝構造調用表達式處的臨時對象
2>. 該臨時對象拷貝構造v2
3>. 析構臨時對象
但是c++ 會優化上述過程,省略了創建臨時對象這一步,直接由ret拷貝
構造v2,就一次拷貝構造函數的代價。
結果如圖:

<4>.
VECTOR3 v3;
v3 = Func2();
執行過程如下:
1>. 構造v3
2>. 進入Func2(),構造ret
3>. 返回ret,用ret拷貝構造到調用表達式處的臨時對象
4>. Func2()結束,ret被析構
5>. 臨時對象賦值給v3
6>. 賦值表達式結束,析構臨時對象
結果如圖:

綜上所述,可得如下結論:
<1>. 在使用一個臨時對象( 可能是無名對象 或者 返回對象值時 ) 創建構造另一個對象的過程的中,c++會優化掉該臨時對象的產生,直接以相同參數調用相關構造函數構或者 直接調用拷貝構造函數 到 目標對象.
<2>. 若不是對象創建,而是對象賦值,則在賦值表達式的右值處的臨時對象
創建不能省略,臨時對象賦值給左值后,表達式結束,臨時對象被析構。
posted on 2010-02-25 14:44
李陽 閱讀(4765)
評論(6) 編輯 收藏 引用 所屬分類:
C++