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&amp;) 通过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&amp; s) 中,字符串不会被复制,但您传递了对它的引用。这通常在您有一个大对象时完成,因此“按值传递”会产生开销(这就是 c++ 支持这两种方法的原因)。通过引用传递意味着您可以更改您传递的“大”对象的值,但由于 const 说明符,您无法修改它。这是一种“保护”。

【讨论】:

const string s 如果能更好地传达意图,则可能需要 string sconst string s 的意图有什么区别? Martin B:这是函数的内部细节,可能会帮助任何必须编写该函数的人,但签名是兼容的(你可以这样做void f(int const); typedef void (*FP)(int); FP p = &amp;f;),它对调用者没有意义。 【参考方案4】:

您的对象可能包含一个 mutable 成员,即使使用 const 引用也可以对其进行修改

【讨论】:

【参考方案5】:

对于outputStringByRef,您必须确保您传递引用的变量在outputStringByRef 需要时保持活动状态且不变。使用outputStringByVal,您传递的变量可能会死亡或超出范围,并且函数具有的副本仍然可以。

【讨论】:

【参考方案6】:

在能够修改函数中的字符串方面(几乎)没有区别。但是,在传递的内容方面存在很大差异。 const mystring &amp;ss 重载采用对字符串的 const 引用。虽然它不能修改它,但它是被寻址的同一个内存。如果字符串很长,这可能是一个很大的因素(假设字符串不是使用copy-on-write 实现的)。 const mystring ss 表单正在复制字符串,因此将寻址不同的内存。

实际上const mystring &amp;ss 表单可以更改字符串,如果使用const_cast&lt;mystring&amp;&gt; - 虽然我不建议在这里。

【讨论】:

【参考方案7】:

假设您要打印一个无法复制的对象:

Thread th;
// ...
cout << th; // print out informations...

参考版本不会复制线程,但会获取th的地址并为其创建一个别名。另一个版本会在按值传递时尝试复制线程 - 但复制对于这样的对象可能没有意义(那么会有一个额外的线程吗?)。

复制对象理解为克隆对象可能对您有所帮助。在 C++ 中复制上面的th 不仅仅意味着拥有与 Java 中相同的对象的另一个句柄 - 它确实意味着隐式克隆表示的对象,并拥有它的完整副本。所以这个问题类似于“为什么 Java 同时支持 Object.clone 和引用的复制?” - 两者都有不同的目的。

毕竟还有性能问题。您不想每次为资源匮乏的对象传递一些东西时都进行复制。

【讨论】:

以上是关于f(const string &) 和 f(const string) 有啥区别吗?的主要内容,如果未能解决你的问题,请参考以下文章

将 const 引用成员设为临时变量是不是安全?

string::front

const rvalue 编译器的区别

临时变量不能作为非const引用

boost::const 和 non-const 的变体

线性基求交板子