我們知道在c++的建立物件是乙個費時,費空間的乙個操作。有些固然是必不可少,但還有一些物件卻在我們不知道的情況下被建立了。通常以下三種情況會產生臨時物件:
1,以值的方式給函式傳參;
2,型別轉換;
3,函式需要返回乙個物件時;
現在我們依次看這三種情況:
一,以值的方式給函式傳參。
我們知道給函式傳參有兩種方式。1,按值傳遞;2,按引用傳遞。按值傳遞時,首先將需要傳給函式的引數,呼叫拷貝建構函式建立乙個副本,所有在函式裡的操作都是針對這個副本的,也正是因為這個原因,在函式體裡對該副本進行任何操作,都不會影響原引數。我們看以下例子:
#include
class
ctemp
;ctemp(
int m =
0,int n =
0);virtual ~ctemp(){};
public:
intgetsum
(ctemp ts)
;
};ctemp::ctemp(
int m ,
int n)
intctemp::getsum
(ctemp ts)
//--------------main函式-----------------
void
main
()
output:
construct function!
a = 10
b = 20
copy function!
sum = 30
tm.a = 10
我們看到有呼叫了拷貝建構函式,這是tm在傳給getsum做引數時:
1,呼叫拷貝建構函式來建立乙個副本為getsum函式體內所用。
2,在getsum函式體內對tm副本進行的修改並沒有影響到tm本身。
解決辦法:
針對第一種情況的解決辦法是傳入物件引用(記住:引用只是原物件的乙個別名(alias)),我們將getsum**修改如下:
intctemp::getsum
(ctemp& ts)
output:
construct function!
a = 10
b = 20
sum = 30
tm.a = 1000
可以通過輸出看本,通過傳遞常量引用,減少了一次臨時物件的建立。這個改動也許很小,但對多繼承的物件來說在構建時要遞迴呼叫所有基類的建構函式,這對於效能來說是個很大的消耗,而且這種消耗通常來說是沒有必要的。
二,型別轉換生成的臨時物件。
我們在做型別轉換時,轉換後的物件通常是乙個臨時物件。編譯器為了通過編譯會建立一起我們不易察覺的臨時物件。再次修改如上main**:
void
main
()
output:
construct function!
a = 10
b = 20
construct function!
a = 0
b = 0
construct function!
a = 1000
b = 0
sum = 1000
main函式建立了兩個物件,但輸出卻呼叫了三次建構函式,這是為什麼呢?
關鍵在 sum = 1000;這段**。本身1000和sum型別不符,但編譯器為了通過編譯以1000為參呼叫建構函式建立了一下臨時物件。
解決辦法:
我們對main函式中的**稍作修改,將sum申明推遲到「=」號之前:
void
main
()
output:
construct function!
a = 10
b = 20
construct function!
a = 1000
b = 0
sum = 1000
只作了稍稍改動,就減少了一次臨時物件的建立。
1,此時的「=」號由原本的賦值變為了構造。
2,對sum的構造推遲了。當我們定義ctmep sum時,在main的棧中為sum物件建立了乙個預留的空間。而我們用1000呼叫構造時,此時的構造是在為sum預留的空間中進行的。因此也減少了一次臨時物件的建立。
三,函式返回乙個物件。
當函式需要返回乙個物件,他會在棧中建立乙個臨時物件,儲存函式的返回值。看以下**:
#include
class
ctemp
;ctemp&
operator=(ctemp& t)
//assignment copy ctor!
ctemp(
int m =
0);virtual ~ctemp(){};
};ctemp::ctemp(
int m)
//copy ctor!
ctemp double
(ctemp& ts)
//-------------main函式-----------------
void
main
()
output:
construct function!
a = 10
construct function!
a = 0
construct function!
a = 0
copy ctor!
assignment copy ctor!
sum.a = 20
我特地加寬了語句:
sum = double(tm);
這條語句竟生成了兩個物件,horrible! 我們現在將這條語句逐步分解一下:
1,我們顯式建立乙個tmp臨時物件,
語句:ctemp tmp;
2,將temp物件返回,返回過程中呼叫copy cotr建立乙個返回物件,
語句:return tmp;
3,將返回結果通過呼叫賦值拷貝函式,賦給sum
語句: sum = 函式返回值;(該步並沒有建立物件,只是給sum賦值)
tm.double返回乙個用拷貝建構函式生成的臨時物件,並用該臨時物件給sum賦值.
上面的第1步建立物件可以不用建立,我們可以直接對返回值進行操作,有些c++編譯器中會有一種優化,叫做(nrv,named return value).不過本人使用的vc++6.0並沒有這個啟用這個優化。
第2步建立的返回物件是難以避免的,你或許想可以返回乙個引用,但你別忘記了在函式裡建立的區域性物件,在返回時就被銷毀了。這時若再引用該物件會產生未預期的行為。(c#中解決了這個問題)。
解決方法:
我們將物件直接操作(manipulate)返回物件,再結合上面的減少臨時物件的方法,將函式double的**,及main函式中的**修改如下:
ctemp double
(ctemp& ts)
//---------main函式-----------
void
main
()
output:
construct function!
a = 10
construct function!
a = 20
sum.a = 20
發現減少了一次構造函式呼叫(tmp),一次拷貝建構函式(tmp拷貝給返回物件)呼叫和一次賦值拷貝函式呼叫.(assignment copy ctor),這是因為:
返回物件直接使用為sum預留的空間,所以減少了返回臨時物件的生成——返回物件即是sum,返回物件的建立即是sum物件的建立.多麼精妙!
C 中的臨時物件
1,臨時物件神秘在於不知不覺就請入程式當中,並且給程式帶來了一定的問題 2,下面的程式輸出什麼?為什麼?1 include 2 3class test411 12 test 這裡程式作者想要 復用,直接呼叫已經構造好的函式來完成沒有引數的建構函式的函式體 1316 17void print 1821...
C 中臨時物件的建立
在某些情況下,編譯器需要建立臨時物件。可能會出於下列原因建立這些臨時物件 使用乙個不同於所初始化的引用的基礎型別的型別的初始值設定項初始化 const 引用。儲存返回使用者定義型別的函式的返回值。僅當您的程式未將返回值複製到物件時,才會建立這些臨時記憶體。例如 複製udt func1 declare...
C 中關於臨時量和臨時物件
一 臨時量的生成 1 內建型別產生的臨時量 常量 不可更改 2 自定義型別產生的臨時量 變數 可以更改 3 隱式產生的臨時量 常量 二 返回值 1 返回內建型別的時候,產生的都是暫存器的立即數 2 返回自定義型別的時候,也是通過暫存器返回的,但是用指標或者引用來接收返回值時,編譯器會自動產生臨時量 ...