C++: Class member reference validity? - c++

Class A
{
A(int& foo) : m_foo(foo) {}
int& m_foo;
};
int main(void)
{
A* bar = 0;
{
int var = 5;
bar = new A(var);
}
std::cout << "Is m_foo still valid?:" << bar.m_foo << std::endl;
}
"m_foo" is a reference and "var" is a local variable which is given to the constructor. "var" gets out of the scope before printing the value so does it make m_foo also invalid?
If m_foo is a pointer, then it would be invalid but does it work the same way with references?

m_foo is not valid when int var falls out of scope. The thing to which it refers has gone away.

Yes, a reference member becomes invalid if referenced object gets de-allocated. Same as with pointers. If you intend to keep references, make sure the lifetimes nest. Or use something like boost::weak_ptr.

Yes. The behaviour is undefined.

Related

passing value to reference works one way [duplicate]

struct A {
A(int) : i(new int(783)) {
std::cout << "a ctor" << std::endl;
}
A(const A& other) : i(new int(*(other.i))) {
std::cout << "a copy ctor" << std::endl;
}
~A() {
std::cout << "a dtor" << std::endl;
delete i;
}
void get() {
std::cout << *i << std::endl;
}
private:
int* i;
};
const A& foo() {
return A(32);
}
const A& foo_2() {
return 6;
}
int main()
{
A a = foo();
a.get();
}
I know, returning references to local values is bad. But, on the other hand, const reference should extend a temporary object lifetime.
This code produce an UB output. So no life extention.
Why? I mean can someone explain whats happening step by step?
Where is fault in my reasoning chain?
foo():
A(32) - ctor
return A(32) - a const reference to local object is created and is returned
A a = foo(); - a is initialized by foo() returned value, returned value goes out of scope(out of expression) and is destroyed, but a is already initialized;
(But actually destructor is called before copy constructor)
foo_2():
return 6 - temp object of type A is created implicitly,a const reference to this object is created(extending its life) and is returned
A a = foo(); - a is initialized by foo() returned value, returned value goes out of scope(out of expression) and is destroyed, but a is already initialized;
(But actually destructor is called before copy constructor)
Rules of temporary lifetime extension for each specific context are explicitly spelled out in the language specification. And it says that
12.2 Temporary objects
5 The second context is when a reference is bound to a temporary. [...] A temporary bound to the returned value in a function return statement
(6.6.3) persists until the function exits. [...]
Your temporary object is destroyed at the moment of function exit. That happens before the initialization of the recipient object begins.
You seem to assume that your temporary should somehow live longer than that. Apparently you are trying to apply the rule that says that the temporary should survive until the end of the full expression. But that rule does not apply to temporaries created inside functions. Such temporaries' lifetimes are governed by their own, dedicated rules.
Both your foo and your foo_2 produce undefined behavior, if someone attempts to use the returned reference.
You are misinterpeting "until function exit". If you really want to use a const reference to extend the life of an object beyond foo, use
A foo() {
return A(32);
}
int main() {
const A& a = foo();
}
You must return from foo by value, and then use a const reference to reference the return value, if you wish to extend things in the way you expect.
As #AndreyT has said, the object is destroyed in the function that has the const &. You want your object to survive beyond foo, and hence you should not have const &
(or &) anywhere in foo or in the return type of foo. The first mention of const & should be in main, as that is the function that should keep the object alive.
You might think this return-by-value code is slow as there appear to be copies of A made in the return, but this is incorrect. In most cases, the compiler can construct A only once, in its final location (i.e. on the stack of the calling function), and then set up the relevant reference.

c++ What happens when alias to unique_ptr goes out of scope?

