Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does std::vector change its address? How to avoid

Tags:

c++

stl

vector

Since vector elements are stored contiguously, I guess it may not have the same address after some push_back's , because the initial allocated space could not suffice.

I'm working on a code where I need a reference to an element in a vector, like:

int main(){
    vector<int> v;
    v.push_back(1);
    int *ptr = &v[0];
    for(int i=2; i<100; i++)
        v.push_back(i);
    cout << *ptr << endl; //?
    return 0;
}

But it's not necessarily true that ptr contains a reference to v[0], right? How would be a good way to guarantee it?

My first idea would be to use a vector of pointers and dynamic allocation. I'm wondering if there's an easier way to do that?

PS.: Actually I'm using a vector of a class instead of int, but I think the issues are the same.

like image 214
kunigami Avatar asked Mar 15 '10 13:03

kunigami


3 Answers

Don't use reserve to postpone this dangling pointer bug - as someone who got this same problem, shrugged, reserved 1000, then a few months later spent ages trying to figure out some weird memory bug (the vector capacity exceeded 1000), I can tell you this is not a robust solution.

You want to avoid taking the address of elements in a vector if at all possible precisely because of the unpredictable nature of reallocations. If you have to, use iterators instead of raw addresses, since checked STL implementations will tell you when they have become invalid, instead of randomly crashing.

The best solution is to change your container:

  • You could use std::list - it does not invalidate existing iterators when adding elements, and only the iterator to an erased element is invalidated when erasing
  • If you're using C++0x, std::vector<std::unique_ptr<T>> is an interesting solution
  • Alternatively, using pointers and new/delete isn't too bad - just don't forget to delete pointers before erasing them. It's not hard to get right this way, but you have to be pretty careful to not cause a memory leak by forgetting a delete. (Mark Ransom also points out: this is not exception safe, the entire vector contents is leaked if an exception causes the vector to be destroyed.)
  • Note that boost's ptr_vector cannot be used safely with some of the STL algorithms, which may be a problem for you.
like image 184
AshleysBrain Avatar answered Sep 25 '22 15:09

AshleysBrain


You can increase the capacity of the underlying array used by the vector by calling its reserve member function:

v.reserve(100);

So long as you do not put more than 100 elements into the vector, ptr will point to the first element.

like image 42
James McNellis Avatar answered Sep 24 '22 15:09

James McNellis


How would be a good way to guarantee it?

std::vector<T> is guaranteed to be continous, but the implementation is free to reallocate or free storage on operations altering the vector contents (vector iterators, pointers or references to elements become undefined as well).

You can achieve your desired result, however, by calling reserve. IIRC, the standard guarantees that no reallocations are done until the size of the vector is larger than its reserved capacity.

Generally, I'd be careful with it (you can quickly get trapped…). Better don't rely on std::vector<>::reserve and iterator persistence unless you really have to.

like image 22
Alexander Gessler Avatar answered Sep 21 '22 15:09

Alexander Gessler