web-dev-qa-db-fra.com

vecteur d'effacement d'itérateur

J'ai ce code:

int main()
{
    vector<int> res;
    res.Push_back(1);
    vector<int>::iterator it = res.begin();
    for( ; it != res.end(); it++)
    {
        it = res.erase(it);
        //if(it == res.end())
        //  return 0;
    }
}

"Un itérateur à accès aléatoire pointant vers le nouvel emplacement de l’élément qui suit le dernier élément effacé par l’appel de la fonction, qui est la fin du vecteur si l’opération efface le dernier élément de la séquence."

Ce code se bloque, mais si j'utilise le if (it == res.end ()), puis le retourne, cela fonctionne. Comment se fait-il? La boucle for encaisse-t-elle res.end () afin que l'opérateur non égal échoue?

59
hidayat

res.erase(it) renvoie toujours le prochain itérateur valide. Si vous effacez le dernier élément, il pointe sur .end().

À la fin de la boucle, ++it est toujours appelé. Vous incrémentez donc .end(), ce qui n'est pas autorisé. 

La simple vérification de .end() laisse toujours un bogue, car vous sautez toujours un élément à chaque itération (it est 'incrémenté' par le retour de .erase(), puis de nouveau par la boucle)

Vous voulez probablement quelque chose comme:

 while (it != res.end()) {
        it = res.erase(it);    
 }

effacer chaque élément

(Par souci d’exhaustivité: je suppose qu’il s’agit d’un exemple simplifié. Si vous souhaitez simplement que tous les éléments disparaissent sans qu’il soit nécessaire d’exécuter une opération dessus (par exemple, supprimez), vous devez simplement appeler res.clear()).

Lorsque vous n’effacez que conditionnellement des éléments, vous voulez probablement quelque chose comme

for ( ; it != res.end(); ) {
  if (condition) {
    it = res.erase(it);
  } else {
    ++it;
  }
}
129
Pieter
for( ; it != res.end();)
{
    it = res.erase(it);
}

ou plus généralement:

for( ; it != res.end();)
{
    if (smth)
        it = res.erase(it);
    else
        ++it;
}
27
crazylammer

Comme modification à la réponse de crazylammer, j'utilise souvent:

your_vector_type::iterator it;
for( it = res.start(); it != res.end();)
{
    your_vector_type::iterator curr = it++;
    if (something)
        res.erase(curr);
}

L'avantage est que vous n'avez pas à craindre d'oublier d'incrémenter votre itérateur, ce qui le rend moins sujet aux bogues lorsque vous avez une logique complexe. Dans la boucle, curr ne sera jamais égal à res.end (), et ce sera au prochain élément, que vous l'effaciez ou non de votre vecteur. 

1
Joseph Petroske

Parce que la méthode erase in vector renvoie l'itérateur suivant de l'itérateur passé.

Je vais donner un exemple de la façon de supprimer un élément dans un vecteur lors d’une itération.

void test_del_vector(){
    std::vector<int> vecInt{0, 1, 2, 3, 4, 5};

    //method 1
    for(auto it = vecInt.begin();it != vecInt.end();){
        if(*it % 2){// remove all the odds
            it = vecInt.erase(it); // note it will = next(it) after erase
        } else{
            ++it;
        }
    }

    // output all the remaining elements
    for(auto const& it:vecInt)std::cout<<it;
    std::cout<<std::endl;

    // recreate vecInt, and use method 2
    vecInt = {0, 1, 2, 3, 4, 5};
    //method 2
    for(auto it=std::begin(vecInt);it!=std::end(vecInt);){
        if (*it % 2){
            it = vecInt.erase(it);
        }else{
            ++it;
        }
    }

    // output all the remaining elements
    for(auto const& it:vecInt)std::cout<<it;
    std::cout<<std::endl;

    // recreate vecInt, and use method 3
    vecInt = {0, 1, 2, 3, 4, 5};
    //method 3
    vecInt.erase(std::remove_if(vecInt.begin(), vecInt.end(),
                 [](const int a){return a % 2;}),
                 vecInt.end());

    // output all the remaining elements
    for(auto const& it:vecInt)std::cout<<it;
    std::cout<<std::endl;

}

sortie aw ci-dessous:

024
024
024

Une méthode plus générée:

template<class Container, class F>
void erase_where(Container& c, F&& f)
{
    c.erase(std::remove_if(c.begin(), c.end(),std::forward<F>(f)),
            c.end());
}

void test_del_vector(){
    std::vector<int> vecInt{0, 1, 2, 3, 4, 5};
    //method 4
    auto is_odd = [](int x){return x % 2;};
    erase_where(vecInt, is_odd);

    // output all the remaining elements
    for(auto const& it:vecInt)std::cout<<it;
    std::cout<<std::endl;    
}
0
Jayhello

Ne pas effacer puis incrémenter l'itérateur. Pas besoin d’augmenter, si votre vecteur a un nombre impair (ou même, je ne sais pas), vous manquerez la fin du vecteur.

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

Cela fonctionne pour moi. Et vous n'avez pas besoin de penser aux index déjà effacés.

0
Dawoon Yi

L'instruction it ++ est faite à la fin du bloc. Donc, si vous effacez le dernier élément, essayez d’incrémenter l’itérateur qui pointe vers une collection vide.

0
Patrice Bernassola

Vous incrémentez it après la fin du conteneur (vide) dans l'expression de boucle de la boucle for.

0
kbjorklu

Ce qui suit semble également fonctionner:

for (vector<int>::iterator it = res.begin(); it != res.end(); it++)
{
  res.erase(it--);
}

Vous ne savez pas s'il y a une faille?

0