STL容器删除

转载 2007年09月23日 22:07:00
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把要删除数后面所有的数都前移一位,最后把剩余的位数删除掉。

 

 

 

C++ STL常用容器删除操作注意事項

C/C++程序员对STL 容器肯定不会陌生, 以下是对常用容器进行 erase 操作注意事项的总结:vectorvector 容器是用数组实现的, 它在内存是连续分布的.对它进行 insert 或 ...

c++ stl容器vector删除(erase),遍历等基本用法介绍及头文件

本文导语:  Vectors 包含着一系列连续存储的元素,其行为和数组类似。访问Vector中的任意元素或从末尾添加元素都可以在常量级时间复杂度内完成,而查找特定值的元素所处的位置或是在Vector...

STL容器中元素的删除的惯用法

来自《编写高质量代码:改善C++程序的150个建议》读书笔记
  • zuolj
  • zuolj
  • 2017年11月03日 14:25
  • 61

c++ stl容器vector删除(erase),遍历等基本用法介绍及头文件

Vectors 包含着一系列连续存储的元素,其行为和数组类似。访问Vector中的任意元素或从末尾添加元素都可以在常量级时间复杂度内完成,而查找特定值的元素所处的位置或是在Vector中插入元素则是线...
  • Xwxcy
  • Xwxcy
  • 2016年01月21日 16:59
  • 754

总结STL中容器中元素的删除方法

首先看下面的关于删除容器中元素的例子(VS2010中编写):  #include "stdafx.h" #include #include #include #include #include...
  • BBOOT
  • BBOOT
  • 2015年03月22日 15:22
  • 763

STL:循环删除容器中元素的方法和陷阱

算法大师Donald Knuth:不成熟的优化是一切恶果的根源(Permature optimization is the root of all evil )。   STL中的容器主要是两种:序...

【学习笔记】《STL使用入门教程》第六讲:容器使用时机、算法、删除元素的注意点

1、容器的共通能力 2、各个容器的使用时机 3、常用算法(Algorithm)的用法介绍...

[C++再学习系列] STL容器删除操作总结

由于容器所对应不同的迭代器、指针和引用的失效规则 ,使得容器的删除操作较为复杂。解决问题的最好方法取决于你是怎样鉴别出哪个对象是要被去掉的,储存它们的容器的类型,和当你删除它们的时候你还想要做什么(如...

STL容器删除元素的陷阱

今天看Scott Meyers大师的stl的用法,看到了我前段时间犯的一个错误,发现我写的代码和他提到错误代码几乎一模一样,有关stl容器删除元素的问题,错误的代码如下: std::vector m...

C++ STL标准容器插入删除算法的复杂度

1 vector 内部实现: 数组 // 就是没有固定大小的数组,vector直接翻译是向量的意思 支持操作: begin(), //取首个元素,返回一个iterator end(), //取末尾(最...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:STL容器删除
举报原因:
原因补充:

(最多只允许输入30个字)