Chapitre XII Bibliothque standard de modles Standard Template
Chapitre XII Bibliothèque standard de modèles (Standard Template Library ou STL) du C++ Emploi des conteneurs standards Chapitre XII - Bibliothèque standard de modèles (STL) Algorithmes génériques
Bibliothèque standard de modèles (STL) du C++ En voie de standardisation. Développée chez HP. Le plus souvent, les compilateurs n’implantent cette bibliothèque partiellement. Il s’agit de pouvoir définir et utiliser aisément des collections d’objets appelées conteneurs comme par exemple, fondamentaux ou séquentiels associatifs les vecteurs à taille variable, <vector> les listes doublement chaînées, <list> les files à double extrémité, <deque> les ensembles ordonnés d’éléments distincts, <set> les ensembles ordonnés d’éléments avec répétition possible, <multiset> les listes associatives sans répétition possible, <map> les listes associatives avec répétition possible, <multimap> Chapitre XII - Bibliothèque standard de modèles (STL) 2
Bibliothèque standard de modèles (STL) du C++ les piles <stack> adaptateurs les files d’attente <queue> les files d’attente avec priorité <priority_queue> Approche basé sur le développement d’algorithmes génériques parce qu’ils peuvent être employés avec de nombreux types de conteneurs différents. Usage intensif du mécanisme des templates du C++. Basé sur l’idée qu’un algorithme n’a pas à être lié à la structure de données utilisée. Les conteneurs et les algorithmes de la STL peuvent travailler conjointement en douceur parce qu’ils ignorent tout les uns des autres. Évite de réécrire continuellement du code : une bibliothèque de structures de données réutilisables permet aux programmes d’être développés plus facilement, promet la fiabilité et la portabilité. Auteurs : Stepanov, Lee & collaborateurs. 3
Les caractéristiques de base de STL sont présentées à partir du conteneur <vector>. Le conteneur <vector> Définition d’un objet de la classe modèle « vector » vector <type_des_composantes> nom_d_objet // Vecteur vide. vector <type_des_composantes> nom_d_objet(taille du vecteur initiale) vector <type_des_composantes> nom_d_objet(taille du vecteur initiale, valeur d’initialisation) Exemple : vector <double> salaires(10); Pour placer des données dans salaires ou y accéder, vous spécifier l’emplacement du vecteur que vous voulez employer. Cela est effectué à l’aide de l’opérateur []. Exemple : double valeur; salaires[4] = 35000; valeur = salaires[0]; La numérotation des emplacements commence à 0. Chapitre XII - Bibliothèque standard de modèles (STL) 4
Le conteneur <vector> Exemple : #include <vector> #include <iostream. h> using namespace std; // Obligatoire void main() { // Créer le crible, initialement vrai. const int taillecrible = 100; vector <bool> crible(taillecrible, true); } // Recher les positions avec vrai et barrer les multiples. for (int i = 2; i * i < taillecrible; i++) if (crible[i]) for (int j = i + i; j < taillecrible; j += i) crible[j] = false; // Affichage des nombres premiers. for (int i = 2; i < taillecrible; i++) if (crible[i]) cout << i << " " ; Chapitre XII - Bibliothèque standard de modèles (STL) 5
Le conteneur <vector> Vous pouvez connaître la taille d’un vecteur en appelant la fonction size. Exemple : double maximum = salaires[0]; for (i = 1; i < salaires. size(); i++) if (salaires[i] > maximum) maximum = salaires[i]; Il arrive souvent que l’on ne sache pas initialement combien d’éléments devront être stockés. Lorsqu’un vecteur est défini sans paramètre de taille, il est vide et ne peut contenir aucun élément. La fonction push_back permet de démarrer avec un vecteur vide et de le faire croître: Exemple : vector <double> salaires; . . . double s; cin >> s; . . . salaires. push_back(s); Modifie la taille du vecteur en lui ajoutant un élément à la fin. 6
Le conteneur <vector> Mise en garde : Si vous savez déjà de combien d’éléments vous avez besoin dans un vecteur, mieux vaut spécifier sa taille lors de sa définition, puis le remplir. Faire grossir à la demande un vecteur avec push_back est inefficace : il faut trouver davantage de mémoire pour stocker le vecteur agrandi; tous les éléments doivent être recopiés dans cet espace de plus grande taille. Une autre fonction membre, pop_back, supprime le dernier élément d’un vecteur en réduisant sa taille d’un élément, mais ne le renvoie pas. Exemple : vector <double> salaires; . . . double dernier = salaires[salaires. size() - 1]; salaires. pop_back(); récupère 7 supprime
Le conteneur <vector> #include <iostream. h> using namespace std; // Obligatoire void main() { vector <int> v, w; // Vecteur vide cout << "Test si v est vide = " << v. empty() << endl; cout << "Taille de v = " << v. size() << endl; cout << "Taille maximum de v = " << v. max_size() << endl; v. push_back(12); v. push_back(9); v. push_back(123); v. push_back(12); cout << "Taille de v = " << v. size() << endl; Chapitre XII - Bibliothèque standard de modèles (STL) 8
Le conteneur <vector> cout << "Le vecteur v est : "; for (size_t i = 0; i < v. size(); i++) cout << v[i] << " "; cout << endl; w. push_back(5); w. push_back(13); cout << "Taille de w = " << w. size() << endl; v. swap(w); cout << "Le vecteur v est : "; for (size_t j = 0; j < v. size(); j++) cout << v[j] << " "; cout << endl; cout << "Le vecteur w est : "; for (size_t k = 0; k < w. size(); k++) cout << w[k] << " "; cout << endl; Chapitre XII - Bibliothèque standard de modèles (STL) 9
Le conteneur <vector> v = w; cout << "Taille de v = " << v. size() << endl; cout << "Vecteurs egaux : " << (v == w) << endl; } Test si v est vide = 1 Taille de v = 0 Taille maximum de v = 1073741823 Taille de v = 4 Le vecteur v est : 12 9 123 12 Taille de w = 2 Le vecteur v est : 5 13 Le vecteur w est : 12 9 123 12 Taille de v = 4 Vecteurs egaux : 1 Chapitre XII - Bibliothèque standard de modèles (STL) 10
Le conteneur <vector> Il est aussi simple d’employer des vecteurs d’objets. Exemple : vector <Employe> personnel(10); . . . personnel[0] = Employe(. . . ); Dépend du constructeur utilisé dans la classe Employe. Les fonctions peuvent posséder des paramètres vecteur. Exemple : #include <iostream> #include <vector> using namespace std; double moyenne(vector <double> v) { double somme = 0; for (int i = 0; i < v. size(); i++) somme = somme + v[i]; return somme / v. size(); 11 }
Le conteneur <vector> Exemple (suite) : void main() { vector <double> salaires(5); salaires[0] = 23000. 0; salaires[1] = 42000. 0; salaires[2] = 53000. 0; salaires[3] = 67000. 0; salaires[4] = 49000. 0; double moyenne_salaires = moyenne(salaires); cout << "Le salaire moyen est : " << moyenne_salaires << endl; } 12
Le conteneur <vector> Une fonction peut modifier un vecteur en modifiant les éléments individuels ou en modifiant l’ordre des éléments. Le vecteur doit être transmis par référence. Exemple : void tri(vector <double> & v) Une fonction peut renvoyer un vecteur. Exemple I : Renvoie toutes les valeurs appartenant à une plage [debut, fin]. vector <double> entre(vector <double> v, double debut, double fin) { vector <double> resultat; for (int i = 0; i < v. size(); i++) if (debut <= v[i] && v[i] <= fin) resultat. push_back(v[i]); return resultat; 13 }
Le conteneur <vector> Exemple I (suite) : void main() { vector <double> salaires(5); salaires[0] = 23000. 0; salaires[1] = 42000. 0; salaires[2] = 53000. 0; salaires[3] = 67000. 0; salaires[4] = 49000. 0; vector <double> salaires_medians = entre(salaires, 40000. 0, 65000. 0); for (int i = 0; i < salaires_medians. size(); i++) cout << salaires_medians[i] << endl; } Chapitre XII - Bibliothèque standard de modèles (STL) 14
Le conteneur <vector> Exemple II : Renvoie les positions de toutes les valeurs appartenant à une plage [debut, fin]. vector <int> positions(vector <double> v, double debut, double fin) { vector <int> pos; for (int i = 0; i < v. size(); i++) if (debut <= v[i] && v[i] <= fin) pos. push_back(i); return pos; } Chapitre XII - Bibliothèque standard de modèles (STL) 15
Le conteneur <vector> Exemple II (suite) : void main() { vector <double> salaires(5); salaires[0] = 23000. 0; salaires[1] = 42000. 0; salaires[2] = 53000. 0; salaires[3] = 67000. 0; salaires[4] = 49000. 0; vector <int> P = positions(salaires, 40000. 0, 65000. 0); for (int i = 0; i < P. size(); i++) cout << salaires[P[i]] << endl; } Chapitre XII - Bibliothèque standard de modèles (STL) 16
Le conteneur <vector> Suppression d’un élément dans un vecteur non ordonné : recouvrez simplement l’élément concerné par le dernier élément du vecteur et réduisez la taille du vecteur. void erase(vector<string> & v, int pos) { int derniere_pos = v. size() – 1; v[pos] = v[derniere_pos]; v. pop_back(); } 0 i size() - 1 Chapitre XII - Bibliothèque standard de modèles (STL) 17
Le conteneur <vector> Suppression d’un élément dans un vecteur ordonné : déplacez tous les éléments situés en dessous de l’élément à supprimer vers le haut d’une case et réduire la taille du vecteur. void erase(vector<string> & v, int pos) { for (int i = pos; i < v. size() – 1; i++) v[i] = v[i + 1]; v. pop_back(); } 0 i . . . size() - 1 Chapitre XII - Bibliothèque standard de modèles (STL) 18
Le conteneur <vector> Insertion d’un élément dans un vecteur ordonné : ajoutez un nouvel élément à la fin du vecteur, en débutant du bas du vecteur, copiez vers le bas dans le nouvel élément le dernier élément, et en remontant jusqu’à la position de l’élément à insérer. void insert(vector<string> & v, int pos, string s) { int dernier = v. size() – 1; v. push_back(v[dernier]); for (int i = dernier; i > pos; i--) v[i] = v[i - 1]; v[pos] = s; } Chapitre XII - Bibliothèque standard de modèles (STL) 0 i . . . size() - 1 19
Rappel Comparaison avec un tableau en C++ Impossible de modifier la taille d’un tableau : elle doit être connue lors de la compilation du programme. La fonction size() n’existe pas pour un tableau : c’est la responsabilité du programmeur. Les tableaux sont plus rapides que les vecteurs. double salaires[10]; au lieu de vector <double> salaires(10); Contrairement à un vecteur, un tableau peut être rempli de valeurs lors de sa définition. double salaires[] = {31000, 25000, 45000, 75000, 43000, 60000, 76000, 87000, 98000, 42000}; Chapitre XII - Bibliothèque standard de modèles (STL) 20
Rappel Comparaison avec un tableau en C++ Une fonction ayant un paramètre tableau exige de placer des crochets [] vides à côté du nom du paramètre : double maximum(const double a[], int taille_a); Les paramètres tableau sont toujours transmis par référence. Les fonctions peuvent modifier les paramètres tableau, ces modifications affectant les tableaux transmis comme paramètres. Vous n’employez jamais de & lors de la définition d’un paramètre tableau. double accroitre(double a[], int taille_a, double augmentation) { for (int i = 0; i < taille_a; i++) a[i] = a[i] + augmentation; Chapitre XII - Bibliothèque standard de modèles (STL) 21 }
Rappel Comparaison avec un tableau en C++ Même si un tableau peut être un paramètre de fonction, ce ne peut être un type de renvoi de fonction. L’appelant de la fonction doit fournir un paramètre tableau devant renfermer les résultats. void lecture(double a[], int capacite_a, int & taille_a) { taille_a = 0; double x; while (taille_a < capacite_a && (cin >> x)) { a[taille_a] = x; taille_a++; } } void main() { double salaires[100]; int taille_salaires = 0; . . . lecture(salaires, 100, taille_salaires); . . . } Chapitre XII - Bibliothèque standard de modèles (STL) 22
Rappel Comparaison avec un tableau en C++ emploie un tableau avec 2 indices pour stocker un tableau bidimensionnel : Exemple : const int soldes_lignes = 11; const int soldes_colonnes = 6; double soldes[soldes_lignes][soldes_colonnes]; . . . soldes[3][4] = 245. 0; l’élément en position 3 * soldes_colonnes + 4 fixés au temps de compilation Un tableau bidimensionnel est stocké comme une séquence de lignes. En transmettant un tel tableau à une fonction, on doit spécifier le # de colonnes comme constante, le nombre de lignes peut être variable. Chapitre XII - Bibliothèque standard de modèles (STL) 23
Rappel Comparaison avec un tableau en C++ Exemple : void print(const double table[][soldes_colonnes], int nombre_de_lignes) { for (int i = 0; i < nombre_de_lignes; i++) for (int j = 0; j < soldes_colonnes; j++) cout << table[i][j]; } void main() {. . . print(soldes, 5); . . . } obligatoire afin de permettre au compilateur de trouver l’élément table[i][j]. Chapitre XII - Bibliothèque standard de modèles (STL) 24
Le conteneur <vector> Pour parcourir un tableau de scalaires (int, char, …), on utilise généralement comme indice un entier. Pour parcourir une liste chaînée dynamique, on utilise généralement comme indice un pointeur. STL fournit un riche ensemble d’indices appelés itérateurs permettant de parcourir aisément et de diverses façons les conteneurs. Les algorithmes ne manipulent jamais directement les conteneurs mais indirectement via les itérateurs. La syntaxe d’utilisation d’un itérateur est celle d’un pointeur; c’est pourquoi un itérateur est souvent appelé un pointeur généralisé. Plus précisément, dans chaque classe de conteneur est défini une classe iterator dont les variables sont les itérateurs. adresse pointeur position dans un conteneur itérateur Chapitre XII - Bibliothèque standard de modèles (STL) 25
Le conteneur <vector> Dans la classe iterator sont définies les surcharges de certains opérateurs de façon à simuler le comportement d’un pointeur. Points communs à toutes les sortes d’itérateurs: i++ fait avancer l’itérateur i d’une position dans le conteneur, i-- fait reculer l’itérateur i d’une position dans le conteneur, i += n (resp. i -= n) fait avancer (resp. reculer) l’itérateur i de n positions, Il existe des fonctions begin() et end() retournant respectivement la position du premier élément et la position qui suit immédiatement celle du dernier élément du conteneur, Si i et j sont deux itérateurs (ou 2 positions), les expressions booléennes (i == j) et (i != j) ont leur signification habituelle. Si i est un itérateur, *i réfère à l’élément du conteneur en position i. Chapitre XII - Bibliothèque standard de modèles (STL) 26
Le conteneur <vector> UN DEUX TROIS FIN TROIS DEUX UN #include <vector> #include <iostream. h> using namespace std; // Obligatoire void main() { vector <char *> s; // Vecteur vide s. push_back("UN"); s. push_back("DEUX"); s. push_back("TROIS"); s. push_back("FIN"); vector <char *>: : iterator i; // Définition d'un itérateur. for (i = s. begin(); i != s. end(); i++) cout << *i << endl; for (i = s. end(), i--; i >= s. begin(); i--) cout << *i << endl; } Chapitre XII - Bibliothèque standard de modèles (STL) 27
Le conteneur <vector> Les itérateurs se divisent en 2 catégories selon leur mode d’accès : - permettent une opération d’écriture dans un conteneur: vector <T>: : iterator i; - ceux qui n’autorisent que la lecture vector <T>: : const_iterator j; Dans STL, il y a 5 catégories d’itérateurs selon les opérateurs pris en charge : Itérateur d’entrée (input_iterator): renvoie une valeur ( ==, !=, ++, *). Itérateur de sortie (output_iterator): affectations ( ==, !=, ++, *). Itérateur avant (forward_iterator) : permet de lire ou d’écrire dans un conteneur, mais le traverse dans une seule direction du premier au dernier ( ==, !=, ++, *). Itérateur arrière (reverse_iterator) : le parcours se fait dans l’autre sens. Itérateur bidirectionnel (bidirectional_iterator) : traverse un conteneur dans les 2 sens. Itérateur d’accès aléatoire (random_access_iterator) : accède en temps constant à n’importe quelle position 28 ( ==, !=, ++, --, [], *, itérateur + n, itérateur - itérateur).
Classes spéciales d’itérateurs (A) Itérateurs rétrogrades Il existe une classe iterator et des fonctions begin() et end() permettant de définir des itérateurs, d’accéder aux éléments du conteneur et de le parcourir dans le sens direct. STL donne la possibilité d’effectuer un parcours inversé grâce à la définition de la classe reverse_iterator, ainsi que des fonctions rbegin() et rend(). Retourne la position du dernier élément. Retourne la position qui précède immédiatement la position du premier élément d’un conteneur. Chapitre XII - Bibliothèque standard de modèles (STL) 29
Classes spéciales d’itérateurs #include <vector> Fait appel à un constructeur de la classe vector dont le prototype est: #include <iostream. h> vector(const T *premier, using namespace std; // Obligatoire const T *dernier); void main() { char * tab[] = {"UN", "DEUX", "TROIS", "FIN"}; vector <char *> s(tab, tab + 4); vector <char *>: : reverse_iterator i; // Définition d'un itérateur. for (i = s. rbegin(); i != s. rend(); i++) cout << *i << endl; Initialise un vecteur avec les valeurs } de type T contenues dans l’intervalle d’adresses [premier, dernier[. Chapitre XII - Bibliothèque standard de modèles (STL) 30
Classes spéciales d’itérateurs (B) Itérateurs d’insertion Un itérateur en sortie permet d’écrire des données dans un conteneur. Il faut par contre s’assurer que la place en mémoire où va s’effectuer l’écriture est bien allouée comme élément du conteneur. Considérons l’algorithme suivant (voir plus loin) : copy(v. begin(), v. end(), w. begin()); recopie les éléments du vecteur v dans le vecteur w en autant que la taille de w soit au moins égale à celle de v. Un itérateur d’insertion: un itérateur qui, préalablement à une écriture dans un conteneur, alloue la place où se fera l’écriture. La taille du conteneur augmentera. Chapitre XII - Bibliothèque standard de modèles (STL) 31
Classes spéciales d’itérateurs (B) Itérateurs d’insertion Un constructeur d’itérateur d’insertion a donc toujours en premier argument le conteneur dans lequel doit se faire l’écriture. 3 classes d’itérateurs d’insertion sont définies par STL : - insert_iterator - back_insert_iterator - front_insert_iterator. Pour les utiliser, il faut inclure #include <iterator>. insert_iterator : Un itérateur de cette classe insère un élément juste avant la position qu’il pointe. Une telle position doit être transmise en argument à son vector<int> w; constructeur pour son initialisation. vector<int>: : iterator i; Itérateur standard i = w. begin() +2; Position d’insertion insert_iterator<vector<int> > j(w, i); Itérateur d’insertion dans w en position i. Chapitre XII - Bibliothèque standard de modèles (STL) 32
Classes spéciales d’itérateurs (B) Itérateurs d’insertion L’itérateur d’insertion j étant défini, l’instruction copy(v. begin(), v. end(), j); a pour effet de recopier les éléments du vecteur v en les insérant dans le vecteur w entre son 2 e et 3 e élément. Note: back_insert_iterator (resp. front_insert_iterator) permet d’insérer un élément à la fin du conteneur (resp. en tête du conteneur). front_insert_iterator est valide pour les listes seulement. Chapitre XII - Bibliothèque standard de modèles (STL) 33
Classes spéciales d’itérateurs #include <vector> #include <iterator> #include <iostream. h> using namespace std; // Obligatoire void main() { char * tab[] = {"UN", "DEUX", "TROIS", "ZERO", "QUATRE", "FIN"}; vector <char *> s(tab, tab + 3), w(tab+3, tab + 6); vector <char *>: : iterator i; // Définition d'un itérateur. i = w. begin() + 1; insert_iterator<vector<char *> > j(w, i); Chapitre XII - Bibliothèque standard de modèles (STL) 34
Classes spéciales d’itérateurs copy(s. begin(), s. end(), j); for(i=w. begin(); i!= w. end(); i++) cout << *i << endl; back_insert_iterator<vector<char *> > k(w); k = "SUITE"; k = "D ’ENTIERS"; for(i=w. begin(); i!= w. end(); i++) cout << *i << endl; } Dans les 3 classes d’itérateurs d’insertion, l’opérateur d’affectation = est surchargé. La chaîne "SUITE" est insérée en position k; la position k est incrémentée. Chapitre XII - Bibliothèque standard de modèles (STL) 35
Classes spéciales d’itérateurs ZERO UN DEUX TROIS QUATRE FIN SUITE D’ENTIERS Chapitre XII - Bibliothèque standard de modèles (STL) 36
Classes spéciales d’itérateurs (C) Itérateurs d’entrée-sortie La STL a introduit les notions d’itérateurs de flots d’entrée et de sortie afin de pouvoir employer ses algorithmes en lien avec les diverses opérations E/S. Deux classes : istream_iterator et ostream_iterator. <iterator> doit être inclus. 2 constructeurs istream_iterator(); istream_iterator(istream & flot); Retourne un itérateur déterminant la fin de fichier Recueille le type de donnée sur le flot d’entrée Chapitre XII - Bibliothèque standard de modèles (STL) 37
Classes spéciales d’itérateurs #include <vector> #include <iterator> #include <iostream> using namespace std; // Obligatoire void main() { vector <int> v; vector <int>: : iterator i; istream_iterator<int>IE(cin); // Itérateur de flot d'entrée // positionné au début du fichier. istream_iterator<int> FIN; // Itérateur de flot d'entrée // positionné à la fin du fichier. Chapitre XII - Bibliothèque standard de modèles (STL) 38
Classes spéciales d’itérateurs while (IE != FIN) { v. push_back(*IE); // *IE contient ce qui est recueilli. cout << "taille de v : » << v. size() << endl; IE++; // Chaque incrémentation de IE permet de // recueillir une nouvelle donnée à l’entrée // jusqu’à réception du caractère de fin de // fichier (CTRL-D sous Unix). } for (i = v. begin(); i != v. end(); i++) cout << *i << endl; } Chapitre XII - Bibliothèque standard de modèles (STL) 39
Classes spéciales d’itérateurs ostream_iterator. 2 constructeurs ostream_iterator(ostream &flot); Un itérateur de cette classe écrit dans flot les données successives sur lesquelles il pointe. ostream_iterator(ostream & flot char * separation); Une chaîne de caractères de séparation est intercalée à chaque fois. Chapitre XII - Bibliothèque standard de modèles (STL) 40
Classes spéciales d’itérateurs #include <vector> #include <iterator> #include <fstream> #include <iostream> using namespace std; // Obligatoire void main() { vector <int> v; vector <int>: : iterator i; istream_iterator<int>IE(cin); // Itérateur de flot d'entrée // positionné au début du fichier. istream_iterator<int> FIN; // Itérateur de flot d'entrée // positionné à la fin du fichier. Chapitre XII - Bibliothèque standard de modèles (STL) 41
Classes spéciales d’itérateurs while (IE != FIN) { v. push_back(*IE); // *IE contient ce qui est recueilli. cout << "taille de v : " << v. size() << endl; IE++; } for (i = v. begin(); i != v. end(); i++) cout << *i << endl; Chapitre XII - Bibliothèque standard de modèles (STL) 42
Classes spéciales d’itérateurs ofstream F_creation("D: \Programmation orientée objets\Essai. doc"); ostream_iterator<int>j(F_creation, " "); // Itérateur de flot de sortie positionné au début du fichier. // Les entiers sont séparés par des espaces. copy(v. begin(), v. end(), j); // Copie de v dans F_creation. ifstream F_lecture("D: \Programmation orientée objets\Essai. doc"); istream_iterator<int> m(F_lecture); istream_iterator<int> fin_de_fichier; ostream_iterator<int> k(cout); copy(m, fin_de_fichier, k); cout << endl; } Chapitre XII - Bibliothèque standard de modèles (STL) 43
Conteneur « list » Définition d’un objet de la classe modèle « list » représentant une liste doublement chaînée list <type_des_composantes> nom_d_objet Exemple : list <string> Nom_des_employes; On peut employer la fonction push_back pour ajouter un élément à la liste. Exemple : Nom_des_employes. push_back("Bob"); Les éléments n’étant pas stockés dans un bloc de mémoire contigu, on ne peut faire référence directement à Nom_des_employes[3]. Vous devez à la place visiter chaque élément tour à tour, à partir du début de la liste. Chapitre XII - Bibliothèque standard de modèles (STL) 44
Conteneur « list » Pour visiter un élément, vous employez un itérateur de liste. Cet itérateur marque une position dans la liste. Exemple : list <string>: : iterator pos; Pour obtenir la position de début dans la liste, il s’agit d’appeler la fonction begin de la classe list. Exemple : pos = Nom_des_employes. begin(); Pour déplacer l’itérateur vers la position suivante (ou précédente), servez-vous de l’opérateur ++ (ou --). Exemple : pos++; Chapitre XII - Bibliothèque standard de modèles (STL) 45
Conteneur « list » Pour obtenir l’élément stocké à une position, utilisez l’opérateur *. Exemple : string element = * pos; Pour modifier l’élément stocké à une position, utilisez aussi l’opérateur *. Exemple : * pos = "Bab"; Pour insérer un élément avant la position de l’itérateur, servez-vous de « insert » . Exemple : Nom_des_employes. insert(pos, "Bibi"); Pour insérer un élément avant le 1 er élément de la liste, servez-vous de « begin » . Exemple : pos = Nom_des_employes. begin(); Nom_des_employes. insert(pos, "Billy"); 46
Conteneur « list » Pour insérer un élément à la fin de la liste, servez-vous de « end » . Cette position de fin ne correspond à aucun élément de la liste mais pointe après la fin de celle-ci. Exemple : pos = Nom_des_employes. end(); Nom_des_employes. insert(pos, "Blaise"); Exemple : string element = * Nom_des_employes. end(); // ERREUR. « end » sert aussi de point d’arrêt de tout parcours de la liste. Exemple : pos = Nom_des_employes. begin(); while (pos != Nom_des_employes. end()) { cout << *pos << "n"; pos++; } ou encore, for (pos = Nom_des_employes. begin(); pos != Nom_des_employes. end(); pos++) 47 cout << *pos << "n";
Conteneur « list » Pour supprimer un élément d’une liste, servez-vous de « erase » après avoir déplacé l’itérateur à la position à supprimer. Exemple : On efface le second élément de la liste : pos = Nom_des_employes. begin(); pos++; Nom_des_employes. erase(pos); 48
Exemple d’utilisation d’un conteneur « list » // // // // Pierre possède un entrepôt de pièces mécaniques. Chaque pièce mécanique possède un numéro unique, une description sommaire de la pièce, le coût unitaire, un seuil de ravitaillement (> 0). Pierre doit savoir à tout moment le nombre d'unités d'une pièce qu’il possède dans son entrepôt. Il doit en assurer la mise à jour immédiate à chaque fois qu’il reçoit une livraison de pièces de l’un de ses fournisseurs. Il en est de même lorsqu’il reçoit une commande de l’un de ses clients. Finalement, à chaque fin de cycle, il doit pouvoir afficher à l’écran les pièces mécaniques dont la quantité est en dessous du seuil de ravitaillement. Donnez la spécification fonctionnelle et l'implantation des classes "piece_mecanique" et "Entrepot". 49
#include <iostream> #include <list> #include <string> #include <cassert> using namespace std; class Piece_mecanique { private : int numero_identification; string description; float cout_unitaire; int seuil_de_ravitaillement; int nb_unites_disponibles; 50
public : Piece_mecanique (int numero, string desc, float cout_unit, int seuil, int nb_unites ); // Constructeur d'une pièce mécanique. // Pré numero >= 0, cout_unit > 0, seuil >= 0, // nb_unites >= 0. int Acces_numero() const; // Retourne le numéro d'identification du produit. string Acces_desc() const; // Retourne la description de la pièce. float Acces_cout() const; // Retourne le coût unitaire de la pièce. int Acces_seuil() const; // Retourne le seuil de ravitaillement de la pièce. 51
int Acces_nb_unites() const; // Retourne le nombre d'unités de la pièce disponibles. void Modifie_nb_unites(int Nb_unites); // Additionne au nombre d'unités disponibles la valeur passée // en paramètre, celle-ci pouvant être négative. }; Piece_mecanique: : Piece_mecanique ( int numero, string desc, float cout_unit, int seuil, int nb_unites ) { assert( (numero >= 0) && (cout_unit > 0) && (seuil >= 0) && (nb_unites >= 0)); numero_identification = numero; description = desc; cout_unitaire = cout_unit; seuil_de_ravitaillement = seuil; nb_unites_disponibles = nb_unites; } 52
int Piece_mecanique: : Acces_numero() const { return numero_identification; } string Piece_mecanique: : Acces_desc() const { return description; } float Piece_mecanique: : Acces_cout() const { return cout_unitaire; } int Piece_mecanique: : Acces_seuil() const { return seuil_de_ravitaillement; } int Piece_mecanique: : Acces_nb_unites() const { return nb_unites_disponibles; } 53
void Piece_mecanique: : Modifie_nb_unites(int Nb_unites) { nb_unites_disponibles += Nb_unites; } ostream & operator << (ostream & out, const Piece_mecanique & p) { out << "Produit : " << p. Acces_numero(); return out; } class Entrepot { private : list <Piece_mecanique> inventaire; 54
public : Entrepot(); // Permet de créer un entrepôt vide. void Ajouter_une_piece( const Piece_mecanique & P); // Permet d'ajouter une pièce mécanique à l'entrepôt. // // Pré Le numéro d'identification de la pièce n'existe pas déjà. // // Post - La pièce mécanique P est ajoutée à l'entrepôt. void Livraison( int numero, int quantite_unites_livrees); // Permet de mettre à jour les quantités en stock dans l'entrepôt // suite à une livraison de "quantite_unites_livrees" unités de la // pièce dont le numéro d'identification est fourni. // // Pré Le numéro d'identification de la pièce existe. // quantite_unites_livrees > 0. // // Post - Les quantités livrées sont ajoutés dans les quantités 55 // en stock pour la pièce identifiée par numéro.
void Commande( int numero, int quantite_commandee); // Permet de mettre à jour les quantités en stock dans l'entrepôt // suite à une commande de "quantite_commandee" unités de la pièce // dont le numéro d'identification est fourni. // // Pré Le numéro d'identification de la pièce existe. // quantite_commandee > 0. // La quantité commandée est disponible dans l'entrepôt. // // Post - Les quantités commandées ne font plus partie des quantités // en stock pour la pièce identifiée par numéro. int Nombre_d_unites_disponibles(int numero); // Retourne le nombre d'unités en stock dans l'entrepôt de la pièce // identifiée par "numero". // // Pré Le numéro d'identification de la pièce existe. // // Post - Retourne le nombre d'unités en stock dans l'entrepôt de la // pièce identifiée par "numero". 56 //
bool Existence_d_une_piece(int numero); // Vérifie l'existence d'une pièce à partir d'un no. d'identification. // // Pré numero >= 0. // Post - Retourne true si le type de pièce existe. // False autrement. void Afficher(); // Permet d'afficher à l'écran les pièces mécaniques dont la quantité // est en dessous du seuil de ravitaillement. }; Entrepot: : Entrepot() {} void Entrepot: : Ajouter_une_piece(const Piece_mecanique & P) { assert(Existence_d_une_piece(P. Acces_numero()) == false); inventaire. push_back(P); } 57
void Entrepot: : Afficher() { list<Piece_mecanique>: : iterator j; for (j = inventaire. begin(); j != inventaire. end(); j++) if((*j). Acces_nb_unites() < (*j). Acces_seuil()) cout << (*j); } void Entrepot: : Livraison( int numero, int quantite_unites_livrees) { assert(Existence_d_une_piece(numero)); assert(quantite_unites_livrees > 0); list<Piece_mecanique>: : iterator i; for (i = inventaire. begin(); i != inventaire. end(); i++) if ((*i). Acces_numero() == numero) { (*i). Modifie_nb_unites(quantite_unites_livrees); return; } 58 }
void Entrepot: : Commande ( int numero, int quantite_commandee ) { assert(Existence_d_une_piece(numero)); assert(quantite_commandee > 0); list<Piece_mecanique>: : iterator i; for (i = inventaire. begin(); i != inventaire. end(); i++) if ((*i). Acces_numero() == numero) { assert(quantite_commandee <= (*i). Acces_nb_unites()); (*i). Modifie_nb_unites(- quantite_commandee); return; } } 59
int Entrepot: : Nombre_d_unites_disponibles ( int numero ) { assert(Existence_d_une_piece(numero)); list<Piece_mecanique>: : iterator i; for (i = inventaire. begin(); i != inventaire. end(); i++) if ((*i). Acces_numero() == numero) return (*i). Acces_nb_unites(); } bool Entrepot: : Existence_d_une_piece(int numero) { assert(numero >= 0); list<Piece_mecanique>: : iterator i; for (i = inventaire. begin(); i != inventaire. end(); i++) if ((*i). Acces_numero() == numero) return true; return false; } 60
void main() { Entrepot T; T. Ajouter_une_piece (* new Piece_mecanique(12305, "cable", 6. 35 f, 3, 5)); T. Ajouter_une_piece (* new Piece_mecanique(29645, "essieu", 123. 45 f, 2, 4)); T. Ajouter_une_piece (* new Piece_mecanique(57890, "hachette", 32. 45 f, 1, 2)); T. Afficher(); T. Commande(12305, 4); T. Livraison(29645, 3); T. Commande(57890, 2); T. Afficher(); cout } << "Piece 29645 : " << T. Nombre_d_unites_disponibles(29645) << endl; 61
Conteneur stack : exemple I #include <iostream> #include <stack> using namespace std; int factorielle(int N) { int i; int fact; stack<int> S; if (N == 0) return 1; for(i = N; i >= 1; i = i - 1) S. push(i); fact = S. top(); S. pop(); Chapitre XII - Bibliothèque standard de modèles (STL) 62
Exemple I (suite) while(S. empty() == false) { i = S. top(); S. pop(); fact = i * fact; }; return fact; } void main() { int n = 0; cout << "Entrez un nombre entier positif ou nul : "; cin >> n; cout << "La factorielle de " << n << " est : " << factorielle(n); Chapitre XII - Bibliothèque standard de modèles (STL) 63 }
Conteneur stack : implantation template <class T, class C = deque<T> > class pile { private: C donnees; public: int size() const; void push(const T & x); T & top(); void pop(); }; template <typename T, typename C> void pile<T, C>: : push(const T & x) { donnees. push_back(x); } template <typename T, typename C> int pile<T, C>: : size() const { return donnees. size(); } template <typename T, typename C> void pile<T, C>: : pop() { donnees. pop_back(); } template <typename T, typename C> T & pile<T, C>: : top() { return donnees. back(); } Chapitre XII - Bibliothèque standard de modèles (STL) 64
Conteneur stack : implantation Caractéristique intéressante des piles et des files : celles-ci ne sont pas réellement des conteneurs au sens propre, mais une adaptation construite à partir d’un des conteneurs précédents : le second argument de la classe pile. Le champ donnees est le conteneur renfermant les éléments. Chaque fonctionnalité de la classe pile est implanté en effectuant un appel à une fonction membre de ce conteneur. La déclaration est identique à pile<int> P; pile<int, deque<int> > P; Si pour une raison quelconque, vous ne voulez pas employer le deque comme conteneur sous-jacent; il s’agit simplement de fournir un 2 e argument : pile<int, vector<int> > P; Les piles fonctionnent avec des vecteurs ou des dèques; les files fonctionnent avec des listes ou des dèques. Exercice : Fournir une implantation d’une file d’attente. Chapitre XII - Bibliothèque standard de modèles (STL) 65
Conteneur deque // // // // // Exemple tiré de C. Horstmann & T. Budd, La Bible C++. Micro Application, pp. 840 -843. Nous allons illustrer l'emploi d'un deque avec un programme simulant le service des clients d'une succursale bancaire. Supposez que vous êtes le directeur de la succursale et deviez décider du nombre de guichets nécessaires. La simulation suivante peut vous aider à décider. Un nouveau client arrive à chaque minute avec une probabilité de 0, 9. Le temps de traitement d'un client est une valeur aléatoire uniforme entre 2 et 8 minutes. Le programme enregistre le temps passé par les clients dans la file d'attente, ainsi que le nombre total de clients servis. La division du premier par le second donne le temps moyen d'attente d'un client. Chapitre XII - Bibliothèque standard de modèles (STL) 66
#include <iostream> #include <deque> #include <cassert> using namespace std; int rand_int(int a, int b) { return a + rand() % (b - a + 1); } double rand_double(double a, double b) { return a + (b - a) * rand() * (1. 0 / RAND_MAX); } const int TEMPS_TRAITEMENT_MIN = 2; const int TEMPS_TRAITEMENT_MAX = 8; Chapitre XII - Bibliothèque standard de modèles (STL) 67
class Client { private : int heure_arrivee; int temps_traitement; public : Client(int arrivee = 0); // // Constructeur qui fixe l'heure d'arrivée du client et génère aléatoirement le temps de traitement. Pré - arrivee >= 0. int Acces_heure_arrivee() const; // Retourne l'heure d'arrivée du client. int Acces_temps_traitement() const; // }; Retourne le temps de traitement du client. Chapitre XII - Bibliothèque standard de modèles (STL) 68
Client: : Client(int arrivee) : heure_arrivee(arrivee), temps_traitement(rand_int(TEMPS_TRAITEMENT_MIN, TEMPS_TRAITEMENT_MAX)) { assert(arrivee >= 0); } int Client: : Acces_heure_arrivee() const { return heure_arrivee; } int Client: : Acces_temps_traitement() const { return temps_traitement; } Chapitre XII - Bibliothèque standard de modèles (STL) 69
class Guichet { private: bool libre; Client client; int heure_debut_de_service; public: Guichet(); // Constructeur d'un guichet libre. bool Est_libre() const; // Retourne true si le commis est en mesure // de servir un client. void Modifie_statut_commis(int horloge); // Dans l'éventualité où le commis du guichet était // occupé à servir un client, mettre son statut à libre // si le temps de traitement de ce client est écoulé. // Pré horloge >= 0. void Assigner_client(const Client & C, int horloge); // Permet d'assigner un client à un guichet libre. // Pré horloge >= 0. Le guichet est libre. }; Chapitre XII - Bibliothèque standard de modèles (STL) 70
Guichet: : Guichet() { libre = true; } bool Guichet: : Est_libre() const { return libre; } void Guichet: : Modifie_statut_commis(int horloge) { assert(horloge >= 0); if (libre == false) if (horloge > heure_debut_de_service + client. Acces_temps_traitement()) libre = true; } void Guichet: : Assigner_client(const Client & C, int horloge) { assert((horloge >= 0) && (libre == true)); libre = false; client = C; heure_debut_de_service = horloge; Chapitre XII - Bibliothèque standard de modèles (STL) } 71
void main() { const int NOMBRE_DE_GUICHETS = 5; const int DUREE_DE_LA_SIMULATION = 60; const double TAUX_ARRIVEE = 0. 9; double attente_totale = 0; int nombre_de_clients = 0; deque <Guichet> Service(NOMBRE_DE_GUICHETS); deque <Client> file_d_attente; Chapitre XII - Bibliothèque standard de modèles (STL) 72
for (int horloge = 0; horloge < DUREE_DE_LA_SIMULATION; horloge++) { if (rand_double(0, 1) < TAUX_ARRIVEE) file_d_attente. push_back(Client(horloge)); for (int i = 0; i < NOMBRE_DE_GUICHETS; i++) { Service[i]. Modifie_statut_commis(horloge); if(Service[i]. Est_libre() && !file_d_attente. empty()) { Client premier_client = file_d_attente. front(); attente_totale += (horloge - premier_client. Acces_heure_arrivee()); Service[i]. Assigner_client(premier_client, horloge); nombre_de_clients++; file_d_attente. pop_front(); } } }; cout << "Attente moyenne : " << (attente_totale / nombre_de_clients) << endl; Chapitre XII - Bibliothèque standard de modèles (STL) 73 }
Conteneur set Un ensemble ne peut contenir 2 fois le même élément. Contrairement au conteneur list, ce conteneur conserve les éléments en ordre trié. Par conséquent, les ensembles ne peuvent renfermer que des valeurs pouvant être comparées. Voici les arguments du modèle de la classe set : template <typename T, typename CMP = less<T> > class set { la valeur par défaut employant … l’opérateur < }; type d’élément l’outil servant à comparer 2 éléments Chapitre XII - Bibliothèque standard de modèles (STL) 74
Conteneur set Exemple : set <int> a; set <string> b; l’opérateur < est défini. bool operator < (const Employe & a, const Employe & b) { return a. Salaire() < b. Salaire(); }; set <Employe> Travailleurs; // Tri par salaire. class Tri_selon_le_nom { public : bool operator() (const Employe & a, const Employe & b) const; }; bool Tri_selon_le_nom : : operator()(const Employe & a, const Employe & b) const { return a. Acces_nom() < b. Acces_nom(); } set <Employe, Tri_selon_le_nom > Travailleurs; // Tri par nom. Chapitre XII - Bibliothèque standard de modèles (STL) 75
Conteneur multiset #include <iostream. h> #include <set> #include <string. h> #include <iterator> #include <functional> using namespace std; pour utiliser les foncteurs // Obligatoire Chapitre XII - Bibliothèque standard de modèles (STL) 76
Conteneur multiset classe_foncteur { private: bool choix; public: classe_foncteur(){ choix = true; } classe_foncteur(bool ch){ choix = ch; } bool operator()(char * c 1, char * c 2) { if (strcmp(c 1, c 2) < 0) return choix; else return !choix; }; }; Chapitre XII - Bibliothèque standard de modèles (STL) 77
Conteneur multiset void main() { char * Liste_Etudiants[] = { "Rioux", "Boivin", "Arthur", "Roy", "Samson"}; classe_foncteur decroissant(0); multiset<char *, classe_foncteur> E(Liste_Etudiants, Liste_Etudiants + 5); multiset<char *, classe_foncteur > P(Liste_Etudiants, Liste_Etudiants + 5, decroissant); multiset<char *, classe_foncteur>: : iterator i; multiset<char *, classe_foncteur >: : iterator j; for(i = E. begin(); i != E. end(); i++) cout << *i << endl; for(j = P. begin(); j != P. end(); j++) cout << *j << endl; Chapitre XII - Bibliothèque standard de modèles (STL) 78 }
Conteneur map Contrairement aux vecteurs et aux dèques, les indices (appelés clés) peuvent être de n’importe quel type ordonné (non nécessairement des entiers). Les éléments sont maintenus en séquence, l’ordre étant déterminé par les clés. Les clés sont uniques. En revanche, une multimap permet d’indexer plusieurs entrées à l’aide de la même clé. Ce conteneur possède trois arguments dans le modèle : template <typename clef, typename valeur, typename CMP = less<clef> > class map { … }; La comparaison pour les clés. Chapitre XII - Bibliothèque standard de modèles (STL) 79
Conteneur map : exemple Fichier "Collection_de_tableaux. h" #include <map> #include <utility> using namespace std; struct tableau { int annee; float valeur_courante; char nom[20+1]; }; Chapitre XII - Bibliothèque standard de modèles (STL) 80
Conteneur map class Collection_de_tableaux { /* Spécification fonctionnelle de la classe " Collection_de_tableaux ". Composantes : Chaque composante représente un tableau avec ses caractéristiques. Structure : La structure choisie est un ensemble. Chaque composante est représentée de façon unique par son numéro d'identification. Domaine : Les caractéristiques d'un tableau sont : - un numéro d'identification, - le nom du peintre, - l'année d'obtention du tableau - l'estimation de la valeur courante de ce tableau. */ Chapitre XII - Bibliothèque standard de modèles (STL) 81
Conteneur map protected: map<int, tableau> T; public: void Ajouter_Tableau ( int numero_identification, char nom_peintre[20+1], int annee_obtention, float estimation_valeur_courante ); /* Permet d'ajouter un nouveau tableau à la collection ayant les caractéristiques passées en paramètres. Pré - La collection a déjà été créée et il n'y existe pas de tableau identifié par "numero_identification". Post - Un nouveau tableau ayant les caractéristiques mentionnées est ajouté à la collection. */ Chapitre XII - Bibliothèque standard de modèles (STL) 82
Conteneur map bool Existe_Tableau(int numero_identification); /* Permet de déterminer si un tableau identifié par "numero_identification » existe dans la collection. Pré - La collection a déjà été créée. Post - Retourne true si ce tableau existe, false sinon. */ void Departir_Tableau(int numero_identification); /* Permet de se départir d'un tableau identifié par "numero_identification » dans la collection. Pré - La collection a déjà été créée et le tableau spécifié existe dans la collection. Post - Le tableau identifié par "numero_identification" est retiré de votre collection. */ Chapitre XII - Bibliothèque standard de modèles (STL) 83
Conteneur map float Valeur_courante_de_collection(); /* Retourne la valeur totale de la collection. Pré - La collection a déjà été créée. Post - Retourne la somme des estimations des valeurs courantes des tableaux de la collection. */ }; Chapitre XII - Bibliothèque standard de modèles (STL) 84
Conteneur map Fichier "Collection_de_tableaux. cpp" #include "string. h" #include "Collection_de_tableaux. h" void Collection_de_tableaux: : Ajouter_Tableau ( int numero_identification, char nom_peintre[20+1], int annee_obtention, float estimation_valeur_courante ) { tableau Q; Q. annee = annee_obtention; Q. valeur_courante = estimation_valeur_courante; strcpy(Q. nom, nom_peintre); pair<int, tableau> p(numero_identification, Q); T. insert(p); Chapitre XII - Bibliothèque standard de modèles (STL) 85 }
Conteneur map bool Collection_de_tableaux: : Existe_Tableau(int numero_identification) { if (T. find(numero_identification) == T. end()) return false; return true; } void Collection_de_tableaux: : Departir_Tableau(int numero_identification) { T. erase(numero_identification); } Chapitre XII - Bibliothèque standard de modèles (STL) 86
Conteneur map float Collection_de_tableaux: : Valeur_courante_de_collection() { float Valeur = 0. 0 f; map<int, tableau>: : iterator i; for (i = T. begin(); i != T. end(); i++) Valeur = Valeur + (*i). second. valeur_courante; return Valeur; } Chapitre XII - Bibliothèque standard de modèles (STL) 87
Conteneur map Fichier "Construction de ma collection. cpp" #include <iostream. h> Aucun changement #include "Collection_de_tableaux. h" void main() { Collection_de_tableaux C; C. Ajouter_Tableau(1802, "Renoir", 1978, 12000. 0); C. Ajouter_Tableau(2436, "Bernin", 1984, 3500. 0); C. Ajouter_Tableau(1504, "Leonard de Vinci", 1967, 24000. 0); if (C. Existe_Tableau(2436) == true) cout << " Trouve"; C. Departir_Tableau(2436); if (C. Existe_Tableau(2436) == false) cout << "Non trouve"; cout << " Valeur : " << C. Valeur_courante_de_collection(); Chapitre XII - Bibliothèque standard de modèles (STL) 88 }
Mise en œuvre d’un conteneur « list » Construction de trois classes modèles : une classe « Node » , une classe « Iterator » , une classe « List » . Voir le site WEB. Chapitre XII - Bibliothèque standard de modèles (STL) 89
Les algorithmes de STL On retrouve aussi dans la bibliothèque standard un jeu de fonctions appelées algorithmes (70 algorithmes standard). Pour ces fonctions, l’accès aux données contenues dans un conteneur s’effectue exclusivement par le biais d’itérateurs. Ces algorithmes sont dits génériques car non seulement ils peuvent fonctionner sur des types de données différents (fonctions modèles) mais aussi avec différents types de conteneurs grâce aux itérateurs. De plus, un pointeur usuel pouvant être considéré comme un cas particulier d’itérateur, les algorithmes sont également utilisables pour les tableaux usuels. Pour utiliser un algorithme, il faut préalablement inclure le fichier <algorithm>. Chapitre XII - Bibliothèque standard de modèles (STL) 90
Exemple I : la fonction for_each template <typename Iterator, typename Action> void for_each(Iterator courant, Iterator arret, Action action) { while (courant != arret) { action(*courant); ++courant; } } une fonction Décrit la séquence de valeurs de la collection. correspondant à l’action. Note : aucun argument de modèle n’indique le type d’élément renfermé dans le conteneur, ni le type de conteneur; cela est définie implicitement par le Chapitre XII - Bibliothèque standard de modèles (STL) 91 type de l’itérateur.
Ex. I : usage de la fonction for_each void Affiche_element(int valeur) { cout << valeur << "n"; } list <int> A; . . . for_each(A. begin(), A. end(), Affiche_element); Note : Chaque valeur de la liste est transmise tour à tour comme argument à la fonction Affiche_element. Chapitre XII - Bibliothèque standard de modèles (STL) 92
Exemple II : la fonction count int count(input_iterator i, input_iterator j, const T &v); compte le nombre n de fois où une valeur v se rencontre entre les positions i et j du conteneur et retourne cette valeur n. #include <iostream> #include <algorithm> using namespace std; int table[] = {12, 321, 34, 13, 12, 15, 12}; ……. int n = 0; n = count(tab, tab + 7, 12); cout << n <<endl; Chapitre XII - Bibliothèque standard de modèles (STL) 93
Notion de prédicat Il s’agit simplement d’une fonction retournant une valeur booléenne. Exemples : bool paire(int valeur) { return 0 == valeur % 2; } Une année est bissextile tous les 400 ans; les siècles ne sont pas bissextiles et une année sur 4 est bissextile. bool annee_bissextile(int annee) { if (0 == annee % 400) return true; if (0 == annee % 100) return false; if (0 == annee % 4) return true; return false; } 94
Exemple d’algorithme générique employant un prédicat L’algorithme utilisé est find_if : iterator find_if(iterator debut, iterator fin, nom d’un prédicat); il renvoie l’itérateur pointant vers le premier élément dans la plage des itérateurs [debut, fin) pour lequel le prédicat est vrai, ou fin s’il n’y a pas de correspondance. Voici une portion de code qui permet de localiser la première année bissextile dans une liste d’années : list<int>: : iterator premiere_annee_bissextile = find_if(A. begin(), A. end(), annee_bissextile); if (premiere_annee_bissextile != A. end()) ……. 95
Calcul du nombre de nombres premiers entre 2 et 100 int count_if(input_iterator i, input_iterator j, fonction booléenne F); compte le nombre d’occurrences dans l’intervalle [i, j[ qui satisfont à la condition du prédicat F et retourne ce nombre. #include <iostream> #include <algorithm> #include <vector> #include <math. h> using namespace std; bool premier(unsigned int n) { for (int i = 2; i <= sqrt(n); i++) if ((n%i) == 0) return 0; return 1; } Chapitre XII - Bibliothèque standard de modèles (STL) 96
Calcul du nombre de nombres premiers entre 2 et 100 void main() { int n = 0; int table[100]; for (int i = 2; i < 100; i++) table[i] = i; cout << "Il y a " << count_if(table+2, table+100, premier) << " nombres premiers entre 2 et 100" << endl; vector<int> v(table, table+100); cout << "Il y a " << count_if(v. begin(), v. end(), premier) << " nombres premiers entre 2 et 100 » << endl; } Chapitre XII - Bibliothèque standard de modèles (STL) 97
Notion de générateur Il s’agit simplement d’une fonction dépourvue d’argument retournant une valeur, parfois différente, à chaque appel. Ex. : le générateur de nombres aléatoires rand ou random. Ex. d’algorithme générique employant un générateur rand : la fonction generate. Cette fonction remplace chaque élément d’une séquence par une nouvelle valeur renvoyée par le générateur. vector<int> a(10); // On crée un vecteur de 10 entiers aléatoires. generate(a. begin(), a. end(), rand); Ex. d’algorithme générique employant un nouveau générateur : la fonction generate. int aleatoire_entre_un_et_cent() { return 1 + rand() % 100; } vector<int> b(50); // On crée un vecteur de valeurs aléatoires entre 1 et 100. 98 generate(b. begin(), b. end(), aleatoire_entre_un_et_cent);
Notion de foncteur ou d’objet fonction Foncteur : le nom d’un objet appartenant à une classe où l’opérateur () est surchargé. Une surcharge de l’opérateur () a un prototype de la forme: type operator() (paramètres) {………. } L’opérateur d’appel de fonction permet de créer des objets à l’aide de classes mais il est employé comme s’il s’agissait de fonctions. Chapitre XII - Bibliothèque standard de modèles (STL) 99
Exemple illustrant le concept de foncteur class Nombre_premier { public: bool operator()(unsigned int n) { for(int i=2; i <= sqrt(n); i++)if ((n%i)==0)return 0; return 1; }; }; Redéfinition de l’opérateur () Chapitre XII - Bibliothèque standard de modèles (STL) 100
Exemple illustrant le concept de foncteur void main() { int n = 0; int table[100]; for (int i = 2; i < 100; i++) table[i] = i; cout << "Il y a " << count_if(table+2, table+100, premier) << " nombres premiers entre 2 et 100" << endl; vector<int> v(table, table+100); cout << "Il y a "<<count_if(v. begin(), v. end(), Nombre_premier()) << " nombres premiers entre 2 et 100 " << endl; } Chapitre XII - Bibliothèque standard de modèles (STL) 101
Raisons qui nous amènent à définir un foncteur au lieu d’une fonction ordinaire : 1. Comme il s’agit d’un objet, il peut être transmis comme argument ou renvoyé comme résultat d’une fonction. 2. Il peut mémoriser un état (des valeurs) depuis sa création à sa destruction. Exemple : - Une fonction rand renvoie des entiers aléatoires. - Une fonction aleatoire_un_cent se sert de rand et renvoie des valeurs aléatoires comprises entre 1 et 100. - Généralisons. Nous voulons générer des valeurs aléatoires comprises entre a et b, a et b étant fixés au temps d’exécution. Pour définir et conserver les valeurs de a et de b, il s’agit de créer une classe et de fixer ces valeurs dans le constructeur. Chapitre XII - Bibliothèque standard de modèles (STL) 102
Exemple d’un foncteur class Aleatoire. Int { public: Aleatoire. Int(int ia, int ib); int operator()(); private: int a, b; }; Aleatoire. Int: : Aleatoire. Int(int ia, int ib) : a(ia), b(ib) { } int Aleatoire. Int: : operator()() { return a + rand() % (b – a + 1); } Chapitre XII - Bibliothèque standard de modèles (STL) 103
Exemple d’un foncteur En créant une instance de cette classe, vous obtiendrez un nombre aléatoire différent à chaque invocation de l’opérateur d’appel de fonction : Aleatoire. Int a(7, 25); cout << "une valeur aléatoire : " << a() << "puis une autre " << a() << endl; On peut aussi créer un vecteur de valeurs aléatoires comprises entre 7 et 25 : vector<int> v(10); generate(v. begin(), v. end(), a); L’instance peut être créée comme temporaire non nommé, une valeur n’existant que pour la durée de l’appel de fonction puis détruit après : vector<int> w(10); generate(w. begin(), w. end(), Aleatoire. Int(7, 25)); Chapitre XII - Bibliothèque standard de modèles (STL) 104
Exemple d’un prédicat écrit comme foncteur Il s’agit de construire un prédicat « divisible par n » où n est une valeur inconnue avant l’exécution. class divisible_par { private : int n; public : divisible_par(int i); bool operator()(int x); }; divisible_par: : divisible_par(int i) : n(i) {} inline bool divisible_par: : operator()(int x) { return 0 == x % n; } Chapitre XII - Bibliothèque standard de modèles (STL) 105
Exemple d’un prédicat écrit comme foncteur Étant donné une liste d’entiers, vous voulez trouver la première valeur après l’élément initial divisible par l’élément initial. list<int> a; … list<int>: : iterator courant = a. begin(); divisible_par predicat(*courant); // Création d’un prédicat. ++courant; // Avancer à l’élément suivant. list<int>: : iterator elem = find_if(courant, a. end(), predicat); if (elem != a. end()) cout << "Trouve"; Chapitre XII - Bibliothèque standard de modèles (STL) 106
Raisons qui nous amènent à définir un foncteur au lieu d’une fonction ordinaire : 3. C’est le cas lorsque chaque invocation doit se souvenir d’un état fixé par des invocations antérieures. Exemple : Création d’un générateur produisant la suite 1, 2, 3, etc. renvoyant une valeur entière différente à chaque invocation. class Generateur_suite { private : int courant; public : Generateur_suite(int sv); // Initialisation de la valeur de départ. int operator()(); }; Generateur_suite: : Generateur_suite(int sv) { courant = sv; } int Generateur_suite: : operator()() { int r = courant; courant++; return r; }. . . vector<int> a(20); generate(a. begin(), a. end(), Generateur_suite(1)); Chapitre XII - Bibliothèque standard de modèles (STL) 107
Conclusion La librairie STL est en voie de devenir la librairie majeure intégrée au C++. Permet de réaliser rapidement de nouvelles tâches. Donne accès à une gamme d’outils extensibles : - les algorithmes existants fonctionneront sur les nouveaux conteneurs à venir, - les conteneurs existants supporteront les nouveaux algorithmes écrits par l’usager, - de nouveaux algorithmes pourront être écrits et fonctionner sur tous les conteneurs présents et à venir. STL demande énormément aux compilateurs au niveau de la génération des templates : - certaines caractéristiques sont non implantées, - la génération de code est difficile à comprendre, - cela donne des exécutables volumineux. Chapitre XII - Bibliothèque standard de modèles (STL) 108
- Slides: 108