STL容器删除

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);
        }
map 是关联容器,由节点组成,每个节点都有自己独立的存储空间。除了包含元素对象之外,节点中还包含定位其前后相邻节点的位置信息(一般用指针表示),而且在定位前、后节点(即对迭代器进行自减、自加运算)时都要根据当前节点中的位置信息进行计算。显然,如果此时当前节点已经被删除、其使用的内存空间被释放的话,那么为了获得位置信息再对已经删除的节点进行访问就是非法的。

执行 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;
}
而不能这样:
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++(int)
{    _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);
}
分析代码我们可以看出,erase会deallocate__position的_M_node, 在__position上再进行++是错误的。
所以不能在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也是失效的.
对于非结点类,如数组类的容器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;
}
----------------------------------------------------------------------------------------------------------------------------------------
 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 是对的
10/18 补充:
看了看vector的代码才知道vector erase同list的完全不一样。
记住vector是一片连续的内存。
 iterator erase(iterator _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); }

 可见erase把要删除数后面所有的数都前移一位,最后把剩余的位数删除掉。

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值