C++ Runtime Error :STL list "list iterator not incrementable"
2015-11-22 13:23
585 查看
问题
最近在项目过程中用到了list这个容器,并且在使用过程中涉及了erase的删除元素操作,在程序的调试过程中经常会弹出异常对话框,提示的异常为:“list iterator not incrementable”,便上网查了一下问题的原因,网上的答案一致认为是遍历容器元素的时候使用erase删除元素,导致++iterator 的时候报了刚才的错误。但是我原来经常使用这类容器,对于erase的操作也不少,知道其中的陷阱在哪里,我确信我的代码中的没有常见的那种错误写法,于是我开始一句一句的调试代码,最后我发现还是我代码的问题,只不过代码隐藏的太隐秘了,一般不容易发现,下面我们先来看看一般的版本:常见版本
#include "stdafx.h" #include <iostream> #include <list> using namespace std; int _tmain(int argc, _TCHAR* argv[]) { list<int> listInt; listInt.push_back(1); listInt.push_back(2); listInt.push_back(3); for (list<int>::iterator iter = listInt.begin(); iter != listInt.end(); ++iter) { if (*iter == 2) listInt.erase(iter); } return 0; }
常见错误解决方法
以上的代码对于没有处理过此类问题的”程序猿“来说确实是个问题,erase中会将迭代器销毁掉,这样在erase之后,iter的指向的内容已经乱了,debug的时候可以看到开始是从1开始的,过后就是负多少了,再执行++iter就会报错list iterator not incrementable,简单的解决方法是在erase之后直接break出来就可以了,如果删除操作不至一次那可以利用erase的返回值,其返回值就指向下一个元素,代码如下 :for (list<int>::iterator iter = listInt.begin(); iter != listInt.end();) { if (*iter == 2) { listInt.erase(iter); } else { ++iter; } }
隐晦的错误
#include "stdafx.h" #include <iostream> #include <iterator> #include <list> using namespace std; typedef void(*EVENT_CALLBACK)(const int); struct EventPair { int nEventId; EVENT_CALLBACK funcCallBack; EventPair(const int nEvent, EVENT_CALLBACK callBack) { nEventId = nEvent; funcCallBack = callBack; } }; class EventMgr { public: EventMgr(){} ~EventMgr(){} void RegisterEvent(const int nEvent, EVENT_CALLBACK callBack) { m_EventList.push_back(EventPair(nEvent, callBack)); } void UnRegisterEvent(const int nEvent, EVENT_CALLBACK callBack) { for (list<EventPair>::iterator iter = m_EventList.begin(); iter != m_EventList.end();) { if ((*iter).nEventId == nEvent) { iter = m_EventList.erase(iter); } else { ++iter; } } } void FilterEvent(const int nEvent, int nParam) { for (list<EventPair>::iterator iter = m_EventList.begin(); iter != m_EventList.end(); ++iter) { if ((*iter).nEventId == nEvent) (*iter).funcCallBack(nParam); } } private: list<EventPair> m_EventList; }; class Handle { private: Handle(){} ~Handle(){} public: static Handle& Singleton(); static void doSomethig(int nParam) { // ... } static void doSomethig2(int nParam) { // ... Handle::Singleton().GetEvent().UnRegisterEvent(2, Handle::doSomethig2); } static void doSomethig3(int nParam) { // ... } inline EventMgr& GetEvent() { return m_event; } private: EventMgr m_event; }; Handle& Handle::Singleton() { static Handle sIns; return sIns; } int _tmain(int argc, _TCHAR* argv[]) { // 事件注册 Handle::Singleton().GetEvent().RegisterEvent(1, Handle::doSomethig); Handle::Singleton().GetEvent().RegisterEvent(2, Handle::doSomethig2); Handle::Singleton().GetEvent().RegisterEvent(3, Handle::doSomethig3); // 事件处理 Handle::Singleton().GetEvent().FilterEvent(2, 2); return 0; }
错误截图
错误分析及解决办法
代码有点长是不是,这还是精简版呢,凑合着看吧,不是这么长怎么错误那么难找呢,话说看了一遍代码有没有发现问题,不卖关子了,问题就出在FilterEvent这个函数上,一般的遍历是没有问题,可是这个函数在遍历的过程中调用了回调函数,回调函数是什么鬼,这个你得仔细查查了,本文中的回调函数就是doSomethig(),doSomethig2(),doSomethig3(),其中doSomethig2注销了事件,也就是FilterEvent遍历的过程中调用了doSomethig2(),而它其中包括erase,删除了list原有的元素,导致原有的iter失效,再调用++iter就报了上面的错误。解决办法
防止在容器循环中处理容器的元素,特别是删除,可以复制一份来循环,修改的代码如下:void FilterEvent(const int nEvent, int nParam) { list<EventPair> originalEventList; std::copy(m_EventList.begin(), m_EventList.end(), std::back_inserter(originalEventList)); for (list<EventPair>::iterator iter = originalEventList.begin(); iter != originalEventList.end(); ++iter) { if ((*iter).nEventId == nEvent) (*iter).funcCallBack(nParam); } }
总结
一定要避免在stl容器中处理迭代器,不可避免的时候查一下源代码,选择处理拷贝副本会比较安全。相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- 关于指针的一些事情
- c++ primer 第五版 笔记前言
- share_ptr的几个注意点
- Lua中调用C++函数示例
- Lua教程(一):在C++中嵌入Lua脚本
- Lua教程(二):C++和Lua相互传递数据示例
- C#中Ilist与list的区别小结
- C#中IList<T>与List<T>的区别深入解析
- C++联合体转换成C#结构的实现方法
- C#对list列表进行随机排序的方法
- C++编写简单的打靶游戏
- C++ 自定义控件的移植问题
- C++变位词问题分析
- C/C++数据对齐详细解析
- C++基于栈实现铁轨问题
- C++中引用的使用总结
- 使用Lua来扩展C++程序的方法
- C++中调用Lua函数实例
- Lua和C++的通信流程代码实例