STL容器的删除元素问题

来源:互联网 发布:网络低俗词 编辑:程序博客网 时间:2024/05/22 11:57

       STL的容器分为两类,一类是序列式容器,即数据顺序连续存储,如:vector、deque;另一类是关联式容器,即数据不连续存储,如:map、list、set。对于STL容器的数据删除操作,有一些需要注意的地方。

1、序列式容器的数据删除

       下面例子以容器list为例:

for (list<int>::iterator iter = lst.begin(); iter != lst.end(); iter++){    if (符合条件)    {         lst.erase(iter);    }}

       上面的代码,乍一看好像是没有什么问题,一个再普通不过的for循环而已,怎么会有问题呢?问题就是出在erase函数上了,对于关联式容器,erase函数会删除传入的迭代器指向的元素,并回收迭代器指向的内存空间,此时该迭代器已经失效。这下问题就清晰了,上面的代码若进行了erase操作后,迭代器iter已经失效了,而for循环的iter++接着会对一个已失效的迭代器iter进行++操作,这种操作不可预期,可能会造成程序crash。
       正确的使用方法有以下两种:
方法一:
       当erase函数执行完之后,会返回下一个有效节点,因此可以使用以下方法

for (list<int>::iterator iter = lst.begin(); iter != lst.end();){    if (符合条件)    {        iter = lst.erase(iter);    }    else    {        iter++;    }}

方法二:
       下面写法中,iter的后置++操作会完成两件事,第一是返回iter的一个副本,这个副本和iter本身都指向同一内存地址;第二是对iter自身进行了++操作,此时iter还是有效的,而且指向下一个有效节点。而副本则作为实参传给了erase函数,在erase函数中,副本所指的内存空间会被回收,副本会失效。可以简单理解为,在iter被删除之前就已经执行了++操作。

for (list<int>::iterator iter = lst.begin; iter != lst.end();){    if (符合条件)    {        lst.erase(iter++);    }    else    {        iter++;    }}

2、序列式容器的数据删除

       对于序列式容器vector、deque的数据删除操作,也有一些点需要注意的,以vector为例,下面代码目标是删除vector中满足条件的数据:

vector<int> vec;//插入数据for (int i = 0; i < 5; ++i){    vec.push_back(i);}//过滤并打印数据for (vector<int>::iterator iter = vec.begin(); iter != vec.end(); ++iter){    //删除值为0的数据    if (0 == *iter)    {        vec.erase(iter);    }    else    {        cout<<*iter<<"\t";    }}
       我们期望的输出结果应该是:1 2 3 4

       可是,上面代码实际输出结果却是:2 3 4,为什么会出现这种情况呢?这就涉及到容器vector本身数据结构的特点和erase()函数的作用问题了。由于vector是序列式容器,数据在其中是连续存储的,当调用了erase()函数删除其中的某个元素时,会将删除元素的下一个有效元素覆盖删除元素的所在位置的数据。
       这下子,对于上面代码的输出结果就清晰了,当0元素被删除后,iter已经指向了1元素,而for循环的++iter操作则将iter再递增一次,使得iter指向了2元素,导致跳过了1元素,因此就出现了上面的输出结果了。假如有两个0元素连续存放,还会跳过第二个0元素的,导致没有完全过滤掉满足条件的数据,这个问题算是比较难发现的。

       上面的代码可能会导致满足条件的数据没有被删除,但是在某些情况下,可能还会出现coredump问题,还是针对上面的例子,将上面代码的“过滤并打印数据”部分替换为以下代码,即删除值为4的数据:

//过滤并打印数据for (vector<int>::iterator iter = vec.begin(); iter != vec.end(); ++iter){    //删除值为4的数据    if (4 == *iter)    {        vec.erase(iter);    }    else    {        cout<<*iter<<"\t";         }}

       二话不说,程序直接segmentation fault。当执行完erase之后,iter已经指向vec.end(),而for循环的++iter,再将iter递增一次,此时iter越界了,当执行*iter时,就导致了非法访问内存,程序crash。

29 0
原创粉丝点击