2013-07-12 208 views
0

關於「我什麼時候使用引用和指針?」有很多問題。他們讓我困惑了一下。我以爲參考資料不會記憶,因爲它只是地址。C++ - 參考,參數指針

現在,我做了一個簡單的Date類,並給他們的代碼審查的社區。他們告訴我不要在下面的例子中使用參考。但爲什麼?
有人告訴我它會分配一個指針分配的相同內存。這與我所學到的相反。

class A{ 
int a; 
public: 
    void setA(const int& b) { a = b; } /* Bad! - But why?*/ 
}; 

class B{ 
int b; 
public: 
    void setB(int c) { b = c; } /* They told me to do this */ 
}; 

那麼我什麼時候在參數中使用引用或指針,何時只是簡單的複製呢?沒有我的例子中的參考,常量是不必要的?

+4

「他們告訴我不要用在下面的示例參考。但是,爲什麼?」他們不是說爲什麼嗎?沒有解釋的理由,獲取代碼審查意見並不是很有用。你不能問他們爲什麼? –

回答

4

如果您擁有像intlong這樣的輕量級類型,則應使用按值傳遞,因爲使用引用不會產生額外成本。但是,當你通過一些重型,你應該使用參考

+0

簡單易懂。謝謝(雖然有點晚...嘿嘿) – Davlog

8

它不保證是壞的。但在這個特定情況下,它是不必要的。

在許多(或大多數)上下文中,引用是作爲僞裝指針來實現的。你的例子恰好是這些情況之一。假設函數沒有被內聯,參數b將作爲指針在「引擎蓋下」實現。所以,你真正通過setA在第一個版本中是指針int,即提供間接訪問您的參數值。在第二個版本中,您立即傳遞int,即提供直接訪問您的參數值。

哪個更好,哪個更差?那麼,在許多情況下,指針的大小比int大,這意味着第一個變體可能會傳遞更大量的數據。這可能被認爲是「不好的」,但是由於兩種數據類型通常都適合硬件字的大小,所以它可能沒有明顯的差別,特別是如果參數在CPU寄存器中傳遞的話。

此外,爲了讀b是變相的指針,你必須取消引用函數內。從性能角度來看,這也是「不好的」。

這些是人們希望通過值傳遞任何小尺寸參數(小於或等於指針大小)的正式原因。對於參數或更大的尺寸,傳遞const引用變成一個更好的主意(假設你沒有明確要求副本)。

然而,在大多數情況下,一個功能簡單很可能會被內聯,這將完全消除兩個變體之間的差別,不管你使用的參數類型。


const在第二個變體中是不必要的事情是一個不同的故事。 const在第一個變體中有兩個重要用途:

1)它阻止您修改參數值,從而保護實際參數不被修改。如果引用不是const,則可以修改引用參數,從而修改參數。

2)它允許你使用rvalues作爲參數,例如,致電some_obj.setA(5)。沒有那個const這樣的呼叫是不可能的。

在第二個版本中這兩個都不是問題。沒有必要保護修改後的實際參數,因爲參數是該參數的本地副本。不管你對參數做什麼,實際的參數都將保持不變。不管參數是否被聲明爲const,您都可以使用rvalues作爲參數SetA

因此,人們通常不會對通過值傳遞的參數使用頂級const限定符。但是,如果您確實聲明const,它將阻止您修改函數內的本地b。有些人確實喜歡這樣做,因爲它強制執行適度流行的「不要修改原始參數值」約定,因此有時可能會在參數聲明中看到頂級const限定符。

+0

不錯的答案,但我建議將「引用只是一個指針」替換爲「引用只是使用指針實現」。如果對這兩者不太瞭解,最終他們會讓「指針是數組」 - 樣式「引用是指針」的錯誤。 – 2013-07-12 18:13:51

1

我同意審查。這裏的原因:

A(const或非const)引用一個簡單的小類型,如int將更加複雜(在指令數量)。這是因爲呼叫代碼必須將參數的地址傳遞到setA,然後在setA內,必須從存儲在b中的地址取消該值。在b是普通的int的情況下,它僅複製該值本身。所以在保存中至少有一個內存引用的步驟。這在大型程序的長運行時間中可能沒有太大的區別,但如果您在任何地方不斷增加一個額外的循環,那麼它會很快加起來明顯變慢。

我看了一下一段代碼了一句這樣的:

class X 
{ 
    vector v; 
    public: 
    ... 
    void find(int& index, int b); 
    .... 
} 

bool X::find(int &index, int b) 
{ 
    while(v[index] != b) 
    { 
     if (index == v.size()-1) 
     { 
      return false; 
     } 
     index++; 
    } 
    return true; 
} 

這段代碼重寫:

bool X::find(int &index, int b) 
{ 
    int i = index; 
    while(v[i] != b) 
    { 
     if (i == v.size()-1) 
     { 
      index = i; 
      return false; 
     } 
     i++; 
    } 
    index = i; 
    return true; 
} 

意味着這個功能從總量的30%去了一些代碼的執行相當多,稱爲find,大約相同測試的執行時間的5%。因爲編譯器將i放在一個寄存器中,並且只有在完成搜索時才更新參考值。

0

引用是作爲指針來實現的(這不是必需的,但我相信它是普遍真實的)。因爲你只是傳遞一個「int」,所以傳遞指向該int的指針將需要傳遞相同數量的空間(相同或多個寄存器,或相同或更多的堆棧空間,取決於你的架構),所以沒有節省。此外,現在您必須對該指針取消引用,這是一個額外的操作(並且幾乎肯定會導致您進入內存,而根據您的體系結構,您可能不需要對第二個內存執行操作)。

現在,如果你傳遞的比一個int大得多,那麼第一個可能會更好,因爲你只傳遞一個指針。 [注意,有些情況下,即使是非常大的物體,通過價值傳遞仍然是有意義的。無論如何,這些情況通常是當你計劃創建自己的副本時。在這種情況下,最好讓編譯器進行復制,因爲總體方法可能會提高它的優化能力。這些情況非常複雜,我認爲如果你問這個問題,你應該在嘗試解決它們之前更多地學習C++。雖然它們確實有趣的閱讀。]

0

傳遞基元作爲const引用不會爲您節省任何東西。指針和int使用相同數量的內存。如果你傳遞一個const引用,機器將不得不爲一個指針分配內存並複製指針地址,這與分配和複製整數具有相同的成本。如果Date類使用單個64位整數(或double)來存儲日期,則不需要使用const-reference。但是,如果Data類變得更復雜並存儲其他字段,則通過const-reference傳遞Date對象的成本應低於按值傳遞Date對象的成本。