面试题:vector迭代器什么时候会失效?

在vector中,我们经常会使用迭代器iterator对vector中的元素进行索引,也经常需要将迭代器作为参数传递到vector的成员函数中,迭代器使用非常方便,但使用不当也会给我们带来巨大的麻烦,下面就深入分析vector迭代器失效的场景。

一、 push_back导致迭代器失效

vector在push_back的时候当容量不足时会触发扩容,导致整个vector重新申请内存,并且将原有的数据复制到新的内存中,并将原有内存释放,这自然是会导致迭代器失效的,因为迭代器所指的内存都已经被释放。
举例如下:

#include<iostream>
using namesspace std;
#include<vector>

int main()
  {
     vector<int> ta;  //定义一个容器
     ta.push_back(1);
     ta.push_back(2);
     vector<int>::iterator it = ta.begin();
  
     cout << "it的值是  " << *it << endl;
     cout << "容量是  " << ta.capacity() << endl;
 
     ta.push_back(3);
     ta.push_back(5);
 
     cout << "push_back后容量是   " << ta.capacity() << endl;
     cout << "此时it指向的值是    " << *it << endl;
     
     return 0;
 }

在Release模式下,这段代码是可以正常执行完成的,如下图所示:
在这里插入图片描述
但是Debug模式下,会抛出异常,如下图所示:
在这里插入图片描述
那么,导致release和debug模式下运行结果不一致的原因是什么呢?如下:
Release模式下能正常运行,是因为迭代器it指向的内存虽然被释放了,但是it保存的内存地址依然是有效的,这时候如果没有往这个地址对应的内存进行写操作的话,得到的结果自然是正确的,而C++并不会对这种情况做判断;
Debug模式下,会抛异常,是由于VC实现的STL中,对debug模式下的迭代器操作做了更为严格的处理,扩容时将迭代器赋值为了nullptr,自然会抛异常。稍后将会详细讲解。

二、insert导致迭代器失效

insert导致的迭代器失效有两种情况:
(1)插入操作导致vector扩容,迭代器失效原因和push_back相同
(2)插入操作引起vector内元素移动,导致被移动部分的迭代器失效
举例如下:

#include<iostream>
using namesspace std;
#include<vector>

int main()
{
    vector<int> ta;
    for (int i = 0; i < 13; i++)
    {
        ta.push_back(i);
    }
 
    vector<int>::iterator it = ta.begin();
 
    it += 5;
 
    cout << "容量是  " << ta.capacity() << endl;
    cout << "it的值是  " << *it << endl;
    ta.insert(it, 100);
 
    cout << "insert后容量是    " << ta.capacity() << endl;
    cout << "此时it指向的值是    " << *it << endl;

    return 0;
}

在Release模式下,一切正常;在Debug模式下,抛出异常,如下图所示:
在这里插入图片描述
这里我尝试在insert后输出原来的it+3的值,依然抛出异常。

三、erase导致迭代器失效

删除操作引起vector内元素移动,导致被移动部分的迭代器失效。

#include<iostream>
using namesspace std;
#include<vector>

int main()
{
    vector<int> ta;
    for (int i = 0; i < 10; ++i)
    {
        ta.push_back(i);
    }

    vector<int>::iterator it = ta.begin();

    it += 5;
    cout << "容量是  " << ta.capacity() << endl;
    cout << "it的值是  " << *it << endl;

    ta.erase(it);
    cout << "erase后容量是    " << ta.capacity() << endl;
    cout << "此时it指向的值是    " << *it << endl;
 
    return 0;
}

这里和insert操作一样会在release下正常运行,但是debug下抛出异常。原因是:在未扩容的情况下,虽然vector的内存是不变的,但依照C++标准,插入和删除位置之后的迭代器是应该失效的。

四、VC++下迭代器检测规则

MSDN Checked Iterators有提到,我们可以通过_ITERATOR_DEBUG_LEVEL宏来控制迭代器检测的严格等级,在Release模式下,默认是0,即关闭迭代器检查,在Debug模式下,默认为2,即开启全部的检查规则。
You can use the _ITERATOR_DEBUG_LEVEL preprocessor macro to enable or disable the checked iterators feature. If _ITERATOR_DEBUG_LEVEL is defined as 1 or 2, unsafe use of iterators causes a runtime error and the program is terminated. If defined as 0, checked iterators are disabled. By default, the value for _ITERATOR_DEBUG_LEVEL is 0 for release builds and 2 for debug builds.
MSDN Debug Iterator Support中,详细介绍了几种在Debug模式下会进行检查的迭代器
Invalid iterators无效迭代器,即C++标准中定义已经失效的迭代器;关闭检测时,使用失效迭代器行为为定义

Unitialized iterators 使用未初始化的迭代器
Incompatible iterators 不兼容的迭代器,使用两个不同vector的迭代器进行比较等操作
Iterators going out of scope 迭代器超出作用域
Destructors for debug iterators 手动释放迭代器的内存

五、总结

(1)我们应当时刻遵守C++标准,避免使用无效迭代器;
(2)应当好好利用VC++在Debug模式下的迭代器检测功能,帮助我们提前发现可能出错的迭代器操作。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值