[英]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.