0

I'm creating objects at runtime with vector.push_back() and i want to store the player object so i can use it whenever i want. So i store the pointer of the just created player to a global pointer variable, but when i push back the next object, the memory adress of the player changes, i think because the vector has to resize and therefore changes all of its elements locations.

for (int y = 0; y < 5; y++)
{
    for (int x = 0; x < 10; x++)
    {
        switch (map[x + 10 * y])
        {
        case '1':
            gameObjects.push_back(player);
            playerPointer = &gameObjects.back();
            break;
        case '2':
            gameObjects.push_back(block);
            gameObjects.back().transform.pos = vec((float)x * 100, (float)y * 150, 0);
            break;
        }
    }
}

but when i use

gameObjects.reserve(10000);

the location doesnt change, because its reserved and doesnt need to resize until the size becomes 10000 in this case.

So whats the catch? can you just reserve(1000000000) with no consequences? my RAM usage doesnt skyrocket.

Thanks in advance

Felix
  • 31
  • 4
  • 1
    Sure, you can just reserve as much space as you want. If it fails to allocate the memory it will throw an exception. However, it seems like a really bad way of solving your problem. You could store a reference to the vector, together with an index instead of storing raw pointers to the objects in the vector. Or if your lazy you can just swap to `std::deque`. It never reallocates it's elements. – super Oct 14 '20 at 09:39
  • indices can be stable. Sometimes a `std::list` is an alternative (it has more stable iterators), but consider the drawbacks – 463035818_is_not_an_ai Oct 14 '20 at 09:41
  • 1
    "when i push back the next object, the memory adress of the player changes" I think you have an XY problem. The real question is, *why does it matter* if this address changes? Is it because you tried to store a direct pointer to the object somewhere else? Then maybe you should do something else - for example, just storing a numeric index that will be used to index into the vector; or using some kind of smart-pointer type; or storing the combination of an index with a pointer to the vector. Or the other way around: store smart pointers in the vector, and point one at the hard-coded `player`. – Karl Knechtel Oct 14 '20 at 09:56
  • 1
    @KarlKnechtel I agree to some extend, but isnt the X obviously `playerPointer = &gameObjects.back();` ? Though, yes the example could be more clear, because the actual problem is not in this code, but some other code that uses `playerPointer` with wrong assumptions – 463035818_is_not_an_ai Oct 14 '20 at 10:04
  • I find it hard to read and understand all the information in the questions sometimes :) – Karl Knechtel Oct 14 '20 at 10:05
  • @OP your question would be more clear if you mention where and why you use `playerPointer`, because the actual problem is not in the code you posted but elsewhere – 463035818_is_not_an_ai Oct 14 '20 at 10:07
  • @KarlKnechtel hope i didnt come across as "come on it is obvious", because thats not what I wanted to say – 463035818_is_not_an_ai Oct 14 '20 at 10:09

2 Answers2

4

The drawback of reserve( a lot ) is that you can either reserve far too much, which is a waste of memory, or too little, then reallocations will still happen.

Pointers to elements get invalidated when iterators get invalidated. For when iterators get invalidated I refer you to this question: Iterator invalidation rules.

Face it: std::vectors iterators are rather unstable (and thus also pointers to elements). You have several options:

  • Reserving enough space upfront can be a solution, though it isn't safe. Once you push more than you reserved, iterators are off.
  • When you are fine with reserving enough space for the maximum number of elements you can as well use a std::array.
  • Use indices. Assuming you do not reorder the vector and you never remove elements, indices are stable.
  • Use a std::list. Iterators of lists are much more stable than iterators into vectors. In particular, inserting to a list does not invalidate iterators. Though, consider the drawbacks of std::list compared to std::vector.
  • Store the elements elsewhere. Instead of storing the player in the vector you can store a (smart-) pointer to it in the vector. Reordering or reallocating the vector will then not affect pointers to the actual objects.
463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185
0

quickly after i posted this i realized that using pointers as an ID is stupid (especially with vectors) so i just gave the gameobject a const char* name and made a function which loops trough my gameobjects till it finds one with the name you seek and returns it. The i set a GameObject* to the gameobject* the function returns and from there i can use player->doStuff

Felix
  • 31
  • 4
  • keeping a handle to avoid searching a container for each access is not stupid! Also I want to mention that the context you refer to in this answer is exactly what was missing from your question. There is no way anybody but you could have come up with this answer based on your question only. Just saying... – 463035818_is_not_an_ai Oct 14 '20 at 14:20
  • yeah i know, my fault, i thought using pointers was the way to go until i realized a completely different approach – Felix Oct 14 '20 at 14:25