11

I have the following code which compiles with no warnings (-Wall -pedantic) with g++

#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;
}

But when I run it, only the newline is printed out. What is going on?

If I were to do this inside stuff:

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

then it works fine!

3 Answers 3

23

The reason why this fails is because it essentially compiles to the following under the hood.

Foo o(std::string("wurd"));

In this case the Foo value is taking a reference to a temporary object which is deleted after the constructor completes. Hence it's holding onto a dead value. The second version works because it's holding a reference to a local which has a greater lifetime than the Foo instance.

To fix this change the memebr from being a const std::string& to a const std::string.

Sign up to request clarification or add additional context in comments.

5 Comments

There's no point taking a const value as a parameter. Just use std::string.
@MilesRout That's not entirely true. You might know you don't want to modify the string, and by declaring it const you allow the compiler to verify that you don't.
@MilesRout That's like saying there's no point declaring any local variable (which a parameter is) to be const. Which is nonsense! const-correctness ensures we don't accidentally mistype or etc and update some parameter or calculation-initialised value or whatever, which could seriously wreck some pieces of code if not noticed. Why take the risk when, for the cost of 1 added qualifier, the compiler can keep us safe? You might as well argue that there's no point ever declaring any variable const, which I like to think would be universally deemed absurd.
@underscore_d Generally there's not much point declaring any variables as const.
@MilesRout In theory, no, it doesn't achieve anything concrete, if you're a 100% error-free coder who never mistypes or forgets the status of a variable (in which case congrats) and your compiler doesn't use const for any optimisations (which most probably don't). For the rest of us mere mortals, it can be extremely useful. But I'll let this debate carry on in other threads where it's already being held and is on-topic.
2

Whats happening is that the reference 'str' is being initialized so that it points to the temporary arg, 's'. Its pretty much the same as using a pointer - you're counting on the continued existence of your constructor arg, 's'. When the temporary is deleted (after the constructor ftn returns), then your reference now points at garbage.

To fix, change str so that its an actual string object and not a reference.

const std::string str;

That way a copy will be made of your arg string, and said copy will have the same lifespan as your Foo object.

Comments

0

Extending on the answers given before: If you want to avoid the copy of the data, you can change the member and constructor parameter of Foo to 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;
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.