假設(shè)有這樣一個(gè)管理對(duì)象的窗口 ActorManager,其實(shí)現(xiàn)大概為
class Actor;
class ActorManager
{
public:
void update()
{
for (actors_t::const_iterator itr = m_actors.begin(); itr != m_actors.end(); ++itr)
{
Actir* actor = itr->second;
actor->update();
}
}
void add(Actor* actor)
{
m_actors[actor->get_id()] = actor;
}
void remove(Actor* actor)
{
m_actors.erase(actor->get_id());
}
private:
typedef std::map<int, Actor*> actors_t;
actors_t m_actors;
};
而Actor類的實(shí)現(xiàn)是這樣:
class Actor
{
public:
void update()
{
// ...
}
有一天,在給Actor添加邏輯的時(shí)候,update函數(shù)變成了這樣
void update()
{
// ...
update_buff_effect();
// ...
}
再往下
class Actor
{
// ...
private:
void update_buff_effect()
{
// ...
apply_hp(-100);
if (get_hp() <= 0)
{
die();
return;
}
// ...
}
然后……
private:
void die()
{
// ...
ActorManager::getInstance().remove(this);
// ...
}
在寫下ActorManager的時(shí)候并沒有想到會(huì)在update循環(huán)里刪除對(duì)象,而實(shí)際上卻有幾次遇到類似的問題。
有些問題沒有這么明顯,但也都是出在遍歷容器對(duì)象的過程中,某個(gè)執(zhí)行函數(shù)刪除了窗口里的對(duì)象,從而導(dǎo)致迭代器失效。
修改的方法很簡(jiǎn)單,給ActorManager添加一個(gè)待刪除對(duì)象列表
在remove方法中并不真正刪除對(duì)象,而是等到update中循環(huán)結(jié)束后再刪除對(duì)象。
代碼看起來(lái)會(huì)是這樣:
class Actor;
class ActorManager
{
public:
void update()
{
m_is_looping = true;
for (actors_t::const_iterator itr = 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_iterator itr = m_removed_actors.begin();
itr != m_removed_actors.end(); ++itr)
{
Actor* actor = *itr;
m_actors.erase(actor->get_id());
}
m_removed_actors.clear();
}
}
void add(Actor* actor)
{
m_actors[actor->get_id()] = actor;
}
void remove(Actor* actor)
{
if (!m_is_looping)
m_actors.erase(actor->get_id());
else
m_removed_actors.push_back(actor);
}
private:
typedef std::map<int, Actor*> actors_t;
actors_t m_actors;
typedef std::vector<Actor*> removed_actors_t;
removed_actors_t m_removed_actors;
bool m_is_looping;
};
沒有給add也加保護(hù)的原因是,不會(huì)在update函數(shù)內(nèi)向ActorManager添加新對(duì)象。
當(dāng)然,有可能在其他地方會(huì)有這樣的需求,同樣也做類似的保護(hù)即可。
問題雖然不大,但是幾次碰到類似的錯(cuò)誤了。記錄之,并強(qiáng)制要求自己,
在遇到會(huì)對(duì)容器內(nèi)的對(duì)象做for…處理時(shí),一定要謹(jǐn)慎的檢查一下remove接口。