只是一个一般性问题说函数的签名是:
void f( const string & s )...
如果您实际上没有更改字符串(因为它是常量),为什么有必要通过引用传递它?
通过引用传递有两种替代方法 - 通过指针传递和按值传递。
通过指针传递类似于通过引用传递:可以为它进行“如果您不想修改指针,为什么要传递它”的相同论点。
按值传递需要复制。复制字符串通常更昂贵,因为需要在后台分配和取消分配动态内存。这就是为什么传递const
引用/指针通常比传递您不打算更改的副本更好的原因。
当您按值传递变量时,它会生成一个副本。通过引用传递可以避免该副本。您想标记它const
的原因相同:由于您不是在制作副本,因此您不想不小心弄乱原件。我认为这也可能允许编译器优化。
您通常不会在int
、char
、float
和其他原始类型中看到这一点的原因是它们复制起来相对便宜,并且在某些情况下,通过引用传递更昂贵(例如,通过char
引用传递 a 可能涉及传递 64位数据(指针)而不是 8 位。通过引用传递也增加了一些间接性,这对于像字符串这样的大类型来说没什么大不了的,但对于像int
.
这不是“必要的”,但常见的答案是“出于性能原因,防止复制”,但这是一个幼稚的答案,事实要复杂一些。
在您的示例中,假设s
真的是不可变的并且您不“拥有或无法更改”的东西,那么const
装饰器适用于s
. 如果没有引用 of s
,那么这将保证复制(不包括编译器优化)。
如果f()
不打算使用s
after f()
return的副本,那么复制的努力s
就白费了。因此,通过引用传递可以防止复制并f()
保留检查字符串的能力s
。伟大的。再一次,这是天真的答案,而 C++11 之前的答案将是最正确的答案。
为了回答“有必要”,还有更多场景值得考虑。但我只关注一个:
如果调用者在调用后
f()
不需要字符串,但需要保留数据的副本。s
f()
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++ 中的函数?.
这不是“必要的”,但这是一个非常好的主意,原因如下:
string
可以从字符串文字构造。如果您指定const
,则允许编译器string
从文字构造一个临时值,以便调用者可以只提供文字而不是string
对象。如果您不指定const
编译器不能这样做,那么调用者也不能这样做。换句话说,您将获得通过引用传递的速度(而不是制作额外的副本)。以及传值的完整性(原变量值不变)