Supprimer un élément du vecteur, alors que dans la boucle C ++ 11 pour la boucle ‘for’?

J’ai un vecteur de IInventory *, et je suis en train de parcourir la liste en utilisant la plage C ++ 11 pour faire des choses avec chacun.

Après avoir fait certaines choses avec un, je peux vouloir le supprimer de la liste et supprimer l’object. Je sais que je peux appeler delete sur le pointeur à tout moment pour le nettoyer, mais quelle est la bonne façon de le supprimer du vecteur, alors que dans la plage for boucle? Et si je le retire de la liste, ma boucle sera-t-elle invalidée?

 std::vector inv; inv.push_back(new Foo()); inv.push_back(new Bar()); for (IInventory* index : inv) { // Do some stuff // OK, I decided I need to remove this object from 'inv'... } 

Non, vous ne pouvez pas. Basé for plage est pour quand vous devez accéder à chaque élément d’un conteneur une fois.

Vous devez utiliser la boucle normale ou l’un de ses cousins ​​si vous devez modifier le conteneur au fur et à mesure, accéder à un élément plusieurs fois ou effectuer une itération non linéaire dans le conteneur.

Par exemple:

 auto i = std::begin(inv); while (i != std::end(inv)) { // Do some stuff if (blah) i = inv.erase(i); else ++i; } 

Chaque fois qu’un élément est supprimé du vecteur, vous devez supposer que les iterators à ou après l’élément effacé ne sont plus valides, car chacun des éléments suivant l’élément effacé est déplacé.

Un for-loop basé sur une plage est simplement un sucre syntaxique pour une boucle “normale” utilisant des iterators, ce qui s’applique ci-dessus.

Cela étant dit, vous pourriez simplement:

 inv.erase( std::remove_if( inv.begin(), inv.end(), [](IInventory* element) -> bool { // Do "some stuff", then return true if element should be removed. return true; } ), inv.end() ); 

Vous ne devriez idéalement pas modifier le vecteur en itérant dessus. Utilisez l’idiome effacer-supprimer. Si vous le faites, vous risquez de rencontrer quelques problèmes. Etant donné que dans un vector un erase invalide tous les iterators en commençant par l’élément effacé jusqu’à la end() vous devrez vous assurer que vos iterators restnt valides en utilisant:

 for (MyVector::iterator b = v.begin(); b != v.end();) { if (foo) { b = v.erase( b ); // reseat iterator to a valid value post-erase else { ++b; } } 

Notez que vous avez besoin du test b != v.end() tel b != v.end() . Si vous essayez de l’optimiser comme suit:

 for (MyVector::iterator b = v.begin(), e = v.end(); b != e;) 

vous courrez dans UB puisque votre e est invalidé après le premier appel d’ erase .

Est-ce une exigence ssortingcte de supprimer des éléments dans cette boucle? Sinon, vous pouvez définir les pointeurs que vous souhaitez supprimer sur NULL et passer une autre fois sur le vecteur pour supprimer tous les pointeurs NULL.

 std::vector inv; inv.push_back( new Foo() ); inv.push_back( new Bar() ); for ( IInventory* &index : inv ) { // do some stuff // ok I decided I need to remove this object from inv...? if (do_delete_index) { delete index; index = NULL; } } std::remove(inv.begin(), inv.end(), NULL); 

désolé pour le nécropostage et également désolé si mon expertise c ++ entrave ma réponse, mais si vous essayez de parcourir chaque élément et d’apporter des modifications possibles (comme l’effacement d’un index), essayez d’utiliser un mot de passe pour la boucle.

 for(int x=vector.getsize(); x>0; x--){ //do stuff //erase index x } 

lors de l’effacement de l’index x, la prochaine boucle sera pour l’élément “devant” la dernière itération. J’espère vraiment que cela a aidé quelqu’un

OK, je suis en retard, mais de toute façon: désolé, pas corrigé ce que j’ai lu jusqu’à présent – c’est possible, vous avez juste besoin de deux iterators:

 std::vector::iterator current = inv.begin(); for (IInventory* index : inv) { if(/* ... */) { delete index; } else { *current++ = index; } } inv.erase(current, inv.end()); 

La simple modification de la valeur indiquée par un iterator n’invalide aucun autre iterator, ce qui nous permet de le faire sans avoir à nous inquiéter. En fait, std::remove_if (l’implémentation de gcc au moins) fait quelque chose de très similaire (en utilisant une boucle classique …), ne supprime rien et n’efface pas.

Sachez, cependant, que ce n’est pas un thread-safe (!) – cependant, cela vaut aussi pour certaines des autres solutions ci-dessus …

Je vais montrer avec exemple, l’exemple ci-dessous supprimer les éléments impairs du vecteur:

 void test_del_vector(){ std::vector 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); } else{ ++it; } } // output all the remaining elements for(auto const& it:vecInt)std::cout< 

sortie aw ci-dessous:

 024 024 024 

Gardez à l'esprit que la méthode erase retournera le prochain iterator de l'iterator passé.

A partir de là , nous pouvons utiliser une méthode plus générasortingce:

 template void erase_where(Container& c, F&& f) { c.erase(std::remove_if(c.begin(), c.end(),std::forward(f)), c.end()); } void test_del_vector(){ std::vector 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< 

Voir ici pour voir comment utiliser std::remove_if . https://fr.cppreference.com/w/cpp/algorithm/remove

Une solution beaucoup plus élégante serait de passer à std::list (en supposant que vous n’ayez pas besoin d’un access aléatoire rapide).

 list widgets ; // create and use this.. 

Vous pouvez alors supprimer avec .remove_if et un foncteur C ++ dans une ligne:

 widgets.remove_if( []( Widget*w ){ return w->isExpired() ; } ) ; 

Donc, je viens d’écrire un foncteur qui accepte un argument (le Widget* ). La valeur de retour est la condition sur laquelle supprimer un Widget* de la liste.

Je trouve cette syntaxe acceptable. Je ne pense pas que j’utiliserais jamais remove_if pour std :: vectors – il y a tellement de inv.begin() et inv.end() que vous feriez probablement mieux d’utiliser une suppression basée sur un index entier ou juste une simple suppression basée sur un iterator régulier (comme indiqué ci-dessous). De toute façon, vous ne devriez pas vraiment vous débarrasser du milieu d’un std::vector ; il est donc conseillé de passer à une list pour ce cas de suppression fréquente au milieu de la liste.

Notez cependant que je n’ai pas eu la possibilité d’appeler delete sur les Widget* qui ont été supprimés. Pour ce faire, cela ressemblerait à ceci:

 widgets.remove_if( []( Widget*w ){ bool exp = w->isExpired() ; if( exp ) delete w ; // delete the widget if it was expired return exp ; // remove from widgets list if it was expired } ) ; 

Vous pouvez également utiliser une boucle régulière basée sur un iterator comme suit:

 // NO INCREMENT v for( list::iterator iter = widgets.begin() ; iter != widgets.end() ; ) { if( (*iter)->isExpired() ) { delete( *iter ) ; iter = widgets.erase( iter ) ; // _advances_ iter, so this loop is not infinite } else ++iter ; } 

Si vous n’aimez pas la longueur de for( list::iterator iter = widgets.begin() ; ... , vous pouvez utiliser

 for( auto iter = widgets.begin() ; ... 

Je pense que je ferais ce qui suit …

 for (auto itr = inv.begin(); itr != inv.end();) { // Do some stuff if (OK, I decided I need to remove this object from 'inv') itr = inv.erase(itr); else ++itr; }