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的右值引用/移动语义。

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

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

这不仅仅是“节省几行”,而是要在标题中明确意图(保证来电者不会对其进行变异)。@james.haggerty:如果仅此而已,
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
会导致未定义的行为(由于悬挂引用)。