迭代器(iterator)是STL库的一大组件,它可以让我们更为便利的对容器中的元素进行操作。从使用层面上讲,它的操作十分类似于指针。例如我们可以使用解引用操作,取得迭代器位置的元素。或者是自增操作,让迭代器指向下一个元素。
在vector容器中,迭代器的底层实际就是指针变量。因为vector本身在物理层面就是线性结构,所以可以很好的和指针的操作兼容。
但是,我们在使用迭代器时,常常会遇到迭代器失效的情况。一旦迭代器失效,就可能出现各种意想不到的错误,下面,我们就来分析迭代器失效的原因,以及迭代器失效的解决办法。
我们在使用insert函数对vector容器进行操作时,需要进行如下调用:
iterator insert (iterator position, const value_type& val);
而迭代器失效就是指传入的position迭代器在经过insert操作后变得不可用。
举个例子:
int main()
{
vector v;
v.push_back(1);
v.push_back(2);
v.push_back(3);//v有三个元素 1,2,3
vector::iterator it = find(v.begin(),v.end(),3);
//找到3并用迭代器it记录该位置
v.insert(it, 8);//在3的位置插入一个8
cout << *it << endl;//输出it位置的元素
return 0;
}
运行上面这段代码,我们会发现程序崩溃了。这是为什么呢?
因为在insert时,vector可能需要进行扩容,而扩容的本质是new一块新的空间,再将数据迁移过去。而我们知道,迭代器的本质是指针,而插入后,若vector扩容,则原有的数据被释放,指向原有数据的迭代器就成了野指针,所以迭代器失效了。
而解决的办法很简单,insert函数提供了返回值,这个返回值是指向插入之后的val的迭代器。我们只需要保存返回的迭代器,并使用这个新的迭代器即可。
另外,插入操作会导致所有的迭代器失效,因为所有的数据都被迁移,原空间的所有迭代器都成了野指针。
使用erase函数删除指定迭代器位置的元素,需要进行如下调用:
iterator erase (iterator position);
下面我们用一个例子来说明erase产生的迭代器失效:
int main()
{
vector v;
v.push_back(1);
v.push_back(1);
v.push_back(1);
v.push_back(1); //此时v的元素为 1,1,1,1
vector::iterator it0 = v.begin();
vector::iterator it1 = v.begin() + 1;
vector::iterator it2 = v.begin() + 2;
vector::iterator it3 = v.begin() + 3;//四个迭代器分别指向下标为0 1 2 3的元素
v.erase(it2);//erase掉it2迭代器位置的元素
cout << *it0 << endl;
cout << *it1 << endl;
cout << *it2 << endl;
cout << *it3 << endl;//对四个迭代器指向的元素进行输出
return 0;
}
我们逐语句进行调试,会发现,it0和it1的值是可以打出来的,而到了it2,程序就会崩溃。
那么,这是为什么呢?其实,在erase后,VS编译器会判定迭代器失效,因为迭代器位置的元素被删除后,不再指向删除前的元素,所以迭代器失效了。故程序运行到输出it2位置,就崩溃了。
int main()
{
vector v;
v.push_back(1);
v.push_back(1);
v.push_back(1);
v.push_back(1);
vector::iterator it0 = v.begin();
vector::iterator it1 = v.begin() + 1;
vector::iterator it2 = v.begin() + 2;
vector::iterator it3 = v.begin() + 3;
v.erase(it2);
cout << *it0 << endl;
cout << *it1 << endl;//q去掉了删除it2位置元素的操作
cout << *it3 << endl;
return 0;
}
接着,我们把输出it2的语句删除,但是我们运行到输出it3所指向的元素时,程序仍然崩溃了。这是因为,删除操作后,由于删除位置后面的数据都要向前挪一位,导致了删除位置后面的迭代器所指向的元素都发生了变化,所以处于it2位置之后的it3,也在所难免的失效了。
解决erase操作导致的迭代器失效的办法也很简单,erase同样会返回一个迭代器,这个迭代器指向了在erase操作前,被删除元素的下一个元素的新位置。所以,我们进行erase操作后,直接使用这个返回的迭代器即可。