vector迭代器失效的问题及解决

        对于迭代器,其实本质上就是一个指针,那么当这个指针指向的位置已经不是我们想要的位置时,我们认为这个迭代器失效了。

一.迭代器失效的场景

        关于迭代器失效,主要有两种场景:

        1.底层空间发生改变

        底层空间发生改变即当我们获取了迭代器之后,又对底层的空间进行了操作使其发生了改变,如resize、reserve、insert、push_back等。这样一旦发生了扩容,导致原空间被释放,那么这个迭代器显然失效了。

#include 
using namespace std;
#include 
int main()
{
    vector v{1,2,3,4,5,6};
 
    auto it = v.begin();
 
 // 将有效元素个数增加到100个,多出的位置使用8填充,操作期间底层会扩容
    v.resize(100, 8);
 
 // reserve的作用就是改变扩容大小但不改变有效元素个数,操作期间可能会引起底层容量改变
    v.reserve(100);
 
 // 插入元素期间,可能会引起扩容,而导致原空间被释放
    v.insert(v.begin(), 0);
    v.push_back();


//此时进行上面这些操作之后,如果使用迭代器打印,便可能发生报错
    while(it != v.end())
    {
        cout<< *it << " " ;
        ++it;
    }
    cout<

        2.指定位置元素的删除--erase

        对于删除元素的操作,并不会发生底层空间的改变,看似迭代器还指向着原来的位置,没有发生什么问题。但迭代器是和空间绑定的吗?并不是的,迭代器绑定的对象应该是元素!那么通过迭代器删除元素之后,迭代器本来指向的元素已经消失,此时指向的只是因为补齐而来的元素。那么,就出现了两个问题:(1)删除操作完成后,迭代器若++,便跳过了补齐来的元素。(2)而如果删除的是最后一个元素,补齐来的元素是end,而end没有数据,解引用时便会报错。而且如果编译器检查不仔细的话,对失效的迭代器++,迭代器便错过了end,就有可能形成死循环的问题。

#include 
using namespace std;
#include 
int main()
{

   int a[] = { 1, 2, 3, 4 };
   vector v(a, a + sizeof(a) / sizeof(int));
   // 使用find查找3所在位置的iterator
   vector::iterator pos = find(v.begin(), v.end(), 3);
   // 删除pos位置的数据,导致pos迭代器失效。
   v.erase(pos);
   cout << *pos << endl; // 此处会导致非法访问
   return 0;
}

        3.被其他操作牵连的失效

        这种情况与list的迭代器失效场景形成鲜明的对比,因为vector的空间连续,所以一旦出现了数据挪动的情况,其他的数据也要随之挪动,而此时若有迭代器指向被牵连的区域,那么因为迭代器指向的内容已经不是本来该指向的内容,我们认为这个迭代器也失效了。如下图:

        vector迭代器失效的问题及解决_第1张图片

        可以看到other指向的内容已经发生了改变,所以该迭代器失效。

二.迭代器失效解决方法

        既然这些情况会使迭代器发生失效,那么该如何解决呢?

        首先,第一个方法,便是不使用这些可能发生迭代器失效的函数,当然这显得不符合现实。

        所以,第二个方法,发生迭代器失效后,在使用之前,重新对迭代器进行赋值即可。就如开文的那句话,我们让迭代器再次指向我们想要的位置即可。如面对第一种情况时,重新调用begin即可,第二种情况,删除之后,重新获取迭代器进行操作,如果是在循环中删除,觉得重新赋值太过浪费,那么可以保存删除之前未失效的迭代器,在删除之后启用未失效的迭代器即可,这其实也是一种另类的重新赋值。

        至此,总的来说,小范围迭代器失效解决很容易,但大范围的迭代器失效就显得十分繁琐了。所以以此篇博客介绍vector的迭代器失效场景,望看过后在编程时不会出现迭代器失效的bug!

你可能感兴趣的:(MyC/C++,c++)