當一個容器變化時,指向該容器中元素的迭代器可能失效。這使得在迭代器變化期間改變容器容易出現問題。在這方面,不同的容器提供不同的保障:
vectors: 引起內存重新分配的插入運算使所有迭代器失效,插入也使得插入位置及其后位置的迭代器失效,刪除運算使得刪除位置及其后位置的迭代器失效.
vector的push_back操作 可能沒事,但是一旦引發內存重分配,所有迭代器都會失效;
vector的insert操作 插入點之后的所有迭代器失效;但一旦引發內存重分配,所有迭代器都會失效;
vector的erase操作 插入點之后的所有迭代器失效;
vector的reserve操作 所有迭代器失效(因為它導致內存重分配);
list/map: 插入不會使得任何迭代器失效;刪除運算使指向刪除位置的迭代器失效,但是不會失效其他迭代器.
deque的insert操作 所有迭代器失效;
deque的erase操作 所有迭代器失效;
1. 對于
關聯容器(如map, set, multimap,multiset),刪除當前的iterator,僅僅會使當前的iterator失效,只要在erase時,遞增當前iterator即可。這是因為map之類的容器,使用了紅黑樹來實現,插入、刪除一個結點不會對其他結點造成影響。erase迭代器只是被刪元素的迭代器失效,但是返回值為void,所以要采用
erase(iter++)的方式刪除迭代器。
for (iter = cont.begin(); it != cont.end();)
{
(*iter)->doSomething();
if (shouldDelete(*iter))
cont.erase(iter++);
else
++iter;
}
2. 對于
序列式容器(如vector,deque),刪除當前的iterator會使后面所有元素的iterator都失效。這是因為vetor,deque使用了連續分配的內存,刪除一個元素導致后面所有的元素會向前移動一個位置。所以不能使用erase(iter++)的方式,還好erase方法可以返回下一個有效的iterator。
for (iter = cont.begin(); iter != cont.end();)
{
(*it)->doSomething();
if (shouldDelete(*iter))
iter = cont.erase(iter); else
++iter;
}
3. 對于
list來說,它使用了不連續分配的內存,并且它的erase方法也會返回下一個有效的iterator,因此
上面兩種方法都可以使用。刪除數組中某個元素后連續重復的元素,例如 1,1,2,3,3,1,1,1,4,0 ---> 1, 2,3,1,4,0。給出問題的一個正確的實現:
#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, 0};
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;
}
PS. 不過實際上這個問題,用vector來實現不是很適合,因為每次刪除一個元素,都會引起vector的一個resize操作。resize的時間復雜度是O(n),整個的resize操作要花費O(n^2)。最好是選擇list最為容器,list最適合那些需要在容器中間做插入、刪除的例子。