Flux rss
Collection CommentCaMarche.net

Introduction à la STL en C++ (standard template library)

Publié par mamiemando, dernière mise à jour le lundi 21 juillet 2008 à 15:45:02 par kilian


Introduction


L'une des difficultés du langage C consiste à implémenter des containers (vecteurs, listes chaînées, ensembles ordonnés) génériques, simple d'utilisation et efficaces. Afin d'être générique on est souvent contraint de faire appel à des pointeurs génériques (void *) et aux opérateurs de cast. De plus, quand ces containers sont imbriqués les uns dans les autres (par exemple un ensemble de vecteurs) le code devient rapidement complexe à utiliser.

Afin de répondre à ce besoin, la STL (standard template library) implémente un grand nombre de classe template décrivant des containers génériques pour le langage C++. La STL fournit en plus des algorithmes permettent de manipuler aisément ces containers (pour les initialiser, rechercher des valeurs, ...). La STL introduit également le concept d'iterator qui permet de parcourir très facilement un container en s'affranchissant complètement de la manière dont il est implémenté.

Les concepts développés dans la STL sont aujourd'hui étendus par la librairie boost qui permet de manipuler des structures de graphe générique.

L'objectif de cette introduction n'est pas de faire un inventaire exhaustif des possibilités offertes par la STL, mais de donner des exemples courants d'utilisation. On pourra trouver un aperçu très détaillé des classes de la STL ici :
http://www.sgi.com/tech/stl/table_of_contents.html

Par la suite, on présentera les classes de la STL avec des paramètres templates "simples". En pratique il faut voir les classes de la STL comme des legos (ou des duplos pour ne pas faire de jaloux) pouvant être imbriqués les uns dans les autres. Ainsi, on pourra tout à fait manipuler un vector d'ensemble de liste d'entier (std::vector<std::set<std::list<int> > >).

Principales classes de la STL


Il est particulièrement important de choisir une classe fournie par la STL cohérente avec son besoin. Certaines structures sont effectivement plus ou moins efficaces pour accéder à une mémoire ou en terme de réallocation mémoire lorsqu'elles deviennent importantes. L'enjeu de cette partie consiste à présenter les avantages et les inconvénients de chacune d'elle.

Il est au préalable nécessaire d'avoir quelques notions de complexité. Soit n la taille d'un container. Un algorithme est dit linéaire (en O(n)) si son temps de calcul est proportionnel à n. De la même façon, un algorithme peut être instantané (O(1)), logarithmique O(log(n)), polynomial O(n^k), exponentiel O(e(n)), etc...

Pour résumer, voici la liste des complexités dans l'ordre croissant des proportions de temps de calcul (plus on descend, plus c'est violent):
  • O(1)
  • O(log(n))
  • O(n)
  • O(n^k)
  • O(e(n))


Ici on s'intéressera principalement à la complexité pour l'accès (recherche) à une donnée stockée dans un container et à la complexité pour insérer une donnée.

std::pair<T1,T2>


Une paire est une structure contenant deux éléments éventuellement de types différents. Certains algorithmes de la STL (find par exemple) retournent des paires (position de l'élément trouvé et un booléen indiquant s'il a été trouvé).

Complexité : insertion et accès en O(1)
#include <pair> // en pratique cet include est sous-entendu car implicitement fait lorsqu'on utilise <vector>, <set> ...
#include <iostream>
#include <string>

int main(){
  std::pair<int,std::string> p = std::make_pair(5,"pouet");
  std::cout << p.first << ' ' << p.second << std::endl;
  return 0;
}

std::list<T,...>


La classe list fournit une structure générique de listes chaînées pouvant éventuellement contenir des doublons.

Complexité
  • Insertion (en début ou fin de liste) : O(1)
  • Recherche : O(n) en général, O(1) pour le premier et le dernier maillon


Cet exemple montre comment insérer les valeurs 4,5,4,1 dans une liste et comment afficher son contenu :
#include <list>
#include <iostream>

int main(){
  std::list<int> ma_liste;
  ma_liste.push_back(4);
  ma_liste.push_back(5);
  ma_liste.push_back(4);
  ma_liste.push_back(1);
  std::list<int>::const_iterator
    lit (ma_liste.begin()),
    lend(ma_liste.end());
  for(;lit!=lend;++lit) std::cout << *lit << ' ';
  std::cout << std::endl; 
  return 0;
}

std::vector<T,...>


La classe vector est proche du tableau du C. Tous les éléments contenus dans le vector sont adjacents en mémoire, ce qui permet d'accéder immédiatement à n'importe quel élément. L'avantage du vector comparé au tableau du C est sa faculté à se réallouer automatiquement en cas de besoin lors d'un push_back par exemple. Cependant comme un tableau classique une case n'est accessible par l'opérateur [ ] que si elle a été allouée au préalable (sinon une erreur de segmentation se déclenche).

