Cours STL

Télécharger au format pdf ou txt
Télécharger au format pdf ou txt
Vous êtes sur la page 1sur 26

Contenu de la STL

Les ingénieurs de Hewlett Packard ont développé


beaucoup de classes génériques qui sont utiles
pour presque tous les programmes. Plutôt que de
se casser la tête à systématiquement tout
reconstruire, il est préférable d'utiliser ces classes
qui sont suffisamment générique pour s'adapter à
toutes les situations.
Les conteneurs séquentiels :
Il est très fréquent d'avoir besoin de stocker dans
une même entité mémoire un ensemble d'objets de
même type. En plus, il peut être intéressant
d'utiliser un système qui fonctionne quelque soit
l'objet que nous développons. Les conteneurs
séquentiels permettent effectivement de stocker
des objets en séquence, c'est-à-dire les uns à la
suite des autres, ce qui permettra ensuite de
parcourir le conteneur dans un ordre particulier. Il
existe plusieurs conteneurs séquentiels :
•vector : cette classe est un vecteur qui représente un tableau de haut
niveau (les cases sont consécutives). Avec cette classe, il est possible
d'atteindre n'importe quelle élément du tableau facilement grâce à
l'indexation « [ ] ». Nous pouvons insérer de nouveaux éléments, en
supprimer, etc.

•list : cette classe implémente une liste doublement chaînée. Avec cette
classe, il est plus facile de supprimer un élément particulier par rapport au
vecteur (en effet pour un vecteur, si nous supprimons une case, il est
nécessaire de décaler les cases suivantes vers le bas). Par contre, cette liste
utilise systématiquement deux pointeurs pour parcourir la séquence ce qui
prend plus de place en mémoire.

•deque : cette classe est très peu utilisée et offre le même comportement
mais plus spécialisé que la classe vecteur. Sa spécialisation consiste à pouvoir
facilement ajouter ou retirer le premier élément. Cette classe est une
abstraction d'une file pour laquelle le premier élément est retiré chaque fois.
Adaptateurs de conteneurs séquentiels :
La bibliothèque standard dispose de trois patrons particuliers qui
s'ajoutent aux conteneurs séquentiels en modifiant leurs
comportements classiques. Généralement, il s'agit d'une restriction
et d'une adaptation à des fonctionnalités données :

•stack : ce patron est destiné à la gestion des


piles LIFO (Last In, First Out).

•queue : ce patron est destiné à la gestion des


piles de type FIFO (First In, First Out).

•priority_queue : un tel conteneur ressemble à


une file d'attente, dans laquelle on introduit
toujours des éléments en fin.
Les conteneurs associatifs :
Les éléments d'un conteneur
associatif ne sont plus placés
dans un ordre particulier. Pour
retrouver une entité, nous ferons
appel dans ce cas là à une clé qui
nous orientera vers la valeur
recherchée.
•map : ce conteneur représente une correspondance entre deux entités sous
la forme d'une paire clé/valeur , la clé étant utilisée pour la recherche et
la valeur contenant les données que l'on souhaite utiliser. Par exemple, un
répertoire téléphonique est représenté par une correspondance entre le nom
de l'individu (la clé ) et son numéro de téléphone (la valeur ).

•multimap : ce conteneur représente une multicorrespondance, il peut donc


stocker plusieurs occurrences d'une même clé. Par exemple, une même
personne peut posséder plusieurs numéros de téléphones.

•set : ce conteneur représente la théorie des ensembles et contient une


valeur de clé unique et supporte les requêtes concernant sa présence ou non.
En effet, grâce à ce conteneur, nous pourrons indiquer si un élément fait parti
de l'ensemble ou pas.

mulitset : représente également la théorie des ensembles avec en plus la


