小問題大思考之C 臨時物件

2021-07-16 02:07:46 字數 4962 閱讀 9300

c++中有這樣一種物件:它在**中看不到,但是確實存在。它就是臨時物件---由編譯器定義的乙個沒有命名的非堆物件(non-heap object)。為什麼研究臨時物件?主要是為了提高程式的效能以及效率,因為臨時物件的構造與析構對系統效能而言絕不是微小的影響,所以我們應該去了解它們,知道它們如何造成,從而盡可能去避免它們。

臨時物件通常產生於以下4種情況:

型別裝換

按值傳遞

按值返回

物件定義

下面我們逐一看看:

1、型別轉換:它通常是為了讓函式呼叫成功而產生臨時物件。發生於 「傳遞某物件給乙個函式,而其型別與它即將繫結上去的引數型別不同」 的時候。

例如:[cpp]view plain

copy

void

test(

const

string& str);  

char

buffer = 

"buffer"

;  test(buffer); // 此時發生型別轉換

此時,編譯器會幫你進行型別轉換:它產生乙個型別為string的臨時物件,該物件以buffer為引數呼叫string constructor。當test函式返回時,此臨時物件會被自動銷毀。

注意:對於引用(reference)引數而言,只有當物件被傳遞給乙個reference-to-const引數時,轉換才發生。如果物件傳遞給乙個reference-to-non-const物件,不會發生轉換。

例如:[cpp]view plain

copy

void

upper(string& str);  

char

lower = 

"lower"

;  upper(lower); // 此時不能轉換,編譯出錯

此時如果編譯器對reference-to-non-const物件進行了型別轉換,那麼將會允許臨時物件的值被修改。而這和程式設計師的期望是不一致的。試想,在上面的**中,如果編譯器允許upper執行,將lower中的值轉換為大寫,但是這是對臨時物件而言的,char lower的值還是「lower」,這和你的期望一致嗎?

有時候,這種隱式型別轉換不是我們期望的,那麼我們可以通過宣告constructor為explicit來實現。explicit告訴編譯器,我們反對將constructor用於型別轉換。

例如:[cpp]view plain

copy

explicit

string(

const

char

*);  

2、按值傳遞:這通常也是為了讓函式呼叫成功而產生臨時物件。當按值傳遞物件時,實參對形參的初始化與t formalarg = actualarg的形式等價。

例如:[cpp]view plain

copy

void

test(t formalarg);  

t actualarg;  

test(actualarg);  

此時編譯器產生的偽碼為:

[cpp]view plain

copy

t _temp;  

_temp.t::t(acutalarg); // 通過拷貝建構函式生成_temp

g(_temp);  // 按引用傳遞_temp

_temp.t::~t(); // 析構_temp

因為存在區域性引數formalarg,test()的呼叫棧中將存在formalarg的佔位符。編譯器必須複製物件actualarg的內容到formalarg的佔位符中。所以,此時編譯器生成了臨時物件。

3、按值返回:如果函式是按值返回的,那麼編譯器很可能為之產生臨時物件。

例如:[cpp]view plain

copy

class

integer   

integer(const

integer& rhs): value(rhs.value)   

integer& operator=(const

integer& rhs);  

~integer()   

private

:  int

value;    

};  

integer operator+(const

integer& a, 

const

integer& b)   

integer c1, c2, c3;  

c3 = c1 + c2;  

編譯器生成的偽**:

[cpp]view plain

copy

struct

integer _tempresult; 

// 表示佔位符,不呼叫建構函式

operator+(_tempresult, c1, c2); // 所有引數按引用傳遞

c3 = _tempresult; // operator=函式執行

integer operator+(const

integer& _tempresult, 

const

integer& a, 

const

integer& b)   

return

retval;  

}  

如果對operator+進行返回值優化(rvo:return value optimization),那麼臨時物件將不會產生。

例如:[cpp]view plain

copy

integer operator+(

const

integer& a, 

const

integer& b)   

編譯器生成的偽**:

[cpp]view plain

copy

integer operator+(

const

integer& _tempresult, 

const

integer& a, 

const

integer& b)   

對照上面的版本,我們可以看出臨時物件retval消除了。

4、物件定義:

例如:[cpp]view plain

copy

integer i1(100); 

// 編譯器肯定不會生成臨時物件

integer i2 = integer(100); // 編譯器可能生成臨時物件

integer i3 = 100; // 編譯器可能生成臨時物件

然而,實際上大多數的編譯器都會通過優化省去臨時物件,所以這裡的初始化形式基本上在效率上都是相同的。

備註:臨時物件的生命期:按照c++標準的說法,臨時物件的摧毀,是對完整表示式求值過程中的最後乙個步驟。該完整表示式照成了臨時物件的產生。

完整表示式通常是指包含臨時物件表示式的最外圍的那個。例如:

((obja >1024)&&(objb <1024) ) ? (obja - objb) :(objb-obja)

這個表示式中一共含有5個表示式,最外圍的表示式是?。任何乙個子表示式所產生的任何乙個臨時物件,都應該在完整表示式被求值完成後,才可以銷毀。

臨時物件的生命週期規則有2個例外:

1、在表示式被用來初始化乙個object時。例如:

[cpp]view plain

copy

string progname(

"test"

);  

string progversion("ver-1.0"

);  

string prognameversion = progname + progversion  

如果progname + progversion產生的臨時物件在表示式求值結束後就析構,那麼prognameversion就無法產生。所以,c++標準規定:含有表示式執行結果的臨時物件,應該保留到object的初始化操作完成為止。

小心這種情況:

[cpp]view plain

copy

const

char

* prognameversion = progname + progversion  

這個初始化操作是一定會失敗的。編譯器產生的偽碼為:

[cpp]view plain

copy

string _temp;  

operator+(_temp, progname, progversion);  

prognameversion = _temp.string::operator char

*();  

_temp.string::~string();  

2、當乙個臨時物件被乙個reference繫結時。例如:

[cpp]view plain

copy

const

string& name = 

"c++"

;  

編譯器產生的偽碼為:

[cpp]view plain

copy

string _temp;  

temp.string::string("c++"

);  

const

string& name = _temp;  

針對這種情況,c++標準上是這樣說的:如果乙個臨時物件被繫結於乙個reference,物件將保留,直到被初始化的reference的生命結束,或直到臨時物件的生命範圍結束-----看哪種情況先到達而定。

臨時物件位址的小問題

其實今天想嘗試一下容器和迭代器的使用,但是卻感受到了其他一些問題的重要性,就是對於臨時物件的一些理解,有時候我們認為關於乙個mian函式種的臨時定義是很方便的,可以暫時為我們儲存一下物件,可以作為乙個臨時物件,不管是int,char還是string,還是其他自定義物件的臨時儲存,關鍵是討厭的指標 很...

小問題大思考之C 裡的inline函式

inline,乙個神奇的關鍵字。有了它,你同時就可以獲取函式和巨集的優點。inline定義的函式,比起沒有inline的函式來說,沒有執行函式呼叫所帶來的負擔 對此可參見 c 程式的記憶體布局 因此它是高效率的 比起巨集來,它具有函式的可預期行為和引數型別檢驗。巨集的行為難於預期,我們看看下面這個巨...

關於C 中的臨時物件問題

關於c 中的臨時物件問題 c 中真正的臨時物件是看不見的,它們不出現在你的源 中,臨時物件的產生在如下幾個時刻 1.用建構函式作為隱式型別轉換函式時,會建立臨時物件。例 class integer integer private int m val void calculate integer it...