1.迭代器失效是什么及原因
迭代器失效是指:如果失效就不能再使用这个迭代器,如果使用了,结果就是未定义的
原因:迭代器失效的主要原因是因为野指针,也可能是因为迭代器的意义改变了
2.insert导致迭代器失效
void insert(iterator pos, const T& x)
{
assert(pos >= _start);
assert(pos <= _finish);
if (_finish == _endofstorage)
{
//size_t len = pos - _start;//迭代器失效第一种:扩容引起的,野指针
reserve(capacity() == 0 ? 4 : capacity() * 2);
//pos = _start + len;
}
iterator end = _finish - 1;
while (end >= pos)
{
*(end + 1) = *end;
--end;
}
*pos = x;
++_finish;
}
看下面的测试用例
第一次头插,请注意那个pos
第二次头插后,_start那几个都变了,pos没有变,这是因为发生了扩容,但是pos迭代器指向的还是那个老空间的位置,所以变成了野指针,发生了错误。
让我们来看reserve
void reserve(size_t n)
{
if (n > capacity())
{
size_t sz = size();
T* tmp = new T[n];//开辟新空间
if (_start)
{
memcpy(tmp, _start, sizeof(T) * sz);//拷贝原空间数据
delete[] _start;//释放
}
_start = tmp;//变为新空间
_finish = _start + sz;
_endofstorage = _start + n;
}
}
针对这种情况,我们可以扩容后重新设置pos指向
void insert(iterator pos, const T& x)
{
assert(pos >= _start);
assert(pos <= _finish);
if (_finish == _endofstorage)
{
size_t len = pos - _start;//迭代器失效第一种:扩容引起的,野指针
reserve(capacity() == 0 ? 4 : capacity() * 2);
pos = _start + len;//扩容后重新设置pos指向
}
iterator end = _finish - 1;
while (end >= pos)
{
*(end + 1) = *end;
--end;
}
*pos = x;
++_finish;
}
提出第二个问题,下面insert后那个it还可以使用吗?
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.push_back(5);
v.push_back(6);
v.push_back(7);
v.push_back(8);
vector<int>::iterator it = find(v.begin(), v.end(), 1);
if (it != v.end()) {
v.insert(it, 2);
}
for (auto e : v) {
cout << e << " ";
}
cout << endl;
答案是不可以的
————因为pos是形参,函数内形参的改变对外面的实参没有影响,调整了pos不代表外面实参it也会调整。
即使编译器没有报错,也不要继续使用
3.erase引发的迭代器失效
看下面清除vector中的偶数
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
std::vector<int>::iterator it = v.begin();
while (it != v.end())
{
if ((*it) % 2 == 0)//清除偶数
{
v.erase(it);
}
++it;
}
for (auto e : v) {
cout << e << " ";
}
cout << endl;
会直接报错,因为
而且当删除(获取增加)it位置的元素时,导致it后面的迭代器全部失效。因此多次调用erase insert导致崩溃
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
std::vector<int>::iterator it = v.begin();
while (it != v.end()) {
if ((*it) % 2 == 0) {
it = v.erase(it);//erase后更新一次
}
else
{
++it;
}
}
for (auto e : v) {
cout << e << " ";
}
cout << endl;
iterator erase(iterator pos)//返回被删除位置的下一个位置
{
assert(pos < _finish);
assert(pos >= _start);
iterator it = pos + 1;
while (it < _finish)
{
*(it - 1) = *it;
++it;
}
--_finish;
return pos;
}
//结论:insert和erase以后迭代器都失效了,不能再访问
4.总结
1 当容器调用erase时,当前位置到容器末尾元素的所有的迭代器全部失效
2 当容器调用insert时,当前位置到容器末尾元素的所有的迭代器全部失效;
3 当容器调用insert时,如果引起容器内存扩容,原来容器的所有的迭代器就全部失效
解决办法:
进行更新操作:erase(insert)后会返回指向下一个元素的迭代器