As with any tool, if it isn't used the way it's supposed to, errors can happen. A vector is interesting if you think of it as a smart array where you don't have to worry about memory management. You can access any of the positions using the [] operator just like an array and you simply add items to the vector using push_back(). You can get the size using the size() member, etc.... It's a very useful tool!
But as a user we need to be aware of some fine print.
Keeping a pointer to a vector position
We have an object Dummy and we declare a vector to old a collection of Dummy objects. We would do something like this:
vector <Dummy> dummy_collection;
We then proceed to add elements to dummy_collection and in the end we have a fine collection of objects that we will use somehow.
We are used to using arrays and pointers, so we get a pointer to the Dummy object at index 666 doing something like this:
Dummy *pDummy = &dummy_collection[666];
We now have a nifty pointer to the object we are using, we do no copies and our changes are done right on the spot! Perfect right?
Right! Unless... we add items to the vector!
One of the ideas with vectors is to make traversing them as fast as possible. One of the ways to achieve that is to keep all of the data in contiguous memory positions. Because of that a vector reserves an amount of space when it is allocated and when we go beyond the reserved space, the vector will reserve more space so we can keep adding stuff to it.
If there isn't enough contiguous memory to grow, the entire vector will be copied to a new location where there is enough space and... pDummy is pointing to... nothing (nothing recognizable anyway)!
Oops... pDummy is lost and we have no idea until we try to access it.
Getting around this!
There are a couple of solutions for getting around this- Use std::deque instead of std::vector - we can still use the same interface (if we only use push and pop) and the code you already have will be "swappable;
- Use std::list, but forget the [] operator and use iterators instead;
- reserve "enough" space so that reallocation wont occur - dummy_collection.reserve(SOME_BIG_ENOUGH_NUMBER);
- keep a reference (some ID of what is in the vector) and after a push_back update the pointer;
- create you own smart pointer class that encapsulates the vector and the index;
- store indexes instead of pointers
In any situation, we should be careful. There are always caveats...
No comments:
Post a Comment