Vous êtes sur la page 1sur 457

Vincent Gouvernelle

LE GUIDE DE SURVIE

C++
LESSENTIEL DU CODE ET DES COMMANDES

Pearson Education France a apport le plus grand soin la ralisation de ce livre afin de vous fournir une information complte et fiable. Cependant, Pearson Education France nassume de responsabilits, ni pour son utilisation, ni pour les contrefaons de brevets ou atteintes aux droits de tierces personnes qui pourraient rsulter de cette utilisation. Les exemples ou les programmes prsents dans cet ouvrage sont fournis pour illustrer les descriptions thoriques. Ils ne sont en aucun cas destins une utilisation commerciale ou professionnelle. Pearson Education France ne pourra en aucun cas tre tenu pour responsable des prjudices ou dommages de quelque nature que ce soit pouvant rsulter de lutilisation de ces exemples ou programmes. Tous les noms de produits ou marques cits dans ce livre sont des marques dposes par leurs propritaires respectifs.

Publi par Pearson Education France 47 bis, rue des Vinaigriers 75010 PARIS Tl. : 01 72 74 90 00 www.pearson.fr Relecteurs techniques : Philippe Georges et Yves Mettier Collaboration ditoriale: Jean-Philippe Moreux Ralisation paO : La B.
ISBN : 978-2-7440-4011-5 Copyright 2009 Pearson Education France

Tous droits rservs

Aucune reprsentation ou reproduction, mme partielle, autre que celles prvues larticle L. 122-5 2 et 3 a) du code de la proprit intellectuelle ne peut tre faite sans lautorisation expresse de Pearson Education France ou, le cas chant, sans le respect des modalits prvues larticle L.122-10 dudit code.

Table des matires


1 Bases hrites du langage C .. . . . . . . . . . . . . . . . . . . . . . .
Hello world en C .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Commentaires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Types fondamentaux . . . . . . . . . . . . . . . . . . . Types labors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Structures conditionnelles . . . . . . . . . . . . . . . . Structures de boucle . . . . . . . . . . . . . . . . . . . Sauts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Fonctions . . . . . . . . . . . . . . . . . . . . . . . . . Prprocesseur . . . . . . . . . . . . . . . . . . . . . . . Oprateurs et priorit (C et C++) . . . . . . . . . . . . . . . . . . . . . . . . 1 1 3 4 6 9 12 14 15 16 18

2 Bases du langage C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23


Hello world en C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Les mots-cls . . . . . . . . . . . . . . . . . . . . . . . Les constantes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Dclarations de variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Les nouveaux types de variables duC++ . . . . . . . . . . . . . . . . Conversion de type C . . . . . . . . . . . . . . . . . . . Conversion avec static_cast . . . . . . . . . . . . . . . Conversion avec const_cast .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . Conversion avec reinterpret_cast . . . . . . . . . . . . . . . . Conversion avec dynamic_cast . . . . . . . . . . . . . . . . . . . . . . . . . . Surcharge . . . . . . . . . . . . . . . . . . . . . . . . . Les espaces de noms . . . . . . . . . . . . . . . . . . . Incompatibilits avec le C .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Lier du code C et C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Embarquer une fonction . . . . . . . . . . . . . . . . . Constantes usuelles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 24 25 27 29 32 33 34 35 36 37 40 42 44 46 46

IV

C++

3 Pointeurs et rfrences .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
Crer et initialiser un pointeur . . . . . . . . . . . . . . Accder aux donnes ou fonctions membres .. . . . . . . . . . . Crer et utiliser une rfrence . . . . . . . . . . . . . . Dclarer un pointeur sur un tableau . . . . . . . . . . . Dclarer un pointeur sur une fonction . . . . . . . . . . . . . . . . . . Passer un objet en paramtre par pointeur/rfrence . . 48 49 50 50 54 57

4 Classes et objets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
Ajouter des donnes des objets . . . . . . . . . . . . . . . . . . . . . . . . Lier des fonctions des objets . . . . . . . . . . . . . . Dterminer la visibilit de fonctions ou de donnes membres . . . . . . . . . . . . . . . . . Expliciter une instance avec le pointeur this . . . . . . . . . . . . Dnir un constructeur/destructeur . . . . . . . . . . . Empcher le compilateur de convertir une donne en une autre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Spcier quune fonction membre ne modie pas lobjet li . . . . . . . . . . . . . . . . . Rendre une fonction/donne membre indpendante de lobjet li . . . . . . . . . . . . . . . . Comprendre le changement de visibilit lors de lhritage .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Comprendre les subtilits de lhritage multiple . . . . . . . . Empcher la duplication de donnes avec lhritage virtuel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Simuler un constructeur virtuel .. . . . . . . . . . . . . . . . . . . . . . . . . Crer un type abstrait laide du polymorphisme . . . . . . Utiliser lencapsulation pour scuriser un objet . . . . . Obtenir des informations de types dynamiquement . . . . Transformer un objet en fonction .. . . . . . . . . . . . . . . . . . . . . . . 60 62 64 67 68 70 72 73 74 78 79 80 82 85 86 88

Table des matires

5 Templates et mtaprogrammation . . . . . . . . . . . . . . . . 95
Crer un modle de classe rutilisable . . . . . . . . . . . . . . . . . . 96 Crer une bibliothque avec des templates . . . . . . . 99 Utiliser un type indirect dans un template .. . . . . . . . . . . . . . . . . 101 Changer limplmentation par dfaut fournie par un template .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102 Spcialiser partiellement limplmentation dun template . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104 Spcialiser une fonction membre . . . . . . . . . . . . . . . . . . . . . . . . 105 Excuter du code la compilation . . . . . . . . . . . . . . . . . . . . . . 106 Crer des mta-oprateurs/mtabranchements . . . . . . . . 108 Avantages et inconvnients de la mtaprogrammation 111

6 Gestion de la mmoire . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113


Rserver et librer la mmoire . . . . . . . . . . . . . . Rednir le systme dallocation mmoire . . . . . . . . . . . . Simuler une allocation dobjet une adresse connue . . Traiter un chec de rservation mmoire .. . . . . . . . . . . . . . . Dsactiver le systme dexception lors de lallocation .. Optimiser lallocation avec un pool mmoire .. . . . . . . . . . . 113 114 115 116 119 120

7 Exceptions . . . . . . . . . . . . . . . . . . . . . . . 123
Principe . . . . . . . . . . . . . . . . . . . . . . . . . . Transmettre une exception . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Expliciter les exceptions .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Utiliser ses propres implmentations des fonctions terminate() et unexpected() . . . . . . . . . . . . . . Utiliser les exceptions pour la gestion des ressources . . Exceptions de la STL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123 127 129 131 132 134

VI

C++

8 Itrateurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137
Les diffrents concepts . . . . . . . . . . . . . . . . . . Comprendre les iterator_traits . . . . . . . . . . . . . . . . . . . . . . . . . . Calculer la distance entre deux itrateurs . . . . . . . . Dplacer un itrateur vers une autre position . . . . . . . . . . Comprendre les itrateurs sur ux dentre/lecture . . . . Comprendre les itrateurs sur ux de sortie/criture . . Utiliser les itrateurs de parcours invers . . . . . . . Utiliser les itrateurs dinsertion . . . . . . . . . . . . . Utiliser les itrateurs dinsertion en dbut de conteneur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Utiliser les itrateurs dinsertion en n de conteneur .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138 140 142 144 145 146 146 148

149 150

9 Conteneurs standard . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151


Crer un conteneur . . . . . . . . . . . . . . . . . . . . Ajouter et supprimer dans un conteneur squentiel . . . . Parcourir un conteneur . . . . . . . . . . . . . . . . . . Accder un lment dun conteneur . . . . . . . . . . Crer et utiliser un tableau . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Crer et utiliser une liste chane .. . . . . . . . . . . . . . . . . . . . . . . Crer et utiliser une le double entre . . . . . . . . . . . . . . . . Crer et utiliser une pile .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Crer et utiliser une queue . . . . . . . . . . . . . . . . Crer et utiliser une queue de priorit . . . . . . . . . . Crer et utiliser un ensemble . . . . . . . . . . . . . . . . . . . . . . . . . . . . Crer et utiliser une table associative . . . . . . . . . . Crer et utiliser une table de hachage . . . . . . . . . . . . . . . . . . Connatre la complexit des fonctions membres des conteneurs . . . . . . . . . . . . . . . . . . . . . . 152 153 154 156 158 160 161 163 164 165 166 167 170 173

Table des matires

VII

10 Chanes de caractres . . . . . . . . . . . . . . . . 177


Crer une chane . . . . . . . . . . . . . . . . . . . . . Connatre la longueur dune chane . . . . . . . . . . . Comparer des chanes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . changer le contenu de deux chanes . . . . . . . . . . Rechercher une sous-chane . . . . . . . . . . . . . . . Extraire une sous-chane . . . . . . . . . . . . . . . . . Remplacer une partie dune chane . . . . . . . . . . . . . . . . . . . . . . Insrer dans une chane . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Concatner des chanes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Effacer une partie dune chane .. . . . . . . . . . . . . . . . . . . . . . . . . Lire des lignes dans un ux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 178 180 180 181 182 184 185 187 188 189 190

11 Fichiers et ux .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193
Ouvrir un chier . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Tester ltat dun ux . . . . . . . . . . . . . . . . . . . Lire dans un chier . . . . . . . . . . . . . . . . . . . . crire dans un chier . . . . . . . . . . . . . . . . . . . Se dplacer dans un ux . . . . . . . . . . . . . . . . . Manipuler des ux .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Manipuler une chane de caractres comme un ux . . crire dans une chane de caractre comme dans un ux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Lire le contenu dune chane comme avec un ux . . . . . . 194 195 197 200 201 202 205 206 206

12 Algorithmes standard . . . . . . . . . . . . . . . . 209


Calculer la somme des lments dune squence . . . . 214 Calculer les diffrences entre lments conscutifs dune squence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215 Chercher la premire occurrence de deux lments conscutifs identiques .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 217

VIII

C++

Rechercher un lment dans une squence . . . . . . . 218 Copier les lments dune squence dans une autre . . . . 219 Copier les lments dune squence dans une autre en commenant par la n .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221 Copier les n premiers lments dune squence dans une autre .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222 Compter le nombre dlments correspondant une valeur donne . . . . . . . . . . . . . . . . . . . 223 Compter le nombre dlments conformes un test donn . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 224 Tester si deux squences sont identiques .. . . . . . . . . . . . . . . 225 Chercher la sous-squence dlments tous gaux un certain lment . . . . . . . . . . . . . . . . . . . 226 Initialiser une squence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 228 Chercher le premier lment tel que . . . . . . . . . . . . . . . . . . 228 Chercher le premier lment parmi .. . . . . . . . . . . . . . . . . . . 229 Appliquer une fonction/foncteur sur tous les lments dune squence . . . . . . . . . . . . 230 Initialiser une squence laide dun gnrateur de valeurs . . . . . . . . . . . . . . . . . . . . . . . . . 231 Tester si tous les lments dune squence sont dans une autre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 232 Calculer le produit intrieur (produit scalaire gnralis) de deux squences . . . . . . . . . . . . . . 234 Initialiser les lments dune squence avec une valeur (en lincrmentant) . . . . . . . . . . . 235 Transformer une squence en tas et lutiliser . . . . . . 236 Comparer lexicographiquement deux squences .. . . . . . . 238 Chercher le premier/dernier endroit o insrer une valeur sans briser lordre dune squence . . . . . . 241 Fusionner deux squences tries . . . . . . . . . . . . . . . . . . . . . . . . 243 Rcuprer le plus petit/grand lment . . . . . . . . . . . . . . . . . . 245 Rcuprer le plus petit/grand lment dune squence 246 Trouver le premier endroit o deux squences diffrent 247

Table des matires

IX

Gnrer la prochaine plus petite/grande permutation lexicographique dune squence . . . . . . . . . . . . . 248 Faire en sorte que le nime lment soit le mme que si la squence tait trie . . . . . . . . . . . . . . . . . . . . . . . . . . . . 250 Trier les n premiers lments dune squence . . . . . . 251 Copier les n plus petits lments dune squence . . . . 252 Calculer une somme partielle gnralise dune squence . . . . . . . . . . . . . . . . . . . . . . 253 Couper la squence en deux en fonction dun prdicat 254 Calculer xi (fonction puissance gnralise) . . . . . . . . . . . . 256 Copier alatoirement un chantillon dune squence . . 257 Copier alatoirement un sous-chantillon (de nlments), en prservant leur ordre dorigine . . . 258 Mlanger les lments dune squence .. . . . . . . . . . . . . . . . . 259 Supprimer certains lments dune squence . . . . . . 260 Copier une squence en omettant certains lments . . 262 Remplacer certains lments dune squence . . . . . . . . . . 263 Inverser lordre de la squence . . . . . . . . . . . . . . . . . . . . . . . . . . 264 Effectuer une rotation des lments de la squence .. . . 264 Chercher une sous-squence . . . . . . . . . . . . . . . . . . . . . . . . . . . . 265 Construire la diffrence de deux squences tries . . . . . . 268 Construire lintersection de deux squences tries .. . . . . 270 Construire la diffrence symtrique des deux squences tries . . . . . . . . . . . . . . . . . . . . 272 Construire lunion de deux squences tries . . . . . . . . . . . . 273 Trier une squence .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 274 changer le contenu de deux variables, itrateurs ou squences . . . . . . . . . . . . . . . . . . . . . . . 275 Transformer une (ou deux) squences en une autre . . . . 276 Supprimer les doublons dune squence (dans ou lors dune copie) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 278 Copier laide du constructeur par copie . . . . . . . . 281 Initialiser laide du constructeur par copie . . . . . . . . . . . . 282

C++

13 BOOST . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 285
Mettre en forme des arguments selon une chane de formatage . . . . . . . . . . . . . . . . . . . . . . . Convertir une donne en chane de caractres .. . . . . . . . . Construire et utiliser une expression rgulire . . . . . . . . . . viter les pertes mmoire grce aux pointeurs intelligents .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Crer des unions de types scurises . . . . . . . . . . . . . . . . . . . . Parcourir un conteneur avec BOOST_FOREACH . . . . . Gnrer des messages derreur pendant le processus de compilation .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 286 289 291 295 300 304 306

14 Programmation multithread avec QT . . . . . . . 311


Crer un thread . . . . . . . . . . . . . . . . . . . . . . Partager des ressources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Se protger contre laccs simultan une ressource avec les mutex .. . . . . . . . . . . . . . . . . . . . . . . . . Contrler le nombre daccs simultans une ressource avec les smaphores .. . . . . . . . . . . . . . . . . . . Prvenir le compilateur de lutilisation dune variable dans plusieurs threads . . . . . . . . . . 311 312 313 316 318

15 Base de donnes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 319


Savoir quand utiliser SQLite .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . Crer et utiliser une base avec linterprteur SQLite . . . . Crer/ouvrir une base (SQLite) . . . . . . . . . . . . . . Lancer une requte avec SQLite . . . . . . . . . . . . . Fermer une base (SQLite) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Crer une table (requte SQL) . . . . . . . . . . . . . . Accder aux donnes dune table (requte SQL) . . . . Dnir un environnement ODBC (wxWidgets) . . . . . . . . . . Se connecter une base (wxWidgets) . . . . . . . . . . Crer la dnition de la table et louvrir (wxWidgets) . . 321 328 331 335 337 338 347 349 350 352

Table des matires

XI

Utiliser la table (wxWidgets) . . . . . . . . . . . . . . . Fermer la table (wxWidgets) . . . . . . . . . . . . . . . Fermer la connexion la base (wxWidgets) . . . . . . . Librer lenvironnement ODBC (wxWidgets) . . . . . . . . . . . .

355 356 357 358

16 XML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 359
Charger un chier XML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 360 Manipuler des donnes XML . . . . . . . . . . . . . . . 363

Annexes A Bibliothques et compilateurs . . . . . . . . . . . 369


Compilateurs .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . IDE et RAD . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Bibliothques . . . . . . . . . . . . . . . . . . . . . . . Bibliothques dominante graphique . . . . . . . . . . Utilitaires . . . . . . . . . . . . . . . . . . . . . . . . . 369 370 372 379 382

B Les ajouts de la future norme C++ (C++0x) . . . . 385


Variable locale un thread . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Unicode et chanes littrales . . . . . . . . . . . . . . . Dlgation de construction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Hritage des constructeurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Constructeur et destructeur par dfaut . . . . . . . . . union tendue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Oprateurs de conversion explicites . . . . . . . . . . . Type numration fort .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Listes dinitialisation .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Initialisation uniformise . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Type automatique . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Boucle for ensembliste . . . . . . . . . . . . . . . . . . Syntaxe de fonction unie .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 386 386 388 389 392 394 395 395 396 397 399 400 401

XII

C++

Concepts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Autorisation de sizeof sur des membres . . . . . . . . . Amlioration de la syntaxe pour lutilisation des templates . . . . . . . . . . . . . . . . . . . . . . . Template externe . . . . . . . . . . . . . . . . . . . . . Expressions constantes gnralises . . . . . . . . . . . . . . . . . . . . Les variadic templates .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Pointeur nul . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Tuple . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Lambda fonctions et lambda expressions . . . . . . . .

402 406 407 407 408 410 411 412 414

C Conventions dappel x86 .. . . . . . . . . . . . . . . . . . . . . . . . . . . 417


Convention dappel cdecl . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Convention dappel pascal . . . . . . . . . . . . . . . . Convention dappel stdcall . . . . . . . . . . . . . . . . Convention dappel fastcall . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Convention dappel thiscall . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 419 421 422 422 423

Index .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 425

Table des matires

XIII

propos de lauteur
Vincent Gouvernelle est ingnieur en informatique, diplm de lESIL Marseille, et titulaire dun DEA en informatique. Aprs une priode consacre lenseignement et la recherche, au cours de laquelle il a particip des travaux sur la paramtrisation de surfaces au sein du CNRS et du BRGM, il sest tourn vers le monde de lindustrie. Chez Gemplus, il a travaill sur la personnalisation des cartes puce et lcriture de drivers optimiss pour le chiffrement de donnes. Puis au sein de Coretech International, maintenant liale de Think3, il revient sa premire spcialit, la CAO, dans le domaine de la conversion et la correction de modles. Il travaille aujourdhui chez Sescoi R&D, socit ditrice de logiciels de CFAO.

Introduction
Il y a longtemps que je souhaitais runir toutes les informations relatives aux algorithmes et lutilisation de la bibliothque standard (STL) et de BOOST. Je me suis dit quil tait temps de passer lacte lorsque loccasion ma t donne dcrire ce livre. Lobjectif de la collection Guide de survie auquel ce dernier appartient est de fournir au monde informatique lquivalent des aide-mmoire linguistiques que lon emmne avec soi ltranger. Ainsi, cet ouvrage nest pas un simple dictionnaire de fonctions ou de mots-cls: un effort particulier a t fait pour mettre en situation chacun deux an de vous permettre den exploiter tout le potentiel dans le contexte qui est le vtre. Les squences de codes (relatives aux bibliothques standard et BOOST) fournies dans ce livre fonctionnent avec les compilateurs mentionns dans le tableau ci-dessous. La liste nest bien entendu pas exhaustive. Pour ce qui est des parties employant wxWidget et QT, il suft de recourir une plate-forme disposant de ces bibliothques. Je voudrais remercier les personnes sans qui cet ouvrage ne serait pas. Pour tout le temps que je nai pas pu leur consacrer pendant la rdaction de ces pages, je remercie mon pouse Audrey et mes trois enfants. Merci galement Patricia Moncorg de la conance quelle ma accorde pour la ralisation de ce projet, aux relecteurs techni quesPhilippe Georges et Yves Mettier et au correcteur Jean-Philippe Moreux. Merci enn Yves Bailly qui fut le premier mencourager dans cette aventure.

XVI

C++

Jespre que cet ouvrage comblera vos attentes de programmeur, que vous soyez dbutant ou chevronn.
Compilateurs Plate-forme Compilateurs

Windows Linux Mac OS X HP-UX IBM AIX True64 Sun Solaris

Visual C++ (7.1 avec SP1 aka 2003, 8.0 aka 2005, 9.0 aka 2008), Intel C++ (10.1), Comeau C++ (4.3), MingGW, Cygwin GCC (3.4, 4.0, 4.1, 4.2, 4.3), Intel C++ (8.1, 9.0, 9.1, 10.0), QLogic (3.1), Sun Compiler (5.9, 5.10 avec stdcxx) GCC 4 (pour PowerPC ou Intel) GCC 4.2, HP C/aC++, HP aCC IBM XL C/C++ (10.1) Compaq C++ (7.1) Sun C++ (5.7, 5.8, 5.9), GCC (3.4)

1
Bases hrites du langage C
Le langage C++ est une volution du langageC. De ce fait, une partie de la syntaxe est commune ces deux langages. Ce chapitre rsume rapidement ces points communs quelque peu amliors au passage. Pour plus de renseignements sur le langageC, vous pouvez vous rfrer au Guide de survie LangageC dYves Mettier (Pearson, 2007).

Hello world en C
#include <stdio.h> int main(int argc, char* argv[]) { printf(Hello world!); return 0; }

La premire ligne est une inclusion dun chier den-tte de la bibliothque C standard. Cela permet dutiliser la fonction printf() pour crire le message Hello world!

CHAPITRE 1 Bases hrites du langage C

sur la console. La deuxime ligne correspond au point dentre standard de tout programmeC (et C++), cest-dire que cette fonction est automatiquement appele lors du lancement du programme. Notez toutefois que le nommage et les paramtres de ce point dentre peuvent varier suivant les systmes. Par exemple, Windows a ajout le point dentre suivant:
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nShowCmd);

Enn, le return 0; indique que le programme sest droul sans erreur.

Compiler avec gcc


Pour compiler cet exemple, vous pouvez utiliser gcc en ligne de commande. Par exemple avec mingw ou cygwin sous Windows, ou toute autre version de gcc (disponibles sous GNU/Linux, Unix, MacOSX, etc.):
gcc helloworld.c o helloworld.exe

Loption c permet de compiler un chier source et de crer un chier objet, rutilisable par la suite. Loption I permet dajouter des chemins de recherche supplmentaires pour les chiers den-tte. Loption lbibliotheque permet de linker avec la bibliotheque donne. Loption L permet dajouter des chemins de recherche supplmentaires pour les bibliothques.

Commentaires

Pour plus de simplicit, il existe aussi des environnements intgrs pour le compilateur GNU. Je vous invite jeter un il sur lexcellent DevCPP (www. bloodshed.net/devcpp.html disponible pour Windows), CodeBlocks (www.codeblocks.org, disponible pour Windows, Linux et Mac OSX) ou encore Anjuta (anjuta.sourceforge.net disponible pour GNU/Linux).

Compiler avec Microsoft Visual C++


Ouvrez lenvironnement de dveloppement et laissez-vous guider par les assistants de cration de projet. Pour cet exemple, choisissez une application en mode console.

Commentaires
/* commentaire C possible sur plusieurs lignes */ // commentaire C++ sur une seule ligne

Les commentaires C commencent avec une barre oblique et un astrisque/* et nissent par un astrisque et une barre oblique */. Leur contenu peut staler sur plusieurs lignes et commencer ou nir en milieu de ligne. Le commentaire C++ commence ds lapparition de deux barres obliques // et nit automatiquement la n de la ligne.

CHAPITRE 1 Bases hrites du langage C

Types fondamentaux
char caractere; short entier_court; int entier; long entier_long; oat ottant_simple_precision; double ottant_double_precision; signed Type v; unsigned Type v; void

Le langage C (et donc C++) comprend de nombreux types de nombres entiers, occupant plus ou moins de bits. La taille des types nest que partiellement standardise: le standard xe uniquement une taille minimale et une magnitude minimale. Les magnitudes minimales sont compatibles avec dautres reprsentations binaires que le complment deux, bien que cette reprsentation soit presque toujours utilise en pratique. Cette souplesse permet au langage dtre efcacement adapts des processeurs trs varis, mais elle complique la portabilit des programmes crits en C/C++. Chaque type dentier a une forme signe (pouvant reprsenter un nombre positif ou ngatif, communment appels nombres relatifs) et une forme non signe (ne pouvant reprsenter que des nombres positifs communment appels nombres naturels). Par dfaut, les types entiers sont signs, rendant le qualicateur signed optionnel. Le type char est un type entier comme les autres, mis part le fait que le standard ne prcise pas sil est sign ou non par dfaut.

Types fondamentaux

Modle de donnes et taille des entiers (en bits) Modle LP32 ILP32 LP64 ILP64 short 16 16 16 16 int 16 32 32 64 long 32 32 64 64 pointeur 32 32 64 64

Limite des types fondamentaux (modle ILP32)


Type
char unsigned char short unsigned short int unsigned int long unsigned long long long
(octets)

Taille min
1 1 2 2 4 4 4 4 8 128 0 32 768 0 2147483648 0 2147483648 0 92233720368547758079 0 10255 102047

max
127 255 32767 65535 2147483647 4294967295 2147483647 4294967295 223372036854775807 18446744073709551615 10255 102047

unsigned long long 8 oat double 4 8

CHAPITRE 1 Bases hrites du langage C

Info Le mot-cl register permet dindiquer au compilateur dutiliser un registre du processeur. Ceci nest quune indication fournie au compilateur et non une garantie.

Le type void est utile pour le type pointeur sur type inconnu void* et pour la dclaration de procdure (une fonction retournant void est une procdure).

Types labors
struct NomStructure { }; union NomUnion { }; enum NomEnum { }; Type *pointeur; Type tableau[taille]; Type fonction(parametres); typedef Type nouveauNom; typedef Type (*MonTypeDeFonction)();

Le C++ a apport son lot damlioration quant la manire de dclarer de nouveaux types struct, union et enum. Il suft maintenant dutiliser le nom de la structure ainsi dnie sans devoir systmatiquement rpter le motcl qui la prcde. Il nest plus besoin de recourir de recourir un alias (typedef) pour parvenir au mme rsultat. Cest cette syntaxe allge qui est prsente ici. Les structures (struct) permettent de regrouper plusieurs variables dans une sorte de groupe et de les rendre ainsi indissociables, comme sil sagissait dun nouveau type.

Types labors

Dans ce cas, le nom utilis pour chaque variable dans le bloc devient le moyen daccder celle-ci.
struct Personne { char *nom, *prenom; int age; }; Personne personne = { Alain, Dupont, 54 }; personne.age = 35;

Les unions de type (union) permettent de voir une mme donne (binaire) de diffrentes manires. La syntaxe est la mme que les struct mais son objectif est diffrent.
union IP { unsigned int adresse; struct { unsigned char a,b,c,d; }; }; IP masque; masque.adresse = 0; // masque.a, .b, .c et .d == 0 masque.a = 255; masque.b = 255; masque.c = 255; // masque <=> 255.255.255.0 printf(%X\n, masque.adresse); // => afche FFFFFF00

Les numrations (enum) permettent dassocier des noms des valeurs entires. Seul le type int est support par le langage (la future norme C++0x lvera cette limitation). Elles permettent ainsi de rendre votre code source beaucoup plus lisible et mieux contrl par le compilateur (par exemple lors dun switch sur une valeur de type numration, le compilateur gnre un warning si aucune clause default

CHAPITRE 1 Bases hrites du langage C

nest prsente alors que toutes les valeurs de lnumration ne sont pas prsentes).
enum Etat { Vrai = 1, Faux = 0, Inconnu = -1 };

Par dfaut, toutes les valeurs se suivent dans lordre croissant (si aucune valeur nest mentionne), en commenant par la valeur zro. Lexemple suivant illustre ce mcanisme:
enum Nombre { Zero, Un, Deux, Quatre = 4, Cinq, Sept = 7,
Huit, Neuf };

Les crochets [] permettent de crer des tableaux (de taille xe).


int vecteur[3]; // trois entiers int matrice[3][3]; // neuf entiers

Le mot-cl typedef permet de crer des alias sur dautres types an de pouvoir les utiliser plus facilement ou bien den crer de nouveau en leur donnant ainsi un nouveau nom.
// un rel de IR et cod en double typedef Reel Vecteur[3]; // vecteur de IR3 typedef Reel Matrice[3][3]; // matrice de transformation Vecteur v,v2; Matrice m; v[0] = 3.0; v[1] = 5.0; v[2] = 1.0; m[0][0] = 1.0; /**/ m[2][2] = 1.0; typedef double Reel;

Structures conditionnelles

Structures conditionnelles
if (test) instruction; if (test) instruction; else instruction;

switch (instruction) { case valeur1: instruction; [ break; ] case valeur2: [ default: [ instruction; [ break; ] ] ] }

Les structures conditionnelles permettent dexcuter une instruction ou une autre en fonction dun test. Les parenthses entourant le test sont obligatoires. Un test est une instruction dont la valeur est entire ou boolenne. Dans le cas dune valeur entire, toute valeur non nulle est considre comme tant vraie.

10

CHAPITRE 1 Bases hrites du langage C

Lorsque linstruction excuter (pas celle du test mais celle choisie en fonction du test) est un bloc de code (entre accolade { }), il ne faut pas mettre de point-virgule aprs celui-ci. Le code suivant lillustre sur un if:
if (test) { // bloc1 } else { // bloc 2 }

La structure conditionnelle switch est une structure de branchement. Les valeurs utilises pour les points dentre des branchements (case) doivent tre des constantes et ne peuvent pas tre des chanes. Lorsquun point dentre est choisi par le test, lexcution se poursuit cet endroit et ne sort du switch que lorsquun break est rencontr (et pas lors du prochain case).
Attention Il est interdit deffectuer une dclaration de variable dans un case. Si vous navez pas dautre choix (pour une variable temporaire), faites-le alors dans un bloc dinstruction. Mais ce dernier ne peut pas contenir de case. int i = ; switch (i) { case 1: case 2: // si i==1 ou i==2, on se retrouve ici {

Structures conditionnelles

11

// on utilise un bloc si besoin de dclarer des


variables temporaires

int j = ; i += 3 * j; } break; case 3: // si i==3, on se retrouve l i += 4; if ( une_fonction_test(i) ) break; // si une_fonction_test(i) est vraie, on nit le switch default: // si i<1 ou i>3 // ou que le test dans le cas 3 tait faux // on se retrouve ici i /= 3; }

Oprateurs de comparaison Symbole a == b a != b a < b a > b a <= b a <= b Signication Vrai si a est gal b Vrai si a est diffrent de b Vrai si a est infrieur b Vrai si a est suprieur b Vrai si a est infrieur ou gal b Vrai si a est suprieur ou gal b

12

CHAPITRE 1 Bases hrites du langage C

Oprateurs logiques Symbole a && b a || b ! a Signication Vrai si a et b sont vrais Vrai si a ou b est vrai (lun, lautre ou les deux) Vrai si a est faux

Oprateurs binaires Symbole a & b a | b a ^ b Signication ET binaire OU binaire OU EXCLUSIF binaire

Structures de boucle
while (instruction) instruction; for ( initialisation ; test; incrment ) instruction; do { instruction; } while (test); break; continue;

Structures de boucle

13

Dans tous les cas, linstruction est excute tant que le test est vrai. Il est possible de modier le cours de lexcution de linstruction lorsque celle-ci est un bloc, laide des mots-cls continue et break : continue interrompt lexcution du bloc et revient au test de la structure de boucle; break interrompt lexcution du bloc et sort de la structure de boucle; lorsque plusieurs structures de boucle sont imbriques, ces sauts se rfrent la boucle dans laquelle ils se trouvent, et pas aux niveaux suprieurs. Le code ci-aprs montre comment crire une boucle for avec un while. La dnition de bloc entourant le code est ncessaire pour lcriture de son quivalence avec while, car en C++ linitialisation est locale la structure de boucle for.
{ initialisation; while (test) instruction; }

La structure do while permet de garantir que linstruction est excute au moins une fois. Cest un peu comme si la mme instruction apparaissait avant et dans un while traditionnel, comme dans lexemple suivant:
instruction; while (test) instruction;

14

CHAPITRE 1 Bases hrites du langage C

Sauts
tiquette: goto tiquette;

Les sauts permettent de sauter dun endroit un autre dans le l dexcution des instructions dun programme. Les tiquettes peuvent tre dotes de tout nom respectant les mmes contraintes syntaxiques que celles dun identicateur. Il nest pas possible deffectuer des sauts en dehors dune fonction. En revanche, il est possible deffectuer des sauts en dehors et lintrieur des blocs dinstructions sous certaines conditions (voir lavertissement ci-aprs). Si au terme dun saut, on sort de la porte dune variable, celleci est dtruite. Enn, il est impossible de faire un saut dans un bloc try {} (voir le Chapitre7 consacr aux exceptions).
Attention Les sauts sont fortement dconseills. Vous avez certainement dj entendu ou entendrez certainement que lon peut toujours sen passer. Cela est vrai, quelques exceptions prs. Toutefois, leur utilisation rend parfois le code plus lisible. Si vous pensez quil est lgitime de les utiliser, ne vous en privez pas. Mais nen abusez pas pour autant, car ils cachent certains piges, surtout avec le C++! En effet, si la dclaration de saut se trouve aprs une dclaration, cette dclaration ne doit pas contenir dinitialisations et doit tre un type simple (comme les variables, les structures ou les tableaux). Vous laurez donc compris, lutilisation de sauts avec des classes peut se rvler dlicate.

Fonctions

15

Fonctions
type identicateur(paramtres) { // instructions } return [ instruction ];

Les paramtres dune fonction sont de la forme type variable ou type variable = valeurParDefaut, spars par une virgule. Attribuer une valeur par dfaut nest possible que pour le (ou les) dernier(s) paramtre(s conscutifs). Si le type de retour dune fonction est void, alors il sagit dune procdure. La valeur de retour dune fonction est donne par linstruction return. Cette instruction fait sortir immdiatement de la fonction (ou de la procdure).
Info Il est possible dcrire des fonctions acceptant un nombre variable de paramtres grce au chier den-tte stdarg.h. Le code ci-aprs montre un exemple. Sachez toutefois quun tel code nest pas toujours portable et que limplmentation de cette fonctionnalit varie suivant les compilateurs. #include <stdarg.h> double somme(int quantite, ...) { double res = 0.0; va_list varg; va_start(varg, quantite); while (quantite-- != 0) res += va_arg(varg, double);

16

CHAPITRE 1 Bases hrites du langage C

va_end(varg); return res; } La fonction printf() utilise cette technique. Le nombre, lordre et le type des arguments sont indiqus par le premier paramtre correspondant la chane de formatage des donnes.

Prprocesseur
// instructions #include chier #include <chier> #dene NOM code #dene NOM(a [, ]) code #undef NOM #if test #ifdef NOM // quivalent #if dened NOM #ifndef NOM // quivalent #if not dened NOM #elif test // C89 #else #endif #pragma // C89 #error Message #warning Message #line numro [chier]

Prprocesseur

17

// constantes __FILE__ __LINE__ __FUNCTION__ // alternative: __func__ __DATE__ __TIME__ __cpluplus

La directive #include permet dinclure le chier mentionn cet endroit. Lorsque le nom est entre guillemets, le chier spci est recherch dans le rpertoire courant dabord, puis de la mme manire quavec les crochets. Si le nom de chier est entre crochets, le chier est recherch dans les rpertoires (ou dossiers) spcis dans les options dinclusion (-I avec g++) puis dans les chemins de recherche des en-ttes du systme. Le chier inclus est lui aussi trait par le prprocesseur. Le prprocesseur dnit un certain nombre de constantes: __LINE__ donne le numro de la ligne courante; __FILE__ donne le nom du chier courant; __DATE__ renvoie la date du traitement du chier par le pr-processeur; __TIME__ renvoie lheure du traitement du chier par le pr-processeur; __cpluplus est dni lors dune compilation en C++. Il permet de distinguer les parties de code crites en C++ de celles crites en C. En principe, la valeur dnie correspond la norme du langage supporte par le compilateur. Par exemple, pour la norme de novembre 1997, la valeur sera 199711L.

18

CHAPITRE 1 Bases hrites du langage C

Astuce Pour convertir un paramtre de macro, il est parfois ncessaire dimbriquer deux macros pour obtenir un rsultat fonctionnant correctement. Cest le cas par exemple lors de la transformation dun paramtre en sa chane de caractres correspondante (par linstruction #). Le code suivant montre comment sen sortir dans ce cas: #dene TO_STR1(x) #x #dene TO_STR(x) TO_STR1(x) La syntaxe ## permet de concatner deux paramtres. Le code suivant permet dobtenir un nom unique. Cette macro, utilise plusieurs fois sur une mme ligne, gnrera le mme nom. #dene UNIQUE_NAME2(name,line) name##line #dene UNIQUE_NAME1(name,line) UNIQUE_NAME2(name,line) #dene UNIQUE_NAME(name) UNIQUE_NAME1(name,__LINE__)

Les directives de compilation sont couramment utilises pour la protection des chiers den-tte contre les inclusions multiples:
#ifndef MON_HEADER #dene MON_HEADER // inclus une seule fois #endif

Oprateurs et priorit (C et C++)


Il est parfois difcile de se souvenir de lordre de priorit des oprateurs entre eux. Voici donc une liste les rassemblant de la plus haute priorit (on commence par valuer ceux-ci) la plus basse (le compilateur nira par ceux-l).

Oprateurs et priorit (C et C++)

19

Si toutefois vous aviez un doute, ou tout simplement parce que vous trouvez cela plus lisible, nhsitez pas utiliser les parenthses () pour forcer lordre dvaluation. Un groupe rassemble les oprateurs ayant mme priorit, cest--dire que leur valuation se fait dans lordre de lecture (sauf mention spciale). Jy ai galement inclus les oprateurs ajouts par leC++ an de tous les runir en un seul endroit.
Oprateurs par ordre de priorit Oprateur :: Groupe 2 () [] . -> ++ -- type() new new[] delete delete[] Modication de la priorit des oprateurs ou appel de fonction lment dun tableau Champ de structure ou dunion (ou de fonction membre enC++) Champ dsign par pointeur (slection de membre par drfrencement) Incrmentation (post-xe, par exemple ++i) Dcrmentation (post-xe, par exemple --i) Transtypage explicite (C++) Cration dynamique dobjets (C++) Cration dynamique de tableaux (C++) Destruction des objets crs dynamiquement (C++) Destruction des tableaux crs dynamiquement (C++) Signication Oprateur de rsolution de port (C++)

Groupe 1 (pas dassociativit)

20

CHAPITRE 1 Bases hrites du langage C

Oprateurs par ordre de priorit (suite) Oprateur ! ~ + - ++ -- & * sizeof() typeid() (type) const_cast dynamic_cast static_cast Groupe 4 .* ->* Slection de membre par pointeur sur membre (C++) Slection de membre par pointeur sur membre par drfrencement Signication Ngation boolenne Complment binaire Plus unaire Oppos (appel aussi moins unaire) Incrmentation (prxe, par exemple i++) Dcrmentation (prxe, par exemple i--) Adresse (pas le et binaire) Accs aux donnes indiques par un pointeur (drfrencement) Taille en nombre doctets de lexpression Identication dun type (C++), pas toujours implment Transtypage (cast) Transtypage de constante (C++) Transtypage dynamique (C++) Transtypage statique (C++)

Groupe 3 (associativit de droite gauche)

reinterpret_cast Rinterprtation (C++)

Oprateurs et priorit (C et C++)

21

Oprateurs par ordre de priorit (suite) Oprateur Groupe 5 * / % Groupe 6 + - Groupe 7 << >> Groupe 8 < <= > >= Groupe 9 == != Groupe 10 & Groupe 11 ^ OU eXclusif (XOR) binaire ET binaire Test gal Test diffrent de Test strictement infrieur Test infrieur ou gal Test suprieur Test suprieur ou gal Dcalage gauche Dcalage droite Addition Soustraction Multiplication Division Modulo (reste de la division euclidienne) Signication

22

CHAPITRE 1 Bases hrites du langage C

Oprateurs par ordre de priorit (suite) Oprateur Groupe 12


|

Signication OU binaire

Groupe 13 && Groupe 14


||

ET logique (ou boolen)

OU logique (ou boolen)

Groupe 15 ? : Oprateur conditionnel

Groupe 16 (associativit de droite gauche) = += -= *= /= %= <<= >>= &=


|=

Affectation Incrmentation et affectation Dcrmentation et affectation Multiplication et affectation Division et affectation Modulo et affectation Dcalage gauche et affectation Dcalage droite et affectation ET binaire et affectation OU binaire et affectation XOR binaire (OU exclusif) et affectation

^= Groupe 17 , (virgule)

Sparateur dans une liste dexpressions

2
Bases du langage C++
Le C++ est un langage de programmation permettant la programmation sous de multiples paradigmes, comme par exemple la programmation procdurale (hrite du langageC), la programmation oriente objet (voir le Chapitre4) et la programmation gnrique (voir le Chapitre5). Depuis 1995 environ, C++ est le langage le plus utilis au monde. Avant daborder des notions plus complexes, ce chapitre se concentre sur les bases du C++, en plus de celles hrites du langageC.

Hello world en C++


#include <iostream> int main(int argc, char* argv[]) { std::cout << Hello world! << std::endl; return 0; }

24

CHAPITRE 2 Bases du langage C++

Voici le pendant C++ du traditionnel HelloWorld du langageC, que nous avons vu dans le chapitre prcdent. Notez que par habitude, lextension du chier nest plus .c mais .cpp (on rencontre aussi .cxx ou encore .C surtout sous Unix et GNU-Linux, qui distinguent minuscules et majuscules dans les noms de chiers). Pour les chiers den-ttes C++, la STL (bibliothque standard du C++) a simplement supprim lextension (plus de .h); mais aujourdhui, lextension standard est .hpp (que lon retrouve dans la bibliothque BOOST). Si vous avez russi compiler ce programme enC, vous y parviendrez sans difcult enC++. Par contre, il ne faut plus invoquer gcc mais g++.

Les mots-cls
Voici la liste des mots rservs duC++ qui ne sont pas dj prsents dans le langageC. Elle vous sera utile pour retrouver facilement la section associe.
Les mots-cls du C++ Mot-cl bool catch class const_cast delete dynamic_cast explicit false friend Page 29 123 62 34 113 36 71 29 66 Mot-cl inline mutable namespace new operator private protected public reinterpret_cast Page 46 72 40 113 37 75 75 75 35

Les constantes

25

Les mots-cls du C++ (suite) Mot-cl static_cast template this throw true try Page 33 96 67 123 29 123 Mot-cl typeid typename using virtual wchar_t Page 86 101 41 79 31

Les constantes
Une constante est une valeur qui ne change pas au cours de lexcution dun programme. Une constante ressemble une macro par diffrents aspects: sa porte est rduite au chier o elle est dclare; les expressions lutilisant sont values la compilation; elle peut tre utilise pour dnir la taille dun tableau de typeC. Toutefois, contrairement aux macros, le compilateur peut allouer un emplacement mmoire o stocker la constante lorsque cela est requis. Cest par exemple le cas lorsque lon utilise son adresse:
const int constante_entiere = 345; const int *ptr_sur_constante = &constante_entiere;

Si les macros C #dene sont toujours disponibles, il est prfrable dutiliser le concept de constantes quoffre leC++. Cela permet une vrication de type la compilation et diminue le risque derreur.

26

CHAPITRE 2 Bases du langage C++

const int a = 2; int const b = 7; const Objet unobjet; const int const c= 7; const double d;

// // // //

quivalent la ligne prcdente un objet peut tre une constante ERREUR : const dupliqu ERREUR : initialisation manquante

Pour les pointeurs, le qualicatif const peut aussi bien sappliquer au pointeur qu llment point:
const int entier1; int* ptr = &entier1; // ERREUR : conversion invalide

// Pointeur sur une constante const int *a = &constante_entiere; int const *b = &constante_entiere; // quivalent // la ligne prcdente a = &constante_entiere; *a = 4; // ERREUR : on ne modie pas une constante // Pointeur constant sur une variable int *const c = &entier; int *const c; // ERREUR : initialisation manquante c = &entier1; // ERREUR : on ne modie pas une constante *c = 5; // Pointeur constant sur une constante const int *const d= &constante_entiere; int const *const e = &constante_entiere; // quivalent // la ligne prcdente const int *const f; // ERREUR : initialisation manquante d = &entier1; // ERREUR : on ne modie pas une constante *d = 5; // ERREUR : on ne modie pas une constante

Dclarations de variables

27

Dclarations de variables
En C++, la dclaration de variables est considre comme une instruction. Il nest pas ncessaire de regrouper toutes les dclarations de variables en dbut de bloccomme enC.
int a; int b = 0; Objet o; // dclaration dune variable // dclaration et initialisation // dclaration et appel du constructeur

Attention La dclaration de variables peut entraner lexcution de beaucoup de code, notamment lors de linitialisation des objets (classes).

Une variable dclare dans une boucle for implique que sa porte reste limite cette dernire:
for (int i=0; i<n; ++i) // for (int i=k; i>=0; --k) //

Attention Certains vieux compilateurs C++, comme Visual C++6, ne grent pas cette spcicit et transforment toute dclaration normalement locale la boucle for en une dclaration prcdant celle-ci. Ainsi, le code ci-dessus ne compilerait pas avec un tel compilateur: un message derreur prtextant que la variablei est dj dclare serait gnr pour la deuxime boucle.

28

CHAPITRE 2 Bases du langage C++

Pour vous assurer quune variable a une porte limite un chier, utilisez un espace de nom anonyme plutt que de recourir au mot-cl static:
static int n; namespace { int n; } // correcte mais de style C // on prfrera le style C++

Lutilisation de static pour une dclaration de variable dans un bloc a le mme effet quenC: linitialisation na lieu quune fois et son comportement est semblable celui dune variable globale uniquement visible dans ce bloc.
int f(int x) { static int a = x; return a; } int main(void) { cout << f(10) << endl; cout << f(12) << endl; }

Produira la sortie suivante:


10 10

Les nouveaux types de variables duC++

29

Les nouveaux types de variables duC++


bool Le type bool accepte deux tats: true (vrai): correspond la valeur1; false (faux): correspond la valeur0. Ce type est cod sur le mme nombre de bits que le type int. Lorsque lon convertit un type numraire en bool, toute valeur non nulle est considre comme true.
bool vrai = true; bool faux = false;

Les rfrences Une rfrence est une sorte de pointeur masqu. Pour dclarer un pointeur, il suft de rajouter un et commercial (&) aprs le type: siT est un type, le type rfrence sur T estT&.
int a= 3; int& r = a; // r est une rfrence sur a

Il est obligatoire dinitialiser une rfrence lors de sa dclaration, sinon vous obtiendrez un message derreur. En effet, r = b; ne transformera pasr en une rfrence surb mais copiera la valeur deb dansr.

30

CHAPITRE 2 Bases du langage C++

Astuce Les rfrences permettent de dnir des raccourcis (ou alias) sur des objets. int& element = tableau[indice]; element = entier; est quivalent : tableau[indice] = entier;

Attention Une fonction ne doit jamais renvoyer une rfrence sur un objet qui lui est local. int& fonction() { int res; // return res; // retourne une rfrence sur un objet // qui sera aussitt dtruit! } De la mme manire, il faut se mer. Dans certains cas, une rfrence retourne par une fonction peut savrer peu stable. class Pile { // public: double& sommet() const { return m_valeurs[m_niveau_ courant 1]; } double depiler() { return m_valeurs[--m_niveau_ courant]; } }; Pile pile; // pile.sommet() = pile.depiler() + 10.0; // Le rsultat est imprvisible!

Les nouveaux types de variables duC++

31

enum, struct et union


enum MonType { mtValeur1, mtValeur2, mtValeur3 }; MonType uneInstance = mtValeur2; struct MaStructure { /* */ }; MaStructure a; union MonUnion { /* */ }; MonUnion u;

Dj connu enC, la dclaration dun enum, dune struct ou dune union se trouve simplie enC++. En effet, il ny a plus besoin de rpter le mot-cl lors dune instanciation; lidentiant choisi lors de la dclaration suft. Nous ne dtaillerons pas ici la syntaxe de leur contenu, qui reste quivalente au langageC.
Info Cest un peu comme si enC, toute dclaration tait automatiquement suivie de: typedef enum MonType MonType; Il convient par l de faire attention au masquage local de toute autre dclaration de niveau suprieur.

wchar_t wchat_t est le type utilis pour les jeux de caractres tendus tel Unicode. Contrairement auC ou ce type est dni par un typedef (via linclusion du chier den-tte <stddef.h>), en C++ wchar_t est bel et bien un mot-cl du langage.

32

CHAPITRE 2 Bases du langage C++

Conversion de type C
lorigine, le comit de standardisation C++ prvoyait de supprimer la conversion de typeC. Cette dernire na t conserve que par soucis de rutilisation de code ancien et de compatibilit (un compilateur C++ doit pouvoir compiler duC). Cest pourquoi tout programmeur C++ est encourag la bannir et plutt utiliser les nouveaux oprateurs de conversion. Pourquoi les conversions de typeC sont-elles maintenant considres comme obsoltes? Voyez plutt:
void *p = &x; int n = (int) p; // conversion de type C

Cette conversion de type C, inoffensive de premier abord, recle en fait plusieurs dangers. Premirement, elle effectue des oprations diffrentes selon le contexte. Par exemple, elle peut transformer de manire sre un int en double, mais elle peut aussi effectuer des oprations intrinsquement dangereuses comme la conversion dun void* en valeur numrique (voir lexemple ci-dessus). En relisant un code source contenant une telle conversion, un programmeur ne pourra pas toujours dterminer si celle-ci est sre ou non, si le programmeur dorigine a fait une erreur ou non. Pire, une conversion de type C peut effectuer plusieurs oprations en une. Dans lexemple suivant, non seulement un char* est converti en unsigned char*, mais en plus le qualicateur const est limin en mme temps:
const char *msg = une chaine constante; unsigned char *ptr = (unsigned char*) msg; // est-ce intentionnel?

Encore une fois, il est impossible de dire si cest la volont du programmeur dorigine ou un oubli. Ces problmes

Conversion avec static_cast

33

relatifs ce type de conversion sont connus depuis des annes. C++ offre une meilleure solution avec les oprateurs de conversion. Ils rendent explicite lintention du programmeur et prservent la capacit du compilateur signaler les bogues potentiels prcits.

Conversion avec static_cast


static_cast<type>(expression)

Cest la conversion la plus proche de lancienne conversionC. Elle reste ventuellement dangereuse, mais rend explicite lintention du programmeur. Sont principalement autorises, en plus des conversions standard, les conversions: dun type entier vers un type numration; de B* vers D*, oB est une classe de base accessible deD (la rciproque est une conversion standard). Elle peut tre utilise mme lorsquune conversion implicite existe:
bool b = true; int n = static_cast<int>(b);

Dans dautres cas, lutilisation de static_cast est obligatoire, par exemple lors de la conversion partir dun void*:
int n = 0; void *ptr = &n; int *i = static_cast<int*>(ptr); // obligatoire

static_cast utilise linformation disponible la compilation pour effectuer la conversion de type requise. Ainsi, la

34

CHAPITRE 2 Bases du langage C++

source et la destination peuvent ne pas avoir la mme reprsentation binaire, comme lors de la conversion dun double vers un int o loprateur de conversion effectue le travail ncessaire une conversion correcte. Utiliser static_cast permet dviter certains cueils comme:
const char *msg = une chaine constante; unsigned char *ptr = static_cast<unsigned char*>( msg); // erreur

Cette fois, le compilateur signale une erreur indiquant quil est impossible denlever le qualicateur const avec ce type de conversion. Il en est de mme avec le qualicateur volatile.

Conversion avec const_cast


const_cast<type>(expression)

Enlever ou ajouter une qualication const ou volatile requiert loprateur de conversion const_cast. Notez que le type et le type de lexpression doivent tre les mmes, aux qualicatifs const et volatile prs, sinon le compilateur gnrera un message derreur.
struct A { void fonction(); // fonction membre non const }; void ma_fonction(const A& a) { a.func(); // erreur: appel une fonction non const

De manire vidente, il sagit dune erreur de conception. La fonction fonction() aurait d tre dclare const.

Conversion avec reinterpret_cast

35

Nanmoins, un tel code existe parfois dans des bibliothques existantes mal conues. Un programmeur inexpriment sera tent dutiliser une conversion brute laC. Pour rgler un tel problme, il est prfrable de supprimer le qualicateur const ainsi:
A &ref = const_cast<A&>(a); // enlve const ref.fonction(); // fonctionne maintenant correctement Attention Souvenez-vous toutefois que si const_cast permet de supprimer le qualicateur const, vous ne devez pas pour autant vous autoriser modier lobjet. Sinon, attendez-vous de mauvaises surprises

Conversion avec reinterpret_cast


reinterpret_cast<type>(expression)

loppos de static_cast, reinterpret_cast effectue une opration relativement dangereuse ou non portable. reinterpret_cast ne change pas la reprsentation binaire de lobjet source.Toutefois, il est souvent utilis dans des applications bas niveau qui convertissent des objets en dautres donnes transmises un ux doctets (et vice versa). Dans lexemple suivant, reinterpret_cast est utilis pour tromper le compilateur, permettant au programmeur dexaminer les octets lintrieur dune variable de type oat:
oat f=123; unsigned char *ptr = reinterpret_cast<unsigned char*>(&f); for (int i=0; i<4; ++i) cout << ptr[i] << endl;

36

CHAPITRE 2 Bases du langage C++

Utilisez loprateur reinterpret_cast pour expliciter toute conversion potentiellement dangereuse (et probablement non portable).
Info En quoi lexemple ci-dessus peut-il tre non portable? Imaginez une sauvegarde/lecture chier dun oat ou dun int avec un tel code. Sauvez votre valeur sur une machine little endian puis rechargez le chier de sauvegarde sur une machine de type big endian. Vous ne retrouverez probablement pas la valeur dorigine

Conversion avec dynamic_cast


dynamic_cast<type>(expression)

dynamic_cast diffre des trois autres oprateurs. Il utilise les informations de type dun objet pendant lexcution du programme plutt que celles connues la compilation (pour plus dinformation ce sujet, voir la section Obtenir des informations de type dynamiquement du Chapitre4). Deux scnarios requirent lutilisation de dynamic_cast: la conversion de spcialisation (ou downcast) lors de la conversion dune rfrence ou dun pointeur de classe vers une rfrence ou un pointeur dune classe drivant de la classe que lon veut downcaster; la conversion transversale (ou crosscast) lors de la conversion dun objet dhritage multiple vers une de ses classes de base.

Surcharge

37

Surcharge
La surcharge de fonctions (les oprateurs sont des fonctions) est une nouveaut apporte par le C++. Ce mcanisme, apparemment fort simple, suit des rgles strictes dont le rsultat nest pas toujours intuitif. Mais avant daller plus loin, voici ce mcanisme: reprer les fonctions candidates; les ltrer en ne gardant que les viables; les ltrer de nouveau selon le critre de la meilleure fonction viable. la suite de ce mcanisme, sil reste plus dune fonction, on aboutit une erreur de compilation du type appel ambigu; si la fonction trouve est inaccessible (comme une fonction membre prive ou protge) ou est virtuelle pure, on aboutit galement une erreur.
Info Les signatures des fonctions ne tiennent pas compte du type de la valeur de retour. Par consquent, le mcanisme de surcharge ne peut pas en tenir compte non plus.

Les fonctions candidates: ont le mme nom que la fonction appele; appartiennent toutes la mme rgion dclarative (la recherche commence dans le mme niveau que lappel, puis remonte au niveau suprieur jusqu en trouver une, puis recense toutes celles de ce mme niveau).

38

CHAPITRE 2 Bases du langage C++

Lexemple suivant illustre ce principe.


void print(double); void afcher() { void print(int); print(1.2); // fonctions candidates: print(int) } class Reel { public: void print(double); }; class Entier: public Reel { public: void print(int); }; Entier e; e.print(1.2); // fonctions candidates: Entier::print(int)

Attention Une exception est faite pour les oprateurs o lensemble des fonctions candidates stend lunion de ces trois domaines de recherche: les fonctions membres candidates (si le premier oprande est un objet); les fonctions non membres candidates; les oprateurs rednis.

Une fonction candidate est viable si: elle possde autant de paramtres que lappel (en tenant compte des valeurs par dfaut); le type de chaque paramtre correspond ( une conversion implicite prs).

Surcharge

39

Info Les fonctions membres sont traites comme des fonctions standard en leur adjoignant comme premier paramtre lobjet du type de la classe. Par exemple: class MaClasse { void fonction_1(int); void fonction_2(double) const; }; sera vu comme: void fonction_1(MaClasse*, int); void fonction_2(const MaClasse*, double); et lappel: monObject.fonction_1(3); comme: fonction_1(&monObjet,3);

La meilleure fonction viable est dtermine en fonction de la qualit des conversions utilises pour dterminer sa viabilit. Une conversion est meilleure si (dans lordre): elle ne ncessite aucune conversion, ou est une conversion triviale comme Type[] vers Type* (et sa rciproque), Type vers const Type (uniquement dans ce sens), f() vers (*f)() (et sa rciproque); elle est une promotion de char vers int, short vers int ou oat vers double; elle est une conversion standard (par exemple int vers oat); elle est une conversion utilisateur.

40

CHAPITRE 2 Bases du langage C++

void f(int, double); void f(double, int); f(1, 1); f(1.0, 1.0);

// appel ambigu // appel ambigu

Exemple de rsolution de surcharge ambigu


Attention Nous avons dit que la rsolution pouvait parfois tre droutante. En voici un exemple: class Object { public: operator int(); }; void fonction(double, int); void fonction(int, Objet); Objet obj; fonction(0.99, obj); Dans ce cas, la meilleure fonction viable sera fonction(int,Obj) malgr la prsence de loprateur de conversion.

Les espaces de noms


namespace nom { // dclarations / implmentations }

Les espaces de noms permettent de ranger du code dans des botes virtuelles. Cela permet par exemple de faire cohabiter

Les espaces de noms

41

des fonctions ayant le mme nom et les mmes types de paramtres. Le mme principe est applicable aux dnitions de classes et aux variables. Si vous disposez de deux (ou plus) entits (donnes ou mthodes) de mme nom, enC standard seule la plus locale est accessible. C++ permet de prciser de quel nom il est question grce la syntaxe o::nom (::nom signie que lon dsire accder lespace global).
namespace perso { void f(); } void f();

Tout ce qui est dni dans perso (donc entre les accolades) est diffrent de ce qui est dni ailleurs. lextrieur de perso, on peut nanmoins accder sa fonction f() grce au code perso::f(). Il est possible dutiliser une prfrence dappel grce la dclaration using. Ainsi, dans notre exemple prcdent, using perso; permet daccder toutes les composantes de perso (dans la porte de la dclaration, videmment). Utilisez-la au niveau global et vous obtiendrez une prfrence par dfaut pour le code qui la suit. Utilisez-la dans un bloc et la prfrence sera locale au bloc.
Info Vous pouvez imbriquer les espaces de noms, mais vous ne pouvez pas en crer dans une classe ou un bloc de code (cest-dire dans le corps dune fonction).

42

CHAPITRE 2 Bases du langage C++

Astuce Utilisez les espaces de noms anonymes pour limiter la porte de vos dclarations plutt que de les dclarer static. Par exemple: namespace A { namespace // anonyme car pas de nom { void fonction() { // } } fonction(); // OK: visible } fonction(); // ERREUR: nest plus visible La fonction() est visible dans la porte du namespace anonyme.

Incompatibilits avec le C
Pointeurs de type void (C90 et C99) En C, il est possible de raliser une conversion implicite dun type donn vers un pointeur gnrique de type void*, et vice versa.
void *ptr_void; int *ptr_int; // ptr_void = ptr_int; ptr_int = ptr_void; // en C++: gnre un message derreur

Incompatibilits avec le C

43

En C++, la conversion dun pointeur gnrique void* vers un pointeur dun type donn doit tre explicite de la manire suivante (voir la section Conversion avec reinterpret_cast):
ptr_int = reinterpret_cast<int*>(ptr_void);

Instruction goto (C90 et C99)


int i = 0; goto start: int j = 1; // ERREUR en C++ // start: //

En C++, linstruction goto ne peut pas tre utilise pour sauter une dclaration comportant une initialisation, sauf si le bloc qui contient cette dclaration est entirement saut. Type de caractres et surcharge (C90 et C99) Les types utiliss pour reprsenter les caractres ne sont pas les mmes en C et en C++. LeC utilise un entier (int) alors que le C++ utilise le type caractre (char). Cela peut avoir son importance si vous surchargez des fonctions (notamment de la bibliothqueC standard), comme dans cet exemple:
int putchar(int c); // prsent dans <stdio.h> int putchar(char c) // ma surcharge { printf(%c\n, c); }

44

CHAPITRE 2 Bases du langage C++

Dans ce cas, le code putchar(a); appellera la seconde fonction et non la premire. Initialisation de tableaux de caractres
char tableau[5] = 12345;

En C, il est possible dinitialiser un tableau de caractres avec une chane ayant la mme longueur, sans compter son zro (\0) terminal. En C++, une telle initialisation gnrera un message derreur. Type de retour des fonctions
fonction();

En C, omettre le type de retour dune fonction lors de sa dclaration quivaut implicitement un retour de type int. Ceci nest pas permis en C++. Type boolen
typedef int bool;

En C, il tait possible de dnir un type bool. En C++, ce nest pas permis et gnre un message derreur du type: error: redeclaration of C++ built-in type bool.

Lier du code C et C++


extern C

Pour grer la surcharge de fonctions (voir la section Surcharge), le compilateur gnre un symbole de noms

Lier du code C et C++

45

plus long que celui de la fonction elle-mme. Ce procd, appel name mangling, adjoint au nom de la fonction des informations permettant de connatre le nombre et le type de ses arguments. Cest ce nom long quutilise lditeur de liens. Les compilateurs C ne disposent pas de ce mcanisme de signature des fonctions. Et pour cause: il ny a pas de surcharge enC. Du coup, il est ncessaire dencadrer toutes les fonctionsC ainsi:
extern C { // dclarations de variables et fonctions C }

Vous pouvez galement faire prcder chaque dclaration par cette mme mention:
extern C void fonctionC(int); extern C double autreFonction(double);

Astuce Nhsitez pas dans vos chiers den-ttesC utiliser la macro __cplusplus qui nest prdnie quen C++. Vous rendrez ainsi ceux-ci portables. #ifdef __cpluplus extern C { #endif // vos dclarations C #if __cpluplus } #endif

46

CHAPITRE 2 Bases du langage C++

Embarquer une fonction


inline Type fonction(...) { ... }

Le langage C++ introduit le concept de fonctions embarques en ajoutant le mot-cl inline, qui permet de dnir des fonctions dont lappel dans le programme sera remplac par le code de la fonction elle-mme. Elles doivent tre rserves de prfrence aux petites fonctions (membres de classe ou globales) frquemment utilises.
inline int doubler(int i) { return 2 * i; } Attention Ce mot-cl est une indication donne au compilateur. Il ne garantit pas que le code sera effectivement embarqu.

Constantes usuelles
#include <limits> #include <climits> #include <coat>

Vous trouverez toutes les constantes usuelles dans ces trois chiers den-tte. Lexemple suivant montre comment connatre la valeur maximale du type double; il utilise <limits> :
std::numeric_limits<double>::max();

3
Pointeurs et rfrences
Un pointeur est simplement une variable contenant une adresse mmoire, indiquant o commence le stockage mmoire dune donne dun certain type. Une rfrence est un pointeur masqu, permettant de manipuler lobjet point par un pointeur comme sil sagissait dune variable ordinaire. Les pointeurs sont couramment utiliss en C et C++.
Attention Il est essentiel de bien comprendre les pointeurs et les rfrences. Une erreur de manipulation est vite arrive. Gardez en tte que la mmoire dun ordinateur nest quune suite ordonne de0 et de1, groups par blocs. Historiquement, il sagissait de bloc de 8bits, cest--dire un octet. Ces blocs sont implicitement numrots. Le numro dun bloc correspond son adresse. Larchitecture des ordinateurs ayant volu, certaines donnes doivent dbuter des adresses multiples de4 (sur une architecture 32bits) ou de8 (sur une architecture 64bits). Cest ce que lon appelle lalignement. Pour en savoir plus sur les problmes dalignement mmoire, vous pouvez vous rfrer la page web http://fr.wikipedia.org/ wiki/Alignement_de_donnes.

48

CHAPITRE 3 Pointeurs et rfrences

Adresses

Mmoire Adresses croissantes

Variables

23444 23443 23442 23441 23440 23439

45 461

i j

23443

pil

2 1 0

Figure3.1: Notions de pointeur et dadresse

Crer et initialiser un pointeur


Type* pointeur; *a

Pour connatre ladresse dune variable, on utilise une indirection avec loprateur&. Lorsque lon veut accder au contenu dune variable pointe, on utilise un drfrencement avec loprateur*. Le code suivant illustre ces manipulations:
int int pa *pa // dclaration dune variable, ici de type entier // dclaration dun pointeur sur entier // indirection: on rcupre ladresse de a // drfrencement: on utilise la variable // prcdemment drfrence *pa = *pa + 1; // encore: ici pour ajouter 1 au contenu // de la variable pointe (autrement dit a) a; *pa; = &i; = 0;

Accder aux donnes ou fonctions membres

49

Attention Le * dans la dclaration dun pointeur ne se rapporte qu la variable immdiatement sa droite. Ainsi: int *a, b; dclare a comme pointeur sur un entier MAISb comme un entier et NON un pointeur sur entier. Pour que a et b soient tous deux des pointeurs, il faut rpter le* devant chaque nom de variable (du moins celles que nous souhaitons tre des pointeurs), comme suit: int *a, *b;

Accder aux donnes ou fonctions membres


pointeur->membre pointeur->fonction() (*pointeur)->membre (*pointeur)->fonction()

Lorsque lon utilise des pointeurs sur des structures ou des classes, accder aux donnes ou fonctions membres se ralise de deux faons:
struct Copain { int m_age; }; Copain toto; Copain *pCopain = &toto; (*pCopain).age = 5; // 1re faon pCopain->age = 7; // 2e faon, plus pratique

50

CHAPITRE 3 Pointeurs et rfrences

Info Historiquement, la bibliothque standard du langageC avait introduit la constante NULL (par lintermdiaire dune macro). La valeur de cette dernire, bien que souvent0, pouvait valoir nimporte quelle valeur (certains compilateurs la dnissaient 1). Le C++ a uni cette valeur 0, facilitant ainsi le portage des programmes. Certains encouragent lutilisation systmatique de0, dautres celle de la macro NULL quitte la dnir soi-mme 0 si elle nexiste pas. Rjouissez-vous, la future norme C++0x (voir lannexe qui lui est consacre) rconciliera certainement tout le monde en introduisant le nouveau mot-cl nullptr.

Crer et utiliser une rfrence


Type& reference;

Les rfrences, ajoutes par le C++, masquent le systme dindirection et de drfrencement des pointeurs. Lexemple suivant montre quel point leur utilisation est simple:
int I; int &rI = I; // rfrence sur la variable I rI = rI + 1; // ajoute 1 rI et donc aussi a I

Dclarer un pointeur sur un tableau


type (*tableau)[N];

Ce code dclare un pointeur sur un tableau de Nlments.


Attention type* tableau[N]; dclare un tableau de Npointeurs.

Pointeurs et tableaux

51

Pointeurs et tableaux
Diffrence de type

Du point de vue du compilateur, il existe une diffrence entre: T var[] qui dnit une variable de type tableau de type T et; T* var qui dnit une variable de type pointeur sur type T. Ainsi, si vous dnissez par exemple char a[6] dans un chier source, pour le rendre public et accessible par dautres chiers sources, vous devez le dclarer (par exemple dans un chier den-tte) comme extern char a[6] ou extern char a[] et non extern char *a.
Diffrence mmoire

On entend souvent dire que T a[] et T* a sont quivalents. Cela est vrai lors de leur passage en tant que paramtre de fonction. Cela est faux vis--vis de la structure mmoire lors de la dclaration. Un exemple valant mieux que mille mots, considrons les deux dclarations suivantes:
char a[] = bonjour; char *p = le monde;

Les donnes correspondantes en mmoire peuvent tre reprsentes ainsi:


+---+---+---+---+---+---+---+----+ a : | b | o | n | j | o | u | r | \0 | +---+---+---+---+---+---+---+----+ +-----+ +---+---+---+---+---+---+---+---+----+ p : | *=====> | l | e | | m | o | n | d | e | \0 | +-----+ +---+---+---+---+---+---+---+---+----+

52

CHAPITRE 3 Pointeurs et rfrences

Il est important de raliser cette diffrence pour comprendre que le code gnr par la suite peut inuer sur les performances. En effet, dans le deuxime cas une opration sur larithmtique des pointeurs se cache derrire linstruction p[3].
quivalence lors de laccs

Mme sil existe une diffrence subtile entre tableauC et pointeur, tout accs un lment de tableau a un quivalent avec un appel par pointeur. Par exemple int t[5]; rserve 5entiers conscutifs en mmoire, et tcorrespond un pointeur sur le dbut de cette mmoire. Ainsi, si int* pi = t; alors *(pi+3) ou pi[3] est quivalent t[3]. Pour les tableaux multidimensionnels, les choses sont un peu plus complexes. Tout dabord, il faut comprendre comment un tableau multidimensionnel est organis en mmoire. Prenons le cas dun tableau deux dimensions. Par exemple T mat[4][3] peut tre reprsent ainsi:
+-----+-----+-----+-----+-- ---+-----+ | a32 | mat == | a00 | a01 | a02 | a11 | +-----+-----+-----+-----+-- ---|-----+ +-----+-----+-----+ mat == mat[0] ---> | a00 | a01 | a02 | +-----+-----+-----+ mat[1] ---> | a10 | a11 | a12 | +-----+-----+-----+ mat[2] ---> | a20 | a21 | a22 | +-----+-----+-----+ mat[3] ---> | a30 | a31 | a32 | +-----+-----+-----+

Pointeurs et tableaux

53

Le tableau dlments est stock en mmoire ligne aprs ligne; pour un tableau de taille mn dlments de type T, ladresse de llment (i,j) peut sobtenir ainsi:
adresse(mat[i][j]) == adresse(mat[0][0]) + (i*n +j) * size(T) adresse(mat[i][j]) == adresse(mat[0][0]) + i * n * size(T) + j * size(T) adresse(mat[i][j]) == adresse(mat[0][0]) + i * size(une ligne de T) + j * size(T)

Dune manire gnrale si on a T tableau[D0][D1][D2] [DN], on peut accder llment tableau[i0][i1][i2] [iN] par la formule suivante:
*(tableau + i0 + D1* (i1 + (D2* (i2 + D3* ( + DN*iN))))

Cette formule omet la taille dun lment car le compilateur la dduit automatiquement en fonction du type du pointeur. Du coup, prenez garde au fait que si le type de pointeur est diffrent, ladresse obtenue nest pas la mme. Si lon a:
int *pi = 0; char* pc = 0;

alors (pi + 1)!= (pc + 1).


quivalence en tant que paramtre

Lors du passage dun tableau une fonction, seul le pointeur sur le dbut du tableau est transmis. Du coup, on a une quivalence stricte entre les deux reprsentations. Par contre, sil sagit dun tableau multidimensionnel, on a tout intrt garder la dclaration sous forme de tableau pour viter de se tromper dans le calcul de la conversion des indices.

54

CHAPITRE 3 Pointeurs et rfrences

Dclarer un pointeur sur une fonction


Type (*pointeur_sur_fonction)(paramtres); typedef Type (*type_pointeur_sur_ fonction)(paramtres); type_pointeur_sur_fonction pointeur_sur_fonction;

Les parenthses autour de *pointeur_sur_fonction sont obligatoires, sinon le compilateur pensera quil sagit de la dclaration dune fonction renvoyant un pointeur sur le Type donn. Lexemple suivant montre comment utiliser les pointeurs sur fonction et donne un aperu de leur intrt. Cet exemple est crit enC, mais fonctionne parfaitement en C++.
#include <stdio.h> #include <string.h> #dene MAX_BUF 256 long arr[10] = { 3,6,1,2,3,8,4,1,7,2}; char arr2[5][20] = { Mickey Mouse, Donald Duck, Minnie Mouse, Goofy, Ted Jensen }; // Dclaration du type de la fonction de comparaison // utilis par le tri bulle typedef int (*FnComparaison)(const void *, const void *); // Fonction de tri bulle gnrique void tri_bulle(void *p, int width, int N, FnComparaison fptr);

Dclarer un pointeur sur une fonction

55

// Deux fonctions de comparaison int compare_chaine_c(const void *m, const void *n); int compare_long(const void *m, const void *n); int main(void) { int i; puts(\nAvant le tri :\n); for (i = 0; i < 10; i++) // Afche les ints de arr printf(%ld ,arr[i]); puts(\n); for (i = 0; i < 5; i++) // Afche les chanes de arr2 printf(%s\n, arr2[i]); tri_bulle(arr, 4, 10, compare_long); // Trie les longs tri_bulle(arr2, 20,5, compare_chaine_c); // Trie les chanes puts(\n\nAprs le tri :\n); for (i = 0; i < 10; i++) // Afche les longs tris printf(%d ,arr[i]); puts(\n); for (i = 0; i < 5; i++) // Afche les chanes tries printf(%s\n, arr2[i]); return 0; } // Implmentation de la fonction de tri bulle gnrique void tri_bulle(void *p, int width, int N, FnComparaison fptr) { int i, j, k; unsigned char buf[MAX_BUF]; unsigned char *bp = (unsigned char*)p; for (i = N-1; i >= 0; i--)

56

CHAPITRE 3 Pointeurs et rfrences

{ for (j = 1; j <= i; j++) { k = fptr((void *)(bp + width*(j-1)), (void *)(bp + j*width)); if (k > 0) { memcpy(buf, bp + width*(j-1), width); memcpy(bp+width*(j-1), bp+j*width, width); memcpy(bp+j*width, buf, width); } } } } int compare_chaine_c(const void *m, const void *n) { char *m1 = (char *)m; char *n1 = (char *)n; return (strcmp(m1,n1)); } int compare_long(const void *m, const void *n) { long *m1, *n1; m1 = (long *)m; n1 = (long *)n; return (*m1 > *n1); }

Dans cet exemple, la fonction tri_bulle est gnralise en passant en paramtre la mthode permettant de comparer les valeurs contenues dans le tableau. Avec compare_long, on considre que le tableau contient des longs; avec compare_ chaine_c on considre que le tableau contient des pointeurs sur des chaines de typeC. Dans tous les cas, il est ncessaire que la taille dun lment du tableau soit gale la taille dun pointeur. Il est impossible dutiliser ce tri_bulle avec un tableau de caracres ASCII.

Dclarer un pointeur sur une fonction

57

Passer un objet en paramtre par pointeur/rfrence


Type1 fonction(, Type2* ptr, ); // par pointeur Type1 fonction(, Type2& ref, ); // par rfrence

Passer un paramtre par pointeur ou par rfrence vite de copier des objets complexes lors de leur passage des fonctions. Cela permet dconomiser de la mmoire et du temps.
Attention Il faut absolument viter de recopier un objet lourd (du fait de sa taille ou du temps que prendrait sa recopie) si cela nest pas ncessaire. Il faut tre conscient que cela se fait de manire implicite lorsquon le transmet une fonction (passage de paramtre). Toutefois, il peut exister un intrt crer une copie temporaire. On peut ainsi la modier souhait, sans toucher loriginal.

Lexemple 1 passe un paramtre par recopie.


MaClasse A; // void fonction(MaClasse B) { // B est une copie de A B.methode(); // B est dtruit la n de la fonction } // fonction(A);

58

CHAPITRE 3 Pointeurs et rfrences

Lexemple2 utilise un paramtre par rfrence. On ne recopie plus lobjet, par contre une modication deB entrane une modication deA. Il est possible de dclarer un paramtre constant ( laide du mot-cl const); dans ce cas, tout appel une fonction membre non const ou toute tentative de modication dune variable membre de lobjet ( condition quelle ne soit pas qualie de mutable) aboutira une erreur de compilation.
MaClasse A; // void fonction(MaClasse& B) { B.methode(); } void fonction(const MaClasse& B) { B.methode(); // erreur si MaClasse::methode non const B.var = 0; // erreur Type v = B.var; // ok }

Enn, lexemple3 illustre un passage de paramtre par pointeur. Toute modication deB entrane une modication deA.
MaClasse* A = new MaClasse(); void fonction(MaClasse* B) { B->methode(); }

Info La signature de ces trois fonctions est identique: void f(int a[10]); void f(int* a); void f(int a[]);

4
Classes et objets
La programmation oriente objet (POO), ou programmation par objet, est un paradigme de programmation informatique qui consiste en la dnition et lassemblage de briques logicielles appeles objets; un objet reprsente un concept, une ide ou toute entit du monde physique: voiture, personne ou encore page dun livre. La programmation oriente objet utilise des techniques comme lencapsulation, la modularit, le polymorphisme et lhritage. Ce chapitre montre comment le langage C++ les met en uvre.
Attention Ne perdez jamais de vue que le C++ est un langage orient objet. Beaucoup semblent loublier lorsquils dcouvrent les possibilits orientes objet du C++. Ils semballent et, sous prtexte de faire de lobjet, dnissent des mthodes et encore des mthodes Cest plus un dfaut quune bonne pratique. Il est facile dinclure dans ses classes des choses qui nont rien y faire. Pour vous aider, posez-vous cette question: la fonctionnalit que jcris fait-elle partie de lobjet ou bien agit-elle dessus? En dautres termes: ne ngligez pas lutilit et le bienfond des fonctions.

60

CHAPITRE 4 Classes et objets

Ajouter des donnes des objets


class MaClasse { type donnee; };

Ajouter une donne un objet se fait de la mme manire quenC avec les structures struct. Lexemple suivant permet de dnir lge dune personne en ajoutant la donne membre m_age lobjet Personne.
class Personne { int m_age; };
Nom de la classe Champs (donnes) Objet donne1 donne2 ...

Figure4.1: Reprsentation UML des donnes dun objet

On distingue deux types de donnes: Celles que lobjet possde. Cest la composition. Dans ce cas, la donne nat et meurt avec lobjet.
struct Voiture { Carburateur m_carbu; };

Exemple de composition (trivial)

Ajouter des donnes des objets

61

class Voiture { Carburateur* m_carbu; public: Voiture() { m_carbu = new Carburateur; } ~Voiture() { delete m_carbu; } };

Exemple de composition (avec pointeur)

Nom de la classe Champs (donnes)

Objet Objet1 donne1 Objet2 donne2 ... Objet2 ... ... Objet1 ... ...

Figure4.2: Reprsentation UML dune composition

Celles qui sont des liens vers un autre objet. C'est lagrgation. Dans ce cas, la dure de vie de lobjet et de la donne lie sont indpendantes. Dans lexemple suivant, la dure de vie du carburateur ne dpend pas de celui dune voiture.
{ Carburateur* m_carbu; public: Voiture() { m_carbu = 0; } ~Voiture() {} void set_carburateur(Carburateur* c) { m_carbu = c; } };

Exemple dagrgation

62

CHAPITRE 4 Classes et objets

Nom de la classe Champs (donnes)

Objet Objet1 donne1 Objet2 donne2 ... Objet2 ... ... Objet1 ... ...

Figure4.3: Reprsentation UML dune agrgation

Lier des fonctions des objets


class MaClasse { type fonction_membre(arguments); };

Pour lier une fonction un objet, ou plus exactement, une instance dobjet, vous devez utiliser une fonction membre. Vous pouvez lappeler avec loprateur. (point) ou-> (che) comme indiqu ci-aprs, selon quil sagit dune instance de lobjet considr ou dun pointeur sur celle-ci.
Type instance, *instance_ptr; instance.fonction_membre(arguments); instance_ptr->fonction_membre(arguments); Attention La validit du pointeur sur une instance dune classe est sous la responsabilit du programmeur. Un appel de fonction membre avec un pointeur invalide peut provoquer un comportement inattendu, mais pas forcment un plantage du programme. Un appel avec un pointeur nul provoque le plus souvent un plantage direct et plus facile dtecter.

Lier des fonctions des objets

63

Nom de la classe Champs (donnes)

Objet donne1 donne2 ... fonction1() fonction2(arguments) ...

Champs (fonctions)

Figure4.4: Reprsentation UML des fonctions dun objet

Lexemple ci-aprs cre une simple classe reprsentant une personne avec son ge et son nom (reportez-vous au Chapitre10 pour en savoir plus sur le type chane de caractres utilis pour cette variable).
class Personne { std::string m_prenom, m_nom; int m_age; public: // int setNom(const std::string& n); // int getAge() const { return m_age; } void setAge(int a) const { m_age = a; } // void afche(std::ostream& o) const { o << Nom : << m_nom << std::endl << Prenom: << m_prenom << std::endl << Age : << m_age << std::endl; } }; // dans le chier source void Personne::setNom(const std::string& n) { m_nom = n; }

64

CHAPITRE 4 Classes et objets

Voici maintenant un exemple dutilisation de cette classe:


Personne moi; moi.setAge( jeux_devinez_le() ); moi.afche( std::cout );

Dterminer la visibilit de fonctions ou de donnes membres


class Classe { public: // protected: // private: // };

Les fonctions et donnes membres public sont visibles (ou accessibles) par nimporte quelle partie du programme utilisant la classe. Elles fournissent linterface publique de la classe. Les fonctions et donnes membres private ne sont visibles que par les fonctions membres de la classe. Elles sont utilises pour cacher les dtails dimplmentation. Les fonctions et donnes membres protected sont visibles par les fonctions membres de la classe ou de ses classes lles directes (par hritage). Elles ne sont pas visibles du reste du programme.

Dterminer la visibilit de fonctions ou de donnes membres

65

Info Les class sont private par dfaut (tant pour lhritage que pour les membres prcdant la premire spcication de visibilit), alors que les struct sont public par dfaut. class MaClasse: /* private */ ClasseMere { }; struct MaClasse: /* public */ ClasseMere { };

Il est possible de reprsenter sous forme dune hirarchie de classes, parfois appele arborescence de classes, la relation de parent qui existe entre les diffrentes classes. Larborescence commence par une classe gnrale appele superclasse (parfois classe de base, classe parent, classe anctre, classe mre ou classe pre, les mtaphores gnalogiques sont nombreuses). Puis les classes drives (classe lle ou sous-classe) deviennent de plus en plus spcialises. Ainsi, on peut gnralement exprimer la relation qui lie une classe lle sa mre par la phrase est un (de langlais is a).
Nom de la classe Champs (donnes) Objet + donne1 public protg # donne2 - donne3 priv + fonction1() public protg # fonction2(arguments) - fonction3(arguments) priv

Champs (fonctions)

Figure4.5: Reprsentation UML de la visibilit des membres

66

CHAPITRE 4 Classes et objets

Astuce Grce au mot-cl friend (ami), il est possible de rendre accessible les donnes et fonctions prives dune classe : une fonction particulire, dnie nimporte o dans le programme; une classe extrieure dtermine; une fonction particulire dune classe extrieure dtermine. Le mot-cl friend est par exemple ncessaire lorsque lon veut implmenter des oprateurs globaux qui utilisent des lments de ladite classe. Pour donner laccs une fonction, vous pouvez procder comme suit: class X { int a; friend void f ( X* ); }; void f (X * p) { p->a = 0; } void main () { X * ptr = new X; f (ptr); } Pour donner laccs une classe, vous pouvez procder comme suit : class B; class A { private: int a; f(); friend B; };

Expliciter une instance avec le pointeur this

67

class B { void h (A*p) { p->a = 0; p->f(); } }; Pour donner laccs une fonction membre dune autre classe, vous pouvez procder comme suit: class B; class A { private: int a; friend void B::f(); }; class B { void f(A * p) { p->a = o; } };

Expliciter une instance avec le pointeur this


type MaClasse::fonction_membre(arguments) { this->variable_membre = ; return valeur; }

Le pointeur this correspond ladresse mmoire de linstance ayant servi pour lappel de la fonction membre. Son utilisation nest pas obligatoire, sauf pour lever une ambigut (par exemple lorsquune variable membre de la

68

CHAPITRE 4 Classes et objets

classe porte le mme nom quun argument de la fonction membre). Le pointeur this nest disponible que dans limplmentation dune fonction membre.
Astuce Lutilisation systmatique de this pour accder des membres de la classe se rvle tre une bonne pratique lusage. Elle rend vidente laccs ces derniers; la lourdeur apparente de cette syntaxe a pour contrepartie une comprhension accrue, facilite, du code.

Dnir un constructeur/ destructeur


class Objet { public: Objet(); Objet(const Objet&); Objet(paramtres); ~Objet(); }; // // // // constructeur par dfaut constructeur par copie constructeur destructeur

Les constructeurs et le destructeur dun objet ressemblent, quelques dtails prs, des fonctions membres: ils nont pas de type de retour; ils portent le mme nom que lobjet. Le destructeur possde en plus les caractristiques suivantes: son nom commence par un ~ (tilde); il na pas dargument.

Dnir un constructeur/destructeur

69

Pour toute instanciation de classe, un constructeur est obli gatoirement appel. Si aucun constructeur nexiste, le compilateur en gnre un automatiquement. Ce dernier consiste appeler le crateur par dfaut pour chacun des membres de la classe, lorsquil existe ou quil peut tre gnr.
Attention Lhritage est cens garantir lordre dappel des destructeurs. Par exemple, si Chrite deB qui hrite elle-mme deA, lors de la destruction de dune instance deC les destructeurs doivent tre appels dans lordre inverse,soit: C::~C, B::~B puis A::~A. Pourtant, ce nest pas toujours le cas, notamment avec certains vieux compilateurs. Prenez donc lhabitude dtre prudent et partez du postulat que ce nest gnralement pas le cas.

Ajoutons maintenant un constructeur et un destructeur notre classe Personne.


class Personne { // public: Personne(std::string prenom, std::string nom, int age=0) : m_prenom(prenom), m_nom(nom), m_age(age) { std::cout << m_prenom << << m_nom << vient de natre\n; } ~Personne() { std::cout << m_prenom << << m_nom << vient de mourir\n; } // };

70

CHAPITRE 4 Classes et objets

Et utilisons-le:
// { Personne reMoi(Vincent, Gouvernelle); // } //

Lors de lexcution du code prcdent, nous obtiendrons cet afchage sur la console:
Vincent Gouvernelle vient de natre Vincent Gouvernelle vient de mourir

La premire ligne apparat lors de linstanciation de la classe. La deuxime apparat lors de la destruction de celle-ci la n du bloc.

Empcher le compilateur de convertir une donne en une autre


class Objet { public: explicit Objet(paramtres); }; // constructeur

La surcharge de fonction implique de manire sousjacente des tentatives de conversion des paramtres. Cela peut tre indsirable, voire dangereux. Pour interdire au

Empcher le compilateur de convertir une donne en une autre

71

compilateur de tenter de convertir une donne en une autre par lintermdiaire dun constructeur, vous avez la possibilit de rendre ce dernier explicit.
struct Age { int valeur; Age(int a): valeur(a) {} }; void fonction(Age a); // fonction(34); // OK: 34 implicitement converti en Age(34) struct Age { int valeur; explicit Age(int a): valeur(a) {} }; void fonction(Age a); // fonction(34); // Erreur fonction(Age(34)); // OK: lutilisateur rend explicite sa volont

Astuce Le mot-cl explicit permet galement de simuler avec le C++, dans une certaine mesure, un langage typage fort comme Pascal ou Ada.

72

CHAPITRE 4 Classes et objets

Spcier quune fonction membre ne modie pas lobjet li


class Objet { // type fonction(arguments) const; // mutable type variable; };

Une fonction spcie comme const ne peut ni appeler dautres fonctions membres non const, ni modier ses variables membres (sauf si elles sont mutable). Mentionnez ds que possible ce spcicateur, car un objet pass en rfrence constante (const type &) ne peut appeler que des fonctions membres const.
Info Nabusez pas du spcicateur mutable pour dtourner const. Dune manire gnrale, cela traduit un dfaut de conception de votre architecture logicielle.

Rendre une fonction/donne membre indpendante de lobjet li

73

Rendre une fonction/donne membre indpendante de lobjet li


class Objet { static type variable; public: static type fonction(arguments); };

Les membres static dune classe ne sont pas lis une instance de cette dernire. Ils agissent comme des variables globales ou des fonctions traditionnelles, mais sont conceptuellement lis la classe. Pour illustrer ceci, examinons rapidement une implmentation simple du pattern Singleton. Lobjet du singleton est de restreindre linstanciation dune classe un seul objet (ou bien quelques objets seulement). Il est utilis par exemple lorsque lon a besoin dexactement un objet pour coordonner des oprations dans un systme.
class Singleton { static Singleton* unique_instance; int status; Singleton(); public: static Singleton* get_instance() const; // Autres dclarations int get_status() const; };

Pattern Singleton (dclaration)

74

CHAPITRE 4 Classes et objets

Singleton* Singleton::unique_instance = 0; static Singleton* Singleton::get_instance() const { if (unique_instance == 0) unique_instance = new Singleton; return unique_instance; } int Singleton::get_status() const { return status; }

Pattern Singleton (implmentation)


int s = Singleton::get_instance()->get_status();

Pattern Singleton (utilisation)

Comprendre le changement de visibilit lors de lhritage


class Fille: public Parente { }; class Fille: private Parente { }; class Fille: protected Parente { };

Info Lhritage est une notion essentielle dans la programmation objet. Lhritage consiste runir les dnitions dune (hritage simple) ou plusieurs classes (hritage multiple) et de leur adjoindre un ensemble de membres spciques. Cette nouvelle classe est communment appele classe drive.

Comprendre le changement de visibilit lors de lhritage

75

classe mre

ObjetA

classe drive

ObjetB

Figure4.6: Reprsentation UML de lhritage

La drivation peut tre de trois types: public, protected (protg) ou private (priv). Selon le type de drivation, la visibilit des membres de la classe parente change. Le tableau suivant rsume les diffrents cas.
Visibilit des membres parents en fonction du type de drivation
Classe parente Hritage priv inaccessible prive protge publique inaccessible inaccessible prive prive Hritage protg Hritage public inaccessible inaccessible protg protg inaccessible inaccessible protg public

Pour bien comprendre cela, considrons la classe mre suivante:


class A { public: void func_publique() {} protected: void func_protegee() {} private: void func_privee() {} };

76

CHAPITRE 4 Classes et objets

Lhritage public engendre les visibilits suivantes:


class B : public A { public: void test_call() { A::func_publique(); // vue comme publique A::func_protegee(); // vue comme protge //A::func_privee(); // erreur : est inaccessible } }; void B_test_use() { B b; b.func_publique(); // vue comme publique //b.func_protegee(); // vue comme protge //b.func_privee(); // est inaccessible }

Lhritage protg engendre les visibilits suivantes:


class C : protected A { public: void test_call() { A::func_publique(); // vue comme protge A::func_protegee(); // vue comme protge //A::func_privee(); // est inaccessible } }; void C_test_use() { C c; //c.func_publique(); // vue comme protge //c.func_protegee(); // vue comme protge //c.func_privee(); // est inaccessible }

Comprendre le changement de visibilit lors de lhritage

77

Lhritage priv engendre les visibilits suivantes:


class D : private A { public: void test_call() { A::func_publique(); // vue comme prive A::func_protegee(); // vue comme prive //A::func_privee(); // est inaccessible } }; void D_test_use() { D d; //d.func_publique(); // vue comme prive //d.func_protegee(); // vue comme prive //d.func_privee(); // est inaccessible }

tendons maintenant notre class Personne pour en faire un employ. Un employ reoit un salaire et peut pointer le matin et le soir.
class Employe: public Personne { double m_salaire; public: Employe(): Personne(,), m_salaire(0) {} void pointer_matin(Heure) { } void pointer_soir(Heure) { } };

78

CHAPITRE 4 Classes et objets

Comprendre les subtilits de lhritage multiple


class Fille: derivation Parent1, derivation
Parent2, { };

Comme nous lavons dit la section Comprendre le changement de visibilit lors de lhritage, lhritage peut tre multiple. Il en rsulte certaines subtilits quil est essentiel de connatre pour matriser cette technique. Pour utiliser sainement lhritage multiple, il vaut mieux le voir comme un moyen dajouter une classe lle un ensemble de mthodes et dattributs de telle sorte que cela ne remette pas en cause ce dont elle hritait dj par ailleurs.
CL

Figure4.7: Reprsentation UML de lhritage multiple.

Le premier problme de lhritage multiple est le clonage de donne, comme le montre lexemple suivant.
class CL { }; class A: public class B: public class M: public { M(): A(), }; CL { }; CL { }; A, public B B() { }

Empcher la duplication de donnes avec lhritage virtuel

79

Dans ce cas, la classe CL est duplique en mmoire. Si lon veut accder lune ou lautre des donnes dune instance de CL, il est alors ncessaire de prciser par quelle branche dhritage il faut passer. Cela ce fait grce un transtypage multiple. Dans le cadre de notre exemple, on a les deux possibilits suivantes: CL* clPtr = (CL*) (A*) mPtr; CL* clPtr = (CL*) (B*) mPtr.

Empcher la duplication de donnes avec lhritage virtuel


class Fille: type_de_derivation virtual Parent1,
{ };

Dans certains cas, lhritage multiple engendre la duplication de classes en mmoire. Cest le cas lorsquen remontant dans lhritage, un ou plusieurs types de classe apparaissent plusieurs fois. Le mot-cl virtual doit tre rpt si ncessaire, comme pour le type de drivation. Lexemple ci-aprs montre comment empcher cette duplication mmoire, grce lhritage virtuel.
class U { }; class A: public class B: public class M: public { M(): U(), }; virtual U { }; virtual U { }; A, public B A(), B() { }

80

CHAPITRE 4 Classes et objets

Comme nous venons de le dire, le principe de lhritage virtuel est dviter la duplication des donnes dune classe parente apparaissant plusieurs fois dans la hirarchie de lhritage. Cela est pratique mais implique de devoir prciser la construction de toutes les classes dont on hrite virtuellement, et ce chaque nouvel hritage. Cest pourquoi, bien souvent, les programmeurs C++ se limitent lhritage simple autant que possible. Ils nutilisent lhritage multiple quavec parcimonie, en prenant soin dviter les cas o une classe apparat plusieurs fois dans une hirarchie. Lhritage virtuel nest en effet que trs peu utilis, car dans une hirarchie complexe, il devient vite trs complexe. Une mthode virtuelle de la classe communeU peut tre rednie dansA ouB. Trois cas se prsentent alors: si elle nest rednie ni dans A ni dansB, alors celle de U sera utilise; si elle est rednie seulement dans A ouB, alors cette dernire sera utilise et pas celle deU; si elle est rednie dansA et dansB, alors le compilateur ne peut choisir. Bien entendu, si elle est rednie aussi dansM, la rednition deM aura priorit sur les autres.

Simuler un constructeur virtuel


class Objet { public: virtual ~Objet() {} virtual Objet* clone() = 0; virtual Objet* create() = 0;

Simuler un constructeur virtuel

81

}; class Type : public Objet { public: virtual Type* clone() { return new Type(*this); } virtual Type* create() { return new Type(); } };

Le langage C++ ne supporte pas directement les constructeurs virtuels. Il est nanmoins possible de les simuler par un idiome. Il est ainsi possible dobtenir le mme effet quun constructeur virtuel grce lutilisation dune fonction membre virtuelle clone() pour le constructeur par copie, ou dune fonction membre virtuelle create() pour le constructeur par dfaut.Vous pouvez bien sr les appeler comme bon vous semble, mais les noms employs ici sont ceux que lon retrouve traditionnellement dans une telle situation. Le principe est simple. La fonction membre clone() appelle le constructeur par copie, ou tout autre code plus complexe, dans le but de copier ltat de linstance actuelle dans un nouvel objet de mme type: cest un clone. La fonction membre create() appelle simplement le constructeur par dfaut de la classe concerne: elle cre une nouvelle instance neuve de mme type.
void fonction(Objet& objet) { Objet* obj1 = objet.clone(); Objet* obj2 = objet.create(); // delete obj1; // vous comprenez ici la ncessit // du destructeur virtuel delete obj2; }

82

CHAPITRE 4 Classes et objets

Le code ci-avant fonctionne quel que soit le sous-type dobjet utilis. Il permet de crer une copie ou une nouvelle instance de mme type que lobjet fourni en paramtre sans le connatre lavance.
Info Le type de retour des fonctions membres clone() ou create() est intentionnellement diffrent du type de retour de la classe de base. Ce mcanisme sappelle covariant return type ou, en franais, type de retour covariant. Il nest pas support par tous les compilateurs, surtout sils ne sont pas rcents. Si cest le cas du vtre, vous naurez pas dautres moyens que de conserver le type de retour de la classe de base, Objet* dans notre cas.

Crer un type abstrait laide du polymorphisme


class Classe { virtual Type Nom(arguments) = 0; virtual Type Nom(arguments) const = 0; };

Une classe devient abstraite partir du moment o elle contient au moins une fonction virtuelle pure. Il devient dans ce cas impossible de linstancier. On appelle parfois les classes abstraites des ADT (abstract data types) ou, en franais, types de donnes abstraits. Lide est dimposer aux concepteurs des classes drives de rednir et dimpl menter ces fonctions. Il est ainsi possible dutiliser ces fonctionnalits avant mme leur implmentation.

Crer un type abstrait laide du polymorphisme

83

class Forme { public: virtual oat Aire() = 0; }; class Carre : public Forme { public: virtual oat Aire() { return m_cote * m_cote; } private: oat m_cote; }; class Cercle : public Forme { public: virtual oat Aire() { return 3.1415926535* m_rayon*m_rayon; } private: oat m_rayon; };

Grce aux fonctions virtuelles, on peut crer un algorithme en nutilisant que la classe de base qui va automatiquement appeler les fonctions des classes drives. En proposant dutiliser un mme nom de mthode pour plusieurs types dobjets diffrents, le polymorphisme permet une programmation beaucoup plus gnrique. Le dveloppeur na pas savoir, lorsquil programme une mthode, le type prcis de lobjet sur lequel la mthode va sappliquer. Il lui suft de savoir que cet objet implmentera la mthode.

84

CHAPITRE 4 Classes et objets

Ainsi, un logiciel de calcul dintrt pour des comptes bancaires se prsenterait de la faon suivante (en pseudocode) dans le cadre dune programmation classique:
si type de <MonCompteBancaire> est un: PEA => MonCompteBancaire->calculeInteretPEA() PEL => MonCompteBancaire->calculeInteretPEL() LivretA => MonCompteBancaire->calculeInteretLivretA() n du choix

Si un nouveau type de compte bancaire PERP apparat (et avec lui un nouveau calcul), il sera ncessaire dune part dcrire la nouvelle mthode calculeInteretPERP(), mais aussi de modier tous les appels du calcul donn ci-dessus. Dans le meilleur des cas, celui-ci sera isol et mutualis de sorte quune seule modication sera ncessaire. Dans le pire des cas, il peut y avoir des centaines dappels modier. Avec le polymorphisme, toutes les mthodes porteront le mme nom, par exemple calculeInteret(), mais auront des implmentations diffrentes: une par type de compte. Lappel sera de la forme :
MonCompteBancaire->calculeInteret()

Lors de larrive du nouveau compte, aucune modication de ce code ne sera ncessaire. Le choix de la mthode relle utiliser sera fait automatiquement lexcution par le langage, alors que dans le cas prcdent cest le dveloppeur qui devait programmer ce choix.
Info Lorsquune classe ne contient que des fonctions membres virtuelles pures, on parle alors de classe interface. Dans certains langages, comme Java, cette notion est directement dnie.

Utiliser lencapsulation pour scuriser un objet

85

Utiliser lencapsulation pour scuriser un objet


class MaClasse { // private: // ou protected: Type mon_objet_encapsule; Type* mon_objet_encapsule2; // public: Type2 methode(); };

Lencapsulation consiste masquer le contenu dun objet et ne mettre disposition que des mthodes permettant de manipuler cet objet. En forant lutilisateur de la classe utiliser des fonctions membres (plutt que directement les donnes), lencapsulation permet dassurer la cohsion interne dun objet. Lobjet peut tre ainsi vu comme une bote noire, laquelle sont associs des proprits et/ou des comportements. Limplmentation est cache et peut tre change sans impact sur le reste du code, condition bien sr que le changement dimplmentation ne change pas le comportement de lobjet. Lencapsulation permet donc de sparer la spcication, ou dnition, dun objet de son implmentation. En troisime lieu, lencapsulation peut permettre de cacher totalement la manire dont un objet est implment. Cela peut savrer particulirement utile si vous voulez crer une bibliothque sans laisser ltrer vos secrets de fabrication.

86

CHAPITRE 4 Classes et objets

Lexemple suivant vous montre comment:


// Fichier en-tte class ClassePrivee; class ClasseVendue { ClassPrivee *implementation; public: Type methode(); }; // Fichier source class ClassePrivee { // donnes public: Type methode(); }; Type ClasseVendue::methode() { implementation->methode(); }

Obtenir des informations de types dynamiquement


#include <typeinfo> class type_info { public: virtual ~type_info(); bool operator==(const type_info&) const; bool operator!=(const type_info&) const; int before(const type_info&) const; const char* name() const; }; class bad_cast : public exception; class bad_typeid : public exception; type_info typeid();

Obtenir des informations de types dynamiquement

87

Nous avons dj parl de dynamic_cast<> dans la section sur les conversions. Le C++ dnit galement la fonction typeid(). Elle permet dobtenir en temps constant, cest-dire indpendamment de la taille ou de la complexit de lobjet , des informations sur une classe ou une instance de celle-ci grce aux informations dynamiques de type (de langlais runtime type information, RTTI). Cette classe diffre souvent selon les compilateurs, mais fort heureusement une trame commune se retrouve dans ces diverses implmentations. Tout dabord, il faut savoir quil nest pas possible de crer soi-mme des instances de classe type_info. La seule manire est de passer par la fonction typeid(), disponible travers les chiers den-tte typeinfo. Il est galement impossible de copier ou daffecter des objets de type type_info. Les oprateurs == et != permettent de tester lgalit et lingalit de type travers les objets type_info. Contraire ment un dynamic_cast, qui effectue un test du type est compatible, ces oprateurs effectuent bien un test du type est un. Cela na pas la mme vocation mais est trs utile dans certains cas. Il ny a aucun lien entre lordre de collecte des types et les relations dhritage. Vous pouvez utiliser la fonction membre before() pour dterminer lordre de collecte des types. Il ny a aucune garantie que cette fonction retourne le mme rsultat selon le programme, voire mme selon lexcution dun mme programme. Sous ce rapport, cette fonction a le mme genre de comportement que loprateur&. La fonction membre name() renvoie le nom du type de lobjet. Parfois, ce nom est partiellement dcor; cela dpend principalement du compilateur utilis. Je vous dconseille donc fortement dutiliser cette valeur comme appui dans vos programmes.

88

CHAPITRE 4 Classes et objets

Info Le type de retour de la fonction membre before() peut varier selon les compilateurs. Par exemple, Visual Studio 2008 retourne un int, alors que g++ 3.4.2 renvoie un bool.

Le chier den-tte typeinfo fournit galement la dnition de deux exceptions bad_cast et bad_typeid. La premire peut tre leve lors dun dynamic_cast invalide. La deuxime le sera lors dun appel typeid() sur une expression invalide, telle un pointeur nul.

Transformer un objet en fonction


struct { type operator()(arguments) const; };

Les foncteurs sont une abstraction des fonctions sous forme dobjet-fonction laide de loprateur parenthses. Les foncteurs sont largement utiliss par la bibliothque standard STL. quoi bon les foncteurs puisquon dispose des fonctions? Ils sont en fait un peu plus puissants que les fonctions. Ilspermettent par exemple dembarquer des paramtres supplmentaires en plus du code de la fonction elle-mme. Ils sont aussi utilisables comme paramtre de patron dalgo rithme (algorithm templates), le plus souvent sous forme dobjet temporaire. Lexemple ci-aprs illustre laddition lment par lment du contenu de deux tableauxa etb. Le rsultat est stock dans le premier tableau. Les deux tableaux contiennent des double et sont de mme taille.

Transformer un objet en fonction

89

std::transform(a.begin(), a.end(), b.begin(), a.begin(), std::plus<double>());

Cet autre exemple montre comment inverser (x = -x) chaque lment:


std::transform(a.begin(), a.end(), a.begin(), std::negate<double>());

Les fonctions daddition et de ngation sont inlines implicitement. Ce qui rend le code au moins aussi performant que si vous laviez crit vous-mme, mais surtout plus synthtique et comportant moins de risque derreur lors de lcriture. Voici encore quelques exemples utiles:
std::vector<int> V(100); std::generate(V.begin(), V.end(), rand);

Remplir un tableau avec des nombres alatoires


struct plus_petit_abs: public std::binary_function<double, double, bool> { bool operator() (double x, double y) { return fabs(x) < fabs(y); } }; std::vector<double> V; // std::sort(V.begin(), V.end(), plus_petit_abs());

Trier les lments dun tableau en ignorant leur signe

90

CHAPITRE 4 Classes et objets

struct Somme: public std::unary_function<double, void> { Somme(): somme(0) {} void operator()(double valeur) { somme+=valeur; } double somme; }; std::vector<double> V; // Somme resultat = std::for_each(V.begin(), V.end(), Somme()); std::cout << Somme = << resultat.somme << std::endl;

Somme des lments dun tableau illustrant lavantage des foncteurs sur les fonctions
std::list<int> L; // std::list<int>::iterator it; it = std::remove_if(L.begin(), L.end(), std::compose2( std::logical_and<bool>(), std::bind2nd(std::greater<int>(), 100), std::bind2nd(std::less<int>(), 1000) )); L.erase(it, L.end());

Suppression des lments (compris entre 100 et 1000) dune liste illustrant limbrication de foncteurs
struct Objet { void afche() { cout << /* */; } }; std::vector<Objet> V(100); std::for_each(V.begin(), V.end(), std::mem_func<void,Objet>(&Objet::afche));

Afcher tous les lments dun tableau via la fonction membre de lobjet utilis

Transformer un objet en fonction

91

Astuce Tous les foncteurs standard drivent des deux classes, unary_ function et binary_function. Elles ne contiennent que des typedef. Cela peut paratre totalement inutile, mais facilite grandement la programmation gnrique. Utilisez cette techni que si vous dcidez dcrire vos propres foncteurs.

Voici pour mmoire et titre dexemple le contenu de ces deux classes: template <class _Arg, class _Result> struct unary_function { typedef _Arg argument_type; // le type de largument typedef _Result result_type; // le type de retour }; template <class _Arg1, class _Arg2, class _Result> struct binary_function { typedef _Arg1 rst_argument_type; // le type du premier argument typedef _Arg2 second_argument_type; // le type du deuxime argument typedef _Result result_type; // le type de retour };

Info Si vous regardez les chiers den-tte de la bibliothque STL, vous rencontrez certainement la notion de gnrateur (generator), notamment dans le nommage de certains paramtres des patrons (templates). Cette notion correspond simplement un foncteur ne prenant pas dargument. On pourrait la reprsenter par cette classe de base: template <class _Result> struct generator { typedef _Result result_type; // le type de retour }; Les gnrateurs qui en hriteraient nauraient plus qu dnir loperator()() sans argument.

92

CHAPITRE 4 Classes et objets

La STL prdnit plusieurs types de foncteurs dans le chier den-tte <functional>. Voici un rcapitulatif de son contenu.
Foncteur sur les oprateurs standard
Foncteurs arithmtiques plus minus multiplies divides modulus negate Oprateur associ + - * / % - (unaire) Foncteurs logiques equal_to not_equal_to greater less greater_equal less_equal logical_and logical_or logical_not Oprateur associ == != > < >= <= && || ! (unaire)

Inverseurs logiques
Foncteur unary_negate binary_negate Helper not11 not21 Code quivalent lexcution not1(pred)(a) quivaut ! pred(a) not2(pred)(a,b) quivaut ! pred(a,b)

1. Les fonctions not1 et not2 prennent un prdicat en argument et retournent, respectivement, une instance de unary_negate ou une instance de binary_negate.

Binders: pour xer des paramtres


Foncteur Helper
1

Code quivalent lexcution bind1st(std::minus<oat>,1.3) (a) quivaut 1.3 - a bind2nd(std::minus<oat>,1.3) (a) quivaut a - 1.3

binder1st bind1st bind2nd1 binder2d

1. Les fonctions bind1st et bind2nd prennent un prdicat en argument et retournent, respectivement, une instance de binder1st ou une instance de binder2nd.

Transformer un objet en fonction

93

Adaptateur de pointeur de fonctions: transformer des fonctions en foncteurs


Foncteur
pointer_to_unary_function pointer_to_binary_function

Helper
ptr_fun ptr_fun

Adaptateur de pointeur de fonctions membres: transformer des fonctions en foncteurs


Type Helper Description
Sert appeler une fonction membre. mem_func_t(p_func)(arg) quivaut (*arg->*p_func)(). Idem pour une fonction membre const Sert appeler une fonction membre par rfrence. mem_func_t(p_func)(arg) quivaut (*arg.*p_func)().

mem_fun_t mem_fun const_mem_fun_t mem_fun mem_fun mem_fun_ref_t

const_mem_fun_ref_t const_mem _fun_ref Idem pour une fonction membre const. mem_fun1_t mem_fun const_mem_fun1_t mem_fun mem_fun mem_fun1_ref_t const_mem mem_fun_ fun1_ref_t Sert appeler une fonction membre un paramtre. mem_func_t(p_func)(arg1,arg2) quivaut (*arg1->* p_func) (arg2). Idem pour une fonction membre const Sert appeler une fonction membre un paramtre, par rfrence. mem_func_t(p_func)(arg1,arg2) quivaut (*arg1.*p_func) (arg2) . Idem pour une fonction membre const.

94

CHAPITRE 4 Classes et objets

Foncteurs spciques
Type
_Identity _Select1st _Select2nd

Plate-forme
1 1 1

Description
Voir identity Voir select1st Voir select2nd Pour std::plus<_T> retourne _T(0), pour std::multiplies<_T> retourne _T(1) Retourne largument tel quel Prend un std::pair<> en argument et retourne arg.rst Prend un std::pair<> en argument et retourne arg.second Retourne le premier argument Retourne le deuxime argument unary_functor(F1,F2)(x) quivaut F1(F2(x)) unary_functor(F1,F2,F3)(x) quivaut F1(F2(x),F3(x)) Gnrateur de nombres pseudo-alatoires bas sur la mthode soustractive employe en FORTRAN

Helper

identity_element 2 4 identity 2 3 4 select1st 2 3 4 select2nd 2 3 4 project1st 2 3 4 project2nd 2 3 4 unary_compose 2 3 4 binary_compose 2 3 4 substrative_rng 2 4

compose1 compose2

1. GCC 3.4 2. GCC 3.4 via <ext/functional> 3.VC++2008 avec _HAS_TRADITIONAL_STL 4. SGI

5
Templates et mtaprogrammation
Le template est une technique de programmation gnrique favorisant lindpendance du code par rapport au type, et ventuellement au nombre darguments utiliss. Ce concept est trs important car il permet daugmenter le niveau dabstraction du langage de programmation. La mtaprogrammation consiste utiliser les modles de telle sorte que le code source soit directement excut par le compilateur. Ces mtaprogrammes peuvent gnrer des constantes ou des structures de donnes. Cette technique est largement utilise en C++, et on la retrouve notamment dans la bibliothque BOOST.
Traduction Comment traduire template en bon franais? Le mot le plus proche dans notre langue se trouve dans le domaine de la couture: patron. Vous rencontrerez peut-tre aussi le terme modle. En couture, un patron sutilise un peu comme un calque pour dcouper des pices de tissus. On est ainsi sr dobte nir la mme forme pour chaque vtement produit. Mais il est bien sr possible de changer le type dtoffe. Je trouve lanalogie

96

CHAPITRE 5 Templates et mtaprogrammation

particulirement bien choisie, mais pour obtenir une traduction vraiment comprhensible, il serait prfrable dutiliser une expression comme patron de classe bien quun peu longue mon got. Dans le prsent ouvrage, jai prfr garder langlicisme pour la concision. Deux raisons sajoutent celle prcite. La premire est quil correspond au mot-cl du langage lui-mme. La deuxime raison est que lusage de cet anglicisme est largement accept.

Crer un modle de classe rutilisable


template <(class | typename) T1 [, ]> class Objet [: ] { }; Objet<Type1 [, ]> objet; template <(class | typename) T1 [, ]> Type1 fonction(Type2 param [, ]); Type1 res = fonction[<Type,[,]>](, t1, );

La notion de modle (de langlais template) est largement utilise dans la STL et dans BOOST. Il est ncessaire de bien la comprendre pour tirer pleinement partie du langage C++. Les templates permettent de sparer la forme du fond, en laissant la charge du compilateur la rcriture dun mme algorithme (le fond) dautres types de donnes (la forme). On peut donc voir les paramtres templates comme un moyen de paramtrer les types dune structure ou dune fonction, rendant ces dernires indpendantes du ou des types de donnes manipuls.

Crer un modle de classe rutilisable

97

Pour mettre en uvre un template, il faut imprativement prciser avec quels types on lutilise. La seule exception o lon peut omettre cette qualication a lieu lors de lappel une fonction template pour laquelle il ny a aucune ambigut de rsolution dappel. Un exemple bien connu de fonction template sont les fonctions min et max fournit avec la STL (attention, Microsoft persiste ne pas les fournir mais les remplace par ses vieilles macros min(a,b) et max(a,b)). Le code suivant est une version simplie de celle fournie par la STL de g++:
template <typename _Tp> inline const _Tp& min(const _Tp& __a, const _Tp& __b) { if (__b < __a) return __b; return __a; } template<typename _Tp> inline const _Tp& max(const _Tp& __a, const _Tp& __b) { if (__a < __b) return __b; return __a; }

Ces fonctions sont plus puissantes que des macros, car elles assurent de ne pas calculer deux fois des valeurs (avant comparaison et retour du min ou du max). Et elles illustrent parfaitement le paramtrage dune fonction avec des types, grce au template. Linstanciation dune fonction template, comme nous lavons dit, peut se faire explicitement, mais aussi implicitement. Par exemple, un appel std::min(1,2) est sufsant. Dans ce cas, 1 et2 sont toutes deux des constantes entires et le compilateur comprend aisment quil faut instancier std:: min<int>(). Par contre, lors dun appel std::min(1,2.0), le

98

CHAPITRE 5 Templates et mtaprogrammation

compilateur ne sait plus quoi faire (sauf sil existe une fonction min(int,double) dans lespace de nom std). Ici, il sera ncessaire demployer une instanciation explicite: std:: min<int>(1,2.0) pour transformer 2.0 en valeur entire, ou std:min<double>(1,2.0) pour transformer1 en rel double prcision. Le compilateur utilise la loi du moindre effort pour instancier un template. Par exemple, utiliser un pointeur sur un objet template ne suft provoquer son instanciation. Celle-ci naura lieu que lors du drfrencement de celuici. De mme, seules les fonctionnalits utilises dune classe template sont, par dnition, instancies. Par exemple, dans lexemple ci-aprs, seule la mthode afche() sera instancie. Ce programme compile donc parfaitement, mme si la mthode non_Codee() nest pas implmente.
#include <iostream> template <typename T> class Exemple { public: void afche(); void non_Codee(); }; template <typename T> void Exemple<T>::afche() { std::cout << Je mafche(). << std::endl; } // la mthode Exemple<T>::non_Codee() nest pas implmente int main(int,char**) { // instanciation de Exemple<char> Exemple<char> ex; // instanciation de Exemple<char>afche() ex.afche(); }

Crer une bibliothque avec des templates

99

class ou typename ? Dans des paramtres templates, les mots-cls class et typename sont strictement quivalents. Ils permettent simplement de spcier que le paramtre template est un type (ce qui nest pas obligatoire). Certains programmeurs prfrent utiliser systmatiquement typename plutt que class, car ils trouvent cela plus clair et conceptuellement plus beau. Dautres, par habitude, donnent une smantique diffrente ces mots-cls: typename pour les cas totalement gnriques, comme les conteneurs STL, et class pour les cas particuliers rservs certaines classes. Dautres encore utilisent exclusivement class. Vous laurez compris, cest une affaire de choix personnel. Jespre simplement que ces quelques lignes vous auront permis dy voir plus clair dans les habitudes de programmation que vous rencontrerez.

Crer une bibliothque avec des templates


template [class|struct] Objet<valeur [, ]>;

Linstanciation explicite complte permet de forcer linstanciation dun template. Cela est particulirement utile pour les bibliothques: ce faisant, il devient possible de coupler lintrt des templates et le fait de ne pas livrer votre implmentation. Par contre, cela peut galement empcher lutilisateur de ladite bibliothque dutiliser vos objets templates sur dautres types que ceux que vous aurez pr-instancis, moins de lui livrer aussi votre implmentation. Pour raliser une instanciation explicite, il suft de prciser les paramtres templates et de faire prcder ce type explicit par le mot-cl template. Par exemple, si vous

100

CHAPITRE 5 Templates et mtaprogrammation

vouliez forcer linstanciation dun modle Pile pour des entiers, vous pourriez crire:
template class Pile<int>;

Cette syntaxe peut tre simplie pour les fonctions templates. Comme pour leur utilisation, il suft que le compilateur puisse dterminer les types des paramtres utiliss. Le code template int min(int, int); suft donc forcer linstanciation de la fonction min() cite prcdemment. Lorsquune fonction ou une classe template possde des valeurs par dfaut pour tous ses paramtres templates, il nest pas ncessaire de prciser nouveau ces paramtres correspondants. Par exemple, le code suivant sufrait:
template class MaClasse<>;

Pour une instanciation concernant une bibliothque dynamique, on utilise conjointement linstanciation explicite et la spcication dexportation du code. Pour simplier lexemple ci-aprs, qui contient trs peu de chiers, jai laiss la dclaration du dene de limplmentation dans le chier source de la DLL.
#ifdef MADLL_IMPLEMENTATION #dene MADLL_API __declspec(dllexport) #else #dene MADLL_EXPORT __declspec(dllimport) #endif template <> class MADLL_API MaClasse { // MADLL_API Type fonction(parametres); };

Fichier en-tte de la bibliothque

Utiliser un type indirect dans un template

101

#dene MADLL_IMPLEMENTATION #include mon_header.hpp template <> MADLL_API Type MaClasse<>::fonction(parametres) { // } // Instanciations explicites template class MADLL_API MaClasse<double>; template class MADLL_API MaClasse<int>;

Fichier source de la bibliothque


#include mon_header.hpp MaClasse<double> mc_d; MaClasse<int> mc_i; MaClasse<oat> mc_f; // mc_d.fonction(); // OK mc_i.fonction(); // OK mc_f.fonction(); // ERREUR de link : fonction non dnie

Exemple dutilisation de la bibliothque

Utiliser un type indirect dans un template


typename identicateur

Le mot-cl typename rvle tout son intrt dans lutilisation dun type dni par une classe ou une structure lorsque cette

102

CHAPITRE 5 Templates et mtaprogrammation

dernire est elle-mme utilise comme paramtre template. Considrez le code suivant:
class A { public: typedef int Type; }; template <class T> class B { typename T::Type m_valeur; }; B<A> b;

Si vous omettez le typename, le code ne compile plus: le compilateur narrive pas comprendre que T::Type est bien un type. Et pour cause, il ne sait pas encore quel sera le type deT.

Changer limplmentation par dfaut fournie par un template


template<> Type fonction<>() { } template<> class<> { };

La spcialisation permet de changer limplmentation par dfaut fournie par un template. Pourquoi? Le but du template est de ne pas dupliquer le code. Mais de ce fait, les algorithmes implments ainsi sont souvent gnriques, parfois au dtriment des performances. Cest l quintervient la spcialisation, partielle ou totale, des fonctions, classes ou membres de classes templates. Ce mcanisme

Changer limplmentation par dfaut fournie par un template

103

permet, comme son nom lindique, de spcialiser ces templates pour certains paramtres donns dans le but de fournir des algorithmes plus performants adapts ces cas particuliers. Par exemple, une implmentation gnrique dun mcanisme de pile travaillant sur des pointeurs sur objets peut avoir intrt tre spcialise pour des objets de petite taille ou des types fondamentaux du langage. En vitant ainsi un niveau dindirection, les performances peuvent tre amliores pour ces types. La spcialisation totale implique de fournir tous les paramtres templates de la fonction ou de la classe mais de les omettre dans la dclaration template. Lexemple ci-aprs montre comment spcialiser la fonction std::min() sur un type de donnes particulier.
struct Structure { int id; // }; namespace std { template<> const Structure& min(const Structure& s1, const Structure& s2) { if (s1.id < s2.id) return s1; return s2; } }

104

CHAPITRE 5 Templates et mtaprogrammation

Spcialiser partiellement limplmentation dun template


template<> Type fonction<>() { } template<> class<> { };

La spcialisation totale implique la rcriture de tout le code concern par la spcication. Pour les classes, cela peut vite savrer fastidieux, surtout si vous navez besoin den spcialiser quune partie. Pour remdier cela, il existe la spcialisation partielle. Elle peut tre partielle sous deux aspects: soit elle ne spcialise quune partie des paramtres templates; soit elle ne spcialise quune mthode de la classe. Ces deux aspects peuvent tre cumuls lorsquil sagit dune fonction membre. La spcialisation partielle des paramtres templates permet de garder certains paramtres du templates comme indnis. Il est possible de changer la nature dun paramtre template (passer dun pointeur un non pointeur et vice versa). Lexemple suivant montre comment faire des spcialisations partielles:
template<int i, class A, class B> class Objet template<int i, class A> class Objet<i,A,A*> template<int i, class A, class B> class Objet<i,A*,B> template<class A> class Objet<2,A,char> {}; {}; {}; {};

Ne vous emballez pas, vous constaterez lusage quil existe aussi certaines restrictions: une expression spcialisant un argument template ne doit pas utiliser un paramtre template de la spcialisation;

Spcialiser une fonction membre

105

template<int I, int J> class Obj {}; template<int K> class Obj<K, 5*K> {}; // ERREUR

le type d'un paramtre spcialisant un argument template ne doit pas dpendre d'un paramtre de la spcialisation;
template<class T, T t> struct Obj {}; template<class T> struct Obj<T, 1> {}; // ERREUR

la liste des arguments de la spcialisation ne doit pas tre implicitement identique au template d'origine.
template<class A, class B> class Obj {}; template<class B, class A> class Obj<B,A> {}; // ERREUR

Spcialiser une fonction membre


template<> Type Objet<>::methode() { } template<> Type Objet<>::methode() { }

Le dernier type de spcialisation concerne une mthode dune classe template. Nul besoin de spcialiser toute une classe si spcialiser une ou plusieurs mthodes de celle-ci suft. La technique est simple: il suft de faire comme si la mthode tait une fonction normale et spcialiser les paramtres templates souhaits. Lexemple suivant vous rappelle comment:
template <typename T> class Objet { T m_valeur;

106

CHAPITRE 5 Templates et mtaprogrammation

public: Objet(T t) { m_valeur = t; } void set(T) { m_valeur = t; } T get() const { return m_valeur; } void afcher() const { std::cout << m_valeur << std::endl; } }; // spcialisation dune fonction membre template <> void Objet<int*>::print() const { cout << *item << endl; }

Excuter du code la compilation


La mtaprogrammation par templates consiste faire excuter du code par le compilateur laide des templates. Elle nest pas lapanage du C++ et se retrouve dans dautres langages comme Curl, D, Eiffel, Haskell, ML ou XL. Pour tre mise en uvre en C++, cette technique repose sur deux oprations: la dnition dun template (ou plusieurs) et sa (ou leurs) instanciation.

Excuter du code la compilation

107

Certains diront peut-tre que les macros sont aussi excutes la compilation. Mais les macros ne peuvent effectuer des contrles de type et manquent de smantique: elles ne sont quun systme de manipulation et de substitution de texte. Les mtaprogrammes templates nont, proprement parler, pas de variables, et ne peuvent manipuler que des constantes. Les valeurs changent de par la rcursion. La mtaprogrammation peut du coup tre vue comme un langage fonctionnel: cest en n de compte une sorte de machine de Turing.
// La mtafonction factorielle template <int n> struct Factorielle { enum { Result = n * Factorielle<n-1>::Result }; }; // On atteint la n de la rcursion grce une // spcialisation partielle template <> struct Factorielle<0> { enum { Result = 1 }; }; // Lancement du calcul par linstanciation Factorielle<5>::Result; // vaut 120 la compilation // Il faut utiliser des constantes uniquement int i=5; Factorielle<i>::Result; // ERREUR const int j=5; Factorielle<j>::Result; // OK, vaudra 120 la compilation

Les mtafonctions templates peuvent bien entendu tre utilises pour en construire dautres, comme en programmation classique. Lexemple ci-aprs illustre lutilisation de

108

CHAPITRE 5 Templates et mtaprogrammation

la mtafonction Factorielle<> pour construire la fonction mathmatique.

n! C i =C (n,i) = i! (ni)! n
Figure5.1: Formule factorielle

template <int n, int i> struct Comb { enum { Result = Factorielle<n>::Result / ( Factorielle <i>::Result * Factorielle<n-i>::Result) }; }; // Comb<5,3>::Result;

Crer des mta-oprateurs/ mtabranchements


template <bool g, class THEN, class ELSE> struct IF { typedef THEN Result; }; template <class THEN, class ELSE> struct IF<false, THEN, ELSE> { typedef ELSE Result; };

Crer des mta-oprateurs/mtabranchements

109

La mtaprogrammation template peut mme utiliser des tests conditionnels du type if then else Cela est certes moins lisible mais fonctionne parfaitement. Le code suivant montre comment utiliser ce genre de mtaoprateurs:
class Allocateur1 {}; class Allocateur2 {}; IF <UseAllocatorNumber==1, Allocateur1, // THEN type Allocateur2> // ELSE type ::Result allocator;

De la mme manire, il est possible de faire comme si lon disposait de valeurs boolennes. Le code ci-aprs illustre comment les simuler. Pour cela, nous rutilisons lexemple prcdant en adaptant la mtaclasse de test IF, et lui adjoignions deux classes FAUX et VRAI qui feront ofce de valeurs boolennes. Ensuite, la mtaclasse isPointer<> permet de dterminer si son paramtre template est ou non de type pointeur en renvoyant VRAI ou FAUX. Ce rsultat peut enn tre utilis lors dun test IF.
struct FAUX {}; struct VRAI {}; template <class BOOL, class THEN, class ELSE> struct IF { typedef THEN Result; }; template <class THEN, class ELSE> struct IF<FAUX, THEN, ELSE> { typedef ELSE Result; }; template <class T> struct isPointer { typedef FAUX Result; };

110

CHAPITRE 5 Templates et mtaprogrammation

template <class T> struct isPointer<T*> { typedef VRAI Result; }; template <class T> class MonTableau { IF< isPointeur<T>::Result, T, T*>::Result type_interne; // };

Si vous testez ce dernier exemple, vous remarquerez certainement que votre compilateur naccepte pas le IF<> prsent dans la classe MonTableau. Cela est frustrant, mais vous montre les limites de linterprtation des templates par le compilateur. Cest pour cette raison, entre autre, que les types traits ou dautres biais sont utiliss.
Curiosit Aprs les macros, aprs les fonctions inline, aprs les templates, voici la version mtaprogramme des fonction min() et max(). Encore? Quel intrt? Deux raisons cette curiosit: elles illustrent un aspect supplmentaire de la mtaprogrammation et pourront vous tre utiles pour mtaprogrammer. Voici donc une implmentation possible de ces dernires pour des entiers: template <int (a < b) ? a template <int (a > b) ? a a, int b> struct Min { enum { Result = : b; }; }; a, int b> struct Max { enum { Result = : b; }; };

Avantages et inconvnients de la mtaprogrammation

111

Avantages et inconvnients de la mtaprogrammation


Compromis entre temps de compilation et temps d'excution. Tout code template est analys, valu et dploy pendant la compilation. Elle peut donc prendre beaucoup de temps mais cela permet dobtenir un code trs rapide lexcution. Ce surcot est gnralement faible mais peut devenir non ngligeable pour de gros projets ou des projets utilisant intensment la mtaprogrammation template. Programmation gnrique. La mtaprogrammation template permet de se concentrer sur larchitecture tout en laissant au compilateur la charge de gnrer le code ncessaire. La mtaprogrammation fournit donc un moyen dcrire du code gnrique et dcrire moins de code. La maintenance du code en est facilite. Lisibilit. En mtaprogrammation C++, la syntaxe et les idiomes du langage sont souvent ardus lire par rapport du C++ classique. Ce code peut mme rapidement devenir trs difcile dchiffrer. Un trs bon niveau et surtout beaucoup dexprience peuvent devenir indispensables. La maintenance dun tel code, qui est cense tre facilite, peut sen trouver rserve un petit nombre de programmeurs qualis. Portabilit. Tous les compilateurs ne se valent pas. Surtout en ce qui concerne lvaluation des templates. Un code reposant massivement sur cette notion peut devenir trs difcile porter, voir impossible si vous ne disposez pas dun compilateur rcent.

6
Gestion de la mmoire
Rserver et librer la mmoire
Type* variable = new Type; delete variable double* variable = new double[n]; delete [] variable;

linstar de la fonction malloc(), loprateur new permet dallouer dynamiquement de la mmoire. Notez toutefois quinvoquer new provoquera linitialisation des donnes dun objet laide de son constructeur, sil existe. new Type alloue un objet, tandis que new Type[n] alloue un tableau de nobjets contigus.

114

CHAPITRE 6 Gestion de la mmoire

Attention Utiliser delete au lieu de delete[], ou inversement, peut entraner un comportement imprvisible de votre application. Dune manire gnrale, utilisez delete pour un new et delete[] pour un new[]. Faites toutefois trs attention: dans certains cas, il nest pas toujours facile de dterminer lequel utiliser. Pour bien comprendre la rgle prsente, examinez cet exemple: struct Livre { }; typedef Bibliotheque Livre[8]; Livre* livre = new Livre; //Bibliotheque *biblio = new Bibliotheque; => ERREUR Livre* biblio = new Bibliotheque; // OK delete livre; // delete biblio; => ERREUR: comportement imprvisible delete[] biblio; // OK Si vous avez compris le principe, vous aurez remarqu que le new Bibliotheque est en fait un new Livre[8] cach qui ncessite bien une destruction par delete[].

Rednir le systme dallocation mmoire


void* operator new(size_t size); void* operator new[](size_t size) operator delete(void* ptr); operator delete[](void *ptr);

Simuler une allocation dobjet une adresse connue

115

Vous pouvez rednir votre propre systme dallocation mmoire en rednissant les oprateurs globaux new et delete. Vous pouvez ainsi optimiser lallocation mmoire de toute une application en utilisant un ou plusieurs pools de mmoire, ou utiliser des ressources systme particulires de manire transparente pour le reste de votre code.
Attention Rednir ces oprateurs globaux aura un effet sur toutes les allocations de votre programme.

Dautres solutions existent si vous souhaitez optimiser lallocation mmoire de vos programmes: loprateur de placement; une surcharge des oprateurs new et delete au niveau de vos classes; la technique des allocateurs (elle est largement utilise au sein de la STL).

Simuler une allocation dobjet une adresse connue


new(adresse) Type new(adresse) Type[n]

Les oprateurs de placement simulent une allocation dobjet une adresse connue. Ces oprateurs ne font que renvoyer ladresse fournie.

116

CHAPITRE 6 Gestion de la mmoire

Attention Vous ne devez jamais utiliser delete sur des objets ainsi crs. Au besoin, appelez explicitement le destructeur pour appliquer les traitements qui y sont associs: Obj* o = new(&quelqueChose) Obj; // o->~Obj(); // appel explicite du destructeur sans dtruire lobjet lui-mme Info Cet oprateur utilise la mme technique de surcharge que loprateur new sans exception (voir la section suivante).

Traiter un chec de rservation mmoire


try { Type *objet = new Objet; } catch (std::bad_alloc) { // }

Un chec dallocation mmoire se traduit par le lancement de lexception std::bad_alloc. Si vous souhaitez que votre application ne se termine pas inopinment, vous avez intrt traiter un tel cas derreur en capturant cette dernire.Vous pourrez ainsi lancer un message derreur ou lancer un systme de rcupration de mmoire (comme un systme de sauvegarde, libration, traitement, rallocation de la sauvegarde) votre gr.

Traiter un chec de rservation mmoire

117

Une autre technique pour intercepter un chec dallocation consiste mettre en place un handler ddi. Lorsque celui-ci se termine normalement, loprateur new tente une nouvelle allocation. Si celle-ci choue nouveau, le handler est nouveau invoqu. Voici quoi pourrait ressembler un tel handler:
#include <iostream> #include <new> #include <exception> using namespace std; void newHandlerPerso() { static int numEssai = 0; switch( numEssai ) { case 0: // compacter le tas std::cout << Compacter le tas\n; numEssai++; break; case 1: // nettoyer la mmoire du code inutile std::cout << nettoyer la mmoire du code inutile\n; numEssai++; break; case 2: // utiliser un chier swap std::cout << utiliser un chier swap\n; numEssai++; break; default: std::cerr << Pas assez de mmoire\n; numEssai++; throw std::bad_alloc(); } }

118

CHAPITRE 6 Gestion de la mmoire

Vous pourrez activer ce handler volont ainsi:


typedef void (*handler)(); handler ancienHandler = 0; void test_handler() { handler ancienHandler = set_new_handler( newHandlerPerso ); // code protg try { while ( 1 ) { new int[5000000]; cout << Allocation de 5000000 ints. << endl; } } catch ( exception e ) { cout << e.what( ) << xxx << endl; } // set_new_handler( ancienHandler ); } int main(int,char**) { test_handler(); return 0; }

Dsactiver le systme dexception lors de lallocation

119

Dsactiver le systme dexception lors de lallocation


#include <new> Type *obj = new(std::nothrow) Type; int *a = new(std::nothrow) int[1000000000];

Le chier den-tte standard <new> dnit une structure appele std::nothrow_t dans lespace de nom std. Cette structure vide sert de directive en forant lappel de new tre redirig sur ces surcharges des oprateurs:
void* operator new(size_t size, const std::nothrow_t&); void* operator new(void* v, const std::nothrow_t& nt)

Dclaration apparaissant dans <new> <new> dnit galement une instance extern const nothrow_t nothrow; permettant dutiliser de manire uniforme ladite syntaxe.
Objet* objets = new(std::nothrow) Objet[1000000000]; if (objets == 0) { std::cout << Erreur allocation memoire << std::endl; }

120

CHAPITRE 6 Gestion de la mmoire

Optimiser lallocation avec un pool mmoire


boost::pool<> pool(tailleEnOctets); boost::object_pool<Type> pool; boost::singleton_pool<PoolTag, TailleEnOctets> pool; boost::pool_allocator<Type>

Plutt que de dvelopper vous-mme un systme de pools (ou bacs) de mmoire, utilisez ceux fournis par BOOST. Pour en savoir plus sur cet ensemble de bibliothques, reportez-vous au Chapitre13, consacr BOOST. Pour les pools de type objet, dtruire le pool libre implicitement tous les blocs mmoire allous par celui-ci. Pour les pools de type singleton, les blocs mmoire ont une dure de vie gale au programme. Ils peuvent donc tre aisment partags. Du coup, ils ont t cods pour tre thread-safe (avec pour corollaire un cot lexcution). Pour librer les ressources mmoire dun pool de type singleton, il faut linstancier avec les fonctions membres release_memory() ou purge_memory(). purge_memory() libre tous les blocs mmoire allous. release_memory() libre tous les blocs internes non utiliss.
Attention Certaines implmentations prfrent retourner0 (NULL) plutt que de dclencher une exception lorsquil ny a plus de mmoire. Soyez donc bien attentif ce point lors de leur utilisation.

Les boost::pool<> sont de type objet et retournent NULL en cas dchec de rservation mmoire. Dans lexemple

Optimiser lallocation avec un pool mmoire

121

ci-aprs, nul besoin de se proccuper de la dsallocation mmoire, puisque le pool sen chargera lors de sa destruction la n de la fonction.
#include <boost/pool/pool.hpp> void fonction() { boost::pool<> p(sizeof(int)); for (int i=0; i < 100000; ++i) { int* const i_ptr = (int*)p.malloc(); // } }

Les boost::object_pool<> sont de type objet et retournent NULL en cas dchec dallocation mmoire. Ils tiennent compte du type dobjet allou. L encore, tous les objets concerns sont dtruits avec le pool.
#include <boost/pool/object_pool.hpp> class Objet { }; void fonction() { boost::object_pool<Objet> p; for (int i=0; i < 100000; ++i) { Objet* const i_ptr = p.malloc(); // } }

Les boost::singleton_pool sont de type singleton. Ils retournent NULL en cas dchec dallocation mmoire (encore). Par contre, la mmoire nest libre quexplicitement. Le tag permet de crer plusieurs instances diffrentes de ce pool de mmoire.

122

CHAPITRE 6 Gestion de la mmoire

#include <boost/pool/singleton_pool.hpp> struct MonPool_Tag{}; typedef boost::singleton_pool<MonPool_Tag, sizeof(int)> MonPool; void fonction() { for (int i=0; i < 100000; ++i) { int* const i_ptr = (int*)MonPool::malloc(); // } // Appel explicite pour librer la mmoire MonPool::purge_memory(); }

Les boost::pool_alloc sont de type singleton. Attention, ceux-ci dclenchent une exception en cas dchec dallocation mmoire. Ils sont conus sur les boost:singleton_pool et sont compatibles avec les allocateurs STL. Dans lexemple ci-aprs, la mmoire ne sera pas libre la n de la fonction. Pour le forcer, il faut appeler boost::singleton_pool<boos:: pool_allocator_tag, sizeof(int)>::release_memory().
#include <boost/pool/pool_alloc.hpp> #include <vector> void fonction() { std::vector<int, boost::pool_allocator<int> > V; for (int i=0; i < 100000, ++i) V.push_back(33); }

7
Exceptions
Obtenir un code able est certainement une des grandes proccupations des programmeurs. De manire gnrale, deux possibilits de gestion des erreurs existent: lutilisation de codes de retour ou les exceptions. Libre vous dutiliser lune ou lautre de ces possibilits. Ce chapitre vous permettra dapprhender de manire sereine les exceptions en C++ an de les utiliser judicieusement.

Principe
#include <exception> #include <stdexcept> try { // code pouvant dclencher directement // ou indirectement une exception } catch(type1) { // code du gestionnaire traitant les exceptions // correspondant au type1 } catch(type2)

124

CHAPITRE 7 Exceptions

{ // code du gestionnaire traitant les exceptions // correspondant au type2 (et non traites par un // des gestionnaires prcdents) } // catch(...) { // code du gestionnaire traitant toute exception // non gre par un des gestionnaires prcdents } throw expression;

Les exceptions permettent une forme de communication directe entre les fonctions bas niveau et les fonctions haut niveau qui les utilisent. Lutilit de cette communication est dautant plus vidente lorsque la diffrence de niveau entre le dclencheur et le gestionnaire est importante. Le principe est le suivant. Une fonction dtecte un comportement exceptionnel et dclenche une exception laide de throw expression. Lexception est construite grce lexpression fournie linstruction throw. Elle peut tre un nombre entier ou ottant, une chane ou un objet. Le plus souvent, il sagit dun objet dont la classe drive de std::exception, contenant les informations relatives aux caractristiques de lvnement survenu. Lexception stoppe le fonctionnement normal du programme. Elle dtruit tous les objets construits localement en remontant bloc par bloc, fonction par fonction (dans la pile dappel) jusqu rencontrer un gestionnaire (catch) qui la gre. Si aucun gestionnaire appropri nest trouv,

Principe

125

la fonction terminate() est appele; elle provoque la n du programme. Si un gestionnaire est utilis, le programme poursuit son cours normal linstruction qui suit le dernier gestionnaire de son groupe de catch.
Info La destruction des objets locaux est un des principes fondamentaux les plus utiles du systme des exceptions en C++.

Voici un exemple simple montrant comment garantir que lon naccde pas un lment en dehors des limites dun tableau:
class Tableau3 { int valeur[3]; public: int get(int i) const { if (i<0 || i>3) throw std::out_of_range(); return valeur[i]; } // }; // int v = tab.get(4);

Slection du gestionnaire de lexception Le premier gestionnaire ligible, cest--dire dont le type est compatible avec lexception, est utilis. Le test dligibilit dun gestionnaire catch est soumis des rgles de conversion proche de celles de la surcharge de fonction (voir le Chapitre2, section Surcharge).

126

CHAPITRE 7 Exceptions

Un gestionnaire catch(T), catch(const T), catch(T&) ou catch (const T&) est compatible avec une exception de typeE si :
T et E dsignent le mme type ou; T est une classe de base visible (accessible) de E ou; T et E sont des types pointeurs et E peut tre converti

en T par une conversion standard.

Contrairement la surcharge de fonction, si T etE ne sont pas de type pointeur, aucune conversion standard nest tente. Voici un exemple: #include <stdexcept> // try { throw std::bad_alloc(); } catch(std::exception& e) { // ce gestionnaire correspond lexception dclenche // car std::bad_alloc hrite de std::exception. } catch(std::bad_alloc&) { // jamais atteint, // car le gestionnaire prcdent est utilis. } Notez quun bon compilateur vous signalera votre tourderie (g++ le fait), par un message de ce type: excep.cpp: In function `int f(bool): excep.cpp:22: warning: exception of type std::bad_alloc will be caught excep.cpp:13: warning: by earlier handler for std::exception

Transmettre une exception 127

Attention Les dbogueurs modernes permettent de sarrter automatiquement sur le dclenchement dune exception. Souvent, cela permet de trouver trs rapidement lendroit dun bogue. Toutefois, si votre programme fait un usage abusif des exceptions, le dbogueur se mettra trs souvent en pause, rendant la recherche du bogue au mieux fastidieuse, au pire impraticable. Peut-tre alors tenterez-vous dutiliser loption Sarrter sur les exceptions non gres. Mais si votre programme met en uvre des gestionnaires trs gnriques, le dbogueur ne sarrtera jamais sur le lieu du bogue. Vous laurez compris: nutilisez pas abusivement le mcanisme des exceptions comme systme de traitement derreurnormale ! Comme son nom lindique, une exception doit rester une exception! Il est prudent de ne les utiliser que pour les rels cas danomalies.

Attention Lappel dun gestionnaire catch est trait comme un appel de fonction. Il commence donc, selon le type de paramtre transmis, par faire une copie de largument! Soyez donc vigilants et prenez plutt lhabitude de dclarer le type par rfrence, surtout avec les objets. Par exemple utilisez catch(const MonException&) plutt que catch(MonException).

Transmettre une exception


throw;

Dans un gestionnaire catch, vous pouvez utiliser throw; pour redclencher lexception en cours de traitement comme si elle navait pas t attrape. Le code qui pourrait suivre cette instruction dans ledit gestionnaire ne sera pas excut.

128

CHAPITRE 7 Exceptions

Le programme suivantillustre le comportement de la transmission dexception:


#include <stdexcept> #include <iostream> int f(bool transmettre) { try { throw std::bad_alloc(); } catch(std::exception& e) { // ce gestionnaire correspond lexception dclenche // car std::bad_alloc hrite de std::exception. std::cout << traitement 1 << std::endl; if (transmettre) throw; std::cout << traitement 1 bis << std::endl; } catch(std::bad_alloc&) { // jamais atteint, // car le gestionnaire prcdent est utilis. std::cout << traitement 2 << std::endl; } } int main(void) { std::cout << --- essai A --- << std::endl; try { f(false); } catch(...) { std::cout << traitement 3 << std::endl; }

Expliciter les exceptions 129

std::cout << --- essai B --- << std::endl; try { f(true); } catch(...) { std::cout << traitement 3 << std::endl; } return 0; }

Il produit la sortie suivante:


--- essai A --traitement 1 traitement 1 bis --- essai B --traitement 1 traitement 3

Expliciter les exceptions


type fonction(parametres) throw(T,) type fonction(parametres) throw()

Lexplicitation des types dexceptions (ou exceptions compatibles) pouvant tre dclenches par une fonction ne fait pas partie de sa signature. Elle permet nanmoins au compilateur de faire certaines vrications et de vous mettre en garde par des messages (le plus souvent des warnings).

130

CHAPITRE 7 Exceptions

#include <stdexcept> #include <iostream> void excep_bad_cast() { throw std::bad_cast(); } void excep_range_error() { throw std::range_error(std::string(toto)); } int f(bool transmettre) throw(std::range_error, std::bad_alloc) { try { // dcommenter au choix pour les tests // excep_bad_cast(); // excep_range_error(); throw std::bad_alloc(); } catch(std::bad_alloc&) { std::cout << traitement 2 << std::endl; throw; } } int main(void) { std::cout << --- essai A --- << std::endl; try { f(false); } catch(...) { std::cout << traitement 3 << std::endl; } return 0; }

Utiliser ses propres implmentations des fonctions terminate() et unexpected() 131

En dcommentant la ligne excep_bad_cast(); vous obtiendrez (avec g++):


--- essai A --terminate called after throwing an instance of std::bad_cast what(): St8bad_cast Abort trap

En dcommentant la ligne excep_range_error(); vous obtiendrez (avec g++):


--- essai A --traitement 3

Utiliser ses propres implmentations des fonctions terminate() et unexpected()


typedef void (*handler)(); handler set_terminate(handler); handler set_unexpected(handler);

Vous pouvez utiliser vos propres implmentations des fonctions terminate() et unexpected(). Elles retournent le handler en vigueur avant lappel. vous de le sauvegarder pour ventuellement le remettre.

132

CHAPITRE 7 Exceptions

Attention Votre fonction terminate() doit imprativement terminer le programme en cours. Elle ne doit ni retourner lappelant, ni lancer une quelconque exception. Votre fonction unexpected() peut ne pas terminer le programme. Dans ce cas, elle doit imprativement dclencher une nouvelle exception.

Lexemple suivant montre comment mettre en place vos propres gestionnaires:


void my_stop() { QMessageBox::critical(0,Erreur irrcuprable, Lapplication a rencontr une erreur inattendue et va se fermer); exit(-1); } void fonction() { std::terminate_handler orignal = set_terminate(my_stop); // set_terminate(original); }

Utiliser les exceptions pour la gestion des ressources


Considrez par exemple un code effectuant les oprations suivantes: 1. Rserver une ressource. 2. Faire un traitement. 3. Librer la ressource.

Utiliser les exceptions pour la gestion des ressources 133

Ce type de code peut paratre anodin et sans danger. Et pourtant que se passerait-il si le traitement devait dclencher une exception? Votre ressource ne serait jamais libre.Vous pourriez alors opter pour cette solution:
try { reserver_ressource(); traitement(); } catch(...) { liberer_ressource(); }

Cette solution peut tre viable, mais devient vite impraticable dans certaines situations. Considrez le cas de plusieurs rservation/traitement avec une libration du tout la n, ou bien plusieurs rservations suivies dun traitement pour enn librer toutes vos ressources Vous rencontrerez ce cas rgulirement avec les allocations mmoire. Que se passerait-il en utilisant la mthode prcdente?
try { ObjetA * objetA = new ObjetA(); ObjetB * objetB = new ObjetB(); // traitement } catch(...) { delete objetA; delete objetB; }

Si une exception bad_alloc est dclenche lors de lallocation de lobjetB, linstruction delete objetB aura un comportement imprvisible! Mme un test sur la valeur du pointeur nest pas sufsant.

134

CHAPITRE 7 Exceptions

Astuce Utilisez des objets pour allouer des ressources et non des pointeurs. Voici une version simpliste de std::auto_ptr pouvant servir de modle pour rsoudre tous vos problmes: template<class T> struct Ptr { Ptr(T* p): ptr(p) {} ~Ptr() { delete p; } T* get() const { return ptr; } T* operator->() const { return ptr; } private: T* ptr; }; Le code prcdent devient alors non seulement plus simple, mais totalement sr. En effet, si la deuxime allocation choue, le deuxime objet ne sera pas cr et son destructeur ne sera pas appel. Par contre, de par le fonctionnement du C++, nous avons la garantie que le premier sera bien dtruit, provoquant la libration de la ressource alloue. Ptr<Objet> ptrA(new ObjetA()); Ptr<ObjetB> ptrB(new ObjetB()); // traitement identique

Exceptions de la STL
Vous trouverez dans le tableau suivant toutes les exceptions utilises (ou qui le seront dans de prochaines volutions) par la STL.

Exceptions de la STL 135

<exception> (inclus par <stdexcept>)


Type
exception + bad_exception

Description
Classe de base de toutes les exceptions pouvant tre dclenches par la bibliothque STL ou certaines expressions du langage C++.Vous pouvez dnir vos propres exceptions en hritant de celle-ci si vous le souhaitez. Peut tre dclenche si une exception non rpertorie dans la liste des exceptions autorises par une fonction est dclenche

<stdexcept>
Type
+ logic_error + + domain_error

Description
Reprsente les problmes de logique interne au programme. En thorie, ils sont prvisibleset peuvent souvent tre reprs par une lecture attentive du code (comme par exemple lors de la violation dun invariant de classe). Dclenche par la bibliothque, ou par vous, pour signaler des erreurs de domaine (au sens mathmatique du terme)

+ + invalid_argument Dclenche pour signaler le passage dargument(s) invalide(s) une fonction + + length_error + + out_of_range + runtime_error + + range_error Dclenche lors de la construction dun objet dpassant une taille limite autorise. Cest le cas de certaines implmentations de basic_string. Dclenche lorsquune valeur dun argument nest pas dans les limites (bornes) attendues. La mthode at() des vector effectue un test de borne et dclenche cette exception si besoin. Reprsente les problmes sortant du cadre du programme. Ces problmes sont difcilement prvisibles et surviennent lors de lexcution du programme. Dclenche pour signaler une erreur de borne issue dun calcul interne

136

CHAPITRE 7 Exceptions

<stdexcept> (suite)
Type
+ + overow_error

Description
Dclenche pour signaler un dbordement arithmtique (par le haut)

+ + underow_error Dclenche pour signaler un dbordement arithmtique (par le bas)

<type_info>
Type
+ bad_cast + bad_typedid

Description
Dclenche lors dune conversion dynamique dynamic_cast invalide Dclenche si un pointeur nul est transmis une expression typeid()

<new>
Type
+ bad_alloc

Description
Dclenche par new (dans sa forme standard, cest--dire sans le nothrow) pour signaler un chec dallocation mmoire

Info Aucune exception ne peut (et ne doit, si vous crivez vos propres classes en hritant de exception) dclencher pendant lexcution dune fonction membre de la classe exception. Astuce La classe de base exception possde une fonction virtuelle const char* what() qui retourne une chane de caractres la dcrivant.

8
Itrateurs
Les itrateurs sont une gnralisation des pointeurs. Notez que je ne parle pas ici de LA gnralisation, mais bien dUNE gnralisation. Dautres types de gnralisation des pointeurs existent, comme la notion de pointeur fort/ pointeur faible. Un itrateur est une interface standardise permettant de parcourir (ou itrer) tous les lments dun conteneur. Les itrateurs prsentent la mme smantique que les pointeurs par plusieurs aspects: ils servent dintermdiaire pour atteindre un objet donn; si un itrateur pointe sur un lment donn dun ensemble, il est possible de lincrmenter pour quil pointe alors sur llment suivant. Les itrateurs sont un des lments essentiels la programmation gnrique. Ils font le lien entre les conteneurs et les algorithmes. Ils permettent dcrire un algorithme de manire unique quel que soit le conteneur utilis, pourvu que ce dernier fournisse un moyen daccder ses lments travers un itrateur.Vous aurez certainement dj compris que ce qui importe pour lalgorithme nest plus de savoir sur quel type de conteneur mais sur quel type ditrateur il travaille.

138

CHAPITRE 8 Itrateurs

Il est donc possible et mme ncessaire davoir plusieurs (ou sufsamment) catgories ditrateurs si lon veut pouvoir crire un large ventail dalgorithmes gnriques. Rassurez-vous, la bibliothque standard (STL) vous a mch le travail en dnissant plusieurs itrateurs, et mme mieux, une hirarchie ditrateurs.

Les diffrents concepts


Les itrateurs sont utiliss pour accder aux membres des classes conteneurs de la STL. Ils peuvent tre utiliss dune manire analogue aux pointeurs. Par exemple, il est possible dutiliser un itrateur pour parcourir les lments dun std::vector. Il existe plusieurs types ditrateurs. Vous les retrouverez dans le tableau ci-aprs. Plus que des types, ce sont surtout des concepts.
Les diffrents itrateurs STL
Type
InputIterator OuputIterator ForwardIterator BidirectionalIterator

Description
Lire des valeurs en avanant. Ils peuvent tre incrments, compars et drfrencs. crire des valeurs en avanant. Ils peuvent tre incrments et drfrencs. Lire et/ou crire des valeurs en avanant. Ils combinent les fonctionnalits des InputIterator et des OutputIterator avec la possibilit de stocker les itrateurs eux-mmes. Lire et/ou crire des valeurs en avanant et/ou en reculant. Ils sont comme les ForwardIterator mais ils peuvent tre incrments et/ou dcrments.

Les diffrents concepts

139

Type

Description

RandomAccessIterator Lire et/ou crire des valeurs dans un ordre alatoire. Alatoire ne signie pas ici au hasard mais dans nimporte quel ordre, selon vos besoins. On le traduit parfois par itrateur daccs direct. Ce sont les itrateurs les plus puissants; ils ajoutent aux fonctionnalits des BidirectionalOperator les possibilits deffectuer des oprations semblables larithmtique des pointeurs et la comparaison des pointeurs.

Chaque classe conteneur de la STL est associe un type ou concept ditrateur. Il en est de mme pour tous les algorithmes fournis par la STL: chacun utilise un type ditrateur particulier. Par exemple, les vecteurs (std:: vector) sont associs aux RandomAccessIterator (itrateur accs alatoire). Il en dcoule quils peuvent tre utiliss avec tous les algorithmes bass sur ce type ditrateur. Comme cet itrateur englobe toutes les caractristiques des autres types ditrateurs, les vecteurs peuvent aussi tre utiliss avec tous les algorithmes conus pour les autres types ditrateurs. Le code suivant cre et utilise un itrateur avec un vecteur:
std::vector<int> V; std::vector<int>::iterator iterateur; for (int i=0; i<10; i++) V.push_back(i); int total = 0; for (iterateur = V.begin(); iterateur != V.end(); iterateur++) total += *iterateur; std::cout << Total = << total << std::endl;

Notez que vous pouvez accder aux lments du conteneur en drfrenant litrateur.

140

CHAPITRE 8 Itrateurs

Comprendre les iterator_traits


#include <iterator> template <class Iterator> struct iterator_traits { typedef typename Iterator::iterator_category iterator_category; typedef typename Iterator::value_type value_type; typedef typename Iterator::difference difference_type; _type typedef typename Iterator::pointer pointer; typedef typename Iterator::reference reference; }; template <class T> struct iterator_traits<T*> { typedef random_access_iterator_tag typedef T typedef ptrdiff_t typedef T* typedef T& };

iterator_category; value_type; difference_type; pointer; reference;

Une des caractristiques les plus importantes des itrateurs est davoir des types associs. un type ditrateur est donc associ dautres types, comme: value_type. Le type de valeur est le type dobjet sur lequel litrateur pointe. difference_type. Le type de distance (ou type de la diffrence) entre deux itrateurs. Ce peut tre un type intgral comme int. Les pointeurs, par exemple, sont des itrateurs. Par exemple, le type de valeur de int* est int. Le type de distance est ptrdiff_t puisque si p1 et p2 sont des pointeurs, lexpression p1 p2 est de type ptrdiff_t.

Comprendre les iterator_traits

141

Les algorithmes gnriques ont souvent besoin daccder des types associs. Un algorithme qui prend en paramtre une squence ditrateurs peut avoir besoin de dclarer une variable temporaire du type de la valeur pointe. La classe iterator_traits fournit un mcanisme pour de telles dclarations. Une technique de prime abord vidente pour une telle fonctionnalit serait dimposer tous les itrateurs dembarquer ces dclarations de type. Ainsi, le type de valeur dun itrateur I serait I::value_type. Nanmoins, cela ne fonctionne pas bien. Les pointeurs sont des itrateurs et ne sont pas des classes: si I tait, par exemple, un int*, il serait impossible de dnir I::value_type. Le concept de traits template a t cr pour remdier ce genre de difcults.
Info Une classe de caractristiques ou traits class est une classe utilise en lieu et place de paramtres templates. Elle embarque des constantes et types utiles. Elle offre un nouveau niveau dindirection pour rsoudre de nombreux problmes. lorigine, ce concept portait le nom de bagage ou valise (baggage) contenant des caractristiques (traits) et a ni par tre appel directement traits.

Si vous dnissez un nouvel itrateurI, vous devez vous assurer que iterator_traits<I> soit correctement dni. Vous avez deux possibilits pour cela. Premirement, vous dnissez votre itrateur de telle sorte quil contienne les types associs I::valu_type, I::difference_type et ainsi de suite. Deuximement, vous pouvez spcialiser iterator_ traits avec votre type. La premire technique est la plupart du temps plus pratique, surtout lorsque vous pouvez crer votre itrateur en hritant dune des classes de base input_ iterator, output_iterator, forward_iterator, bidirectional_iterator, ou random_access_iterator.

142

CHAPITRE 8 Itrateurs

Lexemple suivant illustre lutilisation de cette classe traits:


template <class InputIterator> std::iterator_traits<InputIterator>::value_type derniere_valeur(InputIterator debut, InputIterator n) { std::iterator_traits<InputIterator>::value_type resultat = *debut; for (++debut; debut!= n; ++debut) resultat = *debut; return resultat; }

Cette fonction renvoie la valeur du dernier lment dune squence. Bien videmment, ce nest pas un exemple de bon code. Il existe de bien meilleures faons darriver au mme rsultat, surtout si litrateur est du type bidirectional_iterator ou forward_iterator. Mais vous pouvez noter quutiliser les iterator_traits est le seul moyen dcrire une fonction gnrique de ce type.

Calculer la distance entre deux itrateurs


#include <iterator> iterator_traits<InputIterator>::difference_type distance(InputIterator debut, InputIterator n); void distance(InputIterator debut, InputIterator n, Distance& n);

Ces fonctions retournent la distanced entre debut et n, autrement dit debut doit tre incrment dfois pour quil devienne gal n. La premire version de distance() retourne cette distance, la deuxime lajoute aun donn.

Calculer la distance entre deux itrateurs 143

Info La deuxime version de distance() a t dnie dans la STL originale, et la premire version a t introduite par le comit de standardisation C++. La dnition a t modie car lancienne tait source derreur. En effet, cette version requrait lutilisation dune variable temporaire et son utilisation ntait pas intuitive: elle ajoutait n la distance entre debut et n plutt que simplement lui affecter cette valeur. Du coup, oublier dinitialisern zro tait une erreur rpandue. Bien entendu, les deux versions sont implmentes par souci de compatibilit, mais vous tes encourag toujours utiliser la version du comit (et qui renvoie la distance).

Lexemple ci-aprs montre que la fonction distance() fonctionne mme avec les conteneurs accs squentiel (au sens propre du terme), mais alors sa complexit est linaire et plus en temps constant.Vous devez donc lutiliser en connaissance de cause.
std::list<int> L; L.push_back(0); L.push_back(1); assert(std::distance(L.begin(), L.end()) == L.size());

Attention Si votre compilateur ne supporte pas la spcialisation partielle (voir la section Spcialiser partiellement limplmentation dun template au Chapitre5), vous ne pourrez pas utiliser les fonctions utilisant std::iterator_traits<>. Cette classe repose en effet largement sur cette spcicit du langage C++. Rassurez-vous, la plupart des compilateurs modernes supportent la spcialisation partielle.

144

CHAPITRE 8 Itrateurs

Dplacer un itrateur vers une autre position


#include <iterator> void advance(InputIterator& i, Distance n);

advance() incrmente i de la distancen. Sin est positif, cela est quivalent excuter nfois ++i, et sil est ngatif |n|fois --i. Si nest nul, la fonction na aucun effet. Lexemple suivant illustre lutilisation de cette fonction sur les listes:
std::list<int> L; L.push_back(0); L.push_back(1); std::list<int>::iterator it = L.begin(); std::advance(it, 2); assert(it == L.end());

Astuce distance() et advance() prennent tout leur intrt avec la programmation gnrique (cest--dire avec les templates). Grce elles, vous ntes plus oblig de connatre le type de conteneur sur lequel vous travaillez. Ainsi, vous pouvez passer le type de conteneur comme paramtre template, et le tour est jou: votre algorithme devient gnrique.

Comprendre les itrateurs sur ux dentre/lecture 145

Comprendre les itrateurs sur ux dentre/lecture


#include <iterator> class istream_iterator<T, Distance>;

Un istream_iterator<> est un InputIterator. Il effectue une sortie formate dun objet de typeT sur un istream<> parti culier donn. Quand la n du ux est atteinte, listream_ iterator<> renvoie une valeur ditrateur particulire qui signie n de ux. La valeur de cet itrateur de n peut tre obtenue par le constructeur vide. Vous pourrez noter que toutes les caractristiques dun InputIterator sont applicables.
double somme = 0; std::istream_iterator<double, char> is(std::cin); while (is!= std::istream_iterator<double, char>()) { somme = somme + *is; ++is; } std::cout << Somme = << somme << std::endl;

Cet exemple lit des valeurs relles sur la console (jusqu un Ctrl+Z) puis crit leur somme. Lexemple suivant mmorise une suite dentiers, fournis travers lentre standard, dans un tableau:
std::vector<int> V; std::copy(std::istream_iterator<int>(std::cin), std::istream_iterator<int>(), std::back_inserter(V));

146

CHAPITRE 8 Itrateurs

Comprendre les itrateurs sur ux de sortie/criture


#include <iterator> class ostream_iterator<T>;

Un ostream_iterator<> est un OutputIterator qui effectue une sortie formate dobjets de typeT sur un ostream (ux de sortie) particulier. Notez que toutes les restrictions dutilisation dun OuputIterator doivent tre observes. Lexemple suivant copie les lments dun vecteur sur la sortie standard, en en crivant un par ligne:
std::vector<int> V; // std::copy(V.begin(), V.end(), std::ostream_iterator<int>(std::cout, \n));

Utiliser les itrateurs de parcours invers


#include <iterator> class reverse_iterator<RandomAccessItertator, T, Reference, Distance>; class reverse_bidirectional_iterator< BidirectionalIterator, T, Reference, Distance>;

reverse_iterator<> et reverse_bidirectional_iterator<> sont des adaptateurs ditrateur qui permettent le parcours invers dune squence. Appliquer loprateur++ sur un reverse_iterator<RandomAccessIterator> (ou un reverse

Utiliser les itrateurs de parcours invers 147

_bidirectional_iterator<BidirectionalIterator> revient au mme quappliquer loprateur sur un RandomAccessIterator (ou un BidirectionalIterator). Les deux versions diffrent uniquement sur le concept ditrateur sur lequel elles sappliquent.
template <class T> void afcher_en_avant(const std::list<T>& L) { std::list<T>::iterator debut = L.begin(); std::list<T>::iterator n = L.end(); while (debut!= n) std::cout << *debut << std::endl; } template <class T> void afcher_en_arriere(const std::list<T>& L) { typedef std::reverse_bidirectional_iterator< std::list<T>::iterator, T, std::list<T>::reference_type, std::list<T>::difference_type> reverse_iterator; reverse_iterator rdebut( L.begin() ); reverse_iterator rn( L.end() ); while (rdebut!= rn) std::cout << *rdebut << std::endl; }

Dans la fonction afcher_en_avant(), les lments sont afchs dans lordre: *debut, *(debut+1), , *(n-1). Dans la fonction afcher_en_arriere() les lments sont afchs du dernier au premier: *(n-1), *(n-1), , *debut. Si lon devait crire la deuxime fonction sur un std:: vector, il faudrait utiliser un std::reverse_iterator.

148

CHAPITRE 8 Itrateurs

Utiliser les itrateurs dinsertion


#include <iterator> class insert_iterator<Conteneur>; insert_iterator<Conteneur> inserter(Conteneur& c, Conteneur::iterator i);

insert_iterator<> est un adaptateur ditrateur qui fonctionne comme un OutputIterator: une opration daffectation sur un insert_iterator<> insre un objet dans une squence. Lexemple suivant insre quelques lments dans une liste:
std::list<int> L; L.push_front(3); std::insert_iterator< std::list<int> > ii(L, L.begin()); *ii++ = 0; *ii++ = 1; *ii++ = 2; std::copy(L.begin(), L.end(), std::ostream_iterator <int>(std::cout, )); // Afche: 0 1 2 3

Lexemple ci-aprs fusionne deux listes tries en insrant le rsultat dans un set. Notez quun set (ou ensemble) ne contient jamais deux lments identiques (doublons).
const int N = 6; int A1[N] = { 1, 3, 5, 7, 9, 11 }; int A2[N] = { 1, 2, 3, 4, 5, 6 }; std::set<int> resultat; std::merge(A1, A1+N, A2, A2+N, std::inserer(resultat, resultat.begin())); std::copy(L.begin(), L.end(), std::ostream_iterator <int>(std::cout, )); // Afche: 1 2 3 4 5 6 7 9 11

Utiliser les itrateurs dinsertion en dbut de conteneur 149

Utiliser les itrateurs dinsertion en dbut de conteneur


#include <iterator> class front_insert_iterator<FrontInsertionSequence>; front_insert_iterator<FrontInsertionSequence> front_inserer(FrontInsertionSequence & c);

front_insert_iterator<> est un adaptateur ditrateur qui fonctionne comme un OutputIterator: une opration daffec tation sur un front_insert_iterator<> insre un objet juste avant le premier lment dune squence. Le conteneur doit supporter linsertion dun lment au dbut par la fonction membre push_front(). Ce conteneur doit donc supporter le concept de FrontInsertionSequence.
std::list<int> L; L.push_front(3); std::front_insert_iterator< std::list<int> > i(L); *i++ = 0; *i++ = 1; *i++ = 2; std::copy(L.begin(), L.end(), std::ostream_iterator <int>(std::cout, )); // Afche: 2 1 0 3

150

CHAPITRE 8 Itrateurs

Utiliser les itrateurs dinsertion en n de conteneur


#include <iterator> class back_insert_iterator<BackInsertionSequence>; back_insert_iterator<BackInsertionSequence> front_inserter(BackInsertionSequence & c);

back_insert_iterator<> est un adaptateur ditrateur qui fonctionne comme un OutputIterator: une opration daffectation sur un back_insert_iterator<> insre un objet juste aprs le dernier lment dune squence. Le conteneur doit supporter linsertion dun lment la n par la fonction membre push_back(). Ce conteneur doit donc supporter le concept de BackInsertionSequence.
std::list<int> L; L.push_front(3); std::back_insert_ierator< std::list<int> > bii(L); *bii++ = 0; *bii++ = 1; *bii++ = 2; std::copy(L.begin(), L.end(), std::ostream_iterator <int>(std::cout, )); // Afche: 3 0 1 2

9
Conteneurs standard
La quasi-totalit des compilateurs modernes fournit une implmentation de la STL (Standard Template Library, bibliothque standard fournie avec tous les compilateurs C++ modernes). Ce chapitre en rcapitule les principales fonctions. Les conteneurs standard fournis par la STL sont articuls autour de quelques grandes familles, derrire lesquelles se retrouvent les itrateurs vus au chapitre prcdent. Pour chacune de ces familles, on retrouve, en bonne partie, les mmes fonctions membres. Le tableau suivant donne la liste des conteneurs STL regroups par famille. Les chanes de caractres seront traites part, au chapitre suivant.
Conteneurs STL par famille
Squentiel basic_string string wstring deque vector Associatif map multimap set multiset Adaptateur priority_queue queue stack bitset

list

152

CHAPITRE 9 Conteneurs standard

Crer un conteneur
#include <header_du_conteneur> Conteneur(); Conteneur(const allocator_type& allocateur); Conteneur(const Conteneur&); Conteneur(size_type n, const value_type& valeur); Conteneur(size_type n, const value_type& valeur, const allocator_type& allocateur); Conteneur(InputIterator deb, InputIterator n); Conteneur(InputIterator deb, InputIterator n, const allocator_type& allocateur); // pour les conteneurs associatifs Conteneur(const _Compare& comparateur_de_cles); Conteneur(const _Compare& comparateur_de_cles, const allocator_type& allocateur); Conteneur(InputIterator deb, InputIterator n, const _Compare& comparateur_de_cles); Conteneur(InputIterator deb, InputIterator n, const _Compare& comparateur_de_cles, const allocator_type& allocateur);

Crer un conteneur se fait toujours de la mme manire. Vous pouvez crer un conteneur vide (constructeur par dfaut) ou pr-initialis avec nvaleur (valeur par dfaut ou donne). Il est galement possible de crer un conteneur en copiant le contenu dun autre, totalement ou partiellement travers les itrateurs. Si un allocateur est fourni, il sera copi dans le conteneur. Sinon, un allocateur par dfaut sera instanci et utilis. Pour les conteneurs associatifs, des constructeurs supplmentaires permettent de spcier un foncteur utilis pour comparer les cls (les conteneurs associatifs sont par essence des conteneurs implicitement tris).

Ajouter et supprimer dans un conteneur squentiel

153

Par exemple, le code suivant cre un vecteur de cinq entiers de valeur 42:
std::vector<int> v(5,42); Choisir son conteneur squentiel Dans un cadre gnral, utiliser std::vector<> est prfrable std::deque<> et std::list<>. Un std::deque<> est utile si vous insrez souvent en dbut ou n de squence. Les std:: list<> et std::slist<> sont plus performants lorsque vous insrez le plus souvent en milieu de squence. Dans la plupart des autres situations, un std::vector<> sera le plus efcace.

Ajouter et supprimer dans un conteneur squentiel


conteneur.insert(iter_pos, valeur); conteneur.insert(iter_pos, n, valeur); conteneur.insert(iter_pos, iter_debut, iter_n); conteneur.push_back(valeur); conteneur.push_front(valeur); conteneur.erase(iter_pos); conteneur.erase(iter_debut, iter_n); conenteur.clear();

Linsertion se fait toujours juste avant la position indique par litrateur. Lorsquune quantitn est fournie en plus dune valeur, cette valeur est insre nfois par copie. Si une squence [iter_debut, iter_n[ est fournie, celle-ci sera copie et insre. L encore, elle sera insre juste avant la position iter_pos.

154

CHAPITRE 9 Conteneurs standard

Info Un appel conteneur.insert(p,n,t) est garanti dtre au moins aussi rapide que dappeler nfois conteneur.insert(p,t). Dans certains cas, il peut mme se rvler tre beaucoup plus rapide.

push_back() et push_front() insrent, respectivement, la valeur donne en n ou en dbut de conteneur. Leur pendant pour la suppression, pop_back() et pop_front() sont disponibles suivant les conteneurs. erase() supprime llment la position iter_pos donne, ou toute la sous-squence [iter_debut, iter_n[ donne. Il va de soi que litrateur ou la squence doivent tre valides et donc, par voie de consquence, pointer sur des lments du conteneur utilis. clear() efface toutes les valeurs du conteneur et le rend vide. Notez toutefois que les structures internes du conteneur ne sont pas forcment libres et peuvent toujours occuper de lespace mmoire. Ce choix provient dun souci defcacit lexcution (les oprations dallocation et dsallocation mmoire sont lentes par nature). Si vous tes dans un contexte ou lespace mmoire est plus important, prenez bien garde ce phnomne.

Parcourir un conteneur
conteneur.begin(); conteneur.end(); conteneur.rbegin(); conteneur.rend();

La STL unie la manire de coder. Cet avantage se retrouve dans la manire de parcourir tous les lments

Parcourir un conteneur 155

dun conteneur. Ainsi, que vous ayez faire un tableau (que lon peut aussi parcourir par indice), une liste ou un arbre ( travers les std::map<> ou les std::set<>) il est possible de procder toujours de la mme manire, grce la notion ditrateur. Vous trouverez dans le code ci-aprs tous les exemples de parcours de conteneur: lendroit ou lenvers. Si vous parcourez des conteneurs passs en arguments constants, il vous sera ncessaire dutiliser des itrateurs dits constants.
std::Conteneur<>::iterator it; for (it = conteneur.begin(); it != conteneur.end(); ++it) { // } std::Conteneur<>::const_iterator it; for (it = conteneur.begin(); it != conteneur.end(); ++it) { // } std::Conteneur<>::reverse_iterator it; for (it = conteneur.rbegin(); it != conteneur.rend(); ++it) { // } std::Conteneur<>::const_reverse_iterator it; for (it = conteneur.rbegin(); it != conteneur.rend(); ++it) { // }

Lorsque vous parcourez un conteneur associatif, vous rencontrez les lments selon lordre croissant (suivant la relation dordre fournie au type de conteneur) de leur cl.

156

CHAPITRE 9 Conteneurs standard

Dans lexemple ci-aprs, nous remplissons un std:: set<int> avec quelques valeurs dans un ordre quelconque et constatons que lors du parcours, nous les retrouvons dans lordre croissant.
std::set<int> S; S.insert(7); S.insert(5); S.insert(9); S.insert(1); S.insert(3); for (std::set<int>::const_iterator it = S.begin(); it != S.end(); ++it) { std::cout << *it << ; } std::cout << std::endl; // Afche: 1 3 5 7 9

Accder un lment dun conteneur


conteneur.at(i); conteneur[i]; conteneur.back(); conteneur.front(); conteneur.count(cle); conteneur.nd(cle);

Ces fonctions ne sont pas valables pour tous les conteneurs; leur existence dpend du type de conteneur. Le tableau suivant prcise la disponibilit de chacune delle pour chaque conteneur.

Accder un lment dun conteneur 157

Disponibilit des fonctions par conteneur


Fonction at(i) Conteneurs std::basic_string<>, std::deque<>, std::vector<>

conteneur[] std::basic_string<>, std::deque<>, std::vector<> std::map<>, std::multimap<>, std::set<>, std::multiset<>1 back() front() std::deque<>, std::list<>, std::vector<> std::deque<>, std::list<>, std::vector<>

count(cle) std::map<>, std::multimap<>, std::set<>, std::multiset<> nd(cle) std::map<>, std::multimap<>, std::set<>, std::multiset<>

1. Attention, en plus daccder llment, celui-ci sera cr sil nexiste pas. Si vous ne voulez pas crer une entre, utilisez nd(cle) plutt que conteneur[cle] pour les conteneurs associatifs.

La fonction count() est un moyen simple de tester si une cl est prsente dans un conteneur associatif. Toutefois, si vous souhaitez tester la prsence de la cl puis ventuellement utiliser la valeur associe, utilisez plutt un nd(), comme lillustre le code suivant:
std::map<int,int> M; // std::map<int,int>::iterator it = M.nd(3); if (it != M.end()) { std::cout << 3 existe et est associ << it->second << std::endl << La valeur de la cl est bien << it->rst << std::endl; } else std::cout << La cl 3 nest pas prsente dans M.\n;

158

CHAPITRE 9 Conteneurs standard

Crer et utiliser un tableau


#include <vector> std::vector<Type> V;

Les vecteurs de la STL sont des vecteurs dynamiques (leur taille peut changer au cours de la vie du programme). Ils font parti des conteneurs dits squentiels. De fait, linsertion et la suppression dlments la n dun vecteur seffectuent en temps quasi constant. En dbut ou en tout autre endroit qu la n, le temps de ces oprations sera proportionnel au nombre dlments prsents aprs le lieu dinsertion. Le type ditrateur associ aux vecteurs est de type RandomAccesIterator, autrement dit accs direct.
Attention La bibliothque STL spcialise le type std::vector<bool>. Celui-ci permet de stocker linformation au niveau du bit par lintermdiaire de masques. Si cela lavantage dutiliser trs peu de mmoire, il en cote un trs fort impact sur les performances. Si les performances sont primordiales pour vous, prfrez-lui un std::vector<unsigned char>.

Le dernier constructeur cre un vecteur contenant une copie des lments entre debut et n. Considrez lexemple suivant:
// Cre un vecteur dentiers alatoires std::vector<int> v(10); std::cout << vecteur original:; for (int i=0; i<v.size(); i++) std::cout << (v[i] = (int) rand() % 10) << ; std::cout << std::endl;

Crer et utiliser un tableau 159

// trouver le premier lment pair du tableau std::vector<int>::iterator it1 = v.begin(); while (it1!=v.end() && *it1%2!=0) it1++; // trouver le dernier lment pair du tableau std::vector<int>::iterator it2 = v.end(); do { it2--; } while (it1!=v.begin() && *it1%2!=0); // si au moins un lment trouv if (it1 != v.end()) { std::cout << premier nombre pair: << *it1 << , dernier nombre pair: << *it2 << std::endl << nouveau vecteur: ; std::vector<int> v2(it1, it2); for (int i=0; i<v2.size(); i++) std::cout << v2[i] << ; std::cout << std::endl; }

Son excution produira la sortie suivante:


vecteur original: 1 7 8 3 4 2 0 8 6 5 premier nombre pair: 8, dernier nombre pair: 6 nouveau vecteur: 8 3 4 2 0 8 6

Attention Les itrateurs sur les vecteurs ne sont gnralement pas stables linsertion ou la suppression dlments. En effet, les lments tant stocks de manire contigu, ceux-ci peuvent physiquement changer de place en mmoire et les itrateurs devenir invalides. Mme linsertion en n de vecteur: cette opration peut ncessiter une augmentation de la taille rserve impliquant le dplacement de tous les lments.

160

CHAPITRE 9 Conteneurs standard

Info Pour de petites squences, les vecteurs sont plus performants que les listes. Si donc vous utilisez de petites squences et que la stabilit des itrateurs nest pas importante pour vous, prfrez-les aux listes.

Crer et utiliser une liste chane


#include <list> std::list<Type> L;

Les listes STL sont des listes doublement chanes. La plupart des implmentations utilisent un chanon sans donne comme tte/queue de liste. Un chanon correspond un itrateur. Le chanon de tte se retrouve travers un appel de la fonction membre end(). Les itrateurs des listes sont garantis comme stables linsertion et la suppression (pour autant que ce ne soit pas celui que vous supprimiez).
Attention Dans certaines implmentations de la STL, un itrateur de liste est plus quun pointeur sur un maillon, et contient aussi dautres informations. Cest par exemple le cas avec limplmentation de Microsoft Visual C++8, o il peut atteindre la taille de trois pointeurs en mode debug (celui sur le maillon plus un sur la liste et un autre sur le conteneur, mais qu importe puisque leur implmentation a encore chang avec VC++9).

Crer et utiliser une le double entre

161

Le parcours dune liste chane seffectue grce aux itrateurs:


std::list<int> L; // for (std::list<int>::iterator it = L.begin(); it != L.end(); ++it) { // accs llment // avec (*it).methode() ou it->methode() }

Vous pouvez facilement parcourir une liste lenvers laide des itrateurs inverss:
std::list<int> L; // for (std::list<int>::reverse_iterator it = L.rbegin(); it != L.rend(); ++it) { // accs llment // avec (*it).methode() ou it->methode() }

Si vous tes dans une fonction o la liste que vous utilisez est fournie en rfrence constante, il vous faudra utiliser des itrateurs constants (par exemple, std::list<int>:: const_iterator).

Crer et utiliser une le double entre


#include <deque> std::deque<Type> D;

Les les doubles entres sont un peu comme des piles dans lesquelles il est possible dajouter et de retirer un lment

162

CHAPITRE 9 Conteneurs standard

au dbut (avec push_front() et pop_front()) ou la n (avec push_back() et pop_back()). Laccs au dbut et la n se fait avec front() et back(). De plus, laccs par indice demeure possible et reste en temps constant travers loprateur [] et la fonction at() qui fournit en plus un contrle des bornes. La stabilit des itrateurs que vous auriez stocks nest pas garantie, sauf en cas de suppression en tte ou en queue de le.
Astuce Les les double entre permettent de coder facilement des conteneurs First In First Out (FIFO), ou premier entr, premier sorti. Pour ce faire, il suft dutiliser push_front() et pop_ pack() conjointement. Le code suivant pourrait tre un moyen trs simple de la simuler laide dune std::deque<>: template <class T> struct FIFO { inline T pop() { return m_values.pop_back(); } inline void push(const T& v) { m_values.push_front(v); } protected: std::deque<T> m_values; }; Mais ne vous donnez pas cette peine! Pour les conteneurs FIFO, la STL fournie les std::queue<>. Et pour les conteneurs LIFO, vous disposez des std::stack<> (piles).

Crer et utiliser une pile 163

Crer et utiliser une pile


#include <stack> std::stack<Type,Conteneur> S; std::stack<Type> S;

Une pile supporte linsertion et la suppression dlments une seule extrmit travers push() et pop(). Du coup, on retrouve la rgle du dernier insr, premier sorti, typique dun conteneur LIFO (Last In First Out). Laccs au dernier lment empil se fait avec top(). Vous pouvez dnir une pile avec nimporte quel conteneur squentiel comme std::deque<>, std::list<> ou std::vector<>. Si vous ne spciez pas le type de conteneur utiliser, un std::deque<> le sera par dfaut.
Attention Le constructeur dune pile prenant pour argument un conteneur (dont le type doit correspondre au type de conteneur spci dans linstanciation) neffectue pas un lien vers ce conteneur pour lutiliser mais copie bel et bien tous ses lments au sein de la pile ainsi cre. std::deque<int> D; D.push_back(1); D.push_back(2); D.push_back(3); std::stack<int> S(D); // S contient 1 2 3 D[1] = 20; // D contient 1 20 3 // S contient toujours 1 2 3

164

CHAPITRE 9 Conteneurs standard

Crer et utiliser une queue


#include <queue> std::queue<Type,Conteneur> Q; std::queue<Type> Q;

Une queue supporte linsertion dlments en dbut de conteneur (avec push()) et la suppression dlments en n de conteneur (avec pop()). On retrouve la rgle du premier insr, premier sorti typique dun conteneur FIFO (First In First Out). Laccs au dernier lment empil se fait avec front() et laccs au prochain lment dpil avec back(). Vous pouvez dnir une queue avec nimporte quel conteneur squentiel comme std::deque<>, std::list<> ou std::vector<>. Si vous ne spciez pas le type de conteneur utiliser, un std::deque<> le sera par dfaut. Pour dterminer si une queue est vide, utilisez empty(). Pour connatre le nombre dlments quelle contient, utilisez size().
Info Les piles, queues et queues de priorits sont des adaptateurs, bass sur des conteneurs de la STL. Si vous voulez crer vos propres conteneurs en les basant sur des conteneurs (ou adaptateurs) de la STL, nhsitez pas regarder comment ces classes sont crites. Vous pourrez glaner ainsi beaucoup de techniques et ides utiles pour maximiser leur compatibilit avec la STL et prenniser votre code.

Crer et utiliser une queue de priorit 165

Crer et utiliser une queue de priorit


#include <priority_queue> std::priority_queue<Type,Conteneur,RelationDOrdre> PQ; std::priority_queue<Type,Conteneur> PQ; std::priority_queue<Type> PQ;

Une queue de priorit est une queue un peu particulire: elle classe ses lments de telle sorte que le prochain lment dpiler corresponde llment le plus prioritaire. Un lment est dni comme plus prioritaire sil se trouve en dbut de liste aprs ordonnancement suivant la relation dordre fournie (la relation dordre par dfaut est std:: less<Type>). Le conteneur utilis par dfaut est un std::vector<>.Vous pouvez utiliser nimporte quel conteneur supportant laccs direct (cest--dire par indice). En effet, pour classer ses lments, un tel conteneur utilise les algorithmes std:: make_heap(), std::push_heap() et std::pop_heap() qui reposent sur ce concept. Linsertion se fait avec push(), et la suppression ou dpilement avec pop(). Linterrogation de llment le plus prioritaire se fait avec top(). Comme dhabitude, size() retourne le nombre dlments prsents et empty()indique si le conteneur est vide.
Attention
pop() ne renvoie pas llment mais se contente de supprimer

llment le plus prioritaire du conteneur.

166

CHAPITRE 9 Conteneurs standard

Crer et utiliser un ensemble


#include <set> std::set<Type, RelationDOrdre> S; std::set<Type> S; std::multiset<Type, RelationDOrdre> MS; std::multiset<Type> MS;

Les ensembles, simples ou multiples, sont des conteneurs associatifs simples: ils nont pas de valeur associe chaque cl, la cl elle-mme faisant ofce de valeur. Ce sont des conteneurs tris. Un ensemble simple std::set<> ne peut pas contenir deux lments identiques, alors quun ensemble multiple std::multiset<> lautorise. Les itrateurs sur les ensembles sont stables linsertion et la suppression dlments, lexception (bien sr) de ceux pointant sur des lments retirs de lensemble considr. Pour insrer des lments, utilisez insert(elt). Cette fonction renvoie une std::pair<iterator,bool> contenant lemplacement de la valeur insre si second vaut vrai, ou de lemplacement de la valeur dj existante si second vaut faux. Si vous connaissez dj lendroit o insrer la valeur, utilisez plutt insert(iter_pos,valeur) qui garantit dans ce cas une insertion en temps constant.

Crer et utiliser une table associative 167

Crer et utiliser une table associative


#include <map> std::map<Cle, Type, RelationDOrdre> M; std::map<Cle, Type> M; std::multimap<Cle, Type, RelationDOrdre> M; std::multimap<Cle, Type> M;

Les tables associatives permettent dassocier une cl une (pour les std::map<>) ou plusieurs valeurs (pour les std:: multimap<>) dun Type donn. La relation dordre utilise par dfaut est le foncteur std::less<Cle>, qui utilise loprateur< par dfaut. Pour chercher un lment sans crer dentre dans le conteneur, nutilisez pas loprateur[] mais plutt la fonction membre nd() qui renvoie un itrateur sur llment trouv ou litrateur end() sinon. Lexemple suivant montre comment obtenir toutes les valeurs associes une cl dans un std::multimap<>.
typedef std::multimap<TypeCle,TVal>::const_iterator
ConstIter;

std::pair<ConstIter,ConstIter> bound = mm.equal_range (ma_cle); for (ConstIter it = bound.rst; it != bound.second; ++it) //

168

CHAPITRE 9 Conteneurs standard

Lexemple dutilisation suivant illustre comment retrouver le nombre de jours dans un mois en fonction du nom du mois:
std::map<std::string, int> mois; std::map<std::string, int>::iterator it_cour, it_avant, it_apres; mois[std::string(janvier)] = 31; mois[std::string(fvrier)] = 28; mois[std::string(mars)] = 31; // mois[std::string(dcembre)] = 31; std::cout << mars : << mois[std::string(mars)] << std::endl; it_cour = mois.nd(june); it_avant = it_apres = it_cour; ++it_apres; --it_avant; std::cout << Avant (en ordre alphabtique) : << it_avant->rst << std::endl; std::cout << Aprs (en ordre alphabtique) : << it_apres->rst << std::endl;

Linsertion dans les std::multimap<> est un peu moins intuitive. Lexemple suivant montre comment utiliser ce conteneur:
struct ltstr { bool operator()(const char* s1, const char* s2) const { return strcmp(s1, s2) < 0; } }; // std::multimap<const char*, int, ltstr> m; std::multimap<const char*, int, ltstr>::iterator it;

Crer et utiliser une table associative 169

m.insert(std::pair<const m.insert(std::pair<const m.insert(std::pair<const m.insert(std::pair<const m.insert(std::pair<const m.insert(std::pair<const

char* char* char* char* char* char*

const, const, const, const, const, const,

int>(a, int>(c, int>(b, int>(b, int>(a, int>(b,

1)); 2)); 3)); 4)); 5)); 6));

std::cout << Nombre dlments avec a pour cle : << m.count(a) << std::endl; std::cout << Nombre dlments avec b pour cle : << m.count(c) << std::endl; std::cout << Nombre dlments avec c pour cle : << m.count(c) << std::endl; std::cout << Nombre dlments dans m : << std:: endl; for (it = m.begin(); it != m.end(); ++it) { std::cout << < << it->rst << , << it->second << > << std::endl; }

Info Les std::map<> et std::multimap<> sont gnralement bases sur des arbres binaires quilibrs. Linsertion et la recherche dlments seffectue donc en temps O(logn). Si cela est sufsant dans bien des cas, ce peut tre pnalisant dans dautres. Si donc vous avez besoin de meilleures performances, vous pouvez vous tourner, au prix dune occupation mmoire plus importante, vers dautres solutions. Avant de dvelopper les vtres, essayez les tables de hachage fournies par la STL. Leur temps daccs et dinsertion est en temps quasi constant, comme cela est expliqu la section suivante.

170

CHAPITRE 9 Conteneurs standard

Crer et utiliser une table de hachage


#include <hash_set> std::hash_set<Cle[,FonctionDeHash[,TestEgaliteCle]]> M; std::hash_multiset<Cle[,FonctionDeHash[, TestEgaliteCle]]> M; #include <hash_map> std::hash_map<Cle,Type[,FonctionDeHash[, TestEgaliteCle]]> M; std::hash_multimap<Cle,Type[,FonctionDeHash[, TestEgaliteCle]]> M;

Attention Avec g++, vous trouverez ces classes dans <ext/hash_set> et <ext/hash_map>.

Les tables de hachage sont plus performantes que les conteneurs associatifs (map et set) mais nordonnent pas les lments en fonction de leur cl. Elle repose sur le calcul dune sous-cl de type entier pour donner un indice dans un tableau contenant (suivant le type de table de hachage utilis) des maps ou des sets multiples ou non.

Crer et utiliser une table de hachage

171

Le foncteur de test dgalit des cls par dfaut est std:: equal_to<Cle>. La fonction de hachage par dfaut est le foncteur std::hash<Cle>. Lutilisation des tables de hachage est comparable celle des tables associatives et des ensembles suivant le cas. Les fonctions de hachage par dfaut supportent les types suivants: char*, const char*, char, signed char, unsigned char, short, unsigned short, int, unsigned int, long et unsigned long, plus std::crope<> et std::wrope<> pour une gestion efcace au niveau des chanes de caractres. Si vous voulez crire vos propres fonctions de hachage pour vos types personnaliss, sachez que ce foncteur doit imprativement retourner un std::size_t, comme le montre le modle suivant:
struct MonHash { std::size_t operateor()(const MaCle& cle) { // calcul } };

172

CHAPITRE 9 Conteneurs standard

Connatre la complexit des fonctions membres des conteneurs


Comparaison des complexits
Groupe Itrateurs Capacit Accs Fonction vector deque * O(n) O(n) O(1) O(1) O(1) O(1) O(1) O(1) O(1) O(n) O(1) O(1) - O(1) O(1) O(n) list * O(n) O(n) O(1) O(1) O(1) O(1) O(1)1 O(1)
1

set * O(n) O(n) O(1) O(1) O(1) O(1) O(1) O(1) O(1) - - - - - - - Log3

multiset * O(n) O(n) O(1) O(1) O(1) O(1) O(1) O(1) O(1) - - - - - - - Log3

Constructeur * Destructeur operateur= begin end rbegin rend size max_size empty resize front back top operator[] at O(n) O(n) O(1) O(1) O(1) O(1) O(1) O(1) O(1) O(n) O(1) O(1) - O(1) O(1) O(n)

O(1) O(n) O(1) O(1) - - - O(n) O(m)

Modieurs assign insert

O(n+m) O(m)2

Connatre la complexit des fonctions membres des conteneurs 173

map * O(n) O(n) O(1) O(1) O(1) O(1) O(1) O(1) O(1) - - - - Log - - Log3

multimap * O(n) O(n) O(1) O(1) O(1) O(1) O(1) O(1) O(1) - - - - - - - Log3

bitset * - O(1)4 - - - - O(1) - - - - - - O(1)4 - - -

stack * - - - - - - O(1) - O(1) - - - O(1) - - - -

queue * - - - - - - O(1) - O(1) - O(1) O(1) - - - - -

priority_queue * O(1) O(1) O(1) -

174

CHAPITRE 9 Conteneurs standard

Comparaison des complexits (Suite)


Groupe Fonction erase swap clear push_front pop_front push_back pop_back push pop vector O(n)5 O(1) O(n) - - O(1) O(1) - - - - - - - - - deque O(m)6 O(1) O(n) O(1) O(1) O(1) O(1) - - - - - - - - - list O(m) O(1) O(n) O(1) O(1) O(1) O(1) - - - - - - - - - set O(1)+7 O(1) O(n) - - - - - - O(1) O(1) Log Log Log Log Log multiset O(1)+7 O(1) O(n) - - - - - - O(1) O(1) Log Log Log Log Log

Observeurs key_comp value_comp

Oprations nd count lower_bound upper_bound equal_range

1. En O(n) dans certaines implmentations. 2. En O(n+m) dans certaines implmentations (mest le nombre dlments insrer). 3. insert(x) est logarithmique (O(log n)); insert(position) est en gnral logarithmique (O(log n)), mais peut tre en temps constant amorti (O(1)+) six est insr juste aprs llment point par position; insert(rst, last) est gnralement en mx log(n+m), o mest le nombre dlments insrer et nla taille du conteneur avant insertion, mais linaire (O(m)) si les lments insrs sont tris avec le mme critre que le conteneur.

Connatre la complexit des fonctions membres des conteneurs 175

map O(1)+7 O(1) O(n) - - - - - - O(1) O(1) Log Log Log Log Log

multimap O(1)+7 O(1) O(n) - - - - - - O(1) O(1) Log Log Log Log Log

bitset - - - - - - - - - - - - - - - -

stack - - - - - - - O(1) O(1) - - - - - - -

queue - - - - - - - O(1) O(1) - - - - - - -

priority_queue O(1) O(1) -

4. Avec un surcot non ngligeable d au calcul de dcalage et de masque. 5. Linaire en fonction du nombre dlments effacer (O(m)), plus le nombre dlments dplacer aprs le dernier lment effac (O(m+npos)). 6. Linaire en fonction du nombre dlments effacs (O(m)). Dans certaines implmentations, ajoute galement un temps linaire au nombre dlments restant aprs les lments supprims (O(m+n-pos)). 7. erase(position) est en temps constant amorti (O(1)+); erase(x) est en O(log n); erase(rst,last) est en O(log n) + O(m).

176

CHAPITRE 9 Conteneurs standard

Fonctions spciques Conteneur Fonctions vector list bitset capacity, reserve, splice splice, reserve, remove, remove_if, unique, merge, sort set, reset, ip, to_ulong, to_string, test, any, none

10
Chanes de caractres
Les chanes de caractres sont utilises dans pratiquement tous les programmes informatiques, du moins la quasitotalit de ceux communiquant avec lhomme. Manipuler de simples pointeurs sur des char (ou wchar_t) est non seulement risqu (dbordement de mmoire) mais devient totalement caduc lorsque lon dsire rendre son programme polyglotte. Pour ce faire, la STL fournit diffrent types de chanes de caractres sintgrant parfaitement avec les algorithmes de la STL (le contraire aurait t surprenant) mais surtout facilitant la vie du programmeur. Dautres implmentations existent comme les QString de QT ou les wxString de wxWidgets. Ils ont parfois des avantages par rapport ceux de la STL (ne serait-ce que leur intgration au sein de leur bibliothque respective) mais aussi leurs inconvnients. Il faut donc opter pour lun ou lautre en fonction de vos besoins. Il nest pas rare de les voir cohabiter dans des programmes dune certaine envergure.

178

CHAPITRE 10 Chanes de caractres

Crer une chane


#include <string> std::string une_chaine; #include <wstring> std::wstring une_chaine_unicode;

Les chanes de caractres de la STL ne sont pas forcment des chanes AZT ( zro terminal). Elles dpassent cette limite en embarquant simplement le nombre de caractres. On obtient ainsi directement la taille de ces chanes avec la fonction membre size(). Notez quil sagit bien ici du nombre de caractres et non du nombre doctets. Les std:: string<> stockent des chanes dont le jeu de caractres stend sur 8bits maximum, et les std::wstring<> sont ddies aux chanes utilisant un jeu de caractres tendus comme Unicode.
Info Si vous manipulez de trs grosses chanes de caractres, examinez les std::crope<> et std::wrope<>. #include <rope> // <ext/rope> sous G++ std::crope c; std::wrope w; Elles sont stockes sous une forme volutive conue pour rendre efcace les traitements impliquant la chane dans son ensemble. Ainsi, des oprations comme laffectation, la concat nation et lextraction de sous-chanes prendront un temps presque indpendant de la taille de la chane. Par contre, remplacer un caractre dans une rope est coteux, de mme que la parcourir caractre par caractre. Cest un peu le revers de la mdaille (sinon quelle serait lutilit des string?).

Crer une chane

179

Les constructeurs de chanes de caractres supportent plusieurs formes dinitialisation. Lexemple suivant en dresse la liste et leurs ventuelles limitations:
std::string s1; // une chane vide std::string s2(abc\x00def); // contiendra abc std::string s3 = abc\x00def; // contiendra abc std::string s4(abc\x00def,6); // contiendra abc\x00de std::string s5(5,a); // contiendra aaaaa std::string s6(s5); // contiendra une copie de s5 std::string s7(s4,2,3); // contiendra c\x00d std::set<char> aSet; aSet.insert(e); aSet.insert(a);aSet.insert(z); std::string s8(aSet.begin(),aSet.end()); // contiendra aez s1 = s3; // contiendra une copie de s3

Si vous avez besoin dobtenir une conversion en chane AZT dune std::string, utilisez la fonction membre c_str() qui ajoutera un caractre nul si il ny tait pas (sans modier la taille de votre chane), avant de retourner un pointeur sur le premier caractre. Attention toutefois, si votre chane contient dj en son sein un caractre nul, vous retrouverez les mmes restrictions quavec les chanesC classiques
Astuce Si vous avez besoin de rinitialiser une chane, utilisez les fonctions membres assign(). Elles prennent les mmes arguments que les diffrents constructeurs mais offrent lavantage dviter de crer un objet temporaire dans certains cas.

180

CHAPITRE 10 Chanes de caractres

Connatre la longueur dune chane


#include <string> bool string::empty(); bool string::length(); bool string::size();

La fonction empty() indique si la chane est vide. Les fonctions length() et size() sont identiques et renvoient la longueur de la chane.

Comparer des chanes


int string::compare(const string& s2); int string::compare(const char* s2); int string::compare(size_type index, size_type len, const string& s2); int string::compare(size_type index, size_type len, const string& s2, size_type index_s2, size_type len_s2); int string::compare(size_type index, size_type len, const char* s2, size_type len_s2);

La fonction membre compare() permet de comparer la chane considre (s1) avec une autre (s2). Si le rsultat est ngatif, alors s1 < s2. Si le rsultat est nul, alors s1 == s2. Enn si le rsultat est positif, alors s1 > s2. Notez que ces oprateurs de comparaison (<, > et ==) existent galement, mais prenez garde lallocation dynamique si vous comparez une chane un char*.

changer le contenu de deux chanes

181

std::string noms[] = { Alfred, Robin, 5e lment, inconnu }; for (int i=0; i<4; ++i) { for (int j=0; j<4; ++j) std::cout << noms[i].compare( noms[j] ) << ; std::cout << std::endl; }

changer le contenu de deux chanes


void string::swap(string& s2); void swap(string& s1, string& s2);

Les fonctions swap() sont beaucoup plus performantes que dutiliser une variable temporaire de type std::string. Elles effectuent un change de la taille et du pointeur interne plutt que de recopier toutes les chanes, ce qui est videmment beaucoup plus rapide.
std::string s1 = Premire chane; std::string s2 = Deuxime contenu; s1.swap(s2); std::cout << s1 << std::endl; // Deuxime contenu std::cout << s2 << std::endl; // Premire chane

182

CHAPITRE 10 Chanes de caractres

Rechercher une sous-chane


size_type string::nd(const string& str, size_type
index);

size_type string::nd(const char* str, size_type index); size_type string::nd(const char* str, size_type index, size_type length); size_type string::nd(char ch, size_type index); size_type string::rnd(const string& str, size_type
index);

size_type string::rnd(const char* str, size_type index); size_type string::rnd(const char* str, size_type index, size_type num); size_type string::rnd(char ch, size_type index); size_type string::nd_rst_not_of(const string&
str, size_type index = 0);

size_type string::nd_rst_not_of(const char* str, size_type index = 0 ); size_type string::nd_rst_not_of(const char* str, size_type index, size_type num); size_type string::nd_rst_not_of(char ch, size_type index = 0); size_type string::nd_rst_of(const string &str,
size_type index = 0);

size_type string::nd_rst_of(const char* str, size_type index = 0); size_type string::nd_rst_of(const char* str, size_type index, size_type num); size_type string::nd_rst_of(char ch, size_type index = 0);

Rechercher une sous-chane 183

size_type string::nd_last_not_of(const string& str,


size_type index = npos);

size_type string::nd_last_not_of(const char* str, size_type index = npos ); size_type string::nd_last_not_of(const char* str, size_type index, size_type num); size_type string::nd_last_not_of(char ch, size_type index = npos); size_type string::nd_last_of(const string &str,
size_type index = npos);

size_type string::nd_last_of(const char* str, size_type index = npos); size_type string::nd_last_of(const char* str, size_type index, size_type num); size_type string::nd_last_of(char ch, size_type index = npos);

Dans tous les cas, ces fonctions renvoient soit lindice de la position dans la chane considre (celle qui correspond au this de la fonction membre), soit string::npos si rien nest trouv. Les fonctions nd() renvoient lindice o se trouve la premire occurrence de la chane str ou du caractre ch recherch. Lorsque le paramtre length est donn, la fonction ne recherchera que les length premiers caractres de str. Le paramtre index permet de prciser le point de dpart de la recherche (ceci permet de trouver les occurrences suivantes). Les fonctions rnd() effectuent la recherche en remontant vers le dbut de la chane en partant de lindex donn.
std::string str1( Alpha Beta Gamma Delta ); std::string::size_type loc = str1.nd( Omega, 0 ); if( loc != std::string::npos ) std::cout << Found Omega at << loc << std::endl; else std::cout << Didnt nd Omega << std::endl;

184

CHAPITRE 10 Chanes de caractres

Les fonctions nd_rst_not_of() recherchent le premier caractre nappartenant pas str (ou diffrent de ch). L encore, il est possible de ne considrer que les num premiers caractres de str, et ne commencer la recherche qu lindex voulu. Les fonctions nd_rst_of() recherchent le premier caractre appartenant str. Les fonctions nd_last_not_of() et nd_last_of() sont comme ce que rnd() est nd(), mais recherchent nimporte quel caractre de la chane plutt que la chane elle-mme. Lexemple ci-aprs recherche le premier caractre qui ne soit pas une minuscule. Il afchera la valeur33.
std::string minuscule = abcdefghijklmnopqrstuvwxyz ,-; std::string chaine = ceci est la partie en minuscule, ET CELLE-CI EST LA PARTIE EN MASJUCULE; std::cout << la premire lettre non minuscule dans la chane est lindice : << chaine.nd_rst_not_of (minuscule) << std::endl;

Extraire une sous-chane


string string::substr(size_type index, size_type
length = npos);

La fonction substr() retourne la sous-chane commenant lindice index et de taille length. Si ce dernier paramtre nest pas fourni, la sous-chane contiendra tous les caractres suivant lindex donn, jusqu la n.
Attention La sous-chane renvoye est une copie des caractres et nest pas un lien vers une sous-partie de la chane dorigine.

Remplacer une partie dune chane 185

Rien ne vaut un exemple pour comprendre immdiatement. En voici donc un trs simple:
std::string s(What we have here is a failure to communicate); std::string sub = s.substr(21); std::cout << La chane originale est : << s << std::endl; std::cout << La sous-chane est : << sub << std::endl;

Il produira le rsultat suivant:


La chane originale est : What we have here is a
failure to communicate

La sous-chane est : a failure to communicate

Remplacer une partie dune chane


string& string::replace(size_type index, size_type num, const string& str); string& string::replace(size_type index1, size_type num1, const string& str, size_type index2, size_type num2); string& string::replace(size_type index, size_type num, const char* str); string& string::replace(size_type index, size_type num1, const char* str, size_type num2); string& string::replace(size_type index, size_type num1, size_type num2, char ch); string& string::replace(iterator start, iterator end, const string& str); string& string::replace(iterator start, iterator end, const char* str); string& string::replace(iterator start, iterator end, const char* str, size_type num); string& string::replace(iterator start, iterator end, size_type num, char ch);

186

CHAPITRE 10 Chanes de caractres

Les fonctions replace() font, au choix, les actions suivantes: remplacer les caractres de la chane courante avec au plus num caractres de str, en commenant lindex donn; remplacer jusqu' num1 caractres de la chane courante (en commenant lindex1) avec au plus num2 caractres de str en partant de lindex2; remplacer jusqu' num caractres de la chane courante par ceux de str, en commenant lindice index; remplacer jusqu' num1 caractres de la chane courante (en partant de lindice index1) par les num2 caractres de str partir de lindice index2; remplacer jusqu' num1 caractres de la chane courante (en commenant lindice index) avec num2 copies du caractre ch; remplacer les caractres de la chane courante depuis start jusqu end avec la chane str; remplacer les caractres de la chane courante depuis start jusqu end avec les num premiers caractres de str; remplacer les caractres de la chane courante depuis start jusqu end avec num copies du caractre ch. Par exemple, le code suivant afche la chane Ils disent que a ressemble un truc trs cool,Vincent.
std::string s = Ils disent que a ressemble ... un
trs GROS truc!;

std::string s2 = un truc trs cool, Vincent.; s.replace( 32, s2.length(), s2 ); std::cout << s << std::endl;

Insrer dans une chane 187

Insrer dans une chane


iterator string::insert( iterator it, const char&
ch );

string& string::insert( size_type index, const string& str ); string& string::insert( size_type index, const char* str ); string& string::insert( size_type index1, const string& str, size_type index2, size_type num ); string& string::insert( size_type index, const char* str, size_type num ); string& string::insert( size_type index, size_type num, char ch ); void string::insert( iterator it, size_type num, const char& ch ); void string::insert( iterator it, iterator debut, iterator n );

Ces diffrentes versions de insert() permettent dinsrer dans la chane courante: le caractre ch avant la position indique par litrateur it; la chane str lindice index; la sous-chane str (commenant lindice index2 et de longueur num), lindice index1; les num premiers caractres de str, lindice index; num fois le caractre ch, lindice index; num fois le caractre ch, avant la position indique par litrateur it; les caractres de la squence [debut, n[, avant la position indique par litrateurit.

188

CHAPITRE 10 Chanes de caractres

Concatner des chanes


string& string::append( const string& str ); string& string::append( const char* str ); string& string::append( const string& str, size_type index, size_type len ); string& string::append( const char* str, size_type num ); string& string::append( size_type num, char ch ); string& string::append( input_iterator start, input_iterator end );

Les diffrentes fonctions append() permettent dajouter la n de la chane courante: la chane str; la sous-chane de str commenant lindice index et de longueur len; les num premiers caractres de str; num fois le caractre ch; les caractres contenus dans la squence [debut, n[. Par exemple, le code ci-aprs utilise append() pour ajouter dix points dexclamation une chane. Il afchera la chane Bonjour le monde!!!!!!!!!!.
std::string str = Bonjour le monde; str.append(10, !); std::cout << str << std::endl;

Effacer une partie dune chane 189

Cet autre exemple ajoute une sous-chane une autre chane. Il afchera str1 vaut : Une premire chane rallonge.
std::string str1 = Une premire chane...; std::string str2 = qui peut tre rallonge...; str1.append(str2, 16, 11); std::cout << str1 vaut : << str1 << std::endl;

Effacer une partie dune chane


iterator string::erase( iterator it ); iterator string::erase( iterator debut, iterator n ); string& string::erase( size_type index = 0, size_type num = npos );

Les fonctions erase() permettent deffacer: le caractre point par l'itrateur it, puis de renvoyer litrateur sur le caractre suivant; les caractres de la sous-squence [debut, n[, puis de renvoyer litrateur pointant sur le caractre suivant le dernier supprim; les num caractres partir de lindice index, puis de renvoyer une rfrence sur la chane elle-mme. Les paramtres index et num ont des valeurs par dfaut et peuvent tre omis. Si vous ne spciez pas num, tous les caractres aprs lindice index inclus seront supprims. Si vous nindiquez aucun des deux, toute la chane sera vide; cest lquivalent dun clear().

190

CHAPITRE 10 Chanes de caractres

std::string s(Alors, on aime les beignets ? Il faut


beignetiser le monde entier !);

std::cout << La chane originale est << s << <<


std::endl;

s.erase( 50, 16 ); std::cout << Maintenant cest << s << << std::endl; s.erase( 29 ); std::cout << Maintenant cest << s << << std::endl; s.erase(); std::cout << Maintenant cest << s << << std::endl;

Le code ci-avant produira la sortie ci-aprs.


The original string is Alors, on aime les beignets ?
Il faut beignetiser le monde entier !

Maintenant cest Alors, on aime les beignets ? Il faut


beignetiser !

Maintenant cest Alors, on aime les beignets ? Maintenant cest

Lire des lignes dans un ux


istream& getline(istream& is, string& s, char
delimiteur = \n );

La fonction getline() nest pas une fonction membre de std::string<> mais une fonction globale. Elle lit une ligne dans le uxis et stocke les caractres lus dans la chanes. Lire une ligne consiste lire tous les caractres qui se prsentent jusqu en rencontrer un gal au delimiteur.

Lire des lignes dans un ux

191

Par exemple, le code suivant lit une ligne sur lentre standard et lafche sur la sortie standard:
std::string s; std::getline( std::cin, s ); std::cout << Vous avez ecrit : << s << std::endl;

Aprs avoir rcupr le contenu dun ux dans une chane, vous trouverez probablement utile dutiliser un std::string_stream pour en extraire certains types dinformation. Par exemple, le code ci-aprs lit des nombres sur lentre standard, tout en ignorant les lignes commentes commenant par//.
string s; while ( std::getline(std::cin,s) ) { if ( s.size() >= 2 && s[0] == / && s[1] == / ) { std::cout << * commentaire ignor : << s << std::endl; } else { std::istringstream ss(s); double d; while ( ss >> d ) { std::cout << * un nombre : << d << std:: endl; } } }

192

CHAPITRE 10 Chanes de caractres

Avec ce code, vous pouvez, en entrant les mmes valeurs, obtenir le rsultat suivant:
// test * commentaire ignor : // test 22.3 -1 3.13149 * un nombre : 22.3 * un nombre : -1 * un nombre : 3.14159 // prochaine squence * commentaire ignor : // prochaine squence 1 2 3 4 5 * un nombre : 1 * un nombre : 2 * un nombre : 3 * un nombre : 4 * un nombre : 5 50 * un nombre : 50

11
Fichiers et ux
La bibliothque standard (STL) dnit, travers len-tte <iostream>, une srie de ux dits standard: std::cout est un objet de type std::ostream permettant dafcher des donnes sur la sortie standard; std::cerr est un autre objet std::ostream permettant la mme chose, mais sans mise en tampon; std::clog est la version avec mise en tampon de std:: cerr; std::cin est un objet de type std::istream permettant de lire des donnes sur lentre standard. Len-tte <fstream> contient tout le ncessaire pour effectuer des oprations sur chiers avec les classes std::ifstream, pour la lecture, et std::ofstream, pour lcriture. Certains comportements des ux dentre/sortie de la bibliothque standard C++ (comme la prcision, la justication, etc.) peuvent tre modis grce aux divers manipulateurs de ux.

194

CHAPITRE 11 Fichiers et ux

Ouvrir un chier
#include <fstream> std::fstream(const char* lename, openmode mode); std::ifstream(const char* lename, openmode mode); std::ofstream(const char* lename, openmode mode); std::fstream::open(const char* lename, openmode mode);

Ces trois classes permettent de manipuler des chiers. Le paramtre mode est optionnel.Vous trouverez les diffrentes signications et possibilits dans le tableau ci-aprs. Elles sutilisent de manire analogue aux ux prdnis std:: cin et std::cout.
Mode douverture des chiers Mode std::ios::app std::ios::ate std::ios::binary std::ios::in std::ios::out std::ios::trunc std::ios::nocreate Attention Avec Microsoft, vous trouverez ces constantes dans ios_base en lieu et place de ios. Description Ajouter la n (append) Se placer la n lors de louverture Ouvrir le chier en mode binaire Ouvrir le chier en lecture Ouvrir le chier en criture craser le chier existant Unix seulement, ne cre pas le chier sil nexiste pas

Tester ltat dun ux

195

Lexemple suivant ajoute le contenu dun chier un autre:


char temp; std::ifstream n(chier1.txt); std::ofstream fout(chier2.txt, std::ios::app); while ( n >> temp ) fout << temp; n.close(); fout.close();

Tester ltat dun ux


stream::operator bool(); bool stream::fail(); bool stream::good(); bool stream::bad(); bool stream::eof(); ios::iostate stream::rdstate();

La conversion implicite en boolen permet de tester aisment si une erreur est survenue. Par ce biais, il est facile de tester si louverture dun chier sest bien droule. Le code ci-aprs lillustre simplement. Si vous prfrez un appel plus explicite, utilisez la fonction membre fail().
std::string nom_de_chier = data.txt; std::ifstream chier( nom_de_chier.c_str() ); if ( ! chier ) std::cout << Erreur lors de louverture du chier.\n;

La fonction membre good() permet de vrier quaucune erreur nest apparue. La fonction membre bad() permet de tester si une erreur fatale est survenue. La fonction membre eof() permet de tester simplement si la n du chier est atteinte.

196

CHAPITRE 11 Fichiers et ux

Par exemple, le code ci-aprs lit des donnes sur le ux dentre in et les crits sur le ux de sortie out pour enn utiliser eof() et vrier quaucune erreur nest survenue.
char tampon[TAILLE]; do { in.read( tampon, TAILLE ); std::streamsize n = in.gcount(); out.write( tampon, n ); } while ( in.good() ); if ( in.bad() || !in.eof() ) { // une erreur fatale est survenue } in.close();

Enn, la fonction membre rdstate() retourne ltat complet du ux considr. Le tableau suivant donne la liste des valeurs possibles.
tat dun ux Flag std::ios::badbit std::ios::eofbit std::ios::failbit std::ios::goodbit Attention Avec Microsoft, vous trouverez ces constantes dans ios_base en lieu et place de ios. Description Une erreur fatale est survenue Fin de chier atteinte Une erreur non critique est survenue Aucune erreur nest survenue

Lire dans un chier 197

Lire dans un chier


std::istream& operator >> (std::istream&, ); std::istream& std::istream::getline(char* tampon, stringsize n); std::istream& std::istream::getline(char* tampon, stringsize n, char delim); std::streamsize std::fstream::gcount(); int std::fstream::get(); std::istream& std::istream::get(char& ch); std::istream& std::istream::get(char* tampon, streamsize num); std::istream& std::istream::get(char* tampon, streamsize num, char delim); std::istream& std::istream::get(streambuf& tampon); std::istream& std::istream::get(streambuf& tampon, char delim); int std::fstream::peek(); std::istream& std::istream::read(char* buffer, streamsize num);

Il existe plusieurs faons de lire dans un ux. La plus pratique est dutiliser loprateur de redirection>>, car la plupart des conversions sont alors faites automatiquement. De plus, comme le montre lexemple ci-aprs, utiliser conjointement cet oprateur avec le type chane permet de lire un chier mot par mot. La sparation des mots ne correspond pas exactement au langage naturel. Elle se base uniquement sur les caractres espace, tabulation et retour chariot.
std::ifstream n(donnees.txt); std::string s; while ( n >> s ) std::cout << Mot lu : << s << std::endl;

198

CHAPITRE 11 Fichiers et ux

Lexemple suivant montre comment lire un chier ligne par ligne:


std::ifstream chier(donnees.txt); const int TAILLE = 100; char str[TAILLE]; while ( chier.getline(str, TAILLE) ) std::cout << Ligne lue : << s << std::endl;

Si vous souhaitez viter le tableau de caractres laC, vous pouvez utiliser la fonction std::getline() qui lit des lignes et les stocke dans un std::string (voir la section Lire des lignes dans un ux au Chapitre10 pour de plus amples explications).
std::ifstream chier(donnees.txt); std::string s; while ( std::getline(chier,s) ) std::cout << Ligne lue : << s << std::endl;

gcount() est utile pour connatre le nombre de caractres effectivement lus lors de la dernire opration de lecture. Les fonctions membres get() permettent de: lire un caractre et retourner sa valeur; lire un caractre et le stocker dans la variable ch; lire jusqu' num-1 caractres (sarrte si la n du chier ou un retour chariot ou le caractre delim est atteint); lire tous les caractres jusqu' la n, le prochain retour chariot ou le caractre delim, et les stocker dans le tampon donn.
char ch; std::ifstream chier(donnees.txt); while (chier.get(ch)) std::cout << ch; chier.close();

Lire dans un chier 199

La fonction membre peek() retourne le prochain caractre du ux, sans pour autant le retirer de celui-ci. Enn, la fonction membre read() permet de lire num octets (et pas caractres) dans le ux et de les placer dans le tampon donn. Si la n de chier est atteinte avant, la lecture sarrte et les octets lus sont placs dans le tampon.
struct Rectangle { int h,w; }; // chier.read(reinterpret_cast<char*>(&rect), sizeof(Rectangle)); if (chier.bad()) { std::cerr << Erreur lors de la lecture des donnes\n; exit(0); }

Astuce: ignorer une partie dun ux std::istream& std::istream::ignore(streamsize num=1, int delim=EOF); La fonction membre ignore() sutilise avec les ux dentre. Elle lit et ignore jusqu num caractres ou moins si le caractre delim est rencontr avant. Par dfaut, num vaut1 et delim correspond la n du chier. Cette fonction peut parfois savrer utile lorsque lon utilise conjointement la fonction getline() et loprateur>>. Par exemple, si vous lisez une entre nissant par une n de ligne avec loprateur>>, le retour chariot reste prsent dans le ux. Comme getline() sarrte par dfaut sur le prochain retour chariot, le prochain appel cette fonction renverra une chane vide. Dans ce cas, la fonction ignore() peut tre appele avant getline() pour dbarrasser le ux du retour chariot gnant.

200

CHAPITRE 11 Fichiers et ux

crire dans un chier


std::ostream& std::ostream::put( char ch ); std::ostream& std::ostream::write(const char* tampon, streamsize num ) std::ostream& std::ostream::ush(); std::ostream& operator << (std::ostream&, ); std::istream& std::istream::putback(char ch);

La fonction membre put() crit le caractre ch dans le ux. La fonction membre write() crit les num premiers octets du tampon donn dans le ux. La fonction ush() force lcriture des tampons internes du ux. Ceci est particulirement utile pour lcriture dinformation de dbogage: dans certains cas de plantage, une partie des donnes crites dans un ux de chiers peut ne pas avoir t transfre sur le disque mais tre reste en mmoire, et donc perdue. Un appel judicieux ush() assure le transfert du tampon interne vers le priphrique li au ux. Pour les ux en mode texte, vous disposez de nombreux oprateurs>> sur tous les types de base du C++. Il est mme possible, la printf(), de prciser comment formater les donnes crire. Reportez-vous la section Manipuler des ux un peu plus loin dans ce chapitre. La fonction membre putback(), contrairement ce que lintitul de cette section pourrait laisser penser, sutilise sur les ux de lecture. Elle permet de simuler un retour en arrire en remettant le caractrech dans le ux, comme si on ne lavait pas encore lu. Pour ceux qui connaissent la bibliothqueC, elle correspond la fonctionC int ungetc(int ch, FILE*).

Se dplacer dans un ux

201

Se dplacer dans un ux
std::istream& std::istream::seekg( std::off_type
offset, std::ios::seekdir origine );

std::istream& std::istream::seekg( std::pos_type


position );

std::pos_type std::istream::tellg(); std::ostream& std::ostream::seekp( std::off_type


offset, std::ios::seekdir origine );

std::ostream& std::ostream::seekp( std::pos_type


position );

std::pos_type std::ostream::tellp();

Les fonctions membres seekg() permettent de dplacer le curseur de lecture dun ux dentre, soit la position offset relative lorigine donne, soit la position absolue fournie. Le prochain appel de get() partira de ce nouvel emplacement. La fonction membre tellg() permet de connatre la position actuelle dans le ux de lecture.
Position relative dans un ux Valeur std::ios::beg std::ios::cur std::ios::end Description Dcalage relatif au dbut du chier Dcalage relatif la position courante Dcalage relatif la n du chier

Les fonctions membres seekp() et tellp() sont les quivalentes des prcdentes, mais pour les ux dcriture (ou de sortie).

202

CHAPITRE 11 Fichiers et ux

Lexemple suivant afche la position courante dun ux de chier et lafche sur la sortie standard:
std::string s(Une chane de caractres quelconque...); std::ofstream chier(sortie.txt); for (int i=0; i < s.length(); ++i) { std::cout << Position : << chier.tellp(); chier.put( s[i] ); std::cout << << s[i] << std::endl; } chier.close();

Manipuler des ux
std::fmtags std::stream::ags(); std::fmtags std::stream::ags( fmtags f ) std::fmtags std::stream::setf( fmtags ags ); std::fmtags std::stream::setf( fmtags ags, fmtags needed ); void std::stream::unsetf( fmtags ags );

Les fonctions membres ags() retournent le masque de formatage des donnes du ux courant, ou permettent de le changer. Vous retrouverez les diffrentes valeurs de ce masque dans le tableau ci-aprs. Les fonctions membres setf() permettent dactiver des options de formatage (ags). Le paramtre needed permet de ne changer que les options communes. La valeur retour ne est ltat avant changement. La fonction membre unsetf() permet au contraire de dsactiver loption spcie.

Manipuler des ux 203

int nombre = 0x3FF; std::cout.setf( std::ios::dec ); std::cout << Dcimal : << nombre << std::endl; std::cout.unsetf( std::ios::dec ); std::cout.setf( std::ios::hex ); std::cout << Hxadcimal : << nombre << std::endl;

Grce aux manipulateurs, le code prcdent peut tre remplac par le code suivant, qui est beaucoup plus simple:
int nombre = std::cout << << << << 0x3FF; Dcimal : << std::dec << nombre std::endl Hexadcimal : << std::hex << nombre std::endl;

Manipulateurs de ux
Manipulateur Description boolalpha dec endl ends xed ush hex internal left Afche les boolens sous forme textuelle (true et false) Passe en mode dcimal crit un caractre de n de ligne, vide le tampon crit un caractre nul Afche les nombres rels en mode standard (par opposition scientique) Vide le tampon interne du ux Passe en mode hexadcimal Si un nombre est complt pour remplir une taille donne, des espaces sont insrs entre le signe et le symbole de la base Justie le texte gauche Entre X X _ _ _ _ X Sortie X X X X X X X

_ _

X X

204

CHAPITRE 11 Fichiers et ux

Manipulateurs de ux (suite)
Manipulateur noboolalpha noshowbase noshowpoint noshowpos noskipws nounitbuf nouppercase oct right scientic showbase showpoint showpos skipws unitbuf uppercase ws Description Nafche pas les boolens sous forme textuelle Nafche pas le prxe servant de symbole de la base Dsactive lafchage forc de la virgule et des zros inutiles des nombres rels Dsactive lafchage forc du + devant les nombres positifs Pour ne plus ignorer les blancs Dsactive le mode initbuf Afche le e de la notation scientique et le x de la notation hexadcimale en minuscule Passe en mode octal Passe en alignement droite Afche les rels en mode scientique Afche le prxe, symbole de la base utilise Afche toujours le point des nombres rels Entre X _ _ Sortie X X X

_ X _ _ X _ _ _ _

X _ X X X X X X X X _ X X _

Afcher toujours un plus _ devant les nombres positifs Passe en mode ignorer les blancs X Force lcriture (vide le tampon) _ aprs chaque insertion Passe en mode majuscules forces pour le e de la notation scientique et le x de la notation hexadcimale Saute les blancs restant _ X

Manipuler une chane de caractres comme un ux 205

Manipulateurs dnis dans <iomanip>


Manipulateur Description Entre X _ _ X _ _ Sortie X X X X X X

resetiosags(int long) Met off le ag spci setbase(int base) setll(int ch) setiosags(long f) setprecision(int p) setw(int w) Prcise la base utiliser pour lafchage Prcise le caractre utiliser pour le remplissage Met on le ag spci Fixe le nombre de chiffres aprs la virgule Fixe la largeur pour les fonctions dalignement

Manipuler une chane de caractres comme un ux


#include <sstream> std::stringstream::stringstream( [ std::string s [, std::openmode mode ]] ); void std::stringstream::str(std::string s); std::string std::stringstream::str();

Les ux de chanes de caractres sutilisent de manire analogue aux ux de chiers. Le paramtre mode est le mme que pour ces derniers.

206

CHAPITRE 11 Fichiers et ux

crire dans une chane de caractre comme dans un ux


std::ostringstream::ostringstream( [ std::string s
[, std::openmode mode ]] );

Un objet std::ostringstream peut tre utilis pour crire le contenu dune chane. Cest un peu le pendant de la fonction C sprintf().
std::ostringstream s_ux; int i = 3; s_ux << Coucou puissance << i << std::endl; std::string str = s_ux.str(); std::cout << str;

Lire le contenu dune chane comme avec un ux


std::istringstream::istringstream( [ std::string s
[, std::openmode mode ]] );

Un objet std::istringstream permet de lire le contenu dune chane, un peu comme le sscanf() duC.
std::istringstream ux_chaine; std::string chaine = 33; ux_chaine.str(chaine); int i; ux_chaine >> i; std::cout << i << std::endl; // afche 33

Lire le contenu dune chane comme avec un ux

207

Vous pouvez aussi spcier directement sur quelle chane travailler en la fournissant au constructeur:
std::string chaine = 33; std::istringstream ux_chaine(chaine); int i; ux_chaine >> i; std::cout << i << std::endl; // afche 33

Un objet std::stringstream permet deffectuer la fois des oprations de lecture et dcriture, comme pour les objets std::fstream.

12
Algorithmes standard
La bibliothque standard (STL) fournit de nombreux algorithmes utilisables sur ses conteneurs ou tous conteneurs compatibles. Ce chapitre vous permettra den exploiter tout le potentiel.
Algorithmes standard
Nom accumulate adjacent_difference adjacent_nd binary_search copy copy_backward copy_n Description Calculer la somme des lments dune squence Calculer les diffrences entre lments conscutifs dune squence Chercher la premire occurrence de deux lments conscutifs identiques Rechercher un lment dans une squence Copier les lments dune squence dans une autre Copier les lments dune squence dans une autre en commenant par la n Copier les npremiers lments dune squence dans une autre Page 214 215 217 218 219 221 222

210

CHAPITRE 12 Algorithmes standard

Algorithmes standard (Suite)


Nom count count_if equal equal_range ll ll_n nd nd_end Description Compter le nombre dlments correspondant une valeur donne Compter le nombre dlments conformes un test donn Tester si deux squences sont identiques Chercher la sous-squence dlments tous gaux un certain lment Initialiser une squence avec une valeur Initialiser les npremiers lments dune squence avec une valeur Chercher le premier lment gal une valeur dans une squence Chercher la dernire apparition dune sous-squence donne Chercher le premier lment dont la valeur est prsente dans un ensemble donn Chercher le premier lment vriant un test donn Appliquer une fonction/foncteur sur tous les lments dune squence Initialiser une squence laide dun gnrateur de valeurs Initialiser les npremires valeurs dune squence avec un gnrateur de valeurs Dterminer si tous les lments dune squence sont dans une autre Calculer le produit intrieur (produit scalaire gnralis) de deux squences Fusionner deux squences tries (dans la premire) Initialiser les lments dune squence avec une valeur (en lincrmentant) Page 223 224 225 226 228 228 228 266

nd_rst_of

229

nd_if for_each generate generate_n includes inner_product inplace_merge iota

228 230 231 231 232 234 243 235

Algoritmes standard

211

Algorithmes standard (Suite)


Nom is_heap is_sorted iter_swap lexicographical_ compare lexicographical_ compare_3way Description Tester si la squence est un tas Tester si une squence est trie changer le contenu des deux variables pointes Tester si une squence est lexicographi quement plus petite quune autre Tester si une squence est lexicographi quement plus petite (1), gale (0), ou suprieure (1) quune autre Chercher le premier endroit o insrer une valeur sans briser lordre de la squence Transformer la squence en tas Rcuprer le plus grand lment (entre deux) Rcuprer le plus grand lment dune squence Fusionner deux squences tries (dans une troisime) Rcuprer le plus petit lment (entre deux) Rcuprer le plus petit lment dune squence Trouver le premier endroit o deux squences diffrent Gnrer la prochaine plus grande permu tation lexicographique dune squence Faire en sorte que le nime lment soit le mme que si la squence tait trie et assurer quaucun lment sa gauche ne soit plus grand quun sa droite Trier les npremiers lments dune squence Page 236 274 275 238

238

lower_bound make_heap max max_element merge min min_element mismatch next_permutation

241 236 245 246 243 245 246 247 248

nth_element

250

partial_sort

251

212

CHAPITRE 12 Algorithmes standard

Algorithmes standard (Suite)


Nom partial_sort_copy partial_sum Description Copier les nplus petits lments dune squence (le rsultat est tri) Calculer la somme partielle gnralise dune squence Couper une squence en deux en fonction dun prdicat (ne prserve pas forcment lordre des lments identiques) Retirer le plus grand lment dun tas Calculer xi (fonction puissance gnralise) Gnrer la prochaine plus petite permu tation lexicographique dune squence Ajouter un lment un tas Copier alatoirement un chantillon dune squence (nombre dlments dtermins par la taille de la squence rsultat) Copier alatoirement un sous-chantillon (de nlments) dune squence, en prservant leur ordre dorigine Mlanger les lments dune squence Supprimer les lments gaux une valeur donne Copier une squence en omettant les lments gaux une valeur donne Copier une squence en omettant les lments vriant un test donn Supprimer les lments vriant un test donn Remplacer tous les lments gaux une valeur par une autre Page 252 253

partition

254 236 256 248 236 257

pop_heap power prev_permutation push_heap

random_sample

random_sample_n random_shufe remove remove_copy remove_copy_if remove_if replace

258 259 260 262 262 260 263

Algoritmes standard

213

Algorithmes standard (Suite)


Nom replace_copy Description Copier une squence en remplaant certaines valeurs par une autre Copier une squence en remplaant certaines valeurs vriant un test par une autre Remplacer tous les lments respectant un test donn par une nouvelle valeur Inverser lordre de la squence Copier une squence en inversant son ordre Effectuer une rotation des lments de la squence Copier une squence en effectuant une rotation des lments de la squence Chercher la premire apparition dune sous-squence donne Chercher la premire apparition de noccurrences conscutives dune valeur donne Construire la diffrence de deux squences tries Construire lintersection de deux squences tries Construire la diffrence symtrique de deux squences tries Construire lunion de deux squences tries Trier une squence (ne prserve pas forcment lordre des lments identiques) Transformer un tas en squence trie Couper une squence en deux en fonction dun prdicat (prserve lordre des lments identiques) Page 263 263 263 264 264 264 264 265 265 268 270 272 273 274 236 254

replace_copy_if

replace_if reverse reverse_copy rotate rotate_copy search

search_n

set_difference set_intersection set_symmetric_ difference set_union sort sort_heap stable_partition

214

CHAPITRE 12 Algorithmes standard

Nom stable_sort swap swap_ranges transform unique unique_copy

Description Trier une squence (prserve lordre des lments identiques) changer le contenu de deux variables changer le contenu de deux squences de mme taille Transformer une (ou deux) squences en une autre Supprimer les doublons dune squence Copier une squence en supprimant les doublons dune squence Chercher le dernier endroit o insrer une valeur sans briser lordre de la squence Copier laide du constructeur par copie Copier laide du constructeur par copie (nlments) Initialiser laide du constructeur par copie Initialiser laide du constructeur par copie (nlments)

Page 274 275 275 276 278 278

upper_bound

241

uninitialized_copy uninitialized_copy_n uninitialized_ll uninitialized_ll_n

281 281 282 282

Calculer la somme des lments dune squence


#include <numeric> TYPE accumulate(InputIterator debut, InputIterator n, TYPE init); TYPE accumulate(InputIterator debut, InputIterator n, TYPE init, BinaryFunction f);

Calculer les diffrences entre lments conscutifs dune squence

215

La fonction accumulate() calcule la somme de init plus tout les lments de lensemble [debut, n[. Si la fonction binaire f est fournie, elle sera utilise la place de loprateur+. La complexit est en temps linaire O(n) fois la complexit de loprateur utilis.
std::list<double> l; double moyenne = std::accumulate(l.begin(), l.end(), 0.0) / l.size(); double produit = std::accumulate(l.begin(), l.end(), 1.0, std::multiplies<double>());

Calculer les diffrences entre lments conscutifs dune squence


#include <numeric> TYPE adjacent_difference(InputIterator debut, InputIterator n, OutputIterator resultat); TYPE adjacent_difference(InputIterator debut, InputIterator n, OutputIterator resultat, BinaryFunction f);

La fonction adjacent_difference() calcule la diffrence des lments adjacents de lensemble [debut, n[. Si lensemble en entre contient les lments (a, b, c, d), le rsultat sera (a, b-a, c-b, d-c). Si la fonction binairef est fournie, elle sera utilise la place de loprateur-.

216

CHAPITRE 12 Algorithmes standard

Info La sauvegarde dans le rsultat du premier lment en entre nest pas inutile. Elle permet davoir sufsamment dinformation pour reconstruire la squence dorigine. En particulier, avec les oprateurs arithmtiques que sont laddition et la soustraction, adjacent_difference et partial_sum sont larciproque lune de lautre. std::vector<int> v1; // initialisation de v1 avec des valeurs ... std::vector<int> v2(v1.size()); std::cout << V1: ; std::copy(v1.begin(), v1.end(), std::ostream_iterator <int>(std::cout, )); std::cout << std::endl; std::adjacent_difference(v1.begin(), v1.end(), v2. begin()); std::cout << Diffrences: ; std::copy(v2.begin(), v2.end(), std::ostream_iterator <int>(std::cout, )); std::cout << std::endl; std::cout << Reconstruction: ; std::partial_sum(v2.begin(), v2.end(), std::ostream_ iterator<int>(std::cout, )); std::cout << std::endl;

Chercher la premire occurrence de deux lments conscutifs identiques

217

Chercher la premire occurrence de deux lments conscutifs identiques


#include <algorithm> ForwardIterator adjacent_nd(ForwardIterator debut, ForwardIterator n); ForwardIterator adjacent_nd(ForwardIterator debut, ForwardIterator n, BinaryPredicate pred);

La fonction adjacent_nd() renvoie le premier itrateuri tel que *i == *(i+1) et tel que les itrateursi et i+1 appartiennent la squence [debut, n[ donne. Si un tel itrateur nexiste pas, la fonction renvoie n. Si le prdicat binaire pred est fourni, il sera utilis la place de loprateur==. Par exemple, si v est un vecteur dentier contenant les valeurs (1, 2, 3, 3, 4, 5, 6, 7, 8). Le code suivant permet de reprer lemplacement de la paire de3:
std::vector<int>::iterator it = adjacent_nd(v.begin(),
v.end());

if (it == v.end()) std::cout << Pas dlments contigus gaux dans v\n; else std::cout << Deux lments contigus trouvs, de valeur: << *it << std::endl;

218

CHAPITRE 12 Algorithmes standard

Rechercher un lment dans une squence


#include <algorithm> bool binary_search(ForwardIterator debut, ForwardIterator n, const LessThanComarable& val); bool binary_search(ForwardIterator debut, ForwardIterator n, const TYPE& val, StrickWeakOrdering comp);

La fonction binary_search() recherche la valeur val dans [debut, n[. Les lments dans lensemble de recherche doivent imprativement tre tris en ordre croissant (relativement loprateur<). Si val est trouv, la fonction renvoie true, sinon elle renvoie false. Si une fonction de comparaison comp est fournie, elle sera utilise pour comparer les lments. Bien videmment, les lments de lensemble de recherche doivent tre tris daprs ce comparateur. Cette recherche est logarithmique pour les itrateurs de type RandomAccessIterator, quasi linaire sinon (logarithmique pour les comparaisons, linaire pour le nombre dtapes).
Attention Cette recherche binaire ne fonctionne que si lensemble dans lequel est effectuela recherche est tri. Loprateur de comparaison comp est une relation dordre strict (au sens mathmatique du terme). Cest--dire que si a comp b est vrai, alors b comp a est faux. galement, si a comp b et bcomp c sont vrais, alors a comp c est vrai. Enn si a comp b et b comp a sont faux tous les deux, alorsa etb sont quivalents (gaux).

Copier les lments dune squence dans une autre

219

Le code suivant teste la prsence des nombres 0 9 dans un tableau:


for (int i=0; i<10; i++) { if (binary_search(tab.begin(), tab.end(), i)) std::cout << i << est dans le tableau\n; else std::cout << i << NEST PAS dans le tableau\n; }

Si le tableau en entre vaut {23, 12, 0, 1, 2, 4, 5, 6, 8, 9, 50, 100, 300}, vous obtiendrez:
0 1 2 3 4 5 6 7 8 9 est dans le tableau est dans le tableau est dans le tableau NEST PAS dans le tableau est dans le tableau est dans le tableau est dans le tableau NEST PAS dans le tableau est dans le tableau est dans le tableau

Copier les lments dune squence dans une autre


#include <algorithm> OutputIterator copy(InputIterator debut, InputIterator n, OutputIterator resultat);

La fonction copy() copie un par un les lments de [debut, n[ dans resultat. Le rsultat nal se situe dans [resultat, resultat + (n debut) [. Gnralement, la copie est

220

CHAPITRE 12 Algorithmes standard

effectue par lopration *(result + n) = *(rst + n) pourn allant de0 n - debut, dans lordre croissant.
std::vector<int> source(10), dest(10); // initialisation de la source std::iota(source.begin(), source.end(), 1); // copie les lments de source dans dest std::copy(source.begin(), source.end(), dest.begin());

Vous pouvez aussi utiliser lalgorithme de copie pour crire le contenu dune squence sur la sortie standard trs simplement. Lexemple suivant lillustre, tout en sparant les lments crits par un espace:
// #include <iomanip> pour ostream_iterator std::copy(source.begin(), source.end(), std::ostream_iterator<int>(std::cout, ));

Attention Litrateur resultat doit pointer sur une squence mmoire valide. cause de lordre de la copie, litrateur resultat ne doit pas tre dans la squence [debut, n[. Par contre, la n de la squence rsultat peut avoir une partie commune avec la squence source. Lalgorithme copy_backward a la restriction oppose.

Copier les lments dune squence dans une autre en commenant par la n

221

Copier les lments dune squence dans une autre en commenant par la n
#include <algorithm> BidirectionalIterator2 copy_backward (BidirectionalIterator1 debut, BidirectionalIterator1 n, BidirectionalIterator2 resultat);

Cette fonction est presque la mme que la prcdente (copy()). La diffrence rside dans lordre de la copie. copy_ backward() commence par le dernier et nit par le premier lment (sans inverser la squence). Ainsi, la fonction copy_backward() copie un par un les lments de [debut, n[ dans resultat. Le rsultat nal se situe dans [resultat - (n - debut), resultat[. Gnralement, la copie est effectue par lopration *(result - n - 1) = *(rst - n) pourn allant de0 n - debut, dans lordre croissant. La copie est donc faite depuis le dernier jusquau premier lment de la squence. Lexemple suivant copie les dix premiers lments dun vecteur la n de celui-ci:
std::copy_backward( vec.begin(), vec.begin() + 10,
vec.end() );

Attention Litrateur resultat doit pointer sur une squence mmoire [resultat (n debut), resultat[ valide. cause de lordre de la copie, litrateur resultat ne doit pas tre dans la squence [debut, n[. Par contre, le dbut de la squence de rsultat peut avoir une partie commune avec la squence source. Lalgorithme copy() a la restriction oppose.

222

CHAPITRE 12 Algorithmes standard

Copier les n premiers lments dune squence dans une autre


#include <algorithm> OutputIterator copy_n(InputIterator debut, Size n, OutputIterator resultat);

La fonction copy_n() copie les lments de la squence [debut,debut + n[ dans [resultat,resultat +n[.Gnralement, la copie est faite par lopration *(resultat + i) = *(debut +i) pouri allant de0 n non inclus, dans lordre croissant.
std::vector<int> V(5); std::iota(V.begin(), V.end(), 1); std::list<int> L(V.size()); std::copy_n( V.begin(), V.size(), L.begin());

Attention
Size doit tre de type entier etn doit tre positif. Les squences [debut, debut + n[ et [resultat, resultat + n[ doivent tre valides. Litrateur resultat ne doit pas faire partie de la squence source. La mme restriction que celle de copy

surla superposition des squences source et rsultat doit tre respecte.

Info Cette fonction peut paratre redondante avec copy. Contrai rement cette dernire, copy_n permet dutiliser des itrateurs de type input iterator et pas seulement forward iterator.

Compter le nombre dlments correspondant une valeur donne 223

Compter le nombre dlments correspondant une valeur donne


#include <algorithm> iterator_traits<InputIterator>::difference_type count(InputIterator debut, InputIterator n, const EqualityComparable& valeur); void count(InputIterator debut, InputIterator n, const EqualityComparable& valeur, Size& n); // (ancien)

La fonction count() compte le nombre dlments de la squence [debut, n[ tant gaux valeur. Plus exactement, la premire fonction retourne le nombre ditrateursi de [debut, n[ tels que *i == valeur. La seconde ajoute ce nombre n.
Info La deuxime version est celle que lon trouve dans la STL dorigine, elle est conserve dans certaines implmentations mais est susceptible dtre enleve tout moment. Seule la premire version fait partie du standard C++.

Voici un exemple simple illustrant lutilisation de la STL avec des typesC:


int A[] = { 4, 3, 8, 0, 2, 5, 7, 0, 3, 8, 5, 6 }; const int N = sizeof(A) / sizeof(int); std::cout << Il y a << std::count(A, A+N, 0) << zro(s).\n;

224

CHAPITRE 12 Algorithmes standard

Compter le nombre dlments conformes un test donn


#include <algorithm> iterator_traits<InputIterator>::difference_type count_if(InputIterator debut, InputIterator n, const UnaryPredicate& pred); void count_if(InputIterator debut, InputIterator n, const UnaryPredicate& pred, Size& n); // (ancien)

La fonction count_if() compte le nombre dlments de la squence [debut, n[ qui respectent le prdicat unaire pred. Plus exactement, la premire fonction retourne le nombre ditrateursi de [debut, n[ tels que pred(*i) est vrai. La seconde (ancienne et pas toujours implmente) ajoute ce nombre n. Lexemple suivant compte tous les nombres pairs:
int A[] = { 4, 3, 8, 0, 2, 5, 7, 0, 3, 8, 5, 6 }; const int N = sizeof(A) / sizeof(int); std::cout << Il y a << std::count_if(A, A+N, std::compose1(std::bind2nd(std::equal_to<int>(),0), std::bind2nd(std::modulus<int>(),2))) << nombre(s) pair(s).\n;

Tester si deux squences sont identiques 225

Info Les nouvelles interfaces de count utilisent les classes iterator_traits, qui reposent sur une particularit du C++ connue sous le nom de spcialisation partielle. La plupart des compilateurs nimplmentent pas la totalit de la norme; ce faisant, mme des compilateurs relativement rcents nimplmentent pas toujours la spcialisation partielle. Si tel est le cas, la nouvelle version de count ne sera pas forcment prsente ( moins quelle retourne un size_t); de mme que les autres parties de la STL utilisant les iterator_traits.

Tester si deux squences sont identiques


#include <algorithm> bool equal(InputIterator1 debut1, InputIterator1 n1, InputIterator2 debut2); bool equal(InputIterator1 debut1, InputIterator1 n1, InputIterator2 debut2, BinaryPredicate pred);

La fonction equal() retourne vrai si les deux squences [debut1, n1[ et [debut2, debut2 + (n1 debut1)[ sont gales en les comparant lment par lment. La premire version utilise la comparaison *i == *(debut2 + (i debut1)), et la seconde pred(*i1,* (debut2 + (i debut1))) == true, pour touti appartenant la premire squence. Lexemple suivant compare deux vector de mme taille:
bool egaux = std::equal(v1.begin(), v1.end(), v2.begin());

226

CHAPITRE 12 Algorithmes standard

Chercher la sous-squence dlments tous gaux un certain lment


#include <algorithm> pair<ForwardIterator,ForwardIterator> equal_range(ForwardIterator debut, ForwardIterator n, const LessThanComparable& valeur); pair<ForwardIterator,ForwardIterator> equal_range (ForwardIterator debut, ForwardIterator n, const T& valeur, StricWeakOrdering comp);

La fonction equal_range() est une variante de binary_ search(): elle renvoie la sous-squence [debut, n[ dont les lments sont tous quivalents (voir la note ci-aprs) valeur. La premire utilise loprateur de comparaison<, la deuxime la relation dordre stricte comp.
Info La relation dordre utilise doit tre stricte, mais pas ncessairement totale. Il peut exister des valeursx ety telles que les tests x < y, x > y (plus exactement y < x) et x == y soient tous faux. Trouver une valeur dans la squence ne revient donc pas trouver un lment qui soit gal valeur mais quivalent: ni infrieur, ni suprieur valeur. Si vous utilisez une relation dordre totale, lquivalence et lgalit reprsentent la mme chose. Cest le cas par exemple pour la comparaison dentiers ou de chanes de caractres avec strcmp.

Chercher la sous-squence dlments tous gaux un certain lment 227

Attention
equal_range() ne doit tre utilis que sur une squence trie avec la mme relation dordre, ou selon une relation dordre compatible.

Cette fonction peut tre vue comme une combinaison des fonctions lower_bound() et upper_bound(). Ce sont en effet ces valeurs que lon retrouve dans la paire retourne par equal_range(). Mais cest plus rapide que dinvoquer les deux fonctions prcites. Pour bien comprendre ce que fait cette fonction, considrez son utilisation ci-aprs, en postulant que le vecteurv utilis contienne les valeurs 78, 34, 6, 2, 5, 5, 5, 5, 6, 12.
std::pair<std::vector<int>::iterator, std::vector
<int>::iterator> res;

res = std::equal_range(v.begin(), v.end(), 5); std::cout << Le premier emplacement pour insrer 5 est avant << *res.rst << et le dernier (o il peut tre insr avant) est << *res.second << .\n;

Vous obtiendrez la phrase Le premier emplacement pour insrer5 est avant5 et le dernier (o il peut tre insr avant) est 6. Bien entendu, le 5 o lon peut insrer avant est le premier de la liste.
Info
equal_range() peut retourner une squence vide (une paire contenant deux fois le mme itrateur). Cest le cas ds que la squence dentre ne contient aucun lment quivalent lavaleur recherche. Litrateur renvoy est alors la seule position o la valeur donne peut tre insre sans violer la relation dordre.

228

CHAPITRE 12 Algorithmes standard

Initialiser une squence


#include <algorithm> void ll(ForwardIterator debut, ForwardIterator n, const T& valeur); OutptIterator ll_n(OutputIterator debut, Size n, const T& valeur);

ll() affecte tous les lments de la squence [debut, n[ la valeur donne. ll_n() le fait sur la squence [debut, debut + n[ puis retourne debut + n.
std::vector<int> v(5); std::vector<int>::iterator res; std::ll(v.begin(), v.end(), -2); // v = -2, -2, -2, -2, -2 res = std::ll_n(v.begin(), 3, 0); // v = 0, 0, 0, -2, -2 *res = 1; // v = 0, 0, 0, 1, -2

Chercher le premier lment tel que


#include <algorithm> InputIterator nd(InputIterator debut, InputIterator n, const EqualityComparable& value); InputIterator nd_if(InputIterator debut, InputIterator n, Predicate pred);

nd() retourne le premier itrateur de la squence [debut, n[ tel que *i == value. Lalgorithme nd_if() utilise pred(*i)==true. Si aucun lment ne valide le test, la valeur de retour est n. Lexemple suivant renvoie un itrateur sur le premier lment positif dune liste dentiers :

Chercher le premier lment parmi 229

res = std::nd_if(L.begin(), L.end(),std::bind2nd


(greater<int>, 0));

Celui-ci cherche la premire occurrence de 7:


res = std::nd(L.begin(), L.end(), 7);

Chercher le premier lment parmi


#include <algorithm> InputIterator nd_rst_of(InputIterator debut1, InputIterator n1, ForwardIterator debut2, ForwardIterator n2); InputIterator nd_rst_of(InputIterator debut1, InputIterator n1, ForwardIterator debut2, ForwardIterator n2, BinaryPredicate comp);

nd_rst_of() est un peu comme nd() en ce sens quil recherche linairement travers la squence dentre [debut1, n1[. La diffrence tient au fait que nd() cherche une valeur donne dans la squence, alors que nd_rst_ of() recherche nimporte quelle valeur apparaissant dans la deuxime squence [debut2, n2[. Ainsi, nd_forst_of() retourne litrateur pointant sur la premire valeur de [debut1, n1[ appartenant [debut2, n2[, ou n1 sinon. La premire version de nd_rst_of() utilise loprateur== pour comparer les lments. La deuxime utilise le prdicat comp fourni. Par exemple, essayez la fonction ci-aprs sur ces chanes de caractres: Une phrase avec plusieurs mots. ou UnSeulMot.

230

CHAPITRE 12 Algorithmes standard

char* premier_separateur(char* chaine, const int taille) { const char* blanc = \t\n ; return std::nd_rst_of(chaine, chaine+taille, blanc, blanc+4); }

Appliquer une fonction/foncteur sur tous les lments dune squence


#include <algorithm> UnaryFunction for_each(InputIterator debut, InputIterator n, UnaryFunction f);

for_each() applique la fonctionf sur tous les lments de la squence [debut, n[. Sif retourne une valeur, elle est ignore. Les oprations sont faites dans lordre dapparition des lments de la squence, de debut (inclus) n (exclus). la n, for_each() retourne lobjet fonction pass en paramtre. Voici une faon un peu plus complexe que la combinaison de copy() et ostream_iterator() pour crire le contenu dune squence sur la sortie standard:
template <class T> struct Ecrire : public std::unary_function<T, void> { Ecrire(std::ostream& un_ux) : ux(un_ux), compteur(0) { } void operator() (T x)

Initialiser une squence laide dun gnrateur de valeurs

231

{ ux << x << ; compteur++; } std::ostream& ux; int compteur; }; int main(int, char**) { int tableau[] = { 1, 2, 3, 4, 5, 6, 7 }; const int taille = sizeof(tableau) / sizeof(int); Ecrire<int> E = std::for_each(tableau,
tableau+taille, Ecrire<int>(std::cout));

std::cout << std::endl << E.compteur << objets crits.\n; }

Initialiser une squence laide dun gnrateur de valeurs


#include <algorithm> ForwardIterator generate(ForwardIterator debut, ForwardIterator n, Generator g); OutputIterator generate_n(OutputIterator debut, Size n, Generator g);

generate() et generate_n() affectent chaque lment de la squence [debut, n[ (respectivement [debut, debut + n[) le rsultat de la fonction gnratrice g(), puis retourne litrateur n (respectivement debut + n).

232

CHAPITRE 12 Algorithmes standard

Info La fonction gnratrice est bien appele nfois. Son rsultat nest pas stock puis affect tous les lments. Cela peut paratre lourd si cette fonction retourne le rsultat dun algorithme complexe, mais cela offre la souplesse dutiliser comme fonction gnratrice une fonction qui ne retourne pas toujours le mme rsultat, telle une fonction alatoire ou une fonction incrmentale.

Info Pour affecter la mme valeur tous les lments de la squence, utilisez plutt ll() ou ll_n().

Cet exemple crit une liste de valeurs alatoires sur la sortie standard:
std::generate_n(std::ostream_iterator<int>(std::
cout,\n), 10, rand);

Tester si tous les lments dune squence sont dans une autre
#include <algorithm> bool includes(InputIterator debut1, InputIterator n1, InputIterator debut2, InputIterator n2); bool includes(InputIterator debut1, InputIterator n1, InputIterator debut2, InputIterator n2, StrictWeakOrdering comp);

includes() teste si tous les lments de la deuxime squence [debut2, n2[ sont dans la premire squence [debut1, n1[.

Tester si tous les lments dune squence sont dans une autre 233

Cette fonction, qui sexcute en temps linaire, requiert que les deux squences soient tries selon la relation dordre mentionne (<par dfaut).
#include <iostream> #include <algorithm> using namespace std; #dene TAILLE_TABLEAU(tab) (sizeof(tab) / sizeof(int)) #dene TEST_INCLUDES(tab1,tab2) \ (includes(tab1, tab1 + TAILLE_TABLEAU(tab1), \ tab2, tab2 + TAILLE_TABLEAU(tab2)) \ ? oui : non) #dene PRINT_TABLEAU(tab) \ cout << { << tab[0]; \ for(int i=1; i<TAILLE_TABLEAU(tab); i++) \ cout << , << tab[i]; \ cout << }; #dene PRINT_TEST(tab1,tab2) \ PRINT_TABLEAU(tab1) \ cout << contient ; \ PRINT_TABLEAU(tab2) \ cout << ? << TEST_INCLUDES(tab1,tab2) << endl ; int int int int int int A1[] A2[] A3[] A4[] A5[] A6[] = = = = = = { { { { { { 1, 1, 2, 1, 1, 1, 2, 4, 7, 1, 2, 1, 3, 4, 5, 6, 7 }; 7 }; 9 }; 2, 3, 5, 8, 13, 21 }; 13, 13 }; 3, 21 };

int main(int,char**) { PRINT_TEST(A1,A2) PRINT_TEST(A1,A3) PRINT_TEST(A4,A5) PRINT_TEST(A4,A6) return 0; }

234

CHAPITRE 12 Algorithmes standard

Le programme prcdent produit le rsultat suivant:


{ { { { 1, 1, 1, 1, 2, 2, 1, 1, 3, 3, 2, 2, 4, 4, 3, 3, 5, 5, 5, 5, 6, 6, 8, 8, 7} contient { 1, 7} contient { 2, 13, 21} contient 13, 21} contient 4, 7} ? 7, 9} ? { 1, 2, { 1, 1, oui non 13, 13} ? non 3, 21} ? oui

Calculer le produit intrieur (produit scalaire gnralis) de deux squences


#include <algorithm> T inner_product(InputIterator1 debut1, InputIterator1 n1, InputIterator2 debut2, T init); T inner_product(InputIterator1 debut1, InputIterator1 n1, InputIterator2 debut2, T init, BinaryFunction1 op1, BinaryFunction2 op2);

La fonction inner_product() calcule le produit intrieur (un cas particulier bien connu est le produit scalaire) de deux squences de mme taille. De manire pratique, si la deuxime squence doit contenir au moins autant dlments que la premire, les autres tant ignors, il vaut mieux utiliser cette fonction sur des squences de taille strictement identique. Le rsultat de la premire version est comparable au pseudo-code suivant:
T resultat = init; for (int i=0; i<taille(sequence1); i++) resultat = resultat + sequence1[i] * sequence2[i];

La deuxime version utilise lopration resultat = op1(result, op2(*it1,*it2)).

Initialiser les lments dune squence avec une valeur (en lincrmentant) 235

Voici un exemple simple illustrant le produit scalaire de deux vecteurs:


double V1[3] = { 0.5, 1.2, 5.4 }; double V2[3] = { 10.0, -2.7, 3.76 }; double p = std::inner_product(V1, V1+3, V2, 0.0); // p == 22.064

Initialiser les lments dune squence avec une valeur (en lincrmentant)
#include <numeric> void iota(ForwardIterator debut, ForwardIterator n, T valeur);

iota() affecte un un les lments de la squence [debut, n[ avec la valeur donne, en lincrmentant entre chaque affec tation. Ainsi, le code suivant remplira le tableau dentiers V avec les valeurs 7, 8, 9, ,16.
std::vector<int> V(10); std::iota( V.begin(), V.end(), 7);

Attention Cette fonction est une extension SGI et ne fait pas partie du standard. Elle est dcrite ici au cas o vous la rencontreriez dans du code existant ou si vous travailliez dans cet environnement. Vous pouvez la trouver dans <ext/numeric> dans lespace de noms __gnu_cxx avec g++.

236

CHAPITRE 12 Algorithmes standard

Transformer une squence en tas et lutiliser


#include <algorithm> bool is_heap(RandomAccessIterator debut, RandomAccessIterator n); bool is_heap(RandomAccessIterator debut, RandomAccessIterator n, StrictWeakOrdering comp); void make_heap(RandomAccessIterator debut,
RandomAccessIterator n);

bool make_heap(RandomAccessIterator debut, RandomAccessIterator n, StrictWeakOrdering comp); void sort_heap(RandomAccessIterator debut,


RandomAccessIterator n);

bool sort_heap(RandomAccessIterator debut, Random


AccessIterator n, StrictWeakOrdering comp);

void push_heap(RandomAccessIterator debut,


RandomAccessIterator n);

bool push_heap(RandomAccessIterator debut, Random


AccessIterator n, StrictWeakOrdering comp);

void pop_heap(RandomAccessIterator debut,


RandomAccessIterator n);

bool pop_heap(RandomAccessIterator debut, Random


AccessIterator n, StrictWeakOrdering comp);

is_heap() retourne vrai si la squence [debut, n[ est un tas, et faux sinon. La premire version utilise loprateur<, la seconde la relation dordre comp. make_heap() transforme lordre des lments de la squence de manire ce quelle forme un tas.

Transformer une squence en tas et lutiliser 237

sort_heap() transforme une squence sous forme de tas en une squence trie standard. Ce tri nest pas un tri stable: il ne garantit pas la prservation de lordre relatif dlments gaux. push_heap() ajoute un lment au tas, en postulant que [debut, n-1[ est dj un tas et que llment ajouter est *(n-1). pop_heap() supprime les plus grands lments du tas (en loccurrence *debut). Aprs excution, on retrouve llment supprim la position n-1, et les autre lments sont sous forme de tas dans la squence [debut, n-1[. Ainsi un push_heap() suivi dun pop_heap() revient ne rien faire en y passant du temps.
Info Un tas est une manire particulire dordonner les lments dune squence [debut, n[. Les tas sont utiles, particulirement pour les tris et les queues de priorit, car ils respectent deux proprits importantes. Premirement, *debut est le plus grand lment du tas. Deuximement, il est possible dajouter un lment un tas (en utilisant push_heap()), ou de supprimer *debut, en temps logarithmique. En interne, un tas est un arbre stock sous forme dune squence. Larbre est construit de tel faon que chaque nud soit plus petit ou gal son nud parent.

std::vector<int> tas(9); for (int i=0; i<=8; i++) tas[i]=i; assert(is_heap(tas.begin(),tas.end())==false); std::copy(tas.begin(),tas.end(),std::ostream_iterator <int>(std::cout, )); std::make_heap(tas.begin(), tas.end()); assert(is_heap(tas.begin(),tas.end())==true);

238

CHAPITRE 12 Algorithmes standard

std::copy(tas.begin(),tas.end(),std::ostream_iterator <int>(std::cout, )); tas.push_back(9); std::push_heap(tas.begin(),tas.end()); std::copy(tas.begin(),tas.end(),std::ostream_iterator <int>(std::cout, )); std::pop_heap(tas.begin(),tas.end()); std::copy(tas.begin(),tas.end(),std::ostream_iterator <int>(std::cout, )); std::sort_heap(tas.begin(), tas.end()); assert(is_heap(tas.begin(),tas.end())==false); std::copy(tas.begin(),tas.end(),std::ostream_iterator <int>(std::cout, ));

Lexemple prcdent produira la sortie suivante:


0 8 9 8 0 1 7 8 7 1 2 6 6 6 2 3 3 3 3 3 4 4 7 4 4 5 5 5 5 5 6 2 2 2 6 7 1 1 1 7 8 0 0 4 0 9 8 9

Comparer lexicographiquement deux squences


#include <algorithm> bool lexicographical_compare(InputIterator1 debut1, InputIterator1 n1, InputIterator2 debut2, InputIterator2 n2); bool lexicographical_compare(InputIterator1 debut1, InputIterator1 n1, InputIterator2 debut2, InputIterator2 n2, BinaryPredicate comp);

Comparer lexicographiquement deux squences 239

int lexicographical_compare_3way(InputIterator1
debut1, InputIterator1 n1, InputIterator2 debut2, InputIterator2 n2);

lexicographical_compare() retourne vrai si la squence [debut1, n1[ est lexicographiquement plus petite que la squence [debut2, n2[ et faux sinon. lexicographical_compare_3way() est une gnralisation de la fonctionC strcmp(). Elle retourne un nombre ngatif si la premire squence est (lexicographiquement) plus petite que la deuxime, un nombre positif si la deuxime est plus petite que la premire, et zro sinon (cest--dire si elles sont lexicographiquement quivalentes).
Info
lexicographical_compare_3way() peut paratre identique

au code suivant:

lexicographical_compare(d1,f1, d2,f2) ? -1 : (lexicographical_compare(d2,f2, d1,f1) ? -1 : 0) Cest vrai pour le rsultat obtenu, mais faux vis--vis des performances. Un appel lexicographical_compare_3way() est plus performant que deux appels lexicographical_ compare().

Attention Avec g++, lexicographical_compare_3way() se trouve dans le chier en-tte <ext/algorithm> et dans lespace de noms __gnu_cxx.

Lexemple ci-aprs montre que lon peut comparer des tableaux dentiers comme si lon comparait des chanes de

240

CHAPITRE 12 Algorithmes standard

caractres. Il donne un aperu des possibilits quoffrent ces fonctions.


int int int int A1[] A2[] A3[] A4[] = = = = { { { { 5, 5, 2, 2, = = = = 3, 3, 4, 4, 7, 7, 6, 6, 1, 9, 5, 8 }; 0, 7, 7, 5 }; 8 }; 8, 10}; / / / / sizeof(int); sizeof(int); sizeof(int); sizeof(int);

const const const const

int int int int

N1 N2 N3 N4

sizeof(A1) sizeof(A2) sizeof(A3) sizeof(A4)

int C12 = std::lexicographical_compare(A1, A1+N1, A2, A2+N2); int C34 = std::lexicographical_compare(A3, A3+N3, A4, A4+N4); int C12w = std::lexicographical_compare(A1, A1+N1, A2, A2+N2); int C34w = std::lexicographical_compare(A3, A3+N3, A4, A4+N4); int C34b = std::lexicographical_compare(A3, A3+N3, A4, A4+N4-1); std::cout << A1 < A2 == << (C12 ? vrai : faux) << std::endl; std::cout << A3 < A4 == << (C34 ? vrai : faux) << std::endl; std::cout << A1 ? A2 == << C12w << std::endl; std::cout << A3 ? A4 == << C34w << std::endl; std::cout << A3 ? A4 == << C34b << std::endl;

Cet exemple produira la sortie suivante:


A1 A3 A1 A3 A3 < < ? ? ? A2 == faux A4 == vrai A2 == 1 A4 == -1 A4 == 0

Chercher le premier/dernier endroit o insrer une valeur sans briser lordre dune squence

241

Chercher le premier/dernier endroit o insrer une valeur sans briser lordre dune squence
#include <algorithm> ForwardIterator lower_bound(ForwardIterator debut, ForwardIterator n, const LessThanComparable& valeur); ForwardIterator lower_bound(ForwardIterator debut, ForwardIterator n, const LessThanComparable& valeur, StrictWeakOrdering comp); ForwardIterator upper_bound(ForwardIterator debut,
ForwardIterator n, const LessThanComparable& valeur);

ForwardIterator upper_bound(ForwardIterator debut,


ForwardIterator n, const LessThanComparable& valeur, StrictWeakOrdering comp);

lower_bound() est une variante de binary_search(). Elle cherche et renvoie la premire position o la valeur donne peut tre insre dans la squence trie [debut, n[ sans rompre le tri. upper_bound() cherche et renvoie la dernire position possible. Dans les deux cas, la squence dentre est suppose trie selon loprateur< ou la relation dordre comp donne. Par exemple, le code suivant utilise ces fonctions pour insrer des valeurs tries selon les dizaines:
#include <iostream> #include <iterator> #include <vector>

242

CHAPITRE 12 Algorithmes standard

#include <ext/algorithm> struct CompareDizaines { bool operator()(int a, int b) const { return a/10 < b/10; } }; void afche(const std::vector<int>& V) { std::cout << V = ; std::copy(V.begin(), V.end(), std::ostream_iterator<int>(std::cout, )); std::cout << (; if (__gnu_cxx::is_sorted(V.begin(), V.end(), CompareDizaines()) == false) std::cout << NON ; std::cout << tri) << std::endl; } int main(int,char**) { std::vector<int> V; std::vector<int>::iterator it; int valeur; V.push_back(-123); V.push_back(- 45); V.push_back( 0); V.push_back( 50); V.push_back( 87); V.push_back( 83); V.push_back( 120); afche(V); valeur = 82; it = std::lower_bound(V.begin(), V.end(), valeur, CompareDizaines()); V.insert(it, valeur);

Fusionner deux squences tries 243

std::cout << \nAprs insertion lower_bound de << valeur << :\n; afche(V); valeur = 81; it = std::upper_bound(V.begin(), V.end(), valeur, CompareDizaines()); V.insert(it, valeur); std::cout << \nAprs insertion upper_bound de << valeur << :\n; afche(V); return 0; }

Ce code produit la sortie suivante:


V = -123 -45 0 50 87 83 120 (tri) Aprs insertion lower_bound de 82: V = -123 -45 0 50 82 87 83 120 (tri) Aprs insertion upper_bound de 81: V = -123 -45 0 50 82 87 83 81 120 (tri)

Fusionner deux squences tries


#include <algorithm> OutputIterator merge(InputIterator1 debut1, InputIterator1 n1, InputIterator2 debut2, InputIterator2 n2, OutputIterator resultat); OutputIterator merge(InputIterator1 debut1, InputIterator1 n1, InputIterator2 debut2, InputIterator2 n2, OutputIterator resultat, StrictWeakOrdering comp);

244

CHAPITRE 12 Algorithmes standard

void inplace_merge(BidirectionalIterator debut,


BidirectionalIterator milieu, BidirectionalIterator n);

void inplace_merge(BidirectionalIterator debut, BidirectionalIterator milieu, BidirectionalIterator n, StrictWeakOrdering comp);

merge() combine deux squences tries [debut1, n1[ et [debut2, n2[ en une seule squence trie. Les lments des deux squences dentre sont copis pour former la squence [resultat, resultat + (n1 debut1) + (n2 debut2)[. Les squences dentre ne doivent pas avoir de partie commune avec la squence rsultat. Notez galement que la fonction merge() est stable: sii prcdej dans une des squence dentre, alors la copie dei prcdera la copie dej dans la squence rsultat. inplace_merge() effectue le mme genre dopration. Si [debut, milieu[ et [milieu, n[ respectent lordre de tri< (ou comp), alors la n de inplace_merge(), [debut, n[ respecte lordre de tri< (ou comp). Par respecter lordre de tri, on entend que pour tous itrateursi etj dune squence, sii prcdej, alors *j < *i est faux (respectivement comp(*j,*i) est faux).
Attention La complexit de ces deux algorithmes est diffrente. merge() est linaire sur le nombre total dlments, soit O(N) oN vaut (n1 - debut1) + (n2 - debut2). inplace_merge() est un algorithme adaptatif: sa complexit dpend de la mmoire disponible. Si la premire squence est vide, aucune comparaison nest faite. Dans le pire des cas, lorsquil ny a pas de mmoire tampon possible, sa complexit est en O(N logN), oN vaut debut - n. Dans le meilleur des cas, lorsque lon peut allouer une mmoire tampon sufsante, il y a au plus (n - debut) - 1 comparaisons.

Rcuprer le plus petit/grand lment 245

Voici deux exemples illustrant ces fonctions:


int A[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8 }; std::inplace_merge(A, A + 5, A + 9); std::copy(A, A + 9, std::ostream_iterator<int> (std::cout, )); // La sortie est 1 2 3 4 5 6 7 8 int B[] = { 1, 3, 5, 7 }; int C[] = { 2, 4, 6, 8, 9 }; std:: merge(B, B + 4, C, C+5, std::ostream_iterator <int>(std::cout, )); // La sortie est 1 2 3 4 5 6 7 8 9

Rcuprer le plus petit/grand lment


#include <algorithm> const T& min(const T& x, const T& y); const T& min(const T& x, const T& y, BinaryPredicate comp); const T& max(const T& x, const T& y); const T& max(const T& x, const T& y, BinaryPredicate comp);

Comme lindique le nom trs explicite de ces fonctions, min() et max() retournent le minimum ou le maximum entre deux valeursx ety donnes en utilisant loprateur<, ou le prdicat de comparaison comp fourni. Pour illustrer ces fonctions, lexemple suivant parle de lui-mme:
std::cout << << std::cout << << Le maximum entre 1 et 99 est std::max(1, 99) << std::endl; Le minimum entre a et c std::max(a, c) << std::endl;

246

CHAPITRE 12 Algorithmes standard

Info Cet algorithme est plus puissant quune macro. Il vite en effet dans certains cas deffectuer des calculs inutiles. Cest le cas par exemple lorsque vous souhaitez obtenir le maximum entre le rsultat de deux appels de fonction, comme max(sqrt(3), log(27)).

Rcuprer le plus petit/grand lment dune squence


#include <algorithm> ForwardIterator min_element(ForwardIterator debut, ForwardIterator n); ForwardIterator min_element (ForwardIterator debut, ForwardIterator n, BinaryPredicate comp); ForwardIterator max_element(ForwardIterator debut,
ForwardIterator n);

ForwardIterator max_element (ForwardIterator debut,


ForwardIterator n, BinaryPredicate comp);

min_element() retourne le plus petit lment de la squence [debut, n[. Plus exactement, il retourne le premier itrateuri appartenant la squence donne tel quaucun autre itrateur de cette squence pointe vers un lment plus grand que*i. Cette fonction retourne n uniquement si la squence donne est une squence vide. La deuxime version de min_element() diffre de la premire en ce quelle nutilise pas loprateur< mais le prdicat binaire comp pour comparer les lments. Ainsi litrateuri retourn vrie la condition comp(*j, *i) == false pour toutj (autre quei lui-mme) de la squence.

Trouver le premier endroit o deux squences diffrent 247

max_element() fait la mme chose mais renvoie le plus grand lment. Litrateuri retourn vrie le test (*i < *j) == false ou comp(*i, *j) == false.
int tableau[] = { 5, 6, 2, 8, 5, 8, 0 }; int N = sizeof(tableau) / sizeof(int); int* i = std::max_element(tableau, tableau+N); std::cout << Le plus grand est << *i << la << i-tableau+1 << -me position.\n;

Cet exemple produit la sortie suivante:


Le plus grand est 8 la 4-me position.

Trouver le premier endroit o deux squences diffrent


#include <algoritmh> std::pair<InputIterator1, InputIterator2> mismatch(InputIterator1 debut1, InputIterator1 n1, InputIterator2 debut2); std::pair<InputIterator1, InputIterator2> mismatch(InputIterator1 debut1, InputIterator1 n1, InputIterator2 debut2, BinaryPredicate comp);

mismatch() compare les lments de la premire squence avec ceux de la deuxime, que lon suppose de mme taille (sinon les lments supplmentaires sont ignors), et renvoie le premier endroit o les lments diffrent. Les lments sont compars, soit avec loprateur==, soit avec le prdicat de comparaison comp fourni.

248

CHAPITRE 12 Algorithmes standard

int A1[] = { 3, 1, 3, 5, 3, 7, 8 }; int A2[] = { 3, 1, 3, 5, 4, 9, 2 }; int N = sizeof(A1) / sizeof(int); std::pair<int*, int*> res = std::mismatch(A1, A1+N, A2); std::cout << La premire diffrence se situe lindice << res.rst A1 << std::endl << Les valeurs sont: << *(res.rst) << et << *(res.second) << std::endl;

Gnrer la prochaine plus petite/grande permutation lexicographique dune squence


#include <algorithm> bool next_permutation(BidirectionalIterator debut, BinirectionalIterator n); bool next_permutation(BidirectionalIterator debut, BinirectionalIterator n, StrictWeakOrdering comp); bool prev_permutation(BidirectionalIterator debut,
BinirectionalIterator n);

bool prev_permutation(BidirectionalIterator debut,


BinirectionalIterator n, StrictWeakOrdering comp);

next_permutation() transforme la squence [debut, n[ donne en la prochaine plus grande permutation, lexicographiquement parlant. Il y a un nombre ni de permutations distinctes de Nlments: au plusN! (factoriel N). Ainsi, si ces permutations sont ordonnes en ordre lexicographique, il y a une dnition non ambigu de ce que

Gnrer la prochaine plus petite/grande permutation lexicographique dune squence 249

signie la prochaine plus grande permutation. Donc, si une telle permutation existe, next_permutation() transforme [debut, n[ en celle-ci et renvoie vrai, sinon il la transforme en la plus petite et renvoie faux. Si une relation dordre stricte comp est fournie, elle est utilise comme comparaison dlment pour dnition de lordre lexicographique la place de loprateur<. prev_permutation() est linverse de next_permutation(). Elle transforme la squence en la prcdente permutation, selon le mme critre que prcdemment.
template<class BidiIter> void le_pire_des_tris(BidiIter deb, BidiIter n) { while (std::next_permutation(deb, n)) { } } int main(int, char**) { int A[] = { 8, 3, 6, 1, 2, 5, 7, 4 }; int N = sizeof(A) / sizeof(int); le_pire_des_tris(A, A+N); std::copy(A, A+N, std::ostream_iterator<int>(std:: cout, )); return 0; }

Cet exemple utilise next_permutation() pour implmenter la pire des manires de faire un tri. La plupart des algorithmes de tri sont en O(nlogn), et mme le tri bulles est seulement en O(n). Celui-ci est en O(n!).

250

CHAPITRE 12 Algorithmes standard

Faire en sorte que le nime lment soit le mme que si la squence tait trie
#include <algorithm> void nth_element(RandomAccesIterator debut, RandomAccesIterator n_ieme, RandomAccesIterator n); void nth_element(RandomAccesIterator debut, RandomAccesIterator n_ieme, RandomAccesIterator n, StrickWeakOrdering comp);

nth_element() est comparable partial_sort() en ce sens quelle ordonne une partie des lments de la squence. Elle arrange la squence donne de telle sorte que le n_ieme itrateur pointe sur le mme lment que si la squence avait t trie compltement. De plus, larrangement est tel quaucun lment de la squence [n_ieme, n[ soit plus petit quun des lments de la squence [debut, n_ieme[. Encore une fois, cette fonction utilise soit loprateur<, soit la relation dordre stricte comp fournie pour ordonner les lments de la squence [debut, n[.
Info des sous-squences gauche et droite ne sont tries. Elle garantit seulement que tous les lments de la partie gauche sont plus petits que ceux de la partie droite. En ce sens, nth_element() est plus comparable une partition qu un tri. Elle fait moins que partial_sort() et est donc galement plus rapide. Pour cette raison, il vaut mieux utiliser nth_element() plutt que partial_sort() lorsquelle est sufsante pour vos besoins.
nth_element() diffre de partial_sort() en ce sens quaucune

Trier les n premiers lments dune squence

251

int A[] = { 7, 2, 6, 11, 9, 3, 12, 10, 8, 4, 1, 5 }; const int N = sizeof(A) / sizeof(int); std::nth_element(A, A+6, A+N); std::copy(A, A+6, std::ostream_iterator<int>( std:: cout, )); std::cout << / ; std::copy(A+6, A+N, std::ostream_iterator<int>( std::cout, )); // Afchera : 5 2 6 1 4 3 / 7 8 9 10 11 12

Trier les n premiers lments dune squence


#include <algorithm> void partial_sort(RandomAccessIterator debut, RandomAccessIterator milieu, RandomAccesIterator n); void partial_sort(RandomAccessIterator debut, RandomAccessIterator milieu, RandomAccesIterator n, StrictWeakOrdering comp);

partial_sort() rordonne les lments de la squence [debut, n[ de telle sorte quils soient partiellement dans lordre croissant. Il place les milieu-debut plus petits lments, dans lordre croissant, dans la squence [debut, milieu[. Les n-milieu lments restants se retrouvent, sans ordre particulier, dans la squence [milieu, n[. Cette fonction utilise la relation dordre partielle comp fournie, sinon cest loprateur< qui est utilis. Cet algorithme effectue environ (n debut) * log( middle rst) comparaisons.

252

CHAPITRE 12 Algorithmes standard

Info
partial_sort(debut, n, n) produit le mme rsultat que sort(debut, n). Notez toutefois que lalgorithme utilis nest pas le mme: sort() utilise introsort, une variante de quicksort, alors que partial_sort() utilise un heapsort (tri

par tas). Mme si ces deux types de tris ont une complexit analogue, en O(NlogN), celui de sort() est2 5fois plus rapide que celui de partial_sort(). Ainsi, pour trier la totalit dune squence, prfrez sort() partial_sort().

Voici un petit exemple pour visualiser leffet de cet algorithme:


int A[] = { 7, 2, 6, 13, 9, 3, 14, 11, 8, 4, 1, 5 }; const int N = sizeof(A) / sizeof(int); std::partial_sort(A, A + 5, A+ N); std::copy(A, A+N, std::ostream_iterator<int>(std::cout, )); // Donne: 1 2 3 4 5 13 14 11, 9, 8, 7, 6.

Copier les n plus petits lments dune squence


#include <algorithm> RandomAccessIterator partial_sort_copy (RandomAccessIterator debut, RandomAccesIterator n, RandomAccessIterator debut_resultat, RandomAccesIterator n_resultat); RandomAccessIterator partial_sort_copy (RandomAccessIterator debut, RandomAccesIterator n, RandomAccessIterator debut_resultat, RandomAccesIterator n_resultat, StrictWeakOrdering comp);

Calculer une somme partielle gnralise dune squence 253

partial_sort_copy() copie les N plus petits lments de la squence [debut, n[ dans la squence [debut_resultat, n_ resultat[, oN est la taille de la plus petite des deux squences donnes. Les lments de la squence rsultat sont bien sr ordonns, suivant la relation dordre comp fournie ou< le cas chant. Cette fonction retourne ensuite litrateur debut_resultat + N.
int A[] = { 7, 2, 6, 11, 9, 3, 12, 10, 8, 4, 1, 5 }; const int N = sizeof(A) / sizeof(int); std::vector<int> V(4); std::partial_sort_copy(A, A+N, V.begin(), V.end()); std::copy(V.begin(), V.end(), std::ostream_iterator <int>(std::cout, ));

Lexemple prcdent produit le rsultat suivant:


1 2 3 4

Calculer une somme partielle gnralise dune squence


#include <numeric> OutputIterator partial_sum(InputIterator debut, InputIterator n, OutputIterator res); OutputIterator partial_sum(InputIterator debut, InputIterator n, OutputIterator res, BinaryOperation op);

254

CHAPITRE 12 Algorithmes standard

partial_sum() calcule une somme partielle gnralise. Pour faire simple, cest un peu lquivalent du code suivant:
res[0] = debut[0]; for (int i=1; i < n-debut; i++) { res[i] = res[i-1] + debut[i]; // ou res[i] = op(res[i-1], debut[i]); }

Cette fonction retourne litrateur res+(n-debut).


Info
res peut avoir la mme valeur que debut. Cela peut tre utile lorsque vous voulez stocker le rsultat directement dans la squence dorigine.

De plus, tant donn que lordre des oprations est totalement dni, il nest pas ncessaire que loprateur op que vous fournissez soit associatif ou commutatif.

Couper la squence en deux en fonction dun prdicat


#include <algorithm> ForwardIterator partition(ForwardIterator debut, ForwardIterator n, Predicate pred); ForwardIterator stable_partition(ForwardIterator debut, ForwardIterator n, Predicate pred);

partition() rordonne les lments de la squence [debut, n[ de telle sorte quil existe un itrateur milieu appartenant

Couper la squence en deux en fonction dun prdicat 255

la squence tel que: les lments de la sous-squence [debut, milieu[ vrient pred(*i)==true, et ceux de la soussquence [milieu, n[ vrient pred(*i)==false. La fonction retourne cet itrateur milieu. stable_partition() diffre de partition() en ce sens quelle prserve lordre relatif des lments. Ainsi, six ety sont des lments tels que pred(x) == pred(y) et quex prcdey, alorsx prcdera toujoursy aprs lexcution de stable_sort().
Astuce Les fonctions stables sont toujours plus lentes que les fonctions dont la stabilit (au sens de la prservation de lordre des lments et non de la prsence de bogues, bien sr) nest pas garantie. Ne les utilisez donc que si cette proprit de stabilit vous est ncessaire.

Lexemple ci-aprs montre comment sparer les nombres pairs et les nombres impairs dune squence. Les nombres pairs sont ici placs au dbut de la squence.
std::vector<int> A(10); for (int i=0; i<10; i++) A[i] = i+1; std::partition(A.begin(), A.end(), std::compose1(std::bind2nd( std::equal_to<int>(), 0), std::bind2nd( std::modulus<int>(), 2))); // La squence contient alors les valeurs: // 10 2 8 4 6 5 7 3 9 1.

256

CHAPITRE 12 Algorithmes standard

Calculer xi (fonction puissance gnralise)


#include <numeric> T power(T x, Integer n); T power(T x, Integer n, MonoidOperation op);

power() est une gnralisation de la fonction puissance xn, on est un entier positif. La premire version retourne x *x *x * x, ox est rpt nfois. Si n == 0, alors elle retourne std::identity( std:: multiplies<T>() ). La deuxime version est identique lexception quelle utilise la fonctionop au lieu de multiplies<T> et retourne std::identity(op) si n == 0.
Attention
power() ne repose pas sur le fait que la multiplication est commutative, mais quelle est associative. Si vous dnissez un oprateur* ou une oprationop qui nest pas associatif, alors power() donnera une mauvaise rponse.

Info Une monoid operation est une fonction binaire particulire. Une fonction binaire doit satisfaire trois conditions pour tre une opration monode. Premirement, le type de ses deux arguments et de sa valeur de retour doivent tre les mmes. Deuximement, il doit exister un lment identit. Troisimement, lopration doit tre associative. Par exemple, laddition et la multiplication sont des oprations monodes. Lassociativit implique que f(x, f(y,z)) == f(f(x,y), z). Llment identit id respecte les conditions f(x, id) == x et f(id, x) == x.

Copier alatoirement un chantillon dune squence 257

Info
power() fait partie des extensions SGI et peut tre code dans un autre espace de noms que std, si elle est prsente dans limpl mentation de votre compilateur.

Copier alatoirement un chantillon dune squence


#include <algorithm> RandomAccessIterator random_sample(InputIterator debut,InputIterator n, RandomAccessIterator rDebut, RandomAccesIterator rFin); RandomAccessIterator random_sample(InputIterator debut, InputIterator n, RandomAccessIterator rDebut, RandomAccesIterator rFin, RandomNumberGenerator& rand);

random_sample() copie alatoirement un chantillon de la squence [debut, n[ dans la squence [rDebut, rFin[. Chaque lment de la squence dentre apparatra au plus une fois dans celle de sortie. Les lments sont tirs selon une loi de probabilit uniforme. Lordre relatif des lments de la squence dentre nest pas prserv (si vous en avez besoin, jetez un il sur random_sample_n()). Le nombre n dlments copis est le minimum entre n - debut et rFin - rDebut. Du coup, la fonction renvoie litrateur rDebut + n.
Attention Si vous spciez un gnrateur de nombres alatoires en paramtre, celui-ci doit produire une distribution uniforme. Cest--dire que la frquence dapparition de chaque valeur doit tre la mme.

258

CHAPITRE 12 Algorithmes standard

const int N = 10; const int n = 4; int A[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; int B[n]; std::random_sample(A, A+N, B, B+n); std::copy(B, B+n, std::ostream_iterator<int>(std::cout, ));

Le code prcdent afche une des 5039 possibilits existantes, comme celle-ci:
7 1 5 2

Copier alatoirement un souschantillon (de nlments), en prservant leur ordre dorigine


#include <algorithm> OutpuIterator random_sample_n(ForwardIterator debut, ForwardIterator n, OutpuIterator res, Distance n); OutpuIterator random_sample_n(ForwardIterator debut, ForwardIterator n, OutpuIterator res, Distance n, RandomNumberGenerator& rand);

random_sample_n() copie alatoirement un sous-chantillon (den lments sil y en a assez) de la squence [debut, n[ dans la squence [res, res+n[. Chaque lment de la squence dentre apparatra au plus une fois dans celle de sortie. Les lments sont tirs selon une loi de probabilit uniforme. Lordre relatif des lments de la squence dentre est prserv (si vous ne le voulez pas, jetez un il sur random_sample()).

Mlanger les lments dune squence 259

En ralit, le nombre m dlments copis est le minimum entre n - debut etn. Du coup, la fonction renvoie litrateur res + m. Comme avec random_sample(), vous pouvez fournir votre propre gnrateur de nombres alatoires.
const int N = 10; int A[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; std::random_sample_n(A, A+N, std::ostream_iterator <int>(std::cout, ), 4);

Le code prcdent afche une des 209 possibilits existantes, comme celle-ci:
1 2 5 7

Mlanger les lments dune squence


#include <algorithm> void random_shufe(RandomAccesIterator debut, RandomAccesIterator n); void random_shufe(RandomAccesIterator debut, RandomAccesIterator n, RandomNumberGenerator& rand);

random_shufe() mlange alatoirement les lments de la squence [debut, n[. Cette squence se retrouve alors dans un des N!1 ragencements possibles, o Nest la taille de la squence. Une fois encore, vous avez la possibilit dutiliser votre propre gnrateur de nombres alatoires.

260

CHAPITRE 12 Algorithmes standard

const int N = 10; int A[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; std::random_shufe(A, A+N); std::copy(A, A+N, std::ostream_iterator<int> (std::cout, ));

Le code prcdent afche une des 3628899 (soit 10!1) autres possibilits existantes, comme celle-ci:
9 8 3 1 6 5 2 4 10 7

Supprimer certains lments dune squence


#include <algorithm> ForwardIterator remove(ForwardIterator deb, ForwardIterator n, const T& valeur); ForwardIterator remove_if(ForwardIterator deb, ForwardIterator n, Predicate test);

remove() supprime de la squence [debut, n[ tous les lments gaux la valeur donne, puis retourne le nouvel itrateur de n. Cette fonction est stable: elle prserve lordre relatif des lments conservs. remove_if() fait de mme en supprimant les lments vriant le test donn.

Supprimer certains lments dune squence

261

Attention Le sens du mot supprimer est quelque peu dform dans le contexte de ces fonctions. remove() et remove_if() ne dtruisent aucun itrateur. Ainsi la distance entre deb et n restera inchange. Par exemple, si la squenceV donne est un vecteur (std::vector<>), alors V.size() retournera la mme taille avant et aprs la suppression. Litrateur renvoy indique donc que les lments qui le suivent sont sans intrt, et les valeurs des lments sur lesquels ils pointent ne sont pas garanties. Si vous voulez rellement supprimer les lments dune squenceS, vous pouvez utiliser une formule de ce type: S.erase (std::remove(S.begin, S.end(), x), S.end()).

std::vector<int>::iterator n2; std::vector<int> V; V.push_back(3); V.push_back(5); V.push_back(2); V.push_back(3); V.push_back(8); V.push_back(7); n2 = std::remove(V.begin(), V.end(), 3); std::copy(V.begin(), n2, std::ostream_iterator <int> (std::cout, )); // Afche: 5 2 8 7

262

CHAPITRE 12 Algorithmes standard

Copier une squence en omettant certains lments


#include <algorithm> OutputIterator remove_copy(ForwardIterator deb, ForwardIterator n, OutputIterator res, const T& valeur); OutputIterator remove_copy_if(ForwardIterator deb, ForwardIterator n, OutputIterator res, Predicate test);

remove_copy() copie tous les lments non gaux la valeur donne de la squence [debut, n[ dans la squence commenant litrateur res. Cette fonction est stable: elle prserve lordre relatif des lments conservs. remove_copy_if() fait de mme en copiant les lments ne vriant pas le test donn.
Attention La squence rsultat doit tre assez grande pour contenir lensemble des lments qui y seront copis. Nhsitez pas faire usage des std::back_insert_iterator, std::front_ insert_iterator ou autre utilitaire dans ce contexte.

std::vector<int> V; V.push_back(3); V.push_back(5); V.push_back(2); V.push_back(3); V.push_back(8); V.push_back(7); std::remove_copy_if(V.begin(), V.end(), std::ostream_iterator<int>(std::cout, ), std::bind2nd(std::less_equal<int>(),3) ); // Afche: 5 8 7

Remplacer certains lments dune squence 263

Remplacer certains lments dune squence


#include <algorithm> void replace(ForwardIterator deb, ForwardIterator n, const T& ancienne_valeur, const T& nouvelle_valeur); void replace_if(ForwardIterator deb, ForwardIterator n, Predicate test, const T& nouvelle_valeur); OutputIterator replace_copy(InputIterator deb,
InputIterator n, OutputIterator res, const T& ancienne_valeur, const T& nouvelle_valeur);

OutputIterator replace_copy_if(InputIterator deb,


InputIterator n, OutputIterator res, Predicate test, const T& nouvelle_valeur);

replace() remplace tous les lments de [deb, n[ gaux lancienne_valeur par la nouvelle_valeur. Ainsi tous les itrateursi tels que *i == ancienne_valeur alors *i = nou velle_valeur. replace_if() remplace les lments vriant le prdicat test(*i) == true. replace_copy() et replace_copy_if() procde de mme mais effectue les remplacements sur (et pendant) la copie.
std::vector<int> V; V.push_back(3); V.push_back(5); V.push_back(2); V.push_back(3); V.push_back(8); V.push_back(7); std::list<int> V2; std::replace_copy_if(V.begin(), V.end(), std::front_insert_iterator< std::list<int> >(V2), std::bind2nd(std::less_equal<int>(),3), 0 );

264

CHAPITRE 12 Algorithmes standard

std::copy(V2.begin(), V2.end(), std::ostream_iterator<int>(std::cout, )); // Afche: 7 8 0 2 5 0

Inverser lordre de la squence


#include <algorithm> void reverse(BidirectionalIterator deb, BidirectionalIterator n); OutputIterator reverse_copy(BidirectionalIterator deb, BidirectionalIterator n, OutputIterator res);

reverse() inverse lordre des lments de la squence. reverse_copy() copie la squence en inversant lordre lors de la copie. Si A est un vecteur contenant les lments 1, 2, 3, 4, 5, alors le code suivant inverse lordre de ses lments de sorte quil contienne 5, 4, 3, 2, 1.
std::reverse(A.begin(), A.end());

Effectuer une rotation des lments de la squence


#include <algorithm> ForwardIterator rotate(ForwardIterator debut, ForwardIterator milieu, ForwardIterator n); OutputIterator rotate_copy(ForwardIterator debut, ForwardIterator milieu, ForwardIterator n, OutputIterator res);

Chercher une sous-squence 265

rotate() effectue une rotation des lments de la squence. Ainsi, llment la position milieu est dplac la position debut, llment la position milieu + 1 est dplac la position debut + 1, et ainsi de suite. Une autre manire de voir les choses est de dire que cette fonction change les deux sous-squences [debut, milieu[ et [milieu, n[. Enn, cette fonction retourne lquivalent de debut + (n milieu), la nouvelle position du premier lment de la squence dentre.
Info Certaines implmentations de rotate() retournent void.

rotate_copy() copie le rsultat dans la squence commenant la position res plutt que dcraser la squence dorigine, puis retourne result + (last - rst).
char alphabet[] = abcdefghijklmnopqrstuvwxyz; std::rotate(alphabet, alphabet + 5, alphabet + 26); std::cout << alphabet << std::endl; // Afchera : fghijklmnopqrstuvwxyzabcde

Chercher une sous-squence


#include <algorithm> ForwardIterator1 search(ForwardIterator1 debut1, ForwardIterator1 n1, ForwardIterator2 debut2, ForwardIterator2 n2); ForwardIterator1 search(ForwardIterator1 debut1, ForwardIterator1 n1, ForwardIterator2 debut2, ForwardIterator2 n2, BinaryPredicate comp);

266

CHAPITRE 12 Algorithmes standard

ForwardIterator search_n(ForwardIterator debut,


ForwardIterator n, Size n, const T& valeur);

ForwardIterator search_n(ForwardIterator debut,


ForwardIterator n, Size n, const T& valeur, BinaryPredicate comp);

ForwardIterator1 nd_end(ForwardIterator1 debut1,


ForwardIterator1 n1, ForwardIterator2 debut2, ForwardIterator2 n2);

ForwardIterator1 nd_end(ForwardIterator1 debut1,


ForwardIterator1 n1, ForwardIterator2 debut2, ForwardIterator2 n2, BinaryPredicate comp);

La fonction search() cherche la premire sous-squence identique [debut2, n2[ apparaissant dans [debut1, n1[. La fonction search_n() cherche la premire sous-squence den occurrences conscutives de valeur dans [debut1, n1[. La fonction nd_end() porte mal son nom et aurait d sappeler search_end() car son comportement est plus proche de search() que de nd(). Comme search(), elle cherche une sous-squence de [debut1, n1[ qui soit identique [debut2, n2[. La diffrence tient au fait que search() cherche la premire occurrence, alors que nd_end() cherche la dernire. Si elle existe, nd_end() retourne un itrateur sur le dbut de la sous-squence trouve; sinon, elle retourne n1. La premire version de nd_end() utilise loprateur ==. La deuxime le prdicat comp donn en testant si comp( *(i + (j-debut2)), *j) est vrai pour uni donn de la premire squence et pour toutj de la squence cherche. Le mme principe est valable pour search() et search_n().

Chercher une sous-squence 267

Le programme suivant illustre lutilisation de ces diffrentes fonctions:


#include #include #include #include #include <iostream> <string> <vector> <algorithm> <iterator>

using namespace std; bool ci_equal(char ch1, char ch2) { return toupper((unsigned char)ch1) == toupper((unsigned char)ch2); } int main(int, char**) { string s = Exeexecutable.exe; string t = exe; string::iterator i; i = nd_end(s.begin(), s.end(), t.begin(), t.end()); if (i != s.end()) { cout << nd_end => Trouv position ; cout << (i-s.begin()) << : ; copy(i,i+t.size(),ostream_iterator<char>(cout)); cout << << endl; } i = search(s.begin(), s.end(), t.begin(), t.end()); if (i != s.end()) { cout << search => Trouv position ; cout << (i-s.begin()) << : ; copy(i,i+t.size(),ostream_iterator<char>(cout)); cout << << endl; } i = search(s.begin(), s.end(), t.begin(), t.end(), ci_equal); if (i != s.end())

268

CHAPITRE 12 Algorithmes standard

{ cout << search+pred => Trouv position ; cout << (i-s.begin()) << : ; copy(i,i+t.size(),ostream_iterator<char>(cout)); cout << << endl; } i = search_n(s.begin(), s.end(), 2, e, ci_equal); if (i != s.end()) { cout << search_n => Trouv position ; cout << (i-s.begin()) << : ; copy(i,i+2,ostream_iterator<char>(cout)); cout << << endl; } return 0; }

Il produit la sortie suivante:


nd_end => Trouv position 14 search => Trouv position 3 search+pred => Trouv position 0 search_n => Trouv position 2 : : : : exe exe Exe ee

Construire la diffrence de deux squences tries


#include <algorithm> OutputIterator set_difference(InputIterator1 debut1, InputIterator1 nt1, InputIterator2 debut2, InputIterator2 n2, OutputIterator res); OutputIterator set_difference(InputIterator1 debut1, InputIterator1 n1, InputIterator2 debut2, InputIterator2 n2, OutputIterator res, StrictWeakOrdering comp);

Construire la diffrence de deux squences tries 269

set_difference() construit la diffrence des deux squences tries [debut1, n1[ et [debut2, n2[. Le rsultat est une squence trie commenant litrateur res donn et nissant litrateur retourn par la fonction. Dune manire simple, cette diffrence correspond celle de la thorie des ensembles. Le rsultat est lensemble des lments de [debut1, n1[ qui ne sont pas dans [debut2, n2[. La ralit est un peu plus complexe: des lments peuvent apparatre plusieurs fois dans chacune des squences. Dans ce cas, si un lment apparat mfois dans la premire squence et nfois dans la deuxime, alors il sera max(m-n, 0) fois dans la squence rsultat.
Info Cette fonction est stable car elle prserve aussi lordre dapparition des lments considrs comme identiques.

La deuxime version de set_difference() utilise la relation dordre donne au lieu de loprateur<.


inline bool lt_nocase(char c1, char c2) { return std::tolower(c1) < std::tolower(c2); } int main() { int A1[] = {1, 3, 5, 7, 9, 11}; int A2[] = {1, 1, 2, 3, 5, 8, 13}; char A3[] = {a, b, b, B, B, f, g, h, H}; char A4[] = {A, B, B, C, D, F, F, H }; const int N1 = sizeof(A1) / sizeof(int);

270

CHAPITRE 12 Algorithmes standard

const int N2 = sizeof(A2) / sizeof(int); const int N3 = sizeof(A3); const int N4 = sizeof(A4); std::cout << Difference A1 - A2 : ; std::set_difference(A1, A1 + N1, A2, A2 + N2, std::ostream_iterator<int>(std::cout, )); std::cout << std::endl << Difference A3 - A4 : ; std::set_difference(A3, A3 + N3, A4, A4 + N4, std::ostream_iterator<char>(std::cout, ), lt_nocase); std::cout << std::endl; }

Cet exemple produira la sortie ci-aprs. Notez bien la manire dont elle conserve les lments: ce sont les premires occurrences identiques qui sont supprimes.
Difference A1 - A2 : 7 9 11 Difference A3 - A4 : B B g H

Construire lintersection de deux squences tries


#include <algorithm> OutputIterator set_intersection(InputIterator1 debut1, InputIterator1 nt1, InputIterator2 debut2, InputIterator2 n2, OutputIterator res); OutputIterator set_intersection(InputIterator1 debut1, InputIterator1 n1, InputIterator2 debut2, InputIterator2 n2, OutputIterator res, StrictWeakOrdering comp);

Construire lintersection de deux squences tries

271

set_intersection() construit lintersection des deux squence tries [debut1, n1[ et [debut2, n2[. Le rsultat est une squence trie commenant litrateur res donn et nissant litrateur retourn par la fonction. Cette intersection correspond celle de la thorie des ensembles. Le rsultat est lensemble des lments qui sont la fois des lments de [debut1, n1[ et de [debut2, n2[. La ralit est un peu plus complexe: des lments peuvent apparatre plusieurs fois dans chacune des squences. Dans ce cas, si un lment apparat mfois dans la premire squence et nfois dans la deuxime, alors il sera min(m,-n) fois dans la squence rsultat.
Info Cette fonction est stable car elle prserve aussi lordre dapparition des lments considrs comme identiques.

La deuxime version de set_intersection() utilise la relation dordre donne au lieu de loprateur<. En reprenant les valeurs de lexemple de set_difference() mais en appliquant set_intersection(), on obtient le rsultat suivant:
Intersection de A1 et A2 : 1 3 5 Intersection de A3 et A4 : a b b f h

Vous remarquerez que ce sont les premires apparitions de la premire squence qui sont conserves.

272

CHAPITRE 12 Algorithmes standard

Construire la diffrence symtrique des deux squences tries


#include <algorithm> OutputIterator set_symmetric_difference(InputIterator1 debut1, InputIterator1 n1, InputIterator2 debut2, InputIterator2 n2, OutputIterator res); OutputIterator set_symmetric_difference(InputIterator1 debut1, InputIterator1 n1, InputIterator2 debut2, InputIterator2 n2, OutputIterator res, StrictWeakOrdering comp);

set_symmetric_difference() construit la diffrence symtrique des deux squences tries [debut1, n1[ et [debut2, n2[. Cette nouvelle squence est aussi trie selon loprateur< ou comp fourni. Litrateur retourn est la n de cette squence rsultat. L encore, dans le cas simple, set_symmetric_difference() effectue un calcul de la thorie des ensembles: elle construit lunion des deux ensembles A B et B A, oA etB sont les deux squences dentre. La squence rsultatR contient une copie des lments deA qui ne sont pas dansB, et une copie des lments deB qui ne sont pas dansA. Lorsque A et/ou B contiennent des lments semblables, la rgle suivante est applique: si un lment apparat mfois dansA et nfois dansB, alors il apparat |m n| fois dansR. Cette fonction est stable et prserve lordre dapparition des lments semblables qui se suivent. En reprenant encore les valeurs de lexemple de set_difference() mais en appliquant cette fois-ci set_symmetric_ difference(), on obtient le rsultat suivant:

Construire lunion de deux squences tries 273

Intersection symtrique de A1 et A2 : 1 2 7 8 9 11 13 Intersection symtrique de A3 et A4 : B B C D F g H

Si m > n, alors les m n derniers lments deA sont conservs. Si n < m, alors les n m derniers lments deB sont conservs.

Construire lunion de deux squences tries


#include <algorithm> OutputIterator set_union(InputIterator1 debut1, InputIterator1 n1, InputIterator2 debut2, InputIterator2 n2, OutputIterator res); OutputIterator set_union(InputIterator1 debut1, InputIterator1 n1, InputIterator2 debut2, InputIterator2 n2, OutputIterator res, StrictWeakOrdering comp);

set_union() construit la squence trie rsultant de lunion des deux squences tries fournies. Le tri correspond soit loprateur<, soit la relation dordre partielle stricte comp fournie. Dans le cas simple, cette fonction correspond lunion de la thorie des ensembles. Le rsultat contient une copie de chaque lment apparaissant dans lun, lautre ou les deux squences donnes. Le cas gnral repose sur cette rgle: si un lment apparat mfois dans [debut1, n1[ et nfois dans [debut2, n2[, alors il apparatra max(m, n) fois dans le rsultat. Une fois encore, cette fonction est stable et prserve lordre relatif des lments semblables.

274

CHAPITRE 12 Algorithmes standard

Un set_union() sur les valeurs des exemples prcdents (que lon retrouve dans lexemple de set_difference()) produira le rsultat suivant:
Union de A1 et A2 : 1 1 2 3 5 7 8 9 11 13 Union de A3 et A4 : a b B B C D f F H h

Si un lment apparat mfois dans [debut1, n1[ et nfois dans [debut2, n2[, alors les mlments de [debut1, n1[ puis les max(n-m, 0) premiers de [debut2, n2[ seront copis dans le rsultat. Notez que cette information nest utile que si vous nutilisez pas une relation dordre totale mais partielle; cas dans lesquels des lments peuvent tre quivalents (ou semblables) pas mais gaux, an de bien comprendre quels lments sont conservs.

Trier une squence


#include <algorithm> bool is_sorted(ForwardIterator debut, ForwardIterator n); bool is_sorted(ForwardIterator debut, ForwardIterator n, StreakWeakOrdering comp); bool sort(ForwardIterator debut, ForwardIterator n); bool sort(ForwardIterator debut, ForwardIterator n, StreakWeakOrdering comp);

is_sorted() teste si la squence est trie ou non selon la relation dordre comp (ou<). Pour cela, elle teste si deux lments conscutifsa etb sont tels que comp(b,a) (ou b<a) est faux.

changer le contenu de deux variables, itrateurs ou squences 275

sort() trie la squence selon la relation dordre donne. stable_sort() trie galement la squence selon la relation dordre donne, mais prserve lordre dorigine des lments de valeurs quivalentes: cest un tri stable. Utilisezle uniquement si cette caractristique vous est ncessaire (le tri stable est plus lent que le tri simple).
int A[] = { 1, 4, 7, 2, 5, 7, 9 }; const int N = sizeof(A) / sizeof(int); assert( is_sorted(A,A+N) == false ); std::sort(A, A+N); assert( is_sorted(A,A+N) == true );

Attention Avec g++, is_sorted() se trouve dans <ext/algorithm> dans lespace de noms __gnu_cxx.

changer le contenu de deux variables, itrateurs ou squences


#include <algorithm> void swap(Assignable& a, Assignable& b); void iter_swap(ForwardIterator1 a, ForwardIterator2 b); void swap_ranges(ForwardIterator1 debut1, ForwardIterator1 n1, ForwardIterator2 debut2, ForwardIterator2 n2);

swap() change le contenu de deux variables. iter_swap() est quivalent swap(*a, *b). Strictement parlant, cette fonction est redondante et serait donc inutile.

276

CHAPITRE 12 Algorithmes standard

Sa prsence est due une raison technique: certains compilateurs ont parfois du mal dduire le type requis pour appeler la bonne instanciation de swap(*a, *b). swap_range() change le contenu de deux squences de mme taille. Les deux squences ne doivent pas avoir de partie commune.
std::vector<int> A,B; A.push_back(1); A.push_back(2); // A = { 1, 2 } B.push_back(3); B.push_back(4); // B = { 3, 4 } std::swap(A[0],A[1]); // A = { 2, 1 } et B = { 3, 4 } std::iter_swap(A.begin(), B.begin()); // A = { 3, 1 } et B = { 2, 4 } std::swap_ranges(A.begin(), A.end(), B.begin(), B.end()); // A = { 2, 4 } et B = { 3, 1 }

Transformer une (ou deux) squences en une autre


#include <algorithm> OutputIterator transform(InputIterator debut, InputIterator n, OutputIterator res, UnaryFunction op); OutputIterator transform(InputIterator1 debut1, InputIterator1 n1, InputIterator2 debut2, OutputIterator res, BinaryFunction op_binaire);

Par dnition, transform() effectue une opration sur des objets. La premire version utilise une squence, la deuxime en utilise deux.

Transformer une (ou deux) squences en une autre 277

Dans le premier cas, transform() applique le foncteur chaque objet et laffecte litrateur de sortie: *o=op(*i) puis incrmente celui-ci. Enn elle retourne litrateur correspondant la n de la squence de sortie. Dans le deuxime cas, transform() affecte litrateur de sortie le rsultat de op_binaire(*i1,*i2), en faisant progresser simultanment (paralllement) i1 eti2 sur chacune des deux squences fournies.
Attention On est vite tent dutiliser for_each la place de transform pour transformer une squence. Cela savre tout fait possible dans la pratique mais doit absolument tre vit, car cela romprait la smantique de for_each() qui est quelle ne modie jamais une squence. Dans ce cas, utiliser transform() en utilisant le mme itrateur pour debut et res. Il serait dailleurs agrable que soit ajoute la STL cette n une fonction transform() ne prenant pas ditrateur res. Sinon, il est facile de lajouter vousmme. Voici une possibilit: namespace std { template <class Conteneur1, class Conteneur2, class UnaryFunction> inline void transform_all(Conteneur1 c, Conteneur2 r, UnaryFunction op) { transform(c.begin(), c.end(), r.begin(), op); } }

278

CHAPITRE 12 Algorithmes standard

Lexemple suivant calcule la somme de deux vecteursV1 etV2 de mme taille (bien sr).
std::transform(V1.begin(), V1.end(), V2.begin(), V1plusV2.begin(), std::plus<int>());

Et celui-ci transforme un vecteur de rels en son oppos:


std::transform(V1.begin(), V1.end(),V1.begin(),
std::negate<double>());

Attention Litrateur de sortie res ne doit jamais faire partie de la squence dentre, lexception de debut lui-mme, sous peine dobtenir des rsultats imprvisibles.

Supprimer les doublons dune squence (dans ou lors dune copie)


#include <algorithm> ForwardIterator unique(ForwardIterator debut, ForwardIterator n); ForwardIterator unique(ForwardIterator debut, ForwardIterator n, BinaryPredicate predicat); OutputIterator unique_copy(InputIterator debut,
InputIterator n, OutputIterator res);

OutputIterator unique_copy(InputIterator debut,


InputIterator n, OutputIterator res, BinaryPredicate predicat);

Supprimer les doublons dune squence (dans ou lors dune copie) 279

Ds quun groupe dlments dupliqus apparat dans la squence [debut, n[, la fonction unique() les supprime tous sauf un. Ainsi, unique() renvoie litrateur nouvelle_n tel que la squence [debut, nouvelle_n[ ne contienne aucun lment dupliqu, autrement dit aucun doublon. Les itrateurs de la squence [nouvelle_n, n[ sont dj drfrencs, et les lments sur lesquels ils pointent sont indtermins. unique() est stable et prserve lordre relatif des lments conservs. Une squence [a, b[ est un groupe de doublons si pour tout itrateuri de cette squence i == a ou *i == *(i-1), pour la premire version de unique(). Pour la deuxime version ce sera si i == a ou predicat(*i,*(i-1)) est vrai. Les deux versions de unique_copy() agissent de mme mais ne modient pas la squence dentre et produisent une nouvelle squence ne contenant aucun doublon. Elles renvoient litrateur de n de cette nouvelle squence.
Attention Ces fonctions ne suppriment que les doublons conscutifs. Ainsi, si un vector<int> V contient les valeurs 1, 3, 3, 3, 2, 2,1, alors un appel unique() produira un vecteur contenant les valeurs 1, 3, 2,1.

Lexemple ci-aprs supprime tous les caractres identiques en ignorant la casse. Cela est fait en deux tapes: dabord en triant le contenu alphabtiquement, puis en supprimant les doublons conscutifs.

280

CHAPITRE 12 Algorithmes standard

inline bool eq_insensitif(char c1, char c2) { return std::tolower(c1) == std::tolower(c2); } inline bool inf_insensitif(char c1, char c2) { return std::tolower(c1) < std::tolower(c2); } void lettres_utilisees(const char *chaine) { std::cout << chaine << std::endl; std::vector<char> V(chaine, chaine + strlen(chaine)); std::sort(V.begin(), V.end(), inf_insensitif); std::copy(V.begin(), V.end(), std::ostream_iterator <char>(std::cout)); std::cout << std::endl; std::vector<char>::iterator n2 =
std::unique(V.begin(), V.end(), eq_insensitif);

std::copy(V.begin(), n2, std::ostream_iterator


<char>(std::cout));

std::cout << std::endl; }

Lappel de lettres_utilisees(La Bibliotheque Cpp Standard); produira la sortie ci-aprs.


La Bibliotheque Cpp Standard aaaBbCddeehiilLnoppqrSttu aBCdehilnopqrStu

Copier laide du constructeur par copie

281

Copier laide du constructeur par copie


#include <memory> ForwardIterator2 uninitialized_copy(ForwardIterator1 debut, ForwardIterator1 n, ForwardIterator2 res); ForwardIterator uninitialized_copy_n(InputIterator debut, Size N, ForwardIterator res);

En C++, loprateur new alloue de la mmoire pour un objet et appelle ensuite le constructeur sur ce nouvel emplacement mmoire. Occasionnellement, il est utile de pouvoir sparer ces deux oprations (ce principe est utilis dans limplmentation de certains conteneurs). Si chaque itrateur de la squence [res, res + (n debut)[ pointe vers une portion mmoire non initialise, alors uninitialized_ copy() cre une copie de [debut, n[ dans cette squence. Ainsi, pour chaque itrateuri de la squence dentre, cette fonction cre un copie de *i lemplacement mmoire indiqu par litrateur correspondant dans la squence de sortie en appelant construct(&*(res + i rst), *i). uninitialized_copy_n() procde de mme avec [debut, debut + N[ comme squence copier.
class Entier { public: Entier(int e) : m_entier(e) {} int get() const { return m_entier; } private: int m_entier; };

282

CHAPITRE 12 Algorithmes standard

void test() { int valeurs[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; const int N = sizeof(valeurs) / sizeof(int); Entier* entiers = (Entier*) malloc(N *
sizeof(Entier));

std::uninitialized_copy(valeurs, valeurs + N,
entiers);

Initialiser laide du constructeur par copie


#include <memory> void uninitialized_ll(ForwardIterator debut, ForwardIterator n, const T& x); void uninitialized_ll_n(InputIterator debut, Size N, const T& x);

En C++, loprateur new alloue de la mmoire pour un objet et appelle ensuite le constructeur sur ce nouvel emplacement mmoire. Occasionnellement, il est utile de pouvoir sparer ces deux oprations (ce principe est utilis dans limplmentation de certains conteneurs). Si chaque itrateur de la squence [debut, n[ pointe vers une portion mmoire non initialise, alors uninitialized_ll() cre autant de copies dex que ncessaire dans cette squence. Ainsi, pour chaque itrateuri de la squence dentre, cette fonction cre une copie dex lemplacement mmoire indiqu par cet itrateur en appelant construct(&*i, x).

Initialiser laide du constructeur par copie 283

uninitialized_ll_n() procde de mme avec [debut, debut + N[ comme squence copier. Lexemple suivant rutilise la classe Entier de lexemple des fonctions prcdentes:
Entier* test_initialisation(int val, int quantite) { Entier valeur(val); Entier* entiers = (Entier*) malloc(quantite * sizeof(Entier)); std::uninitialized_ll(entiers, entiers + quantite, valeur); return entiers; }

13
BOOST
BOOST sinscrit dans le prolongement de la bibliothque standard STL. BOOST est une sorte de super-bibliothque. Il sagit en effet dun ensemble de bibliothques runies en une seule. BOOST, conu pour tre utilis dans un vaste spectre dapplications, est largement rpandu. Sa licence (la BOOST licence) encourage aussi bien un usage commercial que non commercial. Dix bibliothques de BOOST sont dj incluses dans le TR1 (rapport technique du comit de standardisation du langage C++) et dans les dernires versions de la STL. Dautres bibliothques de BOOST ont t proposes pour le prochain TR2. Tout programmeur C++ donc rellement intrt connatre BOOST. BOOST fonctionne sur tout systme dexploitation moderne, quil sagisse des systmes Unix, GNU/Linux ou Windows. Les distributions GNU/Linux et Unix les plus populaires, comme Fedora, Debian ou NetBSD incluent des packages pr-compils de BOOST. Cerise sur le gteau, certaines bibliothques de BOOST nont mme pas besoin dtre compiles. En effet, BOOST repose massivement sur les templates Vous pouvez tlcharger BOOST sur http://www.boost .org.

286

CHAPITRE 13 BOOST

Mettre en forme des arguments selon une chane de formatage


boost::format(format-string) % arg1 % arg2 % % argN;

La bibliothque boost::format fournit une classe pour la mise en forme darguments selon une chane de formatage, comme le fait printf. Il existe cependant deux diffrences majeures: format envoie les arguments dans un ux interne et est donc compltement scurise et supporte les types utilisateurs de faon transparente; les points de suspension () ne peuvent pas tre utiliss dans un contexte fortement typ; ainsi lappel de la fonction avec un nombre arbitraire darguments est remplac par des appels successifs un fournisseur dargument: loprateur %. La chane de formatage contient les directives spciales qui seront remplaces par les chanes rsultant de la mise en forme des arguments donns. Notez quen tant que fonction scurise, boost::format contrle le nombre darguments en fonction de la chane de formatage. Ainsi, avec la chane de formatage %1% %10% %3%, il est impratif de fournir dix arguments. En mettre plus ou moins provoque la leve dune exception. Une spcication de format spec est de la forme: [N$] [attributs] [ largeur ] [ . prcision ] caractre-de-type N$ (optionnel). Indique que la spcication de format sapplique au N-ime argument. On ne peut pas mixer des spcications avec ou sans indicateur de position dargument dans une mme chane de formatage;

Mettre en forme des arguments selon une chane de formatage

287

Attributs (optionnel). Un formatage du texte peut tre indiqu grce une suite dattributs choisis parmi les possibilits dcrites dans le tableau suivant.
Attributs
Attribut Signication Effet sur le ux interne

- = _ + # 0

Aligner gauche Centrer Alignement interne Afcher le signe Afcher la base Remplir avec des 0 (aprs le signe ou la base)

N/A (appliqu plus tard sur la chane) N/A (appliqu plus tard sur la chane)1 Utiliser lalignement interne1 Utiliser showpos des nombres positifs Utiliser showbase et showpoint et la dcimale Si pas dalignement gauche, appelle setll(0) et utilise internal Des actions supplmentaires sont faites aprs la conversion de ux pour les sorties personnalises. N/A (appliqu plus tard sur la chane) Comportement diffrent de printf: il nest pas affect par lalignement interne

Si la chane ne commence pas par+ ou, insrer un espace avant la chane convertie

1. Nouvelle fonctionnalit par rapport printf.

Largeur (optionnel). Indique une largeur minimale pour la chane rsultant de la conversion. Si ncessaire, lachane sera complte avec le caractre choisi soit par le manipulateur de ux, soit par celui mentionn dans la chane de formatage (par exemple attributs 00, -, );

288

CHAPITRE 13 BOOST

Prcision (optionnel). Spcie la prcision du ux (elle est toujours prcde dun point). lorsque lon crit un nombre de type rel, il indique le nombre maximum de chiffres: aprs le point dcimal en mode xe ou scientique; au total en mode par dfaut (%g), lorsquon lutilise avec un type chane s ou S il sert limiter la chane convertie aux prcision premiers caractres (avec un ventuel remplissage jusqu obtenir largeur caractres aprs cette troncature), Caractre-de-type. Nimpose pas largument concern tre de ce type mais dnit les options associes au type mentionn (voir le tableau ci-aprs).
Caractres de type
Caractre p ou x o e f g X, E ou G Signication Sortie hexadcimale Sortie octale Format scientique Format virgule xe Format des rels gnral (par dfaut) Comme pour leurs quivalents en minuscules mais utilise des capitales (exposant, valeurs hexadcimales,) Sortie entire Effet sur le ux interne Utiliser hex Utiliser oct Mettre les bits des rels scientic Mettre les bits des rels xed Invalide tous les bits de champ pour les rels Comme x, e, ou g et utiliser uppercase en plus

d, l ou u

Mettre les bits de base dec

Convertir une donne en chane de caractres 289

Caractres de type (Suite)


Caractre s ou S Signication Sortie de chane Effet sur le ux interne La spcication de prcision est invalide, et la valeur est stocke pour une troncature ventuelle (voir lexplication sur prcision ci-dessus) Seul le premier caractre de la conversion en chane est utilis N/A

c ou C %

Sortie dun caractre Afcher le caractre %

Convertir une donne en chane de caractres


#include <boost/lexical_cast.hpp> class bad_lexical_cast; template<typename TDestination, typename TSource> TDestination lexical_cast(const TSource& arg);

lexical_cast convertit arg en chane de caractres de type std::string ou std::wstring. Si la conversion choue, une exception boost::lexical_cast est dclenche. Pour fonctionner, ce patron requiert des types source et destination les capacits suivantes: TSource est redirigeable sur un ux (OutputStreamable). Un operator<< prenant gauche un std::ostream ou std::wostream et droite un TSource doit tre dni; TDestination est redirigeable depuis un ux (InputStreamable). Un operator>> prenant gauche un std::istream ou std::wistream et droite un TDestination doit tre dni;

290

CHAPITRE 13 BOOST

TSource et TDestination doivent avoir un constructeur par copie; TDestination doit avoir un constructeur par dfaut. Le type caractre du ux sous-jacent est suppos tre de type char moins que TSource ou TDestination requiert un type caractre tendu. Les types TSource ncessitant un ux caractre tendu sont wchar_t, wchar_t*, et std:: wstring. Les types TDestination ncessitant un ux caractre tendu sont wchar_t et std::wstring.
Info Si vos conversions requirent un haut niveau de contrle sur la manire doprer la conversion, std::stringstream et std:: wstringstream offrent une solution plus approprie. Si vous ne disposez pas de conversion base sur les ux pour vos types, lexical_cast nest pas le bon outil. Dailleurs, il nest pas conu pour ce type de situation.

Lexemple suivant montre comment traiter la ligne dargument dun programme en tant que donnes numriques:
int main(int argc, char* argv[]) { std::vector<short> args; for (int i=1; i<argc && argv[i]!=0; ++i) { try { args.push_back( boost::lexical_cast<short> (argv[i]) ); } catch(boost::bad_lexical_cast&) { args.push_back(0); } // } }

Construire et utiliser une expression rgulire

291

Cet autre exemple illustre lutilisation dune donne num rique dans une opration sur des chanes:
void afcher(const std::string&); void afcher_erreur(int numero) { using namespace boost; afcher(Erreur n + lexical_cast<std:: string>(numero) + ); }

Voici un dernier exemple montrant la conversion dune chane de caractres vers divers types numriques:
using namespace boost; // std::string txt = 28.654; // oat a = lexical_cast<oat>(txt); double b = lexical_cast<double>(txt); unsigned int c = lexical_cast<unsigned int>(txt); sdt::string d = lexical_cast<std::string>(16978.3201); //

Construire et utiliser une expression rgulire


#include <boost/regex.hpp> boost::regex er(); bool boost::regex_match(BidirectionalIterator debut,
BidirectionalIterator n, [boost::match_results <>& m,] const boost::basic_regex<>& er, boost:: match_ag_type = match_default);

292

CHAPITRE 13 BOOST

bool boost::regex_match(const charT* str, [boost:: match_results<>& m,] const boost::basic_regex<> & er, boost::match_ag_type = match_default); bool boost::regex_match(const std::basic_string<>, [boost::match_results<>& m,] const boost::basic _regex<>& er, boost::match_ag_type = match_default); bool boost::regex_search(BidirectionalIterator
debut, BidirectionalIterator n, [boost::match _results<>& m,] const boost::basic_regex<>& er, boost::match_ag_type = match_default);

bool boost::regex_search(const charT* str,


[boost::match_results<>& m,] const boost:: basic_regex<>& er, boost::match_ag_type = match_default);

bool boost::regex_search(const std::basic_string<>,


[boost::match_results<>& m,] const boost:: basic_regex<>& er, boost::match_ag_type = match_default);

La classe boost::regex permet de construire une expression rgulire que lon utilise ensuite avec lune des deux autres fonctions fournies. Lalgorithme boost::regex_match() dtermine si une expression rgulire correspond lensemble des caractres dune squence fournie (par une paire ditrateurs bidirectionnels). Le plus souvent, cet algorithme est utilis pour valider des donnes utilisateurs. la place dune squence, vous pouvez aussi fournir une chane de caractres simple ou Unicode, styleC ou C++. Fournir une variable pour rcuprer linformation de correspondance est optionnel.

Construire et utiliser une expression rgulire 293

Lexemple suivant utilise les expressions rgulires pour dcoder le message de rponse dun serveur FTP:
boost::regex expr(([0-9]+)(\\-| |$)(.*)); int decoder_msg_FTP(const char* reponse, std::string* msg) { boost::cmatch correspondance; if (boost::regex_match(reponse, correspondance, expr)) { // correspondance[0] contient la chane complte // correspondance[1] contient le code de rponse // correspondance[2] contient le sparateur // correspondance[3] contient le message if (msg) msg->assign( correspondance[3].rst, correspondance[3].second ); return std::atoi( correspondance[1].rst ); } // pas de correspondance trouve if (msg) msg->erase(); return -1; }

Lalgorithme boost::regex_search() recherche une souschane correspondant lexpression rgulire donne. Cet algorithme utilise diverses heuristiques pour rduire le temps de recherche, notamment si la possibilit de trouver une correspondance la position courante est forte. Lexemple ci-aprs recherche toutes les chanes de caractresC dans un chier source pralablement charg comme chane de caractres:
boost::regex chaine_c((\)([^\]*)(\)); void chaines_c(const std::string& contenu_chier) { std::string::const_iterator debut = contenu_chier.begin(), n = contenu_chier.end(); boost::match_results<std::string::const_iterator> quoi;

294

CHAPITRE 13 BOOST

boost::match_ag_type ags = boost::match_default; while (boost::regex_search(debut,n,quoi,chaine_c,ags)) { std::string str( quoi[1].rst, quoi[1].second ); std::cout << Trouv : << str << std::endl; // mise jour de la position debut = quoi[0].second; // mise jour des options ags |= boost::match_prev_avail; ags |= boost::match_not_bob; } }

Caractres spciaux des expressions rgulires


Oprateur1 Description

. x* x+ x? x|y (x) [xy] [x-y] [^x] \x x{n} x{n,m}

Nimporte quel caractre 0, 1, 2, fois le caractre x 1, 2, 3, fois le caractre x 0 ou 1 fois le caractre x x ou y x x ou y (ensemble de caractres) x, y, ou z (intervalle de caractres) nimporte quel caractre diffrent de x x, mme si x est un caractre oprateur n fois x n, n+1, , m fois x

1. Sauf indication contraire, x, y ou z sont des expressions rgulires quelconques. Par exemple, lexpression t* dnote0 noccurrences du caractret et lexpression t|m)* dnote0 noccurrences des caractrest oum. Lexpression correspondant x est dune complexit quelconque.

viter les pertes mmoire grce aux pointeurs intelligents 295

Caractre spcial

Description

En dbut, signie dbut de chane. Par exemple, ^debut signie toute chane commenant par dbut. En n, signie n de chane. Par exemple, n$ signie toute chane nissant par n.

viter les pertes mmoire grce aux pointeurs intelligents


Les pointeurs intelligents sont des objets qui stockent un pointeur sur des objets allous dynamiquement. Ils fonctionnent de manire analogue aux pointeurs classiques, except quils suppriment automatiquement les objets allous au moment opportun. Plus exactement, lorsque plus aucun pointeur fort ne rfrence un objet donn, celui-ci est dtruit et tous les pointeurs faibles qui taient encore lis cet objet deviennent des pointeurs nuls.
Attention Mme avec les pointeurs forts, vous pouvez rencontrer des pertes mmoire. Considrez lexemple suivant: void f(boost::shared_ptr<int>, int); int g(); void ok() { boost::shared_ptr<int> p(new int(1)); f(p, g()); } void perte() { f(boost::shared_ptr<int>(new int(1)), g()); }

296

CHAPITRE 13 BOOST

Les fonctions ok() et perte() semblent identiques, et pourtant, comme leur nom lindique, lune est correcte et lautre peut provoquer une perte mmoire. En effet, la norme du C++ nimpose pas lordre dvaluation des arguments dune fonction. Ainsi, il est possible que linstruction new int(1) soit value en premier, puis que vienne en second g(), pour ne jamais arriver la construction du shared_ptr si la fonction g() lance une exception. Lentier ainsi allou ne sera jamais libr!

Utiliser les pointeurs forts


#include <boost/shared_ptr.hpp> boost::shared_ptr<Type> ptr(new Type); #include <boost/shared_array.hpp> boost::shared_array<Type> ptr(new Type[]);

Les pointeurs forts boost::shared_ptr<> offrent le mme niveau de scurit que les type pointeurs bas niveau vis-vis des accs concurrents: aucune (ou presque). Une instance de shared_ptr peut tre lue simultanment par plusieurs threads. Diffrentes instances de shared_ptr peuvent tre modies (par loprateur= ou la fonction membre reset()) simultanment par diffrents threads, et (do le presque) mme si ces diffrentes instances partagent le mme compteur de rfrence.
// dclaration commune tous les exemples boost::shared_ptr<int> p(new int(42)); // thread A shared_ptr<int> p2(p); // lit p // thread B shared_ptr<int> p3(p); // OK, lecture simultane correcte

viter les pertes mmoire grce aux pointeurs intelligents 297

// thread A p.reset(new int(1912)); // modie p // thread B p2.reset(); // OK, modie p2 // thread A p = p3; // lit p3, modie p // thread B p3.reset(); // crit p3; INDEFINI, lecture/modif. simultane // p3 // // thread A = p2; // lit p2, modie p3 thread B p2 devient hors datteinte: INDEFINI, le destructeur est considr comme une modication

// thread A p3.reset(new int(1)); // thread B p3.reset(new int(2)); // INDEFINI, modication simultane

Info La classe boost::shared_ptr<> ne peut tre utilise que sur des objets simples. Comment faire si vous avez allou un tableau dobjets? Dans ce cas, vous avez votre disposition la classe suivante: boost::shared_array<>, disponible dans <boost/shared_array.hpp>.

298

CHAPITRE 13 BOOST

Utiliser les pointeurs faibles


#include <boost/weak_ptr.hpp> boost::weak_ptr<Type> ptr(boost::shared_ptr<Type>);

Les pointeurs faibles (boost::weak_ptr<>) stockent une rfrence sur un pointeur fort. Mais du coup, leur utilisation nest pas sre, car lobjet sous-jacent peut tre libr tout moment, comme le montre lexemple suivant:
boost::shared_ptr<int> p(new int(10)); boost::weak_ptr<int> q(p); // if (!q.expired()) { // ici q est non nul, mais peut pointer sur nimporte quoi // si lobjet a t libr entre temps cause dun autre // thread excutant par exemple p.reset(). }

Pour cela, il suft de convertir momentanment le pointeur faible en fort:


boost::shared_ptr<int> p(new int(10)); boost::weak_ptr<int> q(p); // if (boost::shared_ptr<int> r = q.lock()) { // ici lutilisation r (par *r ou r->) est sre }

viter les pertes mmoire grce aux pointeurs intelligents 299

Utiliser les pointeurs locaux


#include <boost/scoped_ptr.hpp> boost::scoped_ptr<Type> ptr(new Type); #include <boost/scoped_array.hpp> boost::scoped_array<Type> ptr(new Type[]);

Les pointeurs locaux boost::scoped_ptr<> et boost:: scoped_array<> sont comme des pointeurs forts existence unique. Ils ne peuvent tre copis, et donc nont pas besoin de grer de compteur de rfrence. quoi sont-ils donc utiles? Non grer des singletons mais associer simplement un objet un autre, comme le montre lexemple ci-aprs. Ou bien encore, servir assurer la destruction dobjets allous localement. Nhsitez pas utiliser ce type de pointeur intelligent, car de par sa simplicit intrinsque, il ne conduit aucune perte de performance.
class MaClasse { boost::scoped_ptr<int> ptr; public: MaClasse() : ptr(new int) { *ptr=0; } // };

Mettre en uvre des pointeurs (forts) instrusifs


#include <boost/intrusive_ptr.hpp> boost::intrusive_ptr<Type> ptr(Type*); boost::intrusive_ptr<Type> ptr(Type*,false);

300

CHAPITRE 13 BOOST

Les boost::intrusive_ptr<> permettent de stocker des pointeurs vers des objets possdant dj un compteur de rfrence. Cette gestion se fait par lintermdiaire dappels aux fonctions intrusive_ptr_add_ref(Type*) et intrusive_ ptr_release(Type*), quil vous appartient de surcharger. Si vous ne savez pas quoi utiliser, entre pointeurs fort et intrusif, commencez par tester si les pointeurs forts conviennent votre besoin.

Crer des unions de types scurises


#include <boost/variant.hpp> boost::variant<Type1,Type2,> variable; U * boost::get(variant<T1, T2, , TN> * operand); const U* boost::get(const variant<T1, T2, , TN>* operand); U& boost::get(variant<T1, T2, , TN>& operand); const U& boost::get(const variant<T1, T2, , TN>& operand);

La classe boost::variant<> permet de runir plusieurs types de donnes en un seul, de manire sre, simple et efcace. Runir ne signie pas que lon stocke un objet de chacun des types, mais un seul objet de lun des types spcis. Cela permet de crer une sorte de type gnrique pouvant stocker un objet dun type ou dun autre. Lexemple suivant montre comment utiliser cette solution:
#include <boost/variant.hpp> #include <iostream> struct visiteur_entier : public boost::static_visitor<int>

Crer des unions de types scurises

301

{ // si le variant est un entier int operator()(int i) const { return i; // rien faire } // si cest une chane int operator()(const std::string& s) const { return s.length(); // le convertir en sa longueur } }; int main() { boost::variant< int, std::string > u(bonjour); std::cout << u; // u est une chane, afche : bonjour int r = boost::apply_visitor( visiteur_entier(), u ); std::cout << r; // afchera la longueur de la chane // contenu dans le variant, soit : 7 }

Un autre moyen, peut-tre plus simple mais moins puissant (car neffectuant aucune conversion) est dutiliser la fonction boost::get<>(). Lexemple suivant montre comment la mettre en uvre dans vos programmes:
void fois_trois( boost::variant< int, std::string >& v) { if (int* i_ptr = boost::get<int>(&v)) *i_ptr *= 3; else if (std::string* s_ptr = boost::get <std::string>(&v)) *s_ptr = (*s_ptr) + (*s_ptr) + (*s_ptr); }

302

CHAPITRE 13 BOOST

Mais l encore, si lon ajoute un type supplmentaire, il est facile doublier dajouter le code ncessaire. La robustesse ce niveau rside plutt dans la manire dcrire un visiteur: en utilisant les templates et en se basant sur un oprateur ou une fonction commune aux diffrents types prsent dans le variant. Lexemple suivant lillustre travers un visiteur utilisant loprateur+=:
struct fois_deux_generic :boost::static_visitor<> { template <typename T> void operator()(T& v) const { v += v; } }; // std::vector< boost::variant<int, std::string> > V; V.push_back( 21 ); V.push_back( bonjour ); fois_deux_generic visiteur; std::for_each(V.begin(), V.end(), boost::apply_visitor(visiteur)); // V == { 42, bonjour bonjour }

Info En programmation, il est bien souvent utile de manipuler des types distincts comme sil sagissait dun seul. En C++, on retrouve cet aspect avec les unions de types (union, voir la section Types labors du Chapitre1). Lexemple suivant en montre un bref rappel: union { int i; double d; } nombre; nombre.d = 3.141592654; nombre.i = 12; // crase nombre.d Malheureusement, cette technique devient inoprante ds que lon se replace dans un contexte orient objet. Les unions de type (union) arrivent en effet directement de lhritage du langageC.

Crer des unions de types scurises 303

Par voie de consquence, ils ne supportent que des types primaires ou primaires composs, et ne supportent donc pas la construction ou la destruction dobjet non triviaux. Lexemple suivant illustre clairement cette limitation des unions de type (union): union { int i; std::string s; // ne fonctionne pas, nest pas trivial } u; Une nouvelle approche est donc requise. Vous trouverez, si vous ne les avez pas dj rencontres, des solutions bases sur: l'allocation dynamique; un type de base commun et l'utilisation de fonctions virtuelles; des pointeurs non typs void* (le pire des cas). Les objets rels sont souvent, dans ces solutions, retrouvs laide de transtypage comme dynamic_cast, boost::any_cast, etc. Toutefois, ces solutions sont fortement sujettes erreurs, telles: les erreurs de downcast ne sont pas dtectes la compi lation. Une erreur de ce genre ne peut donc se dtecter que durant lexcution; l'ajout de nouveaux types concrets est ignor. Si un nouveau type concret est ajout la hirarchie, les downcasts existants continueront de fonctionner tel quels, tout en ignorant royalement lexistence de ce nouveau type. Le programmeur doit donc trouver de lui-mme tous les emplacements de codes modier. Beaucoup de temps passer et beaucoup derreurs en perspective De plus, mme bien implmentes, ces solutions resteront pnalises par le cot de labstraction laquelle elles font appel. Ce cot se retrouve dans lappel des fonctions virtuelles et des transtypages dynamiques.

304

CHAPITRE 13 BOOST

Parcourir un conteneur avec BOOST_FOREACH


BOOST_FOREACH(type element, conteneur) { // }

En C++, crire une boucle peut parfois savrer fastidieux. Utiliser les itrateurs, avec leurs dclarations templatises rallonge, peut facilement alourdir lcriture et donc la lisibilit. Lalgorithme std::foreach() quant lui oblige dplacer tout le bloc de code dans un prdicat. Si cela peut tre trs pratique pour une utilisation rcurrente dun mme prdicat, cela lest beaucoup moins pour du ponctuel ou du spcique. BOOST_FOREACH est conu pour la facilit dutilisation et lefcacit: pas dallocation dynamique, pas dappel de fonction virtuelle ou de pointeur de fonction, ni dappel qui ne puisse tre optimis par le compilateur. Le code produit est proche de loptimal que lon aurait obtenu en codant la boucle soi-mme. Bien qutant une macro, celle-ci se comporte sans surprise: ses arguments ne sont valus quune seule fois.
#include <string> #include <iostream> #include <boost/foreach.hpp> // std::string chaine( Bonjour ); BOOST_FOREACH( char ch, chaine ) { std::cout << ch; }

Parcourir un conteneur avec BOOST_FOREACH 305

BOOST_FOREACH parcourt les squences, en se basant sur boost::range, comme par exemple: les conteneurs STL; les tableaux de type C; les chanes zro terminal (char et wchar_t); une std::pair ditrateurs. Vous trouvez qucrire BOOST_FOREACH est trop long? Que lcriture en capitale dimprimerie nest pas votre got? Possible, mais cela nen reste pas moins le standard adopt travers les conventions de nommage de la bibliothque BOOST. Il nappartient qu vous de le renommer ainsi:
#dene foreach BOOST_FOREACH

Attention toutefois de ne pas causer un conit de nom dans votre code.


Attention Nutilisez pas #dene foreach(x,y) BOOST_FOREACH(x,y): cela posera problme lors dune utilisation avec des macros comme paramtres, provoquant une cration de code supplmentaire lors de linterprtation. Utilisez plutt la technique prcite.

306

CHAPITRE 13 BOOST

Gnrer des messages derreur pendant le processus de compilation


#include <boost/static_assert.hpp> BOOST_STATIC_ASSERT( condition )

La macro BOOST_STATIC_ASSERT(x) permet de gnrer des messages derreur pendant le processus de compilation si lexpression constantex est fausse. Elle est lquivalent des macros dassertion classiques, mais contrairement ces dernires qui se dclenchent pendant lexcution du programme, celles-ci se dclenchent pendant la compilation. Les concepteurs de cette partie de BOOST ont choisi de nommer ce type dassertion des assertions statiques ( loppos de dynamique). Notez que si la condition est true, alors aucun code nest gnr par cette macro. Si elle est utilise au sein dun template, elle sera value lors de linstanciation de ce dernier. Ceci est particulirement utile pour vrier certaines conditions sur les paramtres templates. Lun des atouts principaux de BOOST_STATIC_ASSERT() est de gnrer des messages derreur humainement lisibles. Ils indiquent ainsi immdiatement et clairement si le code dune bibliothque (ou votre code) est mal utilis. Lafchage peut varier dun compilateur un autre, mais il devrait ressembler ceci:
Illegal use of STATIC_ASSERTION_FAILURE<false>

Vous pouvez utiliser BOOST_STATIC_ASSERT() au mme endroit que nimporte quelle dclaration: dans une classe, une fonction ou un espace de noms.

Gnrer des messages derreur pendant le processus de compilation

307

Lexemple ci-aprs permet de contrler que le type int est cod sur au moins 32bits, et que le type wchar_t est bien non sign:
#include #include #include #include <climits> <cwchar> <limits> <boost/static_assert.hpp>

namespace mes_conditions { BOOST_STATIC_ASSERT(std::numeric_limits<int>:: digits >= 32); BOOST_STATIC_ASSERT(WCHAR_MIN >= 0); }

Lors de lcriture de template, il est intressant de pouvoir tester si les paramtres fournis vrient bien certaines conditions. Cela permet, en partie, de garantir le bon fonctionnement de la fonction gnrique criture. Sinon, cela apporte au moins lavantage dobtenir des message derreur plus explicites quun traditionnel message relatif un problme de syntaxe Lexemple ci-aprs montre comment utiliser BOOST_STATIC_ASSERT() pour tre certain quun message derreur explicite apparaisse lorsquon utilise un mauvais type ditrateur avec un algorithme donn.
#include <iterator> #include <boost/static_assert.hpp> #include <boost/type_traits.hpp> template <class RandomAccessIterator > RandomAccessIterator mon_algorithme( RandomAccessIterator from, RandomAccessIterator to) { // cet algorithme ne doit tre utilis quavec des

308

CHAPITRE 13 BOOST

// itrateurs accs direct (ou random access iterator) typedef typename std::iterator_traits< RandomAccessIterator >::iterator_category cat; BOOST_STATIC_ASSERT((boost::is_convertible<cat, const std::random_access_iterator_tag&>::value)); // // implmentation // return from; }

Astuce Les doubles parenthses de lexemple prcdent ont pour but dtre certain que la virgule ne soit pas prise comme sparateur dargument de macro.

Lexemple ci-aprs montre lutilisation de BOOST_STATIC_ ASSERT() avec les classes templates. Il expose un moyen de vrier que le paramtre soit au moins un type entier, sign, et sur au moins 16bits.
#include <climits> #include <boost/static_assert.hpp> template <class UnsignedInt> class MaClasse { private: BOOST_STATIC_ASSERT( (std::numeric_limits<UnsignedInt>::digits >= 16) && std::numeric_limits<UnsignedInt>::is_specialized && std::numeric_limits<UnsignedInt>::is_integer && !std::numeric_limits<UnsignedInt>::is_signed); public: // };

Gnrer des messages derreur pendant le processus de compilation 309

Attention Certains compilateurs ont parfois tendance en faire trop. Thoriquement, les assertions statiques prsentes dans une classe ou une fonction template ne sont pas instancies tant que le template dans lequel elles apparaissent nest pas instanci. Pourtant, il existe un cas un peu part: lorsque lassertion statique ne dpend pas dau moins un paramtre template, comme ci-aprs: template <class T> struct ne_peut_pas_etre_instancie { BOOST_STATIC_ASSERTION(false); // ne dpend daucun paramtre template }; Dans ce cas, lassertion statique peut tre value mme si la classe nest jamais instancie. Cest le cas avec, au moins, les compilateurs Intel 8.1 et gcc 3.4. Un moyen de palier cet inconvnient est de rendre lassertion dpendante dun paramtre template. Lexemple suivant montre comme le faire, laide de linstruction sizeof(): template <class T> struct ne_peut_pas_etre_instancie { BOOST_STATIC_ASSERTION(sizeof(T)==0); // dpendant dun paramtre template };

14
Programmation multithread avec QT
Les threads sont maintenant trs rpandus. Il existe diverses bibliothques permettant de les apprhender facilement. BOOST fournit une bibliothque haut niveau pour les crer et les utiliser, wxWidgets une autre. Dans cet ouvrage, jai choisi dutiliser ceux de QT.

Crer un thread
#include <QThread> class MonThread : public Qthread { Q_OBJECT public: void run() { } };

Pour crer un thread avec QT, il suft de driver la classe QThread et de rimplmenter la fonction membre run() de cette dernire. Ensuite, il suft de crer une instance de votre nouvelle classe et dappeler la fonction membre start() pour excuter le contenu de la fonction run() dans un nouveau thread.

312

CHAPITRE 14 Programmation multithread avec QT

Il existe une contrainte avec les threads QT: il est impratif de crer un objet QApplication ou QCoreApplication avant de crer un QThread.

Partager des ressources


#include <QSharedMemory> QSharedMemory sm(const QString& clef, QObject*=0); QSharedMemory sm(QObject*=0); bool QSharedMemory::create(int taille, AccessMode mode = ReadWrite); int QSharedMemory::size() const; bool QSharedMemory::attach(AccessMode mode = ReadWrite ); bool QsharedMemory::detach(); bool QSharedMemory::isAttached() const; const void* QSharedMemory::constData() const; void* QSharedMemory::data(); bool QSharedMemory::lock(); void* QSharedMemory::unlock();

QSharedMemory fournit un accs un segment de mmoire partag par plusieurs threads ou processus. Cette classe procure aussi un moyen simple de bloquer cette mmoire en accs exclusif. Lorsque vous utilisez cette classe, il faut tre conscient quil existe des diffrences dimplmentation en fonction de la plate-forme sur laquelle vous utilisez la bibliothque QT. Sous Windows, la classe ne possde pas le segment de mmoire. Quand tous les threads et processus ayant un lien avec ce segment de mmoire rserv ont dtruit leur instance de QSharedMemory ou se sont termins, alors le noyau de Windows libre le segment de mmoire automatiquement.

Se protger contre laccs simultan une ressource avec les mutex

313

Sous Unix, QSharedMemory possde le segment de mmoire. Le comportement est le mme que sous Windows en ce sens que cest toujours le noyau du systme qui libre le segment. Mais, si lun des threads ou des processus concerns plante, il se peut que le segment de mmoire ne soit jamais libr. Sous HP-UX, un processus ne peut tre li quune fois sur un segment mmoire. Du coup, vous devrez vous-mme grer les accs concurrentiels ce segment entre les diffrents threads dun mme processus. Dans ce contexte, QSharedMemory ne pourra pas tre utilis. Utilisez lock() et unlock() lorsque vous lisez ou crivez des donnes du segment de mmoire partage.

Se protger contre laccs simultan une ressource avec les mutex


#include <QMutex> QMutex m(mode); // == NonRecursive par dfaut m.lock(); bool r = m.tryLock(); bool r = m.tryLock(timeout); m.unLock();

Les mutex permettent de se prmunir contre laccs et/ou la modication dune ressource (mmoire, objet, driver, ) par plusieurs threads en mme temps. Cela est souvent essentiel au bon droulement dun programme. Pour comprendre lintrt dun tel mcanisme, imaginons que deux fonctions func1() et func2() soient excutes dans des threads diffrents, en mme temps, et effectuent chacune des calculs sur la mme variable. Le rsultat serait imprvisible.

314

CHAPITRE 14 Programmation multithread avec QT

Lexemple ci-aprs montre comment protger laccs cette variable pour viter quelle soit modie et utilise par plusieurs threads en mme temps.
QMutex m; double variable = 20.0; void func1() { m.lock(); variable *= 10.0; variable += 3.0; m.unLock(); } void func2() { m.lock(); variable -= 100.0; variable /= 34.0; m.unLock(); }

Nous lavons dit, un mutex sert protger un objet (ici une variable) contre des accs concurrentiels. Pour quoi cela? Simplement pour viter des comportements imprvisibles, dans le meilleur des cas, ou des plantages alatoires et trs difciles cerner dans le pire des cas. Pour comprendre ce risque, imaginez que lexemple prcdent ne contienne aucune protection par mutex. Dans ce cas, lexcution du code pourrait se passer comme suit:
// thread 1 variable *= // thread 2 variable -= variable /= // thread 1 variable += excutant func1 10.0; // 200 excutant func2 100.0; // 100 34.0; // 100/34 = 2 + 16/17 excutant func1 3.0; // 5 + 16/17

Se protger contre laccs simultan une ressource avec les mutex

315

Lutilisation du mutex empchera func2() de commencer ses calculs avant que func1() nait ni les siens.
// thread 1 variable *= variable += // thread 2 variable -= variable /= excutant func1 10.0; // 200 3.0; // 203 excutant func2 100.0; // 103 34.0; // 103/34 = 3 + 1/34

Cet exemple est trivial mais permet de bien comprendre le mcanisme mis en jeu.
Astuce Souvent, les fonctions prsentent un code bien plus complexe, avec plusieurs instructions return dissmines a et l, sans oublier les lancements dexceptions ou les appels dautres fonctions pouvant elles aussi dclencher dautres exceptions. Du coup, il peut devenir dlicat de noublier aucune libration dun mutex. Pour cela, nous pourrions crire nous-mme une classe lie au mutex qui nous intresse et laisser le soin son destructeur de librer ledit mutex. Or la bibliothque de QT fournit dj cette petite merveille: QMutexLocker. Lexemple ci-aprs vous montre comment lutiliser avec la premire fonction. Mme sil reste trivial, il expose lessentiel de ce quil faut savoir: #include <QMutexLocker> void func1() { QMutexLocker(&m); variable *= 10.0; variable += 3.0; // ici le destructeur de QMutextLocker appelle m.unLock() } Ainsi, adieu les risques dsastreux doubli de libration de mutex!

316

CHAPITRE 14 Programmation multithread avec QT

Contrler le nombre daccs simultans une ressource avec les smaphores


#include <QSemaphore> QSemaphore s(n); // n = 0 si omis void QSemaphore::acquire(n); // n = 1 si omis int QSemaphore::available() const; void QSemaphore::release(n) // n = 1 si omis bool QSemaphore::tryAcquire(n); // n = 1 si omis bool QSemaphore::tryAcquire(n, tempsLimite);

Les smaphores sont un peu comme des mutex mais peuvent permettre plusieurs threads dutiliser en mme temps les ressources quelles protgent. On pourrait voir une smaphore comme un agent de location qui dispose den (valeur donne au constructeur de la classe) ressources, comme des vlos par exemple. Un client peut louer un ou plusieurs vlos dun coup (par lappel acquire(v)) et les rendre dun coup ou par lot (par un ou plusieurs appels release(w)). Un appel acquire() oblige se placer dans une le dattente jusqu ce que le nombre de ressources demandes soient disponibles. Si le smaphore nen dispose pas dautant, je vous laisse deviner le problme il risque fort de bloquer tout le monde. Du coup et pour viter ce problme, vous disposez galement de deux autres mthodes: tryAcquire(n) pour tenter dacqurir nressources ou partir sil ny en a pas. Le client devra patienter de luimme en dehors de la le dattente de la boutique et retenter sa chance plus tard;

Contrler le nombre daccs simultans une ressource avec les smaphores

317

tryAcquire(n,tempsLimite) pour demander nressources et se placer dans la le dattente. Mais si le client nest pas servi avant le tempsLimite (en millisecondes) donn, il dcide de sortir de la le dattente (et de perdre sa place) pour poursuivre son chemin.
Info Avec les smaphores, vous pouvez commencer avec un nombre de ressources nul (gal zro). Librer une ou plusieurs ressources revient alors lui en allouer autant. Ce comportement est valable tout moment. Cette implmentation ne gre donc pas de test sur un maximum de ressource que lon dnirait la cration dun smaphore. Le code suivant illustre ce fait: QSemaphore s(3); s.acquire(1); s.acquire(2); s.release(4); s.release(5); // s.available() // // // // s.available() s.available() s.available() s.available() == == == == 3 2 0 4, on a dpass 3

== 9, on en alloue donc bien en plus

Attention Si vous oubliez de librer des ressources ou que vous en demandez trop par rapport leur disponibilit, vous provoquerez un deadlock. Prenez-y garde ds la conception de vos algorithmes. Nattendez pas de les voir apparatre dans vos tests. Le deadlock peut aussi parfois survenir lorsque lon oublie de protger ses ressources avec un mutex ou un smaphore. Du coup, des variables peuvent avoir t modies sans quon le veuille et notre programme se mettre en attente ou en boucle innie

318

CHAPITRE 14 Programmation multithread avec QT

Prvenir le compilateur de lutilisation dune variable dans plusieurs threads


volatile Type variable; // global, local ou membre
de classe.

Le mot-cl volatile permet de prvenir le compilateur que cette variable va tre utilise dans plusieurs threads diffrents. Cela lui permet de la traiter de manire spciale, notamment lorsque lon utilise les options de compilation relatives loptimisation. Ce qualicateur indique que la variable ou lobjet peut tre modi par une interruption matrielle, par un autre programme, un autre processus ou un autre thread. Cela implique de devoir recharger systmatiquement la variable chaque fois quelle est utilise, mme si elle se trouve dj dans un des registres du processeur, ce qui explique le traitement spcial de ce type de variables vis--vis des optimisations de programmes ralises par le compilateur.

15
Base de donnes
Les bases de donnes sont couramment utilises en programmation. Ce chapitre vous prsente quelques outils de base selon une approche multisystme. Nous avons mis laccent sur SQLite en raison de sa simplicit demploi et de sa popularit grandissante. Vous pouvez en tlcharger les sources sur http://www.sqlite.org. La premire section vous aidera dterminer si SQLite rpond vos besoins, les suivantes le mettre en uvre. Les dernires sections vous permettront dutiliser les bases de donnes avec un pilote ODBC et la bibliothque graphique wxWidgets.
Astuce An de rester le plus multiplate-forme possible, prfrez un systme grant directement les requtes SQL.

Voici quelques sites que je vous invite prendre en considration: http://sql.1keydata.com/fr. Pour retrouver la syntaxe dune requte SQL. http://dev.mysql.com/doc/refman/5.1/en/tutorial .html. Pour apprendre mettre en uvre MySQL.

320

CHAPITRE 15 Base de donnes

http://www.sqlapi.com. SQLAPI++ est une API C++ permettant daccder diverses bases de donnes SQL (Oracle, SQL Serveur, DB2, Sybase, Informix, Interbase, SQLBase, MySQL, PostgreSQL, ODBC et SQLite). SQLAPI++ utilise les API natives des moteurs de bases de donnes concernes et sexcute donc rapidement et efcacement. Cette bibliothque fournit galement une interface bas niveau permettant daccder des caractristiques particulires des bases de donnes. En encapsulant les API des divers moteurs, SQLAPI++ agit comme un intergiciel (middleware) et favorise la portabilit. Elle est disponible pour les systmes Win32, Linux et Solaris, et au moins les compilateurs Microsoft Visual C++, Borland C++ Builder, GNU g++. Cette bibliothque est un shareware. http://wxcode.sourceforge.net/components/ wxsqlite3. Pour utiliser SQLite avec wxWidgets. http://sqlitepp.berlios.de. Pour les puristes du C++, voici une bibliothque C++ encapsulant celle de SQLite. http://debea.net. Debea (DataBasE Access library) est une collection dinterfaces permettant de connecter des objets C++ diverses bases de donnes. Cette bibliothque ne cherche pas supprimer les requtes SQL, mais gnre automatiquement certaines dentre elles, acclrant dautant votre vitesse de dveloppement. Disponible pour Linux (g++ 3.6 ou plus), Windows 98/2000/XP (VC++ 2003 et 2005). Cette bibliothque possde galement une interface pour wxWidgets: wxDba. http://www.oracle.com/technology/software/ products/database/index.html. Oracle est prsent gratuit pour coder des prototypes dapplication (non commercialiss, non utiliss intensivement en tant

Savoir quand utiliser SQLite

321

quoutil interne). Disponible pour Windows (32 et 64bits), Linux (32 et 64bits), Solaris (SPARC) 64bits, AIX (PPC64), HP-UX (Itanium et PA-RISC).

Savoir quand utiliser SQLite


SQLite est diffrent de la plupart des autres systmes de bases de donnes SQL. Il a dabord t conu pour tre simple: administrer; utiliser; embarquer dans un programme; maintenir et personnaliser. SQLite est apprci parce quil est petit, rapide et stable. Sa stabilit dcoule de sa simplicit (moins de fonctionnalits, moins de risques derreur). En revanche, pour atteindre ce niveau de simplicit, il a fallu sacrier des fonctionnalits qui, dans certains contextes, sont apprciables, comme la possibilit de faire face une forte demande daccs simultans (high concurrency), une gestion ne des contrles daccs, la mise disposition dun large ensemble de fonctions intgres, les procdures stockes, lvolution vers le trapta-octet, etc. Si vous avez besoin de telles fonctionnalits et que la complexit quelles induisent ne vous fait pas peur, alors SQLite nest probablement pas pour vous. SQLite ne prtend pas concurrencer Oracle ou PostgreSQL. Il na pas t conu (a priori) pour tre le moteur de base de donnes de toute une entreprise. La rgle de base pour dterminer sil convient vos besoins est est la suivante: prfrez SQLite lorsque la simplicit dadministration, de mise en uvre et de maintenance est plus importante que les innombrables (et complexes)

322

CHAPITRE 15 Base de donnes

fonctionnalits que les systmes de bases de donnes dentre prise fournissent. Il se trouve que les situations o la simplicit est primordiale sont bien plus courantes que ce que lon peut croire.
Info SQLite savre aussi tre un moyen de grer des chiers, en remplacement de la fonctionC fopen().

Cas demploi de SQLite


Format de chier dune application

SQLite est utilis avec beaucoup de succs comme un format de chier disque pour des applications bureautiques telles que outils danalyse nancire, progiciels CAD, progiciels de suivi de dossiers, etc. Les oprations traditionnelles douverture de chiers effectuent en ralit un sqlite3_open() suivi de lexcution dun BEGIN TRANSACTION pour verrouiller laccs au contenu. La sauvegarde fait un COMMIT suivi dun autre BEGIN TRANSACTION. Lusage des transactions garantit que les mises jour des chiers de lapplication sont atomiques, durables, isoles et cohrentes. Des dclencheurs (triggers) temporaires peuvent tre ajouts la base de donnes pour enregistrer toutes les modications dans une table temporaire de annuler/refaire. Ces changements peuvent ensuite tre lus lorsque lutilisateur appuie sur les boutons Annuler et Refaire. Grce cette technique, et sans limitation de la profondeur de lhistorique des annuler/refaire, la mise en uvre de cette fonctionnalit peut tre faite laide de trs peu de code.

Cas demploi de SQLite

323

Dispositifs et applications embarques

Une base SQLite requrant peu ou pas dadministration, SQLite est un bon choix pour les dispositifs ou services devant fonctionner sans surveillance et sans intervention humaine. SQLite est adapt pour une utilisation dans les tlphones cellulaires, les assistants numriques (PDA), les botes noires et autres appareils. Il fonctionne aussi en tant que base de donnes embarque dans des applications tlchargeables.
Sites web

SQLite fonctionne habituellement bien comme moteur de base de donnes de sites web faible ou moyen trac (cest--dire plus de 99% des sites). La quantit de trac que SQLite peut traiter dpend bien sr de la faon dont le site fait usage de ses bases de donnes. De manire gnrale, tout site recevant moins de 100000 visites par jour devrait fonctionner correctement avec SQLite. Cette limite est une estimation prudente, et non une limite suprieure infranchissable. Certains sites atteignant un nombre de visites dix fois suprieur fonctionnent parfaitement avec SQLite.
Remplacement de chiers propritaires

Beaucoup de programmes utilisent fopen(), fread(), et fwrite() pour crer et manipuler des chiers de donnes dans des formats propritaires. SQLite fonctionne particulirement bien pour remplacer ces chiers propritaires.
Bases de donnes temporaires

Pour les programmes qui doivent ltrer ou trier un grand nombre de donnes, il est souvent plus facile et plus rapide de charger un modle mmoire de base de

324

CHAPITRE 15 Base de donnes

donnes SQLite et dutiliser des requtes avec jointures et des clauses ORDER BY pour extraire des donnes dans la forme et lordre dsir plutt que de devoir coder soi-mme ces fonctionnalits la main. Utiliser une base de donnes SQL de cette manire permet galement une plus grande souplesse, car de nouvelles colonnes et index peuvent tre ajouts sans avoir recoder chaque requte.
Outils danalyse de donnes en ligne de commande

Ceux qui sont expriments en SQL peuvent utiliser les programmes en ligne de commande fournis par SQLite pour analyser divers jeux de donnes. Les donnes brutes peuvent tre importes de chiers CSV, puis dcoupes et groupes pour gnrer une multitude de rapports. Ce type dutilisation inclut lanalyse des chiers logs des sites web, lanalyse de statistiques sportives, la compilation de mtrique de code source ou lanalyse de rsultats exprimentaux. Vous pouvez bien entendu faire de mme avec des bases de donnes client/serveur professionnelles. SQLite restera toutefois dans ce cas bien plus facile mettre en place dans le cadre de chiers simples, surtout si vous souhaitez les partager avec dautres personnes (via une cl USB, une pice jointe un courrier lectronique, etc.).
Base locale pour tests ou dmos

Si vous crivez une application cliente pour un moteur de base de donnes dentreprise, il est logique dutiliser une interface dorsale (backend) pour vous permettre de vous connecter nimporte quel type de bases de donnes SQL. Dans ce cadre, il est encore plus logique et bnque dinclure (en dition de lien statique) le support de SQLite dans la partie cliente.

Cas demploi de SQLite

325

De cette faon, le programme client pourra tre utilis comme un programme autonome avec des chiers de donnes SQLite pour des phases de test ou mme comme programme de dmonstration.
Enseignement et/ou apprentissage

De par sa simplicit dutilisation, SQLite est un trs bon moteur de bases de donnes pour apprendre ou enseigner le langage SQL. En effet, SQLite est trivial installer: il suft de copier lexcutable sqlite ou sqlite.exe sur la machine souhaite et de lutiliser. Les tudiants (ou vous-mme) peuvent facilement crer autant de bases de donnes quils le souhaitent et peuvent envoyer leurs bases de donnes leur professeur (ou un collgue) par e-mail. Il est ainsi facile de les commenter ou de les archiver. Pour ceux qui sont curieux de comprendre la faon dont un SGBDR est mis en uvre, la modularit, les nombreux commentaires et la documentation du code de SQLite sont apprciables. Cela ne signie pas que SQLite soit un modle de la faon dont les autres moteurs de base de donnes sont mis en uvre. Comprendre comment fonctionne SQLite permet simplement de comprendre plus rapidement les principes de fonctionnement des autres systmes.
Exprimentations et prototypes

La simplicit et la modularit de conception de SQLite en font une plate-forme de choix pour prototyper et exprimenter de nouvelles fonctionnalits ou ides (dutilisation ou dextension du langage SQL lui-mme).

326

CHAPITRE 15 Base de donnes

Cas o un autre moteur est plus appropri


Applications client/serveur

Si vous prvoyez que plusieurs applications clientes accderont travers le rseau (local ou non) une mme base de donnes, alors orientez-vous plutt vers un moteur de base de donnes client/serveur plutt que vers SQLite. Ce dernier fonctionne sur un systme de chiers rseau, mais en raison de la latence associe la plupart de ces systmes, la performance ne sera pas au rendez-vous. De plus, la logique du mcanisme de verrouillage de chier (le locking) de nombreux systmes de chiers contient des bugs (que ce soit sous Unix ou Windows). Si le verrouillage de chiers ne fonctionne pas comme prvu, deux ou plusieurs clients pourraient alors modier la mme partie de la mme base de donnes en mme temps, entranant une corruption de la base. Comme ce problme a pour origine la prsence de bugs dans le systme de chiers sous-jacent, SQLite ne peut rien faire pour lempcher. Une bonne rgle gnrale est dviter dutiliser SQLite dans des situations o la mme base de donnes sera accessible simultanment partir de plusieurs ordinateurs sur un rseau de chiers.
Trs gros sites web

SQLite fonctionne bien comme systme de base de donnes pour site web. Mais si votre site devenait lent et que vous projetiez de dporter la partie base de donnes sur une deuxime machine, alors vous devriez srieusement penser utiliser un moteur client/serveur de type entreprise.

Cas o un autre moteur est plus appropri

327

Trs grosse base de donnes

Lorsque vous commencez une transaction dans SQLite, ce qui arrive automatiquement avant chaque opration qui nest pas explicitement dans un BEGIN COMMIT, le moteur doit crer une image de certains morceaux de la base en cours de modication an de grer son systme dannulation. SQLite a besoin de 256octets de RAM pour chaque mgaoctet de base. Pour de petites bases, ce cot mmoire nest pas un problme; mais lorsque la base de donnes atteint le gigaoctet, la taille de cette image devient importante. Si vous avez besoin de stocker et modier plus de quelques dizaines de mgaoctets, vous devriez envisager dutiliser un autre moteur de base de donnes.
Forte demande daccs simultans

SQLite utilise des verrous de lecture/criture sur lensemble de la base. Cela signie que ds quun processus lit une partie de la base, tous les autres sont empchs dcrire dans nimporte quelle autre partie de celle-ci (et inversement). Dans bien des cas, ce nest pas forcment un problme (une opration dure rarement plus de quelques dizaines de millisecondes). Mais pour les applications devant grer beaucoup daccs simultans, il faudra se tourner vers une autre solution.

328

CHAPITRE 15 Base de donnes

Crer et utiliser une base avec linterprteur SQLite


sqlite3 nom_de_chier // Unix/Linux/Mac OS X sqlite3.exe nom_de_chier // Windows

La bibliothque SQLite inclut un utilitaire en ligne de commande appel sqlite3 (ou sqlite3.exe sous Windows) pour saisir et excuter des requtes SQL sur une base de donne SQLite. Pour lancer ce programme, il suft de taper sqlite3 (ou sqlite3.exe) suivi du nom de chier contenant la base de donnes. Si le chier nexiste pas, ce programme le crera automatiquement. Une invite de commande apparat ensuite dans laquelle vous pourrez saisir et excuter des requtes SQL. Lexemple ci-aprs vous montre comment crer une base nomme exemple1.db avec une seule table table1 contenant quelques champs.
C:\Projet1\> sqlite3.exe exmple1.db SQLite version 3.6.6 Enter .help for instructions sqlite> create table table1(un varchar(10), deux smallint); sqlite> insert into table1 values(bonjour!,10); sqlite> insert into table1 values(au revoir,20); sqlite> select * from table1; bonjour!|10 au revoir|20 sqlite>

Pour quitter linterprteur sqlite3, appuyez sur les touches Ctrl+D ou Ctrl+C. Vous pouvez aussi utiliser la commande spciale .exit (attention au point au dbut de celle-ci).

Crer et utiliser une base avec linterprteur SQLite 329

Attention Prenez garde bien terminer chacune de vos requtes par un point-virgule (;). Cest sa prsence qui permet linterprteur sqlite3 de dterminer la n dune requte. Si vous lomettez, sqlite3 afchera une invite dite de continuation et attendra le reste de la requte. Lexemple suivant montre comment se passe une requte multiligne: sqlite> create table table2 ( > f1 varchar(30) primary key, > f2 text, > f3 real > ); sqlite>

Linterprteur sqlite3 possde galement quelques commandes internes, qui commencent toutes par le signe point (.) (les dot commands). Elles servent principalement modier le format dafchage du rsultat des requtes ou excuter certaines instructions de requtes prconditionnes. Le tableau ci-dessous fournit un rcapitulatif de ces commandes.
Commandes internes (interprteur sqlite3)
Commande .help .bail ON|OFF .databases .dump ?TABLE? ... Description Afche la liste des commandes spciales Sarrter la premire erreur rencontre. OFF par dfaut. Lister les noms et chiers bases lies Faire une image (dump) de la base sous forme de requtes SQL dans un chier texte

330

CHAPITRE 15 Base de donnes

Commandes internes (interprteur sqlite3) (Suite)


Commande .echo ON|OFF .exit .explain ON|OFF .header(s) ON|OFF .help Description Activer ou dsactiver lafchage Quitter linterprteur Activer ou dsactiver le mode de sortie applicable EXPLAIN Activer ou dsactiver lafchage des en-ttes Afche la liste et la description des commandes spciales Importer les donnes de FILE dans la TABLE Montrer le nom de tous les index de TABLE Charger une bibliothque dextensions Choisir le mode de sortie, o MODE est parmi la liste suivante: csv: donnes spares par des points-virgules column: colonnes alignes gauche (voir aussi .width) html: sous forme de <table> HTML insert: sous forme dinstruction dinsertion SQL pour TABLE line: une valeur par ligne list: valeurs spares par la chane de sparation .string tabs: valeurs spares par des tabulations tcl: sous forme dlments de liste TCL Afche STRING la place des valeurs nulles (vides) NULL Envoyer la sortie vers le chier FILENAME Envoyer la sortie vers lcran/console Remplacer les invites (prompts) standard

.import FILE TABLE .indices TABLE .load FILE ?ENTRY? .mode MODE ?TABLE?

.nullvalue STRING

.output FILENAME .output stdout .prompt MAIN CONTINUE

Crer/ouvrir une base (SQLite)

331

Commandes internes (interprteur sqlite3) (Suite)


Commande .quit .read FILENAME Description Quitter linterprteur Excuter la requte SQL prsente dans le chier FILENAME Afcher les instructions CREATE Changer le sparateur utilis pour la sortie et limport Montrer la valeur courante des diffrentes options Lister les noms des tables correspondant un modle LIKE Essayer douvrir une table verrouille pendant MS millisecondes Spcier la largeur des colonnes en mode colonne

.schema ?TABLE? .separator STRING

.show .tables ?PATTERN?

.timeout MS .width NUM NUM ...

Crer/ouvrir une base (SQLite)


int sqlite3_open( const char *lename, sqlite3 **ppDb ); int sqlite3_open16( const void *lename, sqlite3 **ppDb ); int sqlite3_open_v2( const char *lename, sqlite3 **ppDb, int ags, const char *zVfs ); /* Database lename (UTF-8) */ /* OUT: SQLite db handle */ /* Database lename (UTF-16) */ /* OUT: SQLite db handle */ /* /* /* /* Database lename (UTF-8) */ OUT: SQLite db handle */ Flags */ Name of VFS module to use */

332

CHAPITRE 15 Base de donnes

Ces routines permettent douvrir une base de donnes SQLite dont le nom de chier est donn en paramtre. Le nom de chier et lencodage par dfaut de la base sont considrs comme tant de lUTF-8 pour sqlite3open() et sqlite3_open_v2(), mais de lUTF-16 pour sqlite3_open16(). Un pointeur vers une instance de sqlite3 est renvoy par le paramtre ppDb mme si une erreur apparat ( lexception de limpossibilit dallouer la mmoire pour cette instance, auquel cas un pointeur NULL est retourn).
Info Si le nom de chier est une chane vide ou :memory: alors une base prive est cre en mmoire. Cette dernire disparatra sa fermeture.

Si une base est ouverte (et/ou cre) avec succs, alors SQLITE_OK est retourn par la fonction; sinon un code derreur est renvoy. Les fonctions sqlite3_errmsg() ou sqlite3_errmsg16() permettent dobtenir un descriptif de lerreur en anglais.
Attention Selon le code derreur, il peut tre ncessaire de fermer correctement la connexion avec sqlite3_close().

Le paramtre ags supplmentaire de sqlite3_open_v2() permet de spcier loption douverture de la base: SQLITE_OPEN_READONLY. Ouvrir la base en lecture seule. Si la base nexiste pas, une erreur est renvoye; SQLITE_OPEN_READWRITE. Ouvrir la base en lecture/criture. Si le chier est en lecture seule, la base sera ouverte en lecture seule. Dans les deux cas, le chier doit exister sinon une erreur est renvoye;

Crer/ouvrir une base (SQLite) 333

SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE. Ouvrir la base en lecture/criture, et crer celle-ci sil nexiste pas. Cest le comportement par dfaut des fonctions sqlite3_open() et sqlite3_open16(). Il est possible de combiner ces trois modes douverture avec lune des deux autres options suivantes: SQLITE_OPEN_NOMUTEX. La base est ouverte avec le support du multithread (du moins si loption de compilation single-thread na pas t active ou que cette mme option na pas t spcie); SQLITE_OPEN_FULLMUTEX. La base est ouverte avec le support du multithread travers un mcanisme de srialisation. Le dernier paramtre zVfs de sqlite3_open_v2() permet de dnir linterface des oprations systme que la connexion la base SQLite doit utiliser (reportez-vous au manuel de SQLite pour plus dinformations). Un exemple de mise en uvre de la fonction sqlite3_ open() est disponible la section Lancer une requte avec SQLite.
Codes derreur des fonctions sqlite3_open*()
Code SQLITE_OK SQLITE_ERROR SQLITE_INTERNAL SQLITE_PERM SQLITE_ABORT Valeur 0 1 2 3 4 Description Pas derreur Erreur SQL ou base manquante Erreur logique interne SQLite Permission daccs refuse Une fonction callback a demand un abandon

334

CHAPITRE 15 Base de donnes

Codes derreur des fonctions sqlite3_open*() (Suite)


Code SQLITE_BUSY Valeur 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 Description Le chier de base de donnes est verrouill Une table de la base est verrouille Un malloc() a chou Tentative dcriture sur une base en lecture seule Opration termine par sqlite3_interrupt() Une erreur dE/S disque est survenue Limage de la base est corrompue Non utilis. Table ou enregistrement non trouv. chec de linsertion car base pleine Impossible douvrir le chier de base de donnes Non utilis. Erreur du protocole de verrouillage de la base. Base de donnes vide Le schma de la base a chang La chane ou le BLOB dpasse la taille maximale autorise Abandon d la violation dune contrainte Type de donne inadapt Bibliothque utilise incorrectement

SQLITE_LOCKED SQLITE_NOMEM SQLITE_READONLY

SQLITE_INTERRUPT

SQLITE_IOERR SQLITE_CORRUPT SQLITE_NOTFOUND

SQLITE_FULL SQLITE_CANTOPEN

SQLITE_PROTOCOL

SQLITE_EMPTY SQLITE_SCHEMA SQLITE_TOOBIG

SQLITE_CONSTRAIN

SQLITE_MISMATCH SQLITE_MISUSE

Lancer une requte avec SQLite 335

Codes derreur des fonctions sqlite3_open*() (Suite)


Code SQLITE_NOLFS Valeur 22 23 24 25 26 100 101 Description Utilisation de spcicit systme non supporte sur lhte Autorisation refuse Erreur de format de la base auxiliaire 2e paramtre de sqlite3_bind() hors borne Le chier ouvrir nest pas une base de donnes sqlite3_step() a une autre ligne prte sqlite3_step() a ni son excution

SQLITE_AUTH SQLITE_FORMAT SQLITE_RANGE

SQLITE_NOTADB

SQLITE_ROW SQLITE_DONE

Lancer une requte avec SQLite


int sqlite3_exec( sqlite3*, /* An open database */ const char *sql, /* SQL to be evaluated */ int (*callback)(void*,int,char**,char**), /* Callback */ void *, /* 1st argument to callback */ char **errmsg /* Error msg written here */ );

La fonction sqlite3_exec() permet dexcuter une ou plusieurs instructions SQL sans avoir crire beaucoup de code. Les instructions SQL (encodes en UTF-8) sont passes en deuxime paramtre. Ces instructions sont excutes une une jusqu rencontrer une erreur, ou que toutes soient excutes. Le troisime paramtre est un

336

CHAPITRE 15 Base de donnes

callback optionnel appel une fois pour chaque ligne produite par lexcution de toutes les instructions SQL fournies. Le cinquime paramtre prcise o crire un ventuel message derreur.
Attention Tout message derreur renvoy a t allou laide de la fonction sql3_malloc(). Pour viter des pertes mmoire, vous devez imprativement librer cette mmoire avec sqlite3_ free().

Lexemple suivant illustre la manire de lire les donnes dune base. Pour toute autre opration, il suft de crer la requte SQL ncessaire et de lexcuter.
#include <stdio.h> #include <sqlite3.h> static int callback(void *NotUsed, int argc, char **argv, char **azColName) { int i; for(i=0; i<argc; i++) { printf(%s = %s\n, azColName[i], argv[i] ? argv[i] : NULL); } printf(\n); return 0; } int main(int argc, char **argv) { sqlite3 *db; char *zErrMsg = 0;

Fermer une base (SQLite) 337

int rc; if( argc!=3 ) { fprintf(stderr, Usage: %s DATABASE SQL-STATEMENT\n, argv[0]); exit(1); } rc = sqlite3_open(argv[1], &db); if( rc ) { fprintf(stderr, Cant open database: %s\n, sqlite3_errmsg(db)); sqlite3_close(db); exit(1); } rc = sqlite3_exec(db, argv[2], callback, 0, &zErrMsg); if( rc!=SQLITE_OK ) { fprintf(stderr, SQL error: %s\n, zErrMsg); sqlite3_free(zErrMsg); } sqlite3_close(db); return 0; } Exemple dutilisation de lAPI de SQLite

Fermer une base (SQLite)


int sqlite3_close(sqlite3 *);

Cette fonction est le destructeur dun objet sqlite3.

338

CHAPITRE 15 Base de donnes

Attention Votre application doit terminer toutes les instructions prpares et fermer tous les pointeurs de BLOB associs avec lobjet sqlite3 avant de le fermer. Si sqlite3_close() est invoque alors quune transaction tait ouverte, celle-ci sera automatiquement annule.

Un exemple de mise en uvre de la fonction sqlite3_ close() est disponible dans la section Lancer une requte avec SQLite.
Astuce La fonction sqlite3_next_stmt() peut tre utilise pour connatre toutes les instructions prpares associes une connexion. Un exemple type pourrait tre: sqlite3_stmt *pStmt; while ( (pStmt=sqlite3_next_stmt(db,0))!= 0 ) { Sqlite3_nalize(pStmt); }

Crer une table (requte SQL)


CREATE TABLE NomDeLaTable (NomColonne1 Type, NomColonne1 Type, );

Cette requte permet de crer une nouvelle table dans la base de donnes active. Le nom de la table est prciser aprs CREATE TABLE. Si le moteur sous-jacent supporte les

Crer une table (requte SQL) 339

espaces, il faut encadrer le nom par des guillemets doubles (). Vient ensuite la description des colonnes entre parenthses. Il est possible dattribuer des valeurs par dfaut chaque colonne. Cette valeur est utile lorsquaucune valeur nest spcie pour ladite colonne au moment dune insertion. Pour spcier une valeur par dfaut, il suft dajouter default V aprs la dnition du type de donne, oV est la valeur par dfaut souhaite.
CREATE TABLE client (Nom char(50), Prenom char(50), Addresse char(50) default Inconnue, Ville char(50) default Paris, Pays char(25), Date_de_naissance date)

Les tableaux suivants recensent les diffrents types de donnes disponibles pour les bases SQLite, MySQL et Oracle.
Type SQLite
Type NUMERIC Description Si la donne peut tre convertie en entier alors elle est stocke en tant que INTEGER, si elle peut tre convertie en rel alors elle est stocke en tant que REAL, sinon elle est stocke en tant que TEXT. Aucune conversion en BLOB (abrviation de Binary Large Object) ou valeur vide (NULL) nest faite. La valeur est un entier stock sur 1 8octets en fonction de sa valeur La valeur est un rel, stock en reprsentation IEEE 8octets La valeur est un texte, stock dans lencodage de la base (UTF-8, UTF-16-BE ou UTF-16-LE) La valeur est un objet binaire BLOB stock exactement comme fourni

INTEGER REAL TEXT BLOB

340

Type MySQL

Type

Taille

Description

TINYINT [M] [UNSIGNED]

1 octet

Ce type peut stocker des nombres entiers de 128 127 sil ne porte pas lattribut UNSIGNED, dans le cas contraire il peut stocker des entiers de 0 255

SMALLINT [M] [UNSIGNED] 2 octets

Ce type de donnes peut stocker des nombres entiers de 32768 32767 sil ne porte pas lattribut UNSIGNED, dans le cas contraire il peut stocker des entiers de 0 65535

CHAPITRE 15 Base de donnes

MEDIUMINT [M] [UNSIGNED] 3 octets

Ce type de donnes peut stocker des nombres entiers de 8388608 8388607 sil ne porte pas lattribut UNSIGNED, dans le cas contraire il peut stocker des entiers de 0 16777215

INT [M] [UNSIGNED]

4 octets

Ce type de donnes peut stocker des nombres entiers de 2147483648 2147483647 sil ne porte pas lattribut UNSIGNED, dans le cas contraire il peut stocker des entiers de 0 4294967295

INTEGER [M] [UNSIGNED]

Mme chose que le type INT

BIGINT [M] [UNSIGNED]

8 octets

Ce type de donnes stocke les nombres entiers allant de 9223372036854775 808 9223372036854775807 sans lattribut UNSIGNED, et de 0 18446744073709551615 avec

FLOAT [UNSIGNED]

4 ou 8octets

Stocke un nombre de type ottant, en prcision simple de 0 24 ou prcision double de 25 53 (occupe 4octets si la prcision est infrieure 24 ou8 au-del)

FLOAT[(M,D)] [UNSIGNED] 4 octets

M est le nombre de chiffres et D est le nombre de dcimales.

Ce type de donnes permet de stocker des nombres ottants prcision simple.Va de 1,175494351E-38 3,402823466E+38. Si UNSIGNED est activ, les nombres ngatifs sont retirs mais cela ne permet pas davoir des nombres positifs plus grands.

DOUBLE PRECISION[(M,D)] 8 octets

Mme chose que le type DOUBLE

DOUBLE [(M,D)]

8 octets

Stocke des nombres ottants double prcision de 1,7976931348623157E+308 1,7976931348623157E+308 (jusqu 2,2250738585072014E308 en se rapprochant de 0).

Si UNSIGNED est activ, les nombres ngatifs sont retirs mais cela ne permet pas davoir des nombres positifs plus grands.

REAL[(M,D)]

8 octets

Mme chose que le type DOUBLE

DECIMAL[(M[,D])]

M+2 octets si D > 0, M+1 octets si D = 0

Contient des nombres ottants stocks comme des chanes de caractres

NUMERIC [(M,D)]

Mme chose que le type DECIMAL

Crer une table (requte SQL)

DATE

3 octets

Stocke une date au format AAAA-MM-JJ allant de 1000-01-01 9999-12-31

341

342

Type

Taille

Description

DATETIME

8 octets

Stocke une date et une heure au format AAAA-MM-JJ HH:MM:SS allant de 1000-01-01 00:00:00 9999-12-31 23:59:59

TIMESTAMP [M]

4 octets

Stocke une date sous forme numrique allant de 1970-01-01 00:00:00 lanne 2037. Lafchage dpend des valeurs de M: AAAAMMJJHHMMSS, AAMMJJHHMMSS, AAAAMMJJ, ou AAMMJJ pour M gal respectivement 14, 12, 8, et6.

CHAPITRE 15 Base de donnes

TIME

3 octets

Stocke lheure au format HH:MM:SS, allant de -838:59:59 838:59:59

YEAR

1 octet

Anne 2 ou 4chiffres allant de 1901 2155 (4chiffres) et de 1970-2069 (2chiffres)

[NATIONAL] CHAR(M) [BINARY]

M octets

Chane de 255 caractres maximum remplie despaces la n. Loption BINARY est utilise pour tenir compte de la casse.

BIT

1 octet

Mme chose que CHAR(1)

BOOL

1 octet

Mme chose que CHAR(1)

CHAR (M)

M octets

Stocke des caractres. Si vous stockez un caractre et que M vaut255, la donne prendra 255octets. Autant donc employer ce type de donnes pour des mots de longueur identique.

VARCHAR (M) [BINARY]

L+1 octets

Ce type de donnes stocke des chanes de 255caractres maximum. Loption BINARY permet de tenir compte de la casse (L reprsente la longueur de la chane).

TINYBLOB

L+1 octets

Stocke des chanes de 255caractres maximum. Ce champ est sensible la casse (L reprsente la longueur de la chane).

TINYTEXT

L+1 octets

Stocke des chanes de 255caractres maximum. Ce champ est insensible la casse.

BLOB

L+1 octets

Stocke des Chanes de 65535caractres maximum. Ce champ est sensible la casse.

TEXT

L+2 octets

Stocke des chanes de 65535 caractres maximum. Ce champ est insensible la casse (L reprsente la longueur de la chane).

MEDIUMBLOB

L+3 octets

Stocke des chanes de 16777215 caractres maximum.

MEDIUMTEXT

L+3 octets

Chane de 16777215 caractres maximum. Ce champ est insensible la casse.

LONGBLOB

L+4 octets

Stocke des chanes de 4294967295 caractres maximum. Ce champ est sensible la casse.

LONGTEXT

L+4 octets

Stocke des chanes de 4294967295 caractres maximum.

ENUM(valeur_possible1, valeur_possible2, valeur_possible3,...)

1 ou 2octets (la place occupe est fonction du nombre de solutions possibles : 65535 valeurs maximum.

Crer une table (requte SQL) 343

SET(valeur_possible1, valeur_possible2,...)

1, 2, 3, 4 ou 8octets selon de nombre de solutions possibles (de 0 64valeurs maximum).

344

Types de donnes internes (built-in) dOracle


Minimum 1 car. Bonjour DD 10-84 1 car. 01/01/-4712 (avant J.-C.) 1 octet 2000 octets 31/12/9999 2 giga car. 10127 10.9999 AAAHHHHHH...HHH 10-FEB-04 10/02/04 (dpend du format dafchage ou du paramtrage local) 2000 car. A Maximum Exemples de valeurs

Code interne

Type

Description

VARCHAR2 (taille)

Chane de caractres de longueur variable

NVARCHAR2 (taille)

Chane de caractres de longueur variable utilisant le jeu de car. national

NUMBER [(taille[,precision])]

Numrique (prec<=38, exposant max -84 +127)

LONG (taille)

Chane de caractres de longueur variable

CHAPITRE 15 Base de donnes

DATE

Date (du sicle la seconde)

RAW (taille)

Donnes binaires devant tre entres en notation hexadcimale. Taille : 1 255caractres 1 octet 2 Go

LONG RAW (taille)

Donnes binaires devant tre entres en notation hexadcimale

ROWID 1 car.

Type rserv la pseudo-colonne ROWID. Ne peut tre utilis. 255 car. AZERTY W

CHAR (taille)

Chane de caractres de longueur xe

NCHAR (taille)

Chane de caractres de longueur xe utilisant le jeu de car. national

CLOC

LOB de type caractre mono-octet ou 1 octet 1 octet 4 giga-octets 4 giga-octets

1 octet

4 giga car.

NCLOB

multi-octet utilisant le jeu de car. national

BLOB

BLOB ! gros objet binaire

BFILE

Pointeur vers un chier binaire externe

TIMESTAMP (fractional_ seconds_precision)

Date (anne, mois, et jour plus heure, minute, secondes et quantit de fraction de seconde), o fractional_seconds_precision est le nombre de chiffres de la partie fractionnaire des secondes. Les valeurs vont de 0 9 (1pour le dixime, 2pour le centime, ). La valeur par dfaut est6 (le millionime).

Crer une table (requte SQL) 345

TIMESTAMP (fractional_ seconds_precision) WITH LOCAL TIME ZONE

AVEC FUSEAU HORAIRE LOCAL (LOCAL TIME ZONE)

Toutes les valeurs donnes dans le fuseau horaire local, avec lexception suivante: les donnes sont normalises dans le fuseau de la base lorsquelles y sont stockes. Lorsque les donnes sont relues, elles sont converties dans le fuseau horaire local.

346

Code interne

Type

Description

Minimum

Maximum

Exemples de valeurs

CHAPITRE 15 Base de donnes

INTERVAL YEAR (year_ precision) TO MONTH

Stocke en priode de temps en annes et mois, o year_precision est le nombre chiffres pour le nombre dannes. De0 9chiffres aprs la virgule. La valeur par dfaut est2.

INTERVAL DAY (day_preci- Stocke une priode de temps en jours, sion) TO SECOND (fracheures, minutes et secondes, o day_ tional_seconds_precision) precision est le nombre maximum de chiffres pour le nombre de jours (de 0 9, et 2par dfaut) et fractional_ seconds_precision est le nombre de chiffre aprs la virgule pour le nombre de secondes (de0 9, et6 par dfaut).

UROWID [(size)]

Adresse logique dune ligne (chane de caractre en base64) dune table organise en index. La taille (optionnelle) est celle de la colonne de type UROWID. La taille maximale est de 4000octets.

Accder aux donnes dune table (requte SQL) 347

Accder aux donnes dune table (requte SQL)


SELECT * FROM NomDeLaTable SELECT NomDeColonne[, ] FROM NomDeLaTable

Pour parcourir les donnes dune table, utilisez linstruction SELECT FROM Pour garder toutes les colonnes, utilisez le caractre gnrique *. Pour trier les donnes, utilisez loption ORDER BY suivie du ou des noms de colonnes. Linstruction GROUP BY pour regrouper des donnes entre elles. En supposant que la table interroge contienne toutes les ventes (montant de la vente dans la colonne Ventes) effectues par toutes les boutiques (nom de la boutique dans la colonne Boutique et ville de la boutique dans la colonne Ville), la requte suivante donne la recette de chaque boutique en les classant dabord par ville puis par boutique:
SELECT Ville, Boutique, SUM(Ventes) FROM InfoBoutique GROUP BY Boutique ORDER_BY Ville, Boutique

La clause WHERE permet de ltrer les donnes pour ne choisir que celles rpondant un certain critre. La clause DISTINCT permet de ne faire apparatre quune seule fois des entres identiques. Par exemple, la requte suivante permet de slectionner tous les magasins ayant effectu une vente dont le montant se situe entre 500 et 700euros. La clause DISTINCT permet

348

CHAPITRE 15 Base de donnes

de nobtenir quune seule fois le magasin (qui peut avoir effectu plusieurs ventes de ce montant):
SELECT DISTINCT Boutique FROM InfoBoutique WHERE Ventes >= 500 AND Ventes <= 700

Cette requte peut aussi scrire laide de linstruction BETWEEN:


SELECT DISTINCT Boutique FROM InfoBoutique WHERE Ventes BETWEEN 500 AND 700

Enn, supposer quune autre table contienne la rgion dans laquelle se trouve une boutique, il est possible de connatre les ventes par rgion en utilisant une jointure interne avec la requte suivante:
SELECT DISTINCT A1.nom_de_region REGION, SUM(A2. Ventes) VENTES FROM Geographie A1, InfoBoutique A2 WHERE A1.Boutique == A2.Boutique GROUP BY A1.nom_de_region

Ces quelques instructions SQL vous permettront de dbuter rapidement. Pour aller plus loin, jetez un il sur le site mentionn en introduction (http://sql.1keydata.com/fr).

Dnir un environnement ODBC (wxWidgets) 349

Dnir un environnement ODBC (wxWidgets)


#include <wx/dbtable.h> class wxDbConnectInf;

Pour se connecter une source ODBC, il faut fournir au moins trois informations: un nom de source, un identiant dutilisateur et un mot de passe. Une quatrime information, un dossier par dfaut indiquant o sont stockes les donnes, est galement fournir pour des bases de donnes au format texte ou dBase. La classe wxDbConnectInf est conue pour embarquer ces donnes, ainsi que dautres ncessaires pour certaines sources ODBC. Le membre Henv est la cl (handle denvironnement) pour accder une base. Le Dsn fourni doit imprativement et exactement correspondre au nom de la base, tel quil apparat dans la source ODBC (dans le panneau dadministration ODBC sous Windows, ou dans le chier .odbc. ini par exemple). LUid est lidentication utilisateur utiliser pour se connecter la base. Il doit exister dans la base vers laquelle vous souhaitez vous connecter. Il dterminera les droits et privilges de la connexion. Certaines sources ODBC sont sensibles la casse, faites donc attention LAutStr est le mot de passe de lUid, l encore sensible la casse. Le membre defaultDir est utile pour les bases de donnes de type chier. Cest par exemple le cas des bases dBase, FoxPro ou chier texte. Il contiendra un chemin absolu, dans lequel vous aurez intrt utiliser des / plutt que des \ par souci de portabilit.

350

CHAPITRE 15 Base de donnes

Se connecter une base (wxWidgets)


class wxDb; wxDb* wxDbGetConnection(wxDbConnectInf*);

Il y a deux faons de se connecter une base.Vous pouvez: crer une instance de wxDb la main et ouvrir vousmme la connexion; utiliser les fonctions de mise en cache fournies avec les classes wxODBC pour crer, maintenir et dtruire les connexions. Quelle que soit la mthode choisie, il faut dabord crer un objet wxDbConnectInf. Le code suivant montre comment lui fournir tous les paramtres ncessaires la future connexion:
wxDbConnectInf dbConnectInf; dbConnectInf.SetDsn(MonDsn); dbConnectInf.SetUserID(MonNomDUtilisteur); dbConnectInf.SetPassword(MonMotDePasse); dbConnectInf.SetDefaultDir();

Pour allouer lenvironnement de connexion, la classe wxDbConnectInf possde une mthode invoquer, comme le montre la suite de lexemple. Une valeur de retour faux signie un chec de lallocation et le handle est alors indni.
if (dbConnectInf.AllocHenv()) { wxMessageBox(Impossible dallouer lenvironnement ODBC, ERROR de CONNEXION, wxOK | wxICON_EXCLAMATION); return; }

Se connecter une base (wxWidgets)

351

Un moyen plus concis consiste utiliser directement le constructeur:


wxDbConnectInf *dbConnectInf = new wxDbConnectInf(NULL, MonDsn,MonNomDUtilisteur, MonMotDePasse, );

Ds que lenvironnement est cr, vous navez plus vous proccuper de ce handle, mais seulement lutiliser. Il sera libr lors de la destruction de linstance de la classe. Nous parlions tout lheure de deux manires de crer une connexion. La premire, manuelle, consiste crer une instance de wxDb et louvrir:
wxDb* db = newDb(dbConnectInf->GetHenv()); bool opened = db->Open(dbConnectInf);

Lautre solution, plus avantageuse, consiste utiliser le systme de mise en cache fourni. Il offre les mmes possibilits quune connexion manuelle,mais gre automatiquement la connexion, de sa cration au nettoyage lors de sa fermeture en passant par sa rutilisation. Et cela vous vite bien sr de la coder vous-mme. Pour utiliser ce mcanisme, appelez simplement la fonction wxDbGetConnection():
wxDb* db = newDbGetConnection(dbConnectInf);

La valeur retourne pointe ainsi sur un wxDb la fois initialis et ouvert. Si une erreur survient lors de la cration ou de louverture, le pointeur retourn sera nul. Pour fermer cette connexion, utilisez wxDbFreeConnection(db). Un appel wxDbCloseConnections() vide le cache en fermant et dtruisant toutes les connexions.

352

CHAPITRE 15 Base de donnes

Crer la dnition de la table et louvrir (wxWidgets)


class wxDbTable; wxDbTable::wxDbTable(wxDb *pwxDb, const wxString &tblName, const UWORD numColumns, const wxString &qryTblName = , bool qryOnly = !wxDB_QUERY_ONLY, const wxString &tblPath = )

Il est possible daccder au contenu des tables dune base directement travers les nombreuses fonctions membres de la classe wxDb. Heureusement, il existe une solution plus simple, grce la classe wxDbTable qui encapsule tous ces appels. La premire tape consiste crer une instance de cette classe, comme le montre le code suivant:
wxDbTable* table = new wxDbTable(db, tableName, numTableColumns, , !wxDBQUERY_ONLY, );

Le premier paramtre est un pointeur sur une connexion une base (wxDb*); le deuxime est le nom de la table; le troisime est le nombre de colonnes de la table (il ne doit pas inclure la colonne ROWID si vous utilisez Oracle). Viennent ensuite trois paramtres optionnels. qryTableName est le nom de la table ou de la vue sur laquelle les requtes porteront. Il permet dutiliser la vue au lieu de la table elle-mme. Cela permet dobtenir de meilleures performances dans le cas o les requtes impliquent plusieurs tables avec plusieurs jointures. Nanmoins, toutes les requtes INSERT, UPDATE et DELETE seront faites sur la table de base. qryOnly indique si la table sera utilise uniquement

Crer la dnition de la table et louvrir (wxWidgets) 353

en lecture ou non. tblPath est le chemin indiquant o est stocke la base. Cette information est indispensable pour certains types de bases, comme dBase.
Info Une table ouverte en lecture seule offre de meilleures performances et une utilisation mmoire moindre. Si vous tes certain de ne pas avoir modier son contenu, nhsitez pas louvrir dans ce mode. De plus, une table en lecture seule utilise moins de curseurs, et certains types de base sont limits en nombre de curseurs simultans une raison supplmentaire de faire attention.

Lorsque vous dnissez les colonnes rcuprer, vous pouvez conserver de une toutes les colonnes de la table.
table->SetColDefs(0, FIRST_NME, DB_DATA_TYPE_VARCHAR, FirstName, SQL_C_WXCHAR, sizeof(FirstName), true, true); table->SetColDefs( 1, // numro de colonne dans la table LAST_NAME, // nom de la colonne dans la base/requte DB_DATA_TYPE_VARCHAR, // type de donne LastName, // pointeur sur la variable lier SQL_C_WXCHAR, // type de conversion sizeof(LastName), // taille max de la variable lie true, // optionnel, indique si cest une cl (faux par dfaut) true // optionnel, mise jour autorise (vrai par dfaut) );

354

CHAPITRE 15 Base de donnes

La dnition des colonnes commence lindice0 jusquau nombre1 de colonnes spci lors de la cration de linstance de wxDbTable. Les lignes de codes ci-avant lient les colonnes mentionnes de la table des variables de lappli cation. Ainsi, lorsque lapplication appelle wxDbTable:: GetNext() (ou nimporte quelle fonction rcuprant des donnes dans lensemble rsultat), les variables lies aux colonnes contiendront les valeurs correspondantes. Les variables lies ne contiennent aucune valeur tant quaucune Get...() nest appel. Mme aprs un appel Query() le contenu des variables lies est indtermin.Vous pouvez avoir autant dinstances de wxDbTable que souhait sur la mme table. Louverture de la table se contente de vrier si la table existe vraiment, que lutilisateur a au moins un privilge de lecture (clause SELECT), rserve les curseurs ncessaires, lie les colonnes aux variables comme spci et construit une instruction dinsertion par dfaut (clause INSERT) si la table nest pas ouverte en lecture seule.
if (!table->Open()) { // une erreur est survenue lors de louverture }

La seule raison relle dun chec est que lutilisateur na pas les privilges requis. Dautres problmes, comme limpossibilit de lier les colonnes aux variables seront plutt dus des problmes de ressources (comme la mmoire). Toute erreur gnre par wxDbTable::Open() est journalise dans un chier si les fonctions de journalisation (logging) SQL sont actives.

Utiliser la table (wxWidgets) 355

Utiliser la table (wxWidgets)


bool WxDbTable::Query();

Pour utiliser la table et la dnition ainsi cre, il faut maintenant dnir les donnes collecter. Le code suivant montre comment charger une requte SQL dans cette table:
table->SetWhereClause(FIRST_NAME = GEORGE); table->SetOrderByClause(LAST_NAME, AGE); table->SetFromClause();

Si vous souhaitez obtenir un classement invers ajoutez DESC aprs le nom de la colonne, par exemple: LAST_NAME DESC. La clause FROM permet de crer des jointures. Lorsquelle est vide, on utilise directement la table de base. Aprs avoir spci tous les critres ncessaires, il suft de lancer la requte, comme le montre le code suivant:
if (!table->Query()) { // erreur survenue pendant lexcution de la requte }

Bien souvent, lerreur provient dun problme de syntaxe dans la clause WHERE. Pour trouver lerreur, regardez le contenu du membre erreurList[] de la connexion. En cas de succs, cela signie que la requte sest bien droule, mais le rsultat peut tre vide (cela dpend de la requte).

356

CHAPITRE 15 Base de donnes

Il est maintenant possible de rcuprer les donnes. Le code suivant montre comment lire toutes les lignes du rsultat de la requte:
wxString msg; while (table->GetNext()) { msg.Printf(Line #%lu Nom: %s Prnom: %s, table->GetRowNum(), FirstName, LastName); wxMessageBox(msg, Donnes, wxOK | wxICON_ INFORMATION, NULL); }

Fermer la table (wxWidgets)


if (table) { delete table; table = NULL; }

Fermer une table est aussi simple que de dtruire son instance. La destruction libre tous les curseurs associs, dtruit les dnitions de colonnes et libre les handles SQL utiliss par la table, mais pas lenvironnement de connexion.

Fermer la connexion la base (wxWidgets) 357

Fermer la connexion la base (wxWidgets)


wxDb::Close(); wxDbFreeConnection(wxDb*); wxDbCloseConnections();

Lorsque toutes les tables utilisant une connexion donne ont t fermes, vous pouvez fermer cette connexion. La mthode employer dpend de la faon dont vous avez cr cette connexion. Si la connexion a t cre manuellement, vous devez la fermer ainsi:
if (db) { db->Close(); delete db; db = NULL; }

Si elle a t obtenue par la fonction wxDbGetConnection() qui gre un systme de cache, vous devez alors fermer cette connexion comme suit:
if (db) { wxDbFreeConnection(db); db = NULL; }

358

CHAPITRE 15 Base de donnes

Notez que le code ci-dessus libre juste la connexion de telle sorte quelle puisse tre rutilise par un prochain appel wxDbGetConnection(). Pour librer toutes les ressources, vous devez appeler le code suivant:
wxDbCloseConnections();

Librer lenvironnement ODBC (wxWidgets)


wxDbConnectInf::FreeHenv();

Une fois toutes les connexions fermes, vous pouvez librer lenvironnement ODBC:
dbConnectInf->FreeHenv();

Si vous avez utilis la version du constructeur avec tous les paramtres, la destruction de linstance libre lenvironnement automatiquement:
delete dbConnectInf;

16
XML
XML (Extensible Markup Language) est un langage informatique de balisage gnrique de plus en plus rpandu. Il existe principalement deux types dAPI pour la manipulation de chiers XML: DOM (Document Object Modeling). Constitue un objet mmoire de la totalit dun document XML. Cette API permet un accs direct tous les nuds de larbre (lments, texte, attributs) pour les lire, les modier, les supprimer ou en ajouter. Il est par exemple trs utilis avec JavaScript dans les navigateurs web. SAX (Simple API for XML). Systme de traitement squentiel. Il reprsente une relle alternative DOM pour les chiers XML volumineux. Lors de la lecture dun chier XML avec SAX, il est possible de ragir diffrents vnements comme louverture et la fermeture dune balise. Il est galement possible de gnrer des vnements SAX, par exemple lors de la lecture du contenu dune base de donnes, an de produire un document XML.

360

CHAPITRE 16 XML

Il existe diffrentes bibliothques permettant de manipuler des documents XML. En voici quelques-unes parmi les plus rpandues, en rapport avec le C++: MSXML (Microsoft Core XML Services). Trs rpandue, ne fonctionne toutefois que sous Windows. libXML2 (http://www.xmlsof.org). BibliothqueC trs rpandue dans le monde GNU/Linux. Disponible pour Linux, Winodws, Solaris, Mac OSX, HP-UX, AIX.Vous pouvez bien sr lutiliser avec C++. Xerces-C++(http://xerces.apache.org/xerces-c). Implmente des analyseurs (parser) DOM, SAX et SAX2. Disponible pour Windows, Unix/Linux/Mac OSX et Cygwin. eXpat (http://expat.sourceforge.net). Bibliothque crite enC utilise par Firefox (disponibles sur les mmes plate-formes que Firefox). Il existe des ponts (wrappers) C++ de cette bibliothque. Dautres bibliothques haut niveau comme QT (http:// www.trolltech.com) et wxWxWidgets (http://www. wxwidgets.org) fournissent galement leur API pour le traitement des chiers XML. Ce chapitre prsente lutilisation des objets fournis par wxWidgets. Ils utilisent un modle DOM.

Charger un chier XML


#include <wx/xml/xml.h> class WXDLLIMPEXP_XML wxXmlDocument : public wxObject { public: wxXmlDocument(); wxXmlDocument(const wxString& lename, const wxString& encoding = wxT(UTF-8));

Charger un chier XML

361

wxXmlDocument(wxInputStream& stream, const wxString& encoding = wxT(UTF-8)); virtual ~wxXmlDocument(); wxXmlDocument(const wxXmlDocument& doc); wxXmlDocument& operator=(const wxXmlDocum // Lit un chier XML et charge son contenu. // Retourne TRUE en cas de succs, FALSE sinon. virtual bool Load(const wxString& lename, const wxString& encoding = wxT(UTF-8), int ags = wxXMLDOC_NONE); virtual bool Load(wxInputStream& stream, const wxString& encoding = wxT(UTF-8), int ags = wxXMLDOC_NONE);
wxXmlDocument& operator=(const wxXmlDocum

// Enregistre le document dans un chier XML. virtual bool Save(const wxString& lename, int indentstep = 1) const; virtual bool Save(wxOutputStream& stream, int indentstep = 1) const; bool IsOk() const;
wxXmlDocument& operator=(const wxXmlDocum

// Retourne le nud racine du document. wxXmlNode *GetRoot() const;


wxXmlDocument& operator=(const wxXmlDocum

// Retourne la version du document (peut tre vide). wxString GetVersion() const;


wxXmlDocument& operator=(const wxXmlDocum

// Retourne lencodage du document (peut tre vide). // Note : il sagit de lencodage du chier, // et non lencodage une fois charg en mmoire ! wxString GetFileEncoding() const;
wxXmlDocument& operator=(const wxXmlDocum

// Mthodes dcriture : wxXmlNode *DetachRoot(); void SetRoot(wxXmlNode *node); void SetVersion(const wxString& version); void SetFileEncoding(const wxString& encoding); #if !wxUSE_UNICODE // Retourne lencodage de la reprsentation mmoire du

362

CHAPITRE 16 XML

// document (le mme que fourni la fonction Load // ou au constructeur, UTF-8 par dfaut). // NB : cela na pas de sens avec une compilation // Unicode o les donnes sont stockes en wchar_t* wxString GetEncoding() const; void SetEncoding(const wxString& enc); #endif private: // ... };

La classe wxXmlDocument utilise la bibliothque expat pour lire et charger les ux XML. Lexemple suivant charge un chier XML en mmoire:
wxXmlDocument doc; if (!doc.Load(wxT(chier.xml))) { // Problme lors du chargement }

Aprs cela, votre chier se trouve en mmoire sous forme arborescente. Si vous souhaitez garder tous les espaces et les indentations, vous pouvez dsactiver leur suppression automatique. Lexemple suivant montre comment:
wxXmlDocument doc; if (!doc.Load(wxT(chier.xml), wxT(UTF-8), wxXMLDOC_KEEP_WHITESPACE_NODES)) { // Problme lors du chargement }

Manipuler des donnes XM 363

Vous pouvez faire de mme lors de lenregistrement du document dans un chier:


doc.Save(wxT(chier.xml), wxXMLDOC_KEEP_WHITESPACE
_NODES);

Manipuler des donnes XML


// // // // Proprit(es) dun nud XML. Exemple : in <img src=hello.gif id=3/> src est une proprit ayant hello.gif pour valeur et id une autre ayant la valeur 3.

class WXDLLIMPEXP_XML wxXmlProperty { public: wxXmlProperty(); wxXmlProperty(const wxString& name, const wxString& value, wxXmlProperty *next = NULL); virtual ~wxXmlProperty(); wxString GetName() const; wxString GetValue() const; wxXmlProperty *GetNext() const; void SetName(const wxString& name); void SetValue(const wxString& value); void SetNext(wxXmlProperty *next); private: // ... };

364

CHAPITRE 16 XML

// Un nud dun document XML. class WXDLLIMPEXP_XML wxXmlNode { public: wxXmlNode(); wxXmlNode(wxXmlNode* parent, wxXmlNodeType type, const wxString& name, const wxString& content wxXmlProperty* props wxXmlNode* next wxXmlNode(wxXmlNodeType type, const wxString& name, const wxString& content virtual ~wxXmlNode();

= wxEmptyString, = NULL, = NULL); = wxEmptyString);

// Constructeur par copie et oprateur de copie. wxXmlNode(const wxXmlNode& node); wxXmlNode& operator=(const wxXmlNode& node); virtual void AddChild(wxXmlNode *child); virtual bool InsertChild(wxXmlNode *child, wxXmlNode *followingNode); #if wxABI_VERSION >= 20808 bool InsertChildAfter(wxXmlNode *child, wxXmlNode *precedingNode); #endif virtual bool RemoveChild(wxXmlNode *child); virtual void AddProperty(const wxString& name, const wxString& value); virtual bool DeleteProperty(const wxString& name); // Accesseurs : wxXmlNodeType GetType() const; wxString GetName() const; wxString GetContent() const; int GetDepth(wxXmlNode *grandparent = NULL) const; bool IsWhitespaceOnly() const;

Manipuler des donnes XM 365

// // // // //

Rcupre le contenu dun nud wxXML_ENTITY_NODE En effet, <tag>contenu<tag> est reprsent comme wxXML_ENTITY_NODE name=tag, content= |__ wxXML_TEXT_NODE or wxXML_CDATA_SECTION_NODE name= content=contenu wxString GetNodeContent() const; wxXmlNode *GetParent() const; wxXmlNode *GetNext() const; wxXmlNode *GetChildren() const; wxXmlProperty *GetProperties() const; bool GetPropVal(const wxString& propName, wxString *value) const; wxString GetPropVal(const wxString& propName, const wxString& defaultVal) const; bool HasProp(const wxString& propName) const; void SetType(wxXmlNodeType type); void SetName(const wxString& name); void SetContent(const wxString& con); void SetParent(wxXmlNode *parent); void SetNext(wxXmlNode *next); void SetChildren(wxXmlNode *child); void SetProperties(wxXmlProperty *prop); virtual void AddProperty(wxXmlProperty *prop); private: // ...

Un nud XML (wxXmlNode) a un nom et peut avoir un contenu et des proprits. La plupart des nuds sont de type wxXML_TEXT_NODE (pas de nom ni de proprits) ou wxXML_ELEMENT_NODE. Par exemple, avec <title>hi</title>,

366

CHAPITRE 16 XML

on obtient un nud lment ayant pour nom title (sans contenu) possdant pour seul ls un nud texte ayant pour contenu hi. Si wxUSE_UNICODE vaut0, toutes les chanes seront encodes avec la page de codes spcie lors de lappel la fonction Load() du document XML (UTF-8 par dfaut).
Info Le constructeur par copie affecte toujours le pointeur sur le parent et le pointeur sur le suivant NULL. Loprateur daffectation(=) ne recopie ni le pointeur sur le parent ni le pointeur sur le suivant, mais les laissent inchangs. Par contre, il recopie toute larborescence du nud concern (ls et proprits).

Lexemple suivant illustre comment utiliser ces diffrentes classes XML:


wxXmlDocument doc; if ( ! doc.Load(wxT(myle.xml)) ) return false; // Dbut du traitement du chier XML if ( doc.GetRoot()->GetName() != wxT(mon-noeud-racine) ) return false; wxXmlNode *child = doc.GetRoot()->GetChildren(); while (child) { if (child->GetName() == wxT(tag1)) { // process text enclosed by <tag1></tag1> wxString content = child->GetNodeContent(); ... // process properties of <tag1> wxString propvalue1 =

Manipuler des donnes XM 367

child->GetPropVal(wxT(prop1),

wxT(default-value)); wxString propvalue2 = child->GetPropVal(wxT(prop2), wxT(default-value)); ... } else if (child->GetName() == wxT(tag2)) { // process tag2 ... } child = child->GetNext(); }

Le contenu dun nud diffre selon son type. Le tableau suivant vous aidera savoir de quel type de nud il sagit.
wxXmlNodeType: types de nud XML
Nom*
wxXML_ELEMENT_NODE wxXML_ATTRIBUTE_NODE wxXML_TEXT_NODE wxXML_CDATA_SECTION_NODE wxXML_ENTITY_REF_NODE wxXML_ENTITY_NODE wxXML_PI_NODE wxXML_COMMENT_NODE wxXML_DOCUMENT_NODE wxXML_DOCUMENT_TYPE_NODE wxXML_DOCUMENT_FRAG_NODE wxXML_NOTATION_NODE wxXML_HTML_DOCUMENT_NODE

Valeur * 1 2 3 4 5 6 7 8 9 10 11 12 13

* Ces noms et valeurs correspondent lnumration xmlElementType de la bibliothque libXML.

Annexe A
Bibliothques et compilateurs
Compilateurs
Borland TurboC++ et C++Builder Turbo C++: http://cc.codegear.com/free/turbo, gratuit C++ Builder: http://www.codegear.com/products/ cppbuilder, commercial Systmes: Windows La qualit des compilateurs Borland est bien connue. C++ Builder est un vrai IDE (environnement de dveloppement intgr) RAD, comme Delphi mais en C++. Un must. La version 2009 inclut le support du C++0x. Microsoft Visual Studio Site: http://msdn.microsoft.com/en-us/visualc/ default.aspx Systmes: Windows

370

ANNEXE A Bibliothques et compilateurs

Depuis quelque temps maintenant, la version express de Visual Studio C++ est gratuite. Elle peut mme tre utilise pour crer des applications commerciales. GCC GNU Compiler Collection Site: http://gcc.gnu.org Systmes : Windows (cygwin et mingw), Linux Le compilateur libre par excellence. Intel C++ Site: http://www.intel.com/cd/software/products/ asmo-na/eng/compilers/284132.htm Systmes: Windows, Linux, Mac OSX Processeur: Intel seulement Pour ceux dont les performances du code produit sont essentielles

IDE et RAD
Dev-C++ IDE et compilateur Site: http://www.bloodshed.net/devcpp.html http://wxdsgn.sourceforge.net (version RAD avec wxWidgets) Licence: Sources de lapplication (en Delphi) disponibles en GPL Systmes: Windows Compilateurs: IDE pour Mingw ou GCC Dev-C++ est un IDE libre pour programmer en C/C++. Facile dinstallation (une version inclut mme le compilateur Mingw) et pratique (intgration du dbogueur GDB),

IDE et RAD

371

il est le compagnon idal pour ceux qui veulent un IDE simple et rapide. Et il est parfait pour ceux qui veulent dbuter rapidement. WxDevC++ est plus jour que DevC++. De plus, cette version contient les packs WxWindows installs par dfaut. KDevelop IDE Site: http://www.kdevelop.org Licence: GPL Systmes: Linux, Solaris, Unix Compilateurs: GCC Le projet KDevelop a t mis en place en 1998 pour btir un IDE pour KDE facile utiliser. Depuis, KDevelop est disponible pour le public sous la licence GPL et supporte beaucoup de langages de programmation. KDE tant dvelopp avec la bibliothque QT, cet environnement va peut-tre enn devenir disponible pour Windows. suivre car cest un IDE trs largement utilis et dune grande qualit. Ultimate++ Bibliothque graphique et suite RAD Site: http://www.ultimatepp.org Licence: BSD Systmes: Windows, Linux, (version Mac OS X via X11 en cours de dveloppement; WinCE prvue; Solaris et autres systme BSD envisages) Compilateurs: MingGW,Visual (2003+), GCC Ultimate++ est plus quune bibliothque, cest une suite de dveloppement ayant pour ambition la productivit du dveloppeur. Cette suite comprend un ensemble de bibliothque (IHM, SQL, etc.) et un IDE (environnement de

372

ANNEXE A Bibliothques et compilateurs

dveloppement intgr). La rapidit de dveloppement que procure cette bibliothque provient dun usage agressif des possibilits quoffre le C++, plutt que de miser sur un gnrateur de code (comme QT le fait, par exemple). TheIDE, lIDE RAD de cette suite, utilise la technologie BLITZ build pour rduire jusqu quatre fois le temps de compilation. Elle propose galement: un outil de conception visuel dinterface; Topic++, un outil de documentation de code et de documentation dapplication; Assist++, un analyseur de code C++ apportant un systme de compltion automatique de code, de navigation dans le code, et une approche de transformation (refactoring) de code. Si cet environnement vous tente, nhsitez pas Il a tout pour devenir incontournable (il ne lui manque que le support de Mac OSX en natif Cocoa). Autres Borland C++ Builder (voir Compilateurs) Code::Block (http:// www.codeblocks.org) Eclipse (http://www.eclipse.org) XCode (fourni avec le systme dexploitation Mac OSX)

Bibliothques
POCO C++ Dveloppement rseau et XML Site: http://pocoproject.org/poco/info/index.html Licence: Boost Software (licence libre pour le commercial et lopen source) Systmes: Windows, Mac OSX, Linux, HP UX, Tru64, Solaris, QNX, plus Windows CE et tout systme embarqu compatible POSIX.

Bibliothques 373

Compilateurs: Dec CC, Visual C++, GCC, HP C++, IBM xlC, Sun CC POCO C++ (C++ Portable Components) est une collection de bibliothques de classes pour le dveloppement dapplications portables, orientes rseau. Ses classes sint grent parfaitement avec la bibliothque standard STL et couvrent de multiples fonctionnalits: threads, synchroni sation de threads, accs chiers, ux, bibliothques partages et leur chargement, sockets et protocoles rseau (HTTP, FTP, SMTP, etc.), serveurs HTTP et parseurs XML avec interfaces SAX2 et DOM. Blitz++ Calcul scientique en C++ Site: http://www.oonumerics.org/blitz Licence: GPL ou Blitz artistic License Systmes: Linux, Sparc, SGI Irix, Cray, IBM AIX, Solaris, HP UX, Sun, Unix, Dec Alpha, Dec Ultrix. Compilateurs: GCC, Visual C++ (2003+), Intel C++, CRI C++ (Cray), HP C++, KAI C++, etc. Blitz++ est une bibliothque C++ pour le calcul scientique. Elle utilise les templates pour atteindre un niveau de performances proche du Fortran 77/90 (et parfois mme meilleur). ColDet Dtection de collision 3D Site : http://photoneffect.com/coldet Licence: LGPL Systmes: Linux, Windows, Mac OS X, etc. Compilateurs: Visual C++, Borland C++ Builder, GCC, MetroWerks CodeWarrior, etc. Cette bibliothque apporte une solution libre au problme de dtection de collision entre polydres gnriques. Elle vise

374

ANNEXE A Bibliothques et compilateurs

la programmation des jeux3D o lexactitude de la dtection entre deux objets complexes est requise. Cette bibliothque fonctionne sur tout type de modles, y compris des soupes de polygones. Elle utilise une hirarchie de botes englobantes pour optimiser la dtection, puis un test dintersection sur les triangles pour lexactitude. Elle fournit mme, sur demande, le point exact de la collision et les paires de triangles sintersectant. Un systme de timeout peut tre mis en place pour interrompre des calculs trop longs. Il est galement possible de faire des tests dintersection de type lanc de rayon-modle, segmentmodle, et dutiliser directement les primitives de test de collision lanc de rayon-sphre et sphre-sphre. CGAL Computational Geometry Algorithms Library Site: http://www.cgal.org Licence: LGPL/QPL (selon les parties utilises) ou commerciale Systmes: Windows, Linux, Mac OS X, Solaris, SGI Irix Compilateurs: Visual C++, Borland C++, GCC, Intel C++, SunPro, SGI CC, KAI C++ CGAL est une bibliothque de structures et de calculs gomtriques srs et efcaces. Parmi ceux-ci on trouve: les triangulations (2D contraintes ou de Delaunay 2D/3D), les diagrammes de Vorono (points 23/3D, points massiques 2D, segments), les oprations boolennes sur les polydres, les arrangements de courbes et leurs applications (enveloppes 2D/3D, sommes de Minkowski), la gnration de maillage (maillages de Delaunay2D et 3D, peaux), le calcul de gomtries (simplication de maillage de surface, subdivision et paramtrisation, estimation des proprits diffrentielles locales, approximation de crtes et dombiliques), alpha-formes, interpolations, collages, distances, structures de recherche, etc.

Bibliothques 375

Dinkum Compleat Library Standard C++, C99, and Embedded C++ Site: http://www.dinkumware.com Licence: Commercial Systmes: Windows, Linux, Mac OS X, Solaris Compilateurs: Visual C++, GCC, SunC++, Embedded Visual C++ Cette bibliothque est une rimplmentation de la bibliothque standard STL, en y ajoutant le support/mulation du C99 en plus de lISO 14882:1998/2003 et du TR1. Elle met laccent sur la portabilit et les performances. Elle rassemble galement dautres fonctionnalits quil faut glaner dans dautres bibliothques. Cest une bonne solution pour ceux qui en ont les moyens, condition quelle ne fasse pas double emploi avec une autre solution. GC Garbage Collector for C/C++ Site: http://www.hpl.hp.com/personal/ Hans_Boehm/ gc Systmes: Linux, *BSD, Windows, Mac OS X, HP-UX, Solaris, Tru64, IRIX, etc. Si vous tes fatigu de grer la mmoire et avez la possibilit de mettre en place un systme de ramasse-miette, alors essayez cette bibliothque. Elle est utilise par le projet Mozilla (comme dtecteur de perte de mmoire), le projet Mono, le compilateur statique Java GCJ, le runtime Objective C de GNU, et bien dautres. GMP GNU Multiprecision Package Site: http://gmplib.org Licence: LGPL

376

ANNEXE A Bibliothques et compilateurs

Processeurs: AMD64, POWER64, POWER5, PPC970, Alpha, Itanium, x86, Compilateurs: Compilateur C/C++ standard GMP, ou GNUmp, est une bibliothque implmentant des nombres entiers signs, des nombres rationnels et des nombres virgule ottante en prcision arbitraire. Toutes les fonctions ont une interface normalise. GMP est conue pour tre aussi rapide que possible en utilisant les mots machine comme type arithmtique de base, en utilisant des algorithmes rapides, en optimisant soigneusement le code assembleur pour les boucles intrieures les plus communes, et par une attention gnrale porte la vitesse (par opposition la simplicit ou llgance). LEDA Library of Efcient Data types and Algorithms Site: http://www.algorithmic-solutions.com/leda Licence: Gratuite, professionnel, recherche (le contenu diffre selon la licence) Systmes: Windows, Linux, Solaris Compilateurs: Visual C++ (2005+), GCC (3.4+), SunPRO (5.8) LEDA est une immense bibliothque de structures de donnes et dalgorithmes gomtriques et combinatoires. Elle est utilise par certains industrielles pour raliser des bancs dessais sur de grands jeux de donnes. Elle fournit une collection considrable de structures de donnes et dalgorithmes sous une forme qui leur permet dtre employs par des non-experts. Dans la version en cours, cette collection inclut la plupart des structures et algorithmes classiques du domaine. LEDA contient des implmentations efcaces pour chacun de ces types de donnes, par exemple, piles de Fibonacci pour des les dattente prioritaires, tables

Bibliothques 377

dynamiques dadressage dispers parfait (dynamic perfect hashing) pour les dictionnaires, etc. Un atout majeur de LEDA est son implmentation des graphes. Elle offre les itrations standard telles que pour tous les nudsv dun grapheG ou encore pour tous les voisinsW dev; elle permet dajouter et deffacer des sommets et des artes, den manipuler les matrices dincidence, etc. Pantheios C++ Logging Site: http://www.pantheios.org Licence: type BSD Systmes: Windows, Linux, Mac OS X, Unix Compilateurs: Borland (5.5.1+), Comeau (4.3.3+), Digital Mars (8.45+), GCC (3.2+), Intel (6+), Metrowerks(8+), Microsoft Visual C++ (5.0+) Pantheios est une bibliothque de journalisation (logging) offrant un bon quilibre entre contrle des types, performances, gnricit et extensibilit. La portabilit de cette bibliothque est galement un atout. Elle offre un systme de ltrage des messages en fonction des huit niveaux de svrit dnis par le protocole SysLog (RFC 3164, voir http://fr.wikipedia.org/wiki/Syslog et http://tools.ietf.org/html/rfc3164), sans pour autant vous limiter ceux-ci (vous pouvez dnir les vtres). Elle fournit un grand nombre de plug-in dcriture: chier, stderr/stdout, SysLog (avec une implmentation personnalise du protocole SysLog pour Windows), dbogueur Windows, journal dvnement Windows, objet derreur COM, et vous pouvez en crire dautres. La socit Synesis Software (http://synesis.com.au) offre de personnaliser Pantheios selon vos besoins.

378

ANNEXE A Bibliothques et compilateurs

STLport Bibliothque standard alternative Site: http://www.stlport.org Licence: libre Compilateurs: SUN Workshop C++ Compilers 4.0.x-5.0, 5.1, 5.2 (Forte 6, 6u1, 6u2), for Solaris GNU GCC 2.7.2-2.95, 3.0 Compilers, for all GCC platforms IBM xlC 3.1.4.0, for AIX IBM OS/390 C/C++ 1.x - 2.x Compiler, for OS/390 (STLport is the only available and ofcial ANSI library for this platform) IBMVisualAge/ILE C++ for AS/400Version 4 Release 4, for AS/400 IBM Visual Age C++ 3.x-5.x, for OS/2 and Win32 HP ANSI C++ Compiler, for HP-UX SGI MIPSpro C++ 7.x, for IRIX SGI MIPSpro C++ 4.0, for IRIX DEC C++ V5.5-004,V6.x for OSF 3.2/4.0, for Digital Unix. Tandem NCC, for Tandem OS SCO UDK C++, for UnixWare Apogee C++ 4.0, for SUN Solaris KAI C++ 3.1-4.x, for all KAI platforms Portland Group C++ compiler ( pgCC), for all pgCC platforms Microsoft Visual C++ 4.x - 6.x, for Win32 Microsoft Embedded Visual C++ 3.0 Borland C++ 5.02-5.5, for Win16/32

Bibliothques dominante graphique 379

Metrowerks CodeWarrior Pro 1.8-5.3, for Mac OS, Win32 Intel (R) C++ 4.0/4.5/5.0 Reference Compiler, for Win32 Watcom C++ 10.x-11.0, for DOS, Win16, Win32 Powersoft's Power++ 2.0, for Win32 Symantec C++ 7x/8.x, for MacOS and Win32 Apple MPW MrCpp and SCpp compilers, for Mac OS STLport se distingue des STL fournit par la plupart de compilateurs, notamment en intgrant un mode de dbogage la STL laide ditrateurs srs et de prconditions permettant un contrle rigoureux lors de lexcution. Ne cherchez pas plus loin si votre compilateur ne contient pas la bibliothque standard STL.

Bibliothques dominante graphique


SDL Simple DirectMedia Layer Site: http://www.libsdl.org Licence: LGPL (et aussi commerciale avec support pour ceux qui le souhaitent) Systmes: Linux, Windows, Windows CE, BeOS, Mac Os, MacOsX, FreeBSD, NetBSD, OpenBSD, BSD/OS, Solaris, IRIX et QNX. Le support dautres systmes (AmigaOS, Dreamcast, AIX, OSF/Tru64, RISCOS, SymbianOS et OS/2) semble exister mais non ofciellement Compilateurs: Tout compilateur C

380

ANNEXE A Bibliothques et compilateurs

Simple DirectMedia Layer est une bibliothque multimdia multiplate-forme. Elle fournit un accs bas niveau au matriel audio, clavier, souris, joystick, 3D ( travers OpenGL) et tampon vido2D. Elle est utilise par des applications de lecture MPEG, des mulateurs, bon nombre de jeux populaires (dont le port Linux de Civilization: Call To Power). SDL est crite en C, mais fonctionne nativement enC++. Il existe aussi des ponts vers quantit de langages tels Ada, C#, D, Eiffel, Erlang, Euphoria, Guile, Haskell, Java, Lisp, Lua, ML, Objective C, Pascal, Perl, PHP, Pike, Pliant, Python, Ruby, Smalltalk et Tcl. wxWidgets Dveloppement multiplate-forme et IHM Site: http://www.wxwidgets.org Licence: wxWidgets Library Licence (proche de la LPGL) Systmes: MacOSX, GNU/Linux et Unix, Microsoft Windows, OS/2, ainsi que pour du matriel embarqu (embedded) sous GNU/Linux ou Windows CE Compilateurs: Tout compilateur C++ standard wxWidgets (anciennement wxWindows) est une bibliothque graphique libre utilise comme bote outils de programmation dinterfaces utilisateur multiplate-formes. la diffrence dautres botes outils qui tentent de restituer une interface utilisateur identique sur toutes les plateformes, wxWidgets restitue des abstractions comparables, mais avec lapparence native de chaque environnement cible, ce qui est moins dpaysant pour les utilisateurs naux. La bibliothque originale est crite en C++ mais il existe de nombreux ponts vers les langages de programmation courants :Python wxPython;Perl wxPerl;BASIC wxBasic; Lua wxLua; OCaml wxCaml; JavaScript wxJava Script; Java wxJava ou wx4j; Ruby wxRuby; Eiffel

Bibliothques dominante graphique

381

wxEiffel; Haskell wxHaskell; C#/.NET wx.NET; Euphoria wxEuphoria; D wxD. Certains sont plus dvelopps que dautres et les plus populaires restent wxPython, wxPerl et wxBasic. Sous le nom wx , wxWidgets est la base de linterface utilisateur des applications dveloppes avec C++BuilderX (qui nest malheureusement plus disponible) de Borland. QT Dveloppement multiplate-forme et IHM Site: http://trolltech.com/products Licence: QPL Systmes: Linux,Windows,Windows CE, MacOSX, Unix Compilateurs: Tout compilateur C++ standard Qt est une bibliothque logicielle oriente objet et dveloppe en C++. Elle offre des composants dinterface graphique (widgets), daccs aux donnes, de connexions rseau, de gestion des ls dexcution, danalyse XML, etc. Qt permet la portabilit des applications qui nutilisent que ses composants par simple recompilation du code source. Les environnements supports sont les Unix (dont Linux) utilisant le systme graphique X Window System, Windows et Mac OSX. Qt est notamment connue pour tre la bibliothque sur laquelle repose lenvironnement graphique KDE, lun des environnements de bureau les plus utiliss dans le monde Linux. De plus en plus de dveloppeurs utilisent Qt, y compris parmi de grandes entreprises. On peut notamment citer Google, Adobe Systems ou encore la NASA.

382

ANNEXE A Bibliothques et compilateurs

ILOG VIEWS Bibliothque C++ et diteur pour concevoir de trs grosses IHM Site: http://www.ilog.com/products/views Licence: Commerciale Systmes: Windows, Unix (SUN-Solaris, HP-UX, IMBAIX), Linux ILOG VIEWS est une bibliothque C++ haut niveau. Elle inclut un systme de ressource portable, un gestionnaire dvnements, le support PostScript, une bibliothque de composants graphiques trs complte (dont un grant les diagrammes de Gantt), des outils de conception dIHM interactifs trs puissants (avec gestion dtat), des composants grant la connexion et lutilisation de bases de donnes (Oracle, IBM DB2, etc.) La rputation de cette bibliothque nest plus faire.

Utilitaires
Understand for C++ Reverse engineering, documentation and metrics tool Site: http://www.scitools.com/products/understand Licence: Commerciale Systmes: Windows, Linux, Solaris, HP-UX, IRIX, plugin pour Eclipse Langages: C, C++. Il existe aussi des versions pour C# et Java Understand for C++ est un outil danalyse, de documentation et de mtrique (mesure) de code source. Il permet de naviguer dans le code grce un systme de rfrences croises, dditer le code (afch avec mise en forme), et de le visualiser sous diverses vues graphiques.

Utilitaires 383

Cet environnement inclut des API PERL etC permettant dinclure votre propre systme de documentation de votre code source. Cet outil, difcile dutilisation au premier abord, justie leffort dapprentissage par ses qualits. Ch C/C++ interpreter quand le C/C++ devient script Site: http://www.softintegration.com Licence: Commerciale, une version gratuite existe (mais elle nest pas embarquable) Systmes: Windows, Linux, Mac OSX, Solaris, HP-UX, FreeBSD et QNX Compilateurs: GCC, HP C++, Sun CC,Visual C++ Ch est linterprteur C/C++ le plus complet. Il peut tre embarqu dans vos applications. Il supporte la norme ISOC (C90), la plupart des nouvelles fonctionnalits apportes par le C99, les classes en C++, POSIX, X/ Motif, Windows, OpenGL, ODBC, GTK+, C LAPACK, CGI, XML, le dessin graphique 2D/3D, le calcul numrique avanc et la programmation en shell. De plus, Ch possde quelques autres fonctionnalits que lon retrouve dans dautres langages et logiciels. Ch Standard Edition est gratuit, mme pour des applications commerciales. Dommage que la version embarquable (Embedded Ch) ne le soit pas, mme pour les projets non commerciaux

AnnexeB
Les ajouts de la future norme C++ (C++0x)
C++0x, la future norme du langage C++, remplacera lactuelle norme C++ (ISO/IEC 14882), plus connue sous les acronymes C++98 et C++03, publie en 1998 et mise jour en 2003. Celle-ci inclura non seulement plusieurs ajouts au langage lui-mme, mais tendra galement la bibliothque standard C++ en lui incorporant une bonne partie des rapports techniques C++ numro1 (les fameux TR1). Cette norme nest pas encore publie, mais cette annexe en prsente quelques extraits issus du rapport N2597 datant de mai2008. Vous pourrez ainsi vous familiariser avec quelques-unes des avances majeures du C++. g++ a dj commenc implmenter certaines parties de cette future norme. Vous pouvez vous rfrer ladresse web http://gcc.gnu.org/projects/cxx0x.html pour en suivre la progression. Les autres acteurs du secteur des compilateurs (Intel, Borland, etc.) attendent certainement que cette norme soit ge pour nir (ou commencer) leur propre implmentation. Il faudra donc attendre encore avant de pouvoir en utiliser tout le potentiel

386

ANNEXE B Les ajouts de la future norme C++ (C++0x)

Variable locale un thread


thread_local

En environnement multithread, chaque thread possde souvent ses propres variables. Cest le cas par exemple des variables locales dune fonction, mais pas des variables globales ou statiques. Un nouveau type de stockage, en plus des nombreux existants (extern, static, register et mutable) a t propos pour le prochain standard: le stockage local au thread. Le nouveau mot-cl correspondant est thread_local. Un objet thread est analogue un objet global. Les objets globaux existent durant toute lexcution dun programme tandis que les objets thread existent durant la vie du thread. la n du thread, les objets thread sont inaccessibles. Comme pour les objets statiques, un objet thread peut tre initialis par un constructeur et dtruit avec un destructeur.

Unicode et chanes littrales


char s[] = Chaine ASCII standard; wchar_t s[] = LChaine wide-char ; char s[] = u8chaine UTF-8; char16_t s[] = uChaine UTF-16; char32_t s[] = UChaine UTF-32;

Le C++ actuel offre deux types de chanes littrales. La premire, compose entre guillemets, permet de coder des chanes de caractres ASCII zro terminal. La deuxime (L) permet de le faire avec un jeu de caractres large (wchar_t). Aucun de ces deux types ne supporte les chanes Unicode.

Unicode et chanes littrales

387

Linternationalisation des applications devenant la norme aujourdhui, le C++0x permettra au langage de supporter Unicode, laide de trois encodages: UTF-8, UTF-16 et UTF-32. De plus, deux autres types de caractres feront leur apparition: char16_t et char32_t. Vous laurez compris, ils permettront le support de UTF-16 et UTF-32. UTF-8 sera trait directement avec le type char. Lcriture de chanes littrales utilise souvent les squences dchappement pour prciser certains caractres, comme par exemple \x3F. Les chanes Unicode possdent aussi leur squence dchappement. La syntaxe est illustre dans lexemple suivant:
u8caractre Unicode : \u2018.; ucaractre Unicode : \u2018.; Ucaractre Unicode : \u2018.;

Le nombre aprs \u est donn sous forme hexadcimale. Il ne ncessite pas la prsence du prxe0x. Si vous utilisez \u, il est sous-entendu quil sagit dun caractre Unicode 16bits. Pour spcier un caractre Unicode 32bits, utilisez\U et une valeur hexadcimale 32bits. Seules des valeurs Unicode valides sont autorises. Par exemple, des valeurs entre U+D800 et U+DFFF sont interdites car elles sont rserves aux paires de substitution en encodage UTF-16.
R[Une chaine \ bas niveau avec ce quon veut ]; Rdelimiteur[Encore \ ce quon veut ]delimiteur;

Il est parfois utile de dsactiver les squences dchappement. C++0x fournit un moyen de coder des chanes bas niveau. Dans la premire ligne de lexemple prcdent, tout ce qui est entre les crochets ([ et]) fait partie de la chane. Dans la deuxime ligne, delimiteur[ marque le dbut de la chane et ]delimiteur en marque la n.

388

ANNEXE B Les ajouts de la future norme C++ (C++0x)

Le delimiteur est arbitraire et peut tre dni votre convenance; il doit simplement tre le mme au dbut et la n. Comme le montre lexemple suivant, les chanes bas niveau sont galement disponibles en Unicode:
u8RXXX[une chaine UTF-8 bas niveau.]XXX; uR*@[une chaine UTF-16 bas niveau.]*@; UR[une chaine UTF-32 bas niveau.];

Dlgation de construction
class UnType { int nombre; public: UnType(int unNombre): nombre(unNombre) { } UnType(): UnType(42) { } };

Les classes sont un lment essentiel du C++. Les changements apports par la norme C++0x faciliteront et scuriseront la mise en uvre dune hirarchie de classes. En C++ standard, un constructeur ne peut pas en appeler un autre: chaque constructeur doit construire tous les membres de la classe lui-mme, ce qui implique souvent de dupliquer le code dinitialisation. Grce au C++0x, un constructeur pourra en appeler dautres. Dautres langages, comme Java, utilisent dj ce mcanisme (connu sous le nom de dlgation) pour rutiliser le comportement dun constructeur existant.

Hritage des constructeurs 389

Attention C++03 autorisera la syntaxe suivante pour linitialisation des membres: class MaClasse { public: MaClasse() {} explicit MaClasse(int i): m_i(i) {} private: int m_i = 10; }; Tout constructeur initialisera m_i avec la valeur10, sauf si ce constructeur surcharge linitialisation de m_i avec sa propre valeur. Ainsi, le constructeur vide de lexemple prcdent utilise linitialisation de la dnition, mais lautre constructeur initialise la variable avec la valeur donne en paramtre.

Hritage des constructeurs


class B : A { using A::A; // ... };

On aimerait souvent pouvoir initialiser une classe drive avec le mme ensemble de constructeurs que la classe de base. Jusqu prsent, la seule manire de faire cela consiste redclarer tous les constructeurs en les redirigeant sur ceux de la classe de base. Si le compilateur pouvait faire le travail, il en rsulterait moins derreur et lintention serait rendue bien visible. La nouvelle norme C++ va tendre la signication du mot-cl using en ce sens. Il acquiert ainsi une nouvelle signication: permettre dhriter tous les

390

ANNEXE B Les ajouts de la future norme C++ (C++0x)

constructeurs dune classe mre donne, vitant ainsi davoir tous les coder de nouveau.
struct B1 { B1( int ); }; struct B2 { B2( int = 13, int = 42 ); }; struct D1 : B1 { using B1::B1; }; struct D2 : B2 { using B2::B2; };

Dans lexemple prcdent, les constructeurs de B1 candidats lhritage pour D1 sont: B1( const B1 & ); B1( int ). Lensemble des constructeurs de D1 est: D1() constructeur par dfaut implicite, mal form si utilis; D1( const D1 & ) constructeur par copie implicite, non hrit; D1( int ) constructeur hrit implicitement. Les constructeurs de B2 candidats lhritage pour D2 sont: B2( const B2 & ); B2( int = 13, int = 42 ); B2( int = 13 ); B2().

Hritage des constructeurs

391

Lensemble des constructeurs de D2 est: D2() constructeur par dfaut implicite, non hrit; D2( const D2 & ) constructeur par copie implicite, non hrit; D2( int, int ) constructeur hrit implicitement; D2( int ) constructeur hrit implicitement.
Info Si deux dclarations using hritent de constructeurs ayant la mme signature, le programme est mal form; il est alors ncessaire de le surcharger dans la classe lle. struct B1 { B1( int ); }; struct B2 { B2( int ); }; struct D1 : B1, B2 { using B1::B1; using B2::B2; // mal form: double dclaration // implicite dun mme constructeur }; struct D2 : B1, B2 { using B1::B1; using B2::B2; D2( int ); // ok : dclaration utilisateur // prvient le conit };

392

ANNEXE B Les ajouts de la future norme C++ (C++0x)

Constructeur et destructeur par dfaut


struct Type { Type() = default; }; struct Type { Type() = delete; };

En C++ standard, le compilateur peut fournir, pour les objets qui ne les fournissent pas explicitement, un constructeur par dfaut, un constructeur par copie, un oprateur de copie (operator=) et un destructeur. Le C++ dnit galement des oprateurs globaux (comme operator= et operator new) qui peuvent galement tre surchargs. Le problme est quil ny a aucun moyen de contrler la cration de ces versions par dfaut. Par exemple, pour rendre une classe non copiable, il faut dclarer le constructeur par dfaut et loprateur de copie en tant que membres privs et ne pas les implmenter. De cette faon, lutilisation de ces fonctions provoquera soit une erreur de compilation, soit une erreur ddition de liens. Cette technique, si elle fonctionne, est toutefois loin dtre idale. C++0x permettra de forcer, ou empcher, la gnration de fonctions par dfaut. Le code suivant dclare explicitement le constructeur par dfaut dun objet:
struct Type { Type() = default; // dclaration explicite Type(AutreType valeur); };

Constructeur et destructeur par dfaut 393

Inversement, il est possible de dsactiver la gnration automatique de ces fonctions. Le code suivant montre comment crer un type non copiable:
struct Type // non copiable { Type() = default; Type(const Type&) = delete; Type& operator=(const Type&) = delete; };

Le code suivant montre comment empcher un type dtre allou dynamiquement. La seule faon dallouer un tel objet est de crer une variable temporaire (allocation sur la pile). Il est impossible dcrire Type *ptr = new Type; (allocation sur le tas).
struct Type // allocation dynamique interdite { void* operator new(std::size_t) = delete; };

La spcication = delete peut tre utilise sur nimporte quelle fonction membre pour empcher son utilisation. Cela peut tre utile, par exemple, pour empcher lappel dune fonction avec certains paramtres. Lexemple suivant montre comment empcher une conversion implicite de double vers int lors de lappel de la fonction membref() avec un nombre rel. Un appel tel obj->f(1.0) provoquera une erreur de compilation.
struct Type // pas de double { void f(int i); void f(double) = delete; };

394

ANNEXE B Les ajouts de la future norme C++ (C++0x)

La technique employe dans lexemple prcdent peut tre gnralise pour forcer le programmeur nutiliser que le type explicitement dni par la dclaration de la fonction membre. Le code suivant nautorise des appels la fonction membref() quavec le type int:
struct Type // que des int { void f(int i); template<T> void f(T) = delete; };

union tendue
struct Point { Point() {} Point(int x, int y) : m_x(x), m_y(y) {} int m_x, m_y; }; union { int i; double d; Point p; // autoris avec C++0x };

En C++, le type des membres dune union est soumis certaines restrictions. Par exemple, les unions ne peuvent pas contenir des objets dnissant des constructeurs non triviaux. Plusieurs des limitations imposes sur les unions semblant tre superues, le prochain standard enlvera toutes les restrictions sur le type des membres des unions lexception des rfrences sur les types. Cela rendra les unions plus faciles utiliser.

Type numration fort 395

Oprateurs de conversion explicites


struct Type { explicit operator bool() { ... } };

La nouvelle norme permettra dappliquer le mot-cl explicit tout oprateur de conversion, en plus des constructeurs, interdisant au compilateur de convertir automatiquement un type en un autre. Le cas ci-dessus empchera par exemple la conversion implicite en boolen.

Type numration fort


enum class Enumeration { Val1, Val2, Val3 = 10, Val4 // 11 };

Les numrations sont par nature des non-types: ils ont lapparence dun type (et presque la signication) mais nen sont pas rellement. Ce sont en effet de simples entiers. Cela permet de comparer deux valeurs provenant dnumrations distinctes. La dernire norme (datant de 2003) a apport un peu de scurit en empchant la conversion dun entier en numration ou dune numration vers une autre. Autre limitation: le type dentier sous-jacent ne peut tre congur; il est dpendant de limplmentation du compilateur et de la plate-forme.

396

ANNEXE B Les ajouts de la future norme C++ (C++0x)

La norme C++0x permettra, en ajoutant le mot-cl class aprs enum, de crer de vrais types numration part entire. Ainsi avec lexemple gnrique ci-avant, une comparaison du type Val4 == 11 gnrera une erreur de compi lation. Par dfaut, le type sous-jacent dune classe dnumration est toujours le type int. Il pourra nanmoins tre personnalis. Dans lexemple ci-aprs, vous voyez comment le changer en unsigned int ou long. De plus, il sera ncessaire de prciser le nom de lnumration: Enum1::Val1.
enum class Enum1 : unsigned int { Val1, Val2 }; enum class Enum2 : long { Val1, Val2 };

Listes dinitialisation
class Sequence { public: Sequence(std::initializer_list<int> list); }; Sequence s = { 1, 2, 3, 5, 7, 0 }; void Fonction(std::initializer_list<oat> list); Fonction({1.0f, -3.45f, -0.4f});

Le concept de liste dinitialisation existe dj enC. Lide est de fournir une liste darguments pour initialiser un tableau ou une structure (dans ce dernier cas, les lments doivent tre donns dans le mme ordre que ceux de la structure). Le systme est rcursif et permet dinitialiser des structures de structures. C++0x tend ce concept aux classes. Le concept de liste dinitialisation se nomme std::

Initialisation uniformise 397

initializer_list<>. Il pourra tre utilis non seulement avec les constructeurs mais aussi avec les fonctions. Une liste dinitialisation est constante. Ses valeurs ne peuvent pas tre changes ou modies par la suite. Les conteneurs standard pourront tre initialiss grce cette nouvelle fonctionnalit.
std::vector<std::string> v = { abc, defgh, azerty };

Initialisation uniformise
struct Structure { int x; oat y; }; struct Classe { Classe(int x, oat y) : m_x(x), m_y(y) {} private: int m_x; oat m_y; }; Structure var1{5, 3.2f}; Classe var2{2, 4.3f};

C++0x met en place une uniformisation de linitialisation des types. Linitialisation de var1 fonctionne exactement comme une liste dinitialisation de styleC (mais le= nest plus ncessaire). Une conversion implicite est automatiquement utilise si ncessaire (et si possible), sinon il en rsulte une erreur de compilation. Linitialisation de var2 appelle simplement le destructeur.

398

ANNEXE B Les ajouts de la future norme C++ (C++0x)

Cette uniformisation va permettre domettre le type dans certains cas. Le code suivant vous montre en quel sens:
struct StrindAndID { std::string text; int id; }; StringAndID makeDefault() { return {UnTexte, 12}; // Notez lomission du type }

Cette uniformisation ne remplace pas la syntaxe habituelle des constructeurs. En effet, il peut tre indispensable dy avoir recours. Imaginez quune classe implmente un constructeur par liste dinitialisation (Classe(std::initializer_list<T>);). Ce constructeur aura priorit sur tous les autres avec la syntaxe unie. On rencontre ce cas de gure avec la nouvelle implmentation de la STL, comme par exemple std::vector<>. Cela implique que lexemple suivant ne cre pas un tableau de quatre lments mais un tableau contenant4 pour seul lment:
std::vector<int> V{4}; // V contient la valeur 4

Pour crer un vecteur de quatre lments, il faut utiliser la syntaxe de constructeur standard, comme ceci:
std::vector<int> V(4); // V contient 4 lments vides

Type automatique 399

Type automatique
auto variable = ...; decltype(variable)

Jusqu prsent, pour dclarer une variable en C++, il tait obligatoire de spcier son type. Cependant, avec larrive des templates et des techniques de mtaprogrammation associes, le type de quelque chose nest pas toujours ais dnir. Cest particulirement le cas des valeurs de retour des fonctions templates. De mme, il est parfois pnible de dterminer le type de certaines variables que lon souhaite sauvegarder, obligeant fouiller parfois loin dans les dnitions de classes. Grce au mot-cl auto, C++0x permettra la dclaration dune variable sans en connatre le type, condition toutefois dinitialiser celle-ci explicitement. Le type est ainsi spci implicitement par la valeur dinitialisation. Le code suivant illustre cette technique avec lappel dune fonction (le compilateur connat forcment son type de retour) et avec une constante littrale (en loccurrence un int).
auto varDeTypeObscure = std::bind(&uneFonction, _2,
_1, unObjet);

auto varEntiere = 5;

C++0x offrira encore un mot-cl supplmentaire, decltype(...), permettant dobtenir le type dune variable, et surtout de pouvoir lutiliser comme un type traditionnel.
int unEntier; decltype(unEntier) unAutreEntier = 33;

400

ANNEXE B Les ajouts de la future norme C++ (C++0x)

Astuce Le mot-cl auto permettra aussi dobtenir un code plus ais lire. Par exemple, le code suivant: for (std::vector<int>::const_iterator it = V.begin(); it != V.end(); ++it) { // ... } pourra scrire: for (auto it = V.begin(); it != V.end(); ++it) { // ... }

Boucle for ensembliste


int tableau[5] = {1, 2, 3, 4, 5}; for(int &x : tableau) { x *= 2; }

La bibliothque BOOST dnie la notion dintervalle (range). Les conteneurs ordonns sont un surensemble de la notion dintervalle, et deux itrateurs sur un tel conteneur peuvent dnir un intervalle. Ces concepts, et les algorithmes qui les utilisent, seront inclus dans la bibliothque C++0x standard. Cependant, lintrt du concept dintervalle est tel que le C++0x en fournira une implmentation au niveau du langage lui-mme. Dans lexemple prcdent, la variablex a une porte gale celle de la boucle for. La partie aprs de : est un intervalle

Syntaxe de fonction unie

401

sur lequel seffectue litration. Les tableaux de typeC sont automatiquement convertis en intervalle. Tout objet respectant le concept dintervalle peut y tre utilis. Les std::vector<> de la STL du C++0x en sont un exemple.

Syntaxe de fonction unie


[] Fonction(paramtres) -> TypeRetour;

La syntaxe laquelle nous sommes habitus jusqu prsent, issue duC, commenait faire sentir ses limites. Par exemple, il est actuellement impossible dcrire le code suivant:
template <typename A, typename B> T plus(const A& a, const B& b) { return a+b; }

On pourrait tre tent dutiliser la nouvelle fonctionnalit de type automatique que le C++0x fournira, en remplaantT par decltype(A+B). Mais cela ne suft pas, car il faudrait que les typesA etB soient dnis, et cela nest vrai quaprs linterprtation du prototype de la fonction. Do lajout la norme C++0x dune nouvelle syntaxe de fonction unie. Le code suivant montre comment crire une telle fonction en C++0x:
template <typename A, typename B> [] plus(const A& a, const B& b) -> decltype(a+b) { return a+b; }

402

ANNEXE B Les ajouts de la future norme C++ (C++0x)

Pour les fonctions membres, cette syntaxe sutilise ainsi:


struct Structure { []FonctionMembre(int x) -> int; }; [] Structure::FonctionMembre(int x) -> int { return x + 2; }

Concepts
concept NomDuConcept<typename T> { ... } auto concept NomDuConcept<typename T> { ... } template <typename T> requires NomDUnConcept<T> ... concept_map NomDuConcept<Type> { ... } template<typename T> concept_map NomDuConcept<T> { ... }

Lcriture de classes et de fonctions templates ncessite presque obligatoirement de prsupposer certaines conditions sur les paramtres templates. Par exemple, la STL actuelle suppose que les types utiliss avec ses conteneurs soient assignables. Contrairement au contrle de type inh rent au polymorphisme, il est possible de passer nimporte quel type comme paramtre template, condition bien sr quil supporte toutes les oprations employes dans limpl mentation de ce template. Du coup, les prrequis sur le type du paramtre sont implicites au lieu dtre explicites. Les concepts vont permettre de codier linterface quun paramtre template ncessite.

Concepts 403

La premire motivation la cration des concepts est lamlioration des messages derreur du compilateur. En effet, jusqu prsent, une erreur lie un manque de fonctionnalit dun paramtre template provoquait un message derreur souvent trs long (le type template est souvent trs long). De plus, ce message survient bien plus loin dans le code quau moment de linstanciation de la classe ou de la fonction template (au moment de lutilisation de la fonctionnalit manquante), rendant difcile la localisation et la comprhension de lerreur.
template <LessThanComparable T> const T& min(const T& x, const T& y) { return y < x ? : y : x; }

La fonction template suivante, en employant LessThanCompara ble plutt que class ou typename, exprime comme prrequis que le typeT doit respecter le concept LessThanComparable prcdemment dni. Grce cela, si vous utilisez cette fonction template avec un type qui ne respecte pas ce concept, vous obtiendrez un message derreur clair indiquant que le type utilis lors de linstanciation du template ne respecte pas le concept LessThanComparable. Le code suivant est une forme dcriture plus gnrique exprimant la mme chose, mais permettant aussi de spcier plusieurs concepts au lieu dun:
template <typename T> requires LessThanComparable<T> const T& min(const T& x, const T& y) { return y < x ? : y : x; }

404

ANNEXE B Les ajouts de la future norme C++ (C++0x)

Chose intressante, il sera possible dempcher lutilisation dun modle template avec un type conforme un concept donn. Par exemple, requires ! LessThanComparable<T>:
auto concept LessThanComparable<typename T> { bool operator<(T, T); }

Le mot-cl auto signie ici que tout type qui supporte les oprations mentionnes dans le concept est considr comme supportant le concept. Sans ce mot-cl, le type doit utiliser une carte de concept pour signier lui-mme quil supporte ce concept. Le concept prcdent indique que tout typeT tel quil existe un oprateur< prenant deux objets de typeT et retournant un bool sera considr comme LessThanComparable. Cet oprateur ne doit pas ncessairement tre un oprateur global mais peut aussi tre un oprateur membre du typeT.
auto concept Convertible<typename T, typename U> { operator U(const T&); }

Les concepts peuvent impliquer plusieurs types diffrents. Par exemple, le concept prcdent exprime quun type peut tre converti en un autre. Pour utiliser un tel concept, il est obligatoire dutiliser la syntaxe gnralise:
template<typename U, typename T> requires Convertible<T, U> U convert(const T& t) { return t; }

Concepts 405

Les concepts peuvent aussi tre composs. La dnition du concept suivant indique quil doit aussi satisfaire un autre concept, Regular, prcdemment dni:
concept InputIterator<typename Iter, typename Value> { requires Regular<Iter>; Value operator*(const Iter&); Iter& operator++(Iter&); Iter operator++(Iter&, int); }

Les concepts peuvent galement tre drivs dun autre concept, comme avec lhritage.Voici un exemple de syntaxe:
concept ForwardIterator<typename Iter, typename Value> : InputIterator<Iter, Value> { // Autres contraintes }

Des typenames peuvent aussi tre associs un concept. Cela impose au template respectant ce concept de dnir ces types. Le code suivant illustre cela avec le concept ditrateur:
concept InputIterator<typename Iter> { typename value_type; typename reference; typename pointer; typename difference_type; requires Regular<Iter>; requires Convertible<reference, value_type>; reference operator*(const Iter&); // drfrencement Iter& operator++(Iter&); // pr-incrment Iter operator++(Iter&, int); // post-incrment // ... }

406

ANNEXE B Les ajouts de la future norme C++ (C++0x)

Les cartes de concept, que nous avons voques prcdemment, permettent dassocier explicitement un type un concept. Lexemple suivant permet de spcier tous les types du concept dInputIterator pour le type char*:
concept_map { typedef typedef typedef typedef }; InputIterator<char*> char value_type ; char& reference ; char* pointer ; std::ptrdiff_t difference_type ;

Les cartes de concept peuvent elles-mmes bncier de la notion de template. Lexemple suivant gnralise la carte de concept prcdente tous les pointeurs:
template <typename T> concept_map InputIterator<T*> { typedef T value_type ; typedef T& reference ; typedef T* pointer ; typedef std::ptrdiff_t difference_type ; };

Autorisation de sizeof sur des membres


struct UnType { AutreType membre; }; sizeof(UnType::membre);

Avec la norme actuelle, cet exemple produit une erreur de compilation. Avec la norme C++0x, vous obtiendrez la taille de AutreType.

Template externe

407

Amlioration de la syntaxe pour lutilisation des templates


Type<Autre<int>> obj;

Jusqu aujourdhui, les compilateurs confondent>> avec loprateur de redirection. Il est obligatoire de sparer les deux> par un (ou plusieurs) espace. Cette limitation sera leve avec lapparition du support de C++0x.

Template externe
extern template class ...;

En C++, le compilateur doit instancier un template chaque fois que celui-ci est rencontr. Cela peut considrablement augmenter le temps de compilation, en particulier si le template est instanci dans de nombreux chiers avec les mmes paramtres. Il nexiste aucun moyen de dire de ne pas provoquer linstanciation dun template. Le C++ dispose dj dun moyen de forcer linstanciation dun template. Le code suivant en montre un exemple:
template class std::vector<MaClasse>;

C++0x introduit lide de template externe, permettant ainsi de prvenir son instanciation. Le code suivant montre comment, en indiquant au compilateur de ne pas instancier le template dans cette unit de traduction:
extern template class std::vector<MaClasse>;

408

ANNEXE B Les ajouts de la future norme C++ (C++0x)

Expressions constantes gnralises


constexpr

Le C++ dispose dj du concept dexpression constante. Cest le cas par exemple de 3 + 4 qui correspond toujours au mme rsultat. Les expressions constantes sont des opportunits doptimisation pour le compilateur. Il excute lexpression la compilation et stocke le rsultat dans le programme. Dun autre ct, il y existe un certain nombre dendroits o les spcications du langage C++ requirent des expressions constantes: dnir un tableau ncessite une constante, et une valeur dnumration doit tre une constante. Pourtant, alors quune expression constante est toujours le rsultat dun appel de fonction ou dun constructeur, il est impossible dcrire (actuellement) le code ci-aprs. Ce nest pas possible car cinq() + 5 nest pas une expression constante. Le compilateur na aucun moyen de savoir que cinq() est une constante.
int cinq() { return 5; } int tableau[cinq() + 5]; // illgal

C++0x introduit le mot-cl constexpr, qui permet au programmeur de signier quune fonction ou un objet est une constante la compilation. Lexemple prcdant peuttre rcrit comme ci-aprs. Cela permet au compilateur de comprendre, et vrier, que cinq() est une constante.
constexpr int cinq() { return 5; } int tableau[cinq() + 5]; // ok : cre un tableau de 10 entiers

Lutilisation de constexpr sur une fonction impose des limitations trs strictes sur ce que la fonction peut faire.

Expressions constantes gnralises 409

Premirement, la fonction ne peut pas retourner void. Deuximement, le contenu de la fonction doit tre de la forme return expression. Troisimement, expression doit tre une expression constante, aprs substitution des paramtres. Cette expression constante ne peut quappeler dautres fonctions dnies comme constpexpr, ou utiliser dautres variables ou donnes constantes elles aussi. Quatrimement, toute forme de rcursion est interdite. Enn, une fonction ainsi dclare ne peut tre appele que dans sa porte. Les variables peuvent aussi tre des expressions constantes. Les variables ainsi dclares sont implicitement const. Elles peuvent uniquement stocker des rsultats dexpressions ou de constructeurs constants.
constexpr double graviteTerrestre = 9.8; constexpr double graviteLunaire = graviteTerrestre / 6

An de pouvoir crer des objets constants, un constructeur peut tre dclar constexpr. Dans ce cas, son corps doit tre vide et ses membres doivent tre initialiss avec des expressions constantes. Le destructeur dun tel objet doit galement tre trivial. Tout membre dune classe, comme le constructeur par copie, la surcharge doprateur, etc., peut tre dclar comme constexpr, condition quil vrie les contraintes dune fonction constante. Cela permet au compilateur de copier des classes ou de faire des oprations sur celles-ci pendant le processus de compilation. Bien entendu, les fonctions et constructeurs constants peuvent tre appels avec des variables non constantes. Dans ce cas, aucune optimisation ne sera faite la compilation: le code sera appel lors de lexcution. Du coup, le rsultat ne peut videmment pas tre stock dans une variable constante.

410

ANNEXE B Les ajouts de la future norme C++ (C++0x)

Les variadic templates


template <class ... Types> ...;

Les variadic templates sont le pendant pour les templates des fonctions variadiques, dont la plus connue est printf(). Les templates pourront ainsi recevoir un nombre indtermin de paramtres, au sens de non x lavance. La premire notion connatre pour utiliser cette nouvelle fonctionnalit est le template parameter pack. Cest le paramtre template qui reprsente zro ou plusieurs arguments. Dans la dnition gnrique prcdente, il correspond class ... Types. La deuxime notion est le function parameter pack. Cest lquivalent de la premire notion pour ce qui est des paramtres dune fonction template. Dans template<class ... Types> void f(Types ... args); le function parameter pack est Types... args. Lexpression parameter pack dsigne indiffremment lune ou lautre des deux notions prcdentes. Enn, la troisime et dernire notion est le pack expansion. Cest une expression qui permet de transmettre une fonction la liste des arguments du pack. Dans le code suivant, &rest... est un pack expansion:
template <ckass ... Types> void f(Types ... rest); template <ckass ... Types> void f(Types ... rest) { f(&rest ...); }

Le code suivant est utilis dans la nouvelle dnition de tuple (n-uplet). Elle permet de rcuprer le dernier argument

Pointeur nul

411

dun pack et illustre bien lutilisation de cette nouvelle fonctionnalit:


// rcupration du type du dernier argument template <class... Args> struct last; template <class T> struct last<T> { typedef T type; } template <class T, class... Args> struct last<T, Args...> : last<Args...> {}; // rcupration du dernier argument template <class T> const T& last_arg(const T& t) { return t; } template <class T, class... Args> typename last<Args...>::type const & last_arg(const T&, const Args&... args) { return last_arg(args...); }

Pointeur nul
nullptr

Depuis prs dune trentaine dannes, lcriture du pointeur nul est sujette polmique: NULL, 0 ou (void*)0? Le C++0x va enn clore le chapitre; nullptr va devenir un nouveau mot-cl dsignant le pointeur nul.Voici quelques exemples illustrant son utilisation et son intrt.

412

ANNEXE B Les ajouts de la future norme C++ (C++0x)

char* p = nullptr; // ok char c = nullptr; // erreur de type int i = nullptr; // erreur de type Pointeurs et types numriques int (A::*pmf)(char*) = nulltpr; // ok int A::*pmi = nullptr; // ok, pointeur sur une donne membre Pointeurs et membres if (nullptr == 0) // erreur de type if (nullptr) // erreur, pas de conversion implicite vers bool if (p == nullptr) // ok Pointeurs et comparaison void f(int); void f(char*); f(0); // appelle f(int) f(nullptr); // appelle f(char*) Pointeurs et surcharge

Tuple
template <class... Types> class tuple;

Un tuple est une collection de dimension xe dobjets de types diffrents. Tout type dobjets peut tre lment dun tuple. Cette nouvelle fonctionnalit est implmente dans

Tuple

413

un nouveau chier en-tte et bncie des extensions du C++0x telles que: paramtres templates innis (variadic templates), rfrence sur rfrence et arguments par dfaut pour les fonctions templates.
typedef tuple<int, double, long&, const char*> tuple_test; long longueur = 43; tuple_test essai( 22, 3.14, longueur, Bonjour); longueur = get<0>(essai); // longueur vaudra 22 get<3>(essai) = Au revoir; // modie la 4e valeur du tuple

Il est possible de crer le tuple essai sans dnir son contenu si les lments du tuple possdent tous un constructeur par dfaut. De mme, il est possible dassigner un tuple un autre si les deux tuples sont de mme type (dans ce cas, chaque lment doit possder un constructeur par copie) ou si chaque lment de droite est convertible avec le type de llment de gauche (grce un constructeur appropri).
tuple <int, double, std::string> t1; tuple <char, short, const char*> t2; t1 = t2; // Ok car int = char et double = short possible // et std::string(const char*) existe

Les oprateurs relationnels sont disponibles pour les tuples ayant le mme nombre darguments. Deux expressions sont ajoutes pour vrier les caractristiques dun tuple la compilation: tuple_size<T>::value retourne le nombre dlments du tupleT; tuple_element<i, T>::type retourne le type de lobjet en positioni du tupleT.

414

ANNEXE B Les ajouts de la future norme C++ (C++0x)

Lambda fonctions et lambda expressions


[](paramtres){code}

Les lambda fonctions sont bien agrables pour ceux qui utilisent les algorithmes STL, car il devient enn possible de les utiliser sans pour autant crire une fonction qui ne sera utilise quune seule fois. Par exemple, si vous voulez aujourdhui afcher le contenu dun std:vector<> sur la console, deux possibilits soffrent vous: la boucle classique et la fonction std::copy avec les itrateurs de ux. Le code suivant vous rappelle celles-ci:
// lancienne for( std::vector<Objet>::iterator it = V.begin(); it != V.end() +it) std::cout << *it << ; // avec les algorithmes STL std::copy( V.begin(), V.end(), std::ostream_iterator <const Objet>(cout, ) );

Grce aux lambda fonctions, vous pourrez dsormais crire le code suivant:
for_each( V.begin(), V.end(), [](const Objet& o) { std:cout << o << ; });

Lambda fonctions et lambda expressions

415

Leur intrt est encore renforc lorsque cela vite de coder des foncteurs ou des fonctions. Par exemple, le code suivant montre quel point il sera simple de rechercher un lment de conteneur respectant certains critres:
std::nd_if( V.begin(), V.end(), [](const Objet& o) { return w.poids() > 98; });

En n de compte, tous les algorithmes STL de type boucle pourront tre utiliss comme des boucles. Le }); va devenir un classique

Annexe C
Conventions dappel x86
Les bibliothques de codes, sous forme de bibliothques dynamiques (.DLL sous Windows, .so sous Unix/Linux) ou de bibliothque statiques (souvent .lib sous Windows et .a sous Unix/Linux), ne sont pas toutes crites en C ou C++. Cest pourquoi vous rencontrerez certainement des mots-cls tels que stdcall, safecall, cdecl ou WINAPI (ce dernier est en fait une macro dclare par les API Windows). Ils correspondent des conventions dappel et dnissent comment appeler des fonctions souvent compiles sparment, peut-tre mme laide dun compilateur diffrent du vtre au moment de lexcution du programme. Une convention dappel dtermine: lordre dans lequel les paramtres sont transmis la fonction; o les paramtres sont placs (dans la pile ou dans des registres); quels registres peuvent tre utiliss par la fonction; qui de lappel (la fonction) ou de lappelant (la partie de code qui lappelle) doit nettoyer la pile aprs lappel. Un sujet li la convention dappel est la dcoration de nom (ou name mangling). Elle dtermine comment lier les noms utiliss dans le code avec les symboles de noms utiliss par

418

ANNEXE C Conventions dappel x86

le lieur (linker) en ajoutant automatiquement le nombre et le type des paramtres associs au nom dune mthode. Si vous voulez en savoir un peu plus sur larchitecture x86, nhsitez pas aller jeter un il sur cette page web: http://en.wikipedia.org/wiki/X86.
Info Il existe souvent de subtiles diffrences dans lapplication de ces conventions par les divers compilateurs en usage. Il est donc souvent ardu dinterfacer des codes compils par des compilateurs diffrents. Heureusement, les conventions utilises pour les API standard (comme stdcall) sont implmentes de manire uniforme.

Microprocesseurs Intel *
Famille 40xx, 80xx et 80xx Pentium P6 NetBurst Mobile Architecture Core Architecture Nehalem Processeurs 4004, 4040, 8080, 8085, 8086, 8088, 80186, 80188, 80286, 80386, 80486, 486SL, 486SX, 486DX Pentium, Pentium MMX Pentium Pro, Pentium II, Pentium III Pentium 4, Pentium 4-M, Pentium D, Pentium Extreme Edition Pentium M, Core Solo et Duo Core 2 Solo, Duo et Quad, Core 2 Extreme Core i7

Serveur / Calculateur Xeon, Itanium, Itanium 2 Autres XScale RISC Atom, Celeron, Centrino, Pentium Dual-Core PXA250, PXA255, PXA260, PXA270, PXA290 iAPX 432, i860, i960

* Processeurs non x86 en italiques

Convention dappel cdecl

419

Microprocesseurs AMD
Famille Architecture Socket Processeurs K5, K6, K7, K8, K8L, K10 Socket 7, Socket Super 7, Socket A, Socket 563, Socket S1, Socket 754, Socket 939, Socket 940, Socket AM2, Socket AM2+, Socket F, Socket F+, Socket AM3 Am2900, AMD 29000, 8086, 8088, 80286, Am286, Am386, Am486, Am5x86, SSA5, 5k86, AMD K6, AMD K6-2, AMD K6-III Athlon, Athlon XP,Athlon 64, Athlon64 X2, Athlon FX Phenom 8000, Phenom 9000, Phenom FX Duron, Sempron Mobile Athlon XP, Mobile Athlon 64, Turion 64, Turion 64 X2, Turion 64 Ultra Athlon MP, Opteron Geode, Alchemy ATI, AMD

Processeurs antrieurs K7 Athlon Phenom Duron / Sempron Mobile Serveur Autres Chipset

Convention dappel cdecl


void __cdecl f(oat a, char b); // Borland et Microsoft void __attribute__((cdecl)) f(oat a, char b); // GNU GCC

Convention dappel par dfaut enC, elle lest aussi en C++. Lappelant est responsable du dsempilement des arguments, permettant ainsi dutiliser des fonctions nombre darguments dynamiques (dnies laide des points de suspension ...).

420

ANNEXE C Conventions dappel x86

Les paramtres de la fonction concerne sont placs sur la pile de droite gauche (dans lordre inverse de celui de la dclaration de la fonction). La valeur de retour est place dans le registre EAX, sauf pour les valeurs ottantes qui se trouveront dans le registre ottant FP0. Les registres EAX, ECX et EDX sont libres dtre utiliss par la fonction. La fonction appelante nettoie la pile aprs le retour de lappel. Il existe quelques diffrences dans limplmentation de cdecl, notamment vis--vis de la valeur de retour. Du coup, des programmes compils pour diffrents systmes dexploitation et/ou par diffrents compilateurs peuvent tre incompatibles, mme sils utilisent la convention cdecl et ne font pas appel lenvironnement sous-jacent. Microsoft C++ sous Windows retournera les structures de donnes simples de moins de 8octets dans EAX:EDX. Les plus grandes structures ou celles ncessitant un traitement particulier li aux exceptions (tels un constructeur, un destructeur ou un oprateur daffectation particulier) sont retournes en mmoire. G++ sous Linux transmet toutes les structures ou classes en mmoire, quelle que soit leur taille. En outre, les classes qui ont un destructeur sont toujours passes par rfrence, mme pour les paramtres par valeur. Pour ce faire, lappelant alloue la mmoire ncessaire et utilise un pointeur sur celle-ci comme premier paramtre cach. Lappel la remplit et retourne ce pointeur tout en supprimant le paramtre cach.

Convention dappel pascal

421

Convention dappel pascal


void __pascal f(oat a, char b); // Borland et Microsoft

Les paramtres sont placs sur la pile de la gauche vers la droite, loppos de cdecl, et lappel est responsable du nettoyage de la pile. Dans notre cas, lappel f(5,d); correspondrait au code assembleur suivant:
PUSH EBP MOV EBP, ESP PUSH $00000005 PUSH d CALL f

Astuce Que faire si votre compilateur ne supporte pas cette directive et que vous deviez employer une bibliothque utilisant la convention pascal? Vous pouvez ruser en dclarant les en-ttes des fonctions qui vous intressent en stdcall mais en inversant les paramtres. Par exemple, avec gcc: // int __pascal f(int a, int b, int c); int __attribute__((stdcall)) f(int c, int b, int a);

422

ANNEXE C Conventions dappel x86

Convention dappel stdcall


void __stdcall f(oat a, char b); // Borland et Microsoft void __attribute__((stdcall)) f(oat a, char b); // GNU GCC

Cette convention est une variante de la convention pascal dans laquelle les paramtres sont passs par la pile, de droite gauche. Les registres EAX, ECX et EDX sont rservs lusage de la fonction. La valeur de retour se trouve dans le registre EAX.

Convention dappel fastcall


void __fastcall f(oat a, char b); // Borland et Microsoft void __attribute__((fastcall)) f(oat a, char b); // GNU GCC

Cette convention nest pas toujours quivalente suivant les compilateurs. Utilisez-la avec prcaution. Les deux premiers (parfois plus) arguments 32bits sont transmis la fonction dans des registres: le plus souvent dans EDX, EAX et ECX. Les autres arguments sont transmis par la pile, en ordre inverse (de droite gauche) comme cdecl. La fonction appelante est responsable du nettoyage de la pile sil y a lieu.

Convention dappel thiscall 423

Attention En raison de lambigut induite par les diffrentes implmentations de cette convention, il est recommand dutiliser fastcall uniquement pour les fonctions ayant 1, 2 ou3 (selon le compi lateur) arguments 32bits et lorsque la vitesse dexcution est essentielle.

Convention dappel thiscall


thiscall

Cette convention est utilise par les fonctions membres non statiques. Deux versions de thiscall se ctoient en fonction du compilateur et de lutilisation du systme de nombre darguments variable. Avec GCC, thiscall est pratiquement identique cdecl: la fonction appelante nettoie la pile, et les paramtres sont passs de droite gauche. La diffrence tient dans lajout du pointeur this, ajout en dernier la pile comme si ctait le premier argument de la fonction. Avec Microsoft Visual C++, le pointeur this est transmis par le registre ECX et cest lappel qui nettoie la pile, linstar de la convention stdcall utilise enC pour ce compilateur et de lAPI Windows. Si la fonction utilise la syntaxe des paramtres variables, cest lappelant de nettoyer la pile (comme pour cdecl).

424

ANNEXE C Conventions dappel x86

Conventions dappel x86


Mot-cl cdecl pascal Ordre/Nettoyage DG/Appelant GD/Appel Remarque Souvent la convention par dfaut Convention OS/2 16bits, Windows3.x et certaines versions de Borland Delphi Convention de lAPI Win32 2 registres (ECX et EDX) avec __msfastcall 2 registres (ECX et EDX) avec __fastcall 3registres (EAX, EDX et ECX) jusqu 4registres (EAX, EDX, EBX et ECX) en ligne de commande (voir http://www.openwatcom.org/ index.php/Calling_Conventions# Specifying_Calling_Conventions_ the_Watcom_Way) thiscall DG/Appelant/GCC DG/Appel/MSVC versions 2005 ou suprieurs

stdcall fastcall

DG/Appel DG/Appel/GCC DG/Appel/MSVC GD/Appel/Borland DG/Appel/Watcom

Conventions dappel spciques


Mot-cl register Ordre/Nettoyage DG/Appel ------/Appel DG/Appelant DG/Appelant Remarque Anciennement utilis par Borland pour le fastcall Utilis en Delphi Borland pour encapsuler les objets COM/OLE Standard pour les API OS/2 Utilis par les compilateurs VisualAge dIBM

safecall

syscall optlink

Index
A
Abstraction 82 Accder un lment, conteneur standard 156 aux donnes dune table, requte SQL 347 accumulate, algorithme standard 214 adjacent_difference, algorithme standard 215 adjacent_nd, algorithme standard 217 ADT (abstract data types) 82 advance, fonction 144 Agrgation 61 Ajouter, conteneur standard 153 Algorithme standard accumulate 214 adjacent_difference 215 adjacent_nd 217 binary_search 218 copy 219 copy_backward 221 count 223 count_if 224 equal 225 equal_range 226 ll 228 ll_n 228 nd 228 nd_end 265 nd_rst_of 229 nd_if 228 for_each 230 foreach 304 generate 231 includes 232 inner_product 234 inplace_merge 243 iota 235 is_heap 236 is_sort 274 iter_swap 275 lexicographical_compare 238 lexicographical_compare_ 3way 238 lower_bound 241 make_heap 236 max 245 max_element 246 merge 243 min 245 min_element 246 mismatch 247 next_permutation 248

426

Algorithme standard, nth_element

nth_element 250 partial_sort 251 partial_sort_copy 252 partial_sum 253 partition 254 pop_heap 236 power 256 prev_permutation 248 push_heap 236 random_sample 257 random_sample_n 258 random_shufe 259 remove 260 remove_copy 262 remove_copy_if 262 remove_if 260 replace 263 replace_copy 263 replace_copy_if 263 reverse 264 reverse_copy 264 rotate 264 rotate_copy 264 search 265 search_n 265 set_difference 268 set_intersection 270 set_symetric_difference 272 set_union 273 sort 274 sort_heap 236 stable_partition 254 stable_sort 274 swap 275 swap_range 275 transform 276 uninitialized_copy 281 uninitialized_copy_n 281

uninitialized_ll 282 uninitialized_ll_n 282 unique 278 unique_copy 278 upper_bound 241 Alias 8 Voir aussi Raccourcis ; Liens symboliques Allocation dynamique 113 append, fonction 188 Assertion la compilation, bibliothque BOOST 306 assign, fonction 179 at, fonction 156 auto, mot-cl, C++0x 399

INDEX

B
back, fonction 156 back_insert_iterator 150 back_inserter 150 bad, fonction (ux) 195 Base de donnes accder aux donnes dune table, requte SQL 347 BEGIN TRANSACTION, requte SQL 322 BETWEEN, requte SQL 348 COMMIT, requte SQL 322 CREATE TABLE, requte SQL 338 crer la dnition de table et ouverture 352 crer une table, requte SQL 338 dnir un environnement 349 DISTINCT, requte SQL 347 fermer la connexion 357

boost::shared_array, classe, bibliothque BOOST

427

fermer la table 356 GROUP BY, requte SQL 347 jointure interne, requte SQL 348 librer lenvironnement 358 ORDER BY, requte SQL 324, 347 se connecter un base 350 SELECT, requte SQL 347 utiliser la table 355 WHERE, requte SQL 347 begin, fonction 154 BEGIN TRANSACTION, requte SQL 322 BETWEEN, requte SQL 348 Bibliothque dllexport 100 dllimport 100 Bibliothque BOOST assertion la compilation 306 boost::format 286 boost::get<>(variant) 301 boost::intrusive_ptr, classe 300 boost::lexical_cast 289 boost::regex, classe 292 boost::regex_match, fonction 292 boost::regex_search, fonction 293 boost::scoped_array, classe 299 boost::scoped_ptr, classe 299 boost::shared_array, classe 297 boost::shared_ptr, classe 296 boost::variant 300 boost::weak_ptr, classe 298 BOOST_FOREACH 304

BOOST_STATIC_ASSERT 306 expressions rgulires 291 caractres spciaux 294 object_pool 121 pointeurs faibles 298 pointeurs forts 296 pointeurs intelligents 295 pointeurs intrusifs 299 pointeurs locaux 299 pool 120 pool_alloc 122 singleton_pool 121 union de types scurise
300

binary_search, algorithme standard 218 bool, mot-cl 29 boost format, bibliothque BOOST 286 get<>(variant), bibliothque BOOST 301 intrusive_ptr, classe, bibliothque BOOST 300 lexical_cast, bibliothque BOOST 289 regex, classe, bibliothque BOOST 292 regex_match, fonction, bibliothque BOOST 292 regex_search, fonction, bibliothque BOOST 293 scoped_array, classe, bibliothque BOOST 299 scoped_ptr, classe, bibliothque BOOST 299 \hared_array, classe, bibliothque BOOST 297

INDEX

428

boost::shared_ptr, classe, bibliothque BOOST

shared_ptr, classe, biblio thque BOOST 296 variant, bibliothque BOOST 300 weak_ptr, classe, bibliothque BOOST 298 BOOST_FOREACH, bibliothque BOOST 304 BOOST_STATIC_ASSERT, bibliothque BOOST 306 break, mot-cl 13

C
C++0x auto, mot-cl 399 concept, mot-cl 402, 403, 405 constexpr, mot-cl 408 decltype, mot-cl 399 default, mot-cl 392 dlgation 388 delete, mot-cl 392 enum, mot-cl 395 explicit, mot-cl 395 expression constante 408 extern, mot-cl 407 fonction, syntaxe unie 401 for, mot-cl 400 initialisation avec une liste 396 initialisations 397 lambda fonctions 414 nullptr, mot-cl 411 requires, mot-cl 404 sizeof, mot-cl 406 template, mot-cl 410 template, syntaxe 407 template externe 407 thread_local, mot-cl 386

tuple 412, 413 Unicode 386 union, mot-cl 394 using, mot-cl 389 variadic template 410 case, mot-cl 10 catch, mot-cl 123, 125 cdecl, convention dappel 419 Chane de caractres append, fonction 188 assign, fonction 179 chercher 182 compare, fonction 180 comparer 180 concatner 188 crer 178 changer 181 effacer une partie 189 empty, fonction 180 erase, fonction 189 extraire 184 nd, fonction 183 nd_rs_not_of, fonction 184 nd_rst_of, fonction 184 getline, fonction 190 insrer 187 insert, fonction 187 istringstream (ux) 206 length, fonction 180 lire dans un ux 190 longueur 180 ostringstream (ux) 206 rechercher 182 remplacer une partie 185 replace, fonction 186 rnd, fonction 183 size, fonction 180 string_stream 191

INDEX

CREATE TABLE, requte SQL

429

stringstream (ux) 205 substr, fonction 184 swap, fonction 181 Unicode 386 Chane de caractres (string, wstring, crope, wrope), conteneur standard 178 Choix du conteneur standard 153 Classe abstraite 82 de caractristiques 141 clear, fonction 154 COMMIT, requte SQL 322 compare, fonction 180 Composition 60 concept, mot-cl, C++0x 402,
403, 405

const, mot-cl 25, 58, 72 const_cast, mot-cl 34 constexpr, mot-cl, C++0x 408 Constructeur 68, 69 Conteneur standard accder un lment 156 ajouter 153 chane de caractres (string, wstring, crope, wrope) 178 choix 153 complexit algorithmique 173,
175

cration 152 ensemble (set) 166 FIFO 164 le double entre (deque) 161 insrer 153 LIFO 163 liste chane (list) 160

parcourir 154 pile (stack) 163 queue (queue) 164 queue de priorit (priority_queue) 165 supprimer 154 table associative (map, mutlimap) 167, 169 tableau (vector) 158 table de hashage (hash_map, hash_set, hash_mutlimap, hash_mutliset) 170 continue, mot-cl 13 Convention dappel cdecl 419 fastcall 422 pascal 421 stdcall 422 thiscall 423 Conversion de spcialisation 36 de type C 32 donne en chane de caractres 289 explicite 70 qualit 39 transversale 36 copy, algorithme standard 219 copy_backward, algorithme standard 221 copy_n,fonction 222 count, algorithme standard 223 count, fonction 157 count_if, algorithme standard 224 CREATE TABLE, requte SQL 338

INDEX

430

Crer une table, requte SQL

Crer une table, requte SQL 338 crope (chane de caractres), STL 178

D
decltype, mot-cl, C++0x 399 default, mot-cl, C++0x 392 Dlgation, C++0x 388 delete, mot-cl 113 C++0x 392 delete, oprateur, rednition 114 deque (le double entre), STL 161 Drfrencement 48 Destructeur 68, 69 distance, fonction 142 DISTINCT, requte SQL 347 dllexport, mot-cl 100 dllimport, mot-cl 100 DOM, XML 359 dowhile, mot-cl 12 dynamic_cast, mot-cl 36, 87

equal_range, algorithme standard 226 erase, fonction 154, 189 Espace de noms 40 Exception 123, 125 expliciter 129 gestion des ressources 132 relancer 127 STL 134 terminate() 125, 131 transmettre 127 unexpected () 131 explicit, mot-cl 71 C++0x 395 Expression constante, C++0x 408 Expressions rgulires bibliothque BOOST 291 caractres spciaux 294 extern, mot-cl 44 C++0x 407

INDEX

F
fail, fonction 195 fastcall, convention dappel 422 Fichier crire 200 tat 195 lire 197 mode 194 ouvrir 194 se dplacer dans 201 FIFO, conteneur standard 164 File double entre (deque), conteneur standard 161

E
empty, fonction 180 Encapsulation 85 end, fonction 154 Ensemble (set, multiset), conteneur standard 166 enum, mot-cl 7, 31 C++0x 395 eof, fonction (ux) 195 equal, algorithme standard 225

includes, algorithme standard

431

ll, algorithme standard 228 ll_n, algorithme standard 228 nd, algorithme standard 228 nd, fonction 157, 183 nd_end, algorithme standard 265 nd_rs_not_of, fonction 184 nd_rst_of, algorithme standard 229 nd_rst_of, fonction 184 nd_if, algorithme standard 228 ags, fonction (ux) 202 ush, fonction (ux) 200 Foncteurs 88 de la STL 92 Fonction advance 144 copy_n 222 distance 142 embarque 46 membre 62 statique 73 syntaxe unie, C++0x 401 virtuelle pure 82 for, ensembliste, C++0x 400 for, mot-cl 12 for_each, algorithme standard 230 foreach, algorithme standard 304 format, bibliothque BOOST 286 friend (mot-cl) 66 front, fonction 156 front_insert_iterator 149 front_inserter 149

G
gcount, fonction (ux) 198 generate, algorithme standard 231 get, fonction (ux) 198 get<>(variant), bibliothque BOOST 301 getline, fonction 190, 198 getline, fonction (ux) 199 good, fonction (ux) 195 goto, mot-cl 14, 43 GROUP BY, requte SQL 347

H
hash_map (table associative), STL 170 hash_multiset (table de hashage), STL 170 hash_set (table de hashage), STL 170 Hritage multiple 78 priv 77 protg 76 publique 76 simple 74 virtuel 79 visibilit 75 INDEX

I
if, mot-cl 10 ignore, fonction (ux) 199 includes, algorithme standard 232

432

Indirection

Indirection 48 Information dynamique de type 87 Initialisation avec une liste, C++0x 396 C++0x 397 inline, mot-cl 46 inner_product, algorithme standard 234 inplace_merge, algorithme standard 243 Insrer, conteneur standard 153 insert, fonction 187 insert_iterator 148 inserter 148 Instanciation explicite 99 intrusive_ptr, classe, bibliothque BOOST 300 iota, algorithme standard 235 is_heap, algorithme standard 236 is_sort, algorithme standard 274 istream_iterator 145 istringstream (ux) 206 iter_swap, algorithme standard 275 Itrateur 137 advance() 144 back_insert_iterator 150 back_inserter 150 classe de caractristique 140 concepts 138 dinsertion 148 en dbut 149 en n 150

distance 142 front_insert_iterator 149 front_inserter 149 insert_iterator 148 inserter 148 istream_iterator 145 ostream_iterator 146 reverse_bidirectional_iterator 146 reverse_iterator 146 iterator_traits, STL 140

J
Jointure interne, requte SQL 348

INDEX

L
Lamba fonctions, C++0x 414 length, fonction 180 lexical_cast, bibliothque BOOST 289 lexicographical_compare, algorithme standard 238 lexicographical_compare_3way, algorithme standard 238 Liens symboliques Voir aussi Alias, Raccourcis LIFO, conteneur standard 163 list (liste chane), STL 160 Liste chane (list), conteneur standard 160 lower_bound, algorithme standard 241

Mot-cl, throw

433

M
Macros 16, 17 make_heap, algorithme standard 236 Manipulateur (ux) 203, 204 map (table associative), STL 167, 169 max, algorithme standard 245 max_element, algorithme standard 246 Membre fonction 62 pointeur this 67 Mmoire allocation dynamique 113 chec de rservation 116 handler 117 partage, QT 312 pool 120 merge, algorithme standard 243 Mtaprogrammation 106 avantages et inconvnients 111 mta-oprateurs 108, 109 min, algorithme standard 245 min_element, algorithme standard 246 mismatch, algorithme standard 247 Mot-cl auto, C++0x 399 bool 29 break 13 case 10 catch 123, 125

concept, C++0x 402, 403, 405 const 25, 58, 72 const_cast 34 constexpr, C++0x 408 continue 13 decltype, C++0x 399 default, C++0x 392 delete 113 delete, C++0x 392 dowhile 12 dynamic_cast 36, 87 enum 7, 31 enum, C++0x 395 explicit 71 explicit, C++0x 395 extern 44 extern, C++0x 407 for 12 for, C++0x 400 goto 14, 43 if 10 mutable 58, 72 namespace 40 new 113 nothrow 119 nullptr, C++0x 411 reinterpret_cast 35 requires, C++0x 404 sizeof, C++0x 406 static 28, 42, 73 static_cast 33 struct 6, 31 switch 10 template 96 template, C++0x 410 thread_local, C++0x 386 throw 123, 125

INDEX

434

Mot-cl, try

INDEX

try 123, 125 typedef 8 typeid 87 typename 99, 101 union 7, 31 union, C++0x 394 using 41 using, C++0x 389 virtual 79 void 42 volatile 318 wchar_t 31 while 12 multimap (table associative), STL 167, 169 multiset (ensemble), STL 166 mutable, mot-cl 58, 72 Mutex, QT 313

delete, rednition 114 de placement 115 logiques 12 new, rednition 114 priorit 18 ORDER BY, requte SQL 324, 347 ostream_iterator 146 ostringstream (ux) 206

P
Parcourir, conteneur standard 154 partial_sort, algorithme standard 251 partial_sort_copy, algorithme standard 252 partial_sum, algorithme standard 253 partition, algorithme standard 254 pascal, convention dappel 421 Patron dalgorithme 88 peek, fonction (ux) 199 Pile (stack), conteneur standard 163 Pointeurs 48 faibles, bibliothque BOOST 298 forts, bibliothque BOOST 296 intelligents, bibliothque BOOST 295 intrusifs, bibliothque BOOST 299 locaux, bibliothque BOOST 299 Polymorphisme 82, 83 pool, BOOST 120 pool_alloc, BOOST 122

N
name mangling 417 namespace, mot-cl 40 new, mot-cl 113 new, oprateur, rednition 114 next_permutation, algorithme standard 248 nothrow, mot-cl 119 nth_element, algorithme standard 250 nullptr, mot-cl, C++0x 411

O
object_pool, BOOST 121 Oprateur binaires 12 de comparaison 11

rnd, fonction

435

Pool mmoire 120 pop_heap, algorithme standard


236

power, algorithme standard 256 Prprocesseur 16, 17 constantes 17 prev_permutation, algorithme standard 248 printf, quivalent 286 private, hritage 75 private, mot-cl 75 protected, hritage 75 protected, mot-cl 75 public, hritage 75 public, mot-cl 75 push_back, fonction 154 push_front, fonction 154 push_heap, algorithme standard 236 put, fonction (ux) 200 putback, fonction (ux) 200

Q
Queue (queue), conteneur standard 164 Queue de priorit (priority_queue), conteneur standard 165

R
Raccourcis Voir aussi Alias, Liens symboliques random_sample, algorithme standard 257 random_sample_n, algorithme standard 258

random_shufe, algorithme standard 259 rbegin, fonction 154 rdstate, fonction (ux) 196 read, fonction (ux) 199 Rfrences 29, 50 regex, classe, bibliothque BOOST 292 regex_match, fonction, bibliothque BOOST 292 regex_search, fonction, bibliothque BOOST 293 reinterpret_cast, mot-cl 35 remove, algorithme standard 260 remove_copy, algorithme standard 262 remove_copy_if, algorithme standard 262 remove_if, algorithme standard 260 rend, fonction 154 replace, algorithme standard 263 replace, fonction 186 replace_copy, algorithme standard 263 replace_copy_if, algorithme standard 263 requires, mot-cl, C++0x 404 reverse, algorithme standard 264 reverse_bidirectional_iterator 146 reverse_copy, algorithme standard 264 reverse_iterator 146 rnd, fonction 183

INDEX

436

rotate, algorithme standard

rotate, algorithme standard 264 rotate_copy, algorithme standard 264 RTTI (runtime type information) 87

S
SAX DOM 359 XML 359 scoped_array, classe, bibliothque BOOST 299 search, algorithme standard 265 search_n, algorithme standard 265 seekg, fonction (ux) 201 seekp, fonction (ux) 201 SELECT, requte SQL 347 Smaphore, QT 316 set (ensemble), STL 166 set_difference, algorithme standard 268 set_intersection, algorithme standard 270 set_symmetric_difference, algorithme standard 272 set_union, algorithme standard 273 setf, fonction (ux) 202 shared_array, classe, bibliothque BOOST 297 shared_ptr, classe, bibliothque BOOST 296 singleton_pool, BOOST 121 size, fonction 180

sizeof, mot-cl, C++0x 406 sort, algorithme standard 274 sort_heap, algorithme standard 236 Spcialisation partielle 104 totale 102 SQLite 321 accder aux donnes dune table, requte SQL 347 crer une base (API) 331 crer une base, interprteur 328 crer une table, requte SQL 338 installation 325 interprteur (sqlite3) 328 lancer une requte 335 lire des donnes 336 quand lutiliser 322 quand ne pas lutiliser 326 sqlite3_close, fonction 337 sqlite3_errmsg, fonction 332 sqlite3_errmsg16, fonction 332 sqlite3_nalize, fonction 338 sqlite3_free, fonction 336 sqlite3_malloc, fonction 336 sqlite3_next_stmt, fonction 338 sqlite3_open*, fonction, codes derreur 333, 334, 335 sqlite3_close, fonction 337 sqlite3_errmsg, fonction 332 sqlite3_errmsg16, fonction 332 sqlite3_nalize, fonction 338 sqlite3_free, fonction 336 sqlite3_malloc, fonction 336 sqlite3_next_stmt, fonction 338

INDEX

template, mot-cl

437

wrope (chane de sqlite3_open*, fonction, codes caractres) 178 derreur 333, 334, 335 wstring (chane de stable_partition, algorithme caractres) 178 standard 254 stable_sort, algorithme standard string (chane de caractres), STL 178 274 stringstream (ux) 205 stack (pile), STL 163 struct, mot-cl 6, 31 static, mot-cl 28, 42, 73 substr, fonction 184 static_cast, mot-cl 33 swap, algorithme standard 275 stdcall, convention dappel 422 swap, fonction 181 STL swap_range, algorithme crope (chane de standard 275 caractres) 178 switch, mot-cl 10 deque (le double entre) 161 exceptions (liste) 134 T foncteurs 92 hash_map (table de Table associative (map), hashage) 170 conteneur standard 167, 169 hash_multimap (table de hasTableau (vector), conteneur hage) 170 standard 158 hash_multiset (table de Table de hashage (hash_map, hashage) 170 hash_set, hash_mutlimap, hash_set (table de hash_mutliset), conteneur hashage) 170 standard 170 iterator_traits 140 tellg, fonction (ux) 201 list (liste chane) 160 tellp, fonction (ux) 201 map (table associative) 167, 169 Template 96 muliset (ensemble) 166 bibliothque et 99 multimap (table associative) dnition 95 167, 169 externe, C++0x 407 priority_queue (queue de priomtaprogrammation 106 rit) 165 mot-cl, C++0x 410 queue (queue) 164 spcialisation partielle 104 set (ensemble) 166 spcialisation totale 102 stack (pile) 163 syntaxe, C++0x 407 string (chane de caractres) 178 template, mot-cl 96 vector (tableau) 158

INDEX

438

terminate(), exception

terminate(), exception 125, 131 this, pointeur 67 thiscall, convention dappel 423 Thread, QT 311 thread_local, mot-cl, C++0x 386 throw, mot-cl 123, 125 traits classe 141 dnition 141 transform, algorithme standard 276 try, mot-cl 123, 125 tuple, C++0x 412, 413 Type, information dynamique 87 type_info, structure 87 typedef, mot-cl 8 typeid, mot-cl 87 typename, mot-cl 99, 101

unique, algorithme standard 278 unique_copy, algorithme standard 278 unsetf, fonction (ux) 202 upper_bound, algorithme standard 241 using, mot-cl 41 C++0x 389

V
variadic template, C++0x 410 variant, bibliothque BOOST 300 vector (tableau), STL 158 virtual, mot-cl, hritage 79 Virtuel, hritage 79 void, mot-cl 42 volatile, mot-cl 318

INDEX

U
UML, hritage multiple 78 unexpected(), exception 131 Unicode, C++0x 386 uninitialized_copy, algorithme standard 281 uninitialized_copy_n, algorithme standard 281 uninitialized_ll, algorithme standard 282 uninitialized_ll_n, algorithme standard 282 union, mot-cl 7, 31 C++0x 394 Union de types scurise, bibliothque BOOST 300

W
wchar_t, mot-cl 31 weak_ptr, classe, bibliothque BOOST 298 WHERE, requte SQL 347 while, mot-cl 12 write, fonction (ux) 200 wrope (chane de caractres), STL 178 wstring (chane de caractres), STL 178 wxDb, classe 350 wxDbCloseConnections, fonction 357 wxDbConnectInf, classe 349

XML, manipuler des donnes

439

wxDbFreeConnection, fonction 357 wxDbGetConnectInf, classe 350 wxDbTable Query, fonction 355 wxXmlDocument, classe 360 wxXmlNode, classe 364 wxXmlProperty, classe 363

X
XML charger un chier 360 DOM 359 manipuler des donnes 363

INDEX

LE GUIDE DE SURVIE

C++
LESSENTIEL DU CODE ET DES COMMANDES
Ce Guide de survie est le compagnon indispensable pour programmer en C++ et utiliser efcacement les bibliothques standard STL et BOOST, ainsi que QT, wxWidget et SQLite. Cet ouvrage prend en compte la future norme C++0x.

CONCIS ET MANIABLE
Facile transporter, facile utiliser nis les livres encombrants !

PRATIQUE ET FONCTIONNEL
Plus de 150 squences de code pour programmer rapidement et efcacement en C++. Vincent Gouvernelle est ingnieur en informatique, diplm de lESIL Marseille, et titulaire dun DEA en informatique. Il travaille actuellement chez Sescoi R&D, socit ditrice de logiciels spcialiss dans la CFAO (conception et fabrication assiste par ordinateur).

Niveau : Intermdiaire / Avanc Catgorie : Programmation Conguration : Multiplate-forme

Pearson Education France 47 bis, rue des Vinaigriers 75010 Paris Tl. : 01 72 74 90 00 Fax : 01 42 05 22 17 www.pearson.fr

ISBN : 978-2-7440-4011-5