C++ STL 迭代器失效问题
本文有更新,请移步我的个人博客:https://blog.andyqiao.top/article/17/
之前看《C++ Primier》的时候,也解到在顺序型窗口里insert/erase会涉及到迭代器失效的问题,并没有深究。今天写程序的时候遇到了这个问题。
1 莫名其妙的Erase
最初我的程序是酱紫的,别说话,我知道这样是有问题的,可这样是最直观的想法
int arr[]={0,1,2,3,4,5,6,7,8,9,10};
vector<int> a(arr,arr+sizeof(arr)/sizeof(*arr));for (auto it = a.begin(); it != a.end();++it ){
if ((*it)&1){
a.erase(it);
}
}
没错,程序崩溃!删除了迭代器it之后,it迭代器失效了,无法再进行++it操作了。
可是,当我觉得erase做的只是把it之后的元素向前移动一个位置而已,为什么迭代器失效了呢?我翻开《STL源码剖析》,SGI STL的vector<T,Alloc>::erase的源码是这样的:
iterator vector<T, Alloc>::erase(iterator position)
{
if (position + != end())
copy(position + , finish, position);
--finish;
destroy(finish);
return position;
}
正如我所想,erase函数并没有对输入的position迭代器进行改写!我打印出调试信息,发现erase之后,迭代器的_Ptr成员,也就是指针的值并没有发生变化,而此指针所指的元素的确是下一个元素。那么为什么失效了呢?
我又查了《C++ Primier》,发现此书上的标准写法是这样的:
int arr[]={,,,,,,,,,,};
vector<int> a(arr,arr+sizeof(arr)/sizeof(*arr));
for (auto it = a.begin(); it != a.end();){
if ((*it)&){
it=a.erase(it);
}
else
++it;
}
运行了一下,这样是没错的。我打印了调试信息,发现与之前一样,erase之后把结果赋给it,it里的成员_Ptr并没有发生变化。唯一的可能就是迭代器里还有别的标志,如果当前元素被删除之后,该迭代器也就“失效”了。《C++ Primier》并未对此作出过多解释,只是说,erase函数返回被删除元素的下一个元素的迭代器。
结论:在STL里,我们不能以指针来看待迭代器,指针是与内存绑定的,而迭代器是与容器里的元素绑定的,删除了之后,该迭代器就失效了,在对其重新赋值之前,不能再访问此迭代器。
2 更加小心冀冀地Insert
机智如我,自然会去探索一下insert之后,迭代器会怎样。于是:
vector<int> a;
for (int i = ; i < ; ++i)
{
a.push_back(i);
} for (auto it = a.begin(); it != a.end(); ++it){
if (*it == ){
a.insert(it, );
++it;
}
}
你猜怎么着??
啥事儿没有!你可能会问,插入之后为什么要++it。插入之前,it指向5,在5之前插入100后,it指向100。这样下一次循环,it依然会指向5。相信我,你的程序会爆炸的!
我作了个++it之后,it又指向5,下一次循环就直接指向5之后的元素了,顺利完成插入工作。
世界和平~世界和平~我还真不确定。
突然想到,当插入元素过多,vector的capacity会增加,这时会不会问题呢?说干就干:
vector<int> a;
for (int i = ; i < ; ++i)
{
a.push_back(i);
} for (auto it = a.begin(); it != a.end(); ++it){
if (*it == ){
a.insert(it, );
++it;
}
}
BOOM!果然崩溃了!也就是说插入之后的迭代器失效了。那之前的呢?
我决定粗暴地测试一下:
vector<int> a;
for (int i = ; i < ; ++i)
{
a.push_back(i);
}
auto it1=a.begin();
for (auto it = it1; it != a.end(); ++it){
if (*it == ){
a.insert(it, );
it=it1;
}
}
我插入之后,直接让it指向begin(),然后单步调试。执行完it=it1还好好的,可再去执行++it还是崩溃了。
也就是说,capacity变化之后,所有的迭代器都失效了!这是当然了呀!capacity发生变化,容器内部做的不仅仅是增加capacity这么简单,因为容器所在内存后面可能没有足够的内存让我们使用,所以,容器要重新开辟一段足够大的内存来存储容器里的元素,当前内存会被释放。这样一来,迭代器自然失效了。
3 C++ Primier的总结
关于容器的迭代器失效的问题,C++ Primier用了一小节作了总结,我翻译成中文如下:
(1)增加元素到容器后
对于vector和string,如果容器内存被重新分配,iterators,pointers,references失效;如果没有重新分配,那么插入点之前的iterator有效,插入点之后的iterator失效;
对于deque,如果插入点位于除front和back的其它位置,iterators,pointers,references失效;当我们插入元素到front和back时,deque的迭代器失效,但reference和pointers有效;
对于list和forward_list,所有的iterator,pointer和refercnce有效。
(2)从容器中移除元素后
对于vector和string,插入点之前的iterators,pointers,references有效;off-the-end迭代器总是失效的;
对于deque,如果插入点位于除front和back的其它位置,iterators,pointers,references失效;当我们插入元素到front和back时,off-the-end失效,其他的iterators,pointers,references有效;
对于list和forward_list,所有的iterator,pointer和refercnce有效。
(3)在循环中refresh迭代器
当处理vector,string,deque时,当在一个循环中可能增加或移除元素时,要考虑到迭代器可能会失效的问题。我们一定要refresh迭代器。
int arr[] = { , , , , , , , , , , };
deque<int> v(arr,arr+sizeof(arr)/sizeof(*arr));
for (auto it = v.begin(); it != v.end(); )
{
if ((*it) & )
{
it = v.insert(it, *it);
it += ;
}
else
it = v.erase(it);
}
至于it+=2,很容易解释,insert之后,it指向新增加的元素,+2之后,it指向下一个要处理的元素。
(4)在循环不变式中不要store off-the-end迭代器
这个很容易理解了,增加或移除元素之后,off-the-end失效了,不store的话,每次从end()函数中取的都是最新的off-the-end,自然不会失效。
最后:《C++ Primier》是本好书。
C++ STL 迭代器失效问题的更多相关文章
- stl 迭代器失效
1.对于基于连续内存的容器,容器元素的增删,有可能会导致迭代器的失效.考虑: int main(int argc, char* argv[]) { vector<int> intVec; ...
- 转:STL迭代器失效问题
. 对于关联容器(如map, set, multimap,multiset),删除当前的iterator,仅仅会使当前的iterator失效,只要在erase时,递增当前iterator即可.这是因为 ...
- C++ STL中迭代器失效的问题
my_container.erase(iter); 其中my_container是STL的某种容器,iter是指向这个容器中某个元素的迭代器.如果不是在for,while循环中,这种方式删除元素没有问 ...
- STL的erase()陷阱-迭代器失效总结
下面材料整理自Internet&著作. STL中的容器按存储方式分为两类,一类是按以数组形式存储的容器(如:vector .deque):另一类是以不连续的节点形式存储的容器(如:list.s ...
- STL源代码分析--迭代摘要、迭代器失效汇总
Vector 1.内部数据结构:连续存储,比如数组. 2.随机訪问每一个元素,所须要的时间为常量. 3.在末尾添加或删除元素所需时间与元素数目无关,在中间或开头添加或删除元素所需时间随元素数目呈线性变 ...
- vector源码(参考STL源码--侯捷):空间分配导致迭代器失效
vector源码1(参考STL源码--侯捷) vector源码2(参考STL源码--侯捷) vector源码(参考STL源码--侯捷)-----空间分配导致迭代器失效 vector源码3(参考STL源 ...
- stl vector、红黑树、set、multiset、map、multimap、迭代器失效、哈希表(hash_table)、hashset、hashmap、unordered_map、list
stl:即标准模板库,该库包含了诸多在计算机科学领域里所常用的基本数据结构和基本算法 六大组件: 容器.迭代器.算法.仿函数.空间配置器.迭代适配器 迭代器:迭代器(iterator)是一种抽象的设计 ...
- STL迭代器及迭代器失效问题
迭代器失效: 典型的迭代器失效. 首先对于vector而言,添加和删除操作可能使容器的部分或者全部迭代器失效.那为什么迭代器会失效呢?vector元素在内存中是顺序存储,试想:如果当前容器中已经存在了 ...
- STL容器迭代器失效问题讨论
STL源码剖析---迭代器失效小结 vector迭代器的几种失效的情况: .当插入(push_back)一个元素后,end操作返回的迭代器肯定失效. .当插入(push_back)一个元素后,capa ...
随机推荐
- Keypress – 超强大!捕获键盘输入的 JavaScript 库
Keypress 是一个强大的 JavaScript 库,用于捕获键盘输入.这是一个有非常特殊的功能的输入捕获库,它是很容易掌握和使用,并且不依赖第三方库.在网站开发中,经常会碰到需要处理键盘输入的场 ...
- 使用aspose.cell动态导出多表头 EXCEL
效果图: 前台调用: using System; using System.Collections.Generic; using System.Linq; using System.Web; usin ...
- Java魔法堂:注解用法详解——@Override
一.前言 现在有Son和Parent两个类,且类型Son将会重写类型Parent的getName函数.但不幸的是由于码农大意,写成如下代码: public class Parent{ public S ...
- CentOS6.5菜鸟之旅:中文编辑器忍痛放弃Sublime
一.前言 Windows下习惯使用Sublime作为编辑器,谁知道Linux下的Sublime是如此不照顾中文用户,找了N久终于找到一个蹩脚的解决方案,于是我毅然决然地加入Vim的阵营. 二.苦苦追寻 ...
- Linux - root初始密码设置
Ubuntu刚安装后,不能在terminal中运行su命令,因为root没有默认密码,需要手动设定. 以安装ubuntu时输入的用户名登陆,该用户在admin组中,有权限给root设定密码. 给roo ...
- Azure开发者任务之一:解决Azure Storage Emulator初始化失败
初学Windows Azure: 我打算开始学习Windows Azure.我安装了Azure SDK,然后在“Cloud”标签下选择Windows Azure模板,创建了一个项目,然后又创建了一个W ...
- wpf 自定义消息框
相信很多人用过MessageBox.show(),是不是觉得这个消息框有点丑呢,反正我是觉得有点丑的,所以我自己重写了一个.先不说,上两幅图对比先: 当然,也不是很好看,不过比原有的好多了. 不多说了 ...
- ASP.NET几种页面数据绑定的用法及区别: <%#、 <%=、 <% 、<%@
< %#... %>: 是在绑定控件DataBind()方法执行时被执行,用于数据绑定 如: < %# Container.DataItem("tit") %&g ...
- Discuz!X3解读之类引入机制及目录结构
实例: - /source/class/table/table_forum_faq.php - /source/class/model/model_forum_post.php - /source/p ...
- STL---Codeforces675D Tree Construction(二叉树节点的父亲节点)
Description During the programming classes Vasya was assigned a difficult problem. However, he doesn ...