Trouble removing elements from C++ vector - c++

I'm trying to remove 'dead' bullets from my vector of bullets. Every frame, I'm calling the Bullet::update() function which looks like this:
void Bullet::update()
{
for(int i = 0; i != mAmmo.size(); i++)
{
if(mAmmo[i].sprite.getPosition().x > 700)
mAmmo[i].mAlive = false;
if(mAmmo[i].mAlive == false)
{
// I get a Debug Assertion Failed at runtime from this piece of code
mAmmo.erase(mAmmo.begin()+i);
}
if(mAmmo[i].mAlive == true)
{
mAmmo[i].sprite.move(mMovement);
}
}
}
Am I doing this completely incorrectly? This is the first time I've really used vectors more than just following through a tutorial. If I need to post any more code, just tell me. I've been working on this for the past few hours, so I'm a wee bit desperate to get this to work.
Thanks in advance!

You're easily walking into undefined behavior as soon as the ith element is the last element in your list. Use iterators, and pay special attention to the return value of erase(), as it automatically advances the iterator for you so your loop doesn't have to.
void Bullet::update()
{
for (auto it = mAmmo.begin(); it != mAmmo.end();)
{
if(it->sprite.getPosition().x > 700)
it->mAlive = false;
if (!it->mAlive)
{
// erase and get next iterator
it = mAmmo.erase(it);
}
else
{ // move and increment
it->sprite.move(mMovement);
++it;
}
}
}

Related

Vector iterator not incrementable while collision

I am trying to create a pacman game watching tutorials. So when the ghost is in weak state and the pacman collides with the ghost i am getting the error Vector iterator not incrementable. Can anyone please solve this. Thanks in advance.
for (Ghost*ghost : m_ghosts)
{
if (ghost->getCollisionBox().intersects(m_pcMan->getCollisionBox()))
{
if (ghost->isWeak())
{
m_ghosts.erase(std::find(m_ghosts.begin(), m_ghosts.end(),ghost));
m_score += 100;
}
else
m_pcMan->die();
}
}
NOTE: You used a range-based for-loop, but inside the for-loop, you still use std::find to find an iterator to the current element so that you delete, that's inefficient.
Your problem stems from the fact that, after you delete an element pointed to by an iterator, all iterators and references to that element and after becomes invalid. The range-based for-loop tries to eventually increment an invalid iterator which invokes Undefined Behavior. Luckily, your STL implementation has checked iterators, so you got some nice error message.
There are a variety of ways to go about this. Though you may want to consider your Data Structure... Game Devs have more advanced tricks for this sort of thing. Nonetheless, here is a trick, swap the current ghost with the last on the vector, then pop the last element.
auto iter = m_ghosts.begin();
auto End = m_ghosts.end();
while(iter != End)
{
if (iter->getCollisionBox().intersects(m_pcMan->getCollisionBox()))
{
if (iter->isWeak())
{
if(iter != std::prev(End)){ //We want to prevent `std::swap(x,x)`
std::iter_swap(iter, std::prev(End));
End = m_ghost.erase(std::prev(End));
}
else{
iter = End = m_ghost.erase(std::prev(End));
}
m_score += 100;
}
else{
++iter;
m_pcMan->die();
}
}
}
Thank you for the help guys. This worked for me :)
for (int i=0;i<m_ghosts.size();i++)
{
Ghost* ghost = m_ghosts[i];
if (ghost->getCollisionBox().intersects(m_pcMan->getCollisionBox()))
{
if (ghost->isWeak())
{
m_ghosts.erase(std::find(m_ghosts.begin(), m_ghosts.end(), ghost));
m_score += 100;
}
else
m_pcMan->die();
}
}

Debug assertion error - List iterators incompatible

