在循环列表时,删除列表中的对象的正确方法是什么

What is proper way to delete objects that resides in a list that you find while looping that list?

本文关键字:列表 对象 方法 是什么 删除列 循环 删除      更新时间:2023-10-16

我有一个Star结构的列表。这些结构位于std::list 中

我正在对这个列表进行双循环,并对那里的位置进行压缩,以检测碰撞。当发现碰撞时,我会删除质量最低的恒星。但当我处于双循环中时,我如何删除恒星,并保持循环以检查更多碰撞?

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

这是一些代码

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;
                }
            }
        }
        }
}

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

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.

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

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

在大多数实现中,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)的"冲突"(但效率低下,因为具有O2复杂性)

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

  1. 如果你使用外部迭代器擦除,你显然可以避免将这个Star与其他Star进行比较,并打破内部循环。只有当内部循环完成时,您才会希望推进外部迭代器,因为否则它将被erase()推进
  2. 如果使用内部循环擦除,则已经推进了迭代,否则,即,如果没有擦除星形,则需要推进

示例代码看起来像这样:

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;
    }
}