Как правильно удалить объекты, находящиеся в списке, который вы находите при зацикливании этого списка? - PullRequest
2 голосов
/ 03 января 2012

У меня есть список звездных структур. Эти структуры находятся в 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;
                }

            }
        }

        }
}

Ответы [ 3 ]

2 голосов
/ 03 января 2012

Вам необходимо использовать возвращаемое значение метода стирания следующим образом.

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.

, если вы не увеличиваете итератор, если элемент удален, и проверьте, удалили ли вы последний элемент, тогда вам следуетбудь в порядке.

0 голосов
/ 03 января 2012

Вы хотите использовать результат erase (), чтобы получить следующий итератор и продвинуть цикл по-другому:

  1. Если вы удалите с помощью внешнего итератора, вы явно сможете проверить эту Звезду на других и выйти из внутреннего цикла. Только если внутренний цикл завершен, вы захотите продвинуть внешний итератор, потому что в противном случае он будет продвинут 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;
    }
}
0 голосов
/ 03 января 2012

Поскольку изменение списка делает недействительными итераторы (так что вы не можете увеличивать их), вы должны хранить итераторы в безопасности до того, как список будет изменен.

В большинстве реализаций 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 )

...