Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ vector reference

Tags:

c++

vector

I'm confused about how C++ manages objects in vector. Say I do the following:

vector<MyClass> myVector;
myVector.push_back(a);
myVector.push_back(b);
MyClass & c = myVector[1];
myVector.erase(myVector.begin());

Is the reference c still valid (or better yet, is it guaranteed to be valid)? If not, do I have to always make copy from the reference to ensure safety?

like image 827
user1404173 Avatar asked Oct 28 '12 19:10

user1404173


1 Answers

Unlike Java or C# references (which are more like C++ pointers than C++ references), references in C++ are as "dumb" as pointers, meaning that if you get the reference of an object, and then you move that object in memory, your reference is not anymore valid.

Is the reference c still valid (or better yet, is it guaranteed to be valid)?

In the case you're describing, the standard vector is not guaranteed to keep the objects it contains at the same place in memory when the vector contents changes (removal of an item, resizing of the vector, etc.).

This will invalidate both iterators and pointer/references to the object contained.

If not, do I have to always make copy from the reference to ensure safety?

There is multiple ways to continue to "point" to the right objects, all of them implying a level of indirection.

Full/value copy

The simplest is making a full copy of MyClass:

vector<MyClass> x ;
x.push_back(a) ;
x.push_back(b) ;
MyClass c = x[1] ;    // c is a full copy of b, not a reference to b
x.erase(x.begin()) ;

Using the right container

The second simplest is to use a std::list which is specifically designed for element insertion and removal, and will not change the contained objects, nor invalidate pointers, references or iterators to them:

list<MyClass> x ;
x.push_back(a) ;
x.push_back(b) ;
list<MyClass> it = x.begin() ;
++it ;
MyClass & c = *it ;
x.erase(x.begin()) ;

Using pointers (unsafe)

Another would be to make a std::vector<MyClass *>, which would contain pointers to MyClass instead of MyClass objects. You will then be able to keep a pointer or a reference to the pointed object, with a slightly different notation (because of the extra indirection):

vector<MyClass *> x;
x.push_back(a);     // a being a MyClass *
x.push_back(b);     // b being a MyClass *
MyClass * c = x[1]; // c points to the same object as b
x.erase(x.begin()); // note that a will still need separate deallocation

This is unsafe because there is no clear (as far as the compiler is concerned) owner of the objects a and b, meaning there is no clear piece of code responsible to deallocating them when they are not needed anymore (this is how memory leaks happen in C and C++)

So if you us this method, make sure the code is well encapsulated, and as small as possible to avoid maintenance surprises.

Using smart pointers (safer)

Something better would be using smart pointers. For example, using C++11's (or boost's) shared_ptr:

vector< shared_ptr<MyClass> > x;
x.push_back(a);               // a being a shared_ptr<MyClass>
x.push_back(b);               // b being a shared_ptr<MyClass>
shared_ptr<MyClass> c = x[1]; // c points to the same object as b
x.erase(x.begin());           // No deallocation problem

Now, if you use shared_ptr, and know nothing about weak_ptr, you have a problem, so you should close that gap.

Using smart pointers 2 (safer)

Another solution would be to use C++11's unique_ptr, which is the exclusive owner of the pointed object. So if you want to have a pointer or a reference to the pointer object, you will have to use raw pointers:

vector< unique_ptr<MyClass> > x;
x.push_back(a);               // a being a unique_ptr<MyClass>
x.push_back(b);               // b being a unique_ptr<MyClass>
MyClass * c = x[1].get();     // c points to the same object as b
x.erase(x.begin());           // No deallocation problem

Note here that the vector is the unique owner of the objects, unlike the case above with the smart_ptr.

Conclusion

You are coding in C++, meaning you have to choose the right method for your problem.

But first, you want to be sure to understand the level of indirection added by pointers, what pointers do, and what C++ references do (and why they aren't C#/Java references).

like image 79
paercebal Avatar answered Sep 30 '22 01:09

paercebal