360 likes | 443 Views
Conteneurs STL. Bibliothèque de modèles standards (STL – Standard Template Library) STL est une bibliothèque de classes du type container (comme par exemple des listes chaînées), d'algorithmes et d'outils d'itération.
E N D
Bibliothèque de modèles standards (STL – Standard Template Library) • STL est une bibliothèque de classes du type container (comme par exemple des listes chaînées), d'algorithmes et d'outils d'itération. • Un conteneur est une structure qui permet d'organiser un ensemble d'objets du même type en séquence. Voici par exemple différents types de conteneurs: • les listes (list), • les vecteurs (vector), • les ensembles (set), • les listes d'associations (map). • La notion de conteneur est importante. Il s'agit de structures algorithmiques permettant d'organiser un ensemble de données en séquence, puis de parcourir ces données.
Namespaces : • La bibliothèque standard se trouve entièrement dans le namespace std. Un namespace (espace de nom ) est un moyen de lutter contre les conflits de noms, par exemple le type list s'appelle en réalité std::list. • Cependant pour ne pas alourdir les exemples de code (et parce que en général, l'emploi de directives using permet ce raccourci), nous omettrons le std::.
Certains noms sont assez communs et peuvent avoir été utilisés par plusieurs librairies. • Les namespace sont une solution à ce problème. Le fichier suivant • namespace Lib // ouverture du namespace • { • class Toto • { • // ... • }; • }; // fermeture du namespace • définit le type Lib::Toto. Cependant quand aucune ambiguïté n'existe, le programmeur peut utiliser le type Toto directement. • Pour utiliser le contenu du namespace Lib, il faut inclure • usingnamespace Lib;
La notion d'itérateur : • L'itérateur est une généralisation des pointeurs. Il permet de parcourir en séquence les éléments d'un conteneur. Pour comprendre l'analogie, regardons le code C++ classique suivant: • 1 void fonction(char* t,const int& taille) • 2 { • 3 for(int i=0;i<taille;i++) • 4 { • 5 t[i] = t[i]+2; • 6 } • 7 }
Afin d'optimiser un tel parcours de tableau (pour éviter à la ligne 5, de calculer deux fois un déplacement de i par rapport à l'adresse de base de t), on peut remplacer ce code par le code suivant: • void fonction(char* t,const int& taille) • { • for(char *p = t,*pstop = t+taille;p < pstop;p++) • { • *p = (*p)+2; • } • }
vector<int>::iterator i; • Pour que cette itérateur pointe sur le premier élément d'une liste donnée, on utilise la méthode begin() de ce vecteur comme ci-dessous: • vector<int> v; • vector <int>::iterator i = v.begin();
Ainsi la traduction en STL de l'exemple précédent est: • void fonction(vector<char>& t) • { • for (vector<char>::iterator i = t.begin(),istop=t.end(); • i != istop;++i) • { • *i = (*i)+2; • } • }
Exemple : • // fichier vect.cpp (exemple des vecteurs : conteneur vector) • #include <iostream> • #include <vector> • using namespace std; • class Cercle { • private: • double rayon; • public: • Cercle(double rayon = 5.0) { • this->rayon = rayon; • } • double surface() { • return 3.14159* rayon*rayon; • } • void afficher(char * mess = "") { • cout << mess << "<rayon: " << rayon • << ", surface: " << surface() << ">\n"; • } • };
//afficher • void afficher(vector<Cercle> vect, char * message) { • if ( vect.size() == 0 ) • cout << message << " est vide\n"; • else { • int rang = 0; • cout << "Contenu de " << message << " de taille " << vect.size() << " :\n"; • for ( vector<Cercle>::iterator iv = vect.begin(); iv != vect.end(); iv++) • { cout << ++ rang << ") "; • iv -> afficher(); • } • cout << endl; } • }
// deuxième manière pour afficher • void afficher(char * message, vector<Cercle> vect) { • if ( vect.size() == 0 ) • cout << message << " est vide\n"; • else { • int rang = 0; • cout << "(Acces via []) Contenu de " << message << " de taille " << vect.size() << " :\n"; • for ( int i = 0 ; i < vect.size() ; i++) • { • cout << ++ rang << ") "; • vect[i].afficher(); • } • cout << endl; • }} • // troisième manière pour afficher • void afficher(vector<Cercle> vect) { • if ( vect.size() == 0 ) • cout << "Vecteur est vide\n"; • else { • int rang = 0; • cout << "Vecteur de taille " << vect.size() << " :\n"; • for(vector<Cercle>::reverse_iterator iv =vect.rbegin(); iv != vect.rend(); iv++) • { cout << ++ rang << ")"; • iv -> afficher(); • } • cout << endl; • }}
int main(){ • vector<Cercle> vect1; • afficher(vect1, "vect 1 au debut"); • vect1.insert(vect1.begin(), Cercle(10.2)); • vect1.insert(vect1.end(), Cercle(6.8)); • vect1.insert(vect1.end(), Cercle(12.4)); • vect1[0].afficher("premier cercle: "); • afficher(vect1, "vect 1 apres quelques ajouts"); • afficher("vect 1 (par la deuxieme maniere)",vect1); • cout << "\n(par la troisieme maniere)Parcourir en sens inverse:\n"; • afficher(vect1); • system("pause"); • }
Classe list : • La classe list est un conteneur ou chaque élément de la liste a son propre segment de mémoire et pointe son prédécesseur et son successeur (liste doublement chainée). • Il faut inclure : • #include <list> • using namespace std ;
Exemple : • // fichier list.cpp (exemple des listes : conteneur list) • #include <iostream> • #include <list> • using namespace std; • /* afficher le contenu d'une liste utilisant des itérateurs */ • void afficher(list<int> liste, char * message) { • if ( liste.size() == 0 ) • cout << message << " est vide\n"; • else { • cout << "Contenu de " << message << ":\n"; • for (list<int>::iterator il = liste.begin(); • il != liste.end(); il++) • cout << *il << endl; • cout << endl; • // première et dernière valeur de la liste • cout << "Premier element : " << liste.front() << endl; • cout << "Dernier element : " << liste.back() << endl; • } • }
void demo1() { • list<int> liste1; // liste VIDE au début • cout << "\n\nDemo 1:\n\n"; • afficher(liste1, "liste1 au debut"); • // ajouter des multiples de 5 entre 1 et 50 au début de la liste • for (int k = 1 ; k <= 50 ; k++) • if (k % 5 == 0) • liste1.insert(liste1.end(), k); • afficher(liste1, "liste1 avec des multiples de 5 entre 1 et 50"); • list<int>::iterator zz = liste1.end(); • zz--; • cout << " ****************** " << * zz << " *********** \n"; • // modifier la valeur du 1er élément • liste1.front() += 70; • // modifier la valeur du dernier élément • liste1.back() *= 2;
// supprimer tous les 25 de la liste • liste1.remove(25); • afficher(liste1, "liste1 modifiee"); • // trier (sort) la liste • liste1.sort(); • afficher(liste1, "liste1 apres le tri"); • // supprimer le début et la fin de la liste: liste1.pop_front(); • liste1.pop_back(); • afficher(liste1, "liste1 apres avoir retire le 1er et le dernier"); • // ajouter au début et à la fin de la liste • liste1.push_front(30); • liste1.push_front(45); • liste1.push_back (15); • liste1.push_back (50); • liste1.push_back (30); • afficher(liste1, "liste1 apres avoir ajoute quelques val. au debut et a la fin"); • liste1.sort(); • afficher(liste1,"liste1 apres encore trie"); • // enlever des valeurs répétées • liste1.unique(); • afficher(liste1, "liste1 apres avoir enleve des val. doubles"); • }
// une manière pour créer une liste à partir d'un tableau • void creer(list<int> & liste, int tableau[], int nbElem) { • for (int i = 0 ; i < nbElem; i++) • liste.insert(liste.end(), tableau[i]); • } • void demo2() { • cout << "\n\nDemo 2:\n\n"; • int age1[] = { 23, 15, 20, 18, 22, 40, 18, 32 }, • age2[] = { 15, 41, 22, 18, 26 }; • list<int> liste1, liste2; • creer(liste1, age1, 8); • creer(liste2, age2, 5); • afficher(liste1, "liste 1 apres la creation"); • afficher(liste2, "liste 2 apres la creation"); • getchar(); • liste1.sort(); • liste2.sort(); • // fusion de 2 listes • liste1.merge(liste2); • afficher(liste1, "liste 1 apres la fusion avec liste 2"); • } • int main() • { • demo1(); • demo2(); • }
STL vector et list • Les tableaux (vector) • La classe vector est définie dans le fichier d'entête : • #include <vector> • Le tableau est le conteneur le plus pratique de la STL. Il encapsule • efficacement les tableaux dynamiques en gérant la réallocation et • les recopies. • Un tableau connait 2 valeurs: • sa taille: le nombre d'éléments qu'il contient, • sa capacité: le nombre d'éléments qu'il peut contenir. • Quand l'ajout d'un élément entraîne (taille>capacité) alors le tableau se réalloue en • doublant sa capacité (stratégie classique). • Pour déclarer un tableau contenant des int, on utilisera : • vector<int> v; • Le tableau a alors une taille nulle (on ne s'occupe pas de sa capacité). • Pour ajouter des éléments, on peut utiliser push_back,push_front.
Méthodes Description • begin() renvoie un itérateur sur le premier élément du vecteur. • end() renvoie un itérateur sur le dernier élément du vecteur. • assign() remplit le vecteur à la manière des constructeurs :b.assign(a.begin(),a.end()) ;ceci rempli le vecteur b avec le contenu du vecteur a. • Une autre utilisation de assign() :b.assign(10,0) ;rempli les dix premiers élément avec la valeur 0. • at() renvoie l’élément situé à la ième position :a.at(10) ;renvoie l’élément situé à la 11ième position (les vecteurs commencent à l’indice 0)
front() renvoie le premier élément du vecteur (ne renvoie pas un itérateur). • back() renvoie le dernier élément du vecteur (ne renvoie pas un itérateur). • capacity() renvoie le nombre de case mémoire disponible avant de réallouer de la mémoire • clear() supprime tous les éléments du vecteur. • empty() renvoie un booléen indiquant si le vecteur est vide (true) ou non (false) • erase() Supprime un élément ou une plage de valeur :a.erase(10) ; //supprime le 11ème élément.a.erase(a.begin(),a.end()) ; // equivalent à a.clear() ; • insert() insère un ou plusieurs éléments dans un vecteur.a.insert(10,2) ; //insère la valeur 2 à la 11 ème place.a.insert(10,2,3) ; //insère 2 fois la valeur 3 à partir de la 11ème • position. • pop_back() supprime et renvoie le dernier élement du • vecteur.
push_back() insère un élément à la fin du vecteur. • reserve() ajoute n cases mémoires au vecteur. • resize() change la taille du vecteur. • size() renvoie le nombre d’élément du tableau (attention différent de capacity() ) • swap() échange deux éléments de deux vecteurs.
Pour accéder directement aux éléments d'un tableau, en lecture ou en écriture, on utilise la notation [] classique: • #include <vector> • #include <iostream> • usingnamespace std; • int main(){ • vector<int> v; • v.push_back(3); // le tableau vaut [3] et est de taille 1 v.push_front(4); // le tableau vaut [4,3] et est de taille 2 cout<<v[0]; // renvoie 4 • v[1] = 5; • cout<<v[1]; // renvoie 5 • }
Attention, aucun contrôle de dépassement d'indice n'est effectué (sinon la classe serait trop lente et pas assez générique). • Si vous tentez d'accéder un élément au delà de la taille du tableau, vous risquez le plantage
Les listes (list) • La classe list est définie dans le fichier d'entête : • #include <list> • Pour déclarer une liste contenant des int, on utilisera : • list<int> l;
Pour insérer ou retirer des éléments dans la liste, on utilisera les commandes suivantes: • #include <list> • usingnamespace std; • void main(){ • list<int> l; • l.push_front(3); // la liste vaut 3::[] l.push_front(2); // la liste vaut 2::3::[] l.push_back(4); // la liste vaut 2::3::4[] l.pop_front(); // la liste vaut 3::4[] l.pop_back(); // la liste vaut 3::[]}
Les fonctions pop_back et pop_front ne retournent pas de valeurs. Elles ne font que dépiler la valeur de queue ou de tête. • Pour accéder à la valeur de queue ou de tête, sans les dépiler, on utilisera: • l.front();// Retourne la valeur en tête de listel.back();// Retourne la valeur en queue de liste • Il est à noter que ces 2 fonctions retournent des références, c'est à dire qu'on peut s'en servir pour modifier le premier où le dernier élément d'une liste comme dans l'exemple ci-dessous: • l.front() = 4;
Les méthodes: • size_t size() const; • Nombre d’élément. • void push_back(const T& x); • Ajoute l’élément x en fin de liste. • void pop_back(); • Supprime le dernier élément de la liste. • iterator insert(iterator position, const T& x); • Insert l’élément x devant l’élément désigné par position. • iterator insert(iterator position); • Insert un élément construit par défaut devant l’élément désigné par position. • void insert(iterator position, const_iterator first, const_iterator last); • Insert une séquence d’élément désigné par first et last devant l’élément désigné par position. • iterator erase(iterator position); • Efface l’élément à la position donnée. • iterator erase(iterator first, iterator last); • Efface les éléments entre first et last exclus. • void clear(); • Efface tous les éléments de la list.
Les méthodes spécifique à la classe List: • void remove(T valeur) • Supprime tous les éléments égaux à valeur. • void sort() • Tri la liste dans l’ordre croissant. • void unique() • Elimine les éléments en double dans une liste triée. • void merge(const list<T>) • Fusionne la liste list avec la liste concernée. A la fin de l’opération, list est vide.