Avant Propos Avant-propos Le Langage C++ I. Le langage C++ ...
... 221738088 corriges transmath 1ere s 2011 edition nathan - math transmath ...
s nathan exercice corrige, 1s transmath le livre - transmath 1s sommaire index ...
part of the document
HYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_1"Avant ProposHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_1" \l "LI"Avant-proposHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_2"Le Langage C++HYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_2" \l "LII"I. Le langage C++HYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_3"Chapitre 1HYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_3" \l "LIII"1. Première approche du C/C++HYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_3" \l "LIII-A"1.1. Les commentaires en C++HYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_3" \l "LIII-B"1.2. Les types prédéfinis du C/C++HYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_3" \l "LIII-C"1.3. Notation des valeursHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_3" \l "LIII-D"1.4. La définition des variablesHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_3" \l "LIII-E"1.5. Instructions et opérationsHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_3" \l "LIII-F"1.6. Les fonctionsHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_3" \l "LIII-F-1"1.6.1. Définition des fonctionsHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_3" \l "LIII-F-2"1.6.2. Appel des fonctionsHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_3" \l "LIII-F-3"1.6.3. Déclaration des fonctionsHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_3" \l "LIII-F-4"1.6.4. Surcharge des fonctionsHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_3" \l "LIII-F-5"1.6.5. Fonctions inlineHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_3" \l "LIII-F-6"1.6.6. Fonctions statiquesHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_3" \l "LIII-F-7"1.6.7. Fonctions prenant un nombre variable de paramètresHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_3" \l "LIII-G"1.7. La fonction mainHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_3" \l "LIII-H"1.8. Les fonctions d'entrée / sortie de baseHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_3" \l "LIII-H-1"1.8.1. Généralités sur les flux d'entrée / sortie en CHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_3" \l "LIII-H-2"1.8.2. La fonction printfHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_3" \l "LIII-H-3"1.8.3. La fonction scanfHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_3" \l "LIII-I"1.9. Exemple de programme completHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_4"Chapitre 2HYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_4" \l "LIV"2. Les structures de contrôleHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_4" \l "LIV-A"2.1. La structure conditionnelle ifHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_4" \l "LIV-B"2.2. La boucle forHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_4" \l "LIV-C"2.3. Le whileHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_4" \l "LIV-D"2.4. Le doHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_4" \l "LIV-E"2.5. Le branchement conditionnelHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_4" \l "LIV-F"2.6. Le sautHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_4" \l "LIV-G"2.7. Les commandes de rupture de séquenceHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_5"Chapitre 3HYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_5" \l "LV"3. Types avancés et classes de stockageHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_5" \l "LV-A"3.1. Structures de données et types complexesHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_5" \l "LV-A-1"3.1.1. Les structuresHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_5" \l "LV-A-2"3.1.2. Les unionsHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_5" \l "LV-A-3"3.1.3. Les énumérationsHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_5" \l "LV-A-4"3.1.4. Les champs de bitsHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_5" \l "LV-A-5"3.1.5. Initialisation des structures et des tableauxHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_5" \l "LV-A-6"3.1.6. Les alias de typesHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_5" \l "LV-A-7"3.1.7. TranstypagesHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_5" \l "LV-B"3.2. Les classes de stockageHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_6"Chapitre 4HYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_6" \l "LVI"4. Les pointeurs et référencesHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_6" \l "LVI-A"4.1. Notion d'adresseHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_6" \l "LVI-B"4.2. Notion de pointeurHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_6" \l "LVI-C"4.3. Déréférencement, indirectionHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_6" \l "LVI-D"4.4. Notion de référenceHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_6" \l "LVI-E"4.5. Lien entre les pointeurs et les référencesHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_6" \l "LVI-F"4.6. Passage de paramètres par variable ou par valeurHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_6" \l "LVI-F-1"4.6.1. Passage par valeurHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_6" \l "LVI-F-2"4.6.2. Passage par variableHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_6" \l "LVI-F-3"4.6.3. Avantages et inconvénients des deux méthodesHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_6" \l "LVI-F-4"4.6.4. Comment passer les paramètres par variable en C ?HYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_6" \l "LVI-F-5"4.6.5. Passage de paramètres par référenceHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_6" \l "LVI-G"4.7. Références et pointeurs constants et volatilesHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_6" \l "LVI-H"4.8. Arithmétique des pointeursHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_6" \l "LVI-I"4.9. Utilisation des pointeurs avec les tableauxHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_6" \l "LVI-I-1"4.9.1. Conversions des tableaux en pointeursHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_6" \l "LVI-I-2"4.9.2. Paramètres de fonction de type tableauHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_6" \l "LVI-J"4.10. Les chaînes de caractères : pointeurs et tableaux à la fois !HYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_6" \l "LVI-K"4.11. Allocation dynamique de mémoireHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_6" \l "LVI-K-1"4.11.1. Allocation dynamique de mémoire en CHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_6" \l "LVI-K-2"4.11.2. Allocation dynamique en C++HYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_6" \l "LVI-L"4.12. Pointeurs et références de fonctionsHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_6" \l "LVI-L-1"4.12.1. Pointeurs de fonctionsHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_6" \l "LVI-L-2"4.12.2. Références de fonctionsHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_6" \l "LVI-M"4.13. Paramètres de la fonction main - ligne de commandeHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_6" \l "LVI-N"4.14. DANGERHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_7"Chapitre 5HYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_7" \l "LVII"5. Le préprocesseur CHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_7" \l "LVII-A"5.1. DéfinitionHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_7" \l "LVII-B"5.2. Les commandes du préprocesseurHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_7" \l "LVII-B-1"5.2.1. Inclusion de fichierHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_7" \l "LVII-B-2"5.2.2. Constantes de compilation et remplacement de texteHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_7" \l "LVII-B-3"5.2.3. Compilation conditionnelleHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_7" \l "LVII-B-4"5.2.4. Autres commandesHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_7" \l "LVII-C"5.3. Les macrosHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_7" \l "LVII-D"5.4. Manipulation de chaînes de caractères dans les macrosHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_7" \l "LVII-E"5.5. Les trigraphesHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_8"Chapitre 6HYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_8" \l "LVIII"6. Modularité des programmes et génération des binairesHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_8" \l "LVIII-A"6.1. Pourquoi faire une programmation modulaire ?HYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_8" \l "LVIII-B"6.2. Les différentes phases du processus de génération des exécutablesHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_8" \l "LVIII-C"6.3. Compilation séparée en C/C++HYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_8" \l "LVIII-D"6.4. Syntaxe des outils de compilationHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_8" \l "LVIII-D-1"6.4.1. Syntaxe des compilateursHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_8" \l "LVIII-D-2"6.4.2. Syntaxe de makeHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_8" \l "LVIII-E"6.5. Problèmes syntaxiques relatifs à la compilation séparéeHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_8" \l "LVIII-E-1"6.5.1. Déclaration des typesHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_8" \l "LVIII-E-2"6.5.2. Déclaration des variablesHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_8" \l "LVIII-E-3"6.5.3. Déclaration des fonctionsHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_8" \l "LVIII-E-4"6.5.4. Directives d'édition de liensHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_9"Chapitre 7HYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_9" \l "LIX"7. Comment faire du code illisible ?HYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_10"Chapitre 8HYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_10" \l "LX"8. C++ : la couche objetHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_10" \l "LX-A"8.1. GénéralitésHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_10" \l "LX-B"8.2. Extension de la notion de type du CHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_10" \l "LX-C"8.3. Déclaration de classes en C++HYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_10" \l "LX-D"8.4. Encapsulation des donnéesHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_10" \l "LX-E"8.5. HéritageHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_10" \l "LX-F"8.6. Classes virtuellesHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_10" \l "LX-G"8.7. Fonctions et classes amiesHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_10" \l "LX-G-1"8.7.1. Fonctions amiesHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_10" \l "LX-G-2"8.7.2. Classes amiesHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_10" \l "LX-H"8.8. Constructeurs et destructeursHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_10" \l "LX-H-1"8.8.1. Définition des constructeurs et des destructeursHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_10" \l "LX-H-2"8.8.2. Constructeurs de copieHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_10" \l "LX-H-3"8.8.3. Utilisation des constructeurs dans les transtypagesHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_10" \l "LX-I"8.9. Pointeur thisHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_10" \l "LX-J"8.10. Données et fonctions membres statiquesHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_10" \l "LX-J-1"8.10.1. Données membres statiquesHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_10" \l "LX-J-2"8.10.2. Fonctions membres statiquesHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_10" \l "LX-K"8.11. Surcharge des opérateursHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_10" \l "LX-K-1"8.11.1. Surcharge des opérateurs internesHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_10" \l "LX-K-2"8.11.2. Surcharge des opérateurs externesHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_10" \l "LX-K-3"8.11.3. Opérateurs d'affectationHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_10" \l "LX-K-4"8.11.4. Opérateurs de transtypageHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_10" \l "LX-K-5"8.11.5. Opérateurs de comparaisonHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_10" \l "LX-K-6"8.11.6. Opérateurs d'incrémentation et de décrémentationHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_10" \l "LX-K-7"8.11.7. Opérateur fonctionnelHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_10" \l "LX-K-8"8.11.8. Opérateurs d'indirection et de déréférencementHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_10" \l "LX-K-9"8.11.9. Opérateurs d'allocation dynamique de mémoireHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_10" \l "LX-L"8.12. Des entrées - sorties simplifiéesHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_10" \l "LX-M"8.13. Méthodes virtuellesHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_10" \l "LX-N"8.14. DérivationHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_10" \l "LX-O"8.15. Méthodes virtuelles pures - Classes abstraitesHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_10" \l "LX-P"8.16. Pointeurs sur les membres d'une classeHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_11"Chapitre 9HYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_11" \l "LXI"9. Les exceptions en C++HYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_11" \l "LXI-A"9.1. Lancement et récupération d'une exceptionHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_11" \l "LXI-B"9.2. Remontée des exceptionsHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_11" \l "LXI-C"9.3. Liste des exceptions autorisées pour une fonctionHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_11" \l "LXI-D"9.4. Hiérarchie des exceptionsHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_11" \l "LXI-E"9.5. Exceptions dans les constructeursHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_12"Chapitre 10HYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_12" \l "LXII"10. Identification dynamique des typesHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_12" \l "LXII-A"10.1. Identification dynamique des typesHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_12" \l "LXII-A-1"10.1.1. L'opérateur typeidHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_12" \l "LXII-A-2"10.1.2. La classe type_infoHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_12" \l "LXII-B"10.2. Transtypages C++HYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_12" \l "LXII-B-1"10.2.1. Transtypage dynamiqueHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_12" \l "LXII-B-2"10.2.2. Transtypage statiqueHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_12" \l "LXII-B-3"10.2.3. Transtypage de constance et de volatilitéHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_12" \l "LXII-B-4"10.2.4. Réinterprétation des donnéesHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_13"Chapitre 11HYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_13" \l "LXIII"11. Les espaces de nommageHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_13" \l "LXIII-A"11.1. Définition des espaces de nommageHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_13" \l "LXIII-A-1"11.1.1. Espaces de nommage nomméesHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_13" \l "LXIII-A-2"11.1.2. Espaces de nommage anonymesHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_13" \l "LXIII-A-3"11.1.3. Alias d'espaces de nommageHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_13" \l "LXIII-B"11.2. Déclaration usingHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_13" \l "LXIII-B-1"11.2.1. Syntaxe des déclarations usingHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_13" \l "LXIII-B-2"11.2.2. Utilisation des déclarations using dans les classesHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_13" \l "LXIII-C"11.3. Directive usingHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_14"Chapitre 12HYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_14" \l "LXIV"12. Les templateHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_14" \l "LXIV-A"12.1. GénéralitésHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_14" \l "LXIV-B"12.2. Déclaration des paramètres templateHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_14" \l "LXIV-B-1"12.2.1. Déclaration des types templateHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_14" \l "LXIV-B-2"12.2.2. Déclaration des constantes templateHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_14" \l "LXIV-C"12.3. Fonctions et classes templateHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_14" \l "LXIV-C-1"12.3.1. Fonctions templateHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_14" \l "LXIV-C-2"12.3.2. Les classes templateHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_14" \l "LXIV-C-3"12.3.3. Fonctions membres templateHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_14" \l "LXIV-D"12.4. Instanciation des templateHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_14" \l "LXIV-D-1"12.4.1. Instanciation impliciteHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_14" \l "LXIV-D-2"12.4.2. Instanciation expliciteHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_14" \l "LXIV-D-3"12.4.3. Problèmes soulevés par l'instanciation des templateHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_14" \l "LXIV-E"12.5. Spécialisation des templateHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_14" \l "LXIV-E-1"12.5.1. Spécialisation totaleHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_14" \l "LXIV-E-2"12.5.2. Spécialisation partielleHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_14" \l "LXIV-E-3"12.5.3. Spécialisation d'une méthode d'une classe templateHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_14" \l "LXIV-F"12.6. Mot-clé typenameHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_14" \l "LXIV-G"12.7. Fonctions exportéesHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_15"La bibliothèque standard C++HYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_15" \l "LXV"II. La bibliothèque standard C++HYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_16"Chapitre 13HYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_16" \l "LXVI"13. Services et notions de base de la bibliothèque standardHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_16" \l "LXVI-A"13.1. Encapsulation de la bibliothèque C standardHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_16" \l "LXVI-B"13.2. Définition des exceptions standardsHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_16" \l "LXVI-C"13.3. Abstraction des types de données : les traitsHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_16" \l "LXVI-D"13.4. Abstraction des pointeurs : les itérateursHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_16" \l "LXVI-D-1"13.4.1. Notions de base et définitionHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_16" \l "LXVI-D-2"13.4.2. Classification des itérateursHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_16" \l "LXVI-D-3"13.4.3. Itérateurs adaptateursHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_16" \l "LXVI-D-3-a"13.4.3.1. Adaptateurs pour les flux d'entrée / sortie standardsHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_16" \l "LXVI-D-3-b"13.4.3.2. Adaptateurs pour l'insertion d'éléments dans les conteneursHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_16" \l "LXVI-D-3-c"13.4.3.3. Itérateur inverse pour les itérateurs bidirectionnelsHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_16" \l "LXVI-E"13.5. Abstraction des fonctions : les foncteursHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_16" \l "LXVI-E-1"13.5.1. Foncteurs prédéfinisHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_16" \l "LXVI-E-2"13.5.2. Prédicats et foncteurs d'opérateurs logiquesHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_16" \l "LXVI-E-3"13.5.3. Foncteurs réducteursHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_16" \l "LXVI-F"13.6. Gestion personnalisée de la mémoire : les allocateursHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_16" \l "LXVI-G"13.7. Notion de complexité algorithmiqueHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_16" \l "LXVI-G-1"XVI-G-1. 13.7.1. GénéralitésHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_16" \l "LXVI-G-2"13.7.2. Notions mathématiques de base et définitionHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_16" \l "LXVI-G-3"13.7.3. Interprétation pratique de la complexitéHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_17"Chapitre 14HYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_17" \l "LXVII"14. Les types complémentairesHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_17" \l "LXVII-A"14.1. Les chaînes de caractèresHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_17" \l "LXVII-A-1"14.1.1. Construction et initialisation d'une chaîneHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_17" \l "LXVII-A-2"14.1.2. Accès aux propriétés d'une chaîneHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_17" \l "LXVII-A-3"14.1.3. Modification de la taille des chaînesHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_17" \l "LXVII-A-4"14.1.4. Accès aux données de la chaîne de caractèresHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_17" \l "LXVII-A-5"14.1.5. Opérations sur les chaînesHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_17" \l "LXVII-A-5-a"14.1.5.1. Affectation et concaténation de chaînes de caractèresHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_17" \l "LXVII-A-5-b"14.1.5.2. Extraction de données d'une chaîne de caractèresHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_17" \l "LXVII-A-5-c"14.1.5.3. Insertion et suppression de caractères dans une chaîneHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_17" \l "LXVII-A-5-d"14.1.5.4. Remplacements de caractères d'une chaîneHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_17" \l "LXVII-A-6"14.1.6. Comparaison de chaînes de caractèresHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_17" \l "LXVII-A-7"14.1.7. Recherche dans les chaînesHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_17" \l "LXVII-A-8"14.1.8. Fonctions d'entrée / sortie des chaînes de caractèresHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_17" \l "LXVII-B"14.2. Les types utilitairesHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_17" \l "LXVII-B-1"14.2.1. Les pointeurs autoHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_17" \l "LXVII-B-2"14.2.2. Les pairesHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_17" \l "LXVII-C"14.3. Les types numériquesHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_17" \l "LXVII-C-1"14.3.1. Les complexesHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_17" \l "LXVII-C-1-a"14.3.1.1. Définition et principales propriétés des nombres complexesHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_17" \l "LXVII-C-1-b"14.3.1.2. La classe complexHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_17" \l "LXVII-C-2"14.3.2. Les tableaux de valeursHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_17" \l "LXVII-C-2-a"14.3.2.1. Fonctionnalités de base des valarrayHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_17" \l "LXVII-C-2-b"14.3.2.2. Sélection multiple des éléments d'un valarrayHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_17" \l "LXVII-C-2-b-i"14.3.2.2.1. Sélection par un masqueHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_17" \l "LXVII-C-2-b-ii"14.3.2.2.2. Sélection par indexation expliciteHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_17" \l "LXVII-C-2-b-iii"14.3.2.2.3. Sélection par indexation impliciteHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_17" \l "LXVII-C-2-b-iv"14.3.2.2.4. Opérations réalisables sur les sélections multiplesHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_17" \l "LXVII-C-3"14.3.3. Les champs de bitsHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_18"Chapitre 15HYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_18" \l "LXVIII"15. Les flux d'entrée / sortieHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_18" \l "LXVIII-A"15.1. Notions de base et présentation généraleHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_18" \l "LXVIII-B"15.2. Les tamponsHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_18" \l "LXVIII-B-1"15.2.1. Généralités sur les tamponsHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_18" \l "LXVIII-B-2"15.2.2. La classe basic_streambufHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_18" \l "LXVIII-B-3"15.2.3. Les classes de tampons basic_stringbuf et basic_filebufHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_18" \l "LXVIII-B-3-a"15.2.3.1. La classe basic_stringbufHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_18" \l "LXVIII-B-3-b"15.2.3.2. La classe basic_filebufHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_18" \l "LXVIII-C"15.3. Les classes de base des flux : ios_base et basic_iosHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_18" \l "LXVIII-C-1"15.3.1. La classe ios_baseHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_18" \l "LXVIII-C-2"15.3.2. La classe basic_iosHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_18" \l "LXVIII-D"15.4. Les flux d'entrée / sortieHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_18" \l "LXVIII-D-1"15.4.1. La classe de base basic_ostreamHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_18" \l "LXVIII-D-2"15.4.2. La classe de base basic_istreamHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_18" \l "LXVIII-D-3"15.4.3. La classe basic_iostreamHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_18" \l "LXVIII-E"15.5. Les flux d'entrée / sortie sur chaînes de caractèresHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_18" \l "LXVIII-F"15.6. Les flux d'entrée / sortie sur fichiersHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_19"Chapitre 16HYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_19" \l "LXIX"16. Les localesHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_19" \l "LXIX-A"16.1. Notions de base et principe de fonctionnement des facettesHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_19" \l "LXIX-B"16.2. Les facettes standardsHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_19" \l "LXIX-B-1"16.2.1. GénéralitésHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_19" \l "LXIX-B-2"16.2.2. Les facettes de manipulation des caractèresHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_19" \l "LXIX-B-2-a"16.2.2.1. La facette ctypeHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_19" \l "LXIX-B-2-b"16.2.2.2. La facette codecvtHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_19" \l "LXIX-B-3"16.2.3. Les facettes de comparaison de chaînesHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_19" \l "LXIX-B-4"16.2.4. Les facettes de gestion des nombresHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_19" \l "LXIX-B-4-a"16.2.4.1. La facette num_punctHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_19" \l "LXIX-B-4-b"16.2.4.2. La facette d'écriture des nombresHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_19" \l "LXIX-B-4-c"16.2.4.3. La facette de lecture des nombresHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_19" \l "LXIX-B-5"16.2.5. Les facettes de gestion des monnaiesHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_19" \l "LXIX-B-5-a"16.2.5.1. La facette money_punctHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_19" \l "LXIX-B-5-b"16.2.5.2. Les facettes de lecture et d'écriture des montantsHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_19" \l "LXIX-B-6"16.2.6. Les facettes de gestion du tempsHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_19" \l "LXIX-B-6-a"16.2.6.1. La facette d'écriture des datesHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_19" \l "LXIX-B-6-b"16.2.6.2. La facette de lecture des datesHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_19" \l "LXIX-B-7"16.2.7. Les facettes de gestion des messagesHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_19" \l "LXIX-C"XIX-C. 16.3. Personnalisation des mécanismes de localisationHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_19" \l "LXIX-C-1"16.3.1. Création et intégration d'une nouvelle facetteHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_19" \l "LXIX-C-2"16.3.2. Remplacement d'une facette existanteHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_20"Chapitre 17HYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_20" \l "LXX"17. Les conteneursHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_20" \l "LXX-A"17.1. Fonctionnalités générales des conteneursHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_20" \l "LXX-A-1"17.1.1. Définition des itérateursHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_20" \l "LXX-A-2"17.1.2. Définition des types de données relatifs aux objets contenusHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_20" \l "LXX-A-3"17.1.3. Spécification de l'allocateur mémoire à utiliserHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_20" \l "LXX-A-4"17.1.4. Opérateurs de comparaison des conteneursHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_20" \l "LXX-A-5"17.1.5. Méthodes d'intérêt généralHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_20" \l "LXX-B"17.2. Les séquencesHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_20" \l "LXX-B-1"17.2.1. Fonctionnalités communesHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_20" \l "LXX-B-1-a"17.2.1.1. Construction et initialisationHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_20" \l "LXX-B-1-b"17.2.1.2. Ajout et suppression d'élémentsHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_20" \l "LXX-B-2"17.2.2. Les différents types de séquencesHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_20" \l "LXX-B-2-a"17.2.2.1. Les listesHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_20" \l "LXX-B-2-b"17.2.2.2. Les vecteursHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_20" \l "LXX-B-2-c"17.2.2.3. Les dequesHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_20" \l "LXX-B-2-d"17.2.2.4. Les adaptateurs de séquencesHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_20" \l "LXX-B-2-d-i"17.2.2.4.1. Les pilesHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_20" \l "LXX-B-2-d-ii"17.2.2.4.2. Les filesHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_20" \l "LXX-B-2-d-iii"17.2.2.4.3. Les files de prioritésHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_20" \l "LXX-C"17.3. Les conteneurs associatifsHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_20" \l "LXX-C-1"17.3.1. Généralités et propriétés de base des clefsHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_20" \l "LXX-C-2"17.3.2. Construction et initialisationHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_20" \l "LXX-C-3"17.3.3. Ajout et suppression d'élémentsHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_20" \l "LXX-C-4"17.3.4. Fonctions de rechercheHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_21"Chapitre 18HYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_21" \l "LXXI"18. Les algorithmesHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_21" \l "LXXI-A"18.1. Opérations générales de manipulation des donnéesHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_21" \l "LXXI-A-1"18.1.1. Opérations d'initialisation et de remplissageHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_21" \l "LXXI-A-2"18.1.2. Opérations de copieHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_21" \l "LXXI-A-3"18.1.3. Opérations d'échange d'élémentsHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_21" \l "LXXI-A-4"18.1.4. Opérations de suppression d'élémentsHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_21" \l "LXXI-A-5"18.1.5. Opérations de remplacementHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_21" \l "LXXI-A-6"XXI-A-6. 18.1.6. Réorganisation de séquencesHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_21" \l "LXXI-A-6-a"18.1.6.1. Opérations de rotation et de permutationHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_21" \l "LXXI-A-6-b"18.1.6.2. Opérations d'inversionHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_21" \l "LXXI-A-6-c"18.1.6.3. Opérations de mélangeHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_21" \l "LXXI-A-7"18.1.7. Algorithmes d'itération et de transformationHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_21" \l "LXXI-B"18.2. Opérations de rechercheHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_21" \l "LXXI-B-1"18.2.1. Opération de recherche d'élémentsHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_21" \l "LXXI-B-2"18.2.2. Opérations de recherche de motifsHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_21" \l "LXXI-C"18.3. Opérations d'ordonnancementHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_21" \l "LXXI-C-1"18.3.1. Opérations de gestion des tasHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_21" \l "LXXI-C-2"18.3.2. Opérations de triHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_21" \l "LXXI-C-3"18.3.3. Opérations de recherche binaireHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_21" \l "LXXI-D"18.4. Opérations de comparaisonHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_21" \l "LXXI-E"18.5. Opérations ensemblistesHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_21" \l "LXXI-E-1"18.5.1. Opérations d'inclusionHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_21" \l "LXXI-E-2"18.5.2. Opérations d'intersectionHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_21" \l "LXXI-E-3"18.5.3. Opérations d'union et de fusionHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_21" \l "LXXI-E-4"18.5.4. Opérations de différenceHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_21" \l "LXXI-E-5"18.5.5. Opérations de partitionnementHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_22"Chapitre 19HYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_22" \l "LXXII"19. ConclusionHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_23"Annexe AHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_23" \l "LXXIII"A. Priorités des opérateursHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_24"Annexe BHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_24" \l "LXXIV"B. Draft PapersHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_25"Appendix CHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_25" \l "LXXV"C. GNU Free Documentation LicenseHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_26"Annexe DHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_26" \l "LXXVI"D. Licence de documentation libre GNUHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_27"BibliographieHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_27" \l "LXXVII"BIBLIOGRAPHIEHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_28"Annexe EHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_28" \l "LXXVIII"Les ListesHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_28" \l "LXXVIII-A"Liste des tableauxHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_28" \l "LXXVIII-B"Liste des illustrationsHYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_28" \l "LXXVIII-C"Liste des Examples
HYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_1" \l "LI"Avant-propos
Avant-propos
Ce livre est un cours de C et de C++. Il s'adresse aux personnes qui ont déjà quelques notions de programmation dans un langage quelconque. Les connaissances requises ne sont pas très élevées cependant : il n'est pas nécessaire d'avoir fait de grands programmes pour lire ce document. Il suffit d'avoir vu ce qu'est un programme et compris les grands principes de la programmation.
Ce livre est structuré en deux grandes parties, traitant chacune un des aspects du C++. La première partie, contenant les chapitres 1 à 12, traite du langage C++ lui-même, de sa syntaxe et de ses principales fonctionnalités. La deuxième partie quant à elle se concentre sur la bibliothèque standard C++, qui fournit un ensemble de fonctionnalités cohérentes et réutilisables par tous les programmeurs. La bibliothèque standard C++ a également l'avantage d'utiliser les constructions les plus avancées du langage, et illustre donc parfaitement les notions qui auront été abordées dans la première partie. La description de la bibliothèque standard s'étend du chapitre 13 au chapitre 18.
Si la bibliothèque standard C++ est décrite en détail, il n'en va pas de même pour les fonctions de la bibliothèque C. Vous ne trouverez donc pas dans ce livre la description des fonctions classiques du C, ni celle des fonctions les plus courantes de la norme POSIX. En effet, bien que présentes sur quasiment tous les systèmes d'exploitation, ces fonctions sont spécifiques à la norme POSIX et n'appartiennent pas au langage en soi. Seules les fonctions incontournables de la bibliothèque C seront donc présentées ici. Si vous désirez plus de renseignements, reportez-vous aux spécifications des appels systèmes POSIX de HYPERLINK "http://www.unix-systems.org/single_unix_specification/"l'OpenGroup, ou à la documentation des environnements de développement et à l'aide des kits de développement des systèmes d'exploitation (SDK).
Ce livre a pour but de présenter le langage C++ tel qu'il est décrit par la norme ISO 14882 du langage C++. Cependant, bien que cette norme ait été publiée en 1999, le texte officiel n'est pas librement disponible. Comme je ne veux pas cautionner le fait qu'un texte de norme international ne soit pas accessible à tous, je me suis rabattu sur le document du projet de normalisation du langage, datant du 2 décembre 1996 et intitulé « HYPERLINK "http://casteyde.christian.free.fr/cpp/cours/drafts/index.html"Working Paper for Draft Proposed International Standard for Information Systems -- Programming Language C++ ».
Notez que les compilateurs qui respectent cette norme se comptent encore sur les doigts d'une main, et que les informations et exemples donnés ici peuvent ne pas s'avérer exacts avec certains produits. En particulier, certains exemples ne compileront pas avec les compilateurs les plus mauvais. Notez également que certaines constructions du langage n'ont pas la même signification avec tous les compilateurs, parce qu'elles ont été implémentées avant que la norme ne les spécifie complètement. Ces différences peuvent conduire à du code non portable, et ont été signalées à chaque fois dans une note. Le fait que les exemples de ce livre ne fonctionnent pas avec de tels compilateurs ne peut donc pas être considéré comme une erreur, mais plutôt comme une non-conformité des outils utilisés, qui sera sans doute levée dans les versions ultérieures de ces produits.
Après avoir tenté de faire une présentation rigoureuse du sujet, j'ai décidé d'arranger le plan de ce livre dans un ordre plus pédagogique. Il est à mon avis impossible de parler d'un sujet un tant soit peu vaste dans un ordre purement mathématique, c'est-à-dire un ordre où les notions sont introduites une à une, à partir des notions déjà connues (chaque fonction, opérateur, etc. n'apparaît pas avant sa définition). Un tel plan nécessiterait de couper le texte en morceaux qui ne sont plus thématiques. J'ai donc pris la décision de présenter les choses par ordre logique, et non par ordre de nécessité syntaxique.
Les conséquences de ce choix sont les suivantes :
il faut admettre certaines choses, quitte à les comprendre plus tard ;
il faut lire deux fois ce livre. Lors de la première lecture, on voit l'essentiel, et lors de la deuxième lecture, on comprend les détails (de toutes manières, je félicite celui qui comprend toutes les subtilités du C++ du premier coup).
Enfin, ce livre est un document vivant. Il est librement téléchargeable sur HYPERLINK "http://casteyde.christian.free.fr/"mon site web, où la dernière version peut être récupérée. Toute remarque est donc la bienvenue. Je tâcherai de corriger les erreurs que l'on me signalera dans la mesure du possible, et d'apporter les modifications nécessaires si un point est obscur. Si vous prenez le temps de m'envoyer les remarques et les erreurs que vous avez pu détecter, je vous saurais gré de vérifier au préalable qu'elles sont toujours d'actualité dans la dernière version de ce document. À cette fin, un historique des révisions a été inclus en première page pour permettre l'identification des différentes éditions de ce document.
HYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_2" \l "LII"I. Le langage C++
I. Le langage C++
Le C++ est l'un des langages de programmation les plus utilisés actuellement. Il est à la fois facile à utiliser et très efficace. Il souffre cependant de la réputation d'être compliqué et illisible. Cette réputation est en partie justifiée. La complexité du langage est inévitable lorsqu'on cherche à avoir beaucoup de fonctionnalités. En revanche, en ce qui concerne la lisibilité des programmes, tout dépend de la bonne volonté du programmeur.
Les caractéristiques du C++ en font un langage idéal pour certains types de projets. Il est incontournable dans la réalisation des grands programmes. Les optimisations des compilateurs actuels en font également un langage de prédilection pour ceux qui recherchent les performances. Enfin, ce langage est, avec le C, idéal pour ceux qui doivent assurer la portabilité de leurs programmes au niveau des fichiers sources (pas des exécutables).
Les principaux avantages du C++ sont les suivants :
grand nombre de fonctionnalités ;
performances du C ;
facilité d'utilisation des langages objets ;
portabilité des fichiers sources ;
facilité de conversion des programmes C en C++, et, en particulier, possibilité d'utiliser toutes les fonctionnalités du langage C ;
contrôle d'erreurs accru.
On dispose donc de quasiment tout : puissance, fonctionnalité, portabilité et sûreté. La richesse du contrôle d'erreurs du langage, basé sur un typage très fort, permet de signaler un grand nombre d'erreurs à la compilation. Toutes ces erreurs sont autant d'erreurs que le programme ne fait pas à l'exécution. Le C++ peut donc être considéré comme un « super C ». Le revers de la médaille est que les programmes C ne se compilent pas directement en C++ : il est courant que de simples avertissements en C soient des erreurs blocantes en C++. Quelques adaptations sont donc souvent nécessaires, cependant, celles-ci sont minimes, puisque la syntaxe du C++ est basée sur celle du C. On remarquera que tous les programmes C peuvent être corrigés pour compiler à la fois en C et en C++.
Tout le début de cette partie (chapitres 1 à 8) traite des fonctionnalités communes au C et au C++, en insistant bien sur les différences entre ces deux langages. Ces chapitres présentent essentiellement la syntaxe des constructions de base du C et du C++. Le début de cette partie peut donc également être considéré comme un cours allégé sur le langage C. Cependant, les constructions syntaxiques utilisées sont écrites de telle sorte qu'elles sont compilables en C++. Cela signifie qu'elles n'utilisent pas certaines fonctionnalités douteuses du C. Ceux qui désirent utiliser la première partie comme un cours de C doivent donc savoir qu'il s'agit d'une version épurée de ce langage. En particulier, les appels de fonctions non déclarées ou les appels de fonctions avec trop de paramètres ne sont pas considérés comme des pratiques de programmation valables.
Les chapitres suivants (chapitres 8 à 12) ne traitent que du C++. Le Chapitre 8 traite de la programmation orientée objet et de toutes les extensions qui ont été apportées au langage C pour gérer les objets. Le Chapitre 9 présente le mécanisme des exceptions du langage, qui permet de gérer les erreurs plus facilement. L'identification dynamique des types sera décrite dans le Chapitre 10. Le Chapitre 11 présente la notion d'espace de nommage, que l'on utilise afin d'éviter les conflits de noms entre les différentes parties d'un grand projet. Enfin, le Chapitre 12 décrit le mécanisme des template, qui permet d'écrire des portions de code paramétrées par des types de données ou par des valeurs constantes. Ces dernières notions sont utilisées intensivement dans la bibliothèque standard C++, aussi la lecture complète de la première partie est-elle indispensable avant de s'attaquer à la deuxième.
Dans toute cette première partie, la syntaxe sera donnée, sauf exception, avec la convention suivante : ce qui est entre crochets ('[' et ']') est facultatif. De plus, quand plusieurs éléments de syntaxe sont séparés par une barre verticale ('|'), l'un de ces éléments, et un seulement, doit être présent (c'est un « ou » exclusif). Enfin, les points de suspension désigneront une itération éventuelle du motif précédent.
Par exemple, si la syntaxe d'une commande est la suivante :
[fac|rty|sss] zer[(kfl[,kfl[...]])];les combinaisons suivantes seront syntaxiquement correctes :
zer;
fac zer;
rty zer;
zer(kfl);
sss zer(kfl,kfl,kfl,kfl);mais la combinaison suivante sera incorrecte :
fac sss zer()pour les raisons suivantes :
fac et sss sont mutuellement exclusifs, bien que facultatifs tous les deux ;
au moins un kfl est nécessaire si les parenthèses sont mises ;
il manque le point virgule final.
Rassurez-vous, il n'y aura pratiquement jamais de syntaxe aussi compliquée. Je suis sincèrement désolé de la complexité de cet exemple.
HYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_3" \l "LIII"1. Première approche du C/C++ HYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_3" \l "LIII-A"1.1. Les commentaires en C++ HYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_3" \l "LIII-B"1.2. Les types prédéfinis du C/C++ HYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_3" \l "LIII-C"1.3. Notation des valeurs HYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_3" \l "LIII-D"1.4. La définition des variables HYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_3" \l "LIII-E"1.5. Instructions et opérations HYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_3" \l "LIII-F"1.6. Les fonctions HYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_3" \l "LIII-F-1"1.6.1. Définition des fonctions HYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_3" \l "LIII-F-2"1.6.2. Appel des fonctions HYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_3" \l "LIII-F-3"1.6.3. Déclaration des fonctions HYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_3" \l "LIII-F-4"1.6.4. Surcharge des fonctions HYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_3" \l "LIII-F-5"1.6.5. Fonctions inline HYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_3" \l "LIII-F-6"1.6.6. Fonctions statiques HYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_3" \l "LIII-F-7"1.6.7. Fonctions prenant un nombre variable de paramètres HYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_3" \l "LIII-G"1.7. La fonction main HYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_3" \l "LIII-H"1.8. Les fonctions d'entrée / sortie de base HYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_3" \l "LIII-H-1"1.8.1. Généralités sur les flux d'entrée / sortie en C HYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_3" \l "LIII-H-2"1.8.2. La fonction printf HYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_3" \l "LIII-H-3"1.8.3. La fonction scanf HYPERLINK "http://cpp.developpez.com/cours/cpp/?page=page_3" \l "LIII-I"1.9. Exemple de programme complet
1. Première approche du C/C++
Le C/C++ est un langage procédural, du même type que le Pascal par exemple. Cela signifie que les instructions sont exécutées linéairement et regroupées en blocs : les fonctions et les procédures (les procédures n'existent pas en C/C++, ce sont des fonctions qui ne retournent pas de valeur).
Tout programme a pour but d'effectuer des opérations sur des données. La structure fondamentale est donc la suivante :
ENTRÉE DES DONNÉES
(clavier, souris, fichier, autres périphériques)
|
TRAITEMENT DES DONNÉES
|
SORTIE DES DONNÉES
(écran, imprimante, fichier, autres périphériques)
Ces diverses étapes peuvent être dispersées dans le programme. Par exemple, les entrées peuvent se trouver dans le programme même (l'utilisateur n'a dans ce cas pas besoin de les saisir). Pour la plupart des programmes, les données en entrée proviennent du flux d'entrée standard, et les données émises en sortie sont dirigées vers le flux de sortie standard. Toutefois, le processus d'entrée des données peut être répété autant de fois que nécessaire pendant l'exécution d'un programme, et les données traitées au fur et à mesure qu'elles apparaissent. Par exemple, pour les programmes graphiques, les données sont reçues de la part du système sous forme de messages caractérisant les événements générés par l'utilisateur ou par le système lui-même (déplacement de souris, fermeture d'une fenêtre, appui sur une touche, etc.). Le traitement des programmes graphiques est donc une boucle infinie (que l'on appelle la boucle des messages), qui permet de récupérer les messages et de prendre les actions en conséquence. Dans ce cas, la sortie des données correspond au comportement que le programme adopte en réponse à ces messages. Cela peut être tout simplement d'afficher les données saisies, ou, plus généralement, d'appliquer une commande aux données en cours de manipulation.
Les données manipulées sont stockées dans des variables, c'est-à-dire des zones de la mémoire. Comme leur nom l'indique, les variables peuvent être modifiées (par le traitement des données). Des opérations peuvent donc être effectuées sur les variables, mais pas n'importe lesquelles. Par exemple, on ne peut pas ajouter des pommes à des bananes, sauf à définir cette opération bien précisément. Les opérations dépendent donc de la nature des variables. Afin de réduire les risques d'erreurs de programmation, les langages comme le C/C++ donnent un type à chaque variable (par exemple : pomme et banane). Lors de la compilation (phase de traduction du texte source du programme en exécutable), ces types sont utilisés pour vérifier si les opérations effectuées sont autorisées. Le programmeur peut évidemment définir ses propres types.
Le langage fournit des types de base et des opérations prédéfinies sur ces types. Les opérations qui peuvent être faites sont soit l'application d'un opérateur, soit l'application d'une fonction sur les variables. Logiquement parlant, il n'y a pas de différence. Seule la syntaxe change :
a=2+3
est donc strictement équivalent à :
a=ajoute(2,3)
Évidemment, des fonctions utilisateur peuvent être définies. Les opérateurs ne peuvent être que surchargés : il est impossible d'en définir de nouveaux (de plus, la surcharge des opérateurs n'est faisable qu'en C++). La notion de surcharge de fonction sera décrite en détail ci-dessous, dans la Section 1.6.4.
Cette première partie est donc consacrée à la définition des types, la déclaration des variables, la construction et l'appel de fonctions, et aux entrées / sorties de base (flux d'entrée / sortie standards).
1.1. Les commentaires en C++
Les commentaires sont nécessaires et très simples à faire. Tout programme doit être commenté. Attention cependant, trop de commentaires tue le commentaire, parce que les choses importantes sont noyées dans les banalités.
Il existe deux types de commentaires en C++ : les commentaires de type C et les commentaires de fin de ligne (qui ne sont disponibles qu'en C++).
Les commentaires C commencent avec la séquence barre oblique - étoile. Les commentaires se terminent avec la séquence inverse : une étoile suivie d'une barre oblique.
Exemple 1-1. Commentaire C
/* Ceci est un commentaire C */Ces commentaires peuvent s'étendre sur plusieurs lignes.
En revanche, les commentaires de fin de lignes s'arrêtent à la fin de la ligne courante, et pas avant. Ils permettent de commenter plus facilement les actions effectuées sur la ligne courante, avant le commentaire. Les commentaires de fin de ligne commencent par la séquence constituée de deux barres obliques (ils n'ont pas de séquence de terminaison, puisqu'ils ne se terminent qu'à la fin de la ligne courante). Par exemple :
Exemple 1-2. Commentaire C++
action quelconque // Ceci est un commentaire C++
action suivante
1.2. Les types prédéfinis du C/C++
Le C, et encore plus le C++, est un langage typé. Cela signifie que chaque entité manipulée dans les programmes doit disposer d'un type de donnée grâce auquel le compilateur pourra vérifier la validité des opérations qu'on lui appliquera. La prise en compte du type des données peut apparaître comme une contrainte pour le programmeur, mais en réalité il s'agit surtout d'une aide à la détection des erreurs.
Il existe plusieurs types prédéfinis. Ce sont :
le type vide : void. Ce type est utilisé pour spécifier le fait qu'il n'y a pas de type. Cela a une utilité pour faire des procédures (fonctions ne renvoyant rien) et les pointeurs sur des données non typées (voir plus loin) ;
les booléens : bool, qui peuvent prendre les valeurs true et false (en C++ uniquement, ils n'existent pas en C) ;
les caractères : char ;
les caractères longs : wchar_t (ce n'est un type de base que pour le langage C++, mais il est également défini dans la bibliothèque standard C et est donc utilisable malgré tout en C) ;
les entiers : int ;
les réels : float ;
les réels en double précision : double ;
les tableaux à une dimension, dont les indices sont spécifiés par des crochets ('[' et ']'). Pour les tableaux de dimension supérieure ou égale à 2, on utilisera des tableaux de tableaux ;
les structures, unions et énumérations (voir plus loin).
Les types entiers (int) peuvent être caractérisés d'un des mots clés long ou short. Ces mots clés permettent de modifier la taille du type, c'est-à-dire la plage de valeurs qu'ils peuvent couvrir. De même, les réels en double précision peuvent être qualifiés du mot clé long, ce qui augmente leur plage de valeurs. On ne peut pas utiliser le mot clé short avec les double.
On dispose donc de types additionnels :
les entiers longs : long int, ou long (int est facultatif) ;
les entiers courts : short int, ou short ;
les réels en quadruple précision : long double.
Note : Attention ! Il n'y a pas de type de base permettant de manipuler les chaînes de caractères. En C/C++, les chaînes de caractères sont en réalité des tableaux de caractères. Vous trouverez plus loin pour de plus amples informations sur les chaînes de caractères et les tableaux. La taille des types n'est spécifiée dans aucune norme. La seule chose qui est indiquée dans la norme C++, c'est que le plus petit type est le type char. Les tailles des autres types sont donc des multiples de celle du type char. De plus, les inégalités suivantes sont toujours vérifiées :
char = short int = int = long int
float = double = long doubleoù l'opérateur « = » signifie ici « a une plage de valeur plus petite ou égale que ». Cela dit, les tailles des types sont généralement les mêmes pour tous les environnements de développement. Le type char est généralement codé sur un octet (8 bits), le type short int sur deux octets et le type long int sur quatre octets. Le type int est celui qui permet de stocker les entiers au format natif du processeur utilisé. Il est donc codé sur deux octets sur les machines 16 bits et sur quatre octets sur les machines 32 bits. Enfin, la taille des caractères de type wchar_t n'est pas spécifiée et dépend de l'environnement de développement utilisé. Ils sont généralement codés sur deux ou sur quatre octets suivant la représentation utilisée pour les caractères larges.
Note : Remarquez que, d'après ce qui précède, le type int devrait être codé sur 64 bits sur les machines 64 bits. Le type long int devant lui être supérieur, il doit également être codé sur 64 bits ou plus. Le type short int peut alors être sur 16 ou sur 32 bits. Il n'existe donc pas, selon la norme, de type permettant de manipuler les valeurs 16 bits sur les machines 64 bits si le type short int est codé sur 32 bits, ou, inversement, de type permettant de manipuler les valeurs 32 bits si le type short int est codé sur 16 bits. Afin de résoudre ces problèmes, la plupart des compilateurs brisent la règle selon laquelle le type int est le type des entiers natifs du processeur, et fixent sa taille à 32 bits quelle que soit l'architecture utilisée. Ainsi, le type short est toujours codé sur 16 bits, le type int sur 32 bits et le type long sur 32 ou 64 bits selon que l'architecture de la machine est 32 ou 64 bits. Autrement dit, le type qui représente les entiers nativement n'est plus le type int, mais le type long. Cela ne change pas les programmes 32 bits, puisque ces deux types sont identiques dans ce cas. Les programmes destinés aux machines 64 bits pourront quant à eux être optimisés en utilisant le type long à chaque fois que l'on voudra utiliser le type de données natif de la machine cible. Les programmes 16 bits en revanchent ne sont en revanche plus compatibles avec ces règles, mais la plupart des compilateurs actuels ne permettent plus de compiler des programmes 16 bits de toutes manières.
Les types char, wchar_t et int peuvent être signés ou non. Un nombre signé peut être négatif, pas un nombre non signé. Lorsqu'un nombre est signé, la valeur absolue du plus grand nombre représentable est plus petite. Par défaut, les nombres entiers sont signés. Le signe des types char et wchar_t dépend du compilateur utilisé, il est donc préférable de spécifier systématiquement si ces types sont signés ou non lorsqu'on les utilise en tant que type entier. Pour préciser qu'un nombre n'est pas signé, il faut utiliser le mot clé unsigned. Pour préciser qu'un nombre est signé, on peut utiliser le mot clé signed. Ces mots clés peuvent être intervertis librement avec les mots clés long et short pour les types entiers.
Exemple 1-3. Types signés et non signés
unsigned char
signed char
unsigned wchar_t
signed wchar_t
unsigned int
signed int
unsigned long int
long unsigned int
Note : Le C++ (et le C++ uniquement) considère les types char et wchar_t comme les types de base des caractères. Le langage C++ distingue donc les versions signées et non signées de ces types de la version dont le signe n'est pas spécifié, puisque les caractères n'ont pas de notion de signe associée. Cela signifie que les compilateurs C++ traitent les types char, unsigned char et signed char comme des types différents, et il en est de même pour les types wchar_t, signed wchar_t et unsigned wchar_t. Cette distinction n'a pas lieu d'être au niveau des plages de valeurs si l'on connaît le signe du type utilisé en interne pour représenter les types char et wchar_t, mais elle est très importante dans la détermination de la signature des fonctions, en particulier au niveau du mécanisme de surcharge des fonctions. Les notions de signature et de surcharge des fonctions seront détaillées plus loin dans ce cours. Les valeurs accessibles avec les nombres signés ne sont pas les mêmes que celles accessibles avec les nombres non signés. En effet, un bit est utilisé pour le signe dans les nombres signés. Par exemple, si le type char est codé sur 8 bits, on peut coder les nombres allant de 0 à 255 avec ce type en non signé (il y a 8 chiffres binaires, chacun peut valoir 0 ou 1, on a donc 2 puissance 8 combinaisons possibles, ce qui fait 256). En signé, les valeurs s'étendent de -128 à 127 (un des chiffres binaires est utilisé pour le signe, il en reste 7 pour coder le nombre, donc il reste 128 possibilités dans les positifs comme dans les négatifs. 0 est considéré comme positif. En tout, il y a autant de possibilités.).
De même, si le type int est codé sur 16 bits (cas des machines 16 bits), les valeurs accessibles vont de -32768 à 32767 ou de 0 à 65535 si l'entier n'est pas signé. C'est le cas sur les PC en mode réel (c'est-à-dire sous DOS) et sous Windows 3.x. Sur les machines fonctionnant en 32 bits, le type int est stocké sur 32 bits : l'espace des valeurs disponibles est donc 65536 fois plus large. C'est le cas sur les PC en mode protégé 32 bits (Windows 9x ou NT, DOS Extender, Linux) et sur les Macintosh. Sur les machines 64 bits, le type int devrait être 64 bits (DEC Alpha et la plupart des machines UNIX par exemple).
Enfin, le type float est généralement codé sur 4 octets, et les types double et long double sont souvent identiques et codés sur 8 octets.
Note : On constate donc que la portabilité des types de base est très aléatoire. Cela signifie qu'il faut faire extrêmement attention dans le choix des types si l'on veut faire du code portable (c'est-à-dire qui compilera et fonctionnera sans modification du programme sur tous les ordinateurs). Il est dans ce cas nécessaire d'utiliser des types de données qui donnent les mêmes intervalles de valeurs sur tous les ordinateurs. La norme ISO C99 impose de définir des types portables afin de régler ces problèmes sur toutes les architectures existantes. Ces types sont définis dans le fichier d'en-tête stdint.h. Il s'agit des types int8_t, int16_t, int32_t et int64_t, et de leurs versions non signées uint8_t, uint16_t, uint32_t et uint64_t. La taille de ces types en bits est indiquée dans leur nom et leur utilisation ne devrait pas poser de problème. De la même manière, deux représentations d'un même type peuvent être différentes en mémoire sur deux machines d'architectures différentes, même à taille égale en nombre de bits. Le problème le plus courant est l'ordre de stockage des octets en mémoire pour les types qui sont stockés sur plus d'un octet (c'est-à-dire quasiment tous). Cela a une importance capitale lorsque des données doivent être échangées entre des machines d'architectures a priori différentes, par exemple dans le cadre d'une communication réseau, ou lors de la définition des formats de fichiers. Une solution simple est de toujours d'échanger les données au format texte, ou de choisir un mode de représentation de référence. Les bibliothèques réseau disposent généralement des méthodes permettant de convertir les données vers un format commun d'échange de données par un réseau et pourront par exemple être utilisées.
1.3. Notation des valeurs
Les entiers se notent de la manière suivante :
base 10 (décimale) : avec les chiffres de '0' à '9', et les signes '+' (facultatif) et '-'.
Exemple 1-4. Notation des entiers en base 10
12354, -2564base 16 (hexadécimale) : avec les chiffres '0' à '9' et 'A' à 'F' ou a à f (A=a=10, B=b=11, ... F=f=15). Les entiers notés en hexadécimal devront toujours être précédés de « 0x » (qui indique la base). On ne peut pas utiliser le signe '-' avec les nombres hexadécimaux.
Exemple 1-5. Notation des entiers en base 16
0x1AEbase 8 (octale) : avec les chiffres de '0' à '7'. Les nombres octaux doivent être précédés d'un 0 (qui indique la base). Le signe '-' ne peut pas être utilisé.
Exemple 1-6. Notation des entiers en base 8
01, 0154Les flottants (pseudo réels) se notent de la manière suivante :
[signe] chiffres [.[chiffres]][e|E [signe] exposant][f]
où signe indique le signe. On emploie les signes '+' (facultatif) et '-' aussi bien pour la mantisse que pour l'exposant. 'e' ou 'E' permet de donner l'exposant du nombre flottant. L'exposant est facultatif. Si on ne donne pas d'exposant, on doit donner des chiffres derrière la virgule avec un point et ces chiffres. Le suffixe 'f' permet de préciser si le nombre est de type float ou non (auquel cas il s'agit d'un double).
Les chiffres après la virgule sont facultatifs, mais pas le point. Si on ne met ni le point, ni la mantisse, le nombre est un entier décimal.
Exemple 1-7. Notation des réels
-123.56f, 12e-12, 2
« 2 » est entier, « 2.f » est réel. Les caractères se notent entre guillemets simples :
'A', 'c', '('On peut donner un caractère non accessible au clavier en donnant son code en octal, précédé du caractère '\'. Par exemple, le caractère 'A' peut aussi être noté '\101'. Remarquez que cette notation est semblable à la notation des nombres entiers en octal, et que le '0' initial est simplement remplacé par un '\'. Il est aussi possible de noter les caractères avec leur code en hexadécimal, à l'aide de la notation « \xNN », où NN est le code hexadécimal du caractère. Enfin, il existe des séquences d'échappement particulières qui permettent de coder certains caractères spéciaux plus facilement. Les principales séquences d'échappement sont les suivantes :
'\a' Bip sonore
'\b' Backspace
'\f' Début de page suivante
'\r' Retour à la ligne (sans saut de ligne)
'\n' Passage à la ligne
'\t' Tabulation
'\v' Tabulation verticaleD'autres séquences d'échappement sont disponibles, afin de pouvoir représenter les caractères ayant une signification particulière en C :
'\\' Le caractère \
'\"' Le caractère "
'\'' Le caractère 'Bien qu'il n'existe pas à proprement parler de chaînes de caractères en C/C++, il est possible de définir des tableaux de caractères constants utilisables en tant que chaînes de caractères en donnant leur contenu entre doubles guillemets :
"Exemple de chaîne de caractères..."Les caractères spéciaux peuvent être utilisés directement dans les chaînes de caractères constantes :
"Ceci est un saut de ligne :\nCeci est à la ligne suivante."Si une chaîne de caractères constante est trop longue pour tenir sur une seule ligne, on peut concaténer plusieurs chaînes en les juxtaposant :
"Ceci est la première chaîne " "ceci est la deuxième."produit la chaîne de caractères complète suivante :
"Ceci est la première chaîne ceci est la deuxième."
Note : Attention : il ne faut pas mettre de caractère nul dans une chaîne de caractères. Ce caractère est en effet le caractère de terminaison de toute chaîne de caractères. Enfin, les versions longues des différents types cités précédemment (wchar_t, long int et long double) peuvent être notées en faisant précéder ou suivre la valeur de la lettre 'L'. Cette lettre doit précéder la valeur dans le cas des caractères et des chaînes de caractères et la suivre quand il s'agit des entiers et des flottants. Par exemple :
L"Ceci est une chaîne de wchar_t."
2.3e5L
1.4. La définition des variables
Les variables simples peuvent être définies avec la syntaxe suivante :
type identificateur;où type est le type de la variable et identificateur est son nom. Il est possible de créer et d'initialiser une série de variables dès leur création avec la syntaxe suivante :
type identificateur[=valeur][, identificateur[=valeur][...]];
Exemple 1-8. Définition de variables
int i=0, j=0; /* Définit et initialise deux entiers à 0 */
double somme; /* Déclare une variable réelle */Les variables peuvent être définies quasiment n'importe où dans le programme. Cela permet de ne définir une variable temporaire que là où l'on en a besoin.
Note : Cela n'est vrai qu'en C++. En C pur, on est obligé de définir les variables au début des fonctions ou des instructions composées (voir plus loin). Il faut donc connaître les variables temporaires nécessaires à l'écriture du morceau de code qui suit leur définition. La définition d'une variable ne suffit pas, en général, à l'initialiser. Les variables non initialisées contenant des valeurs aléatoires, il faut éviter de les utiliser avant une initialisation correcte. Initialiser les variables que l'on déclare à leur valeur par défaut est donc une bonne habitude à prendre. Cela est d'ailleurs obligatoire pour les variables « constantes » que l'on peut déclarer avec le mot clé const, car ces variables ne peuvent pas être modifiées après leur définition. Ce mot clé sera présenté en détail dans la Section 3.2.
Note : Si les variables utilisant les types simples ne sont pas initialisées lors de leur définition de manière générale, ce n'est pas le cas pour les objets dont le type est une classe définie par l'utilisateur. En effet, pour ces objets, le compilateur appelle automatiquement une fonction d'initialisation appelée le « constructeur » lors de leur définition. La manière de définir des classes d'objets ainsi que toutes les notions traitant de la programmation objet seront décrites dans le Chapitre 8. La définition d'un tableau se fait en faisant suivre le nom de l'identificateur d'une paire de crochets, contenant le nombre d'élément du tableau :
type identificateur[taille]([taille](...));
Note : Attention ! Les caractères '[' et ']' étant utilisés par la syntaxe des tableaux, ils ne signifient plus les éléments facultatifs ici. Ici, et ici seulement, les éléments facultatifs sont donnés entre parenthèses. Dans la syntaxe précédente, type représente le type des éléments du tableau.
Exemple 1-9. Définition d'un tableau
int MonTableau[100];MonTableau est un tableau de 100 entiers. On référence les éléments des tableaux en donnant l'indice de l'élément entre crochet :
MonTableau[3]=0;
Note : La syntaxe permettant d'initialiser les tableaux dès leur création est un peu plus complexe que celle permettant d'initialiser les variables de type simple. Cette syntaxe est semblable à celle permettant d'initialiser les structures de données et sera donc décrite dans la section qui leur est dédiée. En C/C++, les tableaux à plus d'une dimension sont des tableaux de tableaux. On prendra garde au fait que dans la définition d'un tableau à plusieurs dimensions, la dernière taille indiquée spécifie la taille du tableau dont on fait un tableau. Ainsi, dans l'exemple suivant :
int Matrice[5][4];Matrice est un tableau de taille 5 dont les éléments sont eux-mêmes des tableaux de taille 4. L'ordre de déclaration des dimensions est donc inversé : 5 est la taille de la dernière dimension et 4 est la taille de la première dimension. L'élément suivant :
Matrice[2];est donc le troisième élément de ce tableau de taille cinq, et est lui-même un tableau de quatre éléments.
En C/C++, les indices des tableaux varient de 0 à taille-1. Il y a donc bien taille éléments dans le tableau. Dans l'exemple donné ci-dessus, l'élément MonTableau[100] n'existe pas : y accéder plantera le programme. C'est au programmeur de vérifier que ses programmes n'utilisent jamais les tableaux avec des indices plus grands que leur taille ou négatifs.
Un autre point auquel il faudra faire attention est la taille des tableaux à utiliser pour les chaînes de caractères. Une chaîne de caractères se termine obligatoirement par le caractère nul ('\0'), il faut donc réserver de la place pour lui. Par exemple, pour créer une chaîne de caractères de 100 caractères au plus, il faut un tableau pour 101 caractères (déclaré avec « char chaine[101]; »).
1.5. Instructions et opérations
Les instructions sont généralement identifiées par le point virgule. C'est ce caractère qui marque la fin d'une instruction.
Exemple 1-10. Instruction vide
; /* Instruction vide : ne fait rien ! */Il existe plusieurs types d'instructions, qui permettent de réaliser des opérations variées. Les instructions les plus courantes sont sans doute les instructions qui effectuent des opérations, c'est-à-dire les instructions qui contiennent des expressions utilisant des opérateurs.
Les principales opérations utilisables en C/C++ sont les suivantes :
les affectations : variable = valeur
Note : Les affectations ne sont pas des instructions. Ce sont bien des opérations qui renvoient la valeur affectée. On peut donc effectuer des affectations multiples :
i=j=k=m=0; /* Annule les variables i, j, k et m. */les opérations de base du langage : valeur op valeuroù op est l'un des opérateurs suivants : +, -, *, /, %, &, |, ^, ~, .
Note : '/' représente la division euclidienne pour les entiers et la division classique pour les flottants. '%' représente la congruence (c'est-à-dire le reste de la division euclidienne). '|' et '&' représentent respectivement le ou et le et binaire (c'est-à-dire bit à bit : 1 et 1 = 1, 0 et x = 0, 1 ou x = 1 et 0 ou 0 = 0). '^' représente le ou exclusif (1 xor 1 = 0, 0 xor 0 = 0 et 1 xor 0 = 1). '~' représente la négation binaire (1 devient 0 et vice versa). '' effectuent un décalage binaire vers la gauche et la droite respectivement, d'un nombre de bits égal à la valeur du second opérande.
les opérations des autres opérateurs du langage. Le C et le C++ disposent d'opérateurs un peu plus évolués que les opérateurs permettant de réaliser les opérations de base du langage. Ces opérateurs sont les opérateurs d'incrémentation et de décrémentation ++ et --, l'opérateur ternaire d'évaluation conditionnelle d'une expression (opérateur ?:) et l'opérateur virgule (opérateur ,). La syntaxe de ces opérateurs est décrite ci-dessous.
les appels de fonctions. Nous verrons comment écrire et appeler des fonctions dans les sections suivantes.
Bien entendu, la plupart des instructions contiendront des affectations. Ce sont donc sans doute les affectations qui sont les plus utilisées parmi les diverses opérations réalisables, aussi le C et le C++ permettent-ils l'utilisation d'affectations composées. Une affectation composée est une opération permettant de réaliser en une seule étape une opération normale et l'affectation de son résultat dans la variable servant de premier opérande. Les affectations composées utilisent la syntaxe suivante :
variable op_aff valeuroù op_aff est l'un des opérateurs suivants : '+=', '-=', '*=', etc. Cette syntaxe est strictement équivalente à :
variable = variable op valeuret permet donc de modifier la valeur de variable en lui appliquant l'opérateur op.
Exemple 1-11. Affectation composée
i*=2; /* Multiplie i par 2 : i = i * 2. */Les opérateurs d'incrémentation et de décrémentation ++ et -- s'appliquent comme des préfixes ou des suffixes sur les variables. Lorsqu'ils sont en préfixe, la variable est incrémentée ou décrémentée, puis sa valeur est renvoyée. S'ils sont en suffixe, la valeur de la variable est renvoyée, puis la variable est incrémentée ou décrémentée. Par exemple :
int i=2,j,k;
j=++i; /* À la fin de cette instruction, i et j valent 3. */
k=j++; /* À la fin de cette ligne, k vaut 3 et j vaut 4. */
Note : On prendra garde à n'utiliser les opérateurs d'incrémentation et de décrémentation postfixés que lorsque cela est réellement nécessaire. En effet, ces opérateurs doivent contruire un objet temporaire pour renvoyer la valeur de la variable avant incrémentation ou décrémentation. Si cet objet temporaire n'est pas utilisé, il est préférable d'utiliser les versions préfixées de ces opérateurs. L'opérateur ternaire d'évaluation conditionnelle ?: est le seul opérateur qui demande 3 paramètres (à part l'opérateur fonctionnel () des fonctions, qui admet n paramètres, et que l'on décrira plus tard). Cet opérateur permet de réaliser un test sur une condition et de calculer une expression ou une autre selon le résultat de ce test. La syntaxe de cet opérateur est la suivante :
test ? expression1 : expression2Dans cette syntaxe, test est évalué en premier. Son résultat doit être booléen ou entier. Si test est vrai (ou si sa valeur est non nulle), expression1 est calculée et sa valeur est renvoyée. Sinon, c'est la valeur de expression2 qui est renvoyée. Par exemple, l'expression :
Min=(iname, name);
strcpy(p->address, address);
p->next = *lst;
*lst = p;
}
else
{
free(p);
p = NULL;
}
}
return (p != NULL);
}
/* Fonction de suppression d'une personne.
La structure de la liste est modifiée par la suppression
de l'élément de cette personne. Cela peut impliquer la modification
du chaînage de l'élément précédent, ou la modification de la tête
de liste elle-même. */
int remove_person(People *lst, const char *name)
{
/* Recherche la personne et son antécédant : */
Person *prev = NULL;
Person *p = *lst;
while (p != NULL)
{
/* On sort si l'élément courant est la personne recherchée : */
if (strcmp(p->name, name) == 0)
break;
/* On passe à l'élément suivant sinon : */
prev = p;
p = p->next;
}
if (p != NULL)
{
/* La personne a été trouvée, on la supprime de la liste : */
if (prev == NULL)
{
/* La personne est en tête de liste, on met à jour
le pointeur de tête de liste : */
*lst = p->next;
}
else
{
/* On met à jour le lien de l'élément précédent : */
prev->next = p->next;
}
/* et on la détruit : */
free(p->name);
free(p->address);
free(p);
}
return (p != NULL);
}
/* Simple fonction d'affichage. */
void print_list(People const *lst)
{
Person const *p = *lst;
int i = 1;
while (p != NULL)
{
printf("Personne %d : %s (%s)\n", i, p->name, p->address);
p = p->next;
++i;
}
}
/* Fonction de destruction et de libération de la mémoire. */
void destroy_list(People *lst)
{
while (*lst != NULL)
{
Person *p = *lst;
*lst = p->next;
free(p->name);
free(p->address);
free(p);
}
return ;
}
int main(void)
{
int op = 0;
size_t s;
char buffer[16];
char name[256];
char address[256];
/* Crée une liste de personne : */
People p;
init_list(&p);
/* Utilise la liste : */
do
{
printf("Opération (0 = quitter, 1 = ajouter, 2 = supprimer) ?");
fgets(buffer, 16, stdin);
buffer[15] = 0;
op = 3;
sscanf(buffer, "%d", &op);
switch (op)
{
case 0:
break;
case 1:
printf("Nom : ");
fgets(name, 256, stdin); /* Lit le nom. */
name[255] = 0; /* Assure que le caractère nul
terminal est écrit. */
s = strlen(name); /* Supprime l'éventuel saut de ligne. */
if (name[s - 1] == '\n') name[s - 1] = 0;
/* Même opération pour l'adresse : */
printf("Adresse : ");
fgets(address, 256, stdin);
name[255] = 0;
s = strlen(address);
if (address[s - 1] == '\n') address[s - 1] = 0;
add_person(&p, name, address);
break;
case 2:
printf("Nom : ");
fgets(name, 256, stdin);
name[255] = 0;
s = strlen(name);
if (name[s - 1] == '\n') name[s - 1] = 0;
if (remove_person(&p, name) == 0)
{
printf("Personne inconnue.\n");
}
break;
default:
printf("Opération invalide\n");
break;
}
if (op != 0) print_list(&p);
} while (op != 0);
/* Détruit la liste : */
destroy_list(&p);
return 0;
}
Note : Comme vous pouvez le constater, la lecture des chaînes de caractères saisies par l'utilisateur est réalisée au moyen de la fonction fgets de la bibliothèque C standard. Cette fonction permet de lire une ligne complète sur le flux spécifié en troisième paramètre, et de stocker le résultat dans la chaîne de caractères fournie en premier paramètre. Elle ne lira pas plus de caractères que le nombre indiqué en deuxième paramètre, ce qui permet de contrôler la taille des lignes saisies par l'utilisateur. La fonction fgets nécessite malheureusement quelques traitements supplémentaires avant de pouvoir utiliser la chaîne de caractères lue, car elle n'écrit pas le caractère nul terminal de la chaîne C si le nombre maximal de caractères à lire est atteint, et elle stocke le caractère de saut de ligne en fin de ligne si ce nombre n'est pas atteint. Il est donc nécessaire de s'assurer que la ligne se termine bien par un caractère nul terminal d'une part, et de supprimer le caractère de saut de ligne s'il n'est pas essentiel d'autre part. Ces traitements constituent également un bon exemple de manipulation des pointeurs et des chaînes de caractères. Ce programme n'interdit pas les définitions multiples de personnes ayant le même nom. Il n'interdit pas non plus la définition de personnes anonymes. Le lecteur pourra essayer de corriger ces petits défauts à titre d'exercice, afin de s'assurer que les notions de pointeur sont bien assimilées. Rappelons que les pointeurs sont une notion essentielle en C et qu'il faut être donc parfaitement familiarisé avec eux.
4.11.2. Allocation dynamique en C++
En plus des fonctions malloc et free du C, le C++ fournit d'autres moyens pour allouer et restituer la mémoire. Pour cela, il dispose d'opérateurs spécifiques : new, delete, new[] et delete[]. La syntaxe de ces opérateurs est respectivement la suivante :
new type
delete pointeur
new type[taille]
delete[] pointeurLes deux opérateurs new et new[] permettent d'allouer de la mémoire, et les deux opérateurs delete et delete[] de la restituer.
La syntaxe de new est très simple, il suffit de faire suivre le mot clé new du type de la variable à allouer, et l'opérateur renvoie directement un pointeur sur cette variable avec le bon type. Il n'est donc plus nécessaire d'effectuer un transtypage après l'allocation, comme c'était le cas pour la fonction malloc. Par exemple, l'allocation d'un entier se fait comme suit :
int *pi = new int; // Équivalent à (int *) malloc(sizeof(int)).La syntaxe de delete est encore plus simple, puisqu'il suffit de faire suivre le mot clé delete du pointeur sur la zone mémoire à libérer :
delete pi; // Équivalent à free(pi);Les opérateurs new[] et delete[] sont utilisés pour allouer et restituer la mémoire pour les types tableaux. Ce ne sont pas les mêmes opérateurs que new et delete, et la mémoire allouée par les uns ne peut pas être libérée par les autres. Si la syntaxe de delete[] est la même que celle de delete, l'emploi de l'opérateur new[] nécessite de donner la taille du tableau à allouer. Ainsi, on pourra créer un tableau de 10000 entiers de la manière suivante :
int *Tableau=new int[10000];et détruire ce tableau de la manière suivante :
delete[] Tableau;L'opérateur new[] permet également d'allouer des tableaux à plusieurs dimensions. Pour cela, il suffit de spécifier les tailles des différentes dimensions à la suite du type de donnée des élements du tableau, exactement comme lorsque l'on crée un tableau statiquement. Toutefois, seule la première dimension du tableau peut être variable, et les dimensions deux et suivantes doivent avoir une taille entière positive et constante. Par exemple, seule la deuxième ligne de l'exemple qui suit est une allocation dynamique de tableau valide :
int i=5, j=3;
int (*pi1)[3] = new int[i][3]; // Alloue un tableau de i lignes de trois entiers.
int (*pi2)[3] = new int[i][j]; // Illégal, j n'est pas constant.Si l'on désire réellement avoir des tableaux dont plusieurs dimensions sont de taille variable, on devra allouer un tableau de pointeurs et, pour chaque ligne de ce tableau, allouer un autre tableau à la main.
Note : Il est important d'utiliser l'opérateur delete[] avec les pointeurs renvoyés par l'opérateur new[] et l'opérateur delete avec les pointeurs renvoyés par new. De plus, on ne devra pas non plus mélanger les mécanismes d'allocation mémoire du C et du C++ (utiliser delete sur un pointeur renvoyé par malloc par exemple). En effet, le compilateur peut allouer une quantité de mémoire supérieure à celle demandée par le programme afin de stocker des données qui lui permettent de gérer la mémoire. Ces données peuvent être interprétées différemment pour chacune des méthodes d'allocation, si bien qu'une utilisation erronée peut entraîner soit la perte des blocs de mémoire, soit une erreur, soit un plantage. L'opérateur new[] alloue la mémoire et crée les objets dans l'ordre croissant des adresses. Inversement, l'opérateur delete[] détruit les objets du tableau dans l'ordre décroissant des adresses avant de libérer la mémoire.
La manière dont les objets sont construits et détruits par les opérateurs new et new[] dépend de leur nature. S'il s'agit de types de base du langage ou de structures simples, aucune initialisation particulière n'est faite. La valeur des objets ainsi créés est donc indéfinie, et il faudra réaliser l'initialisation soi-même. Si, en revanche, les objets créés sont des instances de classes C++, le constructeur de ces classes sera automatiquement appelé lors de leur initialisation. C'est pour cette raison que l'on devra, de manière générale, préférer les opérateurs C++ d'allocation et de désallocation de la mémoire aux fonctions malloc et free du C. Ces opérateurs ont de plus l'avantage de permettre un meilleur contrôle des types de données et d'éviter un transtypage. Les notions de classe et de constructeur seront présentées en détail dans le chapitre traitant de la couche objet du C++.
Lorsqu'il n'y a pas assez de mémoire disponible, les opérateurs new et new[] peuvent se comporter de deux manières selon l'implémentation. Le comportement le plus répandu est de renvoyer un pointeur nul. Cependant, la norme C++ indique un comportement différent : si l'opérateur new manque de mémoire, il doit appeler un gestionnaire d'erreur. Ce gestionnaire ne prend aucun paramètre et ne renvoie rien.
Selon le comportement de ce gestionnaire d'erreur, plusieurs actions peuvent être faites :
soit ce gestionnaire peut corriger l'erreur d'allocation et rendre la main à l'opérateur new ( le programme n'est donc pas terminé), qui effectue une nouvelle tentative pour allouer la mémoire demandée ;
soit il ne peut rien faire. Dans ce cas, il peut mettre fin à l'exécution du programme ou lancer une exception std::bad_alloc, qui remonte alors jusqu'à la fonction appelant l'opérateur new. C'est le comportement du gestionnaire installé par défaut dans les implémentations conformes à la norme.
L'opérateur new est donc susceptible de lancer une exception std::bad_alloc. Voir le Chapitre 9 pour plus de détails à ce sujet.
Il est possible de remplacer le gestionnaire d'erreur appelé par l'opérateur new à l'aide de la fonction std::set_new_handler, déclarée dans le fichier d'en-tête new. Cette fonction attend en paramètre un pointeur sur une fonction qui ne prend aucun paramètre et ne renvoie rien. Elle renvoie l'adresse du gestionnaire d'erreur précédent.
Note : La fonction std::set_new_handler et la classe std::bad_alloc font partie de la bibliothèque standard C++. Comme leurs noms l'indiquent, ils sont déclarés dans l'espace de nommage std::, qui est réservé pour les fonctions et les classes de la bibliothèque standard. Voyez aussi le Chapitre 11 pour plus de détails sur les espaces de nommages. Si vous ne désirez pas utiliser les mécanismes des espaces de nommage, vous devrez inclure le fichier d'en-tête new.h au lieu de new. Attendez vous à ce qu'un jour, tous les compilateurs C++ lancent une exception en cas de manque de mémoire lors de l'appel à l'opérateur new, car c'est ce qu'impose la norme. Si vous ne désirez pas avoir à gérer les exceptions dans votre programme et continuer à recevoir un pointeur nul en cas de manque de mémoire, vous pouvez fournir un deuxième paramètre de type std::nothrow_t à l'opérateur new. La bibliothèque standard définit l'objet constant std::nothrow à cet usage.
Les opérateurs delete et delete[] peuvent parfaitement être appelés avec un pointeur nul en paramètre. Dans ce cas, ils ne font rien et redonnent la main immédiatement à l'appelant. Il n'est donc pas nécessaire de tester la non nullité des pointeurs sur les objets que l'on désire détruire avant d'appeler les opérateurs delete et delete[].
4.12. Pointeurs et références de fonctions
4.12.1. Pointeurs de fonctions
Il est possible de faire des pointeurs de fonctions. Un pointeur de fonction contient l'adresse du début du code binaire constituant la fonction. Il est possible d'appeler une fonction dont l'adresse est contenue dans un pointeur de fonction avec l'opérateur d'indirection *.
Pour déclarer un pointeur de fonction, il suffit de considérer les fonctions comme des variables. Leur déclaration est identique à celle des tableaux, en remplaçant les crochets par des parenthèses :
type (*identificateur)(paramètres);où type est le type de la valeur renvoyée par la fonction, identificateur est le nom du pointeur de la fonction et paramètres est la liste des types des variables que la fonction attend comme paramètres, séparés par des virgules.
Exemple 4-14. Déclaration de pointeur de fonction
int (*pf)(int, int); /* Déclare un pointeur de fonction. */pf est un pointeur de fonction attendant comme paramètres deux entiers et renvoyant un entier.
Il est possible d'utiliser typedef pour créer un alias du type pointeur de fonction :
typedef int (*PtrFonct)(int, int);
PtrFonct pf;PtrFonct est le type des pointeurs de fonctions.
Si f est une fonction répondant à ces critères, on peut alors initialiser pf avec l'adresse de f. De même, on peut appeler la fonction pointée par pf avec l'opérateur d'indirection.
Exemple 4-15. Déréférencement de pointeur de fonction
#include /* Autorise l'emploi de scanf et de printf. */
int f(int i, int j) /* Définit une fonction. */
{
return i+j;
}
int (*pf)(int, int); /* Déclare un pointeur de fonction. */
int main(void)
{
int l, m; /* Déclare deux entiers. */
pf = &f; /* Initialise pf avec l'adresse de la fonction f. */
printf("Entrez le premier entier : ");
scanf("%u",&l); /* Initialise les deux entiers. */
printf("\nEntrez le deuxième entier : ");
scanf("%u",&m);
/* Utilise le pointeur pf pour appeler la fonction f
et affiche le résultat : */
printf("\nLeur somme est de : %u\n", (*pf)(l,m));
return 0;
}L'intérêt des pointeurs de fonction est de permettre l'appel d'une fonction parmi un éventail de fonctions au choix.
Par exemple, il est possible de faire un tableau de pointeurs de fonctions et d'appeler la fonction dont on connaît l'indice de son pointeur dans le tableau.
Exemple 4-16. Application des pointeurs de fonctions
#include /* Autorise l'emploi de scanf et de printf. */
/* Définit plusieurs fonctions travaillant sur des entiers : */
int somme(int i, int j)
{
return i+j;
}
int multiplication(int i, int j)
{
return i*j;
}
int quotient(int i, int j)
{
return i/j;
}
int modulo(int i, int j)
{
return i%j;
}
typedef int (*fptr)(int, int);
fptr ftab[4];
int main(void)
{
int i,j,n;
ftab[0]=&somme; /* Initialise le tableau de pointeur */
ftab[1]=&multiplication; /* de fonctions. */
ftab[2]="ient;
ftab[3]=&modulo;
printf("Entrez le premier entier : ");
scanf("%u",&i); /* Demande les deux entiers i et j. */
printf("\nEntrez le deuxième entier : ");
scanf("%u",&j);
printf("\nEntrez la fonction : ");
scanf("%u",&n); /* Demande la fonction à appeler. */
if (n < 4)
printf("\nRésultat : %u.\n", (*(ftab[n]))(i,j) );
else
printf("\nMauvais numéro de fonction.\n");
return 0;
}
4.12.2. Références de fonctions
Les références de fonctions sont acceptées en C++. Cependant, leur usage est assez limité. Elles permettent parfois de simplifier les écritures dans les manipulations de pointeurs de fonctions. Mais comme il n'est pas possible de définir des tableaux de références, le programme d'exemple donné ci-dessus ne peut pas être récrit avec des références.
Les références de fonctions peuvent malgré tout être utilisées à profit dans le passage des fonctions en paramètre dans une autre fonction. Par exemple :
#include // Autorise l'emploi de scanf et de printf.
// Fonction de comparaison de deux entiers :
int compare(int i, int j)
{
if (ij) return 1;
else return 0;
}
// Fonction utilisant une fonction en tant que paramètre :
void trie(int tableau[], int taille, int (&fcomp)(int, int))
{
// Effectue le tri de tableau avec la fonction fcomp.
// Cette fonction peut être appelée comme toute les autres
// fonctions :
printf("%d", fcomp(2,3));
⋮
return ;
}
int main(void)
{
int t[3]={1,5,2};
trie(t, 3, compare); // Passage de compare() en paramètre.
return 0;
}
4.13. Paramètres de la fonction main - ligne de commande
L'appel d'un programme se fait normalement avec la syntaxe suivante :
nom param1 param2 [...]où nom est le nom du programme à appeler et param1, etc. sont les paramètres de la ligne de commande. De plus, le programme appelé peut renvoyer un code d'erreur au programme appelant (soit le système d'exploitation, soit un autre programme). Ce code d'erreur est en général 0 quand le programme s'est déroulé correctement. Toute autre valeur indique qu'une erreur s'est produite en cours d'exécution.
La valeur du code d'erreur est renvoyée par la fonction main. Le code d'erreur doit toujours être un entier. La fonction main peut donc (et même normalement doit) être de type entier :
int main(void) ...Les paramètres de la ligne de commandes peuvent être récupérés par la fonction main. Si vous désirez les récupérer, la fonction main doit attendre deux paramètres :
le premier est un entier, qui représente le nombre de paramètres ;
le deuxième est un tableau de chaînes de caractères (donc en fait un tableau de pointeurs, ou encore un pointeur de pointeurs de caractères).
Les paramètres se récupèrent avec ce tableau. Le premier élément pointe toujours sur la chaîne donnant le nom du programme. Les autres éléments pointent sur les paramètres de la ligne de commande.
Exemple 4-17. Récupération de la ligne de commande
#include /* Autorise l'utilisation des fonctions */
/* printf et scanf. */
int main(int n, char *params[]) /* Fonction principale. */
{
int i;
/* Affiche le nom du programme : */
printf("Nom du programme : %s.\n",params[0]);
/* Affiche la ligne de commande : */
for (i=1; i(y)?(x):(y))
#define MIN(x,y) ((x)~A(); // Appel explicite du destructeur de A.
bThrow = true; // Maintenant, le constructeur de A lance
// une exception.
try
{
A *pB=new(pA) A; // Réutilisation de la mémoire de A.
// Si une exception a lieu, l'opérateur
// delete(void *, A *) avec placement
// est utilisé.
delete pB; // Destruction de l'objet.
}
catch (...)
{
// L'opérateur delete(void *, A *) ne libère pas la mémoire
// allouée lors du premier new. Il faut donc quand même
// le faire, mais sans delete, car l'objet pointé par pA
// est déjà détruit, et celui pointé par pB l'a été par
// l'opérateur delete(void *, A *) :
free(pA);
}
return 0;
}
Note : Il est possible d'utiliser le placement avec les opérateurs new[] et delete[] exactement de la même manière qu'avec les opérateurs new et delete.
On notera que lorsque l'opérateur new est utilisé avec placement, si le deuxième argument est de type size_t, l'opérateur delete à deux arguments peut être interprété soit comme un opérateur delete classique sans placement mais avec deux paramètres, soit comme l'opérateur delete avec placement correspondant à l'opérateur new avec placement. Afin de résoudre cette ambiguïté, le compilateur interprète systématiquement l'opérateur delete avec un deuxième paramètre de type size_t comme étant l'opérateur à deux paramètres sans placement. Il est donc impossible de définir un opérateur delete avec placement s'il a deux paramètres, le deuxième étant de type size_t. Il en est de même avec les opérateurs new[] et delete[]. Quelle que soit la syntaxe que vous désirez utiliser, les opérateurs new, new[], delete et delete[] doivent avoir un comportement bien déterminé. En particulier, les opérateurs delete et delete[] doivent pouvoir accepter un pointeur nul en paramètre. Lorsqu'un tel pointeur est utilisé dans une expression delete, aucun traitement ne doit être fait.
Enfin, vos opérateurs new et new[] doivent, en cas de manque de mémoire, appeler un gestionnaire d'erreur. Le gestionnaire d'erreur fourni par défaut lance une exception de classe std::bad_alloc (voir le Chapitre 9 pour plus de détails sur les exceptions). Cette classe est définie comme suit dans le fichier d'en-tête new :
class bad_alloc : public exception
{
public:
bad_alloc(void) throw();
bad_alloc(const bad_alloc &) throw();
bad_alloc &operator=(const bad_alloc &) throw();
virtual ~bad_alloc(void) throw();
virtual const char *what(void) const throw();
};
Note : Comme son nom l'indique, cette classe est définie dans l'espace de nommage std::. Si vous ne voulez pas utiliser les notions des espaces de nommage, vous devrez inclure le fichier d'en-tête new.h au lieu de new. Vous obtiendrez de plus amples renseignements sur les espaces de nommage dans le Chapitre 11. La classe exception dont bad_alloc hérite est déclarée comme suit dans le fichier d'en-tête exception :
class exception
{
public:
exception (void) throw();
exception(const exception &) throw();
exception &operator=(const exception &) throw();
virtual ~exception(void) throw();
virtual const char *what(void) const throw();
};
Note : Vous trouverez plus d'informations sur les exceptions dans le Chapitre 9. Si vous désirez remplacer le gestionnaire par défaut, vous pouvez utiliser la fonction std::set_new_handler. Cette fonction attend en paramètre le pointeur sur le gestionnaire d'erreur à installer et renvoie le pointeur sur le gestionnaire d'erreur précédemment installé. Les gestionnaires d'erreurs ne prennent aucun paramètre et ne renvoient aucune valeur.
Leur comportement doit être le suivant :
soit ils prennent les mesures nécessaires pour permettre l'allocation du bloc de mémoire demandé et rendent la main à l'opérateur new. Ce dernier refait alors une tentative pour allouer le bloc de mémoire. Si cette tentative échoue à nouveau, le gestionnaire d'erreur est rappelé. Cette boucle se poursuit jusqu'à ce que l'opération se déroule correctement ou qu'une exception std::bad_alloc soit lancée ;
soit ils lancent une exception de classe std::bad_alloc ;
soit ils terminent l'exécution du programme en cours.
La bibliothèque standard définit une version avec placement des opérateurs new et new[], qui renvoient le pointeur nul au lieu de lancer une exception en cas de manque de mémoire. Ces opérateurs prennent un deuxième paramètre, de type std::nothrow_t, qui doit être spécifié lors de l'appel. La bibliothèque standard définit un objet constant de ce type afin que les programmes puissent l'utiliser sans avoir à le définir eux-même. Cet objet se nomme std::nothrow
Exemple 8-23. Utilisation de new sans exception
char *data = new(std::nothrow) char[25];
if (data == NULL)
{
// Traitement de l'erreur...
⋮
}
Note : La plupart des compilateurs ne respectent pas les règles dictées par la norme C++. En effet, ils préfèrent retourner la valeur nulle en cas de manque de mémoire au lieu de lancer une exception. On peut rendre ces implémentations compatibles avec la norme en installant un gestionnaire d'erreur qui lance lui-même l'exception std::bad_alloc.
8.12. Des entrées - sorties simplifiées
Les flux d'entrée / sortie de la bibliothèque standard C++ constituent sans doute l'une des applications les plus intéressantes de la surcharge des opérateurs. Comme nous allons le voir, la surcharge des opérateurs > permet d'écrire et de lire sur ces flux de manière très intuitive.
En effet, la bibliothèque standard C++ définit dans l'en-tête iostream des classes extrêmement puissantes permettant de manipuler les flux d'entrée / sortie. Ces classes réalisent en particulier les opérations d'entrée / sortie de et vers les périphériques d'entrée et les périphériques de sortie standards (généralement, le clavier et l'écran), mais elles ne s'arrêtent pas là : elles permettent également de travailler sur des fichiers ou encore sur des tampons en mémoire.
Les classes d'entrée / sortie de la bibliothèque standard C++ permettent donc d'effectuer les mêmes opérations que les fonctions printf et scanf de la bibliothèque C standard. Cependant, grâce au mécanisme de surcharge des opérateurs, elles sont beaucoup plus faciles d'utilisation. En effet, les opérateurs > de ces classes ont été surchargés pour chaque type de donnée du langage, permettant ainsi de réaliser des entrées / sorties typées extrêmement facilement. L'opérateur , ou opérateur d'extraction, permettra de réaliser la lecture d'une nouvelle donnée dans le flux d'entrée. Ces deux opérateurs renvoient tous les deux le flux de données utilisé, ce qui permet de réaliser plusieurs opérations d'entrée / sortie successivement sur le même flux.
Note : Cette section n'a pas pour but de décrire en détail les flux d'entrée / sortie de la bibliothèque standard C++, mais plutôt d'en faire une présentation simple permettant de les utiliser sans avoir à se plonger prématurément dans des notions extrêmement évoluées. Vous trouverez une description exhaustive des mécanismes des flux d'entrée / sortie de la bibliothèque standard C++ dans le Chapitre 15. La bibliothèque standard définit quatre instances particulières de ses classes d'entrée / sortie : cin, cout, cerr et clog. Ces objets sont des instances des classes istream et ostream, prenant respectivement en charge l'entrée et la sortie des données des programmes. L'objet cin correspond au flux d'entrée standard stdin du programme, et l'objet cout aux flux de sortie standard stdout. Enfin, les objets cerr et clog sont associés au flux d'erreurs standard stderr. Théoriquement, cerr doit être utilisé pour l'écriture des messages d'erreur des programmes, et clog pour les messages d'information. Cependant, en pratique, les données écrites sur ces deux flux sont écrites dans le même flux, et l'emploi de l'objet clog est assez rare.
L'utilisation des opérateurs d'insertion et d'extraction sur ces flux se résume donc à la syntaxe suivante :
cin >> variable [>> variable [...]];
cout > ValeurEtendue; // Entre la valeur étendue.
cout next;
delete head;
head = tmp;
}
return;
}
void Bag::print(void)
{
BagList *tmp = head;
cout next;
}
if (tmp1!=NULL) // et le supprime de la liste.
{
if (tmp2!=NULL) tmp2->next = tmp1->next;
else head = tmp1->next;
delete tmp1;
}
return;
}Avec la classe Bag définie telle quelle, il est à présent possible de stocker des objets dérivant de la classe Object avec les fonctions add et remove :
class MonObjet : public Object
{
/* Définir la méthode print() pour l'objet... */
};
Bag MonSac;
int main(void)
{
MonObjet a, b, c; // Effectue quelques opérations
// avec le sac :
MonSac.add(a);
MonSac.add(b);
MonSac.add(c);
MonSac.print();
MonSac.remove(b);
MonSac.add(MonSac); // Un sac peut contenir un sac !
MonSac.print(); // Attention ! Cet appel est récursif !
// (plantage assuré).
return 0;
}Nous avons vu que la classe de base servait de moule aux classes dérivées. Le droit d'empêcher une fonction membre virtuelle pure définie dans une classe dérivée d'accéder en écriture non seulement aux données de la classe de base, mais aussi aux données de la classe dérivée, peut donc faire partie de ses prérogatives. Cela est faisable en déclarant le pointeur this comme étant un pointeur constant sur objet constant. Nous avons vu que cela pouvait se faire en rajoutant le mot clé const après la déclaration de la fonction membre. Par exemple, comme l'identifiant de l'objet de base est placé en protected au lieu d'être en private, la classe Object autorise ses classes dérivées à le modifier. Cependant, elle peut empêcher la fonction print de le modifier en la déclarant const :
class Object
{
unsigned long int new_handle(void);
protected:
unsigned long int h;
public:
Object(void); // Le constructeur.
virtual void print(void) const=0; // Fonction virtuelle pure.
unsigned long int handle(void) const; // Fonction renvoyant
// le numéro d'identification
// de l'objet.
};Dans l'exemple donné ci-dessus, la fonction print peut accéder en lecture à h, mais plus en écriture. En revanche, les autres fonctions membres des classes dérivées peuvent y avoir accès, puisque c'est une donnée membre protected. Cette méthode d'encapsulation est donc coopérative (elle requiert la bonne volonté des autres fonctions membres des classes dérivées), tout comme la méthode qui consistait en C à déclarer une variable constante. Cependant, elle permettra de détecter des anomalies à la compilation, car si une fonction print cherche à modifier l'objet sur lequel elle travaille, il y a manifestement une erreur de conception.
Bien entendu, cela fonctionne également avec les fonctions membres virtuelles non pures, et même avec les fonctions non virtuelles.
8.16. Pointeurs sur les membres d'une classe
Nous avons déjà vu les pointeurs sur les objets. Il nous reste à voir les pointeurs sur les membres des classes.
Les classes regroupent les caractéristiques des données et des fonctions des objets. Les membres des classes ne peuvent donc pas être manipulés sans passer par la classe à laquelle ils appartiennent. Par conséquent, il faut, lorsqu'on veut faire un pointeur sur un membre, indiquer le nom de sa classe. Pour cela, la syntaxe suivante est utilisée :
définition classe::* pointeur
Par exemple, si une classe test contient des entiers, le type de pointeurs à utiliser pour stocker leur adresse est :
int test::*Si on veut déclarer un pointeur p de ce type, on écrira donc :
int test::*p1; // Construit le pointeur sur entier
// de la classe test.
Une fois le pointeur déclaré, on pourra l'initialiser en prenant l'adresse du membre de la classe du type correspondant. Pour cela, il faudra encore spécifier le nom de la classe avec l'opérateur de résolution de portée :
p1 = &test::i; // Récupère l'adresse de i.La même syntaxe est utilisable pour les fonctions. L'emploi d'un typedef est dans ce cas fortement recommandé. Par exemple, si la classe test dispose d'une fonction membre appelée lit, qui n'attend aucun paramètre et qui renvoie un entier, on pourra récupérer son adresse ainsi :
typedef int (test::* pf)(void); // Définit le type de pointeur.
pf p2=&test::lit; // Construit le pointeur et
// lit l'adresse de la fonction.Cependant, ces pointeurs ne sont pas utilisables directement. En effet, les données d'une classe sont instanciées pour chaque objet, et les fonctions membres reçoivent systématiquement le pointeur this sur l'objet de manière implicite. On ne peut donc pas faire un déréférencement direct de ces pointeurs. Il faut spécifier l'objet pour lequel le pointeur va être utilisé. Cela se fait avec la syntaxe suivante :
objet.*pointeurPour les pointeurs d'objet, on pourra utiliser l'opérateur ->* à la place de l'opérateur .* (appelé pointeur sur opérateur de sélection de membre).
Ainsi, si a est un objet de classe test, on pourra accéder à la donnée i de cet objet à travers le pointeur p1 avec la syntaxe suivante :
a.*p1 = 3; // Initialise la donnée membre i de a avec la valeur 3.Pour les fonctions membres, on mettra des parenthèses à cause des priorités des opérateurs :
int i = (a.*p2)(); // Appelle la fonction lit() pour l'objet a.Pour les données et les fonctions membres statiques, cependant, la syntaxe est différente. En effet, les données n'appartiennent plus aux objets de la classe, mais à la classe elle-même, et il n'est plus nécessaire de connaître l'objet auquel le pointeur s'applique pour les utiliser. De même, les fonctions membres statiques ne reçoivent pas le pointeur sur l'objet, et on peut donc les appeler sans référencer ce dernier.
La syntaxe s'en trouve donc modifiée. Les pointeurs sur les membres statiques des classes sont compatibles avec les pointeurs sur les objets et les fonctions non-membres. Par conséquent, si une classe contient une donnée statique entière, on pourra récupérer son adresse directement et la mettre dans un pointeur d'entier :
int *p3 = &test::entier_statique; // Récupère l'adresse
// de la donnée membre
// statique.
La même syntaxe s'appliquera pour les fonctions :
typedef int (*pg)(void);
pg p4 = &test::fonction_statique; // Récupère l'adresse
// d'une fonction membre
// statique.
Enfin, l'utilisation des ces pointeurs est identique à celle des pointeurs classiques, puisqu'il n'est pas nécessaire de fournir le pointeur this. Il est donc impossible de spécifier le pointeur sur l'objet sur lequel la fonction doit travailler aux fonctions membres statiques. Cela est naturel, puisque les fonctions membres statiques ne peuvent pas accéder aux données non statiques d'une classe.
Exemple 8-27. Pointeurs sur membres statiques
#include
using namespace std;
class test
{
int i;
static int j;
public:
test(int j)
{
i=j;
return ;
}
static int get(void)
{
/* return i ; INTERDIT : i est non statique
et get l'est ! */
return j; // Autorisé.
}
};
int test::j=5; // Initialise la variable statique.
typedef int (*pf)(void); // Pointeur de fonction renvoyant
// un entier.
pf p=&test::get; // Initialisation licite, car get
// est statique.
int main(void)
{
cout i; // On va générer une des trois exceptions
// possibles.
cout s2.Clef)
return s1;
else
return s2;
}
Note : Pour quelques compilateurs, la ligne déclarant la liste vide des paramètres template ne doit pas être écrite. On doit donc faire des spécialisations totale sans le mot clé template. Ce comportement n'est pas celui spécifié par la norme, et le code écrit pour ces compilateurs n'est donc pas portable.
12.5.2. Spécialisation partielle
Les spécialisations partielles permettent de définir l'implémentation d'une fonction ou d'une classe template pour certaines valeurs de leurs paramètres template et de garder d'autres paramètres indéfinis. Il est même possible de changer la nature d'un paramètre template (c'est-à-dire préciser s'il s'agit d'un pointeur ou non) et de forcer le compilateur à prendre une implémentation plutôt qu'une autre selon que la valeur utilisée pour ce paramètre est elle-même un pointeur ou non.
Comme pour les spécialisations totales, il est nécessaire de déclarer la liste des paramètres template utilisés par la spécialisation. Cependant, à la différence des spécialisations totales, cette liste ne peut plus être vide.
Comme pour les spécialisations totales, la définition de la classe ou de la fonction template doit utiliser les signes d'infériorité et de supériorité pour donner la liste des valeurs des paramètres template pour la spécialisation.
Exemple 12-13. Spécialisation partielle
// Définition d'une classe template :
template
class A
{
};
// Spécialisation n°1 de la classe :
template
class A
{
};
// Spécialisation n°2 de la classe :
template
class A
{
};
// Spécialisation n°3 de la classe :
template
class A
{
};
// Spécialisation n°4 de la classe :
template
class A
{
};On notera que le nombre des paramètres template déclarés à la suite du mot clé template peut varier, mais que le nombre de valeurs fournies pour la spécialisation est toujours constant (dans l'exemple précédent, il y en a trois).
Les valeurs utilisées dans les identificateurs template des spécialisations doivent respecter les règles suivantes :
une valeur ne peut pas être exprimée en fonction d'un paramètre template de la spécialisation ;
template
struct B
{
};
template
struct B // Erreur !
{ // Spécialisation incorrecte !
};le type d'une des valeurs de la spécialisation ne peut pas dépendre d'un autre paramètre ;
template
struct C
{
};
template
struct C; // Erreur !
// Spécialisation incorrecte !
la liste des arguments de la spécialisation ne doit pas être identique à la liste implicite de la déclaration template correspondante.
Enfin, la liste des paramètres template de la déclaration d'une spécialisation ne doit pas contenir des valeurs par défaut. On ne pourrait d'ailleurs les utiliser en aucune manière.
12.5.3. Spécialisation d'une méthode d'une classe template
La spécialisation partielle d'une classe peut parfois être assez lourde à employer, en particulier si la structure de données qu'elle contient ne change pas entre les versions spécialisées. Dans ce cas, il peut être plus simple de ne spécialiser que certaines méthodes de la classe et non la classe complète. Cela permet de conserver la définition des méthodes qui n'ont pas lieu d'être modifiées pour les différents types, et d'éviter d'avoir à redéfinir les données membres de la classe à l'identique.
La syntaxe permettant de spécialiser une méthode d'une classe template est très simple. Il suffit en effet de considérer la méthode comme une fonction template normale, et de la spécialiser en précisant les paramètres template à utiliser pour cette spécialisation.
Exemple 12-14. Spécialisation de fonction membre de classe template
#include
using namespace std;
template
class Item
{
T item;
public:
Item(T);
void set(T);
T get(void) const;
void print(void) const;
};
template
Item::Item(T i) // Constructeur
{
item = i;
}
// Accesseurs :
template
void Item::set(T i)
{
item = i;
}
template
T Item::get(void) const
{
return item;
}
// Fonction d'affichage générique :
template
void Item::print(void) const
{
cout = c2" 0) cout >(
basic_istream &flux, Personne &p)
{
// Inialisation du flux de sortie :
typename basic_istream::sentry init(flux);
if (init)
{
// Lecture du prénom et du nom :
flux >> p.Prenom;
flux >> p.Nom;
// Lecture de l'âge :
flux >> p.Age;
// Lecture de la taille en mètres :
double Taille;
flux >> Taille;
// Conversion en centimètres ;
p.Taille = (int) (Taille * 100 + 0.5);
}
return flux;
}
int main(void)
{
// Construit une nouvelle personne :
Personne p;
// Demande la saisie d'une personne :
cout > p;
// Affiche les valeurs lues :
cout > S;
// Récupère la facette ctype de la locale courante :
const ctype &f =
use_facet(locale());
// Construit un tampon pour recevoir le résultat de la conversion :
size_t l = S.length() + 1;
char *tampon = new char[l];
// Effectue la conversion :
f.narrow(S.c_str(), S.c_str() + l, 'E', tampon);
// Affiche le résultat :
cout (locale());
// Affiche la longueur de la chaîne d'entrée :
int l1 = s.length();
// Calcule la longueur de la ligne en wchar_t :
mbstate_t etat = mbstate_t();
int l2 = f.length(etat, s.c_str(), s.c_str() + l1,
numeric_limits::max());
// Affiche les deux longueurs :
cout des flux d'entrée / sortie pour les types de données de base du langage. En général, ces opérateurs récupèrent la locale incluse dans le flux d'entrée sur lequel ils travaillent et utilisent la facette num_get de cette locale. Ils appellent alors la méthode get permettant de lire la donnée qu'ils doivent extraire, en fournissant ce même flux en paramètre. Ils testent ensuite la variable d'état retournée par la méthode get et, si une erreur s'est produite, modifient l'état du flux d'entrée en conséquence. Cette dernière opération peut bien entendu provoquer le lancement d'une exception, selon le masque d'exceptions utilisé pour le flux.
16.2.5. Les facettes de gestion des monnaies
16.2.5.1. La facette money_punct
La facette moneypunct est la facette permettant aux deux facettes d'écriture et de lecture des montants d'obtenir les informations relatives à la monnaie de leur locale. Cette facette est déclarée comme suit dans l'en-tête locale :
template
class moneypunct : public locale::facet, public money_base
{
public:
// Les types de données :
typedef charT char_type;
typedef basic_string string_type;
// Le constructeur :
explicit moneypunct(size_t refs = 0);
// Les méthodes de lecture des options de formatage des montants :
charT decimal_point() const;
charT thousands_sep() const;
string grouping() const;
int frac_digits() const;
string_type curr_symbol() const;
pattern pos_format() const;
pattern neg_format() const;
string_type positive_sign() const;
string_type negative_sign() const;
static const bool intl = International;
// L'identificateur de la facette :
static locale::id id;
};
Note : Les méthodes virtuelles d'implémentation des méthodes publiques n'ont pas été écrites dans la déclaration précédente par souci de simplification. Elles existent malgré tout, et peuvent être redéfinies par les classes dérivées afin de personnaliser le comportement de la facette. Comme vous pouvez le constater, cette facette dispose de méthodes permettant de récupérer les divers symboles qui sont utilisés pour écrire les montants de la monnaie qu'elle décrit. Ainsi, la méthode decimal_point renvoie le caractère qui doit être utilisé en tant que séparateur du chiffre des unités de la partie fractionnaire des montants, si celle-ci doit être représentée. De même, la méthode thousands_sep renvoie le caractère qui doit être utilisé pour séparer les groupes de chiffres pour les grands montants, et la méthode grouping renvoie une chaîne contenant, dans chacun de ses caractères, le nombre de chiffres de chaque groupe. Ces méthodes sont donc semblables aux méthodes correspondantes de la facette numpunct. Le nombre de chiffres significatifs après la virgule utilisé pour cette monnaie peut être obtenue grâce à la méthode frac_digits. Ce n'est que si la valeur renvoyée par cette méthode est supérieure à 0 que le symbole de séparation des unités de la partie fractionnaire de la méthode decimal_point est utilisé.
La méthode curr_symbol permet d'obtenir le symbole monétaire de la monnaie. Ce symbole dépend de la valeur du paramètre template International. Si ce paramètre vaut true, le symbole monétaire renvoyé sera le symbole monétaire international. Dans le cas contraire, ce sera le symbole monétaire en usage dans le pays de circulation de la monnaie. La valeur du paramètre International pourra être obtenu grâce à la constante statique intl de la facette.
Les méthodes suivantes permettent de spécifier le format d'écriture des montants positifs et négatifs. Ces méthodes utilisent les définitions de constantes et de types de la classe de base money_base dont la facette moneypunct hérite. La classe money_base est déclarée comme suit dans l'en-tête locale :
class money_base
{
public:
enum part
{
none, space, symbol, sign, value
};
struct pattern
{
char field[4];
};
};Cette classe contient la définition d'une énumération dont les valeurs permettent d'identifier les différentes composantes d'un montant, ainsi qu'une structure pattern qui contient un tableau de quatre caractères. Chacun de ces caractères peut prendre l'une des valeurs de l'énumération part. La structure pattern définit donc l'ordre dans lequel les composantes d'un montant doivent apparaître. Ce sont des motifs de ce genre qui sont renvoyés par les méthodes pos_format et neg_format, qui permettent d'obtenir respectivement le format des montants positifs et celui des montants négatifs.
Les différentes valeurs que peuvent prendre les éléments du motif pattern représentent chacune une partie de l'expression d'un montant. La valeur value représente bien entendu la valeur de ce montant, sign son signe et symbol le symbole monétaire. La valeur space permet d'insérer un espace dans l'expression d'un montant, mais les espaces ne peuvent pas être utilisés en début et en fin de montants. Enfin, la valeur none permet de ne rien mettre à la position où il apparaît dans le motif.
La manière d'écrire les montants positifs et négatifs varie grandement selon les pays. En général, il est courant d'utiliser le signe '-' pour signaler un montant négatif et aucun signe distinctif pour les montants positifs. Cependant, certains pays écrivent les montants négatifs entre parenthèses et la marque des montants négatifs n'est donc plus un simple caractère. Les méthodes positive_sign et negative_sign permettent d'obtenir les symboles à utiliser pour noter les montants positifs et négatifs. Elles retournent toutes les deux une chaîne de caractères, dont le premier est placé systématiquement à l'emplacement auquel la valeur sign a été affectée dans la chaîne de format renvoyée par les méthodes pos_format et neg_format. Les caractères résiduels, s'ils existent, sont placés à la fin de l'expression du montant complètement formatée. Ainsi, dans les locales pour lesquelles les montants négatifs sont écrits entre parenthèses, la chaîne renvoyée par la méthode negative_sign est « () », et pour les locales utilisant simplement le signe négatif, cette chaîne ne contient que le caractère '-'.
16.2.5.2. Les facettes de lecture et d'écriture des montants
Les facettes d'écriture et de lecture des montants sont sans doute les facettes standards les plus simples. En effet, elles ne disposent que de méthodes permettant d'écrire et de lire les montants sur les flux. Ces facettes sont respectivement les facettes money_put et money_get. Elles sont définies comme suit dans l'en-tête locale :
template
class money_put : public locale::facet
{
public:
// Les types de données :
typedef charT char_type;
typedef OutputIterator iter_type;
typedef basic_string string_type;
// Le constructeur :
explicit money_put(size_t refs = 0);
// Les méthodes d'écriture des montants :
iter_type put(iter_type s, bool intl, ios_base &f,
char_type remplissage, long double units) const;
iter_type put(iter_type s, bool intl, ios_base &f,
char_type remplissage, const string_type &digits) const;
// L'identificateur de la facette :
static locale::id id;
};
template
class money_get : public locale::facet
{
public:
// Les types de données :
typedef charT char_type;
typedef InputIterator iter_type;
typedef basic_string string_type;
// Le constructeur :
explicit money_get(size_t refs = 0);
// Les méthodes de lecture des montants :
iter_type get(iter_type s, iter_type end, bool intl,
ios_base &f, ios_base::iostate &err,
long double &units) const;
iter_type get(iter_type s, iter_type end, bool intl,
ios_base &f, ios_base::iostate &err,
string_type &digits) const;
static const bool intl = Intl;
// L'identificateur de la facette :
static locale::id id;
};
Note : Les méthodes virtuelles d'implémentation des méthodes publiques n'ont pas été écrites dans la déclaration précédente par souci de simplification. Elles existent malgré tout, et peuvent être redéfinies par les classes dérivées afin de personnaliser le comportement de la facette. Comme vous pouvez le constater, les méthodes d'écriture et de lecture put et get de ces facettes sont semblables aux méthodes correspondantes des facettes de gestion des nombres. Toutefois, elles se distinguent par un paramètre booléen complémentaire qui permet d'indiquer si les opérations de formatage doivent se faire en utilisant les conventions internationales d'écriture des montants. Les autres paramètres ont la même signification que pour les méthodes put et get des facettes de gestion des nombres. En particulier, l'itérateur fourni indique l'emplacement où les données doivent être écrites ou lues, et le flux d'entrée / sortie spécifié permet de récupérer les options de formatage des montants. L'une des options les plus utiles est sans doute l'option qui permet d'afficher la base des nombres, car, dans le cas des facettes de gestion des montants, elle permet d'activer ou non l'écriture du symbole monétaire. Enfin, les méthodes put et get sont fournies en deux exemplaires, un pour chaque type de donnée utilisable pour représenter les montants, à savoir les double et les chaînes de caractères.
16.2.6. Les facettes de gestion du temps
La bibliothèque standard ne fournit que deux facettes pour l'écriture et la lecture des dates : la facette time_put et la facette time_get. Ces deux facettes utilisent le type de base struct tm de la bibliothèque C pour représenter le temps. Bien que ce document ne décrive pas les fonctions de la bibliothèque C, il est peut-être bon de rappeler comment les programmes C manipulent les dates en général.
La gestion du temps dans un programme peut très vite devenir un véritable cauchemar, principalement en raison de la complexité que les êtres humains se sont efforcés de développer dans leur manière de représenter le temps. En effet, il faut tenir compte non seulement des spécificités des calendriers (années bissextiles ou non par exemple), mais aussi des multiples bases de numération utilisées dans l'écriture des dates (24 heures par jour, 60 minutes par heure et 60 secondes par minutes, puis 10 dixièmes dans une seconde et ainsi de suite) et des conventions locales de gestion des heures (fuseau horaire, heure d'été et d'hiver). La règle d'or lors de la manipulation des dates est donc de toujours travailler dans un référentiel unique avec une représentation linéaire du temps, autrement dit, de simplifier tout cela. En pratique, cela revient à dire que les programmes doivent utiliser une représentation linéaire du temps (généralement, le nombre de secondes écoulées depuis une date de référence) et travailler en temps universel. De même, le stockage des dates doit être fait dans ce format afin de garantir la possibilité d'échanger les données sans pour autant laisser la place aux erreurs d'interprétation de ces dates.
En pratique, la bibliothèque C utilise le type time_t. Les valeurs de ce type représentent le nombre d'instants écoulés depuis le premier janvier 1970 à 0 heure (date considérée comme le début de l'ère informatique par les inventeurs du langage C, Kernighan et Ritchie, et que l'on appelle couramment « Epoch »). La durée de ces instants n'est pas normalisée par la bibliothèque C, mais il s'agit de secondes pour les systèmes POSIX. Le type time_t permet donc de réaliser des calculs simplement sur les dates. Les dates représentées avec des time_t sont toujours exprimées en temps universel.
Bien entendu, il existe des fonctions permettant de convertir les dates codées sous la forme de time_t en dates humaines et réciproquement. Le type de donnée utilisé pour stocker les dates au format humain est la structure struct tm. Cette structure contient plusieurs champs, qui représentent entre autres l'année, le jour, le mois, les heures, les minutes et les secondes. Ce type contient donc les dates au format éclaté et permet d'obtenir les différentes composantes d'une date.
Généralement, les dates sont formatées en temps local, car les utilisateurs désirent souvent avoir les dates affichées dans leur propre base de temps. Cependant, il est également possible de formater les dates en temps universel. Ces opérations de formatages sont réalisées par les bibliothèques C et C++, et les programmes n'ont donc pas à se soucier des paramètres de fuseaux horaires, d'heure d'été et d'hiver et des conventions locales d'écriture des dates : tout est pris en charge par les locales.
Les principales fonctions permettant de manipuler les dates sont récapitulées dans le tableau ci-dessous :
Tableau 16-1. Fonctions C de gestion des dates
FonctionDescriptiontime_t time(time_t *)Permet d'obtenir la date courante. Peut être appelée avec l'adresse d'une variable de type time_t en paramètre ou avec la constante NULL. Initialise la variable passée par pointeur avec la date courante, et renvoie également la valeur écrite. Permet d'obtenir la date courante. Peut être appelée avec l'adresse d'une variable de type time_t en paramètre ou avec la constante NULL. Initialise la variable passée par pointeur avec la date courante, et renvoie également la valeur écrite. struct tm *gmtime(const time_t *)Permet de convertir une date stockée dans une variable de type time_t en sa version éclatée en temps universel. Le pointeur renvoyé référence une structure allouée en zone statique par la bibliothèque C et ne doit pas être libéré. struct tm *localtime(const time_t *)Permet de convertir une date stockée dans une variable de type time_t en sa version éclatée en temps local. Le pointeur renvoyé référence une structure allouée en zone statique par la bibliothèque C et ne doit pas être libéré. time_t mktime(struct tm *)Permet de construire une date de type time_t à partir d'une date en temps local stockée dans une structure struct tm. Les données membres de la structure struct tm peuvent être corrigées par la fonction mktime si besoin est. Cette fonction est donc la fonction inverse de localtime.Fonction Description time_t time(time_t *) Permet d'obtenir la date courante. Peut être appelée avec l'adresse d'une variable de type time_t en paramètre ou avec la constante NULL. Initialise la variable passée par pointeur avec la date courante, et renvoie également la valeur écrite. struct tm *gmtime(const time_t *) Permet de convertir une date stockée dans une variable de type time_t en sa version éclatée en temps universel. Le pointeur renvoyé référence une structure allouée en zone statique par la bibliothèque C et ne doit pas être libéré. struct tm *localtime(const time_t *) Permet de convertir une date stockée dans une variable de type time_t en sa version éclatée en temps local. Le pointeur renvoyé référence une structure allouée en zone statique par la bibliothèque C et ne doit pas être libéré. time_t mktime(struct tm *) Permet de construire une date de type time_t à partir d'une date en temps local stockée dans une structure struct tm. Les données membres de la structure struct tm peuvent être corrigées par la fonction mktime si besoin est. Cette fonction est donc la fonction inverse de localtime. size_t strftime(char *tampon, size_t max, const char *format, constr struct tm *t) Permet de formater une date stockée dans une structure struct tm dans une chaîne de caractères. Cette chaîne doit être fournie en premier paramètre, ainsi que le nombre maximal de caractères que la fonction pourra écrire. La fonction renvoie le nombre de caractères écrits ou, si le premier paramètre est nul, la taille de la chaîne de caractères qu'il faudrait pour effectuer une écriture complète. La fonction strftime prend en paramètre une chaîne de format et fonctionne de manière similaire aux fonctions printf et sprintf. Elle comprend un grand nombre de formats, mais les plus utiles sont sans doute les formats « %X » et « %x », qui permettent respectivement de formater l'heure et la date selon les conventions de la locale du programme.
Note : Il n'existe pas de fonction permettant de convertir une date exprimée en temps universel et stockée dans une structure struct tm en une date de type time_t. De même, la bibliothèque C ne fournit pas de fonction permettant d'analyser une chaîne de caractères représentant une date. Cependant, la norme Unix 98 définit la fonction strptime, qui est la fonction inverse de la fonction strftime. Les fonctions localtime et gmtime ne sont pas sûres dans un environnement multithreadé. En effet, la zone de mémoire renvoyée est en zone statique et est partagée par tous les threads. La bibliothèque C définit donc deux fonctions complémentaires, localtime_r et gmtime_r, qui prennent un paramètre complémentaire qui doit recevoir un pointeur sur la structure struct tm dans lequel le résultat doit être écrit. Cette structure est allouée par le thread appelant et ne risque donc pas d'être détruite par un appel à la même fonction par un autre thread.
Les facettes de la bibliothèque standard C++ ne permettent pas de manipuler les dates en soi. Elles ne sont destinées qu'à réaliser le formatage des dates en tenant compte des spécificités de représentation des dates de la locale. Elles se comportent exactement comme la fonction strftime le fait lorsque l'on utilise les chaînes de format « %X » et « %x ».
16.2.6.1. La facette d'écriture des dates
La facette d'écriture des dates est déclarée comme suit dans l'en-tête locale :
template
class time_put : public locale::facet
{
public:
// Les types de données :
typedef charT char_type;
typedef OutputIterator iter_type;
// Le constructeur :
explicit time_put(size_t refs = 0);
// Les méthodes d'écriture des dates :
iter_type put(iter_type s, ios_base &f, char_type remplissage, const tm *t,
char format, char modificateur = 0) const;
iter_type put(iter_type s, ios_base &f, char_type remplissage, const tm *t,
const charT *debut_format, const charT *fin_format) const;
// L'identificateur de la facette :
static locale::id id;
};
Note : Les méthodes virtuelles d'implémentation des méthodes publiques n'ont pas été écrites dans la déclaration précédente par souci de simplification. Elles existent malgré tout, et peuvent être redéfinies par les classes dérivées afin de personnaliser le comportement de la facette. Cette facette dispose de deux surcharges de la méthode put permettant d'écrire une date sur un flux de sortie. La première permet d'écrire une date sur le flux de sortie dont un itérateur est donné en premier paramètre. Le formatage de la date se fait comme avec la fonction strftime de la bibliothèque C. Le paramètre modificateur ne doit pas être utilisé en général, sa signification n'étant pas précisée par la norme C++. La deuxième forme de la méthode put réalise également une écriture sur le flux, en prenant comme chaîne de format la première sous-chaîne commençant par le caractère '%' dans la chaîne indiquée par les paramètres debut_format et fin_format.
16.2.6.2. La facette de lecture des dates
La facette de lecture des dates permet de lire les dates dans le même format que celui utilisé par la fonction strftime de la bibliothèque C lorsque la chaîne de format vaut « %X » ou « %x ». Cette facette est déclarée comme suit dans l'en-tête locale :
template
class time_get : public locale::facet, public time_base
{
public:
// Les types de données :
typedef charT char_type;
typedef InputIterator iter_type;
// Le constructeur :
explicit time_get(size_t refs = 0);
// Les méthodes de gestion de la lecture des dates :
iter_type get_time(iter_type s, iter_type end, ios_base &f,
ios_base::iostate &err, tm *t) const;
iter_type get_date(iter_type s, iter_type end, ios_base &f,
ios_base::iostate &err, tm *t) const;
iter_type get_weekday(iter_type s, iter_type end, ios_base &f,
ios_base::iostate &err, tm *t) const;
iter_type get_monthname(iter_type s, iter_type end, ios_base &f,
ios_base::iostate &err, tm *t) const;
iter_type get_year(iter_type s, iter_type end, ios_base &f,
ios_base::iostate &err, tm *t) const;
dateorder date_order() const;
// L'identificateur de la facette :
static locale::id id;
};
Note : Les méthodes virtuelles d'implémentation des méthodes publiques n'ont pas été écrites dans la déclaration précédente par souci de simplification. Elles existent malgré tout, et peuvent être redéfinies par les classes dérivées afin de personnaliser le comportement de la facette. Les différentes méthodes de cette facette permettent respectivement d'obtenir l'heure, la date, le jour de la semaine, le nom du mois et l'année d'une date dans le flux d'entrée spécifié par l'itérateur fourni en premier paramètre. Toutes ces données sont interprétées en fonction de la locale à laquelle la facette appartient.
Enfin, la méthode date_order permet d'obtenir l'une des valeurs de l'énumération définie dans la classe de base time_base et qui indique l'ordre dans lequel les composants jour / mois / année des dates apparaissent dans la locale de la facette. La classe de base time_base est déclarée comme suit dans l'en-tête locale :
class time_base
{
public:
enum dateorder
{
no_order, dmy, mdy, ymd, ydm
};
};La signification des différentes valeurs de l'énumération est immédiate. La seule valeur nécessitant des explications complémentaires est la valeur no_order. Cette valeur est renvoyée par la méthode date_order si le format de date utilisé par la locale de la facette contient d'autres champs que le jour, le mois et l'année.
Note : La méthode date_order est fournie uniquement à titre de facilité par la bibliothèque standard. Elle peut ne pas être implémentée pour certaines locales. Dans ce cas, elle renvoie systématiquement la valeur no_order.
16.2.7. Les facettes de gestion des messages
Afin de faciliter l'internationalisation des programmes, la bibliothèque standard fournit la facette messages, qui permet de prendre en charge la traduction de tous les messages d'un programme de manière indépendante du système sous-jacent. Cette facette permet d'externaliser tous les messages des programmes dans des fichiers de messages que l'on appelle des catalogues. Le format et l'emplacement de ces fichiers ne sont pas spécifiés par la norme C++, cependant, la manière d'y accéder est standardisée et permet d'écrire des programmes portables. Ainsi, lorsqu'un programme devra être traduit, il suffira de traduire les messages stockés dans les fichiers de catalogue pour chaque langue et de les distribuer avec le programme.
Note : La manière de créer et d'installer ces fichiers étant spécifique à chaque implémentation de la bibliothèque standard et, dans une large mesure, spécifique au système d'exploitation utilisé, ces fichiers ne seront pas décrits ici. Seule la manière d'utiliser la facette messages sera donc indiquée. Reportez-vous à la documentation de votre environnement de développement pour plus de détails sur les outils permettant de générer les fichiers de catalogue. La facette messages référence les fichiers de catalogue à l'aide d'un type de donnée spécifique. Ce type de donnée est défini dans la classe de base messages_base comme étant un type intégral :
class messages_base
{
public:
typedef int catalog;
};La classe template messages de gestion de la facette hérite donc de cette classe de base et utilise le type catalog pour identifier les fichiers de catalogue de l'application.
La classe messages est déclarée comme suit dans l'en-tête locale :
template
class messages : public locale::facet, public messages_base
{
public:
// Les types de données :
typedef charT char_type;
typedef basic_string string_type;
// Le constructeur :
explicit messages(size_t refs = 0);
// Les méthodes de gestion des catalogues de messages :
catalog open(const basic_string &nom, const locale &l) const;
void close(catalog c) const;
string_type get(catalog c, int groupe, int msg,
const string_type &defaut) const;
// L'identificateur de la facette :
static locale::id id;
};
Note : Les méthodes virtuelles d'implémentation des méthodes publiques n'ont pas été écrites dans la déclaration précédente par souci de simplification. Elles existent malgré tout, et peuvent être redéfinies par les classes dérivées afin de personnaliser le comportement de la facette. Les principales méthodes de gestion des catalogues sont les méthodes open et close. Comme leurs noms l'indiquent, ces méthodes permettent d'ouvrir un nouveau fichier de catalogue et de le fermer pour en libérer les ressources. La méthode open prend en paramètre le nom du catalogue à ouvrir. Ce nom doit identifier de manière unique le catalogue, mais la norme C++ n'indique pas comment il doit être interprété. Cela relève donc de l'implémentation de la bibliothèque standard utilisée. Toutefois, en pratique, il est probable qu'il s'agit d'un nom de fichier. Le deuxième paramètre permet d'indiquer la locale à utiliser pour effectuer les conversions de jeux de caractères si cela est nécessaire. Il permet donc de laisser au programmeur le choix du jeu de caractères dans lequel les messages seront écrits dans le catalogue. La valeur renvoyée par la méthode open est l'identifiant du catalogue, identifiant qui devra être fourni à la méthode get pour récupérer les messages du catalogue et à la méthode close pour fermer le fichier de catalogue. Si l'ouverture du fichier n'a pas pu être effectuée, la méthode open renvoie une valeur inférieure à 0.
Les messages du catalogue peuvent être récupérés à l'aide de la méthode get. Cette méthode prend en paramètre l'identifiant d'un catalogue précédemment obtenu par l'intermédiaire de la méthode open, un identifiant de groupe de message et un identifiant d'un message. Le dernier paramètre doit recevoir la valeur par défaut du message en cas d'échec de la recherche du message dans le catalogue. Cette valeur par défaut est souvent un message en anglais, ce qui permet au programme de fonctionner correctement même lorsque ses fichiers de catalogue sont vides.
La manière dont les messages sont identifiés n'est pas spécifiée par la norme C++, tout comme la manière dont ils sont classés en groupes de messages au sein d'un même fichier de catalogue. Cela relève donc de l'implémentation de la bibliothèque utilisée. Consultez la documentation de votre environnement de développement pour plus de détails à ce sujet.
Note : Cette facette est relativement peu utilisée, pour plusieurs raison. Premièrement, peu d'environnements C++ respectent la norme C++ à ce jour. Deuxièmement, les systèmes d'exploitation disposent souvent de mécanismes de localisation performants et pratiques. Enfin, l'identification d'un message par des valeurs numériques n'est pas toujours pratique et il est courant d'utiliser le message par défaut, souvent en anglais, comme clef de recherche pour les messages internationaux. Cette manière de procéder est en effet beaucoup plus simple, puisque le contenu des messages est écrit en clair dans la langue par défaut dans les fichiers sources du programme.
XIX-C. 16.3. Personnalisation des mécanismes de localisation
Les mécanismes de localisation ont été conçus de telle sorte que le programmeur peut, s'il le désire (et s'il en a réellement le besoin), personnaliser leur fonctionnement. Ainsi, il est parfaitement possible de définir de nouvelles facettes, par exemple pour permettre la localisation des types de données complémentaires définis par le programme. De même, il est possible de redéfinir les méthodes virtuelles des classes de gestion des facettes standards de la bibliothèque et de remplacer les facettes originales par des facettes personnalisées. Cependant, il faut bien reconnaître que la manière de procéder n'est pas très pratique, et en fait les mécanismes internes de gestion des facettes semblent être réservés aux classes et aux méthodes de la bibliothèque standard elle-même.
16.3.1. Création et intégration d'une nouvelle facette
Comme il l'a été expliqué dans la Section 16.1, une facette n'est rien d'autre qu'une classe dérivant de la classe locale::facet et contenant une donnée membre statique id. Cette donnée membre est utilisée par les classes de locale pour identifier le type de la facette et pour l'intégrer dans le mécanisme de gestion des facettes standards.
L'exemple suivant montre comment on peut réaliser deux facettes permettant d'encapsuler les spécificités d'un type de donnée défini par le programme, le type answer_t. Ce type est supposé permettre la création de variables contenant la réponse de l'utilisateur à une question. Ce n'est rien d'autre qu'une énumération contenant les valeurs no (pour la réponse négative), yes (pour l'affirmative), all (pour répondre par l'affirmative pour tout un ensemble d'éléments) et none (pour répondre par la négative pour tout un ensemble d'éléments).
Dans cet exemple, deux facettes sont définies : la facette answerpunct, qui prend en charge la localisation des noms des différentes valeurs de l'énumération answer_t, et la facette answer_put, qui prend en charge le formatage des valeurs de cette énumération dans un flux standard. L'opérateur operator