为什么我的字符串引用成员变量在C ++中设置为空字符串?

时间:2015-01-13 16:36:34

标签: c++ string reference pass-by-reference

请考虑以下代码:

class Foo
{
private:
    const string& _bar;

public:
    Foo(const string& bar)
        : _bar(bar) { }

    const string& GetBar() { return _bar; }
};

int main()
{
    Foo foo1("Hey");
    cout << foo1.GetBar() << endl;

    string barString = "You";
    Foo foo2(barString);
    cout << foo2.GetBar() << endl;
}

当我执行此代码时(在VS 2013中),foo1实例在其_bar成员变量中有一个空字符串,而foo2的相应成员变量包含对值的引用“您”。 为什么?

更新:我当然在本例中使用std :: string类。

4 个答案:

答案 0 :(得分:11)

对于Foo foo1("Hey"),编译器必须执行从const char[4]std::string的转换。它创建了std::string类型的prvalue。这一行相当于:

Foo foo1(std::string("Hey"));

从prvalue到bar发生引用绑定,然后从barFoo::_bar发生另一个引用绑定。这里的问题是std::string("Hey")是一个临时的,当它出现的完整表达式结束时会被销毁。也就是说,在分号后,std::string("Hey")将不存在。

这会导致悬空引用,因为您现在有Foo::_bar引用已被销毁的实例。当您打印字符串时,您会因使用悬空参考而产生未定义的行为

Foo foo2(barString)行很好,因为在barString初始化后foo2存在Foo::_bar,因此std::string仍然引用{{1}}的有效实例。未创建临时,因为初始化程序的类型与引用的类型匹配。

答案 1 :(得分:7)

您正在引用一个在foo1行末尾被销毁的对象。在foo2中,barString对象仍然存在,因此引用仍然有效。

答案 2 :(得分:2)

是的,这是C ++和理解的奇迹:

  1. 对象的生命周期
  2. 该字符串是一个类,文字字符数组不是“字符串”。
  3. 隐式构造函数会发生什么。
  4. 在任何情况下,string都是一个类,“Hey”实际上只是一个字符数组。因此,当您使用“Hey”构造Foo时需要引用字符串,它会执行所谓的隐式转换。发生这种情况是因为string具有来自字符数组的隐式构造函数。

    现在为对象问题的生命周期。为你构造了这个字符串,它存在于哪里以及它的生命周期。实际上对于该调用的值,这里是Foo的构造函数,以及它调用的任何东西。所以它可以调用所有类型的函数,并且该字符串是有效的。

    但是,一旦调用结束,对象就会过期。不幸的是,你已经在你的类中存储了一个const引用,你被允许。编译器不会抱怨,因为你可以将const引用存储到一个寿命更长的对象。

    不幸的是,这是一个令人讨厌的陷阱。我记得曾经故意给我的构造函数,它真的想要一个const引用,一个非const的引用,以确保没有发生这种情况(也不会得到临时的)。可能不是最好的解决方法,但它当时有用。

    你最好的选择大部分时间只是复制字符串。它比你想象的要便宜,除非你真的处理了很多这些。在你的情况下,它可能实际上不会复制任何东西,编译器会秘密移动它所做的副本。

    您还可以对字符串进行非const引用,并在

    中“交换”它

    使用C ++ 11还有一个使用移动语义的选项,这意味着传入的字符串将变为“获取”,本身无效。当你想要接收临时数据时,这是特别有用的,你可以将其作为一个例子(尽管大多数临时数据是通过显式构造函数或返回值构造的)。

答案 3 :(得分:2)

问题在于此代码:

Foo foo1("Hey");

从字符串文字"Hey"(原始char数组,更准确地说const char [4],考虑中的三个字符和终止\0 )创建一个临时 std::string实例,并将其传递给Foo(const string&)构造函数。

此构造函数将引用保存到此临时字符串中const string& _bar数据成员:

Foo(const string& bar)
        : _bar(bar) { }

现在,问题是您要将引用保存到临时字符串。因此,当临时字符串&#34;蒸发&#34; (在构造函数调用语句之后)时,引用变为悬空,即它引用(&#34 ;指向...&#34; )一些垃圾 因此,您会遇到未定义的行为(例如,使用带有g ++的Windows上的MinGW编译代码,我会得到不同的结果)。

相反,在第二种情况下:

string barString = "You";
Foo foo2(barString);

您的foo2::_bar引用与(&#34;指向&#34; barString相关联,{em>不是临时,但是main()中的局部变量。因此,在构造函数调用之后,当您使用barString打印字符串时,cout << foo2.GetBar()仍然存在。

当然,要解决这个问题,您应该考虑使用std::string数据成员,而不是参考 通过这种方式,字符串将被深度复制到数据成员中,即使构造函数中使用的输入源字符串是临时的,它也会持久存在(并且&#34;蒸发&构造函数调用后#34;