C++ Runtime Error :STL list "list iterator not incrementable"

原创 2015年11月22日 13:23:23

问题

最近在项目过程中用到了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;
}

错误截图

运行书错误list

错误分析及解决办法

代码有点长是不是,这还是精简版呢,凑合着看吧,不是这么长怎么错误那么难找呢,话说看了一遍代码有没有发现问题,不卖关子了,问题就出在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++ X STL - Traverse List and Delete Node , Error list iterator not incrementable

Container Traverse for(iterator it = begin(); it != end(); ++it) for(iterator it = begin(); it...

C++ STL学习 List iterator

环境: windows 7 x64, VC 6.0 STL List 中的 iterator存的是什么? 取元素使用 * 运算,比如 *iterSTList ,那么 iterSTList 是指针吗,...

如何避免“iterator not incrementable”错误

最近加入了公司的一个移植项目,目标是把原来在PC平台上的程序放到360上运行。客户给的是PC上的代码和数据,编译过后就能够跑了。但是程序运行时,偶尔会报告错误。在VS2010启动的情况下,可以看到De...

list iterator not decrementable

刚遇到这个“list iterator not decrementable”的问题,网上多数解决方案是这样的: 意思就是在#include "stdafx.h" 的下一行加上#define _HAS...

STL 之 list源代码自行实现(iterator)

一:起因 (1)数据结构里面两种非常重要的存储结构,线性结构中的连续存储结构(代表vector数组)和非连续存储结构(代表list链表),他们两者被广泛的应用在 各个领域,是最基本最基础的两种存储结构...

List With Iterator(STL)

实现标准模板库中的功能#include #include #include using namespace std;template class list { public: struct li...

bug记录:Mybatis-error:Parameter 'xxx' not found. Available parameters are [list]

parameter xxx not found。available parameters are [list]

[C++]List with iterator(链表的迭代器实现)

List with iterator(链表的迭代器实现)我们都知道STL容器内部都有相应的迭代器,本文主要讨论的就是链表内部的迭代器实现。测试代码#include #include using n...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:C++ Runtime Error :STL list "list iterator not incrementable"
举报原因:
原因补充:

(最多只允许输入30个字)