possibilité de comptabiliser le nombre de fois qu'un même élément apparaît
dans l'ensemble. Ce conteneur autorise donc la présence de plusieurs
éléments identiques, ce qui n'est pas le cas pour le conteneur set
Les algorithmes génériques :
De même que la STL est composée de classes
génériques, elle est également composée de
fonctions génériques qui fournissent des
opérations supplémentaires bien utiles sur
les différents conteneurs étudiés
précédemment. Ces fonctions permettent
d'effectuer un certain nombre de
traitements différents, comme des
insertions, des copies, des recherches, etc.,
dans une suite d'éléments d'un des
conteneurs utilisé. L'intérêt de ces fonctions
génériques, que l'on appelle aussi
algorithmes génériques, c'est qu'elles sont
opérationnelles pour tous les types de
conteneur, comme vector, list, map, etc.
La liste ci-dessous vous donnera une idée de
quelques fonctions génériques intéressantes :
•copy : copie d'une séquence dans une autre,
•count : comptabilise le nombre d'élément présent dans une
suite,
•generate : génération de valeurs par une fonction,
•find : recherche d'une valeur particulière,
•max_element : recherche du maximum,
•min_element : recherche du minimum,
•replace : remplacement de valeurs,
•rotate : permutation de valeurs,
•remove : suppression de valeurs,
•unique : suppression de doublons,
•sort : tri d'une séquence,
•merge : fusion de deux conteneurs.
reverse : inverse l'ordre des éléments dans un
conteneur
Quelques classes bien utiles :
Nous avons commencé cette étude en indiquant que le langage C++
ne possédait pas certains éléments qui sont indispensables à la
programmation de haut niveau, comme les chaînes de caractères.
Par ailleurs, dans nos différentes études, nous avons en œuvre de
toute pièce une classe qui représente les nombres complexes. Il faut
savoir qu'une telle classe fait partie de cette bibliothèque. Voici une
liste non exhaustive de classes qui me paraissent intéressante :

•string : cette classe représente une chaîne de caractères et


