Item uit vector verwijderen tijdens iteratie?

Ik heb een vector die items bevat die actief of inactief zijn. Ik wil dat de grootte van deze vector klein blijft vanwege prestatieproblemen, dus ik wil dat items die als inactief zijn gemarkeerd, uit de vector worden gewist. Ik heb geprobeerd dit te doen tijdens het itereren, maar ik krijg de foutmelding “vector iterators incompatibel”.

vector<Orb>::iterator i = orbsList.begin();
    while(i != orbsList.end()) {
        bool isActive = (*i).active;
        if(!isActive) {
            orbsList.erase(i++);
        }
        else {
            // do something with *i
            ++i;
        }
    }

Antwoord 1, autoriteit 100%

De meest leesbare manier waarop ik dit in het verleden heb gedaan, is door std::vector::erasete gebruiken in combinatie met std::remove_if. In het onderstaande voorbeeld gebruik ik deze combinatie om elk getal kleiner dan 10 uit een vector te verwijderen.

(Voor niet-c++0x kunt u de onderstaande lambda gewoon vervangen door uw eigen predikaat:)

// a list of ints
int myInts[] = {1, 7, 8, 4, 5, 10, 15, 22, 50. 29};
std::vector v(myInts, myInts + sizeof(myInts) / sizeof(int));
// get rid of anything < 10
v.erase(std::remove_if(v.begin(), v.end(), 
                       [](int i) { return i < 10; }), v.end());

Antwoord 2, autoriteit 81%

Ik ben het eens met het antwoord van wilx. Hier is een implementatie:

// curFiles is: vector < string > curFiles;
vector< string >::iterator it = curFiles.begin();
while(it != curFiles.end()) {
    if(aConditionIsMet) {
        it = curFiles.erase(it);
    }
    else ++it;
}

Antwoord 3, autoriteit 25%

Je kunt dat doen, maar je zult je while()een beetje moeten herschikken, denk ik. De functie erase()retourneert een iterator naar het volgende element na de gewiste: iterator erase(iterator position);. Citaat uit de standaard van 23.1.1/7:

De iterator keerde terug van a.erase(q)
wijst onmiddellijk naar het element
volgend op q voordat het element is
gewist. Als een dergelijk element niet bestaat,
a.end() wordt geretourneerd.

Hoewel je misschien in plaats daarvan het Erase-remove idioommoet gebruiken.


Antwoord 4, autoriteit 9%

erasegeeft een pointer terug naar de volgende iteratorwaarde (hetzelfde als Vassilis):

vector <cMyClass>::iterator mit
for(mit = myVec.begin(); mit != myVec.end(); )
{   if(condition)
        mit = myVec.erase(mit);
    else
        mit++;
}

Antwoord 5, autoriteit 5%

Als iemand aan indexen moet werken

vector<int> vector;
for(int i=0;i<10;++i)vector.push_back(i);
int size = vector.size();
for (int i = 0; i < size; ++i)
{
    assert(i > -1 && i < (int)vector.size());
    if(vector[i] % 3 == 0)
    {
        printf("Removing %d, %d\n",vector[i],i);
        vector.erase(vector.begin() + i);
    }
    if (size != (int)vector.size())
    {
        --i;
        size = vector.size();
        printf("Go back %d\n",size);
    }
}

Antwoord 6

Zoals ze zeiden, worden de iterators van vectoren ongeldig gemaakt op vector::erase(), ongeacht welke vorm van iteratorverhoging je gebruikt. Gebruik in plaats daarvan een integer-index.


Antwoord 7

U kunt overwegen een std::listte gebruiken in plaats van een std::vectorvoor uw gegevensstructuur. Het is veiliger (minder vatbaar voor bugs) om te gebruiken bij het combineren van wissen met iteratie.


Antwoord 8

Als u items uit het midden van een vector verwijdert, worden alle iterators naar die vector ongeldig, dus u kunt dit niet doen (bijwerken: zonder toevlucht te nemen tot de suggestie van Wilx).

Als je je zorgen maakt over de prestaties, is het sowieso een slecht idee om items uit het midden van een vector te wissen. Misschien wil je een std::listgebruiken?

Other episodes