1. 程式人生 > >6.3 臨時性物件(Temporary Objects)

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生命結束,或是直到臨時物件的生命週期結束——視哪一種情況先到達而定。