C ++通过引用传递大对象

时间:2012-10-22 23:43:01

标签: c++

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

int func(const vector<int> a)

替换它总是有益的
int func(const vector<int> &a)

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

6 个答案:

答案 0 :(得分:4)

总的来说,是的。您应该始终通过引用传递大对象(或者将指针传递给它们,特别是如果您使用的是C)。

答案 1 :(得分: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的右值引用/移动语义。

答案 2 :(得分:2)

大多数情况下它会更有效率 - 但是如果它发生func需要制作自己的矢量副本并且破坏性地修改它,而无论它做什么无论如何,那么你也可以保存几行,让语言隐式地为你复制一个pass-by-value参数。可以想象,如果调用者之后实际上没有使用向量副本,编译器可能会发现可以省略复制。

答案 3 :(得分:0)

正确。传递参考将避免复制。当涉及到副本而您实际上并不需要时,您应该使用引用。 (或者是因为你不打算修改这个值,在这种情况下,对原始文件进行操作很好并且你使用const引用,或者因为你想要修改原文而不是它的副本,在这种情况下你使用非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的结尾,因此尝试分配{{ 1}}到y导致未定义的行为(由于悬空引用)。

请注意,当您处理POD类型(普通旧数据)时,例如zint,您无法通过避免副本获得任何好处。通常,引用与charint一样大(通常与指针一样大),因此通过引用复制long int与复制int相同本身。

答案 4 :(得分:0)

简而言之,是的。既然你无论如何也无法修改a,那么你的所有函数体都可以做另一个副本,你也可以从const-reference中创建它。

答案 5 :(得分:0)

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

  • 它可以更好地平行化。因为没有别名。原始文件可以在不影响函数内部值的情况下进行更改。
  • 更好的缓存位置