您的位置:首页 > 编程语言 > C语言/C++

C++中遍历容器对象时需要注意的问题

2010-08-12 23:02 267 查看
假设有这样一个管理对象的容器ActorManager,其实现大概为

classActor; classActorManager { public: voidupdate() { for(actors_t::const_iteratoritr=m_actors.begin();itr!=m_actors.end();++itr) { Actir*actor=itr->second; actor->update(); } } voidadd(Actor*actor) { m_actors[actor->get_id()]=actor; } voidremove(Actor*actor) { m_actors.erase(actor->get_id()); } private: typedefstd::mapactors_t; actors_tm_actors; };

而Actor类的实现是这样:

classActor { public: voidupdate() { //... }
有一天,在给Actor添加逻辑的时候,update函数变成了这样

voidupdate() { //... update_buff_effect(); //... }

再往下

classActor { //... private: voidupdate_buff_effect() { //... apply_hp(-100); if(get_hp()<=0) { die(); return; } //... }

然后……

private: voiddie() { //... ActorManager::getInstance().remove(this); //... }
在写下ActorManager的时候并没有想到会在update循环里删除对象,而实际上却有几次遇到类似的问题。
有些问题没有这么明显,但也都是出在遍历容器对象的过程中,某个执行函数删除了窗口里的对象,从而导致迭代器失效。

修改的方法很简单,给ActorManager添加一个待删除对象列表
在remove方法中并不真正删除对象,而是等到update中循环结束后再删除对象。
代码看起来会是这样:

classActor;
classActorManager
{
public:
voidupdate()
{
m_is_looping=true;
for(actors_t::const_iteratoritr=m_actors.begin();itr!=m_actors.end();++itr)
{
Actir*actor=itr->second;
actor->update();
}
m_is_looping=false;

if(!m_removed_actors.empty())
{
for(removed_actors_t::const_iteratoritr=m_removed_actors.begin();
itr!=m_removed_actors.end();++itr)
{
Actor*actor=*itr;
m_actors.erase(actor->get_id());
}
m_removed_actors.clear();
}
}

voidadd(Actor*actor)
{
m_actors[actor->get_id()]=actor;
}

voidremove(Actor*actor)
{
if(!m_is_looping)
m_actors.erase(actor->get_id());
else
m_removed_actors.push_back(actor);
}

private:
typedefstd::mapactors_t;
actors_tm_actors;

typedefstd::vectorremoved_actors_t;
removed_actors_tm_removed_actors;
boolm_is_looping;
};

没有给add也加保护的原因是,不会在update函数内向ActorManager添加新对象。

当然,有可能在其他地方会有这样的需求,同样也做类似的保护即可。

问题虽然不大,但是几次碰到类似的错误了。记录之,并强制要求自己,

在遇到会对容器内的对象做for…处理时,一定要谨慎的检查一下remove接口。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: