STL容器删除元素的陷阱

今天看Scott Meyers大师的stl的用法,看到了我前段时间犯的一个错误,发现我写的代码和他提到错误代码几乎一模一样,有关stl容器删除元素的问题,错误的代码如下:
std::vector<struct> mFriendList;
...
std::vector<struct>::iterator iter = mFriendList.begin();
for ( ; iter != mFriendList.end(); ++iter)
{
    if (...)
        mFriendList.erase(iter);
}
记得当时Once给我说过这个问题,还给我改过代码,我当时不明白为什么,只知道程序执行的时候如果if为true那么程序就肯定会崩溃。
大师的说法是:当容易中的一个元素被删除时,指向该元素的所有迭代器都变得无效。上面的代码中,只要执行了erase(iter),那么iter就会变得无效,那么执行++iter就肯定会出错。

在网上看到有人总结如下两条:
1. 对于节点式容器(map, list, set)元素的删除,插入操作会导致指向该元素的迭代器失效,其他元素迭代器不受影响
2. 对于顺序式容器(vector,string,deque)元素的删除、插入操作会导致指向该元素以及后面的元素的迭代器失效

总结了一下,并回想Once当时给我改的代码,所以正确的写法应该是这样的:
1.对于节点式容器
std::list<struct> mList;
...
std::list<struct>::iterator iter = mList.begin();
for ( ; iter != mList.end(); )
{
    if (...)
    {
        //因为节点式只会导致当前节点迭代器失效,所以删除节点的同时对迭代器进行后移的操作,因为其他元素不会失效
        mList.erase(iter++);
    }
    else
    {
        ++iter;
    }
}

2.对于顺序式容器
std::vector<struct> mVector;
...
std::vector<struct>::iterator iter = mVector.begin();
for ( ; iter != mVector.end(); )
{
    if (...)
    {
        //这里就比较有说法了,因为顺序式容器会使本身和后面的元素迭代器都失效,所以不能简单的++操作
        //这里顺序式容器的erase()会返回紧随被删除元素的下一个元素的有效迭代器
        //而节点式容器的erase()的返回值是void,这点我感觉太神奇了,确实太神奇了!!!!
        iter = mVector.erase(iter);
    }
    else
    {
        ++iter;
    }
}

STL容器删除元素的陷阱(原)http://hsw625728.blog.163.com/blog/static/3957072820091189254690/

上面的文章总结的真的很好,包括总结的方式,结合实际,说的够全面。

最后根据自己的理解总结下:

需要注意的是:顺序式容器会使本身和后面的元素迭代器都失效,所以不能简单的++操作。

还有一点需要特别注意的是:使用erase操作删除单个元素必须确保该元素确实存在,--如果删除指向超出末端的下一位置的迭代器,那么erase操作的行为未定义,导致crash。— c++primer 特别指出。

vector<string> vStr;
vector<string>::iterator it ;

...
for ( it= vStr.begin();it != vStr.end();++it)
{
   if (...)//这里条件为真的如果不是容器中最后一个元素,则不会引起crash。
   {        //但是如果是容器中的最后一个元素,必然会crash。
    it= vStr.erase(it);//原因是这里it返回的最后一个元素的下一个迭代器即end迭代器,
   }//这个并没有错,错的是在最后对指向end迭代器的迭代器进行自加操作得到的迭代器是无效的,
}

所以正确的方法是:

vector<string> vStr;
vector<string>::iterator it ;

...
for ( it= vStr.begin();it != vStr.end(); )
{
    if (...)
    {
        it= vStr.erase(it);//
    }
    else
    {
        ++it;
    }
}

给两个实例:

#include <iostream>
#include <string>
#include <vector>
using namespace std;


void main()
{
vector<string> vStr;

vStr.push_back("a");
vStr.push_back("b");
vStr.push_back("c");
vStr.push_back("d");
vStr.push_back("e");

vector<string>::iterator it ;

for ( it= vStr.begin();it != vStr.end(); it++)
{
   cout<< *it<<endl;
}

//删除某个元素
for ( it= vStr.begin();it != vStr.end(); )
{
   if ((*it) == "e")
   {
    it= vStr.erase(it);
    //cout<<"the c is in the vector"<<endl;
   }
   else
    ++it;
}
cout<<"after erase the vector:"<<endl;
//删除后
for ( it= vStr.begin();it != vStr.end(); it++)
{
   cout<<*it<<endl;
}

system("pause");
}

--------------------------------------------------------------------

#include <iostream>
#include <vector>
using namespace std;

int main(int argc, char* argv[])
{
int a[] = {1, 1, 3, 3, 3, 2, 4, 1, 1, 1, 1};
int size = sizeof(a)/sizeof(a[0]);

vector<int> vec(a, a+size);

vector<int>::iterator iter = vec.begin();
int previous = *iter;
++iter;
for (; iter != vec.end();)
{       
   if(*iter == previous)
   {
    iter = vec.erase(iter);
   }
   else
   {
    previous = *iter;
    ++iter;
   }   
}

for(iter = vec.begin(); iter != vec.end(); ++iter)
{
   cout << *iter << endl;
}

return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值