Reputation: 3887
I store some objects in a vector. When I call a member function of such an object that uses a reference the program gets terminated (no error). I wrote the following code do run some tests. It seams like after adding elements, the reference in the first entry fails. Why is that and what can I do to avoid this issue? It's exactly the same behaviour when I use pointers instead of references.
#include <iostream>
#include <vector>
using namespace std;
class A{
public:
A(int i) : var(i), ref(var) {}
int get_var() {return var;}
int get_ref() {return ref;}
private:
int var;
int& ref;
};
int main ()
{
vector<A> v;
for(unsigned int i=0;i<=2 ;i++){
v.emplace_back(i+5);
cout<<"entry "<<i<<":"<<endl;
cout<<" var="<<v.at(i).get_var()<<endl;
cout<<" ref="<<v.at(i).get_ref()<<endl;
}
cout<<endl;
for(unsigned int i=0;i<=2 ;i++){
cout<<"entry "<<i<<":"<<endl;
cout<<" var="<<v.at(i).get_var()<<endl;
cout<<" ref="<<v.at(i).get_ref()<<endl;
}
return 0;
}
The output is:
entry 0:
var=5
ref=5
entry 1:
var=6
ref=6
entry 2:
var=7
ref=7
entry 0:
var=5
ref=0 /////////////here it happens!
entry 1:
var=6
ref=6
entry 2:
var=7
ref=7
v has 3 entries
Upvotes: 2
Views: 414
Reputation: 10064
Okay, so here is what's happening. It really helps to understand your objects in terms of memory location, and remember that vector is allowed to move objects around in memory.
v.emplace_back(5)
You create an A-object in the vector. This object now resides in a block of memory ranging from 0x1234
to 0x123C
. Member variable var sits at 0x1234
and member variable ref sits at 0x1238
. For this object, the value of var is 0x0005
and the value of ref is 0x1234
.
While adding elements to the vector, the vector runs out of space during the second insert. So, it resizes and moves the current elements (which at this moment is just the first element) from location 0x1234
to location 0x2000
. This means the member elements also moved, so var is now located at address 0x2000
and ref is now located at 0x2004
. But their values were copied, so the value of var is still 0x0005
and the value of ref is still 0x1234
.
ref is pointing at an invalid location (but var still contains the right value!). Trying to access the memory ref now points to undefined behavior and generally bad.
Something like this would be a much more typical approach to providing reference access to a member attribute:
int & get_ref() {return var;}
Having references as member attributes isn't wrong in and of itself, but if you are storing a reference to an object, you have to make sure that that object doesn't move.
Upvotes: 3
Reputation: 5470
It's because your calls to emplace_back are causing the vector to resize. In order to do this, the vector may or may not have to move the entire vector to a different place in memory. Your "ref" is still referencing the old memory location.
Whether or not this actually happens is somewhat implementation dependent; compilers are free to reserve extra memory for the vector so they don't have to reallocate every single time you add something to the back.
It's mentioned in the standard documentation for emplace_back:
Iterator validity
If a reallocation happens, all iterators, pointers and references related to this container are invalidated. Otherwise, only the end iterator is invalidated, and all other iterators, pointers and references to elements are guaranteed to keep referring to the same elements they were referring to before the call.
To avoid the problem you could either (as JAB suggested in the comments) create the reference on the fly instead of storing it as a member variable:
int& get_ref() {return var;}
... although I would much rather use a smart pointer instead of this sort of thing.
Or, as RnMss suggested, implement the copy constructor so that it references the new location whenever the object is copied by vector:
A(A const& other) : ref(var) {
*this = other;
}
Upvotes: 6