Question

I have a class Circle whose instances I keep track of with these:

Circle *f1;
vector<Circle> list;
vector<Circle>::iterator it;

I have managed to create multiple Circles and got them to move around. How can I erase a specific instance of Circle? For example, if a certain circle hits a wall, then it should be erased. I've looked around at other questions and I even tried the code they gave out and no luck. Here's what I've got at the moment:

for (it = list.begin(); it != list.end(); ++it) {

    it->x += 1;

    if (it->x == ofGetWindowWidth()) {
        list.erase(it);
    }
}

I have gotten other statements to work with the if statement such as reversing the direction of their movement. list.erase(it); was a line of code I got from here and I don't understand why it crashes my program.

Was it helpful?

Solution

for (it = list.begin(); it != list.end(); /* nothing here */) {

    it->x += 1;

    if (it->x == ofGetWindowWidth()) {
        it = list.erase(it);
    } else {
        ++it;
    }
}

The problem with your original code is that erasing an element invalidates the iterator to that element - the very same iterator you are trying to increment next. This exhibits undefined behavior.

OTHER TIPS

list.erase invalidates iterators to the erased element. Therefore, after you erase the element pointed to by "it", "it" is invalidated and the ++it, which follows after the for loops body, can crash your program. Rewriting your code to something similiar to the following should prevent your crash:

for(it=list.begin();it!=list.end(); ) {
    //your code
    if(it->x==ofGetWindowWidth())
        it=list.erase(it);
    else
        ++it;
}

The problem with the above code using erase() is that it invalidates the content of it when the element is being erase. You can use, e.g., this instead:

for (it = list.begin(); it != list.end(); ) {
    it->x += 1;

    if (it->x == ofGetWindowWidth()) {
        list.erase(it++);
    }
    else {
        ++it;
    }
}

The branch using erase() moves the kept iterator it off its current location before erase()ing the element. Only the temporary object return from it++ gets invalidated. Of course, for this loop to work, you can't unconditionally increment it, i.e., the non-erase()ing branch needs its own increment.

You could use erase with remove_if. This also works for removal of multiple elements. In your case it's

list.erase(std::remove_if(list.begin(), list.end(),
        [](const Circle& c){return c.x == ofGetWindowWidth();},list.end()), 

Example with integers:

#include <algorithm>
#include <vector>
#include <iostream>

int main()
{
    std::vector<int> str1 = {1,3,5,7};
    str1.erase(std::remove_if(str1.begin(), str1.end(),
                              [](int x){return x<4 && x>2;}), str1.end());
   for(auto i : str1) std::cout << i ;
}

prints 157

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top