• 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;
    }
  • 相关阅读:
    一些性能查询的SQL 备忘
    informatica powercenter学习笔记(LookUp 使用)
    BIEE使用技巧
    Linux 系统中用户切换
    手动将自定制的WebPart部署到 SharePoint 2010 中
    C# Process运行cmd命令的异步回显
    Union和Union all的区别
    C#输入输出重定向
    c# 其他技术学习
    Oracle查看所有用户
  • 原文地址:https://www.cnblogs.com/timssd/p/4160659.html
Copyright © 2020-2023  润新知