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 quelques trucs avec un, je veux peut-être le supprimer de la liste et supprimer l'objet. Je sais que je peux appeler delete
le pointeur à tout moment pour le nettoyer, mais quelle est la bonne façon de le supprimer du vecteur, alors que dans la for
boucle de plage ? Et si je le supprime de la liste, ma boucle sera-t-elle invalidée?
std::vector<IInventory*> 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'...
}
std::remove_if
avec un prédicat qui "fait des trucs" et retourne ensuite true si vous voulez que l'élément soit supprimé.std::list
dessousRéponses:
Non, tu ne peux pas. Basé sur la plage,
for
vous devez accéder une fois à chaque élément d'un conteneur.Vous devez utiliser la
for
boucle normale ou l'un de ses cousins si vous devez modifier le conteneur au fur et à mesure, accéder à un élément plus d'une fois ou effectuer une itération non linéaire dans le conteneur.Par exemple:
la source
true
, AFAIU, et il semble préférable de cette façon de ne pas mélanger la logique d'itération avec le prédicat.remove_if
est mieux.erase
renvoie un nouvel itérateur valide. ce n'est peut-être pas efficace, mais il est garanti que cela fonctionne.Chaque fois qu'un élément est supprimé du vecteur, vous devez supposer que les itérateurs à ou après l'élément effacé ne sont plus valides, car chacun des éléments succédant à l'élément effacé est déplacé.
Une boucle for basée sur une plage n'est que du sucre syntaxique pour une boucle "normale" utilisant des itérateurs, donc ce qui précède s'applique.
Cela étant dit, vous pouvez simplement:
la source
vector
ne sera jamais réalloué en raison d'un appel àerase
. La raison pour laquelle les itérateurs sont invalidés est que chacun des éléments succédant à l'élément effacé est déplacé.[&]
serait appropriée, pour lui permettre de "faire des choses" avec des variables locales.remove_if
avec.erase
, sinon rien ne se passe.std::remove_if
est sur).remove_if
doit être fait en interne). Toutefois , si vous avez unvector
5 éléments et vous ne.erase()
1 à un moment, il n'y a pas d' impact de la performance pour l' utilisation itérateurs vsremove_if
. Si la liste est plus longue, vous devriez vraiment basculerstd::list
là où il y a beaucoup de suppression au milieu de la liste.Idéalement, vous ne devriez pas modifier le vecteur en l'itérant. Utilisez l'idiome effacer-supprimer. Si vous le faites, vous rencontrerez probablement quelques problèmes. Comme dans un
vector
anerase
invalide tous les itérateurs en commençant par l'élément en cours d'effacement jusqu'à la,end()
vous devrez vous assurer que vos itérateurs restent valides en utilisant:Notez que vous avez besoin du
b != v.end()
test tel quel. Si vous essayez de l'optimiser comme suit:vous rencontrerez UB car votre
e
est invalidé après le premiererase
appel.la source
std::remove
, et c'est O (N ^ 2) pas O (N).Est-ce une obligation stricte de supprimer des éléments dans cette boucle? Sinon, vous pouvez définir les pointeurs que vous souhaitez supprimer sur NULL et effectuer un autre passage sur le vecteur pour supprimer tous les pointeurs NULL.
la source
désolé pour le nécropostage et aussi désolé si mon expertise C ++ entrave ma réponse, mais si vous essayez d'itérer chaque élément et d'apporter des modifications possibles (comme l'effacement d'un index), essayez d'utiliser un backwords for loop.
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
la source
OK, je suis en retard, mais de toute façon: Désolé, pas correct ce que je lis à ce jour - il est possible, vous avez juste besoin de deux itérateurs:
Le simple fait de modifier la valeur vers laquelle pointe un itérateur n'invalide aucun autre itérateur, nous pouvons donc le faire sans nous inquiéter. En fait,
std::remove_if
(l'implémentation 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 thread-safe (!) - cependant, cela s'applique également à certaines des autres solutions ci-dessus ...
la source
erase
(à condition que vous supprimiez plus d'un élément, bien sûr)?Je vais montrer avec l'exemple, l'exemple ci-dessous supprime les éléments impairs du vecteur:
sortie aw ci-dessous:
Gardez à l'esprit que la méthode
erase
renverra le prochain itérateur de l'itérateur passé.À partir de là , nous pouvons utiliser une méthode plus générer:
Voir ici pour voir comment l'utiliser
std::remove_if
. https://en.cppreference.com/w/cpp/algorithm/removela source
En opposition au titre de ce fil, j'utiliserais deux passes:
la source
Une solution beaucoup plus élégante serait de passer à
std::list
(en supposant que vous n'ayez pas besoin d'un accès aléatoire rapide).Vous pouvez ensuite supprimer avec
.remove_if
et un foncteur C ++ en une seule ligne:Alors ici, j'écris juste un foncteur qui accepte un argument (le
Widget*
). La valeur de retour est la condition sur laquelle supprimer aWidget*
de la liste.Je trouve cette syntaxe acceptable. Je ne pense pas que je pourrais jamais utiliser
remove_if
pour std :: vecteurs - il y a tantinv.begin()
etinv.end()
bruit là , vous êtes probablement mieux à l'aide d' un indice basé entier suppression ou juste une plaine ancienne base iterator régulière de suppression (comme indiqué au dessous de). Mais vous ne devriez pas vraiment supprimer du milieu destd::vector
beaucoup de toute façon, illist
est donc conseillé de passer à un pour ce cas de suppression fréquente du milieu de la liste.Notez cependant que je n'a pas eu l' occasion d'appeler
delete
lesWidget*
« s qui ont été supprimés. Pour ce faire, cela ressemblerait à ceci:Vous pouvez également utiliser une boucle basée sur un itérateur régulier comme ceci:
Si vous n'aimez pas la longueur de
for( list<Widget*>::iterator iter = widgets.begin() ; ...
, vous pouvez utiliserla source
remove_if
unstd::vector
travail fonctionne réellement, et comment il maintient la complexité à O (N).std::vector
élément fera toujours glisser chaque élément après celui que vous avez retiré, ce qui en faitstd::list
un bien meilleur choix.remove_if
fera glisser chaque élément vers le haut du nombre d'espaces libérés. Au moment où vous tenez compte de l'utilisation du cache,remove_if
sur unestd::vector
suppression probable surpasse d'un fichierstd::list
. Et préserveO(1)
l'accès aléatoire.Je pense que je ferais ce qui suit ...
la source
vous ne pouvez pas supprimer l'itérateur pendant l'itération de la boucle car le nombre d'itérateurs ne correspond pas et après une certaine itération, vous auriez un itérateur invalide.
Solution: 1) prendre la copie du vecteur original 2) itérer l'itérateur en utilisant cette copie 2) faire quelques trucs et le supprimer du vecteur original.
la source
Effacer l'élément un par un conduit facilement à des performances N ^ 2. Mieux vaut marquer les éléments à effacer et les effacer aussitôt après la boucle. Si je peux présumer nullptr dans un élément non valide dans votre vecteur, alors
devrait marcher.
Dans le cas où votre "Do some stuff" ne change pas les éléments du vecteur et n'est utilisé que pour prendre la décision de supprimer ou de conserver l'élément, vous pouvez le convertir en lambda (comme cela a été suggéré dans le message précédent de quelqu'un) et utiliser
la source