如果有乙個函式,形式如下:
t operator+(const t &, const t &);
以及兩個t objects,a和b,那麼:
a + b;
可能會導致乙個臨時性物件,以放置傳回的物件.是否會導致乙個臨時性物件,視編譯器的進取性(aggressiveness)以及上述操作發生時的程式上下關係(program context)而定.例如下面這個片段:
t a, b;
t c = a + b;
編譯器會產生乙個臨時性物件,放置a+b的結果,然後再使用t的copy constructor,把該臨時性物件當作c的初值.然而更好的轉換則是直接以拷貝構造的方式,將a+b的值放到c中,於是就不需要臨時性物件,以及對其constructor和destructor的呼叫了.
此外,視operator+()的定義而定,named return value(nrv)優化(詳見2.3節)也可能實施起來.這將導致直接在上述c物件中求表示式結果,避免執行copy constructor和具名物件(named object)的destructor.
三種方式所獲得的c物件,結果都一樣.其間的差異在於初始化的成本.乙個編譯器可能給任何保證嗎?嚴格來說沒有,c++ standard允許編譯器對於臨時性物件的產生有完全的自由度.
但實際上,幾乎任何表示式如果有這種形式:
t c = a + b;
而其中的加法運算子被定義為:
t operator+(const t &, const t &);
或
t t::operator+(const t &);
那麼實現時根本不產生乙個臨時性物件.
然而注意,意義相當的assignment語句:
c = a + b;
不能夠忽略臨時性物件.
所以這樣的初始化操作:
t c = a + b;
總是
比下面的操作更有效率地被編譯器轉換:
c = a + b;
第三種運算形式是,沒有出現目標物件:
a + b; // no target
這時候有必要產生乙個臨時物件,以放置運算後的結果.雖然看起來有點怪異,但這種情況實際上在子表示式中十分普遍.例如,如果這樣寫:
string s("hello"), t("world"), u("!");
那麼不論:
string v;
v = s + t + u;
或
printf("%s\n", s + t);
都會產生乙個臨時物件,與s + t相關聯.
最後乙個表示式帶來了乙個論題,那就是
"臨時物件的生命週期".
一種比較被喜歡的轉換方式是在呼叫printf()之後實施string destructor.在c++ standard下,這正是該表示式的必須轉換方式.標準規格這樣將:
臨時性物件的被摧毀,應該是對完整表示式求值過程中的最後乙個步驟,該表示式造成臨時物件的產生.
什麼是乙個完整表示式?非正式地說,它是被涵括的表示式中最外圍的那個.下面這個表示式:
// tertiary full expression with 5 sub-expressions
((obja > 1024) && (objb > 1024)) ? obja + objb : foo(obja, objb);
一種有五個子表示式,內帶在乙個"?:完成表示式"中.任何乙個子表示式所產生的任何乙個臨時物件,都應該在完整表示式被求值完成後,才可以銷毀.
當臨時性物件是根據程式的執行期語意有條件地被產生出來時,臨時性物件的生命規則就顯得有些複雜了.舉個例子,想這樣的表示式:
if (s + t || u + v)
其中的u+v子算式只有在s+t被評估為 false 時,才會開始被評估.與第二個子算式有關的臨時性物件必須被銷毀.但是,很顯然地,不可以被無條件地銷毀.也就是說,希望只有在臨時性物件被產生出來的情況下才去銷毀它.(如果第乙個子算式為 true,則不產生第二個臨時性物件,不需要銷毀)
把臨時性物件的destructor放在每乙個子算式的求值過程中,可以免除"努力追蹤第二個子算式是否真的需要被評估".然而在c++ standard的臨時物件生命規則中,這樣的策略不再被允許.臨時性物件在完整表示式尚未評估完全之前,不得被銷毀.也就是說,某些形式的條件測試現在必須被插入進來,以決定是否要曉輝何第二算式有關的臨時物件.
臨時物件的生命規則有兩個例外.第乙個例外發生在表示式被用來初始化乙個object時.例如:
bool verbose;
...string prognameversion = !verbose ? 0 : progname + progversion;
其中progname和progversion都是string objects.這時候會生出乙個臨時物件,放置加法運算子的運算結果:
string operator+(const string &, const string &);
臨時物件必須根據對verbose的測試結果有條件地解構.在臨時物件的生命規則下,它應該在完整的"?:表示式"結束評估後盡快被銷毀.然而,如果prognameversion的初始化需要乙個copy constructor:
prognameversion.string::string(temp);
那麼臨時性物件的解構(在"?:完整表示式"之後)當然那就不是期望的.c++ standard要求:
...凡是含有表示式執行結果的臨時性物件,應該存留到object的初始化操作完成為止.
臨時性物件的生命規則的第二個例外是"當乙個臨時性物件被乙個reference繫結"時,例如:
const string &space = " ";
產生出這樣的程式**:
// c++ pseudo code
string temp;
temp.string::string(" ");
const string &space = temp;
很明顯,如果臨時性物件現在被銷毀,那個reference也就沒有用了.所以c++ standard要求:
如果乙個臨時性物件被繫結在乙個reference,物件將殘留,直到被初始化的reference的生命結束,或直到臨時物件的生命範疇(scope)結束--視哪一種情況先到達而定
.有一種說法是,由於當前的c++編譯器會產生臨時性物件,導致程式的執行比較沒有效率.
第六章 臨時性物件
1.是否會產生乙個臨時物件,視編譯器的進取性和 操作發生時的語境而定。c 標準中指出編譯器對臨時性物件有完全的自由度。2.在新開闢的記憶體上以拷貝的方式構造物件時,幾乎所有編譯器都不會產生乙個臨時性物件。t c a b 其中加法運算子形式如下 t operator const t const t 或...
深度探索C 物件模型筆記 第六章
c 程式中所有的global objects都被放置在程式的data segment,如果顯示指定給它乙個值,此object將以該值為初值,否則object所配置到的記憶體內容為0 語言要求每次呼叫new都傳回乙個獨一無二的指標,所以如果size等於0返回乙個1 byte的記憶體區塊 point p...
java第六章類與物件
類修飾符的含義 public 將乙個類宣告為公共類,它可以被任何物件訪問,乙個程式的主類必須為公共類 abstract 將乙個類宣告為抽象類,沒有實現的方法,需要子類提供方法的實現.final 將乙個類宣告為最終類即非繼承類,表示它不能被其他類所繼承 friendly 友元型別,預設的修飾符,只有在...