C++ STL 迭代器失效问题的剖析
来源:互联网 发布:ibm软件的数据仓库 编辑:程序博客网 时间:2024/06/06 00:03
STL中容器分为顺序容器和关联容器,顺序容器有:vector、deque、list;关联容器有:set、map、multiset、multimap。
一、vector、deque、list容器
在vector、deque、list 中遍历删除某些元素时可以使用下面方式
(1)正确用法1
通过erase方法的返回值来获取下一个元素的位置。
std::vector<int> Vec;std::vector<int>::iterator itVec;for(itVec = Vec.begin(); itVec != Vec.end(); ){ if (WillDeleteCondition(*itVec)) itVec = Vec.erase(itVec); else itVec++;}
std::list<int> List;std::list<int>::iterator itList;for(itList = List.begin(); itList != List.end(); ){ if (WillDeleteCondition(*itList)) itList = List.erase(itList); else itList++;}
(2)对于list也可以使用下列方法
在调用erase方法之前先使用"++"来获取下一个元素的位置
std::list<int> List;std::list<int>::iterator itList;for(itList = List.begin(); itList != List.end(); ){ if (WillDeleteCondition(*itList)) List.erase(itList++); else itList++;}
(3)错误用法
在调用erase方法之后使用"++"来获取下一个元素的位置,由于在调用erase方法之后,该元素的位置已经被删除,如果再根据这个旧的位置来获取下一个位置,则会出现异常。
std::list<int> List;std::list<int>::iterator itList;for(itList = List.begin(); itList != List.end(); itList++){ if (WillDeleteCondition(*itList)) List.erase(itList);}
二、set、map容器
在set、map中遍历删除某些元素时可以使用下面方式
(1)正确写法A
std::map<int, int> Mapstd::map<int, int>::iterator itMapfor(itMap = Map.begin(); itMap != Map.end(); ){ if (WillDeleteCondition(itMap->second)) Map.erase(itMap++); else itMap++;}
三、剖析原因
C++标准中,顺序容器的erase函数会返回iterator,但关联容器的erase函数不返回iterator。
(1)对于顺序容器 vector、deque,删除当前的iterator会使后面所有元素的iterator都失效。这是因为vector、deque使用了连续分配的内存,删除一个元素导致后面所有的元素会向前移动一个位置。erase方法可以返回下一个有效的iterator。【即正确用法1】
(2)对于关联容器map、set、multimap、multiset,删除当前的iterator,仅仅会使当前的iterator失效,只要在erase时,递增当前iterator即可。这是因为map之类的容器,使用了红黑树来实现,插入、删除一个结点不会对其他结点造成影响。【即正确用法A】
(3)对于顺序容器list,erase方法可以返回下一个有效的iterator【即正确用法1】。由于list是一个链表,删除当前的iterator,仅仅会使当前的iterator失效,所以也可以在erase时,递增当前的iterator【即正确用法A】。
(4)erase函数返回被删除元素的下一个元素的迭代器。在STL中,不能以指针来看待迭代器,指针是与内存绑定的,而迭代器是与容器里的元素绑定的。
四、迭代器失效的情况
(1)vector
内部数据结构:数组
随机访问每个元素,所需要的时间为常量。
在末尾add/delete元素所需时间与元素数目无关,在开头或中间add/delete元素所需时间随元素数目呈线性变化。
可动态增加或减少元素,内存管理自动完成,但程序员可以使用reserve()成员函数来管理内存。
add元素:所有元素的迭代器失效(内存重新分配时)(当把超过capacity()-size()个元素插入vector中时,内存会重新分配)
当前元素以后的任何元素的迭代器失效(内存没有重新分配时)
delete元素:被删除元素以后的任何元素的迭代器都将失效
(2)deque
内部数据结构:数组
随机访问每个元素,所需要的时间为常量
在开头或末尾add/delete元素所需时间与元素数目无关,在中间add/delete元素所需时间随元素数目呈线性变化。
可动态增加或减少元素,内存管理自动完成,不提供用于内存管理的成员函数。
add元素:迭代器失效
delete元素:迭代器失效(删除中间元素)
指向该元素的迭代器失效(删除头/尾元素)
(3)list
内部数据结构:双向环状链表
不能随机访问一个元素,可双向遍历。
在开头/中间/末尾add/delete元素所需时间都为常量
可动态增加或减少元素,内存管理自动完成。
add元素:不会使迭代器失效
delete元素:指向当前被删除元素的迭代器失效,其它迭代器都不会失效
(4)set
内部数据结构:红黑树
键和值相等,键唯一,元素默认按升序排列。
add元素:不会使迭代器失效
delete元素:指向当前被删除元素的迭代器失效,其它迭代器都不会失效
(5)map
内部数据结构:红黑树
键唯一,元素默认按键的升序排列。
add元素:不会使迭代器失效
delete元素:指向当前被删除元素的迭代器失效,其它迭代器都不会失效
五、实例// iteratorInvalidateVector.cpp#include <iostream>#include <vector>typedef std::vector<int> Vector;typedef std::vector<int>::iterator VectorIt;void printLog(Vector vectOne){ VectorIt it; for (it = vectOne.begin(); it != vectOne.end(); it++) { std::cout<< *it<<" "; } std::cout<<std::endl;}void deleteValue(Vector &vectOne, int n){ VectorIt it; for (it = vectOne.begin(); it != vectOne.end(); ) { if (0 == (*it % n)) it = vectOne.erase(it); else it++; }}int main(){ Vector vectOne; int i,j = 0; for(i = 0; i < 21; i++) { j = i; if (i == 3) j = i+1; vectOne.push_back(j); } printLog(vectOne); deleteValue(vectOne, 2); printLog(vectOne); return 0;}
//iteratorInvalidateList.cpp#include <iostream> #include <list> typedef std::list<int> List;typedef std::list<int>::iterator ListIt;void printLog(List listOne){ ListIt it; std::cout<<std::endl; for (it = listOne.begin(); it != listOne.end(); it++) std::cout << *it << " "; std::cout << std::endl;}void deleteValue(List &listOne, int n){ ListIt it; for (it = listOne.begin(); it != listOne.end(); ) { if (0 == (*it % n)) //it = listOne.erase(it); listOne.erase(it++); else it++; }}int main() { List listOne; int i, j = 0; for(i = 0; i < 21; i++) { j = i; if (i == 3) j = i+1; listOne.push_back(j); } printLog(listOne); deleteValue(listOne, 2); printLog(listOne); return 0;}
// iteratorInvalidateMap.cpp#include <iostream>#include <map>#include <vector>typedef std::map<int, int> Map;typedef std::map<int, int>::iterator MapIt;void printLog(Map m){ MapIt it; for (it = m.begin(); it != m.end(); it++) { std::cout<< it->second <<" "; } std::cout<<std::endl;}void deleteValue(Map &m, int n){ MapIt it; for (it = m.begin(); it != m.end(); ) { if (0 == (it->second % 2)) { m.erase(it++); } else { it++; } }}void deleteValueOne(Map &m, int n){ MapIt it; for (it = m.begin(); it != m.end(); it++) { if (0 == (it->second % 2)) m.erase(it); }}int main(){ Map m; int i = 0; for(i = 0; i < 21; i++) { m[i] = i; } m[3] = 4; printLog(m); deleteValue(m, 5); printLog(m); return 0;}注意:在iteratorInvalidateMap.cpp 中如果使用函数deleteValueOne(),并不会造成crash,但没有得到正确的结果,得到的结果如下
0 1 2 4 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
1 4 5 7 9 11 13 15 17 19
因为(it指向2时)在执行m.erase(it)之后,it自动绑定到下一个元素(第一个4),此时又执行for循环中的it++(it指向第二个4),就跳过了map中的第一个4。阅读全文
0 0
- C++ STL 迭代器失效问题的剖析
- 【STL源码剖析读书笔记】STL容器迭代器失效问题总结
- STL源码剖析-STL容器迭代器失效问题总结
- STL迭代器失效的问题
- [C++] STL迭代器失效
- STL迭代器失效问题
- STL迭代器失效问题
- STL迭代器失效问题
- STL迭代器失效问题
- STL迭代器失效问题
- STL 迭代器失效问题
- STL迭代器失效问题
- STL迭代器失效问题
- STL迭代器失效问题
- STL迭代器失效问题
- c++中STL的迭代器失效问题
- STL源码剖析---迭代器失效小结
- STL源码剖析---迭代器失效小结
- Tomcat下ajax请求路径总结
- HDOJ2080 夹角有多大II
- 使用Gradle打包Kotlin项目代码、生成Kotlin代码文档
- SSM框架css/js/images等静态资源无法加载的解决
- 网络CDN加速及其原理
- C++ STL 迭代器失效问题的剖析
- 交大oj-1009-二哥炒股票 C++ 总结
- 2015第六届蓝桥杯 C/C++A组真题及题解
- 使用numpy时遇到的一些问题
- 表达式解题报告
- 使用cordova进行App打包
- mybatis(1.环境的搭建)(2.部分优化)
- 蓝桥杯 基础练习 字母图形
- git命令小结