2011-02-20 68 views
1

返回如果本地對象在函數調用中返回,它必須做至少有三個步驟:內置類型變量從功能

  1. 拷貝構造函數被調用來保存副本。
  2. 銷燬本地對象。
  3. 的副本回報。

例如:

x = y + z 

如果x爲整數對象。的y + z副本應返回,然後創建一個新的對象,然後x賦值運算符將這個對象作爲參數。

所以我的問題是:

  • 用於相同的過程內置類型,如intdouble ...?
  • 如果他們不一樣,怎麼辦成了嗎?

回答

3

對於內置類型和內置運算符,語言規範沒有說明「如何完成」。該語言只是說內置類型的二進制+的結果是一個右值 - 操作數值的總和。而已。沒有一步一步地描述使用內置操作員時會發生什麼(有些例外,如&&,,等)。

你能想出的重載運算符的工作原理(這是你在你的問題)一步一步的描述,是因爲評估重載運算符的過程來通過幾個序列點。在C++程序的序列點實現的離散時間的概念:它是分離的東西,從事情之後發生的之前發生的唯一的事情。沒有分離序列點,沒有「之前」和「之後」。

在運算符重載的情況下,其評估過程中涉及了不少順序點,這就是爲什麼您可以將此過程描述爲一系列步驟。內置運算符+的評估過程中沒有序列點,因此完全無法以逐步的方式描述發生在那裏的情況。從語言的角度來看,內置的+通過模糊不可分割的不確定操作組合來評估,這些操作會產生正確的結果。

這樣做是爲了在編譯內置運算符時爲編譯器提供更好的優化機會。

+0

謝謝!我想這是我正在尋找的答案。 – Chan

1

這取決於幾個因素,特別是編譯器的水平或能力進行優化。這也可以在一定程度上取決於調用約定。

所有內置類型都可以放在一個寄存器中(除了「long long int」等「異常大」的內置類型)。基本上,所有的調用約定,如果返回類型可以安裝在EAX寄存器,那就是它是由被叫方放,並通過呼叫方檢索。所以,這將是你的問題的答案。

對於較大的對象,您所描述的過程僅在原則上沒錯,但這個整體拷貝銷燬,臨時複製,破壞的事情是非常低效的,它是衆多的編譯器的優化算法的最高優先級。由於對象太大而無法放入寄存器。它們通常放在堆棧上並留在那裏由調用者檢索。由於它們經常被存儲在另一個局部變量中,編譯器會試圖將這些棧槽合併在一起,而且被調用函數中的局部變量通常也會在同一個槽中,所以最後,你沒有複製,沒有破壞,沒有臨時的,沒有開銷......這是理想的「優化」情況,但編譯器並不總是能夠意識到這一點,它也需要該對象是POD類。

1

如果你只是在談論你現在的例子(x = y + z),那麼沒有函數調用 - 增加發生在寄存器中。

如果你實際上正在調用一個函數(x = sum(y, z)),那麼你可以根據調用約定和數據類型獲得一些不同的行爲(不適合單個寄存器的類型得到特殊處理),但與int s,假設它們最終會傳回EAX寄存器是相當安全的。

沒有涉及構造函數/析構函數!

欲瞭解更多有關個人數據類型,檢查返回值節上this page - 我認爲這些都爲的cdecl調用約定,但他們應該是相當普遍。

有關一般調用約定的更多信息,請參閱Wikipedia (x86 calling conventions)的相關工作。您將會對cdecl(標準C函數)和這個調用(C++類成員函數)感興趣。

希望這會有所幫助!

1

這裏有兩個問題。首先,關於從函數返回用戶定義類型需要做什麼的列表基本上是正確的;除了所有實際編譯器使用return value optimization來避免臨時副本。

另一個問題是「內置類型怎麼樣?」從概念上講,同樣的事情發生,只是(1)內置類型具有「簡單的構造函數」和「簡單的析構函數」(即編譯器知道不需要實際調用任何函數來構造/破壞這些類型),(2)編譯器知道更多關於內建類型的操作,而不是用戶定義類型上的操作,而實際上並不需要調用函數來添加兩個int(相反,編譯器只會使用相關的彙編代碼指令,而(3)編譯器比用戶定義的類型更瞭解內置類型,並且可以更頻繁地使用返回值優化

有關記錄,rvalue references and related changes to C++-0x很大程度上是爲了讓程序員更有能力控制返回值優化