2

只是一个一般性问题说函数的签名是:

void f( const string & s )...

如果您实际上没有更改字符串(因为它是常量),为什么有必要通过引用传递它?

4

5 回答 5

6

通过引用传递有两种替代方法 - 通过指针传递和按值传递。

通过指针传递类似于通过引用传递:可以为它进行“如果您不想修改指针,为什么要传递它”的相同论点。

按值传递需要复制。复制字符串通常更昂贵,因为需要在后台分配和取消分配动态内存。这就是为什么传递const引用/指针通常比传递您不打算更改的副本更好的原因。

于 2013-10-07T00:52:16.233 回答
3

当您按值传递变量时,它会生成一个副本。通过引用传递可以避免该副本。您想标记它const的原因相同:由于您不是在制作副本,因此您不想不小心弄乱原件。我认为这也可能允许编译器优化。

您通常不会在intcharfloat和其他原始类型中看到这一点的原因是它们复制起来相对便宜,并且在某些情况下,通过引用传递更昂贵(例如,通过char引用传递 a 可能涉及传递 64位数据(指针)而不是 8 位。通过引用传递也增加了一些间接性,这对于像字符串这样的大类型来说没什么大不了的,但对于像int.

于 2013-10-07T00:50:23.907 回答
1

这不是“必要的”,但常见的答案是“出于性能原因,防止复制”,但这是一个幼稚的答案,事实要复杂一些。

在您的示例中,假设s真的是不可变的并且您不“拥有或无法更改”的东西,那么const装饰器适用于s. 如果没有引用 of s,那么这将保证复制(不包括编译器优化)。

如果f()不打算使用safter f()return的副本,那么复制的努力s就白费了。因此,通过引用传递可以防止复制并f()保留检查字符串的能力s。伟大的。再一次,这是天真的答案,而 C++11 之前的答案将是最正确的答案。

为了回答“有必要”,还有更多场景值得考虑。但我只关注一个:

如果调用者在调用后 f()不需要字符串,但需要保留数据的副本。sf()f()

假设代码是:

void f(const std::string& arg1);   // f()'s signature
void g(const std::string& arg1) {
   std::string s(arg1);
   s.append(" mutate s");
   f(s);
}

在这种情况下,您将构建字符串s,通过const引用传递它,f()如果您假设f()它是不透明的并且没有进一步的优化可用,那么从性能角度来看一切都很好。

现在,假设f()需要 中的数据副本s,然后呢?好吧,f()将调用一个复制构造函数并复制s到一个局部变量中:

 // Hypothetical f()
 void f(const std::string& s) {
   this->someString_ = s;
 }

在这种情况下,当数据存储在 中someString_时,正常的构造函数将被调用g(),并且复制 ctor 将被调用f(),但是所做的工作g()将被浪费掉。为了提高性能,可以做两件事,按值传递和/或使用移动构造函数。

// Explicitly move arg1 in to someString_
void f(std::string&& arg1) {
  this->somestring_ = std::move(arg1);
}

void g(const std::string& arg1) {
   std::string s(arg1);
   s.append(" mutate s");
   f(s);
}

这是明确地做编译器将从开始自动做的事情C++11,这意味着更正确的版本是按值传递并让编译器做正确的事情:

void f(std::string arg1) {
  this->somestring_ = arg1; // Implicit move, let the compiler do the right thing
}

void g(const std::string& arg1) {
   std::string s(arg1);
   s.append(" mutate s");
   f(s);
}

在这种情况下,字符串是在其中构建的,g()并且没有在任何地方进行任何额外的工作。所以在这种情况下,答案是,

如果您实际上没有更改字符串(因为它是常量),为什么有必要通过引用传递它?

字符串没有改变,但被复制了,因此不需要 const 引用

读者可以在练习中列出编译器s在调用f().

我不能推荐人们关注大卫艾布拉姆斯的帖子,'想要速度?按值传递' 还是在 C++ 中按值传递或通过常量引用传递更好?并发布如何将对象传递给 C++ 中的函数?.

于 2013-10-07T05:02:12.137 回答
0

这不是“必要的”,但这是一个非常好的主意,原因如下:

  1. 效率。不创建或销毁新字符串比创建和销毁它们更有效,特别是因为字符串的长度是任意的,因此需要动态分配的内存。
  2. Astring可以从字符串文字构造。如果您指定const,则允许编译器string从文字构造一个临时值,以便调用者可以只提供文字而不是string对象。如果您不指定const编译器不能这样做,那么调用者也不能这样做。
于 2013-10-07T01:03:50.867 回答
0

换句话说,您将获得通过引用传递的速度(而不是制作额外的副本)。以及传值的完整性(原变量值不变)

于 2013-10-07T00:55:33.273 回答