通过值或引用传递构造函数参数

Passing constructor arguments by value or by reference

本文关键字:构造函数 参数 引用      更新时间:2023-10-16

只是一个关于这样一个函数的快速问题:

class Test {
  public:
    Test(vector<int>& v) {
      v_ = v;
    }
  private:
    std::vector<int> v_;
};

使用Test(vector<int>& v)Test(vector<int> v)有什么区别?我似乎知道第一个应该更快,因为它是通过引用传递的。但我不确定是否还有其他不同之处。

不同之处在于,Test(vector<int>& v)(顺便说一句,它是一个左值引用)v指的是原始对象,而Test(vector<int> v)有一个副本。下面的示例代码演示了int和普通函数的区别(注意,对于int,按值传递实际上更快!):

#include <iostream>
int global_i;
void pass_by_value(int i)
{
  std::cout << "pass by value:n";
  std::cout << "initially: i = " << i << ", global_i = " << global_i << "n";
  i++;
  std::cout << "after i++: i = " << i << ", global_i = " << global_i << "n";
  global_i++;
  std::cout << "after global_i++: i = " << i << ", global_i = " << global_i << "n";
}
void pass_by_reference(int& i)
{
  std::cout << "pass by reference:n";
  std::cout << "initially: i = " << i << ", global_i = " << global_i << "n";
  i++;
  std::cout << "after i++: i = " << i << ", global_i = " << global_i << "n";
  global_i++;
  std::cout << "after global_i++: i = " << i << ", global_i = " << global_i << "n";
}
void pass_by_const_reference(int const& i)
{
  std::cout << "pass by const reference:n";
  std::cout << "initially: i = " << i << ", global_i = " << global_i << "n";
  // i++; not allowed!
  // std::cout << "after i++: i = " << i << ", global_i = " << global_i << "n";
  global_i++;
  std::cout << "after global_i++: i = " << i << ", global_i = " << global_i << "n";
}
int main()
{
  global_i = 1;
  pass_by_value(global_i);
  global_i = 1;
  pass_by_reference(global_i);
  global_i = 1;
  pass_by_const_reference(global_i);
}

它的输出是:

pass by value:
initially: i = 1, global_i = 1
after i++: i = 2, global_i = 1
after global_i++: i = 2, global_i = 2
pass by reference:
initially: i = 1, global_i = 1
after i++: i = 2, global_i = 2
after global_i++: i = 3, global_i = 3
pass by const reference:
initially: i = 1, global_i = 1
after global_i++: i = 2, global_i = 2
如你所见,在按值调用中,实参和传递的变量是完全分开的。增加参数不会改变传递的变量,增加传递的变量也不会改变参数。另一方面,使用引用传递时,实参只提供对传递的变量的访问:您增加哪个变量并不重要,因为实际上它们是相同的。通过const引用传递,它们也是一样的,但是不允许对参数进行修改(但是有一些方法可以解决这个问题)。然而,参数仍然会反映对传递的变量的任何更改。

这些是功能上的差异。然而,有更多的区别:对于按值传递和按const引用传递,您可以使用右值,如call_by_value(2)call_by_const_reference(2)。对于按值调用,发生的事情很明显:参数获得值2,仅此而已。然而,对于const引用,需要一个对象(例如,可以在函数中获取该对象的地址)。因此,在这种情况下,将创建一个临时对象。对于非const引用调用,不能传递右值。

c++ 11添加了另一种类型,即右值引用。用&&代替&表示。在函数内部,它们的行为与普通的(左值)引用完全相同,但它们的不同之处在于它们可以绑定到右值,即使它们不是const。而且,如果使用它们作为返回类型,调用表达式将是右值类型,就像您返回了一个值一样。特别是,您不能将返回右值引用的函数的结果传递给期望左值引用的函数,就像您不能对字面量2那样做。

您的代码中没有右值引用,只是大量不必要的复制。

然而,既然我们在这个话题上,这里是用move语义写这个的正确方法:

Test(std::vector<int> v)  // by value!
:  v_(std::move(v))
{
}

在11之前的世界中,次佳方法是通过const-reference获取实参并复制它:

Test(std::vector<int> const & v)
:  v_(v)
{
}