STL中迭代器失效详解

来源:互联网 发布:mac 文件 改名 编辑:程序博客网 时间:2024/06/06 19:59

1.迭代器

   迭代器是一种面向对象的广义指针,用于指向容器中或流中的对象。可以看做是一种指向数据的指针。

2.迭代器失效

    向容器中添加或者删除元素的操作可能会使指向容器元素的迭代器失效,失效的迭代器将不指向任何元素。    一般有两种情况无法通过迭代器++操作遍历整个stl容器; 无法通过迭代器存取迭代器所指向的内存。 

3.vector一些操作使迭代器失效

3.1使用erase不恰当是迭代器失效:

删除容器中的偶数
测试用例:

#include  <iostream>#include  <vector>using namespace std;void Printvector(vector<int> &l){    vector<int>::iterator it = l.begin();    while (it != l.end())    {        if (*it % 2 == 0)        {            //l.erase(it);    //(1)   1  输出之后崩溃            //it=l.erase(it); //(2)   1 3 5 输出之后崩溃        }        cout << *it << " ";        ++it;    }    cout << endl;}void Testvector(){    vector<int> l;    l.push_back(1);    l.push_back(2);    l.push_back(3);    l.push_back(4);    l.push_back(5);    l.push_back(6);    Printvector(l);}int main(){    Testvector();    return 0;}

(1)程序输出1之后就崩溃的原因:

通过调试观察现象:
这里写图片描述

   明显可以看到使用erase删除某一个结点之后,vector迭代器虽然还是指向当前位置,而且也引起了元素前挪,但是由于删除结点的迭代器就已经失效,指向删除点后面的元素的迭代器也全部失效,所以不能对当前迭代器进行任何操作;需要对迭代器重新赋值或者接收erase它的返回值;

(2)程序输出1 3 5 之后就崩溃的原因:

通过调试观察现象:
这里写图片描述

   使用it接收erase的返回值,假如vector最后一个元素为奇数按照(2)的方式运行程序是不会崩溃的,这里崩溃的原因就好像数组访问越界,因为元素6已经被删除,不属于vector(数组)了,而你还继续对指向它的指针解引用,所以程序崩溃。

STL中erase实现的源代码:

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

如何解决上述问题:

void Printvector(vector<int> &l){    vector<int>::iterator it = l.begin();    while (it != l.end())    {        if (*it % 2 == 0)        {            it = l.erase(it);        }        else        {            cout << *it << " ";            ++it;        }    }    cout << endl;}

3.2使用push_bakc导致迭代器失效:

测试用例2:每次发现一个偶数就尾插一个0

void Printvector(vector<int> &l){    vector<int>::iterator it = l.begin();    while (it != l.end())    {        if (*it % 2 == 0)        {            l.push_back(0);        }        cout << *it << " ";        ++it;    }    cout << endl;}void Testvector(){    vector<int> l;    l.push_back(1);    l.push_back(2);    l.push_back(3);    l.push_back(4);    l.push_back(5);    Printvector(l);}
   想要了解push_back为什么失效,就要了解一下vector的增容方式,经过调试可以发现vector的增容方式为: 0  1  2  3  4  6 ... 从第三项开始基本遵循capacity=capacity+capacity/2这个规律;而push_back是没有返回值的。

这里写图片描述

    当插入(push_back)一个元素后,capacity的返回值与没有插入元素之前相比有改变,进行了深拷贝,而迭代器所指向的位置已经被析构函数释放所以导致迭代器失效。而当你在不增容的情况下尾插数据并不会导致迭代器失效。insert使迭代器失效的与push_back类似。

3.3使用end返回的迭代器

      使用了end返回的迭代器,而导致迭代器没有更新,使得程序崩溃。

这里写图片描述

4.容器list的一些操作使迭代器失效

4.1删除元素eraser导致指向被删除元素的迭代器、指针引用失效

   测试用例,删除当前迭代器,并不会对指向容器其他位置的迭代器造成影响
#include  <iostream>#include <list>using namespace std;void Printlist(list<int> &l){    list<int>::iterator it = l.begin();    list<int>::iterator it1 = l.begin();    list<int>::iterator it2 = l.begin();    it1++;    while (it != l.end())    {        if (*it % 3 == 0)        {            l.erase(it1);        }        cout << *it << " ";        ++it;    }}void Testlist(){    list<int> l;    l.push_back(1);    l.push_back(2);    l.push_back(3);    l.push_back(5);    l.push_back(7);    Printlist(l);}int main(){    Testlist();    return 0;}

这里写图片描述

   这是因为list的为链式结构,且为双向循环链表,删除一个元素并不会像vector那样需要挪动数据,只需要将删除结点的两端重新链接就行了,所以不会造成指向容器其他位置的迭代器失效。

5.总结

1.对于list中只有元素的删除操作会导致指向该元素的迭代器失效,其他元素迭代器不受影响。

2.对于vector元素的删除、插入操作会导致指向该元素以及后面的元素的迭代器失效。

2.1当插入(push_back)一个元素后,end操作返回的迭代器肯定失效。
2.2当插入(push_back)一个元素后,capacity返回值与没有插入元素之前相比有改变,则需要重新加载整个容器,此时first和end操作返回的迭代器都会失效。不增容不会导致迭代器失效。
2.3当进行删除操作(erase,pop_back)后,指向删除点的迭代器全部失效;指向删除点后面的元素的迭代器也将全部失效。
0 0