Introduction
不論在C或C++中,當函式要傳遞一個「傳回值」時,通常可以透過「暫存器」來傳遞,也就是:函式把將要回傳的值,暫存在某個暫存器中,接下來呼叫方就可以去讀取該暫存器,也就達到傳遞的效果,以上作法的前提是,該「暫存器」容納得下該「傳回值」;然而,在C++中,當函式的「傳回值」不再是一般小尺寸的型態,而是大尺寸的物件時,會發生什麼事?編譯器會如何來處理這種情況?本篇文章即要討論這個情況。
Return Value — 「object」
當傳回值是個C++的「物件」,且程式沒有設定傳回值最佳化時(Return Value Optimization),則需要透過兩次的物件複製來達到傳遞目的:
- 將該物件複製到堆疊上的臨時空間
- 將存放在臨時空間中的物件複製到儲存函式回傳值的物件上
然而,根據不同的compiler及不同的calling convention,甚至不同的平台,在傳遞物件上皆會有不同的實作方式。
接下來在實驗的地方,我將比較在「VC9」(WITH Windows XP)及「g++」(WITH Ubuntu 10.04 64bit)這兩種不同的compiler及不同的平台下,執行物件回傳的異同,以及Return Value Optimization的設定對於執行結果的影響。
Experiment
在實驗一跟實驗二的地方,我將在VC9及g++底下分別執行測試的程式碼,並將執行結果的截圖貼上來。而由於前面兩個實驗產生了不同的執行結果,我將在short summary的地方進一步分析原因,並透過實驗三來做驗證
在實驗一跟實驗二的地方,我將在VC9及g++底下分別執行測試的程式碼,並將執行結果的截圖貼上來。而由於前面兩個實驗產生了不同的執行結果,我將在short summary的地方進一步分析原因,並透過實驗三來做驗證
- Source Code
#include <iostream> using namespace std; struct cpp_obj { cpp_obj() { cout << "ctor\n"; } cpp_obj(const cpp_obj& c) { cout << "copy ctor\n"; } cpp_obj& operator=(const cpp_obj& rhs) { cout << "operator=\n"; return *this; } ~cpp_obj() { cout << "dtor\n"; } }; cpp_obj return_test() { cpp_obj b; cout << "before return\n"; return b; } int main() { cpp_obj n; n = return_test(); }
- Exp.1
- Exp.2
- short summary of Exp.1 & Exp.2
- 呼叫「copy constructor」來將物件複製到堆疊上的臨時空間
- 呼叫「operator=」來將存放在臨時空間中的物件複製到儲存函式回傳值的物件上
因此,在實驗三,我們要做的就是將VC9的程式最佳化打開,我們可以預料得到的是: 最佳化打開後,物件勢必會減少一次的複製。
- Exp.3
Conclusion
本篇文章介紹了函式是如何傳遞一個C++物件,且透過實驗來說明不同的compiler會有不同的設定,根據不同的compiler及不同的calling convention,甚至不同的平台,在傳遞物件上皆會有不同的實作方式。
筆者是在VC9(WITH Windows XP)及g++(WITH Ubuntu 10.04 64bit)這兩種compiler下編譯程式的,而我們發現Return Value Optimization提升了程式執行時的效能,深深影響著實驗的結果,更多討論可以詳見Reference 2。
回傳一個物件是一件非常繁雜的工作,也因此,我們應該盡量避免回傳「物件」。
Reference
- 程式設計師的自我修養 — 連結、載入、程式庫,俞甲子、石凡、潘愛民
- MSDN, http://msdn.microsoft.com/en-us/library/ms364057%28VS.80%29.aspx
2 意見:
You did a great job.
thanks...
Post a Comment