possède beaucoup de méthodes qui permettent tous les
traitements possibles sur ses caractères. Dorénavant, lorsque
nous aurons besoin d'une chaîne de caractères, ce sera
systématiquement cette classe que nous prendrons.
•complex : cette classe représente les nombres complexes,
•bitset : cette classe représente les nombres binaires ou plus
précisément un ensemble de bits et possèdent des méthodes
associées à leurs traitements.
La classe « string »
•Un objet de type « string » contient a un instant donné, une
suite formée d'un nombre quelconque de caractères.
•Sa taille peut évoluer dynamiquement au fil de l'exécution du
programme. En fait, cette chaîne réserve un bloc mémoire
suffisant pour stocker un certain nombre de caractères.
•Si la chaîne désirée est plus grand que cette zone réservée, la
classe augmente automatiquement ce bloc en proposant une
nouvelle allocation mémoire et en prenant la précaution
d'avoir un bloc plus grand que nécessaire afin de répondre
rapidement à une petite augmentation de la taille de la chaîne.
Constructions
La classe « string » dispose de plusieurs constructeurs :
Les autres méthodes
La classe « string » dispose de beaucoup de méthodes (que l'on
retrouvera dans d'autres classes génériques) bien utiles :

•append : ajoute une chaîne, à la fin d'une autre. Il s'agit d'une concaténation qui peut
être également traitée par l'opérateur += .
•assign : affecte à l'objet une nouvelle chaîne de caractères.
•at : permet de lire ou de récupérer un caractère à la position indiquée. La première
position est 0. Il est nécessaire de donner une position compatible et inférieure à la
taille de la chaîne sinon une exception est levée. Cette méthode est similaire à la
redéfinition de l'opérateur « [ ] ».
•capacity : retourne la dimension du bloc mémoire réservé. Cette méthode fournit donc
le nombre maximal de caractères qu'on pourra introduire, sans qu'il soit besoin de
procéder à une nouvelle allocation mémoire. Les méthodes reserve et resize pouront
être utilisée pour agir directement sur la capacité de ce bloc mémoire. La valeur
retournée est toujours plus grande ou égale à la valeur que retourne la méthodesize.
•clear : vide entièrement la chaîne de caractères.
•compare : cette méthode gère l'ordre alphabétique et retourne une valeur numérique
négative ou positive suivant le placement de la chaîne par rapport à celle qui est passée
en argument. Une valeur négative indique que la chaîne se trouve avant celle qui est
passée en argument. Une valeur positive dans le cas contraire, et une valeur nulle dans
le cas où les deux chaînes sont rigoureusement identiques. La plupart du temps, il sera
préférable d'utiliser les opérateurs relationnels « <, <=, ==, !=, >, >= » pour gérer ce
genre de problème.
•c_str : cette méthode permet de passer d'une chaîne de type « string » vers une chaîne
classique du C++ (const char *). Attention, il est possible de récupérer cette chaîne sans
toutefois pouvoir la modifier puisque une constante est déclarée.
•empty : retourne true si la chaîne est vide (sans aucun caractère), sinon retourne false .
•erase : efface une partie de la chaîne ou un caractère spécifié en argument.
•find, rfind, find_first_of, find_last_of, find_first_not_of, find_last_not_of : effectuent des
recherches sur une partie de la chaîne ou sur un caractère spécifié en argument.
•insert : permet d'insérer une autre chaîne ou bien un ou plusieurs caractères donnés.
•length : retourne la longueur de la chaîne de caractères. Similaire à la méthode size.
•replace : remplace une partie de chaîne.
•reserve : réserve un bloc mémoire dont la taille est fixé par l'argument. Cette méthode
doit être rarement utilisé, juste dans le cas où la performance en terme de rapidité est
primordiale ou alors, éventuellement, dans le cas où nous sommes très limité dans la
capacité de la mémoire.
•resize : donne une nouvelle dimension à votre chaîne de caractères. Attention ! à utiliser
avec beaucoup de précaution.
•size : retourne la longueur d'une chaîne de caractères. Similaire à length.
•substr : retourne une partie de chaîne.
•swap : assure la permutation de deux chaînes de caractères.
•begin et end : ces opérations retournent des itérateurs au début et à la fin de la chaîne.
Un itérateur est une abstraction d'un pointeur de classe générique, fourni par la
bibliothèque standard. Ce sujet sera traité ultérieurement lorsque nous utiliserons la
classe « vector ».
La classe vector en tant que tableau - #include <vector>
nous avons besoin d'une classe qui palie au manque de
compétence du langage C++. La classe vector sera presque
systématiquement utilisée pour implémenter les tableaux.
Toutefois, la classe vector représente bien plus que cela.
Elle fait également partie de l'ensemble des conteneurs, et
notamment des conteneurs de type séquentiel.
Le tableau vector
La classe vector remplace aisément les tableaux classiques
en offrant des manipulations simples et intuitives. Ainsi, il
est possible de construire des tableaux de type
quelconque, en indiquant le nombre de cases requis. Il est
également possible d'initialiser un tableau avec une valeur
particulière pour toutes les cases du tableau ou même de
spécifier une valeur d'initialisation différente pour chacune
des cases du tableau.
Avec ce tableau, un certain nombre d'opérations peuvent
être réalisées simplement, comme :
•= : l'affectation est possible entre deux tableaux de même type (Attention,
il faut aussi qu'ils comportent le même nombre de cases).
•[ ] : l'opérateur d'indexation à bien évidemment été redéfini pour
supporter le comportement classique d'un tableau, puisque cet opérateur à
été spécialement créé pour les tableaux.
•==, !=, <, <=, >, >= : Il est de plus possible de comparer le contenu de deux
tableaux entre eux en utilisant les opérateurs classiques de comparaison.
Vu la simplicité d'utilisation, il est impératif d'utiliser cette classe pour
implémenter les tableaux.
Propriétés communes aux conteneurs séquentiels vector, list, deque
Itérateur et parcours d'un conteneur
Un itérateur fournit un processus général pour accéder successivement
à chaque élément à l'intérieur de n'importe quel type de conteneur. Un
itérateur correspond à un pointeur qui, comme tous les pointeurs,
permet d'utiliser l'incrémentation ou la décrémentation. Ainsi, il est
possible de consulter dans le sens direct ou en sens inverse une suite
d'éléments faisant partie de la séquence.

•Un itérateur peut être incrémenté par l'opérateur « ++ », de manière à pointer sur
l'élément suivant du même conteneur.
•Un itérateur peut être déréférencé par l'opérateur « * » ; nous pouvons donc
récupérer la valeur de l'élément de la séquence.
begin() : retourne un itérateur qui adresse le premier élément du conteneur,
end() : retourne un itérateur qui adresse un élément après le dernier élément du
conteneur.
Constructions
Ces trois classes disposent de plusieurs constructeurs qui
permettent de résoudre la plupart des situations envisagées :

•Construction d'un conteneur vide : L'appel d'un constructeur sans argument


construit un conteneur vide, c'est-à-dire ne comportant aucun élément.
•Construction avec un nombre donné d'éléments : De façon comparable à ce qui se
passe avec la déclaration d'un tableau classique, l'appel d'un constructeur avec un
seul argument entier « n » construit un conteneur comprenant « n » éléments.
L'initialisation de ces éléments n'est correctement gérée que dans le cas ou les
éléments sont des objets. En effet, ces derniers disposent d'un constructeur par
défaut. Dans le cas des types primitifs, les valeurs sont aléatoires.
•Construction avec un nombre d'éléments initialisés avec une valeur précise : Le
premier argument fourni le nombre d'éléments alors que le second fixe la valeur
d'initialisation.
•Construction à partir d'une séquence : Nous pouvons construire un conteneur à
partir d'une séquence d'éléments de même type. Dans ce cas, nous fournissons
simplement au constructeur deux arguments représentant les bornes de l'intervalle
correspondant.
•Construction par recopie : Chaque type de conteneur dispose de son propre
constructeur de copie. Attention, il est nécessaire d'utiliser des conteneurs
rigoureusement identiques (même conteneur et même type d'éléments).
Affectation et comparaisons
Ce que nous avons découvert sur la classe vector en tant
que tableau s'applique également sur tous les conteneurs
séquentiels.
Ainsi, il est possible d'affecter un conteneur d'un type
donné à un autre conteneur de même type, c'est-à-dire
ayant le même nom de patron et le même type
d'éléments. Bien entendu, il n'est nullement nécessaire
que le nombre d'éléments de chacun des conteneurs soit
identique.
De la même façon, les opérateurs relationnels « ==, !=,
<, <=, >, >= » ont été redéfinis pour supporter tous les
types de comparaison, quelque soit le conteneur utilisé
Les autres méthodes
•assign( début , fin ) : alors que l'affectation n'est possible qu'entre conteneurs de même type, la
méthode « assign » permet d'affecter, à un conteneur existant, les éléments d'une autre
séquence définie par un intervalle ( début , fin ), à condition que les éléments des deux séquences
soient de même type.
•assign( nombreDeFois , valeur ) : il existe également une version permettant d'affecter à un
conteneur, un nombre donné d'éléments ayant une valeur imposée.
•clear() : vide le conteneur de son contenu.
•empty() : teste si le conteneur est vide et renvoie true si c'est le cas, et false ans le cas contraire.
•swap() : permet d'échanger le contenu de deux conteneurs de même type.
•insert( position , valeur ) : insère une valeur avant l'élément pointé par la position.
•insert( position , nombreDeFois , valeur ) : insère un certain nombre de fois une valeur avant
l'élément pointé par la position.
•insert( début , fin , position ) : insère les valeurs de l'intervalle ( début , fin ) avant l'élément
pointé par la position.
•push_back( valeur ) : cette méthode est spécialisée pour insérer une valeur en fin de conteneur
à la manière d'une pile.
•erase( position ) : supprime l'élément désigné par la position
•erase( début , fin ) : supprime les valeurs de l'intervalle « début ( compris ) , fin ( non
compris ) ».
•pop_back() : cette méthode est spécialisée pour supprimer la dernière valeur du conteneur à la
manière d'une pile.
•size() : détermine le nombre d'éléments que contient le conteneur.
Les algorithmes génériques
Les conteneurs ont en commun beaucoup de méthodes. Chaque conteneur dispose
également de méthodes supplémentaires qui font leur spécificité. Cependant, une fois que
nous avons choisi un conteneur, il peut être intéressant de rajouter d'autres fonctionnalités
non intégrées par le conteneur. Dans ce cas là, nous avons besoin des fonctions génériques.
Rappelons que les fonctions génériques sont opérationnelles quelque soit le conteneur
utilisé.

Voici quelques fonctions génériques intéressantes :


•copy ( conteneur1début , conteneur1fin , conteneur2début ) : copie d'une séquence dans
une autre. Il suffit de préciser l'intervalle désiré en donnant les itérateurs du premier
conteneur. Cet intervalle est ensuite copié à partir de l'itérateur donné par le second
conteneur.
•count ( iterateurdébut , iterateurfin , valeurRecherchée ) : comptabilise le nombre de fois
qu'un élément est présent dans un conteneur,
•iterateur find ( iterateurdébut , iterateurfin , valeurRecherchée ) : recherche d'une valeur
particulière par rapport à l'intervalle d'une séquence. La fonction retourne l'itérateur
correspondant à l'endroit où se situe la valeur recherchée. Si la recherche n'a pas aboutie, la
fonction renvoie un itérateur sur la fin de la séquence - conteneur.end().
•iterateur max_element ( iterateurdébut , iterateurfin ) : recherche la valeur maximale
d'une séquence. La fonction retourne l'itérateur correspondant à l'endroit où se situe la
valeur recherchée.
•iterateur min_element ( iterateurdébut , iterateurfin ) : recherche la valeur
minimale d'une séquence. La fonction retourne l'itérateur correspondant à
l'endroit où se situe la valeur recherchée.
•replace ( iterateurdébut , iterateurfin , ancienneValeur , nouvelleValeur ) :
remplace toutes les instances d'une valeur particulière par une nouvelle valeur,
•remove ( iterateurdébut , iterateurfin , valeurASupprimer ) : supprime toutes
les instances d'une valeur particulière par rapport à l'intervalle proposé.
•sort ( iterateurdébut , iterateurfin ) : tri de la séquence. Reclasse les éléments
de l'intervalle proposé dans l'ordre croissant.
•reverse ( iterateurdébut , iterateurfin ) : inverse l'ordre des éléments dans un
conteneur.

Vous aimerez peut-être aussi