繁体   English   中英

为什么我们不通过函数中的引用传递POD?

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM