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

时间:2011-09-26 21:42:25

标签: c++

我一直被告知我们不应该通过引用传递POD。但最近我发现一个引用实际上根本没有内存。

那么我们为什么选择写作:

void DoSomething(int iNumber);

而不是:

void DoSomething(const int& riNumber);

效率不高吗?

8 个答案:

答案 0 :(得分:5)

实际上在这种情况下(使用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

答案 1 :(得分:2)

不通过引用传递POD似乎是一个太普遍的规则。 POD可能很大,并且传递对它的引用是值得的。在您的特定情况下,int的大小与指针的大小相同 - 如果并非所有实现在后台使用以实际实现引用。传递int或引用之间没有大小差异,但现在你有一个额外的间接级别的惩罚。

答案 2 :(得分:1)

通过引用传递是伪装传递指针。

对于小数据,可以更快地将其作为值访问,而不是必须多次使用指针。

答案 3 :(得分:1)

因为它是无意义的效率增益99.999%的时间,并且它改变了语义。它还会阻止您传递常量值。例如:

void Foo(int &i) { }
Foo(1);  // ERROR!

然而,这会奏效:

void Foo(const int &i) { }
Foo(1);

此外,这意味着该函数可以修改i的值,使其对调用者可见,这可能是一件坏事(但同样,你当然可以采用const引用)。它归结为优化程序中重要的部分,并使其余代码的语义尽可能正确。

  

引用实际上根本没有内存。

不确定是谁告诉你的,但事实并非如此。

答案 4 :(得分:1)

没有正确答案,甚至没有明显的普遍偏好。在某些情况下,通过值传递可以更快,并且非常重要的是,可以消除副作用。在其他情况下,通过引用传递可以更快,并且允许从给定函数更容易地返回信息。这适用于POD数据类型。请记住,结构可以是POD,因此POD之间的大小因素也会有所不同。

答案 5 :(得分:1)

真正的答案是习惯 我们有一种根深蒂固的文化,试图对我们的代码进行宏优化(即使它很少有任何真正的区别)。如果你能告诉我代码传递的值/参考(整数)会有所不同,我会感到惊讶。

一旦你开始通过模板隐藏类型,我们再次开始使用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
    }
}

答案 6 :(得分:0)

取决于效率的含义。

我们通过常量引用传递对象的主要原因是因为按值执行操作会调用对象的复制构造函数,这可能是一项昂贵的操作。

复制int是微不足道的。

答案 7 :(得分:0)

不,不是。 int的示例完全相同。 当你有“更重”的物体时,这很重要。