3

如果我有一个 C++ 函数声明:

int func(const vector<int> a)

用它代替它总是有益的吗

int func(const vector<int> &a)

因为后者不需要复制a来传递给函数?

4

6 回答 6

4

一般来说,是的。您应该始终通过引用传递大对象(或传递指向它们的指针,尤其是在使用 C 时)。

于 2012-10-22T23:44:51.597 回答
4

就您所想的效率而言,几乎总是肯定的。有时(据称)这可能会更慢,通常是基本类型或小型类型:

// copy x? fits in register: fast
void foo(const int x);

// reference x? requires dereferencing on typical implementations: slow
void foo(const int& x); 

但是内联无论如何都无关紧要,而且您可以自己按值键入它;这仅与通用模板函数有关。

然而,重要的是要注意您的转换可能并不总是有效的,即因为您的函数获得了自己的数据副本。考虑这个更简单的例子:

void foo(const int x, int& y)
{
    y += x;
    y += x;
}

int v = 1;
foo(v, v); // results in v == 3

进行转型,您将获得:

void foo(const int& x, int& y)
{
    y += x;
    y += x;
}

int v = 1;
foo(v, v); // results in v == 4

因为即使您无法写入x,也可以通过其他方式写入。这称为混叠。虽然可能与您给出的示例无关(尽管全局变量仍然可以别名!),但请注意原则上的差异。

最后,如果您仍然要制作自己的副本,只需在参数列表中进行即可;编译器可以为您优化,尤其是使用 C++11 的右值引用/移动语义。

于 2012-10-22T23:50:28.790 回答
2

大多数情况下它会更有效-但是如果发生func需要制作自己的向量副本并在它无论如何都做任何事情时破坏性地修改它,那么您最好保存几行并让语言制作副本为您隐含地作为按值传递的参数。可以想象,如果调用者之后实际上没有使用其向量的副本,编译器可能会发现可以省略复制。

于 2012-10-22T23:45:43.023 回答
0

正确的。传递引用将避免复制。当涉及副本而您实际上并不需要时,您应该使用参考。(或者是因为您不打算修改该值,在这种情况下对原始值进行操作很好并且您将使用 const 引用,或者因为您确实想要修改原始值而不是它的副本,在这种情况下您将使用非常量引用。)

当然,这不仅限于函数参数。例如,看这个函数:

std::string foo();

大多数人会以这种方式使用该功能:

std::string result = foo();

但是,如果您不修改result,这会更好:

const std::string& result = foo();

没有复制。此外,与指针相反,引用保证由返回的临时foo()对象保持有效并且不会超出范围(指向临时对象的指针是危险的,而对临时对象的引用是完全安全的。)

C++-11 标准通过使用移动语义解决了这个问题,但是大多数现有代码还没有使用这个新特性,所以尽可能使用引用是一个很好的习惯。

另外,请注意,在将临时对象绑定到引用时,您必须小心临时生命周期,例如:

const int& f(const int& x)
{ return x; }

const int& y = f(23);
int z = y; /* OOPS */

关键是值为 23 的临时变量的生命周期int不会超出表达式绑定f(23)到的结尾y,因此尝试分配y给会z导致未定义的行为(由于悬空引用)。

请注意,当您处理诸如intor之类的 POD 类型(普通旧数据)时,char避免复制不会赢得任何东西。通常引用与intlong int一样大(通常与指针一样大),因此int通过引用复制与复制int自身相同。

于 2012-10-22T23:44:52.317 回答
0

简而言之,是的。由于无论如何您都无法修改a,因此您的函数体所能做的就是制作另一个副本,您也可以从 const-reference 制作它。

于 2012-10-22T23:45:22.937 回答
0

我可以想象按值传递可能更有效的一些原因:

  • 它可以更好地并行化。因为没有混叠。原始可以更改而不影响函数内部的值。
  • 更好的缓存局部性
于 2012-10-22T23:50:41.393 回答