6

我有一个使用 Visual Studio 2003 构建的旧项目,最近我用 vs2005 重新编译了它。但是,在运行时,我收到以下错误:

列表迭代器不可递增

我将程序跟踪到此功能:

void InputQueue::update()
{
    list<PCB>::iterator iter;
    list<PCB>::iterator iterTemp;
    for(iter = begin(); iter != end(); iter++)
    {
        if(iter->arrivalTime == 0)
        {           
            ReadyQueue::getInstance()->add(*iter);
            iterTemp = iter;
            iter++;
            erase(iterTemp);
        }
    }
}

我不是 C++ 专家,这是 VS 调试器给我的。有人可以向我解释问题是什么吗?

谢谢

4

8 回答 8

14

我会将您的循环重写为如下所示:

while (iter != end())
{
  if (iter->arrivalTime == 0)
  {
    ReadyQueue::getInstance()->add(*iter);
    iter = erase(iter);
  }
  else
  {
    ++iter;
  }
}

现在您正在正确地循环检查每个索引的列表。

于 2008-10-13T08:20:07.363 回答
9

请注意,如果iter->arrivalTime == 0,则列表迭代器会增加两次:一次是在元素删除之前,一次是在循环结束时。

如果要删除的项目是列表中的最后一项,这显然不能正常工作。我敢说即使在 VS2003 中它也无法正常工作,但 VS2005 会更好地提醒您。:-)

请记住,迭代过去是未定义的行为end()。绝对可能发生任何事情,例如程序崩溃或(在这种情况下)错误消息。

于 2008-10-13T08:17:20.053 回答
1

我将省略几行代码以显示问题所在:

    for(iter = begin(); iter != end(); iter++) // ***
    {
        if(iter->arrivalTime == 0)
        {                       

                iter++; // ***

        }
    }

在标记为 *** 的两行上,您正在递增迭代器。问题是在两行中的第二行,您没有检查您是否还没有走到容器的末尾。实际上,如果您进入内部循环,您将增加两次,但只检查您是否能够增加一次。

end()一种解决方案是在进行第二次增量之前检查是否处于这种情况,但同样适用于大多数 STL 容器)。

于 2008-10-13T08:19:32.217 回答
1

根本原因是“list.erase()”会改变迭代器。“for”循环的正确写法:

   for (list<CMessage*>::iterator it=que.begin(); it!=que.end(); ++it)
   {
    if(m_type == (*it)->m_type)
    {
        delete *it;
        it=que.erase(it); //"list.erase()" will change the iterator!!!
        if(it==que.end()) break; //Check again!!!
        //still has side effect here. --it?
    }
   }

但它仍然有副作用,所以马克的 while 解决方案将是最好的。

于 2013-11-06T16:31:36.757 回答
0

如果您得到“列表迭代器不兼容”,可能是因为在您的“ReadyQueue::getInstance()->add(*iter);”中 您正在更改 *iter 中的某些内容,这使得哈希算法返回的擦除值与插入期间不同。

于 2008-10-13T09:32:13.087 回答
0

我相信克里斯是对的。但是,另一个问题可能源于您分配给迭代器的事实。– 列表迭代器是否保证可赋值?不看标准,我不这么认为,因为迭代器的 SGI 文档中没有提到可分配性。

于 2008-10-13T08:19:57.910 回答
0

这只是一个旁注,但很重要。

我猜你继承自std::ist<PCB>. 我必须说:继承以重用功能对我来说并不是很好。但是由于您也在“继承”该项目,因此没有什么可做的......

于 2008-10-13T08:26:49.817 回答
0

我可以建议一个更简单的算法吗?

free 函数std::remove_if可用于将您的列表划分为 2,匹配或不匹配谓词的元素(即到达时间==0)。它返回分隔范围的迭代器。然后您可以调用ReadyQueue::getInstance()->add(subrange_begin, subrange_end) (您确实有过载,对吗?)然后擦除子范围。

只是您可以使用 STL 算法而不是编写自己的循环的情况。

于 2008-10-13T09:56:57.607 回答