首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > VC/MFC >

剖析STL容器遍历删除时诡异的erase(iter++),BLOG没人看,贴来这里了,再放点分.该如何解决

2012-01-11 
剖析STL容器遍历删除时诡异的erase(iter++),BLOG没人看,贴来这里了,再放点分..剖析STL容器遍历删除时诡异

剖析STL容器遍历删除时诡异的erase(iter++),BLOG没人看,贴来这里了,再放点分..
剖析STL容器遍历删除时诡异的erase(iter++)
---------------------------------
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;
}




------解决方案--------------------


低调mark
[解决办法]
看看
[解决办法]
jfmark来了
[解决办法]
study
[解决办法]
收藏!
[解决办法]
支持楼主,精神可嘉。
[解决办法]
up
[解决办法]
呵呵
[解决办法]
hao
[解决办法]

正在学习C++,先占个位置。LZ辛苦
[解决办法]
支持楼主! 华为的面试题里面就有一道这样的改错题! 
  删除偶数:  看了楼主这个帖子就能OK了~! 顶顶顶~!!!!!
void DeleteEvent(std::vector <int> &intVect)
{
std::vector
...
...
...
}
[解决办法]
我通常是这么做的:

// 如果T不是指针,这段代码可以忽略
for(list <T> ::iterator it = m_list.begin(); it != m_list.end(); ++it)
{
if (需要删除)
{
delete (*it);
}
}

m_list.erase(m_list.begin(), m_list.end());

放在循环中写代码不容易理解

热点排行