f(const string &) 和 f(const string) 有啥区别吗?
Posted
技术标签:
【中文标题】f(const string &) 和 f(const string) 有啥区别吗?【英文标题】:Any differences between f(const string &) and f(const string )?f(const string &) 和 f(const string) 有什么区别吗? 【发布时间】:2010-12-29 23:34:11 【问题描述】:class mystring
friend ostream& operator<<(ostream &out, const mystring ss)
out << ss.s;
return out;
private:
string s;
public:
mystring(const char ss[])
cout << "constructing mystring : " << ss << endl;
s = ss;
;
void outputStringByRef(const mystring &ss)
cout << "outputString(const string& ) " << ss << endl;
void outputStringByVal(const mystring ss)
cout << "outputString(const string ) " << ss << endl;
int main(void)
outputStringByRef("string by reference");
outputStringByVal("string by value");
outputStringByRef(mystring("string by reference explict call mystring consructor"));
outputStringByVal(mystring("string by value explict call mystring constructor"));
///:~
考虑到上面的例子,我们不能修改传递引用变量,也不能修改传递值变量。每种方法的输出是相同的。由于这两种方法没有区别,为什么 C++ 支持这两种方法?
谢谢。
【问题讨论】:
相关***.com/questions/1567138/const-t-arg-vs-t-arg/… 【参考方案1】:两者是有区别的。考虑以下几点:
#include <iostream>
#include <string>
using std::string;
string g_value;
void callback()
g_value = "blue";
void ProcessStringByRef(const string &s)
callback();
std::cout << s << "\n";
void ProcessStringByValue(const string s)
callback();
std::cout << s << "\n";
int main()
g_value = "red";
ProcessStringByValue(g_value);
g_value = "red";
ProcessStringByRef(g_value);
输出:
red
blue
仅仅因为引用是函数内部的 const ,并不意味着不能通过其他引用来修改引用(一个对象具有多个引用或指向它的指针的情况称为“别名”)。因此,传递一个 const 引用和传递一个 const 值是不同的——在引用的情况下,对象可能在调用后发生变化。值的情况下,被调用者有一个私有副本,不会改变。
由于他们做不同的事情,C++ 让你选择你想要的。
无论哪种方式都会对性能产生影响 - 当您通过价值传递时,必须制作副本,这会产生成本。但是编译器然后知道只有你的函数可能有对该副本的任何引用,这可能允许其他优化。在callback()
返回之前,ProcessStringByRef 无法加载要打印的字符串内容。 ProcessStringByValue 可以,如果编译器认为这样做更快。
通常您关心的是副本,而不是指令的执行顺序,因为通常副本的成本要高得多。因此,通常,您会在可能的情况下通过引用传递非平凡的对象。但是混叠的可能性有时会对性能产生非常严重的影响,即使实际上没有混叠发生,也会阻止某些优化。这就是为什么存在“严格的别名规则”以及 C99 中的 restrict
关键字的原因。
【讨论】:
【参考方案2】:f(const string&)
通过const
引用获取字符串:f
直接对通过引用传递的字符串对象进行操作:不涉及复制。 const
阻止修改原始对象。
f(const string)
接受一个字符串值,这意味着f
获得了原始字符串的副本。即使你删除了const
,当按值传递时,当f
返回时,对字符串的任何修改都会丢失。
我不知道您所说的“为什么 C++ 支持这两种方法?”是什么意思。这只是适用的一般重载规则。
【讨论】:
why do c++ support both methods
,因为起初,我没有看到这两个函数之间的区别。我认为两者是相同的。在深入研究g++和msvc生成的程序集之后,似乎同样的方式,传递相同的常量地址。【参考方案3】:
f(string s)
按值传递字符串 s,换句话说,它创建一个副本并使用您传递的字符串的值对其进行初始化。对副本的任何更改都不会传播到您传递给调用函数的原始字符串。
在f(const string s)
中,const 是多余的,因为无论如何您都无法更改原始值。
在f(const string& s)
中,字符串不会被复制,但您传递了对它的引用。这通常在您有一个大对象时完成,因此“按值传递”会产生开销(这就是 c++ 支持这两种方法的原因)。通过引用传递意味着您可以更改您传递的“大”对象的值,但由于 const 说明符,您无法修改它。这是一种“保护”。
【讨论】:
const string s
如果能更好地传达意图,则可能需要
string s
和const string s
的意图有什么区别?
Martin B:这是函数的内部细节,可能会帮助任何必须编写该函数的人,但签名是兼容的(你可以这样做void f(int const); typedef void (*FP)(int); FP p = &f;
),它对调用者没有意义。
【参考方案4】:
您的对象可能包含一个 mutable 成员,即使使用 const 引用也可以对其进行修改
【讨论】:
【参考方案5】:对于outputStringByRef
,您必须确保您传递引用的变量在outputStringByRef
需要时保持活动状态且不变。使用outputStringByVal
,您传递的变量可能会死亡或超出范围,并且函数具有的副本仍然可以。
【讨论】:
【参考方案6】:在能够修改函数中的字符串方面(几乎)没有区别。但是,在传递的内容方面存在很大差异。 const mystring &ss
重载采用对字符串的 const 引用。虽然它不能修改它,但它是被寻址的同一个内存。如果字符串很长,这可能是一个很大的因素(假设字符串不是使用copy-on-write 实现的)。 const mystring ss
表单正在复制字符串,因此将寻址不同的内存。
实际上const mystring &ss
表单可以更改字符串,如果使用const_cast<mystring&>
- 虽然我不建议在这里。
【讨论】:
【参考方案7】:假设您要打印一个无法复制的对象:
Thread th;
// ...
cout << th; // print out informations...
参考版本不会复制线程,但会获取th
的地址并为其创建一个别名。另一个版本会在按值传递时尝试复制线程 - 但复制对于这样的对象可能没有意义(那么会有一个额外的线程吗?)。
将复制对象理解为克隆对象可能对您有所帮助。在 C++ 中复制上面的th
不仅仅意味着拥有与 Java 中相同的对象的另一个句柄 - 它确实意味着隐式克隆表示的对象,并拥有它的完整副本。所以这个问题类似于“为什么 Java 同时支持 Object.clone
和引用的复制?” - 两者都有不同的目的。
毕竟还有性能问题。您不想每次为资源匮乏的对象传递一些东西时都进行复制。
【讨论】:
以上是关于f(const string &) 和 f(const string) 有啥区别吗?的主要内容,如果未能解决你的问题,请参考以下文章