关闭

STL容器删除

780人阅读 评论(0) 收藏 举报
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

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:72377次
    • 积分:940
    • 等级:
    • 排名:千里之外
    • 原创:14篇
    • 转载:8篇
    • 译文:6篇
    • 评论:8条
    最新评论
    我的链接