C++:传递字符串字面值而不是const std :: string&?

11

我有以下这段代码,使用g++编译时没有警告(-Wall -pedantic)。

#include <iostream>
#include <string>

using namespace std;

class Foo
{
public:
    Foo(const std::string& s) : str(s)
    { }

    void print()
    {
        cout << str << endl;
    }

private:
    const std::string& str;
};


class Bar
{
public:

    void stuff()
    {
        Foo o("werd");
        o.print();
    }
};


int main(int argc, char **argv)
{
    Bar b;
    b.stuff();

    return 0;
}

但是当我运行它时,只有换行符被打印出来。发生了什么事?

如果我在里面做这件事:

string temp("snoop");
Foo f(temp);
f.print();

然后它就能正常工作了!

3个回答

23
这种方法失败的原因在于它在底层实际上编译成了以下内容。
Foo o(std::string("wurd"));
在这种情况下,Foo 值引用了一个临时对象,在构造函数完成后被删除。因此,它持有一个无效的值。第二个版本起作用是因为它持有对具有比 Foo 实例寿命更长的局部变量的引用。
要解决这个问题,将成员从 const std::string& 更改为 const std::string

没有必要将一个常量值作为参数传递,直接使用 std::string 即可。 - Miles Rout
3
这并非完全正确。你可能知道你不想修改字符串,通过声明它为const,你允许编译器验证你不会修改它。 - Erik
@MilesRout 这就像说没有必要声明任何本地变量(参数也是如此)为const。这是荒谬的! const正确性确保我们不会意外地输错或更新某些参数或计算初始化值等,如果不注意可能会严重破坏某些代码。为什么要冒险呢?为了添加一个限定符,编译器可以让我们更加安全。你可能会认为没有必要将任何变量声明为const,但我认为这是普遍被认为是荒谬的。 - underscore_d
@underscore_d 通常情况下,将任何变量声明为“const”没有太大意义。 - Miles Rout
在理论上,如果你是一个百分之百没有错误、从不打错字或忘记变量状态的编码者(在这种情况下恭喜你),并且你的编译器不使用const进行任何优化(大多数可能不会),那么它并没有实现任何具体的东西。对于我们这些凡人来说,它可能非常有用。但我会让这个辩论在其他已经进行并且与主题相关的线程中继续。 - underscore_d

2
发生的情况是,引用“str”被初始化,以便它指向临时参数“s”。这与使用指针几乎相同-您依赖于构造函数参数“s”的持续存在。当临时对象被删除(在构造函数返回后),您的引用现在指向垃圾。
要修复问题,请更改“str”,使其成为实际的字符串对象而不是引用。
const std::string str;
这样,将会复制您的参数字符串,并且该副本的生命周期与Foo对象相同。

0
延伸之前给出的答案:如果你想避免数据的复制,你可以将Foo的成员和构造函数参数更改为const char*
class Foo
{
public:
    Foo(const char* s) : str(s)
    { }

    void print()
    {
        cout << str << endl;
    }

private:
    const char* str;
};


class Bar
{
public:

    void stuff()
    {
        Foo o("werd");
        o.print();
    }
};


int main(int argc, char **argv)
{
    Bar b;
    b.stuff();

    return 0;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接