6.3 臨時性物件(Temporary Objects)
如果我們有一個函式,以及兩個T object,a和b:
T operator+(const T&,const T&);
a+b;
那麼可能導致一個臨時物件,以放置傳回的物件。是否導致一個臨時物件,視編譯器的進取性(aggressiveness)以及上述操作發生的程式語境(program context)而定。例如:
T a,b;
T c = a+b;
1.編譯器可能產生一個臨時物件,放置a+b的結果,然後再使用T的copy constructor,把該臨時性物件當做c的初始值。2.然而比較有可能的做法直接拷貝構造,將a+b放到c中,於是就不需要臨時物件,以及對其constructor和destructor的呼叫了。
3.此外,視operator+()的定義而定,NRV優化會被實施,這將導致直接在上述c物件中求表示式的結果,避免執行copy constructor和具名(name object)物件的destructor。
三種方式獲得的c物件,結果都一樣。期間差異在於初始化的成本。但是編譯器沒有保證哪種做法,C++ Standard允許編譯器對於臨時物件產生的完全自由度。理論上,C++ Standard允許編譯器廠商有完全的自由度,但實際上,由於市場的競爭,幾乎保證任何表示式如下有這種形式,那麼實現時根本不產生臨時性物件:
T c = a+b; //其加法運算子被定義 T operator+(const T&,const T&); //或是這樣 T T::operator+(const T&);
然而assignment語句,不能忽略臨時物件:
c = a + b;
//C++ pseudo code
//T temp a + b;
T temp;
temp.operator(a+b); //(1)
//c = temp
c.operator = (temp);//(2)
temp.T::~T();
所以T c = a+b;總是比c = a+b;更加有效率。
第三種形式如下:
a + b;
這時候就會產生一個臨時物件以存放運算的結果。這種情況實際上的子表示式(subexpressions)中十分普遍。如下:
String s("hello"),t("world"),u("!"); //以下會產生臨時物件 String v; v = s + t + u; //也會產生臨時物件 printf("%s\n",s + t);
對於“臨時物件的生命週期而言”,在 Standard C++之前,臨時物件的生命並沒有顯示指定,如上由編譯器廠商自行決定;在C++ Standard之下標準:臨時性物件的摧毀,應該是對完整表示式(full-expression)求值過程中的最後一個步驟,該完整表示式造成臨時物件的產生。
臨時物件的生命規則有兩個例外。第一個是例外發生在表示式用來初始化一個object時。例如:
book verbose;
...
String progNameVersion =
!verbose
? 0
:progName + progVersion;
其中progName和progVersion都是String objects,這時候產生一個臨時物件,放置加法運算的結果。
臨時物件必須根據verbose的測試結果,有條件的析構。規則下,它應該再三目運算子之後儘快被摧毀,但是progNameVersion的初始化需要呼叫copy constructor。這種情況下等號右邊已經被析構就是錯誤的。所以C++ Standard 說凡是持有表達執行結果的臨時性物件,應該保留到object的初始化操作完整為止。
例如以下的初始化保證失敗:
const char* progNameVersion =
progName + progVersion;
//被編譯器內部轉化
String temp;
operator+(temp,progName,progVersion);
progNameVersion = temp.String::operator char*();
temp.String::~String();
此時如果臨時物件被摧毀,progNameVersion指向未定義的heap。
臨時性物件的生命規則的第二個例外是“當一個臨時性物件被一個reference繫結時”,例如:
const String &space = "";
//被編譯器內部轉化
String temp;
temp.String::String("");
const String& space = temp;
很明顯,如果臨時物件被摧毀,那麼reference就沒用了。
所以規則上說:如果一個臨時物件綁定於一個reference,物件將殘留,直到被初始化reference生命結束,或是直到臨時物件的生命週期結束——視哪一種情況先到達而定。