使用STL的hash_map也有几年的时间了(这里主要指SGI的STL库,Microsoft的可能实现方式有点区别)
我的代码中经常会出现这样的一段
hash_map<.,.>::iterator iter,itertemp;
for( iter = h.begin();iter!=h.end();)
{
itertemp = iter++;
if( (*itertemp ).second == vaule )
{
h.erase(itertemp );
}
}
今天突然发现身边的同事不是这样用的,他们给我提出了一个问题,
h.erase(itertemp ); 有可能会导致迭代器iter失效.
有这个可能吗?
察看了一下hash_map中的迭代器的实现
struct _Hashtable_iterator
{
.....
...
_Node* _M_cur;
_Hashtable* _M_ht;
};
其中只列出了我们关心的 两个成员,一个是 _M_cur, _M_ht,说正确点,其实只关心 _M_cur;
_Node的实现也可以在STL的源代码中找到,这里就不列出来了,它实际上是一个单向连表
_Node结构主要包括2个成员
_M_next ,指向下一个成员
_M_val ,指向节点所关联的数据值,这里其实就是一个pair
分析一下++操作
_Hashtable_iterator<_Val,_Key,_HF,_ExK,_EqK,_All>&
_Hashtable_iterator<_Val,_Key,_HF,_ExK,_EqK,_All>::operator++()
{
const _Node* __old = _M_cur;
_M_cur = _M_cur->_M_next;
if (!_M_cur) {
size_type __bucket = _M_ht->_M_bkt_num(__old->_M_val);
while (!_M_cur && ++__bucket < _M_ht->_M_buckets.size())
_M_cur = _M_ht->_M_buckets[__bucket];
}
return *this;
}
迭代器的++操作,实际上都是对内部的单向链表进行操作的,只是将链表指向下一个节点
迭代器本身不会做什么.
erase 操作也是这样的
template <class _Val, class _Key, class _HF, class _Ex, class _Eq, class _All>
void hashtable<_Val,_Key,_HF,_Ex,_Eq,_All>::erase(const iterator& __it)
{
_Node* __p = __it._M_cur;
if (__p) {
const size_type __n = _M_bkt_num(__p->_M_val);
_Node* __cur = _M_buckets[__n];
if (__cur == __p) {
_M_buckets[__n] = __cur->_M_next;
_M_delete_node(__cur);
--_M_num_elements;
}
else {
_Node* __next = __cur->_M_next;
while (__next) {
if (__next == __p) {
__cur->_M_next = __next->_M_next;
_M_delete_node(__next);
--_M_num_elements;
break;
}
else {
__cur = __next;
__next = __cur->_M_next;
}
}
}
}
}
实际操作都是里面 _Node成员
如果一个迭代器导致另外一个迭代器失效,
一种是导致另外一个迭代器的_Node成员的值发生变化,或者导致另外一个迭代器的 _Node成员失效,这里我们看到
erase操作并不能造成这样的效果.