开发者

std containers iterator invalidation during erase [duplicate]

开发者 https://www.devze.com 2023-03-15 07:55 出处:网络
This ques开发者_开发技巧tion already has answers here: Closed 11 years ago. Possible Duplicates:
This ques开发者_开发技巧tion already has answers here: Closed 11 years ago.

Possible Duplicates:

vector erase iterator

Problem with std::map::iterator after calling erase()

I am having a concern about a piece of my code that I have. I have components and an object which stores the components. The problem is during an update the component can tell to remove a component from a the object. But its called from another function.

void Object::update() { //using std::map here
   for(ComponentMap::iterator i = components.begin(); i != components.end(); ++i) {
      (*i).second->update();
   }
}

void HealthComponent::update() {
   if(health <= 0) object->removeComponent("AliveComponent"); //this is wrong logic. but its just an example :D
}

void Object::removeComponent(string component) {

  ComponentMap::iterator i = components.find(component);
  if(i == components.end()) return;

  components.erase(i);

}

and suppose I have lots of components - Health, Alive, Graphics, Physics, Input etc.

I tried something like this (with some test components) and no errors during during update. But I am really concerned. Can it pop me an error in the future? If yes, how to fix it?

Thanks in advance,

Gasim


You cannot loop through your container and say ++i when i is potentially no longer valid (because you erased it). A typical erase loop goes like this:

for (it = x.begin(); it != x.end(); /* nothing here! */)
{
  if (must_erase(*it))
  {
    x.erase(it++); // advance it while still valid, return previous and erase
  }
  else
  {
    ++it;
  }
}

Rewrite your code in this spirit.

To spell out your problem: In Object::update(), you call HealthComponent::update() which invalidates the iterator i, and then you call ++i, which is undefined behaviour.


In MSVC erase will return the next valid iterator however in GCC it returns void so the only portable way to deal with this issue is keeping the previous iterator, erasing the current element then incrementing the previous iterator for next iteration.

http://www.cplusplus.com/reference/stl/map/erase/

  void Object::removeComponent(string component, ComponentMap::iterator& _prev ) 
  {
     ComponentMap::iterator i = components.find(component);
     if(i == components.end()) 
        return;
     _prev = i;
     --_prev;
     components.erase(i);
     ++prev;
   }
0

精彩评论

暂无评论...
验证码 换一张
取 消