STL容器的删除元素问题
STL的容器分为两类,一类是序列式容器,即数据顺序连续存储,如:vector、deque;另一类是关联式容器,即数据不连续存储,如:map、list、set。对于STL容器的数据删除操作,有一些需要注意的地方。
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。
更多推荐
所有评论(0)