只是一个普遍的问题,一个函数的签名是:
void f( const string & s )...
如果您实际上没有更改字符串(因为它是常量),为什么有必要通过引用传递它?
答案 0 :(得分:6)
通过引用传递有两种方法 - 通过指针传递,并通过值传递。
通过指针传递类似于通过引用传递:可以为它做出“如果你不想修改它,为什么传递指针”的相同参数。
按值传递需要复制。复制字符串通常更昂贵,因为需要在封面下分配和取消分配动态内存。这就是为什么传递const
引用/指针通常比传递您不打算更改的副本更好的原因。
答案 1 :(得分:3)
按值传递变量时,它会复制。通过引用传递避免了该副本。您希望将其标记为const
,原因相同:由于您没有制作副本,因此您不希望意外地弄乱原件。我认为这也可能允许编译器优化。
int
,char
,float
以及其他原始类型通常不会看到这一点的原因是它们复制相对便宜,在某些情况下,通过引用传递更昂贵(例如,通过引用传递char
可能涉及传递64位数据(指针)而不是8位。通过引用传递也会增加一些间接,这不是一个像字符串这样的大字体很重要,但对int
这样的东西很浪费。
答案 2 :(得分:1)
这不是“必要的”,但常见的答案是“出于性能原因,防止复制”,但这是一个天真的答案而且事实有点复杂。
在您的示例中,假设s
确实是不可变的,而您不“拥有或不能更改”,则const
装饰器适用于s
。如果没有采用s
的引用,则可以保证复制(不包括编译器优化)。
如果f()
在s
返回后不会使用f()
的副本,那么复制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()
中调用普通构造函数,并且将在f()
中调用复制ctor,但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()
变为或未变异时编译器可以采取的优化。
我不能建议人们查看David Abrams的帖子,“Want Speed? Pass by value”或Is it better in C++ to pass by value or pass by constant reference?并发布How to pass objects to functions in C++?。
答案 3 :(得分:0)
答案 4 :(得分:0)
这不是必要的,但这是一个非常好的主意,原因如下:
string
。如果指定const
,则允许编译器从文字构造临时string
,以便调用者可以只提供文字而不是string
对象。如果您未指定const
,则编译器无法执行此操作,因此调用方也无法执行此操作。