Reputation: 4882
I understand that if a std::vector is resized (I believe only increased in size) that the memory location of the vector is re-located in an effort to find a new location in heap memory that will actually fit the new size. In that event, if I have pointer A, B, and C that previously pointed to elements inside the vector, they will be pointing to the old, de-allocated memory locations and no longer be valid.
I wanted to know if A: If it was possible to be notified when such an event happens, with out me explicitly managing when a std::vector is resized, and B: how to deal with the pointers no longer referencing the correct location in memory.
Part B is kind of vague, so I will narrow the use case where I want to use this behavior. In my situation, I have a class that maintains a vector of objects that have several pointers inside themselves, like so:
class MultiPointer{
public:
Type1* t1;
Type2* t2;
Type3* t3;
MultiPointer(Type1*t1, Type2*t2, Type3*t3);
};
...
// attempt to create singleton pattern to make pool of each object type for each class
class Type1{
...
static vector<Type1> arr1 = ...
}
class Type2{
...
static vector<Type2> arr2 = ...
}
class Type3{
...
static vector<Type3> arr3 = ...
}
...
//note, would actually use CRTP to make sure I don't have to type out static vector<TypeN> each time
MultiPointer a(&arr1[0], arr2[0], arr3[0]);
MultiPointer b(&arr1[1], arr2[1], arr3[1]);
MultiPointer c(&arr1[2], arr2[2], arr3[2]);
std::vector<MultiPointer> mpv= {a,b,c};
... insertions into arr1,2,3 ...
//How do I make sure that a, b, and c still point to the same types?
Note that in my situation, I have access to arr1->3 at all times that are relevant, as its a static variable and I know the types I'm working with.
My idea was to copy the value of the pointer for arr1 into a seperate variable, ie
Type1* arr1ptrcpy = &(Type1::arr1[0]);
Then I would check if the size changed when needed, and if the size changed I would check if the pointers were the same (or just check if the pointers are the same)
//ie in main
if(arr1ptrcpy != &(Type1::arr1[0])){
// for each multi pointer in mpv, adjust pointers for arr1.
...
}
If I noticed the addresses changed, I would do pointer arhimetic to find the correct placement of the old pointers relative to the new address.
// in MultiPointer
...
// if pointers don't match for arr1
t1 = (arr1ptrcpy - t1) + Type1::arr1[0];
...
// if pointers don't match for arr2
t2 = (arr2ptrcpy - t2) + Type2::arr2[0];
...
// if pointers don't match for arr3
t2 = (arr3ptrcpy - t3) + Type3::arr3[0];
While I could do this all with handles, that requires runtime overhead for pointer de-referencing where I'll have a lot of pointers. In this situation, I'll be checking for vector changes much less often than de-referencing pointers. If this could be done with iterators with out additional memory overhead per iterator I would be willing to see how that could be done as well.
EDIT: I want to give more information on what my actual application is trying to accomplish/ how it is structured. In the actual usecase, there are many types of MultiPointer, some which reference the same object, but have otherwise different pointer types (hence the need to have pointers in the first place) and the objects they reference are used elsewhere outside of the context of a multipointer as well.
In my actual application, 'MultiPointer' acts as a grouping of the objects with different functionality attached to these groupings. For example, you might have, say, a PhysicsMultiPointer, which has a Position* and Velocity*, and a GraphicMultiPointer which has a DisplayImage* and a Position*. They both will point to the same Position object here.
SECOND EDIT:
I should mention I need all elements in the type vectors to be in contiguous memory, if that wasn't the case I wouldn't even bother with this whole ordeal since I would just have pointers to heap objects who wouldn't change location.
Upvotes: 1
Views: 2756
Reputation: 473946
A: If it was possible to be notified when such an event happens, with out me explicitly managing when a std::vector is resized,
No.
You can easily tell when it is going to happen, but you cannot tell after the fact when it has happened. vector
reallocation only happens when you do something that increases the size of the vector
past its current capacity
(or you call shrink_to_fit
). So if you insert 4 items into the vector
, it will not reallocate itself if the capacity - size is 4 or more.
Now, you can use this fact to build wrapper functions/objects for the insertion functions, which will check the capacity before and after the insertion and return whether it has changed (if reallocation occurs, the capacity has to be increased).
However, a far better way to deal with this is to simply... not let it happen. Use the reserve
function to make the capacity sufficiently large that you simply will have no need for reallocation. By doing that, you won't have to care about resetting pointers and the like.
Note that the above only covers pointer invalidation due to reallocation. Pointers and references are invalidated if you insert objects into the middle of the vector
, but only those pointers/references to the element at the insertion point and all subsequent elements.
B: how to deal with the pointers no longer referencing the correct location in memory.
Don't use pointers. Instead, use indices. Unless you're inserting into the middle of the vector
; indices can't help there.
If indices can't work for you, then I would strong reconsider using vector
at all. If you need stability of elements that badly, then list
or forward_list
seems like a far more appropriate tool for the job. Or, if you have access to Boost, boost::stable_vector
.
Upvotes: 2
Reputation: 249394
To be notified when the internal pointer in the vector is changed, you can wrap std::vector
. You'll need to hook the functions push_back()
, emplace_back()
, resize()
, shrink_to_fit()
(just disable that one), reserve()
, and insert()
. It's easy enough to just check data()
before and after, and invoke a callback if it changed.
Upvotes: 1