emoPuppy
emoPuppy

Reputation: 3

string is assigned in constructor but becomes blank thereafter C++

I encountered a problem which I am baffled with. This pertains to whether I choose to store an object, or a pointer to it.

This is my class:

class Test {
public:
    std::string abc;
    int x;

    Test(std::string def, int y) {
        abc = def;
        cout << abc << endl; //THIS ALWAYS GIVES "ghi"
        x = y;
    }
};

Storing it as a pointer:

    Test* T1 = &Test{ "ghi", 100 };
    cout << T1->abc << endl; //**THIS IS BLANK -- WHY!?**
    cout << T1->x << endl; //THIS GIVES 100

Storing it as an object itself:

    Test T2 = Test{ "ghi", 100 };
    cout << T2.abc << endl; //THIS GIVES "ghi"
    cout << T2.x << endl; //THIS GIVES 100

I'm relatively new to C++ but based on my understanding, T1->abc should dereference the pointer T1 (and therefore I have a Test object), and then access the member abc of that Test object. Since I already created the object, it should be "ghi". But this gives blank instead.

Apparently, this applies only to the string member and not the integer member, as T1->x works fine.

And the problem doesn't exist when I store the object instead of a pointer to it, as shown by the object T2.

I have checked that during construction of T1 and T2, abc is always "ghi" inside the constructor. Somehow, after construction, the string just disappears.

Also, based on what I read, abc = def copies the string content and not the pointer, so it probably isn't a scope issue.

https://msdn.microsoft.com/en-us/library/b930c881.aspx makes it very clear that in either case, whether -> or . is used, assignment can be made.

Searching Google and stackoverflow did not help. Hence yours appreciated. Thanks in advance.

class Test {
public:
    std::string abc;
    int x;

    Test(std::string def, int y) {
        abc = def;
        x = y;
    }
};

int main() {

    Test* T1 = &Test{ "ghi", 100 };
    cout << T1->abc << endl; //THIS IS BLANK -- WHY!?
    cout << T1->x << endl; //THIS GIVES 100

    Test T2 = Test{ "ghi", 100 };
    cout << T2.abc << endl; //THIS GIVES "ghi"
    cout << T2.x << endl; //THIS GIVES 100

    return 0;
}

Upvotes: 0

Views: 327

Answers (4)

Olivier Poulin
Olivier Poulin

Reputation: 1796

Test* T1 = &Test{ "ghi", 100 };

Please don't do this. This is exactly what's causing your problem. Instead use

Test* T1 = new Test("ghi", 100);

Because the first way created a reference to a temporary object, which disappears before you try to get the string for printing.

It works for the non-pointer because you're giving the temporary object to a new object.

don't remember to delete (because you've used new)

delete T1;

Or, as πάντα ῥεῖ pointed out, what would be safer, and better c++ coding practice, would be to use a smart pointer. The most relevant one that comes to mind is unique_ptr.

std::unique_ptr<Test> T1(new Test());
std::cout << T1->abc << std::endl;
...

Upvotes: 1

Vlad from Moscow
Vlad from Moscow

Reputation: 310980

This code snippet

Test* T1 = &Test{ "ghi", 100 };
cout << T1->abc << endl; //**THIS IS BLANK -- WHY!?**
cout << T1->x << endl; //THIS GIVES 100

has undefined behaviour because after statement

Test* T1 = &Test{ "ghi", 100 };

temporary object Test{ "ghi", 100 } will be deleted and the pointer becomes invalid.

A valid code could look the following way

Test* T1 = new Test{ "ghi", 100 };

cout << T1->abc << endl;
cout << T1->x << endl; 

delete T1;

Or instead of the pointer you could use a constant reference to the temporary object. For example

const Test &T1 = Test{ "ghi", 100 };

cout << T1.abc << endl;
cout << T1.x << endl; 

The temporary object will be alive while the reference will be alive.

Upvotes: 1

Mateusz Grzejek
Mateusz Grzejek

Reputation: 12058

This construct:

Test{ "ghi", 100 };

creates temporary variable, that you are trying to point to. This is just wrong.

If you want to store it using pointer:

Test* T1 = new Test{ "ghi", 100 };

cout << T1->abc << endl; //This is not blank anymore
cout << T1->x << endl;

delete T1; //Do NOT forget this!

By the way, if you are wondering, why this line:

cout << T1->x << endl;

was still evaluating to 100: when object is destroyed, all its members are also destroyed (in terms of calling their destructors). Since x is an int, its destructor in no-op and content of memory occupied by it does not change.

On the other hand, abc is and std::string, which frees allocated memory during destruction - so T1->abc was referring to an empty string.

Upvotes: 2

πάντα ῥεῖ
πάντα ῥεῖ

Reputation: 1

  Test* T1 = &Test{ "ghi", 100 };

The temporary rvalue of Test{ "ghi", 100 } immediately goes out of scope and ceases to exist after this statement.

Thus dereferencing it like

 cout << T1->abc << endl;

simply calls Undefned Behavior on a dangling pointer.

Upvotes: 1

Related Questions