迭代器简介
迭代器(iterator)是一个可以对其执行类似指针的操作(如:解引用 operator*() 、自增 operator++() 的对象,我们可以将它理解成为一个指针,但它又不是我们所谓普通的指针,我们可以称之为广义指针。
相比顺序结构(vector、string等),list无法通过[i]的方式进行随机访问,迭代器的出现便方便了链表等非顺序数据结构的遍历操作。
vector函数内部发生迭代器失效
我们在使用vector等容器或者模拟实现这些容器时,通常会出现迭代器失效问题,即函数外部或函数内部的迭代器所指向的内容发生了变化。
考察以下代码
// vector.h
iterator insert(iterator pos, const T& x)
{
assert(pos >= _start);
assert(pos <= _finish);
iterator tmp = begin();
reserve(capacity() == 0 ? 4 : 2 * capacity());
for (iterator cur = end(); cur > pos; --cur)
{
*cur = *(cur - 1);
}
*pos = x;
++_finish;
return pos;
}
// test.cpp
void test_vector1()
{
zyh::vector<int> v1(3, 1);
v1.insert(v1.begin(), 1);
v1.insert(v1.begin(), 1);
v1.insert(v1.begin(), 1);
v1.print_vector();
}
int main()
{
test_vector1();
return 0;
}
运行正常
但是往v1里多 insert 一个数据时,程序就出现了问题。
通过调试发现,多 insert 一个数据时发生了扩容,但是没有及时更新 pos,导致迭代器 pos 出现了失效。
解决方法:扩容之后对 pos 进行更新即可。
若数据结构产生扩容,需要及时更新对应的迭代器。
vector函数外部发生迭代器失效
考察以下代码,test_vector2 函数中对 v1 里小于0的值进行了删除操作。
void test_vector2()
{
zyh::vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(-1);
v1.push_back(-2);
v1.push_back(3);
v1.push_back(4);
v1.push_back(5);
v1.push_back(6);
v1.print_vector();
zyh::vector<int>::iterator it = v1.begin();
while (it != v1.end())
{
if (*it < 0)
{
v1.erase(it);
}
++it;
}
v1.print_vector();
v1.print_vector();
}
int main()
{
test_vector2();
return 0;
}
运行结果如下
在 erase 的过程中,将 it 后面的数据往前进行了拷贝,但同时对 it 进行了++操作,迭代器失效,导致略过了 -2;此外,在SGI版本的STL中,可能会在 insert 或 erase 后进行缩容,导致 it 所指的空间被提前释放,这种代码在不同的平台下具有偶发性。
解决方法:充分利用 insert 或 erase 的返回值。
zyh::vector<int>::iterator it = v1.begin();
while (it != v1.end())
{
if (*it < 0)
{
it = v1.erase(it);
}
else
{
++it;
}
}
结论
-
reserve 的迭代器失效问题,扩容后地址可能发生变化,原先的迭代器就指向了无效空间,导致迭代器失效。
-
不同编译器可能在 erase 后采取缩容或者不缩容的策略,如果缩容,就会出现迭代器失效问题。
-
insert 和 erase 后形参 pos 可能失效,insert 和 erase 过的迭代器不要使用。