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

C++ 在循环列表时,删除列表中的对象的正确方法是什么?,c++,list,std,erase,C++,List,Std,Erase,我有一个星型结构的列表。这些结构位于std::列表中 我正在双倍循环这个列表,并比较这些位置以检测碰撞。当发现碰撞时,我将删除质量最低的恒星。但是,当我在双循环中时,如何删除恒星,并保持循环继续检查更多碰撞 值得一提的是,第二个循环是反向循环 这里有一些代码 void UniverseManager::CheckCollisions() { std::list<Star>::iterator iStar1; std::list<Star>::reverse

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

我正在双倍循环这个列表,并比较这些位置以检测碰撞。当发现碰撞时,我将删除质量最低的恒星。但是,当我在双循环中时,如何删除恒星,并保持循环继续检查更多碰撞

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

这里有一些代码

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

            }
        }

        }
}
void UniverseManager::CheckCollisions()
{
std::list::迭代器iStar1;
std::list::reverse_迭代器iStar2;
bool totalbreak=false;
对于(iStar1=mStars.begin();iStar1!=mStars.end();iStar1++)
{
对于(iStar2=mStars.rbegin();iStar2!=mStars.rend();iStar2++)
{
如果(*iStar1==*iStar2)
打破
Star&star1=*iStar1;
Star&star2=*iStar2;
if(计算距离(起始1.m位置,起始2.m位置)<10)
{
//碰撞
//得到最重的星星
如果(star1.mMass>star2.mMass)
{
star1.mMass+=star2.mMass;
//我需要删除star2并继续循环;
}
其他的
{
star2.mMass+=star1.mMass;
//我需要删除star1并继续循环;
}
}
}
}
}

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

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\u on\u collized)

(但效率低下,因为它具有O2复杂性)

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

  • 如果您使用外部迭代器擦除,您显然可以停止检查此星与其他星,并打破内部循环。只有当内部循环完成时,您才希望推进外部迭代器,否则它将由erase()推进
  • 如果使用内部循环进行擦除,则已推进迭代,否则,即如果未擦除星号,则需要推进
  • 示例代码如下所示:

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

    你所写的算法会认为每颗恒星都与自身发生了碰撞。考虑替换<代码> istAR2!=mStars.rend()with
    iStar2!=iStar1
    如果(*iStar1==*iStar2)中断,那么第一行就是这个;但是你的路更干净。所以我会改变它。但没有回答我的问题。啊,我现在明白了。考虑一下这个变化,因为否则你会比较每对恒星TWIC.istAR1= MSTAR。擦除(ISTAR1);还请注意,如果决定删除Star1,则不希望继续循环内部循环。您将比较更多的星星与刚才删除的星星。因此,如果我理解正确,调用erase将删除它当前指向的对象,然后返回列表中的下一个元素?您应该使哪个=迭代器?对于reverse::iterator,这是否也适用?如果我有reverse::iterator rit;我将rit=mStars.erase(rit);不是吗?它实际上并没有删除指针以释放指向的内存,它只是从列表中删除指针。您可以手动删除内存,也可以创建一个智能容器来执行此操作。正如您所指出的,对于反向迭代器,它的工作原理应该是相同的。