`

C++ 迭代器失效

    博客分类:
  • c++
 
阅读更多

众所周知当使用一个容器的insert或者erase函数通过迭代器插入或删除元素"可能"会导致迭代器失效,因此很多建议都是让我们获取insert或者erase返回的迭代器,以便用重新获取新的有效的迭代器进行正确的操作:

iter=vec.insert(iter);   
 iter=vec.erase(iter);    

 想想究竟为什么迭代器失效,原因也不难理解。以vector为例,当我们插入一个元素时它的预分配空间不够时,它会重新申请一段新空间,将原空间上的元素复制到新的空间上去,然后再把新加入的元素放到新空间的尾部,以满足vector元素要求连续存储的目的。而后原空间会被系统撤销或征做他用,于是指向原空间的迭代器就成了类似于“悬垂指针”一样的东西,指向了一片非法区域。如果使用了这样的迭代器会导致严重的运行时错误就变得很自然了。这也是许多书上叙述vector在insert操作后“可能导致所有迭代器实效”的原因。但是想到这里我不禁想到vector的erase操作的叙述是“会导致指向删除元素和删除元素之后的迭代器失效”。但是明显感觉erase带来失效要比insert来得轻得多。似乎“此失效非彼失效”,想想似乎也是这样的:erase操作是在原空间上进行的,假设有一个存有"12345"序列的vector<int>容器原本指向3的迭代器在我删除2之后无非变成指向4了,我只要注意别用到超过end位置的迭代器不就行了吗?


  说了这么多似乎可以归纳一下迭代器失效的类型了:

  1.由于容器元素整体“迁移”导致存放原容器元素的空间不再有效,从而使得指向原空间的迭代器失效。

  2.由于删除元素使得某些元素次序发生变化使得原本指向某元素的迭代器不再指向希望指向的元素。

  对于第一种类型没什么好就是的了,原因应该确定如此了。可对于第二种,我写了如下的代码

vector<int> vec;   
for(int i=0;i<10;i++)   
vec.push_back(i);   
 
vector<int>::iterator iter =vec.begin()+2;   
vec.erase(iter);//注:这里真的不建议这么写   
cout<<*iter<<endl;   
for(vector<int>::iterator it=vec.begin();it!=vec.end();it++)   
cout<<*it<<endl; 

 按照我的猜测尽管我在注释的位置的写法很“危险”,但是我并未涉及到上面总结第一种失效类型的范畴。程序应该还是会如预期的一样删除在vec[2]位置上的2然后输出前移到vec[2]位置上的3,并输出0到10不含2的所有数字,于是vs2008的c++环境下运行一下,竟然一个是个无情的“红叉”。按道理说这种写法是绝对不对出现严重的运行时错误的,难道是猜测得不对吗?


   当时被这样的疑问困扰了几天,有一天突然想起一个关于容器迭代器作参数的例子,在vs2008下运行不了,但是在vc6.0下却可以,有人说是因为08采用了更为严格的类型检测机制。于是将上面的代码放到6.0里,果然得到了预期的效果,看来系统果然是这么处理的。至于08为什么不可以,现在我只能认为是采用了更加安全的检查机制。使得第二种类型的失效后果同样“不可饶恕”。

  这样我就又想到假如insert元素时原空间够用的话,是不是也不会产生第一类失效而产生第二类失效呢?

vector<int> vec;   
for(int i=0;i<10;i++)   
    vec.push_back(i);   
       cout<<"capacity:"<<vec.capacity()<<endl;//查看预分配空间大小   
vector<int>::iterator iter =vec.begin()+2;   
vec.insert(iter,100);   
cout<<"capacity:"<<vec.capacity()<<endl;     
cout<<*iter<<endl;   
for(vector<int>::iterator it=vec.begin();it!=vec.end();it++)   
cout<<*it<<endl;  

  同样在vc6.0下,证明了假设是对的。但是上面的种种做法只是为了帮助我理解迭代器实效的原因,建议使用insert和erase操作时还是像许多书中介绍的如第一段代码那样的写法,这是一种好的且安全的习惯。总之一句话去相信“insert和erase操作后所有的迭代器都会失效”。



迭代器(iterator)是一个可以对其执行类似指针的操作(如:解除引用(operator*())和递增(operator++()))的对象,我们可以将它理解成为一个指针。但它又不是我们所谓普通的指针,我们可以称之为广义指针,你可以通过sizeof(vector::iterator)来查看,所占内存并不是4个字节。

    首先对于vector而言,添加和删除操作可能使容器的部分或者全部迭代器失效。那为什么迭代器会失效呢?vector元素在内存中是顺序存储,试想:如果当前容器中已经存在了10个元素,现在又要添加一个元素到容器中,但是内存中紧跟在这10个元素后面没有一个空闲空间,而vector的元素必须顺序存储一边索引访问,所以我们不能在内存中随便找个地方存储这个元素。于是vector必须重新分配存储空间,用来存放原来的元素以及新添加的元素:存放在旧存储空间的元素被复制到新的存储空间里,接着插入新的元素,最后撤销旧的存储空间。这种情况发生,一定会导致vector容器的所有迭代器都失效。

我们看到实现上述所说的分配和撤销内存空间的方式以实现vector的自增长性,效率是极其低下的。为了使vector容器实现快速的内存分配,实际分配的容器会比当前所需的空间多一些,vector容器预留了这些额外的存储区,用来存放新添加的元素,而不需要每次都重新分配新的存储空间。你可以从vector里实现capacity和reserve成员可以看出这种机制。

    capacity和size的区别:size是容器当前拥有的元素个数,而capacity则指容器在必须分配新存储空间之前可以存储的元素总数。

    vector迭代器的几种失效的情况: 1.当插入(push_back)一个元素后,end操作返回的迭代器肯定失效。 2.当插入(push_back)一个元素后,capacity返回值与没有插入元素之前相比有改变,则需要重新加载整个容器,此时first和end操作返回的迭代器都会失效。 3.当进行删除操作(erase,pop_back)后,指向删除点的迭代器全部失效;指向删除点后面的元素的迭代器也将全部失效。

    deque迭代器的失效情况: 在C++Primer一书中是这样限定的: 1.在deque容器首部或者尾部插入元素不会使得任何迭代器失效。 2.在其首部或尾部删除元素则只会使指向被删除元素的迭代器失效。 3.在deque容器的任何其他位置的插入和删除操作将使指向该容器元素的所有迭代器失效。但是:我在vs2005测试发现第一条都不满足,不知为何?等以后深入STL以后慢慢的领会吧!

    只有list的迭代器好像很少情况下会失效。也许就只是在删除的时候,指向被删除节点的迭代器会失效吧,其他的还没有发现。

分享到:
评论

相关推荐

    c++迭代器失效问题

    以下是对C++中迭代器失效问题的详细分析: 1. **插入和删除操作**:在容器中进行插入或删除元素时,迭代器可能失效。例如,当你在迭代过程中向容器添加或移除元素,特别是如果插入或删除发生在迭代器当前指向的位置...

    c++迭代器失效的情况汇总

    C++迭代器失效的情况汇总 C++迭代器失效是指在使用容器时,迭代器失去了指向正确元素的能力,而导致程序出错或崩溃。这种情况在C++编程中非常常见,尤其是在使用STL容器时。了解迭代器失效的情况可以帮助我们更好地...

    关于迭代器失效的一些认识

    ### 关于迭代器失效的理解与探讨 #### 一、引言 在C++标准模板库(STL)中,迭代器是一种用于遍历容器中的元素的重要工具。然而,在使用过程中,迭代器可能会因为某些操作(例如插入和删除)而失效。本文将深入探讨...

    [C++][经验总结]vectory迭代器(iterator)失效

    总之,掌握`vector`迭代器失效的原因和处理方法是每个C++开发者必备的技能。在编程实践中,应始终警惕这些潜在的陷阱,以确保程序的稳定性和正确性。通过不断地学习和实践,我们能更好地利用C++的强大功能,同时避免...

    浅谈c++ stl迭代器失效的问题

    之前看《C++ Primier》的时候,也解到在顺序型窗口里insert/erase会涉及到迭代器失效的问题,并没有深究。今天写程序的时候遇到了这个问题。 1 莫名其妙的Erase 最初我的程序是酱紫的,别说话,我知道这样是有问题的...

    C++ 迭代器ppt 为什么无效

    5. **迭代器失效的情况**:迭代器可能因以下操作而失效: - 插入或删除元素,特别是在迭代器当前指向的位置。 - 改变容器的大小,如通过`resize`函数。 - 重新分配容器的内存,例如在`vector`的动态增长过程中。 ...

    vector的earse造成迭代器失效的问题

    ### vector的erase造成迭代器失效的问题 在C++标准库中,`std::vector`是一种动态数组,能够根据需要自动调整大小。它提供了一系列的方法来插入、删除元素,包括`push_back`, `pop_back`, `insert` 和 `erase`等。...

    关于vector迭代器失效的几种情况总结

    然而,由于其动态性,当我们对`vector`进行某些操作时,可能会导致迭代器失效,这在编程中是一个需要注意的重要问题。迭代器失效意味着,之前有效的迭代器不再指向原来的元素或者不再可用。下面是关于`vector`迭代器...

    基于list循环删除元素,迭代器失效的问题详解

    在编程中,特别是涉及到容器(如C++中的`std::list`)的迭代操作时,一个常见的问题是迭代器失效。本文将深入探讨这个问题,并提供一个关于如何在删除元素时避免迭代器失效的示例。 首先,我们需要理解迭代器是什么...

    关于STL的erase()陷阱-迭代器失效问题的总结

    然而,使用`erase()`时需要特别注意的一个问题是迭代器失效,即在删除元素后,与该元素相关的迭代器将不再有效。本文将深入探讨这个问题,并通过实例分析在不同类型的STL容器中如何安全地使用`erase()`。 首先,让...

    关于迭代器的Demo

    - 某些操作如插入、删除元素可能导致迭代器失效,因此在进行这些操作后,需重新获取有效迭代器。 9. **迭代器的分类**: - 输入迭代器只能向前移动且只读,输出迭代器只允许写入; - 前向迭代器比输入迭代器多一...

    C++与操作系统等面试题57

    - **迭代器失效**:当使用`erase()`函数删除元素时,该元素之后的所有元素都会向前移动一位,从而导致这些元素的迭代器失效。 - **返回值**:`erase()`函数会返回下一个有效迭代器,可以用来继续遍历容器。 **示例...

    STL迭代器的说明

    使用插入迭代器可能会导致容器中的其他对象被移动,从而导致原本有效的迭代器失效。 #### 混合迭代器函数 除了上述迭代器类型外,STL还提供了一些迭代器操作函数,用于更灵活地控制迭代器的移动: - **`advance()...

    vector list map 遍历删除制定元素 防止迭代器失效的实例

    然而,在遍历这些容器并删除元素时,如果不小心,可能会导致迭代器失效,从而引发运行时错误。下面将详细解释如何在遍历过程中安全地删除指定元素,以防止迭代器失效。 ### 1. `vector` 的遍历与删除 `vector` 是...

    搜狗-2015-研发类笔试题

    6. C++迭代器失效问题:在容器中进行插入或删除操作时,某些操作可能会使迭代器失效,导致其不再指向原来的元素。 7. C++函数重载:允许定义多个同名函数,但它们的参数类型或个数不同。 8. Java线程与线程状态:...

    学习学习学习学习学习学习学习学习学习学习学习学习学习学习学习学习

    为避免迭代器失效,通常需要在操作后及时更新迭代器,或者使用迭代器安全的方法,如`insert`函数的返回值来获取新的有效迭代器。 对于map这种关联容器,它内部维护了一个有序的键值对集合,每个键是唯一的,并且...

    详解C++中的vector容器及用迭代器访问vector的方法

    需要注意的是,在范围for循环中修改vector的大小是不安全的,因为这样做可能会导致迭代器失效。在进行这样的操作前,需要仔细检查代码以确保迭代器的合法性。 总之,C++中的vector容器为程序员提供了一个功能强大、...

    C++与操作系统等面试题98

    - 在执行可能导致迭代器失效的操作之前,存储关键数据或索引。 - 尽量避免使用可能导致迭代器失效的操作。 #### 七、总结 理解迭代器以及如何正确地使用它们对于有效地使用C++标准库至关重要。了解不同类型的迭代...

Global site tag (gtag.js) - Google Analytics