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

fohoo 2007-01-23 02:24:25
剖析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;
}


...全文
1040 16 打赏 收藏 转发到动态 举报
写回复
用AI写文章
16 条回复
切换为时间正序
请发表友善的回复…
发表回复
zx_sanjin 2007-01-25
  • 打赏
  • 举报
回复
不过你的钻研精神和分享精神还是非常值得大家学习的!

www.cpptk.com 在招版主,我是站长,呵呵
zx_sanjin 2007-01-25
  • 打赏
  • 举报
回复
重复遍历检查

具体问题具体分析,我的观点是商业项目中在效率可以承受的范围内:可读性 > 效率
zx_sanjin 2007-01-25
  • 打赏
  • 举报
回复
多一次遍历换来了程序的可读性,值得,程序的可读性包括(自己+别人),呵呵~~

你可以做一次测试,看看到底节省了多少时间,应该是可以忽略的,如果要求非常高的效率,那么就另当别论了

fohoo 2007-01-25
  • 打赏
  • 举报
回复
zx_sanjin:

在循环外来erase,你还要用functor判断一次删除条件,做无用功了,不删除,如果m_list需要重复遍历检查怎么办?

STL的erase(first, last)也是使用erase(iter++)来做的。。理解了它,用起来代码很清晰

template <class _Tp, class _Alloc>
typename list<_Tp,_Alloc>::iterator list<_Tp, _Alloc>::erase(iterator __first,
iterator __last)
{
while (__first != __last)
erase(__first++);
return __last;
}

zx_sanjin 2007-01-25
  • 打赏
  • 举报
回复
我通常是这么做的:

// 如果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());

放在循环中写代码不容易理解
tiger波波 2007-01-24
  • 打赏
  • 举报
回复
up
ljh_0728 2007-01-24
  • 打赏
  • 举报
回复
支持楼主! 华为的面试题里面就有一道这样的改错题! 
  删除偶数:  看了楼主这个帖子就能OK了~! 顶顶顶~!!!!!
void DeleteEvent(std::vector <int> &intVect)
{
std::vector
...
...
...
}
rularys 2007-01-24
  • 打赏
  • 举报
回复

正在学习C++,先占个位置。LZ辛苦
xiaoliuzi 2007-01-24
  • 打赏
  • 举报
回复
hao
vcnewer 2007-01-24
  • 打赏
  • 举报
回复
呵呵
hurryboylqs 2007-01-23
  • 打赏
  • 举报
回复
支持楼主,精神可嘉。
yydrewdrew 2007-01-23
  • 打赏
  • 举报
回复
收藏!
myfriend023 2007-01-23
  • 打赏
  • 举报
回复
study
qiujian5628 2007-01-23
  • 打赏
  • 举报
回复
jfmark来了
xdlous 2007-01-23
  • 打赏
  • 举报
回复
看看
pomelowu 2007-01-23
  • 打赏
  • 举报
回复
低调mark

16,465

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC相关问题讨论
社区管理员
  • 基础类社区
  • Web++
  • encoderlee
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

        VC/MFC社区版块或许是CSDN最“古老”的版块了,记忆之中,与CSDN的年龄几乎差不多。随着时间的推移,MFC技术渐渐的偏离了开发主流,若干年之后的今天,当我们面对着微软的这个经典之笔,内心充满着敬意,那些曾经的记忆,可以说代表着二十年前曾经的辉煌……
        向经典致敬,或许是老一代程序员内心里面难以释怀的感受。互联网大行其道的今天,我们期待着MFC技术能够恢复其曾经的辉煌,或许这个期待会永远成为一种“梦想”,或许一切皆有可能……
        我们希望这个版块可以很好的适配Web时代,期待更好的互联网技术能够使得MFC技术框架得以重现活力,……

试试用AI创作助手写篇文章吧