const引用行为 - 为什么这个输出?

时间:2015-08-29 17:35:09

标签: c++

以下输出对我来说并不清楚。为什么在第一种情况下输出是:Joni Gallo,而在第二种情况下(在我只更改了一行之后),输出是:Jonlo

输出:

// classes and default constructors
#include <iostream>
#include <string>
using namespace std;

class Example3 {
    string data;
  public:
        Example3 (const string& str) 
        {
            data = str;
           // data.erase(3,5);
        }

    Example3() {}
    void doit()
    {
    data.erase(3,5);
    }
    const string& content() const {return data;}
};

int main () {
  Example3 foo("Joni Gallo");
  Example3 bar (foo);
  bar.doit();
  cout << "content: " << foo.content() << '\n';
  return 0;
}

Joni Gallo,输出结果为:

// classes and default constructors
#include <iostream>
#include <string>
using namespace std;

class Example3 {
    string data;
  public:
        Example3 (const string& str) 
        {
            data = str;
            data.erase(3,5);
        }

    Example3() {}
    void doit()
    {
   // data.erase(3,5);
    }
    const string& content() const {return data;}
};

int main () {
  Example3 foo("Joni Gallo");
  Example3 bar (foo);
  bar.doit();
  cout << "content: " << foo.content() << '\n';
  return 0;
}

是:Jonlo

?为什么?为什么擦除会影响第二种情况下的原始对象,但不是第一种情况?

PS还有什么在构造函数中传递const string& str然后将其分配给成员变量意味着 - 这是否意味着每当我更改成员变量内容时,原始对象内容(其引用被传递给构造函数)也将是改变了吗? (就像指针一样)

4 个答案:

答案 0 :(得分:4)

首先请注意,即使您通过引用传递字符串进行构造,您也可以在没有引用的情况下将其声明为类的成员。所以每个类实例都有自己的字符串。为非引用变量分配引用时,将生成副本。如果您随后更改了非引用副本(此处为您的数据成员变量),则不会更改引用。

在第一个程序中,您可以在doit成员函数中编辑数据成员。在第二个中,您可以在构造函数中执行此操作。

第一个程序:

  • foo由&#34; Joni Gallo&#34;设为数据。
  • bar是用foo构造的。所以它也有&#34; Joni Gallo&#34;作为数据。
  • 现在你可以用酒吧做任何你想做的事。它不会改变foo的数据。
  • 结果仍然是&#34; Joni Gallo&#34;

第二个程序:

  • foo由&#34; Joni Gallo&#34;并且擦除操作从长度为5的位置3移除。因此foo的数据成员是&#34; Jonlo&#34;。
  • 同样,bar中没有任何内容会改变foo的数据成员。
  • 结果是&#34; Jonlo&#34;。

总之,您观察到的结果与foo的成员因参考而被修改无关。你可能错过了这个结构会影响foo和bar。

答案 1 :(得分:2)

在您的第一个示例中,您更改了bar实例,因此foo保持不变,内容就是您在构造函数中放置的内容。

Example3构造函数中的第二种情况下,您执行了foo中反映的一些更改。它们是不同的对象,所以修改一个不会影响另一个。

答案 2 :(得分:2)

区别在于data.erase的时间:在第二种情况下,它是在foo的构造函数中完成的,然后修改后的字符串被复制构造函数复制到bar 。即使您注释掉bar的声明以及对doIt的调用(无论如何都是无操作),也会打印相同的修改后的字符串。

然而,在第一种情况下,根本没有对foo的{​​{1}}进行删除,因此打印原始字符串。同样,data的声明和对bar的调用可以得到评论,因为doIt的{​​{1}}与foo的{​​{1}}断开连接。

答案 3 :(得分:1)

全局

string data;

是一个字符串。不是对字符串或指向字符串的指针的引用。将复制分配给数据的任何字符串。

data = string:复制。

data = string reference:复制。

data = string pointer:编译错误。

data = char array:复制。

data = char pointer:复制。

假设char数组和char指针正确终止。如果没有,则定义不明确的行为。

来源1:

Example3 foo("Joni Gallo");

让foo。 foo复制提供的字符串foo.data =&#34; Joni Gallo&#34;。

Example3 bar (foo);

做吧。 Bar复制foo,因此它获得了foo的源字符串副本的副本。 bar.data =&#34; Joni Gallo&#34;。

bar.doit();

删除部分栏的字符串副本。 bar.data =&#34; Jonlo&#34;。

cout << "bar's content: " << foo.content() << '\n';
是一个谎言。这打印出foo的字符串副本。

来源2:

Example3 foo("Joni Gallo");

让foo。 foo复制提供的字符串并删除部分副本。 foo.data =&#34; Jonlo&#34;。

Example3 bar (foo);

做吧。 Bar复制foo,因此它获得了foo编辑的源字符串副本的副本。 bar.data =&#34; Jonlo&#34;。

bar.doit();

什么都不做。 bar.data =&#34; Jonlo&#34;。

cout << "bar's content: " << foo.content() << '\n';

仍然是谎言,但这次foo的副本和Bar的副本将是相同的。