Saturday, September 4, 2010

[C++] 函式,如何傳遞一個C++物件

Introduction
不論在C或C++中,當函式要傳遞一個「傳回值」時,通常可以透過「暫存器」來傳遞,也就是:函式把將要回傳的值,暫存在某個暫存器中,接下來呼叫方就可以去讀取該暫存器,也就達到傳遞的效果,以上作法的前提是,該「暫存器」容納得下該「傳回值」;然而,在C++中,當函式的「傳回值」不再是一般小尺寸的型態,而是大尺寸的物件時,會發生什麼事?編譯器會如何來處理這種情況?本篇文章即要討論這個情況。



Return Value — 「object」
當傳回值是個C++的「物件」,且程式沒有設定傳回值最佳化時(Return Value Optimization),則需要透過兩次的物件複製來達到傳遞目的:
  1. 將該物件複製堆疊上的臨時空間
  2. 將存放在臨時空間中的物件複製儲存函式回傳值的物件
然而,根據不同的compiler及不同的calling convention,甚至不同的平台,在傳遞物件上皆會有不同的實作方式。

接下來在實驗的地方,我將比較在「VC9(WITH Windows XP)及「g++(WITH Ubuntu 10.04 64bit)這兩種不同的compiler及不同的平台下,執行物件回傳的異同,以及Return Value Optimization的設定對於執行結果的影響。




Experiment
實驗一實驗二的地方,我將在VC9g++底下分別執行測試的程式碼,並將執行結果的截圖貼上來。而由於前面兩個實驗產生了不同的執行結果,我將在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
          實驗一是在windows XP下使用VC9,底下是執行結果的截圖



  • Exp.2
          實驗二是在Ubuntu下使用g++,底下是執行結果的截圖




  • short summary of Exp.1 & Exp.2
實驗一當中,我們發現物件被複製了兩次,分別是
  1. 呼叫「copy constructor」來將物件複製到堆疊上的臨時空間
  2. 呼叫「operator=」來將存放在臨時空間中的物件複製到儲存函式回傳值的物件上
實驗二,我們發現減少了copy constructor的呼叫,原因是因為g++ compiler在執行程式時自動做了最佳化(Return Value Optimization),將物件直接建構在臨時空間上,因此物件少了一次的複製。


因此,在實驗三,我們要做的就是將VC9的程式最佳化打開,我們可以預料得到的是: 最佳化打開後,物件勢必會減少一次的複製。


  • Exp.3
實驗三是在windows XP下使用VC9,且打開程式的最佳化(Return Value Optimization),底下是執行結果的截圖




Conclusion
本篇文章介紹了函式是如何傳遞一個C++物件,且透過實驗來說明不同的compiler會有不同的設定,根據不同的compiler及不同的calling convention,甚至不同的平台,在傳遞物件上皆會有不同的實作方式。

筆者是在VC9(WITH Windows XP)及g++(WITH Ubuntu 10.04 64bit)這兩種compiler下編譯程式的,而我們發現Return Value Optimization提升了程式執行時的效能,深深影響著實驗的結果,更多討論可以詳見Reference 2。

回傳一個物件是一件非常繁雜的工作,也因此,我們應該盡量避免回傳「物件」


Reference 
  1. 程式設計師的自我修養 — 連結、載入、程式庫,俞甲子、石凡、潘愛民 
  2. MSDN, http://msdn.microsoft.com/en-us/library/ms364057%28VS.80%29.aspx

2 意見:

Anonymous said...

You did a great job.

Trek said...

thanks...