I'm working on a program that is supposed to put every Window in a list, resize it, and move it to a screen position according to a specified layout.
When I'm running this function however I get a debug assertion error saying "list iterators incompatible".
Here is the code:
void Control::checkForNewWindows()
{
for (std::list<Window>::iterator i = mainDetector.getWindowList().begin(); i != mainDetector.getWindowList().end(); ++i)
{
bool forBreak = false;
if ((i->getTitle().find("sample_title") != std::string::npos) && (i->getState() == false))
{
for (int y = 0; y < 3; y++)
{
for (int x = 0; x < 4; x++)
{
if (activeLayout.windowLayout[y][x].getHandle() == 0)
{
moveWindow(*i, activeLayout.dimensionsLayout[y][x].x, activeLayout.dimensionsLayout[y][x].y, activeLayout.dimensionsLayout[y][x].width,
activeLayout.dimensionsLayout[y][x].height);
activeLayout.windowLayout[y][x] = *i;
activeLayout.windowLayout[y][x].setState(true);
forBreak = true;
}
if (forBreak)
{
break;
}
}
if (forBreak)
{
break;
}
}
}
}
}
The error occurs during the first for loop, hope someone can help me fix this
Edit:
Here is the getWindowList function:
std::list <Window> Detector::getWindowList()
{
return windowList;
}
and the windowList definition:
std::list <Window> windowList;
Your loop looks like this:
for (std::list<Window>::iterator i = mainDetector.getWindowList().begin();
i != mainDetector.getWindowList().end();
++i)
Given the above, the issue is this:
std::list <Window> Detector::getWindowList()
{
return windowList;
}
You're returning a copy of the list, not the original. Thus the iterator to the copy will be used in the loop and not the iterator of windowList. In fact, you are using two different iterators in the loop construct, and neither one of them refers to the original list, only copies.
The fix is to return a reference:
std::list <Window>& Detector::getWindowList()
{
return windowList;
}
You're now returning a reference to the actual list, not a copy. Now the iterators you are using in the loop constraints refer to the same list, not different lists.
FYI for others researching this, you can also get the same error message when dealing with dangling pointers and deleted memory. I ran across this thread while tracking down that error message. In my case, there was an iterator to a list that had been deleted.

List of bullets c++

I'm writing the Contra Game by Directx9 and c++
please help me about list of bullets
i'm trying below code but it's error: vector intertor incompatible
std::vector<Bullet*> bullets
if (mKeyboard->IsKeyPress(DIK_X))
{
Bullet* toShoot = new Bullet(noneType, _position.x, _position.y, RIGHT);
toShoot->Init();
bullets.push_back(toShoot);
}
Update Funtion:
std::vector<Bullet*>::iterator it = bullets.begin();
while ((it) != bullets.end())
{
(*it)->Update(gameTime, c);
if ((*it)->IsLive() == false)
{
bullets.erase(it++);
}
}
Render funtion
std::vector<Bullet*>::iterator it = bullets.begin();
while (it != bullets.end())
{
if ((*it)->IsLive())
{
(*it++)->Render(gr, cx, cy);
}
}
You can't just increment an iterator passed to erase(…). Do this instead:
if (!(*it)->IsLive()) {
it = bullets.erase(it);
} else {
++it;
}
Your Render function has a different bug. It gets stuck on the first non-live bullet, since the increment is inside the if-block. This is one reason for(…) is usually preferable to while(…):
for (auto it = bullets.begin(); it != bullets.end(); ++it) {
if (…) {
…
}
}
In fact, the Update function should be likewise changed, but omit the ++it.
In your Update function, you are calling the erase while iterating over a vector. The problem is that your it iterator can get invalidated if it is being used in the loop at the same time you're erasing from the vector.
The alternative is to use the erase/remove_if idiom:
#include <algorithm>
//...
bullets.erase(std::remove_if(bullets.begin(), bullets.end(),
[&](Bullet& b) { b.Update(gameTime, c); return !b.IsLive(); }),
bullets.end());
The algorithm function remove_if() is called to determine which bullets will be removed by the erase() function. Note that the lambda includes the logic you used to determine if a bullet should be removed.

Vector Collision

