4
\$\begingroup\$

So I have a vector of bullets that show up on the screen and I do not want these bullets to live forever. I want them to "die as soon as they go off screen or collide with an enemy. How can I make sure they are removed from the array and no longer take up memory.

code:

std::vector<Bullet> bulletArray;

for(int i=0; i<bulletArray.size(); i++)
{
    bulletArray[i].MoveBullet();
    bulletArray[i].Draw();
    if(bulletArray[i].PosY<0)
    {
        //Delete object forever 
    }
}
\$\endgroup\$
5
  • 2
    \$\begingroup\$ Answers which will work in this situation are here: stackoverflow.com/questions/875103/… \$\endgroup\$ Jan 1, 2013 at 19:54
  • \$\begingroup\$ bulletArray.erase(enemyArray.begin() + 1); This i have tried and i saw this post but i get this error : Error 2 error C2664: 'std::_Vector_iterator<_Myvec> std::vector<_Ty>::erase(std::_Vector_const_iterator<_Myvec>)' : cannot convert parameter 1 from 'std::_Vector_iterator<_Myvec>' to 'std::_Vector_const_iterator<_Myvec>' \$\endgroup\$
    – joncodo
    Jan 1, 2013 at 19:59
  • 3
    \$\begingroup\$ You cannot erase an element of enemyArray from bulletArray. You can only erase elements of bulletArray from bulletArray, because enemyArray is not bulletArray. You can only use iterators from a particular vector to erase elements from that same vector, not any other vector. \$\endgroup\$ Jan 1, 2013 at 20:04
  • \$\begingroup\$ @JonathanO In future please post purely C++-related questions like this on stackoverflow.com, not here. Thanks. \$\endgroup\$
    – Engineer
    Jan 2, 2013 at 13:48
  • \$\begingroup\$ @Arcane Engineer - This seems like a game development question to me. Maybe back off with the italics. \$\endgroup\$
    – Tom Schulz
    Dec 8, 2016 at 17:24

2 Answers 2

12
\$\begingroup\$

There are two ways to do it:

  1. You can simply remove object i from the vector using bulletArray.erase(bulletArray.begin() + i) and then i-- to process everything for the new bullet in i'th position. But this way vector will move all the object after the that specific bullet, meaning this kind of removal is O(bulletArray.size()).

  2. But usually order of the bullets is not important, so you can simply put the last bullet in the i'th cell, and only remove it's spot. This way it's much more faster since these operations are O(1). And it's implemented as bulletArray[i] = bulletArray.back(); bulletArray.pop_back(); i--; again you need to decrement i by one to check this new object in this i'th position in the next loop.

\$\endgroup\$
3
  • \$\begingroup\$ In #2, you should use swap rather than a copy if bullets are moderately complex objects --- it is much faster if there is a string in it, for instance. Actually, it looks like a vector is the wrong data structure for this job. \$\endgroup\$ Jan 2, 2013 at 0:19
  • \$\begingroup\$ @FedericoPoloni using swap will result in two copies, which is much more expensive to one copy! besides, using vector is not a bad idea in general, except std::vector is slow. \$\endgroup\$
    – Ali1S232
    Jan 2, 2013 at 0:26
  • \$\begingroup\$ Good point. According to how swap and the copy constructor are implemented, either choice could be faster. We cannot say more without knowing what a bullet is. I think that this is the exact problem that is solved by the introduction of move constructors in C++11. If the OP is willing to use C++11 extensions, this looks like the right tool for the job. \$\endgroup\$ Jan 2, 2013 at 10:39
2
\$\begingroup\$

In general, I find that it's a good idea to add a flag (normally I call this "dead") to the objects.

Then you can do this (in pseudo-pythonic code):

// Processing
for obj in my_list:
  if obj has hit enemy:
    obj.dead = true
  if obj outside play area:
    obj.dead = true
// Rendering (at some different point)
for obj in my_list:
  render somehow

Then you can use the std::remove_if higher-order function (in algorithms) to remove the ones with the "dead" flag set. Remember to use std::erase to truncate the vector to the right point.

The main advantage of using this remove_if/erase is that it avoids problems when trying to remove elements while iterating the list.

Additionally, sometimes you may want to remove object X while processing object Y (in collision detection, such situations are normal) - we can do this easily without breaking any iterator objects.

(Note that iterator objects are normally only valid as long as no new elements are added or removed. Especially for std::vector)

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .