删除驻留在循环列表时找到的列表中的对象的正确方法是什么?

时间:2012-01-02 20:35:57

标签: c++ list std erase

我有星形结构列表。这些结构位于std :: list

我正在对此列表进行双循环并对那些位置进行比较以检测碰撞。当发现A碰撞时,我将删除质量最低的Star。但是当我在双循环中时如何删除Star,并保持循环以检查更多碰撞?

值得一提的是,第二个循环是一个反向循环。

这是一些代码

void UniverseManager::CheckCollisions()
{
    std::list<Star>::iterator iStar1;
    std::list<Star>::reverse_iterator iStar2;
    bool totalbreak = false;

    for (iStar1 = mStars.begin(); iStar1 != mStars.end(); iStar1++)
    {
        for (iStar2 = mStars.rbegin(); iStar2 != mStars.rend(); iStar2++)
        {
            if (*iStar1 == *iStar2)
                break;
            Star &star1 = *iStar1;
            Star &star2 = *iStar2;

            if (CalculateDistance(star1.mLocation, star2.mLocation) < 10)
            {
                // collision
                // get heaviest star
                if (star1.mMass > star2.mMass)
                {
                    star1.mMass += star2.mMass;
                    // I need to delete the star2 and keep looping;
                }
                else
                {
                    star2.mMass += star1.mMass;
                    // I need to delete the star1 and keep looping;
                }

            }
        }

        }
}

3 个答案:

答案 0 :(得分:2)

你需要像这样使用擦除方法的返回值。

iStar1 = mStars.erase(iStar1);
erase = true;
if (iStar1 == mStars.end())
   break; //or handle the end condition

//continue to bottom of loop

if (!erase)
   iStar1++; //you will need to move the incrementation of the iterator out of the loop declaration, because you need to make it not increment when an element is erased.

如果你没有增加迭代器,如果一个项目被删除并检查你是否删除了最后一个元素那么你应该没事。

答案 1 :(得分:0)

由于修改列表会使迭代器无效(因此无法递增它们),因此必须在更改列表之前保持迭代器的安全。

在大多数实现中,std :: list是一个双链表,因此是一个像

这样的迭代
for(auto i=list.begin(), ii; i!=list.end(); i=ii)
{
    ii = i; ++ii; //ii now is next-of-i

    // do stuff with i

    // call list.erasee(i).
    // i is now invalid, but ii is already the "next of i"
}

最安全的方法是创建一个包含所有“碰撞”的列表,然后迭代“碰撞”调用list.remove(*iterator_on_collided) (但效率低,因为O 2 复杂度)

答案 2 :(得分:0)

您希望使用erase()的结果来获取下一个迭代器并以不同方式推进循环:

  1. 如果你使用外部迭代器擦除,你显然可以突然检查这个星星对其他人并打破内循环。只有在内部循环完成时,你才需要推进外部迭代器,否则它将被erase()提前。
  2. 如果你使用内循环擦除你已经提前迭代,否则,即如果没有星被擦除,你需要前进。
  3. 示例代码看起来像这样:

    for (auto oit(s.begin()), end(s.end()); oit != end; )
    {
        auto iit(s.begin());
        while (iit != end)
        {
            if (need_to_delete_outer)
            {
                oit = s.erase(oit);
                break;
            }
            else if (need_to_delete_inner)
            {
                iit = s.erase(iit);
            }
            else
            {
                ++iit;
            }
        }
        if (iit == end)
        {
            ++oit;
        }
    }