I am quite green regarding vectors, and this is my first time actually using them for collision checking. This is for my project, and I am stumped on how to implement the collision. The current Collision check and response codes I have seem to be ... bad design.
This is my code:
for(auto it = ArrayofEntities.begin(); it != ArrayofEntities.end(); it++)
{
CEntity * go = (*it);
for(auto i = ArrayofEntities.begin(); i != ArrayofEntities.end();)
{
//Collision for entities. Collision Event returns the iterator after an element is erased.
CEntity * other = (*i);
if (go != other)
{
if (!theCollision.CheckCollision(go, other, false, false, false, false)) //Checks if it has collided go with other
{
i = go->CollisionEvent(*other, ArrayofEntities); //Run collision code, setting i to the iterator which is returned.
//break;
}
else
{
i++;
}
}
else
{
i++;
}
}
}
CEntity is the base class for all the entities.
My CheckCollision just returns a true or false on the collision, and my collision event runs the collision and returns an iterator (because I might have to destroy things in the vector).
My collision event is below
vector<CEntity*>::iterator bullet::CollisionEvent(CEntity &other, vector<CEntity*> & theArray)
{
case ZOMBIE:
{
other.hp -= power * 0.01;//Effect
int Counter, index, bulletindex;
auto it = theArray.begin();
//Find the bullet and the other in the array.
for (it = theArray.begin(), Counter = 0; it != theArray.end();it++, Counter++)
{
CEntity *go = NULL;
go = (*it);
if (go == &other)
{
index = Counter;
}
if(go->ID == BULLET && go->GetX() == GetX() && go->GetY() == GetY())
{
bulletindex = Counter;
}
}
this->~bullet();//Delete the bullet
theArray.erase(theArray.begin() + bulletindex);
if(other.hp <= 0)
{
other.~CEntity();
it = theArray.erase(theArray.begin() + index); //delete from array.
return it;
}
it = theArray.begin() + index;
return it;
}
}
I have basically done this like how I would do an array. Just check it against itself. The error it gives is "Vector Iterator not Incrementable", on the first for loop after the collision event has been run.
So my question: 1) What am I doing wrong?
2) Is my thinking to do this like checking arrays wrong?
This is my school project, so I have full control of the codes.
I would prefer to have a quick fix over a complete rewriting of all the collision codes, but if it really comes down to it, I will rewrite my codes.
If you look at the implementation of std::remove_if, you'll see that they've solved the issue of iterator invalidation in another way. instead of erasing elements, they move them to the end of the array.
This may be the easiest solution for you as well. Keep an iterator which points after the last "live" entirty. It starts out at .end but as bullets hit things, you swap the entities to the back of your range and decrement that last-live iterator.
Then, when you're done looping over your array, you clean up with a single call to .erase.
And yes, you should use either std::unique_ptr<CEntity> or std::shared_ptr<CEntity> in the collection. In that way, .erase won't just erase the pointer but also the object pointed to.

Vector iterator not incremental .erase()

I am trying to delete any element of this vector that collides with player. However when I try to remove the element from the vector the program crashes and I get the error; "vector iterator not incremental".
for (std::vector<Coin>::iterator i=CoinSet.begin(); i!=CoinSet.end(); i++)
{
if (i->PlayerClear(player.collider()) == true)
{
score++;
cout<<score<<endl;
CoinSet.erase(i);
}
}
This code works perfectly well until "CoinSet.erase(i)", I tried using "CoinSet.clear()" at various points, but to no avail. Any help on this would be great, thanks in advance!
This has been discussed to death. You mustn't operate on an invalid iterator. You want something like this:
for (auto it = CoinSet.begin(); it != CoinSet.end(); /* no increment here! */ )
{
if (/* ... */)
{
// ...
CoinSet.erase(it++);
}
else
{
++it;
}
}
I don't like putting ++-statements inside the argument. Therefore erase() returns an iterator that points to the next element, so one could replace the erase line with:
it = CoinSet.erase(it); // iterator is replaced with valid one