请问STL中vector的迭代器为什么会失效?

std::vector<int>v{ 0, 1, 2, 3 };

    for (auto i = v.begin(); i != v.end(); i++)
    {
        cout << *i << endl;
        v.erase(i);
        cout << *i << endl;  //报错的地方
    }

这段代码在VS2013上运行会在第二处cout处报错,如下:

clipboard.png
根据错误提示,迭代器不能被解引用,也就是说这里不能使用“*i”?那么为什么第一处*i不会报错,而第二处就会报错呢?这是迭代器失效导致的,那么为什么迭代器会失效呢?

都说迭代器失效的办法就是用erase的返回值来更新i,但是我看了一下SGI STL中erase的源码:

iterator erase(iterator __position) {
    if (__position + 1 != end())
      copy(__position + 1, _M_finish, __position);
    --_M_finish;
    destroy(_M_finish);
    return __position;
  }

整个过程也没有修改过position啊,只是把后面的数据拷贝到前面来了,并没有涉及到内存的重新分配(VS STL中源码也是类似的做法)我还把erase的返回值和实参进行比较,也都完全相同,为什么用erase的返回值更新迭代器就有用,不更新迭代器就失效了呢?

看了好几篇文章,似乎都把迭代器失效看作一个规定,也没有解释具体原因。那么失效的具体原因又是什么呢?

阅读 4.9k
3 个回答
  1. 你使用的是 VS2013,你看的源码是 SGI STL,就算要看也要看 VS2013 的源码
  2. 对一个已删除的迭代器进行解引用是未定义的行为
  3. 为什么你要这样用迭代器?就像你说的,应该使用 i = v.erase(i); 来更新迭代器,即使这样使用,也要先判断它是不是等于 v.end(), 才能决定能不能对其进行解引用
看了好几篇文章,似乎都把迭代器失效看作一个规定,也没有解释具体原因。那么失效的具体原因又是什么呢?

它本来就是一个规定。因为删除之后,原来的 iterator 所指向的元素已经不存在了,没有任何办法对它解引用做一个合理的定义,所以做了如此的一个规定。iterator 是直接指向元素的,iterator 不是下标。

至于具体实现上“失效”的“原因”,STL 有很多实现,“失效”的原因每一个都会不相同。(而且,这是一个未定义行为,就算有些实现“不失效”,也没有问题 ...)

微软最近刚刚开源了自己用的 STL ,它的 vector::erase 在这里。

不过这依然应该不是 VS2013 用的 STL ,每一版 VS 的 STL 都会有所不同。

要了解你所用的 STL 里为啥会失效,得先找到你的 VS2013 里的的实现。文件名已经在提示消息里了。

因为vector是连续动态内存的封装,而其迭代器就是指针的简单包装,当vector中的元素被删除后,因为要保证元素连续排列,因此删除元素后面的元素都要向前移动,另一方面,当vector中不断插入元素,预申请的内存不够用,则需要重新申请更大的内存,然后将元素都move或者copy过去,这个过程也会导致指针指向的内存已经不再存储该vector的元素了,所以索性让vector的元素只要有变化,则让迭代器失效,这样会保证正确性。
你说能不能实现vector变化过程中不失效的迭代器呢,我觉得未必不能,但那样的话迭代器就不能简单的封装一下指针了,为此付出的空间和时间代价不符合stl的定位。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题