[英]Why do we not pass POD by reference in functions?
我一直被告知我們不應該通過參考傳遞POD。 但最近我發現一個引用實際上根本沒有內存。
那么為什么我們選擇寫:
void DoSomething(int iNumber);
代替:
void DoSomething(const int& riNumber);
是不是更有效率?
實際上在這種情況下(使用int)傳遞值可能更有效,因為只需要1個內存讀取而不是2來訪問傳遞的值。
示例(使用-O2優化):
int gl = 0;
void f1(int i)
{
gl = i + 1;
}
void f2(const int& r)
{
gl = r + 1;
}
int main()
{
f1(1);
f2(1);
}
ASM
.file "main.cpp"
.text
.p2align 2,,3
.globl __Z2f1i
.def __Z2f1i; .scl 2; .type 32; .endef
__Z2f1i:
LFB0:
pushl %ebp
LCFI0:
movl %esp, %ebp
LCFI1:
movl 8(%ebp), %eax
incl %eax
movl %eax, _gl
leave
ret
LFE0:
.p2align 2,,3
.globl __Z2f2RKi
.def __Z2f2RKi; .scl 2; .type 32; .endef
__Z2f2RKi:
LFB1:
pushl %ebp
LCFI2:
movl %esp, %ebp
LCFI3:
movl 8(%ebp), %eax
movl (%eax), %eax
incl %eax
movl %eax, _gl
leave
ret
LFE1:
.def ___main; .scl 2; .type 32; .endef
.p2align 2,,3
.globl _main
.def _main; .scl 2; .type 32; .endef
_main:
LFB2:
pushl %ebp
LCFI4:
movl %esp, %ebp
LCFI5:
andl $-16, %esp
LCFI6:
call ___main
movl $2, _gl
xorl %eax, %eax
leave
ret
LFE2:
.globl _gl
.bss
.align 4
_gl:
.space 4
不通過引用傳遞POD似乎是一個太普遍的規則。 POD可能很大,並且傳遞對它的引用是值得的。 在您的特定情況下, int
的大小與指針大小相同 - 如果不是所有實現在后台使用以實際實現引用。 傳遞int
或引用之間沒有大小差異,但現在你有額外的間接級別的懲罰。
通過引用傳遞是偽裝通過指針。
對於小數據,可以更快地將其作為值訪問,而不是必須多次使用指針。
因為它是無意義的效率增益99.999%的時間,並且它改變了語義。 它還會阻止您傳遞常量值。 例如:
void Foo(int &i) { }
Foo(1); // ERROR!
但這可行:
void Foo(const int &i) { }
Foo(1);
此外,它意味着該函數可以修改i的值,使其對調用者可見,這可能是一件壞事(但同樣,你當然可以采用const引用)。 它歸結為優化程序中重要的部分,並使其余代碼的語義盡可能正確。
引用實際上根本沒有內存。
不確定是誰告訴你的,但事實並非如此。
沒有正確的答案,甚至沒有明顯的普遍偏好。 在某些情況下,通過值傳遞可以更快,並且非常重要的是,可以消除副作用。 在其他情況下,通過引用傳遞可以更快,並且允許從給定函數更容易地返回信息。 這適用於POD數據類型。 請記住,結構可以是POD,因此POD之間的大小因素也會有所不同。
真正的答案是習慣 。
我們有一種根深蒂固的文化,試圖對我們的代碼進行宏優化(即使它很少有任何真正的區別)。 如果你能告訴我代碼傳遞的值/參考(整數)有什么不同,我會感到驚訝。
一旦你開始通過模板隱藏類型,我們再次開始使用const引用(因為復制某些類型可能很昂貴)。
現在,如果您曾詢問過通用POD,那么可能存在成本差異,因為POD會變得非常大。
如果我們改變原始值,第一個版本中有一個小優勢,我們不需要額外的標識符。
void DoSomething(int iNumber)
{
for(;iNumber > 0; --iNumber) // mutating iNumber
{
// Stuff
}
}
// No real cost difference in code.
// Just in the amount of text we need to read to understand the code
//
void DoSomething(int const& iNumber)
{
for(int loop = iNumber;loop > 0; --loop) // mutating new identifier
{
// Stuff
}
}
取決於你的效率意味着什么。
我們通過常量引用傳遞對象的主要原因是因為通過value執行此操作將調用對象的復制構造函數,這可能是一項昂貴的操作。
復制int
是微不足道的。
不它不是。 int的示例完全相同。 當你有“更重”的物體時,這很重要。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.