map<int,int> amap;
amap.insert(map<int,int>::value_type(3,5));
amap.insert(map<int,int>::value_type(4,5));
amap.insert(map<int,int>::value_type(5,5));
map<int,int>::iterator iter = amap.begin();
for(;iter != amap.end();)
{
amap.erase(iter);
}
amap.insert(map<int,int>::value_type(3,5));
amap.insert(map<int,int>::value_type(4,5));
amap.insert(map<int,int>::value_type(5,5));
map<int,int>::iterator iter = amap.begin();
for(;iter != amap.end();)
{
amap.erase(iter);
}
map 是关联容器,由节点组成,每个节点都有自己独立的存储空间。除了包含元素对象之外,节点中还包含定位其前后相邻节点的位置信息(一般用指针表示),而且在定位前、后节点(即对迭代器进行自减、自加运算)时都要根据当前节点中的位置信息进行计算。显然,如果此时当前节点已经被删除、其使用的内存空间被释放的话,那么为了获得位置信息再对已经删除的节点进行访问就是非法的。
执行 amap.erase(iter) 之后再对 iter 进行 ++ 运算就是这种情况。对于关联容器来说,如果某一个元素已经被删除,那么其对应的迭代器就失效了,不应该再被使用;否则会导致程序无定义的行为。
解决上述问题的途径有两种方式,可任选其一:
1. 使用删除之前的迭代器定位下一个元素。一个技巧而又简单的实现是:amap.erase( iter++ );。iter++ 运算在函数调用执行之前(即 iter 指向的元素删除之前)完成,避开了迭代器失效的问题;而删除使用的 iter 是 ++ 之前的值。前者用到了顺序点的概念,后者用到了后加运算的特点,两者结合形成了解决此类问题的一个完美方案。
2. erase() 成员函数返回下一个元素的迭代器,可以直接使用这个迭代器访问下一个元素。
执行 amap.erase(iter) 之后再对 iter 进行 ++ 运算就是这种情况。对于关联容器来说,如果某一个元素已经被删除,那么其对应的迭代器就失效了,不应该再被使用;否则会导致程序无定义的行为。
解决上述问题的途径有两种方式,可任选其一:
1. 使用删除之前的迭代器定位下一个元素。一个技巧而又简单的实现是:amap.erase( iter++ );。iter++ 运算在函数调用执行之前(即 iter 指向的元素删除之前)完成,避开了迭代器失效的问题;而删除使用的 iter 是 ++ 之前的值。前者用到了顺序点的概念,后者用到了后加运算的特点,两者结合形成了解决此类问题的一个完美方案。
2. erase() 成员函数返回下一个元素的迭代器,可以直接使用这个迭代器访问下一个元素。
set 以及 map 的 erase( iterator pos ) 返回为空(实现效率的要求)。上面的 2 适用于 list,对 set、map 等无效。
---------------------------------------------------------------------------------------------------------------------------------
STL中结点类容器(如:list,hash_map)遍历时进行删除时,需要这样做:
for(list<int>::iterator iter = m_list.begin(); iter != m_list.end(); )
{
if(需要删除)
{
m_list.erase(iter++);
}
else
++iter;
}
{
if(需要删除)
{
m_list.erase(iter++);
}
else
++iter;
}
而不能这样:
for(list<int>::iterator iter = m_list.begin(); iter != m_list.end(); ++iter)
{
if(需要删除)
{
m_list.erase(iter);
}
}
for(list<int>::iterator iter = m_list.begin(); iter != m_list.end(); ++iter)
{
if(需要删除)
{
m_list.erase(iter);
}
}
为什么呢?
以STL list为例:
iterator的相关操作
_Self& operator++()
{
this->_M_incr();
return *this;
}
_Self& operator++()
{
this->_M_incr();
return *this;
}
_Self operator++(int)
{ _Self __tmp = *this;
this->_M_incr();
return __tmp; //后缀++按照语意返回了++前的iterator,
}
{ _Self __tmp = *this;
this->_M_incr();
return __tmp; //后缀++按照语意返回了++前的iterator,
}
void _M_incr() { _M_node = _M_node->_M_next; } //++的操作对于list结构来说,就是使iterator的_M_node指向下一个结点
iterator erase(iterator __position)
{ _List_node_base* __next_node = __position._M_node->_M_next;
_List_node_base* __prev_node = __position._M_node->_M_prev;
_Node* __n = (_Node*) __position._M_node;
__prev_node->_M_next = __next_node;
__next_node->_M_prev = __prev_node; //上面的代码把删除结点__position的前后结点串起来,而移除_positoin
_STLP_STD::_Destroy(&__n->_M_data); //call T::~T()
this->_M_node.deallocate(__n, 1); //释放结点内存
return iterator((_Node*)__next_node);
}
{ _List_node_base* __next_node = __position._M_node->_M_next;
_List_node_base* __prev_node = __position._M_node->_M_prev;
_Node* __n = (_Node*) __position._M_node;
__prev_node->_M_next = __next_node;
__next_node->_M_prev = __prev_node; //上面的代码把删除结点__position的前后结点串起来,而移除_positoin
_STLP_STD::_Destroy(&__n->_M_data); //call T::~T()
this->_M_node.deallocate(__n, 1); //释放结点内存
return iterator((_Node*)__next_node);
}
分析代码我们可以看出,erase会deallocate__position的_M_node, 在__position上再进行++是错误的。
所以不能在m_list.erase(iter)后,进行iter++.
所以不能在m_list.erase(iter)后,进行iter++.
哪为什么m_list.erase(iter++)可以呢?为什么不能用m_list.erase(++iter)?
参照operator++的代码我们可以找到答案,iter++返回了++之前的iter值,erase使用这个值能正确进行__position的前后结点的串接及删除正确的结点,而++iter返回的是++之后的iter,所以m_list.erase(++iter)串接不正确,iter->_M_node也是失效的.
参照operator++的代码我们可以找到答案,iter++返回了++之前的iter值,erase使用这个值能正确进行__position的前后结点的串接及删除正确的结点,而++iter返回的是++之后的iter,所以m_list.erase(++iter)串接不正确,iter->_M_node也是失效的.
对于非结点类,如数组类的容器vector,string,deque,如果erase会返回下个有效的iterator,可以这样处理:
for(vector<int>::iterator iter = m_vector.begin(); iter != m_vector.end();)
{
if(需要删除)
{
iter=m_vector.erase(iter);
}
else
++iter;
}
----------------------------------------------------------------------------------------------------------------------------------------
for(vector<int>::iterator iter = m_vector.begin(); iter != m_vector.end();)
{
if(需要删除)
{
iter=m_vector.erase(iter);
}
else
++iter;
}
----------------------------------------------------------------------------------------------------------------------------------------
vector<int> m_vector;
m_vector.push_back(1);
m_vector.push_back(2);
m_vector.push_back(3);
m_vector.push_back(4);
m_vector.push_back(5);
m_vector.push_back(6);
m_vector.push_back(7);
m_vector.push_back(8);
for(vector<int>::iterator iter = m_vector.begin(); iter != m_vector.end();)
{
if((*iter)%2 == 1)
{
m_vector.erase(iter); //这里erase了,iter++自动自增了??
}
else
++iter;
}
int nn = m_vector.size(); //nn =4 是对的
m_vector.push_back(1);
m_vector.push_back(2);
m_vector.push_back(3);
m_vector.push_back(4);
m_vector.push_back(5);
m_vector.push_back(6);
m_vector.push_back(7);
m_vector.push_back(8);
for(vector<int>::iterator iter = m_vector.begin(); iter != m_vector.end();)
{
if((*iter)%2 == 1)
{
m_vector.erase(iter); //这里erase了,iter++自动自增了??
}
else
++iter;
}
int nn = m_vector.size(); //nn =4 是对的
10/18 补充:
看了看vector的代码才知道vector erase同list的完全不一样。
记住vector是一片连续的内存。
iterator erase(iterator _P)
{copy(_P + 1, end(), _P);
_Destroy(_Last - 1, _Last);
--_Last;
return (_P); }
{copy(_P + 1, end(), _P);
_Destroy(_Last - 1, _Last);
--_Last;
return (_P); }
template<class _II, class _OI> inline
_OI copy(_II _F, _II _L, _OI _X)
{for (; _F != _L; ++_X, ++_F)
*_X = *_F;
return (_X); }
_OI copy(_II _F, _II _L, _OI _X)
{for (; _F != _L; ++_X, ++_F)
*_X = *_F;
return (_X); }
可见erase把要删除数后面所有的数都前移一位,最后把剩余的位数删除掉。