class sample
{
std::string mString;
public:
void Set(const std::string &s)
{
mString = s;
}
//std::string Get() const
const std::string& Get() const
{
return mString;
}
};
Let's say I have such a class as above and now I use this class like this:
sample *p = new sample;
p->Set("abcdefg");
const std::string& ref = p->Get();
delete p;
p = nullptr;
std::cout << ref << std::endl;
As you see, the member function Get
returns a reference, which is assigned to the outer reference ref
. After that, the whole object is deleted.
However, it seems that this code works without any error.
I'm a little confused. It could run just because I'm lucky or it's the reference ref
that prolongs the lifetime of the member variable mString
?
CodePudding user response:
No, lifetime extension by reference binding applies only to temporary objects materialized from prvalues.
Objects created with new
never have their lifetime extended past delete
.
What you are seeing here is just a manifestation of undefined behavior. ref
is dangling after delete p;
and therefore reading from it in the cout
statement has undefined behavior.
If Get
was returning by-value (std::string Get() const
), then it would be true that the temporary materialized from the return value of the function would have its lifetime extended to that of the reference ref
.
And in that case the program behavior would be well-defined.
Similarly if the sample
object had automatic storage duration, the reference would not extent its lifetime or the lifetime of one of its subobjects either:
/* DO NOT USE: Undefined behavior */
const std::string& ref = []() -> const std::string& {
sample s;
p.Set("abcdefg");
return s->Get();
// lifetime of s ends with function returning
}();
/* UNDEFINED BEHAVIOR */
std::cout << ref << std::endl;
Here it is the lambda's return type that would need to be changed from const std::string&
to std::string
to avoid the UB. Only the first reference binding can extent lifetime and therefore changing the return type of Get
would not be sufficient in this case.