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

本文介绍了一种在ActorManager类中安全地更新和删除Actor对象的方法。为了避免在更新过程中直接删除对象导致迭代器失效的问题,引入了一个待删除对象列表,并在update循环结束后统一处理删除操作。

假设有这样一个管理对象的容器 ActorManager,其实现大概为

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 actors_t;
actors_t m_actors;
};

 

而Actor类的实现是这样:

class Actor
{
public:
void update()
{
// ...

}

有一天,在给Actor添加逻辑的时候,update函数变成了这样

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的时候并没有想到会在update循环里删除对象,而实际上却有几次遇到类似的问题。
有些问题没有这么明显,但也都是出在遍历容器对象的过程中,某个执行函数删除了窗口里的对象,从而导致迭代器失效。
 
修改的方法很简单,给ActorManager添加一个待删除对象列表
在remove方法中并不真正删除对象,而是等到update中循环结束后再删除对象。
代码看起来会是这样:
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 actors_t;
actors_t m_actors;

typedef std::vector removed_actors_t;
removed_actors_t m_removed_actors;
bool m_is_looping;
};

 

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

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

 

 

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

在遇到会对容器内的对象做for…处理时,一定要谨慎的检查一下remove接口。

<think>嗯,用户想了解如何在C++遍历容器的方法或示例。首先,我需要回忆一下C++中的迭代器相关知识。根据用户提供的引用[1],迭代器是指向容器元素的对象,类似于指针,可以用来循环访问容器。所以,我应该从迭代器的基本用法开始。 然后,可能需要介绍不同的迭代器类型,比如使用begin()和end()来获取迭代器的起点和终点,这是最常见的做法。例如,对于vector这样的容器,可以用for循环配合迭代器来遍历。用户可能需要具体的代码示例,所以得举几个例子,比如使用传统for循环和C++11引入的基于范围的for循环。 接着,考虑到不同的容器类型,比如map和set,它们的迭代器使用方式可能有所不同。例如,map的每个元素是一个键值对,遍历需要解引用成pair类型。这候应该分别给出不同容器遍历示例,以展示通用性。 另外,用户提供的引用[2]提到了基本数据类型和控制结构,所以示例代码应该保持简单,使用基本语法,避免复杂的特性,方便初学者理解。可能需要检查代码中的语法是否正确,比如是否正确使用迭代器的操作符和解引用。 还要注意引用格式,用户要求引用的标识符放在相关段落末尾,比如在提到迭代器概念加上[^1]。同,确保所有代码块使用正确的格式,行内公式用$...$,独立公式用$$...$$,但在这个问题中可能不太涉及数学公式,所以主要关注代码部分。 然后,生成相关问题候,要围绕遍历容器的不同方法、迭代器类型、C++11特性以及具体容器遍历技巧来提问。确保问题覆盖用户可能感兴趣的扩展内容,比如性能比较或者不同迭代器的区别。 最后,检查整个回答是否符合用户的要求:使用中文,结构清晰,代码正确,引用标识正确放置,并且相关问题与主题相关。可能还需要确认是否遗漏了其他遍历方法,比如使用算法库中的函数,但考虑到用户请求的是基础示例,可能不需要深入高级话题,保持回答简洁明了。</think>### C++容器遍历方法详解 在C++遍历容器主要使用迭代器(Iterator)和C++11引入的基于范围的for循环。以下是具体方法和示例: #### 一、迭代器基础 迭代器是连接算法与容器的桥梁,所有标准容器(如$vector$、$list$、$map$)都提供迭代器接口。 ```cpp #include <vector> using namespace std; vector<int> nums{1,2,3,4,5}; // 传统迭代器遍历 for(vector<int>::iterator it = nums.begin(); it != nums.end(); ++it) { cout << *it << " "; } // C++11自动类型推导 for(auto it = nums.begin(); it != nums.end(); ++it) { cout << *it << " "; } ``` #### 二、不同容器遍历示例 1. **顺序容器**($vector$/$list$/$deque$) ```cpp #include <list> list<string> names{"Alice", "Bob", "Charlie"}; for(auto it = names.begin(); it != names.end(); ++it) { cout << *it << endl; } ``` 2. **关联容器**($map$/$set$) ```cpp #include <map> map<int, string> id_name{{1,"Tom"}, {2,"Jerry"}}; for(auto it = id_name.begin(); it != id_name.end(); ++it) { cout << "ID:" << it->first << " Name:" << it->second << endl; } ``` #### 三、C++11基于范围的for循环 ```cpp // 遍历vector vector<double> prices{9.99, 24.5, 3.14}; for(const auto& price : prices) { cout << price << " "; } // 遍历map for(const auto& pair : id_name) { cout << pair.first << "=>" << pair.second << endl; } ``` #### 四、反向迭代器 ```cpp // 逆向输出vector内容 for(auto rit = nums.rbegin(); rit != nums.rend(); ++rit) { cout << *rit << " "; } ``` [^1]: 迭代器作为容器元素的指针式访问工具,支持标准化的遍历操作 [^2]: C++11新特性简化了容器遍历的语法结构
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值