首页 > 代码库 > STL容器的遍历删除

STL容器的遍历删除

STL容器的遍历删除       今天在对截包程序的HashTable中加入计时机制时,碰到这个问题。对hash_map中的每个项加入时间后,用查询函数遍历hash_map,以删除掉那些在表存留时间比某个阈值长的表项(当然这个函数是应该运行在另起一个线程上的),但是在按照下面的方法对hash_map(用迭代器)遍历删除时,当找到第一个满足删除条件的元素并将其删除后,程序将提示非法:for(list<int>::iterator iter = m_map.begin(); iter != m_map.end(); ++iter)  //这种做法是错误的{    if(需要删除)    {        m_map.erase(iter);    }}原因是当前节点已经被删除后,其使用的内存空间被释放的,那么再对已经删除的节点获得位置信息,这种访问当然就是非法的。正确的做法是:for(list<int>::iterator iter = m_map.begin(); iter != m_map.end(); )  //这是正确的做法{    if(需要删除)    {        m_map.erase(iter++);    }    else        ++iter;}为什么呢?    map 是关联容器,由节点组成,每个节点都有自己独立的存储空间。除了包含元素对象之外,节点中还包含定位其前后相邻节点的位置信息(一般用指针表示),而且在 定位前、后节点(即对迭代器进行自减、自加运算)时都要根据当前节点中的位置信息进行计算。显然,如果此时当前节点已经被删除、其使用的内存空间被释放的 话,那么为了获得位置信息再对已经删除的节点进行访问就是非法的。    STL中对结点类容器(如list,hash_map)遍历时进行删除时,涉及到iterator的相关操作(以list为例):_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; }    //++的操作是使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;      _STLP_STD::_Destroy(&__n->_M_data); //call T::~T()    this->_M_node.deallocate(__n, 1);                return iterator((_Node*)__next_node);}     分析代码我们可以看出,erase会释放掉__position的_M_node, 在__position上再进行++是错误的。所以不能在m_map.erase(iter)后,进行iter++。哪为什么m_map.erase(iter++)可以呢?为什么不能用m_map.erase(++iter)?    参照operator ++的代码我们可以找到答案。iter++返回了++之前的iter值,erase使用这个值能正确进行__position的前后结点的串接及删除正确 的结点,而++iter返回的是++之后的iter,所以m_map.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;}

 

STL容器的遍历删除