Complexité :
  • Accès O(1)
  • Insertion : O(n) en début de vector (pop_back), O(1) en fin de vector (push_back). Dans les deux cas des réallocations peuvent survenir.


ll ne faut pas perdre de vue qu'une réallocation mémoire est coûteuse en terme de temps de calcul. Ainsi si la taille d'un vecteur est connue par avance, il faut autant que possible le créer directement à cette taille.

Exemple :
#include <vector>
#include <iostream>

int main(){
  std::vector<int> mon_vecteur;
  mon_vecteur.push_back(4);
  mon_vecteur.push_back(2);
  mon_vecteur.push_back(5);
  // pour parcourir un vector (même const) on peut utiliser les iterators ou les index
  for(std::size_t i=0;i<mon_vecteur.size();++i) std::cout << mon_vecteur[i] << ' '
  std::cout << std::endl;

  std::vector<int> mon_vecteur(5,69); // crée le vecteur 69,69,69,69,69
  v[0] = 5;
  v[1] = 3;
  v[2] = 7;
  v[3] = 4;
  v[4] = 8;
  return 0;
}

std::set<T,...>


La classe set permet de décrire un ensemble ordonné et sans doublons d'éléments. Il faut a priori passer cet ordre en paramètre template (plus précisément d'un foncteur). Par défaut, le foncteur std::less (basé sur l'opérateur <) est utilisé, ce qui revient à avoir un ensemble d'éléments classés du plus petit au plus grand. Concrètement il suffit donc d'implémenter l'opérateur < d'une classe ou d'une structure de type T pour pouvoir définir un std::set<T>. De plus, le type T doit disposer d'un constructeur vide T().

Complexité :
  • O(log(n)) pour la recherche et l'insertion. En effet, la structure std::set tire partie de l'ordre sur T pour construire une structure d'arbre rouge noir, ce qui permet une recherche logarithmique d'un élément.

#include <set>
#include <iostream>

int main(){
  std::set<int> s; // équivaut à std::set<int,std::less<int> >
  s.insert(2); // s contient 2
  s.insert(5); // s contient 2 5
  s.insert(2); // le doublon n'est pas inséré
  s.insert(1); // s contient 1 2 5
  std::set<int>::const_iterator
    sit (s.begin()),
    send(s.end());
  for(;sit!=send;++sit) std::cout << *sit << ' ';
  std::cout << std::endl;
  return 0;
}


Attention : le fait de supprimer ou ajouter un élément dans un std::set remet invalide ses iterators. Il ne faut pas modifier un std::set dans une boucle for basée sur ses iterators

std::map<K,T,...>


Une map permet d'associer une clé (identifiant) à une donnée (table associative).

La map prend au moins deux paramètres templates :
  • le type de la clé K
  • le type de la donnée T


À l'image du std::set, le type K doit être ordonné (cet ordre peut être passé en 3e paramètre template, std::less<K> par défaut) et . Le type T impose juste d'avoir un constructeur vide.

Complexité :
  • O(log(n)) pour la recherche et l'insertion. En effet, la structure std::map tire partie de l'ordre sur T pour construire une structure d'arbre rouge noir, ce qui permet une recherche logarithmique d'un élément.


Attention : le fait de supprimer ou ajouter un élément dans un std::map remet invalide ses iterators. Il ne faut pas modifier un std::map dans une boucle for basée sur ses iterators

Attention : le fait d'accéder à une clé via l'opérateur [ ] insère cette clé (avec la donnée T()) dans la map. Ainsi l'opérateur [ ] n'est pas adapté pour vérifier si une clé est présente dans la map, il faut utiliser la méthode find. De plus, il ne garantit pas la constance de la map (à cause des insertions potentielles) et ne peut donc pas être utilisé sur des const std::map.

Exemple :
#include <map>
#include <string>
#include <iostream>

int main(){
  std::map<std::string,unsigned> map_mois_idx;
  map_mois_idx["janvier"] = 1;
  map_mois_idx["février"] = 2;
  //...
  std::map<std::string,unsigned>::const_iterator
    mit (map_mois_idx.begin()),
    mend(map_mois_idx.end());
  for(;mit!=mend;++mit) std::cout << mit->first << '\t' << mit->second << std::endl;
  return 0;
}

Les iterators


Nous avons vu dans la section précédente que les iterators permettaient de parcourir aisément une structure de la STL d'un bout à l'autre. Un iterator rappelle un peu la notion de pointeur, mais ce n'est pas une adresse. Nous allons à présent voir quatre iterators classiques de la STL.

Ils sont définis pour toutes les classes de la STL évoquées ci-dessus (hormis bien sûr std::pair)

iterator et const_iterator


Un iterator (et un const_iterator) permet de parcourir un container du début à la fin. Un const_iterator, contrairement à un iterator, donne un accès uniquement en lecture à l'élément "pointé". Ainsi, un parcours avec des const_iterator maintient la constance du container. C'est pourquoi un container "const" peut être parcouru par des const_iterators mais pas des iterators.

De manière générale, quand on a le choix entre des iterators ou des const_iterator, il faut toujours privilégier les const_iterator car ils rendent la section de code à laquelle ils servent plus générique (applicable aux containers const ou non const).
  • begin() : retourne un iterator qui pointe sur le premier élément
  • end() : retourne un iterator qui pointe juste "après" le dernier élément
  • ++ : permet d'incrémenter l'iterator en le faisant passer à l'élément suivant.


Exemple :
#include <list>
#include <iostream>

const std::list<int> creer_liste(){
  std::list<int> l;
  l.push_back(3);
  l.push_back(4);
  return l;
}

int main(){
  const std::list<int> ma_liste(creer_liste());
  // ne compile pas car l est const
  // std::list<int>::iterator
  //  lit1 (l.begin()),
  //  lend(l.end());
  //for(;lit1!=lend1;++lit1) std::cout << *lit1 << ' ';
  //std::cout << std::endl;
  std::list<int>::const_iterator
    lit2 (l.begin()),
    lend2(l.end());
  for(;lit2!=lend2;++lit2) std::cout << *lit2 << ' ';
  std::cout << std::endl; 
  return 0;
}

reverse_iterator et const_reverse_iterator


Le principe des reverse_iterators et const_reverse_iterator est similaire aux iterators et const_iterator à ceci prês que le parcours se fait dans le sens opposé.

On utilise alors :
  • rbegin() : retourne un iterator qui pointe sur le dernier élément
  • rend() : retourne un iterator qui pointe juste "avant" le premier élément
  • -- : permet de décrémenter le reverse_iterator en le faisant passer à l'élément précédent.

Les algorithmes


Si le concept d'iterator est maîtrisé, il suffit de se référer à
http://www.sgi.com/tech/stl/table_of_contents.html

On retiendra quelques méthodes bien pratiques comme find (pour les std::set et std::map) pour chercher un élément, std:fill pour (ré)initialiser un std::vector, des algorithmes de tri, de recherche de min ou de max.
Changer compte standard en compte admi (Résolu) Bonjour, Voilà mon soucis est que je me suis inscrite en compte standard au lieu de compte admi de plus j'ai un soucis de mot de passe refusé en bloc . donc j'ai voulu installer os avec les cd vendu avec mon macbook 10.5 . J'ai insèrre... www.commentcamarche.net/forum/affich-5086742-changer-compte-standard-en-compte-admi
[C++]Edition de lien pour template (Résolu) Bonjour, à la compilation d'un programme utilisant une classe générique, j'ai obtenu des erreurs du à l'édition de lien. J'ai regardé sur le net, qu'il fallait créer un fichier .tcc qui contiendrait les templates. Le soucis,... www.commentcamarche.net/forum/affich-2761797-c-edition-de-lien-pour-template
Template (Résolu) Bonjour, j'ai un problème aider moi : comment créer un template d'un site web avec dreamweaver et l'appliquer dans joomla???? www.commentcamarche.net/forum/affich-4007627-template
Les templates en C++Introduction Avantages Inconvénients Quand utiliser des templates ? Que dois-je mettre dans les .hpp et dans les .cpp ? Convention de notations Quelques templates célèbres STL BGL Premiers pas Spécifications de templates Template par... www.commentcamarche.net/faq/sujet-11194-les-templates-en-c
NVU et les templates (Résolu)Bonjour, J'aime bien le concept des templates sous dreamweaver. Et quand j'ai vu que NVU le a implémentés je me suis décidé à passer au gratuit. Seulement j'ai rencontré un petit problème. J'arrive à créer un template et créer des... www.commentcamarche.net/forum/affich-1318845-nvu-et-les-templates
[Découpe Template] E-Coasters (Résolu)Bonjour, Nous sommes actuellement à la recherche d'une personne pouvant découper en Php/Html/Css un Template réalisé en PSD. Pour vous expliquer en gros notre demande nous souhaitons que tout ce que est header/footer/menus (en gros toute la... www.commentcamarche.net/forum/affich-5080811-decoupe-template-e-coasters
Template html (Résolu)Bonjour, je me suis fais un site : www.boolat.net "serveur accepte seulement du html" c'est vrai que pour un début c'est pas terrible ! je cherche un template gratuit tous simple pour ma page d'accueil ! un fond image sympa puis un... www.commentcamarche.net/forum/affich-3941852-template-html
Télécharger Pattern-Making CalculatorPour les nombres, les anglophones sont habitués à utiliser des valeurs dites "impériales" ou en fraction et non des valeurs décimales. Cette fonction n'est pas toujours pris en charge par les machines à calculer standards. Par exemple dans la mesure... www.commentcamarche.net/telecharger/telecharger-34056241-pattern-making-calculator
Télécharger CalculCalcul est une calculatrice gratuite. Il s'apparente à la calculatrice intégrée à Windows. Il propose une interface très simple. L'utilisateur pourra effectuer des calculs standards : addition, soustraction, division, multiplication. Faire des... www.commentcamarche.net/telecharger/telecharger-34056674-calcul
Télécharger TYPSoft FTP Serveur TYPSoft FTP Serveur est un ftp serveur rapide et facile avec le support des commandes Standard de FTP, Interface propre et claire, architecture de système de fichiers virtuelle, capacité de reprendre le téléchargement interrompu tant en download qu’en... www.commentcamarche.net/telecharger/telecharger-34055220-typsoft-ftp-serveur
Terratec Cinergy T USB XEStandard:Numérique,Interne/Extrene:Externe,Connexion:USB 2.0,Analogique/Numérique:DVB-T,Connexion:USB 2.0,Interne/Extrene:Externe,Standard:Numérique,Télécommande,Télétexte,Entrée... www.commentcamarche.net/guide-achat/terratec-cinergy-t-usb-xe-850579-fiche-technique
Buffalo AirStation G54 High Power Wireless CardBus Adapter (WLI-CB-G54HP) / 802.11g/bG 54 54 HP 54HP 54HP,Poids:60.0 grams,Profondeur:5.5 cm,Hauteur:12.2 cm,Standards WLAN:802.11g/b,Longueur:1.3 cm,Vitesse de transfert max. WLAN:125.0 WLAN (Mbits),Vitesse de transfert max. LAN:0.0 LAN (Mbits),Divers:128/64-bit WEP,Interface :PC... www.commentcamarche.net/guide-achat/buffalo-airstation-g54-high-power-wireless-cardbus-adapter-wli-cb-g54hp-802-11g-b-542292-fiche-technique
NetGear RangeMax WN511T / PC Card (WN511T-100GES) / IEEE 802.11b/g/n DraftWN 511 511 T 511T 511T,Hauteur:0.9 cm,Longueur:12.3 cm,Standards WLAN:IEEE 802.11b/g/n Draft,Vitesse de transfert max. WLAN:300.0 WLAN (Mbits),Divers:Microsoft Windows XP,Microsoft Windows 2000 SP4,Vitesse de transfert max. LAN:0.0 LAN... www.commentcamarche.net/guide-achat/netgear-rangemax-wn511t-pc-card-wn511t-100ges-ieee-802-11b-g-n-draft-676245-fiche-technique
[Brève] Palm offre un compagnon aux smartphones(Paris - Relaxnews) - Le fabricant Palm a présenté hier son nouveau-né : Foleo. Ce petit ordinateur portable pourvu d'un écran 10,2 pouces et de toutes les touches d'un clavier standard, permet, via ses connectiques Wi-Fi et Bluetooth de synchroniser... www.commentcamarche.net/actualites/breve-palm-offre-un-compagnon-aux-smartphones-3057601-actualite.php3
CSS - Les couleursLes couleurs Le standard CSS propose différentes façons de définir des couleurs : par un nom avec la notation hexadécimale avec la notation décimale Appel d'une couleur par son nom Le langage HTML définit des noms pour un nombre limité de... www.commentcamarche.net/contents/css/css-couleurs.php3
Compression vidéo (codecs)Notion de codec Une image d'une vidéo non compressée occupe une taille d'environ 1 Mo. Afin d'obtenir une vidéo paraissant fluide il est nécessaire d'avoir une fréquence d'au moins 25 ou 30 images par seconde, ce qui produit un flux de données... www.commentcamarche.net/contents/video/compvid.php3
Le codage RGB (RVB)Le codage RGB Le codage RGB (Red, green, blue, pour Rouge Vert Bleu, en français RVB), mis au point en 1931 par la Commission Internationale de l'Eclairage (CIE) consiste à représenter l'espace des couleurs à partir de trois rayonnements... www.commentcamarche.net/contents/video/rgb-rvb.php3