2010-01-11 96 views
3

非模板類,沒有任何理由,更喜歡函數的返回簽名的形式const <type>& foo();<type> foo();?其中<type>是一種內在類型。那麼如果<type>是一個類/結構對象呢?常量<type>和富()與<type> FOO()

的功能是否是常量也有興趣有差別:const <type>& foo() const;<type> foo() const;

例如在非模板類,非模板函數:

const int& foo() const { return member_variable_; } 

對戰

int foo() const { return member_variable_; } 
+1

重複:HTTP://計算器.com/questions/494756/when-you-you-return-a-reference-to-a-object-from-a-class-method – 2010-01-11 21:55:51

+0

@Stefan細微差別,你會注意到我的問題是標記優化..你發現的另一個問題是詢問最佳實踐。 – paxos1977 2010-01-12 16:46:46

回答

0

如果需要返回一個值,你必須返回一個值 - const引用是不是一個可以接受的替代品。但我懷疑你是在問這樣的事情:

struct A { 
    X x; 
    ... 
    X f1() { return x; } 
    const X & f2() { return x; } 
}; 

我從來沒有想出一個這樣的萬無一失的指導方針。

+0

@Neil:對於內在類型的情況更感興趣,在你的例子中X是一個整數。(爲什麼值得我傾向於返回值) – paxos1977 2010-01-11 21:39:23

+1

我認爲,使用'auto',通過引用返回會變得越來越危險。現在像'auto x = a.f2()'會導致'x'靜靜地成爲一個引用,如果'a'在'x'之前被破壞(如果它實際上是一個智能指針,這很有可能),我們得到一個懸而未決的參考。國際海事組織,除非意圖是要返回一個引用(這可以通過返回一個[可能是聰明的]指針來更好地突出顯示),只需返回值,並讓RVO開始。 – 2010-01-11 21:41:11

+1

哦,是的,同樣,在C++ 0x中的移動語義對於具有移動構造函數的任何類型(涵蓋所有標準類型,對於任何昂貴的 - 複製用戶定義類型定義一個類型),對於按值返回的perf命中將會忽略不計, 。 – 2010-01-11 21:42:10

0

這一切都取決於你想要做什麼。

如果你想引用返回一個對象由用戶代碼中使用,則:

<type>& foo() { ... } 

如果你想引用返回一個對象,但只讀方式,允許只有進入此對象類型的const函數,則:

const <type>& foo() { ... } 

如果你想引用返回一個對象由用戶代碼中使用,即使類的實例是隻讀的使用(如常量)然後:

<type>& foo() const { ... } 

如果您希望以只讀方式返回對對象的引用,只允許訪問此對象類型的常量函數,並且如果類實例以只讀方式使用(如const),則:

const <type>& foo() const { ... } 

只需添加一個警告:不要返回函數的變量的引用,它們不是活着出來的功能...

+1

我不得不說,我非常強烈地說:「這就是說,這種功能(但不是所有情況下)的良好實踐......」 - 這只是極少數情況下的良好實踐 - 操作符[]用於容器是我能想到的唯一例子。 – 2010-01-11 21:41:53

+0

好吧,我將刪除這個rec​​omandation,因爲我覺得不夠有經驗,但我不得不說我經常使用與此方案相匹配的accessor-like成員函數,使用const-correctness使整個只讀訪問系統可能沒有通過使用非const成員函數打破它。也就是說,我可能沒有足夠的經驗來看到這個問題。 – Klaim 2010-01-11 21:51:47

+0

僅僅因爲你有一個類的非const實例(或引用)並不意味着你應該有對其數據成員的無限制訪問。如果您完全依賴您正在訪問的實例的常量,那麼最好不要使用成員函數並使用普通結構。 – 2010-01-11 21:56:14

0
const <type>& foo(); 

你可能有相似元素的容器資源在你的班級和foo()返回特定元素,如果您知道資源可用時間足夠長,則可以返回對容器元素的const引用,這樣可以避免不必要的數據分配/副本。 它可能是const或者不是,取決於你如何處理數據,如果你不想通過foo()返回的引用修改數據,那麼使它成爲const。也許你有一個情況,你想修改資源和一些額外的工作需要完成,你會使非const。你可以讓你的api用戶都選擇基於用例。

<type> foo(); 

返回資源<type>的新副本。這意味着一個副本。這對於簡單的數據很有用,或者可以共享值,並且您想要確保<type>的每個實例都是單獨的 - 非共享像某些字符串或int。

0

如果這個類型是一個內在類型,那麼繞過const refs不會提高性能,並且可能會降低它的性能,也會降低安全性;最好是喜歡傳球和按價值迴歸。我不是一個ASM人,但據我瞭解,通過引用返回意味着該值無法存儲在寄存器中,因爲它需要返回地址,因此可以禁用一些其他有用的優化;儘管如此,我對此並不積極。

如果它是一個類或結構,它取決於值的生命週期;如果它是本地的函數,那麼它不能被返回,否則,如果它是一個類成員,我更喜歡常量類型&返回,因爲它可以通過避免複製構造函數提供更好的性能。 (意見不同,因爲價值被認爲更安全)。它還可以讓你不必執行復制構造函數或使用潛在的惡意默認值,這很好。

3

對於原始類型,只需按值返回即可。沒有理由通過const引用返回一個原語。

對於非原始類型,它取決於。

如果返回的值是函數的局部變量,則必須按值返回。否則,在調用者使用返回的引用之前,該變量將被銷燬。

如果返回的值是類數據成員,那麼您可以選擇。

通過const引用返回避免了副本,這可能是一個性能優勢。但是,它將您的接口綁定到實現,即數據成員和返回值必須是相同的類型。這也是不太安全的,因爲主叫方可以保留參考長於對象的生命週期,如:

const X& x = y->foo(); 
delete y; 
// use x, oops! 

返回的值會帶來一個或兩個拷貝,這取決於你是否能取返回值優化的優勢(這很可能是像這樣簡單的get()方法)。另外,使用默認拷貝構造函數的對象副本可以更高效地複製(想想:編譯器可以簡單地對數據進行memcpy)。

建議:只有在確定該調用/複製被認爲是性能問題後,纔可以按值返回,然後切換到僅由const引用返回。

最後,另一種選擇是通過參數返回,這對於更復雜的對象(例如,字符串)有用:

void foo(X& return_val) { return_val = member_variable_; } 

或:

void foo(X* return_val) { 
    assert(return_val != 0); 
    *return_val = member_variable_; 
} 
+0

'使用default ... memcpy'=>的對象副本不太完美。該標準允許編譯器在所謂的返回值優化中省略副本。 'T f(){T tmp;/*操作* /返回tmp; }編譯器允許做的是在分配給返回值的內存中創建'tmp'變量,並避免在return語句中複製該變量。編譯器不會將'memcpy'對象從一個位置移動到另一個位置。 – 2010-01-12 00:09:36

+0

我的memcpy評論不是指返回值優化,而是指向POD對象與間接副本的副本。例如,請參閱http://docs.sun.com/app/docs/doc/820-7599/bkahu?a=view。 – 2010-01-12 01:22:14

相關問題