C++;大型对象的按参照传递 如果我有C++函数声明: int func(const vector<int> a) int func(常量向量a)
将其替换为C++;大型对象的按参照传递 如果我有C++函数声明: int func(const vector<int> a) int func(常量向量a),c++,C++,将其替换为 int func(const vector<int> &a) int func(常量向量&a) 因为后者不需要复制a来传递到函数中?一般来说,是的。您应该始终通过引用传递大型对象(或传递指向它们的指针,尤其是在使用C时)。正确。传递引用将避免复制。当涉及到一份副本而实际上不需要时,你应该使用引用。(可能是因为您不想修改该值,在这种情况下,对原始值进行操作是可以的,您会使用常量引用,或者是因为您确实想修改原始值而不是其副本,在这种情况下,您会使用非常量引用。)
int func(const vector<int> &a)
int func(常量向量&a)
因为后者不需要复制
a
来传递到函数中?一般来说,是的。您应该始终通过引用传递大型对象(或传递指向它们的指针,尤其是在使用C时)。正确。传递引用将避免复制。当涉及到一份副本而实际上不需要时,你应该使用引用。(可能是因为您不想修改该值,在这种情况下,对原始值进行操作是可以的,您会使用常量引用,或者是因为您确实想修改原始值而不是其副本,在这种情况下,您会使用非常量引用。)
当然,这并不局限于函数参数。例如,看看这个函数:
std::string foo();
大多数人会以这种方式使用该功能:
std::string result = foo();
但是,如果您不修改结果
,这会更好:
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
会导致未定义的行为(由于悬挂引用)
请注意,在处理POD类型(普通的旧数据)时,如
int
或char
,避免复制不会带来任何好处。通常引用与int
或long int
一样大(通常与指针一样大),因此通过引用复制int
与复制int
本身是一样的。简言之,是的。既然你无论如何都不能修改a
,你的函数体所能做的就是复制另一个副本,你也可以从const引用中复制它。大多数情况下,它会更有效——但是如果发生这种情况,func
需要自己复制向量,并在执行任何操作时对其进行破坏性的修改,然后,您还可以保存几行代码,让该语言隐式地作为传递值参数为您创建副本。可以想象,如果调用方事后没有实际使用向量的副本,编译器可能会发现复制可以省略。就效率而言,就像您所想的,几乎总是肯定的。有时(据称)这可能较慢,通常是基本类型或小型类型:
// 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的右值引用/移动语义。我可以想象传递值可能更有效的一些原因:
- 它可以更好地平行化。因为没有别名。原始值可以在不影响函数内部值的情况下更改
- 更好的缓存位置
const
应该足够了。我将其改为当您*不*想要避免复制*或*当您想要修改引用时,您应该使用引用。有时,您确实希望复制发生(例如在具有适当编写的复制构造函数的赋值运算符中)@MikeBantegui这是一个很好的观点。我用(希望)更清晰的措辞更新了答案。不需要移动语义,C++98通过复制省略解决了这个问题。在C++-11之前,许多编译器为这类事情实现了名为返回值优化的方法,因此实际上可能不会发生复制。此外,注意,在将临时变量绑定到引用时,必须注意临时生命周期,例如const int&f(const int&x){return x;}const int&y=f(23);int z=y;/*哎呀*/
。要点是,值为23
的临时int
的生存期不会超出将f(23)
绑定到y
的表达式末尾,因此尝试将y
赋值给z
会导致未定义的行为(由于悬挂引用)。