STL - UQAC
Transcription
STL - UQAC
Les conteneurs associatifs La bibliothèque standard contient plusieurs conteneurs permettant l’ajout, la suppression et la recherche dans des équences ordonnées d’objets. Ces classes sont généralement implémentées à l’aide d’arbres binaires de recherche auto-balancés. La plupart des implémentation utilisent des arbres rouge-noirs plutôt que des arbres AVL. 1 La classe map Nous commencerons par discuter de la classe map. Un map (aussi appelé dictionnaire) est un conteneur gérant des paires de valeurs. Il permet, à partir d’une valeur (la clef), d’accéder à une autre (la valeur mappée). La définition d’un map ressemble à: template<class Clef, class Valeur> class map { public: typedef Clef key_type; typedef Valeur mapped_type; typedef pair<const Clef,Valeur> value_type; iterator begin(); iterator end(); mapped_type& operator[] (const key_type& k); iterator find(const key_type& k); pair<iterator, bool> insert(const value_type& val); void erase(iterator pos); int erase(const key_type& k); void clear(); // autres membres }; Les seules conditions imposées par le modèle est que l’operateur < doit être défini pour les éléments de type key_type et un constructeur de défaut doit 1 être défini pour les éléments de type mapped_type. Le type value_type est défini à l’aide de la structure pair déclarée dans le fichier d’en-tête <utility>: template<class A, class B> struct pair { A first; B second; pair(const A& x, const B& y) : first(x), second(y) {} }; Les itérateurs de la classe map pointent vers les noeuds de l’arbre contenant des éléments de type value_type. Ils servent à parcourir une séquence de noeuds selon l’ordre défini pour key_type. Par exemple, supposons que M est un conteneur de type map<string, int> alors il est possible d’afficher la séquence ordonnée de ses éléments à l’aide du code suivant: map<string,int>::iterator it; for (it=M.begin(); it!=M.end(); it++) cout<< it->first << "\t" << it->second <<endl; La principale caractéristique de la classe map est l’examen associatif des éléments fourni par l’opérateur d’indiçage (operator[]). Contrairement au vector, l’indice est de type const key_type n’est pas nécessairement un entier. Poursuivant avec l’exemple précédent, on pourrait écrire: string s; cin >> s; cout << M[s]; L’opération d’indiçage M [s] identifie la paire (string, int) appropriée et retourne une référence de sa partie int. Si la clef s n’est pas trouvée, la paire (s, d) sera crée et ajoutée à M , où d est la valeur de défaut d’un mapped_type. C’est la raison pour laquelle la classe mapped_type doit posséder un constructeur par défaut. Une instruction telle que M["INF"]=109; 2 aura donc pour effet, ou bien de modifier une valeur existante, ou encore d’ajouter la nouvelle paire (00 IN F 00 , 109). La fonction insert tente d’ajoutee une paire (s, k) au conteneur M . Les clefs d’un map étant unique, l’insertion est réalisée seulement s’il n’existe pas déjà un élément dans M possédant la clef s. La valeur retournée est une pair<iterator, bool>. Le bool est 1 si l’insertion a réussi et 0 sinon. Dans les deux cas, après l’insertion M contiendra un élément dont la clef est s et l’iterateur retourné pointera vers cet élément. 2 La classe set La classe set peut être vu comme un cas particulier de la classe map où les deux composantes d’une paire sont identiques. On utilise un set lorsque la clef est la seule information qui nous intéresse. template<class Clef> class set { public: typedef Clef value_type; typedef Clef key_type; iterator begin(); iterator end(); iterator find(const key_type_type& k); pair<iterator, bool> insert(const value_type& val); void erase(iterator pos); int erase(const key_type& k); void clear(); // autres membres }; On remarque que la définition des classe set et map sont similaires. La principale différence est l’absence de l’opérateur d’indiçage dans la classe set. 3 3 Multimap et multiset Les multimap et les multiset sont similaires aux classes map et set sauf que plusieurs éléments peuvent posséder la même clef. Les fonctions membres suivantes sont aussi disponibles pour les classes set et map mais sont surtout utiles pour les multiset et les multimap. iterator lower_bound(const key_type& k); iterator upper_bound(const key_type& k); pair<iterator,iterator> equal_range(const key_type& k); La fonction lower(k) retourne un itérateur pointant sur le premier éléments possédant la clef k. La fonction upper(k) retourne un itérateur vers le premier élément dont la clef est supérieure à k. Dans les deux cas, si l’élément recherché est inexistant, l’iterateur end() est retourné. Finalement, la fonction equal_range(const clef& k) retourne la paire d’itérateurs (lower(k), upper(k)). L’exemple suivant illustre comment afficher de façon ordonnée tous les éléments de m possédant la clef s. void afficher(multimap<string,int>& m, string s) { typedef multimap<string,int>::iterator MI; pair<MI,MI> g=m.equal_range(s); for(MI p=g.first; p!=g.second; ++p) cout<<p->Elem<<endl; } 4 Utilisation des objets fonctions Les modèles set, map, multi_set et multi_map utilisent un paramètre supplémentaire qui n’a pas été mentionné dans les sections précédentes. Par exemple la classe map ressemble à: 4 template<class Clef, class Valeur, class Cmp=less<Clef> > class map { public: typedef Clef key_type; typedef Valeur mapped_type; typedef pair<const Clef,Valeur> value_type; typedef Cmp key_compare; // Les reste est identique sauf que les occurences x<y // sont remplacées par key_compare(x,y) }; Le paramètre Cmp est utilisé comme fonction de comparaison des clefs. Lorsque Cmp n’est pas spécifié, la valeur par défaut est l’objet fonction less défini dans le fichier d’en-tête <functional>: template <class Elem> struct less { bool operator()(const Elem& a, const Elem& b) { return (a<b); } }; Ainsi, lorsque l’on ne spécifie pas le paramètre Cmp, il est nécessaire que l’opérateur de comparaison < soit définit pour les objets de type Clef. Dans certaine situation, il est préférable de fournir explicitement le paramètre Cmp. Considérons par exemple un dictionnaire map<string, string> dans lequel les mots sont ordonnés sans tenir compte des lettres minuscules et majuscules. L’opérateur par défaut des chaı̂nes de caractères ne peut pas être utilisé puique, avec le jeu de caractère ASCII, on aura "Zoo" < "ane". Nous devons donc construire notre propre opérateur de comparaison sous la forme d’un objet fonction: 5 struct SansMajuscule { bool operator()(const string&, const string&) const; }; /* * retourne x<y sans tenir compte des majuscules. */ bool SansMajuscule::operator()(const string& x, const string& y) const { string:: const_iterator p=x.begin(); string:: const_iterator q=y.begin(); // toupper est défini dans <ctype.h> while (p!=x.end() && q!=y.end() && toupper(*p)==toupper(*q)) { ++p; ++q; } if (p==x.end()) return q!=y.end(); if (q==y.end()) return false; return toupper(*p)<toupper(*q); } L’exemple suivant montre comment créer et utiliser un dictionnaire possédant la propriété désirée: map<string, string, SansMajuscule> Dictionnaire; D["professeur"]="Titre d’un enseignant au debut du trimestre"; D["oppresseur"]="Titre d’un enseignant a la fin du trimestre"; D["Zombi"]="Etat d’un etudiant a la fin du trimestre"; D["Voisins du sud"]="Nom que l’on donne aux americains"; D["CRETIN du sud"]="Nom que l’on donne au président americain"; map<string,string,SansMajuscule>::iterator it; for (it=D.begin(); it!=D.end(); ++it) cout<<it->first<<"\t"<<it->second<<endl; 6