If I have a unique pointer and I create an alias for it in a function, and that alias goes out of scope, why doesn't the original unique_ptr also get destroyed? After all, 'b' as defined in the function below is basically the same object in memory as 'x'. What is going on behind the scenes?
#include <iostream>
#include <memory>
void testfunc(std::unique_ptr<int>& x) {
std::unique_ptr<int>& b = x;
}
int main() {
std::unique_ptr<int> a(new int(5));
std::cout << *a << std::endl; // 5
testfunc(a);
std::cout << *a << std::endl; // 5
}
What you're using is a reference, and a reference in C++ is a distinct type from what it is referencing. You can interact with an object through a reference, but the reference itself and the object being referred to have separate lifetimes. When one is destroyed, the other doesn't automatically get destroyed. This means you can pass a reference into a function and then at the end of a function when the reference is destroyed the original object is still valid. This allows passing around large complex objects without needing to copy or even moving them. It's a implementation detail, but it's common for compilers to simply use a pointer "behind the scenes" as references.
As a side note, this aspect of references in C++ leads to the infamous dangling reference issue. If you hold a reference to some object and that object is destroyed the reference you have is now technically invalid, and you'll invoke undefined behavior if you use it. Unfortunately there is nothing built into the language to automatically detect or deal with this situation. You must architect your program to avoid it.
A reference is can be considered like an alias to an element, hence it references another variable by taking up its value and working just like it does, but it doesn't get destroyed until called by the destructor or forcibly destroyed by the programmer which will also destroy the variable it references... since a reference is just an editable alias... However their lifespan differs since a non-reference type can be moved and it becomes out of scope...
"What is going on behind the scenes?"
Inside the memory, the reference allows us to change the value of an element and if often used instead of pointers which were a common practice in C... But, its value cannot be moved unless passed... A reference's value won't change unless changed using an assignment operation directly or indirectly i.e, from the function parameter x which itself is an alias...
Like: x = std::make_unique<int>(6); will change the value of a to 6 instead... But what you have done here instead is...
auto& b = x;
Nothing actually happens except the value that x(references to a) is referencing to is copied and passed to b (which just acts like another alias)... So it is similar to doing: auto& b = a;, but since a is outside the scope, it references a's value indirectly...
#include <iostream>
#include <memory>
void testfunc(std::unique_ptr<int>& x)
{
auto& b(x); // 'b' is an alias of 'x' and 'x' is an alias of 'a'
b = std::make_unique<int>(6); // Setting 'b' to 6 meaning setting 'a' to 6...
/* Now you can't do 'x = b' since you cannot assign a value to an alias and it is
like a 'circular assignment operation'...*/
}
int main()
{
std::unique_ptr<int> a(new int(5));
std::cout << *a << std::endl; // 5 : Nothing happens, just initialization...
testfunc(a); // It does not affect the reference...
std::cout << *a << std::endl; /* 6 : Since reference is an 'alias', you
changed 'a' as well...*/
} // It is freed after use by the destructor...
So, a general advice from people would be that you should avoid references if you are unsure of what it does (It can change the real variable if you are unknown of its consequences)... and take some time to learn about them...
If you destroy the original however..., all the references themselves will become invalidated... In such a case, when trying to access the value of destroyed (nullified) object is undefined causing undefined behavior...
#include <iostream>
#include <memory>
void testfunc(std::unique_ptr<int>& x) { // you take a reference to a unique_ptr
std::unique_ptr<int>& b = x; // which will do nothing to the lifetime of
} // the unique_ptr you pass to the function,
// then you assign the passed parameter
// to another reference. again, that does
// nothing to the lifetime of the original.
int main() {
std::unique_ptr<int> a(new int(5));
std::cout << *a << std::endl; // 5
testfunc(a);
std::cout << *a << std::endl; // 5
}
After all, 'b' as defined in the function below is basically the same object in memory as 'x'.
Not at all. x is a reference. A reference is not an object, and no constructor or destructor is called for it. There are no "aliases" for variables. There are for types, also known as typedefs.
Consider the same code with pointers instead:
void testfunc(std::unique_ptr<int>* x) {
std::unique_ptr<int>* b = x;
}
int main() {
std::unique_ptr<int> a(new int(5));
std::cout << *a << std::endl; // 5
testfunc(&a);
std::cout << *a << std::endl; // 5
}
The only time a reference can affect the lifetime of an object is when a reference binds to a temporary, but even then, it extends the lifetime rather than reducing it:
struct A {};
int main() {
{
A(); // Constructed and destructed
}
{
A const& a = A(); // Constructed
// Other instructions
} // Destructed
}
Demo

Is there a good way to catch or avoid this subtle bug?

My c++ is rusty, so while trying to improve some code I wrote a few days ago by changing some calls from passing a MyClass *const thing to Myclass& thing, I noticed that nothing complained about code that followed this contrived example.
#include <iostream>
class Foo {
public:
Foo() {
std::cout << "foo created" << std::endl;
}
~Foo() {
std::cout << "foo destroyed" << std::endl;
}
Foo(Foo& other) {
member = other.member;
std::cout << "foo copied" << std::endl;
}
bool member = false;
};
class Bar {
public:
Bar(Foo& foo) :foo_(foo) { }
Foo foo_; // **** HERE IS THE BUG this should be: Foo& foo_;
};
int main() {
Foo foo;
Bar barOne(foo);
Bar barTwo(foo);
foo.member = true;
std::cout << barOne.foo_.member << std::endl;
std::cout << barTwo.foo_.member << std::endl;
}
I really wanted to have one Foo object, but since I forgot the & I got three instead.
foo created
foo copied
foo copied
0
0
foo destroyed
foo destroyed
foo destroyed
adding the & I get the right result.
foo created
1
1
foo destroyed
Note: the Foo, constructors and destructor are just there to demonstrate what's happening.
I know is legal, but is there a compiler flag that would warn you if you declare an Object as a member variable? Is it a bad practice to store a reference in a member variable? I would not think it is, but like I said my c++ is rusty to say the least.
Update
To answer the question of what I was refactoring from. I was doing something similar to this. I was refactoring to references as everything I read about modern c++ says to prefer references rather than pointers.
class Bar {
public:
Bar(Foo const* foo) :foo_(foo) { }
Foo const* foo_;
};
int main() {
Foo foo;
Bar barOne(&foo);
Bar barTwo(&foo);
foo.member = true;
std::cout << barOne.foo_->member << std::endl;
std::cout << barTwo.foo_->member << std::endl;
}
I know is legal, but is there a compiler flag that would warn you if you declare an Object as a member variable?
I doubt there is such a flag. Objects of one type are stored as member variables of other types too many times and too many places for that flag to be useful.
Is it a bad practice to store a reference in a member variable?
No, it is not. However, you have to be aware of where you run into problems.
As long the life of the object that holds the reference ends before the life of the object to which it holds the reference ends, you will be fine. Otherwise, you end up holding on to a dangling reference. Using a dangling reference is cause for undefined behavior.
Storing object as a member of other object is prefectly fine.
Storing reference as a member is OK if you are sure that the object holding the reference is never going to outlive the referenced variable.

