关于错误map/set iterator not incrementable

来源:互联网 发布:淘宝删除差评步骤 编辑:程序博客网 时间:2024/05/17 23:58

今天Debug代码时,碰到一个关于迭代器的崩溃错误(仅限Debug模式,release会被容错):map/set iterator not incrementable,相关代码如下(仅演示思路代码):

//pretypedeftypedef std::multimap<int, int>::iterator multimapIterator;typedef std::pair<multimapIterator, multimapIterator> milmapIteratorPair;std::set<int> testSet;std::multimap<int, int> muiMap;//muiMap will be sorted automatically:  { (1, 1), (1, 2), (2, 3), (3, 4) }//even through with chaos insert ordermuiMap.insert(std::pair<int, int>(1, 1));muiMap.insert(std::pair<int, int>(2, 3));muiMap.insert(std::pair<int, int>(1, 2));muiMap.insert(std::pair<int, int>(2, 4));for (multimapIterator pMultimapIt = muiMap.begin(); pMultimapIt != muiMap.end(); ++pMultimapIt){    unsigned int iCount = muiMap.count(pMultimapIt->first);    if (2 <= iCount)    {        milmapIteratorPair resultPair = muiMap.equal_range(pMultimapIt->first);        multimapIterator it = resultPair.first;        while (it != resultPair.second)        {            testSet.insert(it->second);            ++it;        }        //erase all elements with key pMultimapIt->first, return erased count        muiMap.erase(pMultimapIt->first);        pMultimapIt = muiMap.begin();    }}

代码初看之下没有问题,功能也都正常:将muiMap中key值出现次数等于或者多于两次的元素的值(value)存到testSet中,并从muiMap将这些元素删除。
但是,实际上该段代码存在两个问题:
1. 特定情况下会访问非法的iterator。
2. 遍历时,会有元素遗漏,尽管对功能没有影响。
我们一个一个来看。

1. 特定情况下会访问非法的iterator

这个问题多少跟for循环的执行顺序有关系,对于如下for循环:

for(A.initialization; B.condition; C.increment){    D...}

其执行顺序为:
A->B->D->C->B->D->C->B->D…C->B->D,直到条件不满足。

而当muiMap中所有元素的出现次数都满足条件时,对于如下代码:

for (multimapIterator pMultimapIt = muiMap.begin(); pMultimapIt != muiMap.end(); ++pMultimapIt){    ...    pMultimapIt = muiMap.begin();    ...}

此时muiMap为空,muiMap.begin()等于muiMap.end(),这样pMultimapIt也就指向了一个非法的iterator,所以下次for循环时,++pMultimapIt是对非法iterator自增操作,会产生错误,该错误的一个修改方法是添加muiMap判空操作,如果为空直接跳出循环,但这样解决不了第二个问题。而且在下面会看到,解决第二个问题的同时,这个问题也可以解决掉。

2. 遍历时,会有元素遗漏,尽管对功能没有影响

例如,对于如下multimap: { (1, 1), (1, 2), (2, 3), (3, 4) }
第一次循环时,key值1出现次数满足条件,所以删除之:

muiMap.erase(pMultimapIt->first);

multimap为:{ (2, 3), (3, 4) },之后执行:

pMultimapIt = muiMap.begin();

此时pMultimapIt指向pair: (2, 3)。然后for循环判断未到达容器末尾,因此执行++pMultimapIt,此时pMultimapIt指向(3, 4),对于(2, 3)的遍历被跳过了,之所以在此例子中不影响功能,是因为如果被跳过的元素的key值出现次数多于两次,跳过一个仍然可以被删掉,这得益与multimap自动排序,key相同的元素会被排到一起。所以,在某些情况下,pMultimapIt不应该自增。修改后的代码如下:

...//no ++pMultimapIt anymorefor (multimapIterator pMultimapIt = muiMap.begin(); pMultimapIt != muiMap.end();)   {    unsigned int iCount = muiMap.count(pMultimapIt->first);    if (2 <= iCount)    {        milmapIteratorPair resultPair = muiMap.equal_range(pMultimapIt->first);        multimapIterator it = resultPair.first;        while (it != resultPair.second)        {            testSet.insert(it->second);            ++it;        }        muiMap.erase(pMultimapIt->first);        pMultimapIt = muiMap.begin();    }    else//increase pMultimapIt under conditions.    {        ++pMultimapIt;    }}

经过如上修改,由于删掉元素后,pMultimapIt会指向新的元素,素以此时pMultimapIt不需要增加,只有未删掉元素(iCount > 2)时,需要继续遍历下一个元素,所以自增pMultimapIt。
对于问题1,如果muiMap被删为空,则此时pMultimapIt等于muiMap.end(),跳出for循环,问题1解决。

总结

对于容器的迭代器(iterator)操作,一定要小心迭代器非法的情况,此时对于迭代器的任何操作,都会导致显性或者隐性的问题,尤其很多容器在erase操作后,迭代器会失效,此时需要重新初始化迭代器,保证后对于它的操作合法。

0 0
原创粉丝点击