2010-11-22 48 views
8

Je viens de Java en C++, et j'ai une situation de conception commune dans laquelle j'ai un élément (un non-primitif) que je voudrais retirer d'un fichier std :: vector. En Java, j'écrirais quelque chose comme arrayList.remove (arrayList.indexOf (myClassInstance));IndexOf de style ArrayList pour std :: vector en C++?

en C++, avec un std :: vector, quel est le meilleur moyen/le plus performant/le plus propre de faire cela? La meilleure chose à laquelle je puisse penser est de créer une référence à l'instance que je recherche, puis de parcourir le vecteur jusqu'à trouver cette référence. essentiellement, comparer l'adresse mémoire de chaque élément du vecteur avec la référence jusqu'à ce que j'obtienne une correspondance.

suis-je sur la bonne voie? ou y a-t-il une meilleure façon de le faire? (Peut-être en utilisant un autre conteneur std, je ne l'ai utilisé std :: vecteur à ce jour.)

+0

En supposant que vous avez une collection de pointeurs ou shared_ptr, std :: set peut bien fonctionner pour vous , juste en comparant les adresses des pointeurs. Si vous connaissez l'adresse de l'article que vous cherchez juste mySet.effacer (ptr); – CashCow

+0

@CashCow - Y at-il une grande différence entre les performances d'itération de tous les membres d'un ensemble std :: vs std: vector? ailleurs dans mon code, j'appelle une méthode sur chaque élément de l'ensemble, à chaque cycle. – ericsoco

Répondre

8
#include <algorithm> 

std::vector<Foo>::iterator it = std::find(vec.begin(), vec.end(), foo_2b_found); 
if (it != vec.end()) vec.erase(it); 
+1

Je pense qu'il vous manque des choses à la fin de cet appel 'std :: find' :) –

+1

n'est-ce pas une mauvaise idée d'effacer en itérant? ou je suppose que ce n'est pas une mauvaise idée ici car une fois que nous appelons vector.erase, nous en avons fini avec l'itérateur et cela n'a plus d'importance s'il est invalidé. – ericsoco

+0

@Billy: Merci d'avoir repéré ça :) @eric: Si nous avions * fait * itérer manuellement, nous devions faire très attention (mais nous ne le faisons pas). L'invalidation d'Iterator est un sujet très intéressant. Est-ce que je sens une autre FAQ? ;-) – fredoverflow

4

Utilisez std::find pour trouver l'élément et vector::erase pour le supprimer. Passe par le vecteur pour trouver l'élément, et vous ne pouvez pas faire mieux avec un vecteur simple (le même est le cas avec ArrayList de Java). Que vous utilisiez ou non un conteneur différent dépend de vos besoins.

+0

+1. Notez que si vous supprimez plusieurs éléments correspondant à un prédicat, vous devez utiliser 'std :: remove_if' trop :) –

+0

oo. ne se rendait pas compte qu'il y avait tant de choses que l'on pouvait faire avec des conteneurs std ... - http://www.cplusplus.com/reference/algorithm/ – ericsoco

+0

@eric: Bienvenue dans le merveilleux monde de la programmation générique! – fredoverflow

1

Si vous souhaitez rechercher de façon linéaire à travers le vecteur puis

seq.erase(std::find(seq.begin(), seq.end(), elt)); 

Si vous avez un prédicat et que vous voulez supprimer tous les éléments qui correspondent au prédicat alors:

seq.erase(std::remove_if(seq.begin(), seq.end(), Pred), seq.end()); 

Aucun ces méthodes sont les plus performantes car elles nécessitent une recherche linéaire et même si votre élément est trouvé très tôt, l'effacement est coûteux car il doit déplacer tous les autres éléments en les gardant contigu s. L'utilisation de std :: list traiterait de la dernière de celles-ci: la recherche serait linéaire mais l'effacement serait un temps constant.

S'il est possible de stocker vos éléments dans un conteneur associatif qui utilise une recherche de clé, cela serait plus efficace: recherche O (log N) et suppression du temps constant.

Une carte de hachage peut être encore meilleure, proche de la recherche et de la suppression du temps constant. Pour ce que vous suggérez, c'est-à-dire en effaçant par le pointeur de l'objet, vous pouvez utiliser std :: set pour votre type T. Ensuite, utilisez mySet.erase(pt); où pt est votre pointeur. Vous devez gérer la durée de vie de vos pointeurs, bien sûr, mais le fait que vous sachiez lequel effacer de votre collection suggère que vous en ayez une copie ailleurs.

Vous pouvez utiliser std :: set, SharedPtrLess>

où vous définissez SharedPtrLess comme suit:

template< typename T > 
struct SharedPtrLess 
{ 
    bool operator()(boost::shared_ptr<T> left, boost::shared_ptr<T> right) const 
    { 
    return std::less<T>()(left.get(), right.get()); 
    } 
}; 
+0

de bons conseils pour l'avenir. Je vais commencer par mettre std :: vector sous ma ceinture, et passer à autre chose ... merci! – ericsoco