vector erase()函数的注意事项

      在STL中用好迭代器是很关键的事情,虽然下面的注意事项十分简单,但是对于初学STL来说也是一个雷点,简单写两句记录一下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>
#include <vector>

using namespace std;

int removeDuplicates(vector<int>& nums){
vector<int>::iterator pos1;
pos1=nums.begin();
vector<int>::iterator pos2;
while(pos1!=nums.end())
{
for(pos2=pos1+1;(*pos2==*pos1&&pos2!=nums.end());pos2++){}
pos1= nums.erase(pos1+1,pos2);
}
return nums.size();
}

int main()
{
vector<int> example{1,1,2,3,4,4};
cout<<removeDuplicates(example);
return 0;
}

上面是一道LeetCode上比较简单的问题,问题的要求是对于一段有序数列,把其中重复的数删除。(上面给出的代码是已经正确的代码)
写的时候尝试了迭代器的用法,但在使用erase()函数时没有在意,出现了类似于野指针的错误。

在原先的错误代码中有一段是这样写的:

1
2
3
4
5
6
 while(pos1!=nums.end())
{
for(pos2=pos1+1;(*pos2==*pos1&&pos2!=nums.end());pos2++){}
nums.erase(pos1+1,pos2);
pos1=pos2;
}

这里我错误地认为pos2迭代器的指向不会受到erase()的影响,但实际上运行之后会出现pos2指向错误,导致循环中下一次使用pos2时候循环出错。

简单查了一下erase()的介绍:
删除迭代器所指向的元素,返回一个指向被删元素之后元素的迭代器,若迭代器指向尾元素,则返回尾后迭代器,若迭代器是尾后迭代器,则会产生未定义行为。

这样主要需要避免两种错误:
1.避免迭代器指向失效,也就类似野指针。
2.erase()返回的本来就已经是被删除元素之后的那个元素的地址了,注意有时候不应该再iterator++。

对于第二点举例如下:

1
2
3
4
5
for (iter = vec.begin(); iter != vec.end(); iter++)  
{
if (*iter == 5)
iter = vec.erase(iter);
}

这时候如果例子是1,2,2,5,5,6。运行之后会变成1,2,2,5,6。会发现本意是把等于5的元素全部删除,但少删除了一个。

正确的写法应该是这样:

1
2
3
4
5
6
7
for (iter = vec.begin(); iter != vec.end(); )  
{
if (*iter == 5)
iter = vec.erase(iter);
else
iter++;
}