2

I'm going through the code from Cinder's Box2D template, and want to modify the code so I can remove boxes to the screen as well as add them. I'm new to C++, but based on what I've learned from other SO posts I'm trying to delete boxes from the vector using this if case:

for( vector<b2Body*>::iterator boxIt = mBoxes.begin(); boxIt != mBoxes.end(); ++boxIt ) {
    if( (*boxIt)->GetPosition().x > scaledX){
        boxIt = mBoxes.erase(boxIt);
    }
    else {

        Vec2f pos( (*boxIt)->GetPosition().x, (*boxIt)->GetPosition().y );
        float t = toDegrees( (*boxIt)->GetAngle() );

        glPushMatrix();
        gl::translate( pos );
        gl::rotate( t );

        Rectf rect( -BOX_SIZE, -BOX_SIZE, BOX_SIZE, BOX_SIZE );
        gl::drawSolidRect( rect );

        glPopMatrix();
    }

}

But this is causing bad access crash when "(*boxIt)->GetPosition().x" executes the second time. Any ideas?

jag
  • 193
  • 2
  • 4
  • 13
  • what do you mean by second time? – 4pie0 Jul 04 '13 at 23:05
  • I added some cout lines to see where the program is crashing; it successfully runs through the if case once (which suggests to me that the call to erase is working), but then when the if guard is executed again it crashed. I'm assuming that *boxIt is not longer valid, but Im not sure why. – jag Jul 04 '13 at 23:09

3 Answers3

2

You should not ++boxIt after erasing. The assignment already moves your iterator to the next item.

Remove ++boxIt from the for line and put it in the else only.

Yohan Danvin
  • 885
  • 6
  • 13
1

The reason you observe such behavior is because a vector's erase() invalidates existing iterators. You cannot then increment your iterator ++boxIt. However erase() returns a new iterator pointing to the element after the one that was removed. And you can use this returned iterator to continue iterating over your vector.

So, you might code it like this:

vector<b2Body*>::iterator boxIt = mBoxes.begin();
while (boxIt != mBoxes.end();) {
    if( (*boxIt)->GetPosition().x > scaledX){
        boxIt = mBoxes.erase(boxIt);
    }
    else {

        Vec2f pos( (*boxIt)->GetPosition().x, (*boxIt)->GetPosition().y );
        float t = toDegrees( (*boxIt)->GetAngle() );

        glPushMatrix();
        gl::translate( pos );
        gl::rotate( t );

        Rectf rect( -BOX_SIZE, -BOX_SIZE, BOX_SIZE, BOX_SIZE );
        gl::drawSolidRect( rect );

        glPopMatrix();
        boxit++;
    }
}

see here:

Vector.erase(Iterator) causes bad memory access

deleting while iterating

iterate vector, remove certain items as I go

Community
  • 1
  • 1
4pie0
  • 29,204
  • 9
  • 82
  • 118
0

Your problem is that you are erasing an iterator and then continuing iterating with it.

For a vector every iterator and reference after the point of erase is invalidated. (Good overview for other cases here: http://kera.name/articles/2011/06/iterator-invalidation-rules/)

You could try using erase with remove_if (the latter doesn't actually erase anything on its own)

http://en.wikipedia.org/wiki/Erase-remove_idiom

kamjagin
  • 3,614
  • 1
  • 22
  • 24
  • I was under the impression that the line "boxIt = mBoxes.erase(boxIt);" will maintain the validity of the iterator (because boxIt will now include only elements that still exist). Is this incorrect? – jag Jul 04 '13 at 23:16