wake-up-neo.net

Entferne Elemente eines Vektors innerhalb der Schleife

Ich weiß, dass es ähnliche Fragen wie diese gibt, aber ich habe es nicht geschafft, den Weg meines Codes mit ihrer Hilfe zu finden Ich möchte lediglich ein Element eines Vektors löschen/entfernen, indem ich ein Attribut dieses Elements in einer Schleife prüfe. Wie kann ich das machen? Ich habe den folgenden Code ausprobiert, erhalte jedoch die vage Fehlermeldung:

'operator =' Funktion ist in 'Player' nicht verfügbar.

 for (vector<Player>::iterator it = allPlayers.begin(); it != allPlayers.end(); it++)
 {
     if(it->getpMoney()<=0) 
         it = allPlayers.erase(it);
     else 
         ++it;
 }

Was soll ich machen? 

Update: Denken Sie, dass die Frage Vektor :: mit Zeigerglied löschen dasselbe Problem betrifft? Benötige ich also einen Zuweisungsoperator? Warum? 

61
arjacsoh

Sie sollten it in der for-Schleife nicht erhöhen:

for (vector<Player>::iterator it=allPlayers.begin(); 
                              it!=allPlayers.end(); 
                              /*it++*/) <----------- I commented it.
{

   if(it->getpMoney()<=0) 
      it = allPlayers.erase(it);
  else 
      ++it;
 }

Beachten Sie den kommentierten Teil; it++ wird dort nicht benötigt, da it im For-Body selbst inkrementiert wird.

Der Fehler "'operator =' ist in 'Player'" nicht verfügbar. Er kommt von erase(), die intern operator= zum Verschieben von Elementen im Vektor verwendet. Um erase() verwenden zu können, müssen die Objekte der Klasse Player zuweisbar sein. Dies bedeutet, dass Sie operator= für die Klasse Player implementieren müssen.

Auf jeden Fall sollten Sie rohe Schleife vermeiden.1 so viel wie möglich und sollte lieber Algorithmen verwenden. In diesem Fall kann das beliebte Erase-Remove Idiom die Arbeit erleichtern.

allPlayers.erase(
    std::remove_if(
        allPlayers.begin(), 
        allPlayers.end(),
        [](Player const & p) { return p.getpMoney() <= 0; }
    ), 
    allPlayers.end()
); 

1. Es ist eines von das beste Gespräch von Sean Parent , das ich je gesehen habe.

106
Nawaz
if(allPlayers.empty() == false) {
    for(int i = allPlayers.size() - 1; i >= 0; i--) {
        if(allPlayers.at(i).getpMoney() <= 0) {
            allPlayers.erase( allPlayers.begin() + i ); 
        }
    }
}

Auf diese Weise entferne ich Elemente in vector .. _. Es ist leicht zu verstehen und benötigt keine Tricks.

11
Dawoon Yi

Vergessen Sie die Schleife und verwenden Sie den Standard- oder Boost-Bereich.
Mit Boost.Range und Lambda würde es so aussehen:

boost::remove_if( allPlayers, bind(&Player::getpMoney, _1)<=0 );
11
TimW

Ihr spezielles Problem ist, dass Ihre Player-Klasse keinen Zuweisungsoperator hat. Sie müssen "Player" entweder kopierbar oder beweglich machen, um ihn aus einem Vektor zu entfernen. Dies liegt daran, dass der Vektor zusammenhängend sein muss und daher Elemente neu angeordnet werden muss, um Lücken zu füllen, die beim Entfernen von Elementen erstellt werden.

Ebenfalls:

Verwenden Sie den Standardalgorithmus

allPlayers.erase(std::remove_if(allPlayers.begin(), allPlayers.end(), [](const Player& player)
{
    return player.getpMoney() <= 0;
}), allPlayers.end());

oder noch einfacher, wenn Sie einen Schub haben:

boost::remove_erase_if(allPlayers, [](const Player& player)
{
    return player.getpMoney() <= 0;
});

Siehe TimWs Antwort, wenn Sie keine Unterstützung für C++ 11-Lambdas haben.

5
ronag

Oder machen Sie die Schleife rückwärts.

for (vector<Player>::iterator it = allPlayers.end() - 1; it != allPlayers.begin() - 1; it--)
    if(it->getpMoney()<=0) 
        it = allPlayers.erase(it);
4
hhhhhhhhh

C++ 11 hat eine neue Sammlung von Funktionen eingeführt, die hier von Nutzen sein werden.

allPlayers.erase(
    std::remove_if(allPlayers.begin(), allPlayers.end(),
        [](auto& x) {return x->getpMoney() <= 0;} ), 
    allPlayers.end()); 

Und dann haben Sie den Vorteil, dass Sie nicht so viel Endelemente verschieben müssen.

3
UKMonkey

Späte Antwort, aber ineffiziente Varianten gesehen:

  1. std::remove oder std::remove_if ist der Weg zu gehen.
  2. Wenn diese aus irgendeinem Grund nicht verfügbar sind oder aus irgendeinem anderen Grund nicht verwendet werden können, tun Sie, was sich vor Ihnen verbirgt.

Code zum effizienten Entfernen von Elementen:

auto pos = container.begin();
for(auto i = container.begin(); i != container.end(); ++i)
{
    if(isKeepElement(*i)) // whatever condition...
    {
        *pos++ = *i; // will move, if move assignment is available...
    }
}
// well, std::remove(_if) stops here...
container.erase(pos, container.end());

Möglicherweise müssen Sie eine solche Schleife explizit schreiben. E. G. Wenn Sie den Iterator selbst benötigen, um festzustellen, ob das Element entfernt werden soll (der Bedingungsparameter muss einen Verweis auf das Element akzeptieren, denken Sie daran?), e. G. aufgrund einer bestimmten Beziehung zum Nachfolger/Vorgänger (wenn diese Beziehung jedoch Gleichheit ist, gibt es std::unique ).

0
Aconcagua