C++关于迭代器删除(erase)插入(insert)失效问题
2014-05-23 16:06
483 查看
初学者的我在学习迭代器的时候(今天这里主要说的是vector的迭代器)碰到了一些问题,纠结了好些时候,总算弄明白了一点。
迭代器会在删除插入等操作后失效,即在其删除插入位置后的迭代器会失效,那所谓的失效是什么意思?
失效一般是指迭代器指向了和你预期不一样的位置了,但这个时候,你可以通过自增自减使它指向正确位置,但是,注意,有些时候,你是不能这样做的,迭代器会完全失效,必须重新初始化。
迭代器像指针但又不是指针,指针是指向某个地之后,除非你改变它的指向,要不然它是不会因为其他操作使它的指向发生变化的,迭代器不一样,在删除插入后它的指向是会变化的,尤其是插入(迭代器有点像浮动的游标)
例如:
std::vector data;
std::vector<int>::iterator i;
data.erase(i);
erase之后,i失效;
这里的失效,其实你在IDE中调试监视i看的话会发现,i只是指向了删除后的元素,并没有发生什么,但是在这之后你就是不能用它,一用它就会报错,而必须这样:i=data.erase(i)//i指向了被删除前的下一个元素;这样,你就可以用了。
其实删除还好,它并没有改变向量在内存的存储位置,你只要像上边这样做既可(或许重新初始化i),而插入有时就比较严重了:vector是支持动态插入的,什么是动态插入呢?就是原先分配给这个vector的内存如果不够的话,你再插入,能成功,但其实这个时候,插入后的vertor内存地址完全改变了,这样,原先的迭代器全部失效,必须重新初始化。你做插入动作,vector分配到的内存不够,这个时候系统会重新开辟一个内存空间(你在初始化vector的时候系统会自动开辟内存存储它,这个时候系统给它的内存空间你可以用vectorName.capacity()来查看他的大小,不过系统一般使capacity是等于size的,除非你手动初始化它),将原先vector的值复制过来,再执行插入操作,这样,你原先的迭代器就全部失效(因为地址完全改变了),不能再用了。
但如果你插入后,vector分配到的内存是够的,迭代器就不会“失效”,这里不会“失效”,是指vector还在这段内存上,只是插入后,插入位置后的迭代器指向了前一个元素,有点像删除,这个时候你只要执行“--i;”语句就可以使i回到原先位置
但是,重要的来了,迭代器这里你一定要重点关注下这个:iteratorName.end();这个位置很特殊,它无论是你删除也好,插入也好,一旦执行了这个操作之后,它是完全无效了的,它不会因为删除而指向后一个元素(这个好理解,后面是未定义的,当然不能),也不会因为插入而指向前一个元素(如果不是end位置,内存足够的话,插入后,这个位置后的迭代器是变成指向前一个元素的,并没有完全失效)。
说了这么多,还是看一个比较典型的程序,更好说明:
01 #include <algorithm>
02 #include <iostream>
03 #include <string>
04 #include <vector>
05 #include <iterator>
06 #include <ciso646>
07
08 int main()
09 {
10 int v;
11 std::string s;
12 std::vector<int> data;
13 std::vector<int>::iterator i,p;
14 data.insert(data.begin(),std::istream_iterator<int>(std::cin),std::istream_iterator>());
15 i=data.begin();
16 while(i!=data.end())
17 {
18 v=*i;
19 p=std::lower_bound(data.begin(),i,v);//从向量begin()开始,到i中找到值大于等于v的第一个迭代器(p)
20 i=data.erase(i);//删除i,并返回指向下一个元素的迭代器
21 data.insert(p,v);//在p位置之前插入数据v
22 ++i;
23 }
24 for(std::vector>::iterator iter(data.begin());iter!=data.end();++iter)
25 std::cout<<*iter<<'
';
26 system("pause");
27 }
这是Exploring C++一本书上的一个程序(对输入的数据进行排序),但这个程序是错误的,起码我在VS2010上运行会提示:Expression:vector iterator not incrementable.(迭代器未定义)(我用的是VS2010)
然后,我输入“3 2 1”试着调试,看看哪里错了,结果发现,前两个循环正确,第三个循环,在执行++i;之前也都是正确的,事实上到这个时候,序列已经排好序了,只要跳出循环就好了,可是执行这句语句(++i;)后,报错,我看了这个时候i的指向,data.end()前一个位置,按理说++i;后应该就可以和data.end()相等,这样下一个while循环就可以跳出来了,可事实上没有,这是为什么?
一开始不知道具体原理,还以为因为删除插入的原因,迭代器就马上无效,可一想前两个循环不也有删除插入?怎么就没事?弄了好久才明白:最后一个循环的时候执行 i=data.erase(i);,这个时候,i就指向了这个时候的data.end()位置,这是一个特殊的迭代器,之后执行插入操作,(data这个时候所拥有的内存是足够的),i就无条件无效了,不能用了,成为历史位置(==!不知道这样理解对不对),它是不会因为插入而指向前一个元素的,所以,执行++i;报错(end+1,位置未定义)。
那要实现原功能,怎么办,一开始,我这样改:
在++i;语句前面加一个if判断:
if(i!=data.end())
++i;
else
break;
可这样做还是错了,报错:Expression:vector iterator incompatible。迭代器不相容。
有了之前讲的,就很容易理解,i这个时候已经成了“历史”迭代器了,它和现在的data.end()完全是两码事,不能比较。
正确的做法是:(while循环里面代码)
01 v=*i;
02 p=std::lower_bound(data.begin(),i,v);//从向量begin()开始,到i中找到值大于等于v的第一个迭代器(p)
03 i=data.erase(i);//删除i,并返回指向下一个元素的迭代器
04 if(i==data.end())
05 {
06 data.insert(p,v);
07 break;
08 }
09 else
10 {
11 data.insert(p,v);//在p位置之前插入数据v
12 ++i;
13 }
迭代器会在删除插入等操作后失效,即在其删除插入位置后的迭代器会失效,那所谓的失效是什么意思?
失效一般是指迭代器指向了和你预期不一样的位置了,但这个时候,你可以通过自增自减使它指向正确位置,但是,注意,有些时候,你是不能这样做的,迭代器会完全失效,必须重新初始化。
迭代器像指针但又不是指针,指针是指向某个地之后,除非你改变它的指向,要不然它是不会因为其他操作使它的指向发生变化的,迭代器不一样,在删除插入后它的指向是会变化的,尤其是插入(迭代器有点像浮动的游标)
例如:
std::vector data;
std::vector<int>::iterator i;
data.erase(i);
erase之后,i失效;
这里的失效,其实你在IDE中调试监视i看的话会发现,i只是指向了删除后的元素,并没有发生什么,但是在这之后你就是不能用它,一用它就会报错,而必须这样:i=data.erase(i)//i指向了被删除前的下一个元素;这样,你就可以用了。
其实删除还好,它并没有改变向量在内存的存储位置,你只要像上边这样做既可(或许重新初始化i),而插入有时就比较严重了:vector是支持动态插入的,什么是动态插入呢?就是原先分配给这个vector的内存如果不够的话,你再插入,能成功,但其实这个时候,插入后的vertor内存地址完全改变了,这样,原先的迭代器全部失效,必须重新初始化。你做插入动作,vector分配到的内存不够,这个时候系统会重新开辟一个内存空间(你在初始化vector的时候系统会自动开辟内存存储它,这个时候系统给它的内存空间你可以用vectorName.capacity()来查看他的大小,不过系统一般使capacity是等于size的,除非你手动初始化它),将原先vector的值复制过来,再执行插入操作,这样,你原先的迭代器就全部失效(因为地址完全改变了),不能再用了。
但如果你插入后,vector分配到的内存是够的,迭代器就不会“失效”,这里不会“失效”,是指vector还在这段内存上,只是插入后,插入位置后的迭代器指向了前一个元素,有点像删除,这个时候你只要执行“--i;”语句就可以使i回到原先位置
但是,重要的来了,迭代器这里你一定要重点关注下这个:iteratorName.end();这个位置很特殊,它无论是你删除也好,插入也好,一旦执行了这个操作之后,它是完全无效了的,它不会因为删除而指向后一个元素(这个好理解,后面是未定义的,当然不能),也不会因为插入而指向前一个元素(如果不是end位置,内存足够的话,插入后,这个位置后的迭代器是变成指向前一个元素的,并没有完全失效)。
说了这么多,还是看一个比较典型的程序,更好说明:
01 #include <algorithm>
02 #include <iostream>
03 #include <string>
04 #include <vector>
05 #include <iterator>
06 #include <ciso646>
07
08 int main()
09 {
10 int v;
11 std::string s;
12 std::vector<int> data;
13 std::vector<int>::iterator i,p;
14 data.insert(data.begin(),std::istream_iterator<int>(std::cin),std::istream_iterator>());
15 i=data.begin();
16 while(i!=data.end())
17 {
18 v=*i;
19 p=std::lower_bound(data.begin(),i,v);//从向量begin()开始,到i中找到值大于等于v的第一个迭代器(p)
20 i=data.erase(i);//删除i,并返回指向下一个元素的迭代器
21 data.insert(p,v);//在p位置之前插入数据v
22 ++i;
23 }
24 for(std::vector>::iterator iter(data.begin());iter!=data.end();++iter)
25 std::cout<<*iter<<'
';
26 system("pause");
27 }
这是Exploring C++一本书上的一个程序(对输入的数据进行排序),但这个程序是错误的,起码我在VS2010上运行会提示:Expression:vector iterator not incrementable.(迭代器未定义)(我用的是VS2010)
然后,我输入“3 2 1”试着调试,看看哪里错了,结果发现,前两个循环正确,第三个循环,在执行++i;之前也都是正确的,事实上到这个时候,序列已经排好序了,只要跳出循环就好了,可是执行这句语句(++i;)后,报错,我看了这个时候i的指向,data.end()前一个位置,按理说++i;后应该就可以和data.end()相等,这样下一个while循环就可以跳出来了,可事实上没有,这是为什么?
一开始不知道具体原理,还以为因为删除插入的原因,迭代器就马上无效,可一想前两个循环不也有删除插入?怎么就没事?弄了好久才明白:最后一个循环的时候执行 i=data.erase(i);,这个时候,i就指向了这个时候的data.end()位置,这是一个特殊的迭代器,之后执行插入操作,(data这个时候所拥有的内存是足够的),i就无条件无效了,不能用了,成为历史位置(==!不知道这样理解对不对),它是不会因为插入而指向前一个元素的,所以,执行++i;报错(end+1,位置未定义)。
那要实现原功能,怎么办,一开始,我这样改:
在++i;语句前面加一个if判断:
if(i!=data.end())
++i;
else
break;
可这样做还是错了,报错:Expression:vector iterator incompatible。迭代器不相容。
有了之前讲的,就很容易理解,i这个时候已经成了“历史”迭代器了,它和现在的data.end()完全是两码事,不能比较。
正确的做法是:(while循环里面代码)
01 v=*i;
02 p=std::lower_bound(data.begin(),i,v);//从向量begin()开始,到i中找到值大于等于v的第一个迭代器(p)
03 i=data.erase(i);//删除i,并返回指向下一个元素的迭代器
04 if(i==data.end())
05 {
06 data.insert(p,v);
07 break;
08 }
09 else
10 {
11 data.insert(p,v);//在p位置之前插入数据v
12 ++i;
13 }
相关文章推荐
- C++关于迭代器删除(erase)插入(insert)失效问题http://m.blog.csdn.net/blog/a327369238/26715187
- 关于STL的list,vector等用迭代器iterator,用erase删除元素出现的问题。
- 容器insert、erase操作引起迭代器失效问题
- 关于STL的erase()陷阱-迭代器失效问题的总结
- c++关于顺序容器指针迭代器失效问题
- c++关于顺序容器指针迭代器失效问题
- 关于vector的erase操作后,迭代器失效问题
- C++中容器迭代器删除失效问题
- STL中,deque插入或删除元素时,迭代器是否失效的问题
- 关于list循环删除元素,迭代器失效的问题
- 如何处理C++删除容器中元素时导致的迭代器失效问题
- STL容器的遍历插入或删除(迭代器失效问题的统一解决)
- 关于STL的list,vector等用迭代器iterator,用erase删除元素出现的问题。
- C++中防止STL中迭代器失效__map/set等关联容器vector/list/deque等序列容器_如何防止迭代器失效_即erase()的使用
- 关于vector中使用erase删除元素后导致越界的问题
- 关于SQL触发器的问题 ?同时删除多行,这些行插入到另一张表中?
- STL list erase 删除一个节点后,迭代器会失效吗
- 关于动态存储分配函数的调用,在已经过排序的数组中查找及删除内容的操作,余数的分析,删除字符数组中的空格,对链表的逆置,在源字符串中查找子字符串的个数,函数指针以及函数的调用,循环赋值带来的问题以及插入
- 从零开始学C++之STL(十):迭代器适配器{(插入迭代器back_insert_iterator)、IO流迭代器(istream_iterator、ostream_iterator)}
- C++中防止STL中迭代器失效__map/set等关联容器vector/list/deque等序列容器_如何防止迭代器失效_即erase()的使用