Reference to reassigned variable

Say I have a this code:
class A{
...
};
//somewhere else
A a;
A & ref = a;
a = A();
Does ref still reference the a? Regardless of how many assignments happen? That is, the assignment operator would never cause a memory location change, right?
Yes it does so. Actually after the last line ref will point to whatever a points to.
Whatever you do to the reference is also done to the original object.
So all you do is defining an alternative name for the same variable.
There are few things you need to remember
i) references are different from pointers
ii) pointer may be undefined /null but reference should always be associated with a variable.
iii) pointer may be able to point to different variable at different time; reference always associated with the same variable throughout it's life.
Check this question
Does ref still reference the a?
Yes . Look at the code below. It shows how the assignment does not change the memory location...
A a;
std::cout<<"a:"<< &a<<std::endl;
A & ref = a;
std::cout<<"ref:"<< &ref<<std::endl;
a = A();
std::cout<<"a:"<< &a<<std::endl;
The output looks like so:
a:0x7fffaaa5fcaf
ref:0x7fffaaa5fcaf
a:0x7fffaaa5fcaf
Does ref still reference the a?
Yes.
A reference variable is an alias for another variable.
ref is bound to a, to it's original variable memory. Assigning something to a will also affect ref since it refers to the memory of a. Below is a small example:
class A {
public:
A(int x, int y) : m_x(x), m_y(y) {}
A() = default;
int m_x;
int m_y;
};
int main()
{
A a;
A &ref = a;
a = A(500, 500);
cout << ref.m_x << " " << ref.m_y << endl;
}
Output is 500 500

C++ const lvalue references

Assuming I have:
class A which is non-copyable
class B which has as a member, const A& a (and takes an A in its constructer and sets it in its initialization list)
a function A GenerateA();
Does this mean that it should be valid to do:
B(GenerateA())
?
i.e, does the const ref mean that no copy of the A that generateA() returns is done? And does that mean that the scope of the returned temporary is extended for as long as B exists?
EDIT: Addon question from the comments:
Is it acceptable to return a A& from GenerateA() to a local A, if the lvalue is a const A&?
Thanks!
If A is non-copyable, then the function A GenerateA() is invalid since returning by value requires creating a copy.
If the function returns a reference instead (i.e. A &GenerateA()) and the reference is to a locally created A object, it becomes invalid as soon as the function exits. C++ doesn't have any form of garbage collection, so there is no way to "extend" the lifetime of an object as long as it is in use.
As it has already been stated by others, A GenerateA() cannot compile if A is not copyable.
Regarding the const ref : no, the lifetime of the temporary will not be extended to the lifetime of B. The standard [12.2.5] states :
A temporary bound to a reference member in a constructor's ctor-initializer (12.6.2) persists until the constructor exits. [...] A temporary bound to the returned value in a function return statement (6.6.3) persists until the function exits.
So yes, extension of the lifetime of a temporary exists in some contexts (and is sometime truly useful : see this article), but not in the one you presented.
Regarding your last question, it's not legal to return a reference to a local variable from GenerateA() (and binding the result to a const reference won't be of any help).
Yes and No.
Yes, the const reference will bind to the temporary variable. No, const references which are class members do not extend lifetime the way const references with automatic duration do.
Here's an example:
#include <iostream>
using namespace std;
int& GenX(bool reset)
{
static int* x = new int;
*x = 100;
if (reset)
{
delete x;
x = new int;
*x = 200;
}
return *x;
}
class YStore
{
public:
YStore(int& x);
int& getX() { return my_x; }
private:
int& my_x;
};
YStore::YStore(int& x)
: my_x(x)
{
}
int main()
{
YStore Y(GenX(false));
cout << "X: " << Y.getX() << endl;
GenX(true); // side-effect in Y
cout << "X: " << Y.getX() << endl;
return 0;
}
Output:
X: 100
X: 200