2015-07-19 124 views
2

我正在閱讀C++ Primer Plus(第6版)一書,我遇到了一些讓我感到困惑的東西,所以請耐心等待我試着解釋...傳遞(int x)和(const int x)之間的區別

如果我有它的原型看起來像這樣的功能:

void cube(double x); 

和另一個函數的原型誰看起來像這樣:

void cube(const double &x); 

是什麼之間的區別二?對於第一個函數原型,該值通過值傳遞,這意味着它將被複制並因此不被該函數改變。對於第二個原型,值是通過引用傳遞的,但是它是一個常量引用,因此C++將創建一個匿名臨時變量並將該參數的值分配給臨時變量,從而模仿按值傳遞。所以,從本質上來說,這兩個函數原型真的沒有區別,對吧?那麼(const double & x)是什麼意思呢?

+0

它不會創建臨時變量,您將傳遞雙倍地址,除非調用者被麻醉,否則它不能爲空(如果您嘗試解引用空引用,您將得到異常拋出)以及你可以使用。而不是 - >。此外,你不能改變儲存在x – Creris

+0

內的內容我喜歡你如何描述,詳細地說,兩者之間的差異。然後你說:「所以兩者沒有區別,對吧?」你的描述也不太準確:在這裏沒有「匿名臨時」。 –

回答

4

對於第二個原型,該值是通過引用傳遞但它是一個恆定的基準所以C++將創建匿名臨時變量和由值參數的值分配給臨時變量從而模擬通。

雖然C++會在某些情況下做到這一點。例如,如果你通過返回double表達,C++會創建一個臨時的:

double v = 123.456; 
cube(5*v+321.0123); 

但是,它不一定會做到這一點。例如,如果您撥打這個電話

double v = 123.456; 
cube(v); 

C++將傳遞一個參考v直接向cube()功能。在這種情況下,v的同時修改在運行時對cube()功能是「可見的」。

所以,實質上這兩個函數原型之間確實沒有區別,對吧?

沒錯,兩者沒有太大的區別。假設double所佔空間與double相同,則性能也不會有差異。

那麼(const double &x)的要點是什麼呢?

雖然經過雙通過值或恆定參考之間的差別不大,你可以與其他類型的交易時,如std::string得到相當大的差異。通過不斷參考拍攝參數時,你必須寫

void cube(const T& v); 

當你的代碼的算法爲模板,即你的T可以是任何類型變得非常有用。常量引用方法允許您控制正在進行的複製量,因爲從某個對象大小開始,傳遞引用變得比傳遞副本便宜得多。

+0

我對這個迴應有點困惑。如果按值傳遞,函數將接收它允許編輯的變量的副本。如果通過常量引用傳遞,而不管引擎蓋下發生了什麼,語義上該函數將接收對其無法修改的對象的引用。這似乎是一個很大的差異。我是否錯過了這一觀點,而問題是真正關注價值是如何傳遞的? –

+0

很好解釋。非常感謝你。 – Jonathan

+0

有一個重要的區別:如果通過const引用傳遞,調用者可以在代碼運行時修改引用(例如,從另一個線程或者代碼調用另一個函數時)。這改變了你的函數內的值。按價值傳遞給你一個無人修改的副本。 –

-2

儘管讀/寫操作會減慢速度,但32位機器上的參考/指針僅爲32位,其中雙位爲64位。這與32位字大小無關。

這是沒有道理的,真的。優化編譯器可能會將它們視爲相同並生成相同的代碼。

編輯:爲了解釋我自己,我應該強調「可能」。

嚴格的標準符合性目前是一種判斷編譯器是否確實是C++編譯器的不好方法 - 編譯器經常濫用/忽略標準而偏愛優化和語法糖。 (性能)的差異,我試圖說測量傳遞一個雙(超過字的大小)的函數VS的成本從指針/參考(字大小)獲取成本 - 並在大多數情況下它可以忽略不計。您傳遞的值越大,從指針/引用獲取的速度將越快。
凱西Muratori提到這一點,並鼓勵通過結構稍微超過字的大小。

+2

任何將它們視爲相同並生成相同代碼的編譯器都不是C++編譯器。 – hvd

+1

你似乎對此事有一些認識,但我認爲你只是在這裏猜測。 –

4

對於以下兩種原型

void cube(double x); 

void cube(const double &x); 

行爲將是從呼叫者的,所述的角度與被呼叫者相同也不會允許修改傳播到呼叫者。然而,在後者(const引用)示例中,x將通過引用而不是按值傳遞。如果double足夠大,或者它是一個更大的數據類型(例如,struct),通過引用傳遞將避免複製大量數據(因爲只傳遞指針)。

二階性能影響(例如,對緩存權衡影響數據副本的影響)都與實現有關,並且不僅取決於處理器/緩存架構和編譯器,還取決於結構和運行時該程序的用例。

1

對於原始類型,您不會通過使用const&獲得任何收益。 const&傳統上用於大對象以避免可能的昂貴和不必要的複製。

如果類型不支持複製,您還需要const&但是您不希望修改引用的對象。

隨着C++ 11,移動語義出現問題傳統智慧「以const引用傳遞的價值小物件,大物」據我所看到的,C++專家社區有沒有但在這個話題上達成了新的共識。例如,請參閱How true is "Want Speed? Pass by value"

1

好,有一定的區別:

  • 隨着void cube(double x),你讓變量x的本地副本在你的函數,你可以在不改變值更改其價值的原始x。因此,在本地函數void cube中,可以使用序號變量與x進行交互。
  • 隨着void cube(const double& x),你路過一家指針(在C++中,引用是指針,但隨着使用的輕微另一種語法)和const這裏意味着你不能改變變量的值這個地址(指針指向)。所以,在本地函數void cube中,您應該像使用常量變量一樣與x交互。

性能差異呢?

double隨着有一個在性能上沒有區別,因爲double需要64位,而提及double需要32或64位,沒有太大的區別。但是,假設你有一個結構:

struct some_very_big_struct { 
    ... 
} 

其中sizeof(some_very_big_struct)2^10字節,所以使得這個結構的副本,需要真正大量的時間和更多的內存,所以在這種情況下,通過一個參考是最好的選擇。

+0

大小是實現定義的。例如,使用Visual Studio編譯x86 sizeof(double)== 8和sizeof(double *)== 4 – Creris

+0

@Creris哦,我假定OP使用的是64位系統。編輯,謝謝^^ – FalconUA

1

根據[dcl.init.ref] #5.1中的規則,該標準清楚地要求將參數參數綁定到參數,而不是(例如)將引用綁定到某個副本。差異是可檢測的,所以沒有「如果」規則適用(在所有情況下)。

double a; 
void foo(const double &x) { assert(&a == &x); } 
void bar() { foo(a); } 

有實際相差太大。在const double &x的情況下,名稱x只是可能通過其他左值訪問的雙精度的別名,這對優化有很多影響(如寄存器保存/恢復,指令重新排序等),例如:

double a; 
    void foo(const double &x) { 
     ... = x; 
     a = bar(); // here the compiler must assume that the assignment 
        // may modify x, the two statements can't be reordered 
    } 

    void foo(double x) { 
     ... = x; 
     a = bar(); // here the compiler knows that the assignment 
        // cannot modify x, and the two statements can be 
        // reordered 
    } 
相關問題