小编典典

在 C++ 中通过值传递还是通过常量引用传递更好?

all

在 C++ 中通过值传递还是通过常量引用传递更好?

我想知道哪个是更好的做法。我意识到通过常量引用传递应该在程序中提供更好的性能,因为您没有制作变量的副本。


阅读 69

收藏
2022-06-09

共1个答案

小编典典

通常建议最佳实践1对 所有类型 使用 pass by const ref ,除了内置类型(, ,等),迭代器和函数对象
__char``int``double
(lambdas,派生自的类std::*_function)。

在移动语义 存在之前尤其如此。原因很简单:如果您按值传递,则必须制作对象的副本,并且除了非常小的对象外,这总是比传递引用更昂贵。

使用 C++11,我们获得了 移动语义
。简而言之,移动语义允许,在某些情况下,一个对象可以被传递“y值”而不复制它。特别是,当您传递的对象是
rvalue
时,就是这种情况。

就其本身而言,移动对象仍然至少与通过引用传递一样昂贵。然而,在许多情况下,一个函数无论如何都会在内部复制一个对象——即它将获得参数的 所有权 。2

在这些情况下,我们有以下(简化的)权衡:

  1. 我们可以通过引用传递对象,然后在内部复制。
  2. 我们可以按值传递对象。

“按值判断”仍然会导致对象被复制,除非对象是右值。在右值的情况下,可以移动对象,因此第二种情况突然不再是“opy, then move”而是“ove,
then (potentially) move again”。

对于实现正确移动构造函数的大型对象(例如向量、字符串“”),第二种情况比第一种更有效
因此,如果函数拥有参数的所有权,并且对象类型支持高效移动 ,则建议使用按值传递。


历史注释:

事实上,任何现代编译器都应该能够找出何时按值传递代价高昂,并尽可能隐式地将调用转换为使用 const ref。

理论上。
在实践中,编译器可以——总是在不破坏函数的二进制接口的情况下改变它。在某些特殊情况下(当函数被内联时),如果编译器能够确定原始对象不会通过函数中的操作进行更改,则实际上会省略副本。

但总的来说,编译器可以确定这一点,而 C++ 中移动语义的出现使这种优化变得不那么重要了。


1例如,Scott Meyers, Effective C++

2对于对象构造函数来说尤其如此,它可以接受参数并将它们存储在内部,以成为构造对象状态的一部分。

2022-06-09