delc++ 2006 12/09/07 11:00 page 1 claude delannoy pour les...
TRANSCRIPT
Acqueacuterir une parfaite maicirctrise du C++ et de la programmation objet
C++ pour les programmeurs C est la reacuteeacutedition avec un nouveau titre mieux adapteacute au public viseacute du grand classique deClaude Delannoy Programmer en C++ qui srsquoest imposeacute au fil de ses six eacuteditions successives comme la reacutefeacuterence en languefranccedilaise sur ce langage Destineacute aux programmeurs C souhaitant migrer vers le C++ lrsquoouvrage insiste tout particuliegraverement sur la bonne compreacutehen-sion des concepts objet et sur lacquisition de meacutethodes de programmation rigoureuses Entiegraverement fondeacute sur la norme ANSIISO lrsquoouvrage couvre tous les aspects du langage et de sa bibliothegraveque standard (STLou Standard Template Library) et traite en profondeur des points les plus deacutelicats auxquels est confronteacute un programmeurC++ lors de la creacuteation de ses propres classes et de la conception drsquoapplications professionnellesChaque notion nouvelle et chaque fonction du langage sont illustreacutees de programmes complets dont le code source est fournisur le site wwweditions-eyrollescom Tout au long de lrsquoouvrage des notes soulignent les diffeacuterences majeures entre le C++et Java de maniegravere agrave eacutetablir des passerelles entre les deux langages Un autre ouvrage du mecircme auteur conccedilu pour les programmeurs issus drsquoautres environnements que le C est publieacute simultaneacutement aux Eacuteditions Eyrolles sous letitre Apprendre le C++
Au sommaireC++ et programmation orienteacutee objet bull Points communs et diffeacuterences entre C et C++ bull Les entreacutees-sorties conversationnelles duC ++ bull Les speacutecificiteacutes du C ++ bull Classes et objets bull Les proprieacuteteacutes des fonctions membres bull Construction destruction et initialisa-tion des objets bull Les fonctions amies bull La surdeacutefinition dopeacuterateurs bull Les conversions de type deacutefinies par lutilisateur bull Lespatrons de fonctions bull Les patrons de classes bull Lheacuteritage simple bull Lheacuteritage multiple bull Les fonctions virtuelles et le polymorphis-me bull Les flots bull La gestion des exceptions bull Geacuteneacuteraliteacutes sur la bibliothegraveque standard (STL) bull Les conteneurs seacutequentiels bull Les conte-neurs associatifs bull Les algorithmes standards bull La classe string bull Les outils numeacuteriques bull Les espaces de noms Annexes Mise encorrespondance drsquoarguments bull Utilisation de code eacutecrit en C bull Compleacutements sur les exceptions bull Les diffeacuterents types de fonctionsen C++ bull Comptage de reacutefeacuterences bull Les pointeurs sur des membres bull Les algorithmes standards bull Corrigeacute des exercices
Sur le site wwweditions-eyrollescom
bull Dialoguez avec lrsquoauteurbull Teacuteleacutechargez le code source des exemples du livre
Code
eacutedite
ur
G122
31 bull
ISBN
-13
978
-2-2
12-1
2231
-2
32 euro
C++p
our
les
prog
ram
meu
rsC
C D
elann
oy
Claude DelannoyIngeacutenieur informaticien au CNRS Claude Delannoy possegravede une grande pratique de la formationcontinue et de lrsquoenseignement supeacuterieur Reacuteputeacutes pour la qualiteacute de leur deacutemarche peacutedagogique ses ouvrages sur les langages et la programmation totalisent plus de 250 000 exemplaires vendus
97
82
21
21
22
31
2
Claude Delannoy
C++pour les
programmeursCC++
pour lesprogrammeursC
delC++ 2006 120907 1100 Page 1
C++pour les
programmeursC
delannoy C++ titre 30907 2041 Page 2
CHEZ LE MEcircME EacuteDITEUR
Du mecircme auteur
C Delannoy ndash Exercices en langage C++ Ndeg12201 3e eacutedition 2007 336 pages
C Delannoy ndash Apprendre le C++ Ndeg12135 2007 760 pages
C Delannoy ndash Programmer en Java (Java 5 et 6) Ndeg12232 5e eacutedition 780 pages + CD-Rom
C Delannoy ndash Exercices en Java (Java 5) Ndeg11989 2e eacutedition 2006 330 pages
C Delannoy ndash Langage C Ndeg11123 1998 944 pages (reacuteeacutedition au format semi-poche)
C Delannoy ndash Programmer en langage C Avec exercices corrigeacutes Ndeg11072 1996 280 pages
C Delannoy ndash Exercices en langage C Ndeg11105 1997 260 pages
Autres ouvrages dans la mecircme collection
P Roques ndash UML 2 par la pratique Cours et exercices Ndeg12014 5e eacutedition 2006 360 pages
X Blanc I MounIeR ndash UML 2 pour les deacuteveloppeurs Cours et exercices corrigeacutes Ndeg12029 2006 218 pages
H BeRsInI I Wellesz ndash Lrsquoorienteacute objet Cours et exercices en UML 2 avec PHP Java Python C et C++ Ndeg12084 3e eacutedition 2007 520 pages
J engels ndash XHTML et CSS cours et exercices Ndeg11637 2005 350 pages
J engels ndash PHP 5 cours et exercicesNdeg11407 2005 518 pages
Autres ouvrages
I HoRton ndash Visual C++ 6 Avec un CD-Rom contenant le produit Microsoft Visual C++ 6 Introductory Edition Ndeg9043 1999 1 250 pages
G leBlanc ndash C et NET 20 Ndeg11778 2006 700 pages
E DasPet et C PIeRRe de geyeR ndash PHP 5 avanceacute Ndeg12167 4e eacutedition 2007 780 pages
a goncalves ndash Cahier du programmeur Java EE5 Ndeg12038 2007 330 pages
c PoRteneuve ndash Bien deacutevelopper pour le Web 20 Ndeg12028 2006 560 pages
Claude Delannoy
C++pour les
programmeursC
delannoy C++ titre 30907 2041 Page 1
EacuteDITIONS EYROLLES61 bd Saint-Germain75240 Paris Cedex 05
wwweditions-eyrollescom
Le code de la proprieacuteteacute intellectuelle du 1er juillet 1992 interdit en effet expresseacutement la photocopie agrave usage collectif sans autorisation des ayants droit Or cette pratique srsquoest geacuteneacuteraliseacutee notamment dans les eacutetablissements drsquoenseignement provoquant une baisse brutale des achats de livres au point que la possibiliteacute mecircme pour les auteurs de creacuteer des œuvres nouvelles et de les faire eacutediter correctement est aujourdrsquohui menaceacuteeEn application de la loi du 11 mars 1957 il est interdit de reproduire inteacutegralement ou partiellement le
preacutesent ouvrage sur quelque support que ce soit sans autorisation de lrsquoeacutediteur ou du Centre Franccedilais drsquoExploitation du Droit de Copie 20 rue des Grands-Augustins 75006 Pariscopy Groupe Eyrolles 1993-2004 pour le texte de la preacutesente eacuteditioncopy Groupe Eyrolles 2007 pour la nouvelle preacutesentation (nouveau titre) ISBN 978-2-212-12231-2
6e eacutedition 2004 2e tirage 2007 avec nouvelle preacutesentation
Deacutepocirct leacutegal Septembre 2007Ndeg drsquoeacutediteur 7703
Table des matiegraveres
Avant-propos XXIII
Chapitre 1 Geacuteneacuteraliteacutes concernant C++ 1
1 - La Programmation Orienteacutee Objet 1
11 Probleacutematique de la programmation 1
12 La programmation structureacutee 2
13 Les apports de la Programmation Orienteacutee Objet 2
131 Objet 2
132 Encapsulation 3
133 Classe 3
134 Heacuteritage 3
135 Polymorphisme 4
14 POO et langages 4
2 - C++ C ANSI et POO 5
3 - Les speacutecificiteacutes de C++ 5
4 - C++ et la programmation orienteacutee objet 6
Chapitre 2 Les incompatibiliteacutes entre C++ et C 9
1 - Les deacutefinitions de fonctions en C++ 10
2 - Les prototypes en C++ 10
3 - Arguments et valeur de retour drsquoune fonction 13
31 Points communs agrave C et C++ 13
C++ pour programmeurs CVI
32 Diffeacuterences entre C et C++ 13
321 Fonctions sans arguments 14
322 Fonctions sans valeur de retour 14
4 - Le qualificatif const 14
41 Porteacutee 14
42 Utilisation dans une expression 15
5 - Compatibiliteacute entre le type void et les autres pointeurs 16
Chapitre 3 Les entreacutees-sorties conversationnelles du C++ 17
1 - Geacuteneacuteraliteacutes 17
2 - Affichage agrave lrsquoeacutecran 18
21 Quelques exemples 18
22 Le fichier en-tecircte iostream 20
23 Les possibiliteacutes drsquoeacutecriture sur cout 20
3 - Lecture au clavier 21
31 Introduction 21
32 Les diffeacuterentes possibiliteacutes de lecture sur cin 22
33 Exemple classique drsquoutilisation des seacuteparateurs 23
34 Lecture drsquoune suite de caractegraveres 23
35 Les risques induits par la lecture au clavier 24
351 Manque de synchronisme entre clavier et eacutecran 24
352 Blocage de la lecture 25
353 Boucle infinie sur un caractegravere invalide 25
Chapitre 4 Les speacutecificiteacutes du C++ 27
1 - Le commentaire de fin de ligne 28
2 - Deacuteclarations et initialisations 28
21 Regravegles geacuteneacuterales 28
22 Cas des instructions structureacutees 29
3 - La notion de reacutefeacuterence 30
31 Transmission des arguments en C 30
32 Exemple de transmission drsquoargument par reacutefeacuterence 31
33 Proprieacuteteacutes de la transmission par reacutefeacuterence drsquoun argument 33
331 Induction de risques indirects 33
332 Absence de conversion 34
333 Cas drsquoun argument effectif constant 34
334 Cas drsquoun argument muet constant 35
34 Transmission par reacutefeacuterence drsquoune valeur de retour 35
341 Introduction 35
342 On obtient une lvalue 36
343 Conversion 36
344 Valeur de retour et constance 37
Table des matiegraveresVII
35 La reacutefeacuterence dune maniegravere geacuteneacuterale 37
351 La notion de reacutefeacuterence est plus geacuteneacuterale que celle drsquoargument 38
352 Initialisation de reacutefeacuterence 38
4 - Les arguments par deacutefaut 39
41 Exemples 39
42 Les proprieacuteteacutes des arguments par deacutefaut 41
5 - Surdeacutefinition de fonctions 42
51 Mise en œuvre de la surdeacutefinition de fonctions 42
52 Exemples de choix dune fonction surdeacutefinie 43
53 Regravegles de recherche dune fonction surdeacutefinie 46
531 Cas des fonctions agrave un argument 46
532 Cas des fonctions agrave plusieurs arguments 47
533 Le meacutecanisme de la surdeacutefinition de fonctions 47
6 - Les opeacuterateurs new et delete 49
61 Exemples dutilisation de new 49
62 Syntaxe et rocircle de new 50
63 Exemples dutilisation de lopeacuterateur delete 50
64 Syntaxe et rocircle de lopeacuterateur delete 51
65 Lrsquoopeacuterateur new (nothrow) 51
66 Gestion des deacutebordements de meacutemoire avec set_new_handler 52
7 - La speacutecification inline 53
71 Rappels concernant les macros et les fonctions 53
72 Utilisation de fonctions en ligne 54
8 - Les espaces de noms 56
9 - Le type bool 57
10 - Les nouveaux opeacuterateurs de cast 57
Chapitre 5 Classes et objets 61
1 - Les structures en C++ 62
11 Rappel les structures en C 62
12 Deacuteclaration dune structure comportant des fonctions membres 63
13 Deacutefinition des fonctions membres 64
14 Utilisation dune structure comportant des fonctions membres 65
15 Exemple reacutecapitulatif 66
2 - Notion de classe 67
3 - Affectation drsquoobjets 70
4 - Notions de constructeur et de destructeur 72
41 Introduction 72
42 Exemple de classe comportant un constructeur 73
43 Construction et destruction des objets 75
44 Rocircles du constructeur et du destructeur 76
45 Quelques regravegles 79
C++ pour programmeurs CVIII
5 - Les membres donneacutees statiques 80
51 Le qualificatif static pour un membre donneacutee 80
52 Initialisation des membres donneacutees statiques 81
53 Exemple 82
6 - Exploitation drsquoune classe 83
61 La classe comme composant logiciel 83
62 Protection contre les inclusions multiples 85
63 Cas des membres donneacutees statiques 86
64 En cas de modification drsquoune classe 86
641 La deacuteclaration des membres publics nrsquoa pas changeacute 86
642 La deacuteclaration des membres publics a changeacute 87
7 - Les classes en geacuteneacuteral 87
71 Les autres sortes de classes en C++ 87
72 Ce quon peut trouver dans la deacuteclaration dune classe 87
73 Deacuteclaration dune classe 88
Chapitre 6 Les proprieacuteteacutes des fonctions membres 91
1 - Surdeacutefinition des fonctions membres 91
11 Exemple 92
12 Incidence du statut public ou priveacute drsquoune fonction membre 93
2 - Arguments par deacutefaut 94
3 - Les fonctions membres en ligne 95
4 - Cas des objets transmis en argument drsquoune fonction membre 97
5 - Mode de transmission des objets en argument 99
51 Transmission de ladresse dun objet 99
52 Transmission par reacutefeacuterence 101
53 Les problegravemes poseacutes par la transmission par valeur 102
6 - Lorsqursquoune fonction renvoie un objet 102
7 - Autoreacutefeacuterence le mot cleacute this 103
8 - Les fonctions membres statiques 104
9 - Les fonctions membres constantes 106
91 Rappels sur lrsquoutilisation de const en C 106
92 Deacutefinition drsquoune fonction membre constante 107
93 Proprieacuteteacutes drsquoune fonction membre constante 107
10 - Les membres mutables 109
Chapitre 7 Construction destruction et initialisation des objets 111
1 - Les objets automatiques et statiques 112
11 Dureacutee de vie 112
12 Appel des constructeurs et des destructeurs 113
13 Exemple 113
Table des matiegraveresIX
2 - Les objets dynamiques 115
21 Les structures dynamiques 115
22 Les objets dynamiques 116
221 Points communs avec les structures dynamiques 116
222 Les nouvelles possibiliteacutes des opeacuterateurs new et delete 117
223 Exemple 117
3 - Le constructeur de recopie 118
31 Preacutesentation 118
311 Il nexiste pas de constructeur approprieacute 119
312 Il existe un constructeur approprieacute 119
313 Lorsqursquoon souhaite interdire la contruction par recopie 120
32 Exemple 1 objet transmis par valeur 121
321 Emploi du constructeur de recopie par deacutefaut 121
322 Deacutefinition dun constructeur de recopie 123
33 Exemple 2 objet en valeur de retour dune fonction 125
4 - Initialisation dun objet lors de sa deacuteclaration 127
5 - Objets membres 129
51 Introduction 129
52 Mise en œuvre des constructeurs et des destructeurs 129
53 Le constructeur de recopie 132
6 - Initialisation de membres dans lrsquoen-tecircte drsquoun constructeur 132
7 - Les tableaux drsquoobjets 133
71 Notations 133
72 Constructeurs et initialiseurs 134
73 Cas des tableaux dynamiques dobjets 135
8 - Les objets temporaires 136
Chapitre 8 Les fonctions amies 141
1 - Exemple de fonction indeacutependante amie drsquoune classe 142
2 - Les diffeacuterentes situations drsquoamitieacute 144
21 Fonction membre dune classe amie dune autre classe 145
22 Fonction amie de plusieurs classes 146
23 Toutes les fonctions dune classe amies dune autre classe 147
3 - Exemple 147
31 Fonction amie indeacutependante 148
32 Fonction amie membre dune classe 149
4 - Exploitation de classes disposant de fonctions amies 150
Chapitre 9 La surdeacutefinition drsquoopeacuterateurs 151
1 - Le meacutecanisme de la surdeacutefinition drsquoopeacuterateurs 152
11 Surdeacutefinition dopeacuterateur avec une fonction amie 153
12 Surdeacutefinition dopeacuterateur avec une fonction membre 154
13 Opeacuterateurs et transmission par reacutefeacuterence 156
C++ pour programmeurs CX
2 - La surdeacutefinition drsquoopeacuterateurs en geacuteneacuteral 157
21 Se limiter aux opeacuterateurs existants 157
22 Se placer dans un contexte de classe 158
23 Eviter les hypothegraveses sur le rocircle drsquoun opeacuterateur 159
24 Cas des opeacuterateurs ++ et -- 160
25 Les opeacuterateurs = et amp ont une signification preacutedeacutefinie 161
26 Les conversions 162
27 Choix entre fonction membre et fonction amie 163
3 - Exemple de surdeacutefinition de lrsquoopeacuterateur = 163
31 Rappels concernant le constructeur par recopie 163
32 Cas de lrsquoaffectation 164
33 Algorithme proposeacute 165
34 Valeur de retour 167
35 En deacutefinitive 167
36 Exemple de programme complet 167
37 Lorsqursquoon souhaite interdire lrsquoaffectation 169
4 - La forme canonique dune classe 170
5 - Exemple de surdeacutefinition de lopeacuterateur [ ] 171
6 - Surdeacutefinition de lopeacuterateur () 173
7 - Surdeacutefinition des opeacuterateurs new et delete 174
71 Surdeacutefinition de new et delete pour une classe donneacutee 175
72 Exemple 175
73 Drsquoune maniegravere geacuteneacuterale 177
Chapitre 10 Les conversions de type deacutefinies par lrsquoutilisateur 181
1 - Les diffeacuterentes sortes de conversions deacutefinies par lrsquoutilisateur 182
2 - Lopeacuterateur de cast pour la conversion type classe ndashgt type de base 184
21 Deacutefinition de lopeacuterateur de cast 184
22 Exemple dutilisation 184
23 Appel implicite de lopeacuterateur de cast lors drsquoun appel de fonction 185
24 Appel implicite de lopeacuterateur de cast dans leacutevaluation dune expression 187
25 Conversions en chaicircne 188
26 En cas dambiguiumlteacute 190
3 - Le constructeur pour la conversion type de base -gt type classe 191
31 Exemple 191
32 Le constructeur dans une chaicircne de conversions 193
33 Choix entre constructeur ou opeacuterateur daffectation 193
34 Emploi dun constructeur pour eacutelargir la signification dun opeacuterateur 195
35 Interdire les conversions implicites par le constructeur le rocircle drsquoexplicit 197
4 - Les conversions drsquoun type classe en un autre type classe 198
41 Exemple simple dopeacuterateur de cast 198
42 Exemple de conversion par un constructeur 199
43 Pour donner une signification agrave un opeacuterateur deacutefini dans une autre classe 200
5 - Quelques conseils 203
Table des matiegraveresXI
Chapitre 11 Les patrons de fonctions 205
1 - Exemple de creacuteation et drsquoutilisation drsquoun patron de fonctions 206
11 Creacuteation dun patron de fonctions 206
12 Premiegraveres utilisations du patron de fonctions 207
13 Autres utilisations du patron de fonctions 208
131 Application au type char 208
132 Application agrave un type classe 209
14 Contraintes drsquoutilisation drsquoun patron 210
2 - Les paramegravetres de type drsquoun patron de fonctions 211
21 Utilisation des paramegravetres de type dans la deacutefinition dun patron 211
22 Identification des paramegravetres de type dune fonction patron 212
23 Nouvelle syntaxe dinitialisation des variables des types standard 213
24 Limitations des patrons de fonctions 214
3 - Les paramegravetres expressions drsquoun patron de fonctions 215
4 - Surdeacutefinition de patrons 216
41 Exemples ne comportant que des paramegravetres de type 216
42 Exemples comportant des paramegravetres expressions 219
5 - Speacutecialisation de fonctions de patron 220
51 Geacuteneacuteraliteacutes 220
52 Les speacutecialisations partielles 221
6 - Algorithme drsquoinstanciation drsquoune fonction patron 222
Chapitre 12 Les patrons de classes 225
1 - Exemple de creacuteation et drsquoutilisation drsquoun patron de classes 226
11 Creacuteation dun patron de classes 226
12 Utilisation dun patron de classes 228
13 Contraintes drsquoutilisation drsquoun patron de classes 228
14 Exemple reacutecapitulatif 229
2 - Les paramegravetres de type drsquoun patron de classes 231
21 Les paramegravetres de type dans la creacuteation dun patron de classes 231
22 Instanciation dune classe patron 231
3 - Les paramegravetres expressions drsquoun patron de classes 233
31 Exemple 233
32 Les proprieacuteteacutes des paramegravetres expressions 235
4 - Speacutecialisation drsquoun patron de classes 235
41 Exemple de speacutecialisation dune fonction membre 236
42 Les diffeacuterentes possibiliteacutes de speacutecialisation 237
421 On peut speacutecialiser une fonction membre pour tous les paramegravetres 237
422 On peut speacutecialiser une fonction membre ou une classe 237
423 On peut preacutevoir des speacutecialisations partielles de patrons de classes 237
5 - Paramegravetres par deacutefaut 238
6 - Patrons de fonctions membres 238
C++ pour programmeurs CXII
7 - Identiteacute de classes patrons 239
8 - Classes patrons et deacuteclarations drsquoamitieacute 239
81 Deacuteclaration de classes ou fonctions ordinaires amies 239
82 Deacuteclaration dinstances particuliegraveres de classes patrons ou de fonctions patrons 240
83 Deacuteclaration drsquoun autre patron de fonctions ou de classes 241
9 - Exemple de classe tableau agrave deux indices 241
Chapitre 13 Lrsquoheacuteritage simple 245
1 - La notion drsquoheacuteritage 246
2 - Utilisation des membres de la classe de base dans une classe deacuteriveacutee 248
3 - Redeacutefinition des membres drsquoune classe deacuteriveacutee 250
31 Redeacutefinition des fonctions membres drsquoune classe deacuteriveacutee 250
32 Redeacutefinition des membres donneacutees drsquoune classe deacuteriveacutee 252
33 Redeacutefinition et surdeacutefinition 252
4 - Appel des constructeurs et des destructeurs 254
41 Rappels 254
42 La hieacuterarchisation des appels 255
43 Transmission dinformations entre constructeurs 255
44 Exemple 256
45 Compleacutements 258
5 - Controcircle des accegraves 258
51 Les membres proteacutegeacutes 259
52 Exemple 259
53 Inteacuterecirct du statut proteacutegeacute 260
54 Deacuterivation publique et deacuterivation priveacutee 261
541 Rappels concernant la deacuterivation publique 261
542 Deacuterivation priveacutee 262
55 Les possibiliteacutes de deacuterivation proteacutegeacutee 263
56 Reacutecapitulation 264
6 - Compatibiliteacute entre classe de base et classe deacuteriveacutee 264
61 Conversion dun type deacuteriveacute en un type de base 265
62 Conversion de pointeurs 266
63 Limitations lieacutees au typage statique des objets 266
64 Les risques de violation des protections de la classe de base 269
7 - Le constructeur de recopie et lrsquoheacuteritage 270
71 La classe deacuteriveacutee ne deacutefinit pas de constructeur de recopie 271
72 La classe deacuteriveacutee deacutefinit un constructeur de recopie 271
8 - Lrsquoopeacuterateur drsquoaffectation et lrsquoheacuteritage 273
81 La classe deacuteriveacutee ne surdeacutefinit pas lopeacuterateur = 273
82 La classe deacuteriveacutee surdeacutefinit lopeacuterateur = 274
9 - Heacuteritage et forme canonique dune classe 277
Table des matiegraveresXIII
10 - Lrsquoheacuteritage et ses limites 278
101 La situation dheacuteritage 278
1011 Le type du reacutesultat de lappel 279
1012 Le type des arguments de f 279
102 Exemples 280
1021 Heacuteritage dans pointcol dun opeacuterateur + deacutefini dans point 280
1022 Heacuteritage dans pointcol de la fonction coincide de point 280
11 - Exemple de classe deacuteriveacutee 281
12 - Patrons de classes et heacuteritage 284
121 Classe ordinaire deacuterivant dune classe patron 285
122 Deacuterivation de patrons avec les mecircmes paramegravetres 286
123 Deacuterivation de patrons avec introduction drsquoun nouveau paramegravetre 286
13 - Lrsquoheacuteritage en pratique 287
131 Deacuterivations successives 287
132 Diffeacuterentes utilisations de lrsquoheacuteritage 289
133 Exploitation drsquoune classe deacuteriveacutee 289
Chapitre 14 Lheacuteritage multiple 291
1 - Mise en œuvre de lheacuteritage multiple 292
2 - Pour reacutegler les eacuteventuels conflits les classes virtuelles 296
3 - Appels des constructeurs et des destructeurs cas des classes virtuelles 298
4 - Exemple drsquoutilisation de lrsquoheacuteritage multiple et de la deacuterivation virtuelle 300
Chapitre 15 Les fonctions virtuelles et le polymorphisme 305
1 - Rappel drsquoune situation ougrave le typage dynamique est neacutecessaire 306
2 - Le meacutecanisme des fonctions virtuelles 306
3 - Autre situation ougrave la ligature dynamique est indispensable 308
4 - Les proprieacuteteacutes des fonctions virtuelles 311
41 Leurs limitations sont celles de lrsquoheacuteritage 311
42 La redeacutefinition dune fonction virtuelle nest pas obligatoire 312
43 Fonctions virtuelles et surdeacutefinition 313
44 Le type de retour drsquoune fonction virtuelle redeacutefinie 313
45 On peut deacuteclarer une fonction virtuelle dans nimporte quelle classe 314
46 Quelques restrictions et conseils 314
461 Seule une fonction membre peut ecirctre virtuelle 314
462 Un constructeur ne peut pas ecirctre virtuel 315
463 Un destructeur peut ecirctre virtuel 315
464 Cas particulier de lrsquoopeacuterateur drsquoaffectation 316
5 - Les fonctions virtuelles pures pour la creacuteation de classes abstraites 317
6 - Exemple drsquoutilisation de fonctions virtuelles liste heacuteteacuterogegravene 319
7 - Le meacutecanisme drsquoidentification dynamique des objets 324
C++ pour programmeurs CXIV
8 - Identification de type agrave lexeacutecution 326
81 Utilisation du champ name de type_info 326
82 Utilisation des opeacuterateurs de comparaison de type_info 328
83 Exemple avec des reacutefeacuterences 328
9 - Les cast dynamiques 329
Chapitre 16 Les flots 333
1 - Preacutesentation geacuteneacuterale de la classe ostream 335
11 Lopeacuterateur ltlt 335
12 Les flots preacutedeacutefinis 336
13 La fonction put 337
14 La fonction write 337
141 Cas des caractegraveres 337
142 Autres cas 337
15 Quelques possibiliteacutes de formatage avec ltlt 338
151 Action sur la base de numeacuteration 338
152 Action sur le gabarit de linformation eacutecrite 339
153 Action sur la preacutecision de lrsquoinformation eacutecrite 341
154 Choix entre notation flottante ou exponentielle 341
2 - Preacutesentation geacuteneacuterale de la classe istream 342
21 Lrsquoopeacuterateur gtgt 343
211 Cas des caractegraveres 343
212 Cas des chaicircnes de caractegraveres 344
213 Les types accepteacutes par gtgt 344
22 La fonction get 345
23 Les fonctions getline et gcount 345
24 La fonction read 347
241 Cas des caractegraveres 347
242 Autres cas 347
25 Quelques autres fonctions 347
3 - Statut drsquoerreur drsquoun flot 348
31 Les bits derreur 348
32 Actions concernant les bits derreur 348
321 Accegraves aux bits derreur 349
322 Modification du statut derreur 349
33 Surdeacutefinition des opeacuterateurs () et 349
34 Exemples 350
4 - Surdeacutefinition de ltlt et gtgt pour les types deacutefinis par lrsquoutilisateur 352
41 Meacutethode 352
42 Exemple 353
5 - Gestion du formatage 355
51 Le statut de formatage dun flot 356
52 Description du mot deacutetat du statut de formatage 357
Table des matiegraveresXV
53 Action sur le statut de formatage 358
531 Les manipulateurs non parameacutetriques 358
532 Les manipulateurs parameacutetriques 359
533 Les fonctions membres 360
6 - Connexion drsquoun flot agrave un fichier 362
61 Connexion dun flot de sortie agrave un fichier 362
62 Connexion dun flot dentreacutee agrave un fichier 364
63 Les possibiliteacutes daccegraves direct 365
64 Les diffeacuterents modes douverture dun fichier 367
7 - Les anciennes possibiliteacutes de formatage en meacutemoire 368
71 La classe ostrstream 369
72 La classe istrstream 370
Chapitre 17 La gestion des exceptions 373
1 - Premier exemple drsquoexception 374
11 Comment lancer une exception linstruction throw 374
12 Utilisation dun gestionnaire dexception 375
13 Reacutecapitulatif 376
2 - Second exemple 378
3 - Le meacutecanisme de gestion des exceptions 380
31 Poursuite de lexeacutecution du programme 380
32 Prise en compte des sorties de blocs 382
4 - Choix du gestionnaire 382
41 Le gestionnaire reccediloit toujours une copie 383
42 Regravegles de choix drsquoun gestionnaire drsquoexception 383
43 Le cheminement des exceptions 384
44 Redeacuteclenchement drsquoune exception 386
5 - Speacutecification dinterface la fonction unexpected 387
6 - Les exceptions standard 390
61 Geacuteneacuteraliteacutes 390
62 Les exceptions deacuteclencheacutees par la bibliothegraveque standard 390
63 Les exceptions utilisables dans un programme 391
64 Creacuteation drsquoexceptions deacuteriveacutees de la classe exception 391
641 Exemple 1 392
642 Exemple 2 393
Chapitre 18 Geacuteneacuteraliteacutes sur la bibliothegraveque standard 395
1 - Notions de conteneur diteacuterateur et dalgorithme 395
11 Notion de conteneur 396
12 Notion diteacuterateur 396
13 Parcours dun conteneur avec un iteacuterateur 397
131 Parcours direct 397
132 Parcours inverse 398
C++ pour programmeurs CXVI
14 Intervalle diteacuterateur 398
15 Notion dalgorithme 399
16 Iteacuterateurs et pointeurs 400
2 - Les diffeacuterentes sortes de conteneurs 400
21 Conteneurs et structures de donneacutees classiques 400
22 Les diffeacuterentes cateacutegories de conteneurs 401
3 - Les conteneurs dont les eacuteleacutements sont des objets 401
31 Construction copie et affectation 402
32 Autres opeacuterations 403
4 - Efficaciteacute des opeacuterations sur des conteneurs 403
5 - Fonctions preacutedicats et classes fonctions 404
51 Fonction unaire 404
52 Preacutedicats 405
53 Classes et objets fonctions 405
531 Utilisation dobjet fonction comme fonction de rappel 405
532 Classes fonction preacutedeacutefinies 406
6 - Conteneurs algorithmes et relation dordre 407
61 Introduction 407
62 Proprieacuteteacutes agrave respecter 408
7 - Les geacuteneacuterateurs dopeacuterateurs 409
Chapitre 19 Les conteneurs seacutequentiels 411
1 - Fonctionnaliteacutes communes aux conteneurs vector list et deque 412
11 Construction 412
111 Construction dun conteneur vide 412
112 Construction avec un nombre donneacute deacuteleacutements 412
113 Construction avec un nombre donneacute deacuteleacutements initialiseacutes agrave une valeur 412
114 Construction agrave partir dune seacutequence 413
115 Construction agrave partir dun autre conteneur de mecircme type 413
12 Modifications globales 413
121 Opeacuterateur daffectation 414
122 La fonction membre assign 414
123 La fonction clear 415
124 La fonction swap 415
13 Comparaison de conteneurs 415
131 Lopeacuterateur == 415
132 Lopeacuterateur lt 416
133 Exemples 416
14 Insertion ou suppression deacuteleacutements 416
141 Insertion 417
142 Suppression 417
143 Cas des insertionssuppressions en fin pop_back et push_back 418
Table des matiegraveresXVII
2 - Le conteneur vector 418
21 Accegraves aux eacuteleacutements existants 419
211 Accegraves par iteacuterateur 419
212 Accegraves par indice 419
213 Cas de laccegraves au dernier eacuteleacutement 420
22 Insertions et suppressions 420
23 Gestion de lemplacement meacutemoire 420
231 Introduction 420
232 Invalidation diteacuterateurs ou de reacutefeacuterences 421
233 Outils de gestion de lemplacement meacutemoire dun vecteur 421
24 Exemple 422
25 Cas particulier des vecteurs de booleacuteens 423
3 - Le conteneur deque 424
31 Preacutesentation geacuteneacuterale 424
32 Exemple 425
4 - Le conteneur list 426
41 Accegraves aux eacuteleacutements existants 426
42 Insertions et suppressions 427
421 Suppression des eacuteleacutements de valeur donneacutee 427
422 Suppression des eacuteleacutements reacutepondant agrave une condition 427
43 Opeacuterations globales 428
431 Tri dune liste 428
432 Suppression des eacuteleacutements en double 428
433 Fusion de deux listes 429
434 Transfert dune partie de liste dans une autre 430
44 Gestion de lemplacement meacutemoire 430
45 Exemple 431
5 - Les adaptateurs de conteneur queue stack et priority_queue 432
51 Ladaptateur stack 432
52 Ladaptateur queue 433
53 Ladaptateur priority_queue 434
Chapitre 20 Les conteneurs associatifs 437
1 - Le conteneur map 438
11 Exemple introductif 438
12 Le patron de classes pair 440
13 Construction dun conteneur de type map 440
131 Constructions utilisant la relation dordre par deacutefaut 441
132 Choix de lordre intrinsegraveque du conteneur 441
133 Pour connaicirctre la relation dordre utiliseacutee par un conteneur 442
134 Conseacutequences du choix de lordre dun conteneur 443
14 Accegraves aux eacuteleacutements 443
141 Accegraves par lopeacuterateur [ ] 443
142 Accegraves par iteacuterateur 443
143 Recherche par la fonction membre find 444
C++ pour programmeurs CXVIII
15 Insertions et suppressions 444
151 Insertions 445
152 Suppressions 446
16 Gestion meacutemoire 446
17 Autres possibiliteacutes 447
18 Exemple 447
2 - Le conteneur multimap 448
21 Preacutesentation geacuteneacuterale 448
22 Exemple 449
3 - Le conteneur set 451
31 Preacutesentation geacuteneacuterale 451
32 Exemple 451
33 Le conteneur set et lensemble matheacutematique 452
4 - Le conteneur multiset 452
5 - Conteneurs associatifs et algorithmes 453
Chapitre 21 Les algorithmes standard 455
1 - Notions geacuteneacuterales 455
11 Algorithmes et iteacuterateurs 455
12 Les cateacutegories diteacuterateurs 456
121 Iteacuterateur en entreacutee 456
122 Iteacuterateur en sortie 456
123 Hieacuterarchie des cateacutegories diteacuterateurs 457
13 Algorithmes et seacutequences 457
14 Iteacuterateur dinsertion 458
15 Iteacuterateur de flot 460
151 Iteacuterateur de flot de sortie 460
152 Iteacuterateur de flot dentreacutee 461
2 - Algorithmes dinitialisation de seacutequences existantes 462
21 Copie dune seacutequence dans une autre 462
22 Geacuteneacuteration de valeurs par une fonction 463
3 - Algorithmes de recherche 466
31 Algorithmes fondeacutes sur une eacutegaliteacute ou un preacutedicat unaire 466
32 Algorithmes de recherche de maximum ou de minimum 467
4 - Algorithmes de transformation dune seacutequence 468
41 Remplacement de valeurs 469
42 Permutations de valeurs 469
421 Rotation 469
422 Geacuteneacuteration de permutations 470
423 Permutations aleacuteatoires 471
43 Partitions 472
5 - Algorithmes dits de suppression 472
6 - Algorithmes de tris 474
Table des matiegraveresXIX
7 - Algorithmes de recherche et de fusion sur des seacutequences ordonneacutees 476
71 Algorithmes de recherche binaire 476
72 Algorithmes de fusion 476
8 - Algorithmes agrave caractegravere numeacuterique 478
9 - Algorithmes agrave caractegravere ensembliste 479
10 - Algorithmes de manipulation de tas 480
Chapitre 22 La classe string 485
1 - Geacuteneacuteraliteacutes 486
2 - Construction 486
3 - Opeacuterations globales 487
4 - Concateacutenation 488
5 - Recherche dans une chaicircne 488
51 Recherche dune chaicircne ou dun caractegravere 489
52 Recherche dun caractegravere preacutesent ou absent dune suite 489
6 - Insertions suppressions et remplacements 490
61 Insertions 490
62 Suppressions 491
63 Remplacements 492
7 - Les possibiliteacutes de formatage en meacutemoire 493
71 La classe ostringstream 494
72 La classe istringstream 495
721 Preacutesentation 495
722 Utilisation pour fiabiliser les lectures au clavier 495
Chapitre 23 Les outils numeacuteriques 499
1 - La classe complex 499
2 - La classe valarray et les classes associeacutees 501
21 Constructeurs des classes valarray 501
22 Lrsquoopeacuterateur [] 502
23 Affectation et changement de taille 502
24 Calcul vectoriel 502
25 Seacutelection de valeurs par masque 504
26 Sections de vecteurs 505
27 Vecteurs drsquoindices 506
3 - La classe bitset 507
Chapitre 24 Les espaces de noms 511
1 - Creacuteation drsquoespaces de noms 511
11 Exemple de creacuteation drsquoun nouvel espace de noms 512
12 Exemple avec deux espaces de noms 513
C++ pour programmeurs CXX
13 Espace de noms et fichier en-tecircte 514
14 Instructions figurant dans un espace de noms 514
15 Creacuteation increacutementale drsquoespaces de noms 515
2 - Les instructions using 516
21 La deacuteclaration using pour les symboles 516
211 Preacutesentation geacuteneacuterale 516
212 Masquage et ambiguiumlteacutes 518
22 La directive using pour les espaces de noms 519
3 - Espaces de noms et surdeacutefinition 521
4 - Imbrication des espaces de noms 523
5 - Transitiviteacute de la directive using 523
6 - Les alias 524
7 - Les espaces anonymes 524
8 - Espaces de noms et deacuteclaration drsquoamitieacute 525
Annexes 527
Annexe A Mise en correspondance drsquoarguments 529
1 - Deacutetermination des fonctions candidates 529
2 - Algorithme de recherche drsquoune fonction agrave un seul argument 530
21 Recherche dune correspondance exacte 530
22 Promotions numeacuteriques 531
23 Conversions standard 531
24 Conversions deacutefinies par lutilisateur 532
25 Fonctions agrave arguments variables 532
26 Exception cas des champs de bits 532
3 - Fonctions agrave plusieurs arguments 533
4 - Fonctions membres 533
Annexe B Utilisation de code eacutecrit en C 535
1 - Prototypes 535
2 - Fonctions sans arguments 535
3 - Fonctions sans valeur de retour 535
4 - Le qualificatif const 536
5 - Les pointeurs de type void 536
6 - Mots cleacutes 536
7 - Les constantes de type caractegravere 537
8 - Les deacutefinitions multiples 537
9 - Linstruction goto 538
10 - Les eacutenumeacuterations 538
Table des matiegraveresXXI
11 - Initialisation de tableaux de caractegraveres 538
12 - Les noms de fonctions 538
Annexe C Compleacutements sur les exceptions 539
1 - Les problegravemes poseacutes par les objets automatiques 539
2 - La technique de gestion de ressources par initialisation 540
3 - Le concept de pointeur intelligent la classe auto_ptr 542
Annexe D Les diffeacuterentes sortes de fonctions en C++ 545
Annexe E Comptage de reacutefeacuterences 547
Annexe F Les pointeurs sur des membres 551
1 - Rappels sur les pointeurs sur des fonctions en C 551
2 - Les pointeurs sur des fonctions membres 552
3 - Les pointeurs sur des membres donneacutees 553
4 - Lrsquoheacuteritage et les pointeurs sur des membres 554
Annexe G Les algorithmes standard 557
1 - Algorithmes dinitialisation de seacutequences existantes 558
2 - Algorithmes de recherche 559
3 - Algorithmes de transformation dune seacutequence 561
4 - Algorithmes de suppression 564
5 - Algorithmes de tri 566
6 - Algorithmes de recherche et de fusions sur des seacutequences ordonneacutees 568
7 - Algorithmes agrave caractegravere numeacuterique 570
8 - Algorithmes agrave caractegravere ensembliste 571
9 - Algorithmes de manipulation de tas 574
10 - Algorithmes divers 575
Correction des exercices 577
Index 593
Avant-propos
1 Historique de C++La programmation orienteacutee objet (en abreacutegeacute POO) est doreacutenavant universellement recon-
nue pour les avantages qursquoelle procure Notamment elle ameacuteliore largement la productiviteacute
des deacuteveloppeurs la robustesse la portabiliteacute et lrsquoextensibiliteacute de leurs programmes Enfin et
surtout elle permet de deacutevelopper des composants logiciels entiegraverement reacuteutilisables
Un certain nombre de langages dits langages orienteacutes objet (LOO) ont eacuteteacute deacutefinis de tou-
tes piegraveces pour appliquer les concepts de POO Crsquoest ainsi que sont apparus dans un premier
temps des langages comme Smalltalk Simula ou Eiffel puis plus reacutecemment Java Le lan-
gage C++ quant agrave lui a eacuteteacute conccedilu suivant une deacutemarche quelque peu diffeacuterente par B
Stroustrup (ATampT) son objectif a eacuteteacute en effet dadjoindre au langage C un certain nombre
de speacutecificiteacutes lui permettant dappliquer les concepts de POO Ainsi C++ preacutesente-t-il sur
un vrai LOO loriginaliteacute decirctre fondeacute sur un langage reacutepandu Ceci laisse au programmeur
toute liberteacute dadopter un style plus ou moins orienteacute objet en se situant entre les deux extrecirc-
mes que constituent la poursuite dune programmation classique dune part une pure POO
dautre part Si une telle liberteacute preacutesente le risque de ceacuteder dans un premier temps agrave la faci-
liteacute en meacutelangeant les genres (la POO ne renie pas la programmation classique - elle lenri-
chit) elle permet eacutegalement une transition en douceur vers la POO pure avec tout le
beacuteneacutefice quon peut en escompter agrave terme
De sa conception jusquagrave sa normalisation le langage C++ a quelque peu eacutevolueacute Initiale-
ment un certain nombre de publications de ATampT ont servi de reacutefeacuterence du langage Les der-
niegraveres en date sont la version 20 en 1989 les versions 21 et 3 en 1991 Crsquoest cette derniegravere
qui a servi de base au travail du comiteacute ANSI lequel sans la remettre en cause la enrichie de
Avant-proposAVANT-PROPOS
XXIV
quelques extensions et surtout de composants standard originaux se preacutesentant sous forme de
fonctions et de classes geacuteneacuteriques qursquoon deacutesigne souvent par le sigle STL1 La norme deacutefini-
tive de C++ a eacuteteacute publieacutee par lANSI et par lISO en 1998 et a fait lobjet dune reacutevision
publieacutee en 2003 sous la reacutefeacuterence ISO 148822003
2 Objectifs et structure de lrsquoouvrageCet ouvrage a eacuteteacute speacutecifiquement conccedilu pour tous ceux qui posseacutedant deacutejagrave une pratique du
langage C2 souhaitent maicirctriser la programmation orienteacutee objet en C++ Il srsquoadresse agrave la
fois aux eacutetudiants aux deacuteveloppeurs et aux enseignants en informatique
Conccedilu sous forme drsquoun cours complet il expose progressivement agrave la fois
bull les diffeacuterentes notions fondamentales de la POO et la faccedilon dont elles srsquoexpriment en C++
(classes et objets meacutethodes constructeur destructeur heacuteritage polymorphisme)
bull les speacutecificiteacutes non orienteacutees objet du langage C++ crsquoest-agrave-dire celles qui permettent agrave
C++ drsquoecirctre un C ameacutelioreacute (reacutefeacuterence argument par deacutefaut surdeacutefinition de fonctions fonc-
tions en ligne espaces de noms)
bull les speacutecificiteacutes orienteacutees objet du C++ fonctions amies surdeacutefinition drsquoopeacuterateurs patrons
de classes et de fonctions heacuteritage multiple flots bibliothegraveque standard
Chacune de ces notions est illustreacutee systeacutematiquement par un programme complet assorti
dun exemple dexeacutecution montrant comment la mettre en œuvre dans un contexte reacuteel Celui-
ci peut eacutegalement servir agrave une prise de connaissance intuitive ou agrave une reacutevision rapide de la
notion en question agrave une expeacuterimentation directe dans votre propre environnement de travail
ou encore de point de deacutepart agrave une expeacuterimentation personnelle
Les chapitres les plus importants ont eacuteteacute doteacutes dexercices3 comportant
bull des suggestions de manipulations destineacutees agrave mieux vous familiariser avec votre
environnement par effet dentraicircnement elles vous feront probablement imaginer dautres
expeacuterimentations de votre cru
bull des programmes agrave reacutediger dans ce cas un exemple de correction est fourni en fin de volu-
me
Lrsquoaspect didactique a eacuteteacute privileacutegieacute sans pour autant nuire agrave lrsquoexhaustiviteacute de lrsquoouvrage
Nous couvrons lrsquoensemble de la programmation en C++ des notions fondamentales de la
POO jusqursquoaux aspects tregraves speacutecifiques au langage (mais neacuteanmoins fondamentaux) afin
1 Standard Template Library
2 Le cas eacutecheacuteant on pourra trouver un cours complet de langage C dans Programmer en langage C ou une reacutefeacuterence
exhaustive de sa norme dans Langage C du mecircme auteur chez le mecircme eacutediteur
3 De nombreux autres exercices peuvent ecirctre trouveacutes dans Exercices en langage C++ du mecircme auteur chez le mecircme
eacutediteur
3 - Lrsquoouvrage la norme de C++ C et JavaXXV
de rendre le lecteur parfaitement opeacuterationnel dans la conception le deacuteveloppement et la
mise au point de ses propres classes Crsquoest ainsi que nous avons soigneusement eacutetudieacute les
conseacutequences de la liberteacute qursquooffre C++ de choisir le mode de gestion de la meacutemoire alloueacutee
aux objets (automatique ou dynamique)1 De mecircme nous avons largement insisteacute sur le rocircle
du constructeur de recopie ainsi que sur la redeacutefinition de lopeacuterateur daffectation eacuteleacutements
qui conduisent agrave la notion de classe canonique Toujours dans le mecircme esprit nous avons
pris soin de bien deacutevelopper les notions indispensables que sont la ligature dynamique et les
classes abstraites lesquelles deacutebouchent sur la notion la plus puissante du langage quest le
polymorphisme De mecircme la STL a eacuteteacute eacutetudieacutee en deacutetail apregraves avoir pris soin drsquoexposer
preacutealablement drsquoune part les notions de classes et de fonctions geacuteneacuteriques drsquoautre part celles
de conteneur diteacuterateur et dalgorithmes qui conditionnent la bonne utilisation de la plupart
de ses composants
3 Lrsquoouvrage la norme de C++ C et JavaCet ouvrage est entiegraverement fondeacute sur la norme ANSIISO du langage C++ Degraves le deacutebut le
lecteur est sensibiliseacute aux quelques incompatibiliteacutes existant entre C++ et C de sorte qursquoil
pourra reacuteutiliser convenablement en C++ du code eacutecrit en C Drsquoautre part compte tenu de la
populariteacute du langage Java nous avons introduit de nombreuses remarques titreacutees En Java
Elles mettent lrsquoaccent sur les diffeacuterences majeures existant entre Java et C++ Elles seront
utiles au lecteur qui apregraves la maicirctrise du C++ souhaitera aborder lrsquoeacutetude de Java
Cet ouvrage correspond en fait agrave une refonte des eacuteditions preacuteceacutedentes de Programmer en
C++ Nous continuons drsquoy mentionner les apports de la norme par rapport agrave la version 3 du
langage publieacutee en 1991 ainsi que les quelques diffeacuterences avec les versions anteacuterieures
Ces remarques initialement preacutevues pour faciliter lrsquoutilisation drsquoanciens environnements de
programmation deviennent de moins en moins pertinentes mais dans la mesure ougrave elles ne
pertubent pas lrsquoapprentissage du langage nous avons preacutefeacutereacute les conserver pour leur carac-
tegravere historique en particulier elles mettent en avant les points deacutelicats du langage pour les-
quels la genegravese a eacuteteacute quelque peu difficile
1 Certains langages objet dont Java gegraverent tous les objets de maniegravere dynamique
1
Geacuteneacuteraliteacutes concernant C++
Le langage C++ a eacuteteacute conccedilu agrave partir de 1982 par Bjarne Stroustrup (ATampT Bell Laboratories)
avec un objectif preacutecis ajouter au langage C des classes analogues agrave celles du langage
Simula Il srsquoagissait donc de greffer sur un langage classique des possibiliteacutes de Program-
mation Orienteacutee Objet Avant de vous preacutesenter le reacutesultat auquel a abouti B Stroustrup
commenccedilons par examiner succinctement ce qursquoest la Programmation Orienteacutee Objet
1 La Programmation Orienteacutee Objet
11 Probleacutematique de la programmationJusqursquoagrave maintenant lrsquoactiviteacute de programmation a toujours susciteacute des reacuteactions diverses
allant jusqursquoagrave la contradiction totale Pour certains en effet il ne srsquoagit que drsquoun jeu de cons-
truction enfantin dans lequel il suffit drsquoenchaicircner des instructions eacuteleacutementaires (en nombre
restreint) pour parvenir agrave reacutesoudre nrsquoimporte quel problegraveme ou presque Pour drsquoautres au
contraire il srsquoagit de produire (au sens industriel du terme) des logiciels avec des exigences
de qualiteacute qursquoon tente de mesurer suivant certains critegraveres notamment
bull lrsquoexactitude aptitude drsquoun logiciel agrave fournir les reacutesultats voulus dans des conditions nor-
males drsquoutilisation (par exemple donneacutees correspondant aux speacutecifications)
bull la robustesse aptitude agrave bien reacuteagir lorsque lrsquoon srsquoeacutecarte des conditions normales
drsquoutilisation
Geacuteneacuteraliteacutes concernant C++CHAPITRE 1
2
bull lrsquoextensibiliteacute faciliteacute avec laquelle un programme pourra ecirctre adapteacute pour satisfaire agrave une
eacutevolution des speacutecifications
bull la reacuteutilisabiliteacute possibiliteacute drsquoutiliser certaines parties (modules) du logiciel pour reacutesoudre
un autre problegraveme
bull la portabiliteacute faciliteacute avec laquelle on peut exploiter un mecircme logiciel dans diffeacuterentes
impleacutementations
bull lrsquoefficience temps drsquoexeacutecution taille meacutemoire
La contradiction nrsquoest souvent qursquoapparente et essentiellement lieacutee agrave lrsquoimportance des projets
concerneacutes Par exemple il est facile drsquoeacutecrire un programme exact et robuste lorsqursquoil com-
porte une centaine drsquoinstructions il en va tout autrement lorsqursquoil srsquoagit drsquoun projet de dix
hommes-anneacutees De mecircme les aspects extensibiliteacute et reacuteutilisabiliteacute nrsquoauront guegravere
drsquoimportance dans le premier cas alors qursquoils seront probablement cruciaux dans le second
ne serait-ce que pour des raisons eacuteconomiques
12 La programmation structureacuteeLa programmation structureacutee a manifestement fait progresser la qualiteacute de la production des
logiciels Mais avec le recul il faut bien reconnaicirctre que ses propres fondements lui impo-
saient des limitations naturelles En effet la programmation structureacutee reposait sur ce que
lrsquoon nomme souvent lrsquoeacutequation de Wirth agrave savoir
Algorithmes + Structures de donneacutees = Programmes
Bien sucircr elle a permis de structurer les programmes et partant drsquoen ameacuteliorer lrsquoexactitude et
la robustesse On avait espeacutereacute qursquoelle permettrait eacutegalement drsquoen ameacuteliorer lrsquoextensibiliteacute et
la reacuteutilisabiliteacute Or en pratique on srsquoest aperccedilu que lrsquoadaptation ou la reacuteutilisation drsquoun logi-
ciel conduisait souvent agrave casser le module inteacuteressant et ceci parce qursquoil eacutetait neacutecessaire de
remettre en cause une structure de donneacutees Preacuteciseacutement ce type de difficulteacutes eacutemane direc-
tement de lrsquoeacutequation de Wirth qui deacutecouple totalement les donneacutees des proceacutedures agissant
sur ces donneacutees
13 Les apports de la Programmation Orienteacutee Objet
131 ObjetCrsquoest lagrave qursquointervient la Programmation Orienteacutee Objet (en abreacutegeacute POO) fondeacutee justement
sur le concept drsquoobjet agrave savoir une association des donneacutees et des proceacutedures (qursquoon appelle
alors meacutethodes) agissant sur ces donneacutees Par analogie avec lrsquoeacutequation de Wirth on pourrait
dire que lrsquoeacutequation de la POO est
Meacutethodes + Donneacutees = Objet
1 - La Programmation Orienteacutee Objet3
132 Encapsulation
Mais cette association est plus qursquoune simple juxtaposition En effet dans ce que lrsquoon pour-
rait qualifier de POO pure1 on reacutealise ce que lrsquoon nomme une encapsulation des don-
neacutees Cela signifie qursquoil nrsquoest pas possible drsquoagir directement sur les donneacutees drsquoun objet il
est neacutecessaire de passer par lrsquointermeacutediaire de ses meacutethodes qui jouent ainsi le rocircle drsquointer-
face obligatoire On traduit parfois cela en disant que lrsquoappel drsquoune meacutethode est en fait
lrsquoenvoi drsquoun message agrave lrsquoobjet
Le grand meacuterite de lrsquoencapsulation est que vu de lrsquoexteacuterieur un objet se caracteacuterise unique-
ment par les speacutecifications2 de ses meacutethodes la maniegravere dont sont reacuteellement implanteacutees les
donneacutees eacutetant sans importance On deacutecrit souvent une telle situation en disant qursquoelle reacutealise
une abstraction des donneacutees (ce qui exprime bien que les deacutetails concrets drsquoimpleacutementation
sont cacheacutes) A ce propos on peut remarquer qursquoen programmation structureacutee une proceacutedure
pouvait eacutegalement ecirctre caracteacuteriseacutee (de lrsquoexteacuterieur) par ses speacutecifications mais que faute
drsquoencapsulation lrsquoabstraction des donneacutees nrsquoeacutetait pas reacutealiseacutee
Lrsquoencapsulation des donneacutees preacutesente un inteacuterecirct manifeste en matiegravere de qualiteacute de logiciel
Elle facilite consideacuterablement la maintenance une modification eacuteventuelle de la structure
des donneacutees drsquoun objet nrsquoa drsquoincidence que sur lrsquoobjet lui-mecircme les utilisateurs de lrsquoobjet
ne seront pas concerneacutes par la teneur de cette modification (ce qui nrsquoeacutetait bien sucircr pas le cas
avec la programmation structureacutee) De la mecircme maniegravere lrsquoencapsulation des donneacutees facilite
grandement la reacuteutilisation drsquoun objet
133 Classe
En POO apparaicirct geacuteneacuteralement le concept de classe3 qui correspond simplement agrave la geacuteneacute-
ralisation de la notion de type que lrsquoon rencontre dans les langages classiques En effet une
classe nrsquoest rien drsquoautre que la description drsquoun ensemble drsquoobjets ayant une structure de
donneacutees commune4 et disposant des mecircmes meacutethodes Les objets apparaissent alors comme
des variables drsquoun tel type classe (en POO on dit aussi qursquoun objet est une instance de sa
classe)
134 Heacuteritage
Un autre concept important en POO est celui drsquoheacuteritage Il permet de deacutefinir une nouvelle
classe agrave partir drsquoune classe existante (qursquoon reacuteutilise en bloc ) agrave laquelle on ajoute de nou-
1 Nous verrons en effet que les concepts de la POO peuvent ecirctre appliqueacutes drsquoune maniegravere plus ou moins rigou-
reuse En particulier en C++ lrsquoencapsulation ne sera pas obligatoire ce qui ne veut pas dire qursquoelle ne soit pas sou-
haitable
2 Noms arguments et rocircles
3 Dans certains langages (Turbo Pascal par exemple) le mot classe est remplaceacute par objet et le mot objet par varia-
ble
4 Bien entendu seule la structure est commune les donneacutees eacutetant propres agrave chaque objet En revanche les meacutetho-
des sont effectivement communes agrave lrsquoensemble des objets drsquoune mecircme classe
Geacuteneacuteraliteacutes concernant C++CHAPITRE 1
4
velles donneacutees et de nouvelles meacutethodes La conception de la nouvelle classe dite qui
heacuterite des proprieacuteteacutes et des aptitudes de lrsquoancienne peut ainsi srsquoappuyer sur des reacutealisations
anteacuterieures parfaitement au point et les speacutecialiser agrave volonteacute Comme on peut srsquoen douter
lrsquoheacuteritage facilite largement la reacuteutilisation de produits existants drsquoautant plus qursquoil peut ecirctre
reacuteiteacutereacute autant de fois que neacutecessaire (la classe C peut heacuteriter de B qui elle-mecircme heacuterite
de A)1
135 Polymorphisme
Geacuteneacuteralement en POO une classe deacuteriveacutee peut redeacutefinir (crsquoest-agrave-dire modifier) certaines
des meacutethodes heacuteriteacutees de sa classe de base Cette possibiliteacute est la cleacute de ce que lrsquoon nomme
le polymorphisme crsquoest-agrave-dire la possibiliteacute de traiter de la mecircme maniegravere des objets de
types diffeacuterents pour peu qursquoils soient tous de classes deacuteriveacutees de la mecircme classe de base
Plus preacuteciseacutement on utilise chaque objet comme srsquoil eacutetait de cette classe de base mais son
comportement effectif deacutepend de sa classe effective (deacuteriveacutee de cette classe de base) en par-
ticulier de la maniegravere dont ses propres meacutethodes ont eacuteteacute redeacutefinies Le polymorphisme ameacute-
liore lrsquoextensibiliteacute des programmes en permettant drsquoajouter de nouveaux objets dans un
sceacutenario preacuteeacutetabli et eacuteventuellement eacutecrit avant drsquoavoir connaissance du type effectif de ces
objets
14 POO et langagesNous venons drsquoeacutenoncer les grands principes de la POO sans nous attacher agrave un langage par-
ticulier
Or manifestement certains langages peuvent ecirctre conccedilus (de toutes piegraveces) pour appliquer agrave
la lettre ces principes et reacutealiser ce que nous nommons de la POO pure Crsquoest par exem-
ple le cas de Simula Smalltalk ou plus reacutecemment Eiffel ou Java Le mecircme pheacutenomegravene a eu
lieu en son temps pour la programmation structureacutee avec Pascal
A lrsquoopposeacute on peut toujours tenter drsquoappliquer avec plus ou moins de bonheur ce que nous
aurions tendance agrave nommer une philosophie POO agrave un langage classique (Pascal C)
On retrouve lagrave une ideacutee comparable agrave celle qui consistait agrave appliquer les principes de la pro-
grammation structureacutee agrave des langages comme Fortran ou Basic
Le langage C++ se situe agrave mi-chemin entre ces deux points de vue Il a en effet eacuteteacute obtenu en
ajoutant agrave un langage classique (C) les outils permettant de mettre en œuvre tous les princi-
pes de la POO Programmer en C++ va donc plus loin qursquoadopter une philosophie POO en
C mais moins loin que de faire de la POO pure avec Eiffel
La solution adopteacutee par B Stroustrup a le meacuterite de preacuteserver lrsquoexistant (compatibiliteacute avec
C++ de programmes deacutejagrave eacutecrits en C) elle permet eacutegalement une transition en douceur de
la programmation structureacutee vers la POO En revanche elle nrsquoimpose nullement lrsquoapplica-
1 En C++ les techniques de meacutethodes virtuelles eacutelargissent encore plus la porteacutee de lrsquoheacuteritage mais il nrsquoest pas
possible pour lrsquoinstant drsquoen faire percevoir lrsquointeacuterecirct
2 - C++ C ANSI et POO5
tion stricte des principes de POO Comme vous le verrez en C++ rien ne vous empecircchera
(sauf votre bon sens ) de faire cohabiter des objets (dignes de ce nom parce que reacutealisant
une parfaite encapsulation de leurs donneacutees) avec des fonctions classiques reacutealisant des effets
de bord sur des variables globales
2 C++ C ANSI et POOPreacuteceacutedemment nous avons dit drsquoune faccedilon quelque peu simpliste que C++ se preacutesentait
comme un sur-ensemble du langage C offrant des possibiliteacutes de POO Il nous faut main-
tenant nuancer cette affirmation car il existe quelques incompatibiliteacutes entre le C et le C++
tels qursquoils sont deacutefinis par leurs normes respectives Celles-ci comme nous le verrons sont
neacuteanmoins mineures elles sont pour la plupart dues agrave la diffeacuterence drsquoesprit des deux langa-
ges ainsi qursquoagrave la toleacuterance dont a fait preuve la norme ANSI du C en cherchant agrave preacuteserver
lrsquoexistant (certaines toleacuterances ont disparu en C++)
Drsquoautre part les extensions du C++ par rapport au C ANSI ne sont pas toutes veacuteritablement
lieacutees agrave la POO Certaines pourraient en effet ecirctre ajouteacutees avec profit au langage C sans
qursquoil devienne pour autant orienteacute objet1
En fait nous pourrions caracteacuteriser C++ par cette formule
C++ = C + E + S + P
bull C deacutesigne le C norme ANSI
bull E repreacutesente les eacutecarts de C++ par rapport agrave la norme ANSI de C
bull S repreacutesente les speacutecificiteacutes de C++ qui ne sont pas veacuteritablement axeacutees sur la POO
bull P repreacutesente les possibiliteacutes de POO
Les principaux eacutecarts par rapport agrave la norme sont deacutecrits au chapitre 2 ils sont accompa-
gneacutes de rappels concernant la norme C ANSI Ils concernent essentiellement
bull les deacutefinitions de fonctions en-tecirctes prototypes arguments et valeur de retour
bull la porteacutee du qualificatif const
bull les compatibiliteacutes entre pointeurs
3 Les speacutecificiteacutes de C++Comme nous lrsquoavons dit C++ preacutesente par rapport au C ANSI des extensions qui ne sont
pas veacuteritablement orienteacutees POO Elles seront deacutecrites au chapitre 4 En voici un bref
reacutesumeacute
1 Drsquoailleurs certaines extensions de C++ par rapport agrave la premiegravere deacutefinition du C ont eacuteteacute introduites dans le C
ANSI (prototypes fonctions agrave arguments variables)
Geacuteneacuteraliteacutes concernant C++CHAPITRE 1
6
bull nouvelle forme de commentaire (en fin de ligne)
bull plus grande liberteacute dans lrsquoemplacement des deacuteclarations
bull notion de reacutefeacuterence facilitant la mise en œuvre de la transmission drsquoarguments par adresse
bull surdeacutefinition des fonctions attribution drsquoun mecircme nom agrave diffeacuterentes fonctions la recon-
naissance de la fonction reacuteellement appeleacutee se faisant drsquoapregraves le type et le nombre des argu-
ments figurant dans lrsquoappel (on parle parfois de signature)
bull nouveaux opeacuterateurs de gestion dynamique de la meacutemoire new et delete
bull possibiliteacute de deacutefinir des fonctions en ligne (inline) ce qui accroicirct la vitesse drsquoexeacutecution
sans perdre pour autant le formalisme des fonctions
4 C++ et la programmation orienteacutee objetLes possibiliteacutes de POO repreacutesentent bien sucircr lrsquoessentiel de lrsquoapport de C++
C++ dispose de la notion de classe (geacuteneacuteralisation de la notion de type deacutefini par lrsquoutilisa-
teur) Une classe comportera
bull la description drsquoune structure de donneacutees
bull des meacutethodes
Sur le plan du vocabulaire C++ utilise des termes qui lui sont propres On parle en effet de
bull membres donneacutees pour deacutesigner les diffeacuterents membres de la structure de donneacutees asso-
cieacutee agrave une classe
bull fonctions membres pour deacutesigner les meacutethodes
A partir drsquoune classe on pourra instancier des objets (nous dirons geacuteneacuteralement creacuteer des
objets)
bull soit par des deacuteclarations usuelles (de type classe)
bull soit par allocation dynamique en faisant appel au nouvel opeacuterateur new
C++ permet lrsquoencapsulation des donneacutees mais il ne lrsquoimpose pas On peut le regretter mais il
ne faut pas perdre de vue que par sa conception mecircme (extension de C) le C++ ne peut pas
ecirctre un langage de POO pure Bien entendu il reste toujours possible au concepteur de faire
preuve de rigueur en srsquoastreignant agrave certaines regravegles telles que lrsquoencapsulation absolue
Comme la plupart des langages objets C++ permet de deacutefinir ce que lrsquoon nomme des cons-
tructeurs de classe Un constructeur est une fonction membre particuliegravere qui est exeacutecuteacutee au
moment de la creacuteation drsquoun objet de la classe Le constructeur peut notamment prendre en
charge lrsquoinitialisation drsquoun objet au sens le plus large du terme crsquoest-agrave-dire sa mise dans un
eacutetat initial permettant son bon fonctionnement ulteacuterieur il peut srsquoagir de banales initialisa-
tions de membres donneacutees mais eacutegalement drsquoune preacuteparation plus eacutelaboreacutee correspondant au
deacuteroulement drsquoinstructions voire drsquoune allocation dynamique drsquoemplacements neacutecessaires agrave
4 - C++ et la programmation orienteacutee objet7
lrsquoutilisation de lrsquoobjet Lrsquoexistence drsquoun constructeur garantit que lrsquoobjet sera toujours initia-
liseacute ce qui constitue manifestement une seacutecuriteacute
De maniegravere similaire une classe peut disposer drsquoun destructeur fonction membre exeacutecuteacutee
au moment de la destruction drsquoun objet Celle-ci preacutesentera surtout un inteacuterecirct dans le cas
drsquoobjets effectuant des allocations dynamiques drsquoemplacements ces derniers pourront ecirctre
libeacutereacutes par le destructeur
Une des originaliteacutes de C++ par rapport agrave drsquoautres langages de POO reacuteside dans la possibi-
liteacute de deacutefinir des fonctions amies drsquoune classe Il srsquoagit de fonctions usuelles (qui ne sont
donc pas des fonctions membres drsquoune classe) qui sont autoriseacutees (par une classe) agrave acceacuteder
aux donneacutees (encapsuleacutees) de la classe Certes le principe drsquoencapsulation est violeacute mais
uniquement par des fonctions ducircment autoriseacutees agrave le faire
La classe est un type deacutefini par lrsquoutilisateur La notion de surdeacutefinition drsquoopeacuterateurs va per-
mettre de doter cette classe drsquoopeacuterations analogues agrave celles que lrsquoon rencontre pour les types
preacutedeacutefinis Par exemple on pourra deacutefinir une classe complexe (destineacutee agrave repreacutesenter des
nombres complexes) et la munir des opeacuterations drsquoaddition de soustraction de multiplication
et de division Qui plus est ces opeacuterations pourront utiliser les symboles existants + -
C dispose de possibiliteacutes de conversions explicites ou implicites C++ permet de les eacutelargir
aux types deacutefinis par lrsquoutilisateur que sont les classes Par exemple on pourra donner un sens
agrave la conversion int -gt complexe ou agrave la conversion complexe -gt float (complexe eacutetant une
classe)
Naturellement C++ dispose de lrsquoheacuteritage et mecircme de possibiliteacutes dites drsquoheacuteritage multiple
permettant agrave une classe drsquoheacuteriter simultaneacutement de plusieurs autres Le polymorphisme est
mis en place sur la demande explicite du programmeur par le biais de ce que lrsquoon nomme
(curieusement) des fonctions virtuelles (en Java le polymorphisme est natif et le program-
meur nrsquoa donc pas agrave srsquoen preacuteoccuper)
En matiegravere drsquoentreacutees-sorties C++ comporte de nouvelles possibiliteacutes fondeacutees sur la notion de
flot Leurs avantages sur les entreacutees-sorties de C sont en particulier
bull la simpliciteacute drsquoutilisation
bull une taille meacutemoire reacuteduite (on nrsquointroduit que ce qui est utile)
bull la possibiliteacute de leur donner un sens pour les types deacutefinis par lrsquoutilisateur que sont les clas-
ses (gracircce au meacutecanisme de surdeacutefinition drsquoopeacuterateur)
Bien qursquoelles soient lieacutees agrave lrsquoaspect POO nous ferons une premiegravere preacutesentation de ces
nouvelles possibiliteacutes drsquoentreacutees-sorties degraves le chapitre 3 Cela nous permettra de reacutealiser rapi-
dement des programmes dans lrsquoesprit du C++
Dans ses derniegraveres versions (et a fortiori dans sa norme ANSI) le C++ a eacuteteacute doteacute de la notion
de patron Un patron permet de deacutefinir des modegraveles utilisables pour geacuteneacuterer diffeacuterentes clas-
ses ou diffeacuterentes fonctions qualifieacutees parfois de geacuteneacuteriques mecircme si cette geacuteneacutericiteacute nrsquoest
pas totalement inteacutegreacutee dans le langage lui-mecircme comme crsquoest par exemple le cas avec
ADA
Geacuteneacuteraliteacutes concernant C++CHAPITRE 1
8
Enfin la norme ANSI a notablement accru le contenu de la bibliothegraveque standard de C++
qui vient compleacuteter celle du C toujours disponible En particulier on y trouve de nombreux
patrons de classes et de fonctions permettant de mettre en œuvre les structures de donneacutees les
plus importantes (vecteurs dynamiques listes chaicircneacutees chaicircnes) et les algorithmes les plus
usuels eacutevitant ainsi drsquoavoir agrave reacuteinventer la roue agrave la moindre occasion
2
Les incompatibiliteacutesentre C++ et C
A priori le langage C++ peut ecirctre consideacutereacute comme une extension du langage C Tout pro-
gramme eacutecrit en C devrait donc pouvoir ecirctre traduit correctement par un compilateur C++ et
son exeacutecution devrait alors fournir les mecircmes reacutesultats que ceux obtenus en utilisant un com-
pilateur C
Si ce point de vue correspond effectivement au souhait du concepteur du langage C++ en
pratique un certain nombre drsquoincompatibiliteacutes avec le C ANSI ont subsisteacute inheacuterentes agrave
lrsquoesprit dans lequel les deux langages ont eacuteteacute conccedilus
Nous allons deacutecrire ici les incompatibiliteacutes les plus importantes en particulier celles qui se
reacuteveacuteleraient quasiment agrave coup sucircr dans la mise au point de vos premiers programmes C++
Par ailleurs quelques autres incompatibiliteacutes mineures seront abordeacutees au cours des pro-
chains chapitres Elles seront toutes reacutecapituleacutees en Annexe B
Les incompatibiliteacutes entre C++ et CCHAPITRE 2
10
1 Les deacutefinitions de fonctions en C++Suivant la norme ANSI il existe en C deux faccedilons de deacutefinir1 une fonction Supposez que
nous ayons agrave deacutefinir une fonction nommeacutee fexple fournissant une valeur de retour2 de type
double et recevant deux arguments lrsquoun de type int lrsquoautre de type double Nous pouvons
pour cela proceacuteder de lrsquoune des deux faccedilons suivantes
double fexple (u v) double fexple (int u double v)int u double v corps de la fonction corps de la fonction
La premiegravere forme eacutetait la seule preacutevue par la deacutefinition initiale de Kernighan et Ritchie La
seconde a eacuteteacute introduite par la norme ANSI qui nrsquoa toutefois pas exclu lrsquoancienne3
Le langage C++ nrsquoaccepte quant agrave lui que la seconde forme
double fexple (int u double v) corps de la fonction
Remarque
Comme en C ANSI lorsqursquoune fonction fournit une valeur de type int le mot int peut
ecirctre omis dans lrsquoen-tecircte Cependant nous ne vous conseillons guegravere drsquoemployer cette
possibiliteacute qui nuit agrave la lisibiliteacute des programmes
2 Les prototypes en C++Nous venons de voir que le C++ eacutetait plus restrictif que le C ANSI en matiegravere de deacutefinition
de fonctions Il en va de mecircme pour les deacuteclarations de fonctions En C ANSI lorsque vous
utilisiez une fonction qui nrsquoavait pas eacuteteacute deacutefinie auparavant dans le mecircme fichier source
vous pouviez
bull ne pas la deacuteclarer (on consideacuterait alors que sa valeur de retour eacutetait de type int)
bull la deacuteclarer en ne preacutecisant que le type de la valeur de retour par exemple
double fexple
1 Ne confondez pas la deacutefinition drsquoune fonction et sa deacuteclaration La premiegravere correspond agrave la description agrave
lrsquoaide drsquoinstructions C de ce que fait une fonction La seconde correspond agrave une simple information (nom de la
fonction et eacuteventuellement type des arguments et de la valeur de retour) fournie au compilateur
2 On parle eacutegalement de reacutesultat fourni par la fonction de valeur retourneacutee
3 Dans le seul but de rendre compatible avec la norme des anciens programmes ou des anciens compilateurs
2 - Les prototypes en C++11
bull la deacuteclarer agrave lrsquoaide de ce que lrsquoon nomme un prototype par exemple
double fexple (int double)
En C++ un appel de fonction ne sera accepteacute que si le compilateur connaicirct le type des argu-
ments et celui de sa valeur de retour Cela signifie que la fonction en question doit avoir fait
lrsquoobjet drsquoune deacuteclaration sous la forme drsquoun prototype (ou agrave la rigueur avoir eacuteteacute preacuteala-
blement deacutefinie dans le mecircme fichier source1)
Nrsquooubliez pas que chaque fois que le compilateur rencontre un appel de fonction il compare
les types des arguments effectifs avec ceux des arguments muets correspondants2 En cas de
diffeacuterence il met en place les conversions neacutecessaires pour que la fonction reccediloive des argu-
ments du bon type Les conversions possibles ne se limitent pas aux conversions non deacutegra-
dantes (telles que char -gt double int -gt long) En effet elles comportent toutes les
conversions autoriseacutees lors drsquoune affectation On peut donc y rencontrer des conversions
deacutegradantes telles que int -gt char double -gt float double -gt int
Voici un exemple illustrant ce point
double fexple (int double) deacuteclaration de fexple
main()
int n
char c
double z res1 res2 res3
res1 = fexple (n z) appel normal - aucune conversion
res2 = fexple (c z) conversion avant appel de c en int
res3 = fexple (z n) conversion avant appel de z en int
et de n en double
Exemple de conversions de types lors de lrsquoappel drsquoune fonction
Lorsque la deacutefinition de la fonction et sa deacuteclaration (sous forme drsquoun prototype) figurent
dans le mecircme fichier source le compilateur est en mesure de veacuterifier la coheacuterence entre lrsquoen-
tecircte de la fonction et le prototype Srsquoil nrsquoy a pas correspondance de types (exacte cette fois)
on obtient une erreur de compilation Voici un exemple correct
1 Toutefois mecircme dans ce cas le prototype reste conseilleacute notamment pour eacuteviter tout problegraveme en cas drsquoeacuteclate-
ment du fichier source
2 Nous supposons que le compilateur connaicirct le type des arguments de la fonction ce qui est toujours le cas en C++
Les incompatibiliteacutes entre C++ et CCHAPITRE 2
12
double fexple (int double) deacuteclaration de fexple main()
deacutefinition de fexple double fexple (int u double v) corps de la fonction
En revanche celui-ci conduit agrave une erreur de compilation
double fexple (int float) deacuteclaration de fexple main() deacutefinition de fexple double fexple (int u double v) erreur de compilation corps de la fonction
Bien entendu si la deacutefinition de la fonction et sa deacuteclaration (donc son utilisation1) ne figu-
rent pas dans le mecircme fichier source aucun controcircle ne peut plus ecirctre effectueacute par le compi-
lateur En geacuteneacuteral agrave partir du moment ougrave lrsquoon doit utiliser une fonction en dehors du fichier
ougrave elle est deacutefinie (ou agrave partir de plusieurs fichiers source diffeacuterents) on place son prototype
dans un fichier en-tecircte ce dernier est incorporeacute en cas de besoin par la directive include
ce qui eacutevite tout risque de faute drsquoeacutecriture du prototype
Remarques
1 Comme en C la porteacutee du prototype est limiteacutee agrave
ndash la partie du fichier source situeacutee agrave la suite de sa deacuteclaration si elle figure agrave un niveau
global crsquoest-agrave-dire en dehors de toute deacutefinition de fonction2 crsquoeacutetait le cas du proto-
type de fexple dans nos preacuteceacutedents exemples
ndash la fonction dans laquelle il figure dans le cas contraire
2 Le prototype peut prendre une forme plus eacutetoffeacutee3 dans laquelle figurent eacutegalement des
noms drsquoarguments Ainsi le prototype de notre fonction fexple du deacutebut du
paragraphe 2 pourrait eacutegalement srsquoeacutecrire
double fexple (int a double x)
1 A moins qursquoon ne lrsquoait deacuteclareacutee sans lrsquoutiliser ce qui arrive freacutequemment lorsque lrsquoon fait appel agrave des fichiers en-
tecircte
2 Y compris la fonction main
3 On parle alors parfois de prototype complet par opposition agrave prototype reacuteduit
3 - Arguments et valeur de retour drsquoune fonction13
ou encore
double fexple (int u double v)
Dans ce cas les noms drsquoarguments (a et x dans le premier exemple u et v dans le
second) ne jouent aucun rocircle Ils sont purement et simplement ignoreacutes par le compila-
teur de sorte que ces prototypes restent parfaitement eacutequivalents aux preacuteceacutedents On
peut trouver un inteacuterecirct agrave cette possibiliteacute lorsque lrsquoon souhaite accompagner ce proto-
type de commentaires deacutecrivant le rocircle des diffeacuterents arguments (cela peut srsquoaveacuterer pra-
tique dans le cas ougrave lrsquoon place ce prototype dans un fichier en-tecircte)
3 Arguments et valeur de retour drsquoune fonction
31 Points communs agrave C et C++En C++ comme en C ANSI les arguments drsquoune fonction ainsi que la valeur de retour
peuvent
bull ne pas exister
bull ecirctre une valeur scalaire drsquoun des types de base (caractegraveres entiers flottants pointeurs)
bull ecirctre une valeur de type structure
La derniegravere possibiliteacute a eacuteteacute introduite dans le langage C par la norme ANSI Nous verrons
qursquoen C++ elle se geacuteneacuteralise aux objets de type classe Pour lrsquoinstant notez simplement qursquoil
subsiste en C++ comme en C ANSI une dispariteacute entre les tableaux et les structures puisque
bull il est possible de transmettre la valeur drsquoune structure aussi bien en argument qursquoen valeur
de retour
bull il nrsquoest pas possible de faire de mecircme avec les tableaux
Notez qursquoil srsquoagit lagrave drsquoune dispariteacute difficile agrave reacutesorber compte tenu de la volonteacute des con-
cepteurs du langage C de rendre eacutequivalents le nom drsquoun tableau et son adresse
Bien entendu il est toujours possible de transmettre lrsquoadresse drsquoun tableau et cette remarque
vaut eacutegalement pour une structure
32 Diffeacuterences entre C et C++En fait ces diffeacuterences ne portent que sur la syntaxe des en-tecirctes et des prototypes des fonc-
tions et uniquement dans deux cas preacutecis
bull fonctions sans arguments
bull fonctions sans valeur de retour
Les incompatibiliteacutes entre C++ et CCHAPITRE 2
14
321 Fonctions sans argumentsAlors qursquoen C ANSI on peut employer le mot void pour deacutefinir (en-tecircte) ou deacuteclarer (proto-
type) une fonction sans argument en C++ on fournit une liste vide Ainsi lagrave ougrave en C on
deacuteclarait
float fct (void)
on deacuteclarera en C++
float fct ( )
322 Fonctions sans valeur de retourEn C ANSI on peut utiliser le mot void pour deacutefinir (en-tecircte) ou deacuteclarer (prototype) une
fonction sans valeur de retour En C++ on doit absolument le faire comme dans cet
exemple
void fct (int double)
La deacuteclaration
fct (int double)
conduirait C++ agrave consideacuterer que fct fournit une valeur de retour de type int (voir la remarque
du paragraphe 1)
4 Le qualificatif constLa norme C ANSI a introduit le qualificatif const Il permet de speacutecifier qursquoun symbole cor-
respond agrave quelque chose dont la valeur ne doit pas changer ce qui peut permettre au com-
pilateur de signaler les tentatives de modification (lorsque cela lui est possible )
Si cela reste vrai en C++ un certain nombre de diffeacuterences importantes apparaissent qui con-
cernent la porteacutee du symbole concerneacute et son utilisation dans une expression
41 PorteacuteeLorsque const srsquoapplique agrave des variables locales automatiques aucune diffeacuterence nrsquoexiste
entre C et C++ la porteacutee eacutetant limiteacutee au bloc ou agrave la fonction concerneacutee par la deacuteclaration
En revanche lorsque const srsquoapplique agrave une variable globale C++ limite la porteacutee du sym-
bole au fichier source contenant la deacuteclaration (comme srsquoil avait reccedilu lrsquoattribut static) C
nrsquoimposait aucune limitation
Pourquoi cette diffeacuterence La principale raison reacuteside dans lrsquoideacutee qursquoavec la regravegle adopteacutee
par C++ il devient plus facile de remplacer certaines instructions define par des deacuteclarations
de constantes Ainsi lagrave ougrave en C vous proceacutediez de cette faccedilon
define N 8 define N 3
fichier 1 fichier 2
4 - Le qualificatif const15
vous pouvez en C++ proceacuteder ainsi
const int N = 8 const int N = 3
fichier 1 fichier 2
En C vous auriez obtenu une erreur au moment de lrsquoeacutedition de liens Vous auriez pu lrsquoeacuteviter
bull soit en deacuteclarant N static dans au moins un des deux fichiers (ou mieux dans les deux)
static const int N = 8 static const int N = 3
Cela aurait alors eacuteteacute parfaitement eacutequivalent agrave ce que fait C++ avec les premiegraveres
deacuteclarations
bull soit si N avait eu la mecircme valeur dans les deux fichiers en placcedilant dans le second fichier
extern const int N
Mais dans ce cas il ne se serait plus agi drsquoun remplacement de define
42 Utilisation dans une expressionRappelons que lrsquoon nomme expression constante une expression dont la valeur est calculeacutee
lors de la compilation Ainsi avec
const int p = 3
lrsquoexpression
2 p 5
nrsquoest pas une expression constante en C alors qursquoelle en est une en C++
Ce point est particuliegraverement sensible dans les deacuteclarations de tableaux (statiques ou automa-
tiques) dont les dimensions doivent obligatoirement ecirctre des expressions constantes (mecircme
pour les tableaux automatiques le compilateur doit connaicirctre la taille agrave reacuteserver sur la pile )
Ainsi les instructions
const int nel = 15 double t1 [nel + 1] t2[2 nel] [nel]
seront accepteacutees en C++ alors qursquoelles eacutetaient refuseacutees en C
Remarques
1 En toute rigueur la possibiliteacute que nous venons de deacutecrire ne constitue pas une incompa-
tibiliteacute entre C et C++ puisqursquoil srsquoagit drsquoune faciliteacute suppleacutementaire
2 Drsquoune maniegravere geacuteneacuterale C++ a eacuteteacute conccedilu pour limiter au maximum lrsquoemploi des direc-
tives du preacuteprocesseur (on devrait pouvoir se contenter de include et des directives
drsquoinclusion conditionnelle) Les modifications apporteacutees au qualificatif const vont
effectivement dans ce sens1
1 Encore faut-il que le programmeur C++ accepte de changer les habitudes qursquoil avait ducirc prendre en C (faute de
pouvoir faire autrement)
Les incompatibiliteacutes entre C++ et CCHAPITRE 2
16
5 Compatibiliteacute entre le type void et les autres pointeurs
En C ANSI le type geacuteneacuterique void est compatible avec les autres types pointeurs et ce
dans les deux sens Ainsi avec ces deacuteclarations
void gen int adi
ces deux affectations sont leacutegales en C ANSI
gen = adi adi = gen
Elles font intervenir des conversions implicites agrave savoir
int -gt void pour la premiegravere
void -gt int pour la seconde
En C++ seule la conversion drsquoun pointeur quelconque en void peut ecirctre implicite Ainsi
avec les deacuteclarations preacuteceacutedentes seule lrsquoaffectation
gen = adi
est accepteacutee Bien entendu il reste toujours possible de faire appel explicitement agrave la conver-
sion void -gt int en utilisant lrsquoopeacuterateur de cast
adi = (int ) gen
Remarque
On peut dire que la conversion drsquoun pointeur de type quelconque en void revient agrave ne
srsquointeacuteresser qursquoagrave lrsquoadresse correspondant au pointeur en ignorant son type En revanche
la conversion inverse de void en un pointeur de type donneacute revient agrave associer (peut-ecirctre
arbitrairement ) un type agrave une adresse Manifestement cette seconde possibiliteacute est plus
dangereuse que la premiegravere elle peut mecircme obliger le compilateur agrave introduire des modi-
fications de lrsquoadresse de deacutepart dans le seul but de respecter certaines contraintes drsquoali-
gnement (lieacutees au type drsquoarriveacutee) Crsquoest la raison pour laquelle cette conversion ne fait
plus partie des conversions implicites en C++
3
Les entreacutees-sortiesconversationnelles du C++
C++ dispose de toutes les routines offertes par la bibliothegraveque standard du C ANSI Mais il
comporte eacutegalement de nouvelles possibiliteacutes dentreacutees-sorties Celles-ci reposent sur les
notions de flots et de surdeacutefinition dopeacuterateur que nous naborderons quulteacuterieurement
Il ne serait pas judicieux cependant dattendre que vous ayez eacutetudieacute ces diffeacuterents points pour
commencer agrave eacutecrire des programmes complets reacutedigeacutes dans lesprit de C++ Cest pourquoi
nous allons vous preacutesenter ces nouvelles possibiliteacutes dentreacutees-sorties dans ce chapitre de
maniegravere assez informelle en nous limitant agrave ce que nous nommons laspect conversationnel
agrave savoir
bull la lecture sur lentreacutee standard geacuteneacuteralement le clavier
bull lrsquoeacutecriture sur la sortie standard geacuteneacuteralement lrsquoeacutecran
1 GeacuteneacuteraliteacutesLes routines (fonctions et macros) de la bibliothegraveque standard du C ANSI1 sont utilisables
en C++ en particulier celles relatives aux entreacutees-sorties pour ce faire il vous suffit
1 Lun des grands meacuterites de cette norme est de deacutefinir outre le langage C lui-mecircme les caracteacuteristiques dun certain
nombre de routines formant ce que lon nomme la bibliothegraveque standard
Les entreacutees-sorties conversationnelles du C++CHAPITRE 3
18
dinclure les fichiers en-tecircte habituels1 pour obtenir les prototypes et autres deacuteclarations
neacutecessaires agrave leur bonne utilisation
Mais C++ dispose en outre de possibiliteacutes dentreacutees-sorties ayant les caracteacuteristiques
suivantes
bull simpliciteacute dutilisation en particulier on pourra souvent saffranchir de la notion de for-
mat si chegravere aux fonctions de la famille printf ou scanf
bull diminution de la taille du module objet correspondant alors que par exemple un seul appel
de printf introduit obligatoirement dans le module objet un ensemble dinstructions en cou-
vrant toutes les eacuteventualiteacutes lemploi des nouvelles possibiliteacutes offertes par C++ nrsquointrodui-
ra que les seules instructions neacutecessaires
bull possibiliteacutes extensibles aux types que vous deacutefinirez vous-mecircme sous forme de classes
Nous allons examiner ces possibiliteacutes en nous limitant agrave lrsquoaspect conversationnel crsquoest-agrave-dire
agrave la lecture sur lrsquoentreacutee standard et agrave lrsquoaffichage sur la sortie standard
2 Affichage agrave lrsquoeacutecran
21 Quelques exemplesAvant deacutetudier les diffeacuterentes possibiliteacutes offertes par C++ examinons quelques exemples
Exemple 1Lagrave ougrave en C on eacutecrivait
printf (bonjour)
en C++ on utilisera
cout ltlt bonjour
Linterpreacutetation deacutetailleacutee de cette instruction neacutecessiterait des connaissances qui ne seront
introduites quulteacuterieurement Pour linstant il vous suffit de retenir les points suivants
bull cout deacutesigne un flot de sortie preacutedeacutefini associeacute agrave lentreacutee standard du C (stdout)
bull ltlt est un opeacuterateur dont lopeacuterande de gauche (ici cout) est un flot et lopeacuterande de droi-
te une expression de type quelconque Linstruction preacuteceacutedente peut ecirctre interpreacuteteacutee com-
me ceci le flot cout reccediloit la valeur bonjour
1 Toutefois la norme de C++ a preacutevu que les noms de ces fichiers soient preacutefixeacutes par c et que le suffixe h disparaisse
Par exemple on trouvera ltcstdiogt au lieu de ltstdiohgt
2 - Affichage agrave lrsquoeacutecran19
Exemple 2
Consideacuterons ces instructions
int n = 25
cout ltlt valeur
cout ltlt n
Elles affichent le reacutesultat suivant
valeur 25
Vous constatez que nous avons utiliseacute le mecircme opeacuterateur ltlt pour envoyer sur le flot cout
dabord une information de type chaicircne ensuite une information de type entier Le rocircle de
lopeacuterateur ltlt est manifestement diffeacuterent dans les deux cas dans le premier on a transmis
les caractegraveres de la chaicircne dans le second on a proceacutedeacute agrave un formatage1 pour convertir une
valeur binaire entiegravere en une suite de caractegraveres Cette possibiliteacute dattribuer plusieurs signifi-
cations agrave un mecircme opeacuterateur correspond agrave ce que lon nomme en C++ la surdeacutefinition dopeacute-
rateur (que nous aborderons en deacutetail au chapitre 9)
Exemple 3
Dans les exemples preacuteceacutedents nous avions utiliseacute une instruction diffeacuterente pour chaque
information transmise au flot cout En fait les deux instructions
cout ltlt valeur
cout ltlt n
peuvent se condenser en une seule
cout ltlt valeur ltlt n
Lagrave encore linterpreacutetation exacte de cette possibiliteacute sera fournie ulteacuterieurement mais dores
et deacutejagrave nous pouvons dire quelle reacuteside dans deux points
bull lopeacuterateur ltlt est associatif de gauche agrave droite comme lrsquoopeacuterateur drsquoorigine
bull le reacutesultat fourni par lopeacuterateur ltlt quand il reccediloit un flot en premier opeacuterande est ce mecircme
flot apregraves quil a reccedilu linformation concerneacutee
Ainsi linstruction preacuteceacutedente est eacutequivalente agrave
(cout ltlt valeur ) ltlt n
Celle-ci peut sinterpreacuteter comme ceci
bull dans un premier temps le flot cout reccediloit la chaicircne valeur
bull dans un deuxiegraveme temps le flot cout ltlt valeur cest-agrave-dire le flot cout augmenteacute de
bonjour reccediloit la valeur de n
Si cette interpreacutetation ne vous paraicirct pas eacutevidente il vous suffit dadmettre pour linstant
quune instruction telle que
cout ltlt ----- ltlt ----- ltlt ----- ltlt -----
1 Ici ce formatage est implicite dans le cas de printf on laurait expliciteacute (d)
Les entreacutees-sorties conversationnelles du C++CHAPITRE 3
20
permet denvoyer sur le flot cout les informations symboliseacutees par des traits dans lordre ougrave
elles apparaissent
22 Le fichier en-tecircte iostreamEn C lrsquoutilisation de fonctions telles que printf ou scanf neacutecessitait lrsquoincorporation du fichier
en-tecircte ltstdiohgt qui contenait les deacuteclaration approprieacutees De faccedilon comparable les deacutecla-
rations neacutecessaires agrave lrsquoutilisation des entreacutees-sorties speacutecifiques agrave C++ figurent dans un
fichier en-tecircte de nom ltiostreamgt Cependant lrsquoutilisation des symboles deacuteclareacutes dans ce
fichier iostream fait appel agrave la notion drsquoespace de noms introduite elle-aussi par la norme
Cette notion sera preacutesenteacutee sommairement au chapitre 4 et eacutetudieacutee plus en deacutetail au chapitre
24 Pour lrsquoinstant sachez qursquoelle oblige agrave introduire dans votre programme une instruction de
deacuteclaration using dont la porteacutee se deacutefinit comme celle de toute deacuteclaration et qui se preacute-
sente ainsi
using namespace std on utilisera les symboles deacutefinis dans lrsquoespace de noms standard srsquoappelant std
Remarque
Avant la norme on utilisait un fichier nommeacute ltiostreamhgt et lrsquoinstruction using nrsquoexis-
tait pas Certains environnements fonctionnent encore ainsi Parfois il faut recourir agrave
ltiostreamgt sans utiliser using1 (qui pourrait provoquer une erreur de compilation)
23 Les possibiliteacutes drsquoeacutecriture sur coutNous venons de voir des exemples deacutecriture de chaicircnes et dentiers Dune maniegravere geacuteneacuterale
vous pouvez utiliser lopeacuterateur ltlt pour envoyer sur cout la valeur dune expression de lun
des types suivants
bull type de base quelconque (caractegravere entier signeacute ou non flottant)
bull chaicircne de caractegraveres (char ) on obtient laffichage des caractegraveres constituant la chaicircne
bull pointeur autre que char on obtient ladresse correspondante (en hexadeacutecimal) si on
veut obtenir ladresse dune chaicircne de caractegraveres et non les caractegraveres quelle contient on
peut toujours convertir cette chaicircne (de type char ) en void
Voici un exemple complet de programme illustrant ces possibiliteacutes accompagneacute drsquoun exem-
ple drsquoexeacutecution dans un environnement PC2
1 Ces impleacutementations acceptent geacuteneacuteralement using pour les fichiers en-tecircte relatifs aux composants standard
2 Lrsquoimpleacutementation nrsquointervient ici que dans lrsquoaffichage de pointeurs
3 - Lecture au clavier21
include ltiostreamgtusing namespace std main() int n = 25 long p = 250000 unsigned q = 63000 char c = a float x = 123456789 double y = 123456789e16 char ch = bonjour int ad = amp n
cout ltlt valeur de n ltlt n ltlt n cout ltlt valeur de p ltlt p ltlt n cout ltlt caractere c ltlt c ltlt n cout ltlt valeur de q ltlt q ltlt n cout ltlt valeur de x ltlt x ltlt n cout ltlt valeur de y ltlt y ltlt n cout ltlt chaine ch ltlt ch ltlt n cout ltlt adresse de n ltlt ad ltlt n cout ltlt adresse de ch ltlt (void ) ch ltlt n
valeur de n 25valeur de p 250000caractere c avaleur de q 63000valeur de x 123457valeur de y 123457e+017chaine ch bonjouradresse de n 006AFDF4adresse de ch 0046D0D4
Les possibiliteacutes drsquoeacutecriture sur cout
3 Lecture au clavier
31 IntroductionDe mecircme quil existe un flot de sortie preacutedeacutefini cout associeacute agrave la sortie standard du C (stdout)
il existe un flot dentreacutee preacutedeacutefini nommeacute cin associeacute agrave lentreacutee standard du C (stdin) De
mecircme que lopeacuterateur ltlt permet denvoyer des informations sur un flot de sortie (donc en
particulier sur cout) lopeacuterateur gtgt permet de recevoir1 de linformation en provenance dun
flot dentreacutee (donc en particulier de cin)
1 On dit aussi extraire
Les entreacutees-sorties conversationnelles du C++CHAPITRE 3
22
Par exemple linstruction (n eacutetant de type int)
cin gtgt n
demandera de lire des caractegraveres sur le flot cin et de les convertir en une valeur de type int
Dune maniegravere geacuteneacuterale
cin gtgt n gtgt p
sera eacutequivalent agrave
(cin gtgt n) gtgt p
Pour donner une interpreacutetation imageacutee (et peu formelle) analogue agrave celle fournie pour cout
nous pouvons dire que la valeur de n est dabord extraite du flot cin ensuite la valeur de p
est extraite du flot cin gtgt n (comme pour ltlt le reacutesultat de lopeacuterateur gtgt est un flot) cest-
agrave-dire de ce quest devenu le flot cin apregraves quon en a extrait la valeur de n
32 Les diffeacuterentes possibiliteacutes de lecture sur cinDune maniegravere geacuteneacuterale vous pouvez utiliser lopeacuterateur gtgt pour acceacuteder agrave des informations
de type de base quelconque (signeacute ou non pour les types entiers) ou agrave des chaicircnes de caractegrave-
res1 (char ) Comme avec scanf ou getchar les caractegraveres frappeacutes au clavier sont apregraves vali-
dation par une fin de ligne enregistreacutes dans un tampon La lecture se fait dans ce tampon qui
est reacutealimenteacute en cas de besoin drsquoinformations suppleacutementaires Les informations du tampon
non exploiteacutees lors drsquoune lecture restent disponibles pour la lecture suivante
Par ailleurs une bonne part des conventions danalyse des caractegraveres lus sont les mecircmes que
celles employeacutees par scanf Ainsi
bull les diffeacuterentes informations sont seacutepareacutees par un ou plusieurs caractegraveres parmi ceux-ci2 es-
pace tabulation horizontale (t) ou verticale (v) fin de ligne (n) ou encore changement de
page (f)3
bull un caractegravere invalide pour lusage quon doit en faire (un point pour un entier une lettre
pour un nombre) arrecircte lexploration du flot comme si lon avait rencontreacute un seacuteparateur
mais ce caractegravere invalide sera agrave nouveau pris en compte lors dune prochaine lecture
En revanche contrairement agrave ce qui se produisait pour scanf la lecture dun caractegravere sur cin
commence par sauter les seacuteparateurs aussi nest-il pas possible de lire directement ces
caractegraveres seacuteparateurs Nous verrons comment y parvenir dans le chapitre consacreacute aux flots
1 Il nest pas preacutevu de lire la valeur dun pointeur en pratique cela naurait guegravere dinteacuterecirct et de plus preacutesenterait de
grand risques
2 On les appelle parfois des espaces-blancs (de langlais white spaces)
3 Dans les environnements PC la fin de ligne est repreacutesenteacutee par deux caractegraveres conseacutecutifs elle nrsquoen reste pas
moins vue par le programme comme un seul et unique caractegravere (n) cette remarque vaut en fait pour tous les fichiers
de type texte comme on le verra dans le chapitre consacreacute aux flots
3 - Lecture au clavier23
33 Exemple classique drsquoutilisation des seacuteparateursLe programme suivant illustre une situation classique dans laquelle la gestion des caractegraveres
seacuteparateurs ne pose pas de problegraveme particulier
include ltiostreamgtusing namespace std main() int n float x char t[81] do cout ltlt donnez un entier une chaine et un flottant cin gtgt n gtgt t gtgt x cout ltlt merci pour ltlt n ltlt ltlt t ltlt et ltlt x ltlt n while (n)
donnez un entier une chaine et un flottant 15 bonjour 825merci pour 15 bonjour et 825donnez un entier une chaine et un flottant 15
bonjour
825merci pour 15 bonjour et 825donnez un entier une chaine et un flottant 0 bye 0merci pour 0 bye et 0
Usage classique des seacuteparateurs
34 Lecture drsquoune suite de caractegraveresLrsquoexemple suivant montre que la faccedilon dont C++ gegravere les seacuteparateurs peut srsquoaveacuterer deacuterou-
tante lorsque lrsquoon est ameneacute agrave lire une suite de caractegraveres
include ltiostreamgtusing namespace std main() char tc[129] pour conserver les caractegraveres lus sur cin int i = 0 position courante dans le tableau tc
cout ltlt donnez une suite de caracteres terminee par un point n do cin gtgt tc[i] attention pas de test de deacutebordement dans tc while (tc[i++] = ) cout ltlt nnVoici les caracteres effectivement lus n i=0
Les entreacutees-sorties conversationnelles du C++CHAPITRE 3
24
do cout ltlt tc[i] while (tc[i++] = )
donnez une suite de caracteres terminee par un point Voyez comme
C++pose quelques problegravemeslors de la lecture dune
suite de caractegraveres
Voici les caracteres effectivement lus VoyezcommeC++posequelquesproblegravemeslorsdelalecturedunesuitedecaractegraveres
Quand on cherche agrave lire une suite de caractegraveres
35 Les risques induits par la lecture au clavierNous vous proposons deux exemples montrant que en C++ comme en C on peut dans cer-
tains cas aboutir agrave
bull un manque de synchronisme apparent entre le clavier et lrsquoeacutecran
bull un blocage de la lecture par un caractegravere invalide
bull une boucle infinie due agrave la preacutesence drsquoun caractegravere invalide
351 Manque de synchronisme entre clavier et eacutecran
Cet exemple illustre lrsquoexistence drsquoun tampon On y voit comment une lecture peut utiliser
une information non exploiteacutee par la preacuteceacutedente Ici lrsquoutilisateur nrsquoa pas agrave reacutepondre agrave la ques-
tion poseacutee agrave la troisiegraveme ligne
include ltiostreamgtusing namespace std main() int n p cout ltlt donnez une valeur pour n cin gtgt n cout ltlt merci pour ltlt n ltlt n cout ltlt donnez une valeur pour p cin gtgt p cout ltlt merci pour ltlt p ltlt n
3 - Lecture au clavier25
donnez une valeur pour n 12 25merci pour 12donnez une valeur pour p merci pour 25
Quand le clavier et lrsquoeacutecran semblent mal synchroniseacutes
352 Blocage de la lecture
Voyez cet exemple qui montre comment une maladresse de lrsquoutilisateur (ici frappe drsquoune let-
tre au lieu drsquoun chiffre) peut entraicircner un comportement deacuteconcertant du programme
include ltiostreamgtusing namespace std main() int n = 12 char c = rsquoarsquo cout ltlt donnez un entier et un caractere n cin gtgt n gtgt c cout ltlt merci pour ltlt n ltlt et ltlt c ltlt n cout ltlt donnez un caractere cin gtgt c cout ltlt merci pour ltlt c
donnez un entier et un caractere x 25merci pour 4467164 et adonnez un caractere merci pour a
Clavier bloqueacute par un caractegravere invalide
Lors de la premiegravere lecture de n lrsquoopeacuterateur gtgt a rencontreacute le caractegravere x manifestement
invalide Comme il nrsquoeacutetait alors pas capable de fabriquer une valeur entiegravere il a laisseacute la
valeur de n inchangeacutee et il a bloqueacute la lecture Ainsi la tentative de lecture ulteacuterieure drsquoun
caractegravere dans c nrsquoa pas deacutebloqueacute la situation la lecture eacutetant bloqueacutee la valeur de c est res-
teacutee inchangeacutee (le flot est resteacute bloqueacute et drsquoautres tentatives de lecture seraient traiteacutees de la
sorte)
353 Boucle infinie sur un caractegravere invalide
Ce petit exemple montre comment une maladresse lors de lrsquoexeacutecution (ici frappe drsquoune lettre
pour un chiffre) peut entraicircner le bouclage drsquoun programme
include ltiostreamgtusing namespace std
Les entreacutees-sorties conversationnelles du C++CHAPITRE 3
26
main() int n do cout ltlt donnez un nombre entier cin gtgt n cout ltlt voici son carre ltlt nn ltlt n while (n)
donnez un nombre entier 3voici son carre 9donnez un nombre entier agravevoici son carre 9donnez un nombre entier voici son carre 9donnez un nombre entier voici son carre 9donnez un nombre entier voici son carre 9donnez un nombre entier voici son carre 9
Boucle infinie sur un caractegravere invalide
Ici il faudra interrompre lrsquoexeacutecution du programme suivant une deacutemarche approprieacutee deacutepen-
dant de lrsquoenvironnement
Remarque
Il est possible drsquoameacuteliorer le comportement des programmes preacuteceacutedents Pour ce faire il
est neacutecessaire de faire appel agrave des eacuteleacutements qui seront preacutesenteacutes plus tard dans cet
ouvrage Nous verrons comment tester lrsquoeacutetat drsquoun flot et le deacutebloquer au paragraphe 3 du
chapitre 16 et mecircme geacuterer convenablement les lectures en utilisant un formatage en
meacutemoire au paragraphe 72 du chapitre 22
4
Les speacutecificiteacutes du C++
Le langage C++ dispose dun certain nombre de speacutecificiteacutes qui ne sont pas veacuteritablement
axeacutees sur la POO Il sagit essentiellement des suivantes
bull nouvelle forme de commentaire (en fin de ligne)
bull emplacement libre des deacuteclarations
bull notion de reacutefeacuterence
bull arguments par deacutefaut dans les deacuteclarations des fonctions
bull surdeacutefinition de fonctions
bull opeacuterateurs new et delete
bull fonctions en ligne (inline)
bull nouveaux opeacuterateurs de cast
bull existence drsquoun type booleacuteen bool
bull notion drsquoespace de noms
Dune maniegravere geacuteneacuterale ces possibiliteacutes seront pour la plupart souvent utiliseacutees conjointe-
ment avec celles de POO Cest ce qui justifie que nous les exposions degraves maintenant
Les speacutecificiteacutes du C++CHAPITRE 4
28
1 Le commentaire de fin de ligneEn C ANSI un commentaire peut ecirctre introduit en nimporte quel endroit ougrave un espace est
autoriseacute1 en le faisant preacuteceacuteder de et suivre de Il peut alors eacuteventuellement seacutetendre sur
plusieurs lignes
En C++ vous pouvez en outre utiliser des commentaires de fin de ligne en introduisant les
deux caractegraveres Dans ce cas tout ce qui est situeacute entre et la fin de la ligne est un com-
mentaire Notez que cette nouvelle possibiliteacute napporte quun surcroicirct de confort et de
seacutecuriteacute en effet une ligne telle que
cout ltlt bonjourn formule de politesse
peut toujours ecirctre eacutecrite ainsi
cout ltlt bonjourn formule de politesse
Vous pouvez mecircler (volontairement ou non ) les deux formules Dans ce cas notez que
dans
partie1 partie2 partie3
le commentaire ouvert par ne se termine quau prochain donc partie1 et partie2 sont
des commentaires tandis que partie3 est consideacutereacute comme appartenant aux instructions De
mecircme dans
partie1 partie2 partie3 partie4
le commentaire introduit par srsquoeacutetend jusqursquoagrave la fin de la ligne Il concerne donc partie2
partie3 et partie 4
Remarques
1 Le commentaire de fin de ligne constitue le seul cas ougrave la fin de ligne joue un rocircle signifi-
catif autre que celui dun simple seacuteparateur
2 Si lon utilise systeacutematiquement le commentaire de fin de ligne on peut alors faire
appel agrave et pour inhiber un ensemble dinstructions (contenant eacuteventuellement des
commentaires) en phase de mise au point
2 Deacuteclarations et initialisations
21 Regravegles geacuteneacuteralesC++ savegravere plus souple que le C ANSI en matiegravere de deacuteclarations Plus preacuteciseacutement en C++
il nest plus obligatoire de regrouper au deacutebut les deacuteclarations effectueacutees au sein dune fonc-
tion ou au sein dun bloc Celles-ci peuvent ecirctre effectueacutees ougrave bon vous semble pour peu
1 Donc en pratique nimporte ougrave pourvu quon ne coupe pas en deux un identificateur ou une chaicircne constante
2 - Deacuteclarations et initialisations29
quelles apparaissent avant que lon en ait besoin leur porteacutee reste limiteacutee agrave la partie du bloc
ou de la fonction suivant leur deacuteclaration
Par ailleurs les expressions utiliseacutees pour initialiser une variable scalaire peuvent ecirctre quel-
conques alors quen C elles ne peuvent faire intervenir que des variables dont la valeur est
connue degraves lentreacutee dans la fonction concerneacutee
Voici un exemple incorrect en C ANSI et accepteacute en C++
main() int n n = int q = 2n - 1
La deacuteclaration tardive de q permet de linitialiser1 avec une expression dont la valeur neacutetait
pas connue lors de lentreacutee dans la fonction (ici main)
22 Cas des instructions structureacuteesLinstruction suivante est accepteacutee en C++
for (int i=0 )
Lagrave encore la variable i a eacuteteacute deacuteclareacutee seulement au moment ougrave lon en avait besoin Sa porteacutee
est dapregraves la norme limiteacutee au bloc reacutegi par linstruction for On notera quil nexiste aucune
faccedilon deacutecrire des instructions eacutequivalentes en C
Cette possibiliteacute sapplique agrave toutes les instructions structureacutees crsquoest-agrave-dire aux instructions
for switch while et dowhile
Remarque
Le rocircle de ces deacuteclarations agrave linteacuterieur dinstructions structureacutees na eacuteteacute fixeacute que tardive-
ment par la norme ANSI Dans les versions anteacuterieures ce genre de deacuteclaration eacutetait cer-
tes autoriseacute mais tout se passait comme si elle figurait agrave lexteacuterieur du bloc ainsi
lexemple preacuteceacutedent eacutetait interpreacuteteacute comme si lon avait eacutecrit
int i
for (i=0 )
1 Noubliez pas quen C (comme en C++) il est possible dinitialiser une variable automatique scalaire agrave laide dune
expression quelconque
Les speacutecificiteacutes du C++CHAPITRE 4
30
3 La notion de reacutefeacuterenceEn C les arguments et la valeur de retour dune fonction sont transmis par valeur Pour simu-
ler en quelque sorte ce qui se nomme transmission par adresse dans dautres langages il est
alors neacutecessaire de jongler avec les pointeurs (la transmission se fait toujours par valeur
mais dans ce cas il sagit de la valeur dun pointeur) En C++ le principal inteacuterecirct de la notion
de reacutefeacuterence est quelle permet de laisser le compilateur mettre en œuvre les bonnes instruc-
tions pour assurer un transfert par adresse Pour mieux vous en faire saisir linteacuterecirct nous
vous proposons de vous rappeler comment il fallait proceacuteder en C
31 Transmission des arguments en C
Exemple 1Consideacuterons lrsquoexemple suivant qui illustre le fait qursquoen C les arguments sont toujours trans-
mis par valeur
include ltiostreamgtusing namespace std main()
void echange (int int) int n=10 p=20 cout ltlt avant appel ltlt n ltlt ltlt p ltlt n echange (n p) cout ltlt apres appel ltlt n ltlt ltlt p ltlt n
void echange (int a int b)
int c cout ltlt debut echange ltlt a ltlt ltlt b ltlt n c = a a = b b = c cout ltlt fin echange ltlt a ltlt ltlt b ltlt n
avant appel 10 20debut echange 10 20fin echange 20 10apres appel 10 20
Les arguments sont transmis par valeur
Lors de lappel drsquoechange il y a transmission des valeurs de n et de p on peut consideacuterer que
la fonction les a recopieacutees dans des emplacements locaux correspondant agrave ses arguments for-
mels a et b et quelle a effectivement travailleacute sur ces copies
3 - La notion de reacutefeacuterence31
Exemple 2
Bien entendu il est toujours possible de programmer une fonction echange pour quelle opegravere
sur des variables de la fonction qui lappelle il suffit tout simplement de lui fournir en argu-
ment ladresse de ces variables comme dans lexemple suivant
include ltiostreamgt
using namespace std
main()
void echange (int int )
int n=10 p=20
cout ltlt avant appel ltlt n ltlt ltlt p ltlt n
echange (ampn ampp)
cout ltlt apres appel ltlt n ltlt ltlt p ltlt n
void echange (int a int b)
int c
cout ltlt debut echange ltlt a ltlt ltlt b ltlt n
c = a a = b b = c
cout ltlt fin echange ltlt a ltlt ltlt b ltlt n
avant appel 10 20
debut echange 10 20
fin echange 20 10
apres appel 20 10
Mise en œuvre par le programmeur dune transmission par adresse
Notez bien les diffeacuterences entre les deux exemples agrave la fois dans leacutecriture de la fonction
echange mais aussi dans son appel Ce dernier point signifie que lutilisateur de la fonction
(qui nest pas toujours celui qui la eacutecrite) doit savoir sil faut lui transmettre une variable ou
son adresse1
32 Exemple de transmission drsquoargument par reacutefeacuterenceC++ permet de demander au compilateur de prendre lui-mecircme en charge la transmission des
arguments par adresse on parle alors de transmission dargument par reacutefeacuterence Le pro-
gramme ci-dessous montre comment appliquer un tel meacutecanisme agrave notre fonction echange
1 Certes ici si lon veut que la fonction echange puisse faire correctement son travail le choix simpose Mais il
nen va pas toujours ainsi
Les speacutecificiteacutes du C++CHAPITRE 4
32
include ltiostreamgt
using namespace std
main()
void echange (int amp int amp)
int n=10 p=20
cout ltlt avant appel ltlt n ltlt ltlt p ltlt n
echange (n p) attention ici pas de ampn ampp
cout ltlt apres appel ltlt n ltlt ltlt p ltlt n
void echange (int amp a int amp b)
int c
cout ltlt debut echange ltlt a ltlt ltlt b ltlt n
c = a a = b b = c
cout ltlt fin echange ltlt a ltlt ltlt b ltlt n
avant appel 10 20
deacutebut echange 10 20
fin echange 20 10
apregraves appel 20 10
Utilisation de la transmission darguments par reacutefeacuterence en C++
Dans linstruction
void echange (int amp a int amp b)
la notation int amp a signifie que a est une information de type int transmise par reacutefeacuterence
Notez bien que dans la fonction echange on utilise simplement le symbole a pour deacutesigner
cette variable dont la fonction aura reccedilu effectivement ladresse (et non la valeur) il nest
plus utile (et ce serait une erreur ) de faire appel agrave lopeacuterateur dindirection
Autrement dit il suffit davoir fait ce choix de transmission par reacutefeacuterence au niveau de
len-tecircte de la fonction pour que le processus soit entiegraverement pris en charge par le com-
pilateur1
Le mecircme pheacutenomegravene sapplique au niveau de lutilisation de la fonction Il suffit en effet
davoir speacutecifieacute dans le prototype les arguments (ici les deux) que lon souhaite voir trans-
mis par reacutefeacuterence Au niveau de lappel
echange (n p)
nous navons plus agrave nous preacuteoccuper du mode de transmission utiliseacute
1 Cette possibiliteacute est analogue agrave lutilisation du mot cleacute var dans len-tecircte dune proceacutedure en Pascal
3 - La notion de reacutefeacuterence33
Remarques
1 Nous avons parleacute de transmission par reacutefeacuterence drsquoun argument Mais on peut eacutegalement
dire qursquoon transmet agrave une fonction la reacutefeacuterence agrave un argument effectif cela traduit mieux
le fait que lrsquoinformation fournie agrave la fonction est bien lrsquoadresse drsquoun emplacement Mais
en comparaison avec la transmission par pointeurs ce mode de transmission offre une
certaine protection en effet la reacutefeacuterence agrave une variable ne peut pas ecirctre utiliseacutee directe-
ment (volontairement ou par erreur) pour acceacuteder agrave des emplacements voisins comme on
le ferait avec une adresse reccedilue par le biais drsquoun pointeur1
2 Lorsquon doit transmettre en argument la reacutefeacuterence agrave un pointeur on est ameneacute agrave utili-
ser ce genre deacutecriture
int amp adr adr est une reacutefeacuterence agrave un pointeur sur un int
En Java
La notion de reacutefeacuterence existe en Java mais elle est entiegraverement transparente au program-
meur Plus preacuteciseacutement les variables drsquoun type de base sont transmises par valeur tandis
que les objets sont transmis par reacutefeacuterence Il reste cependant possible de creacuteer explicite-
ment une copie drsquoun objet en utilisant une meacutethode approprieacutee dite de clonage
33 Proprieacuteteacutes de la transmission par reacutefeacuterence drsquoun argumentLa transmission par reacutefeacuterence drsquoun argument entraicircne un certain nombre de conseacutequences qui
nrsquoexistaient pas dans le cas de la transmission par valeur ou lorsqursquoon transmettait lrsquoadresse
drsquoun emplacement par le biais drsquoun pointeur
331 Induction de risques indirectsComme on lrsquoa vu sur lrsquoexemple preacuteceacutedent la transmission darguments par reacutefeacuterence simpli-
fie leacutecriture de la fonction correspondante Le choix du mode de transmission par reacutefeacuterence
est fait au moment de leacutecriture de la fonction concerneacutee Lutilisateur de la fonction na plus agrave
sen soucier ensuite si ce nest au niveau de la deacuteclaration du prototype de la fonction
(dailleurs ce prototype proviendra en geacuteneacuteral dun fichier en-tecircte)
En contrepartie lemploi de la transmission par reacutefeacuterence accroicirct les risques drsquoeffets de
bord non deacutesireacutes En effet lorsquil appelle une fonction lutilisateur ne sait plus sil trans-
met au bout du compte la valeur ou ladresse dun argument (la mecircme notation pouvant deacutesi-
gner lune ou lautre des deux possibiliteacutes) Il risque donc de modifier une variable dont il
pensait navoir transmis quune copie de la valeur
1 Il reste cependant possible drsquoaffecter agrave un pointeur lrsquoadresse de la variable dont on a reccedilu la reacutefeacuterence
Les speacutecificiteacutes du C++CHAPITRE 4
34
332 Absence de conversionDegraves lors qursquoune fonction a preacutevu une transmission par reacutefeacuterence les possibiliteacutes de conver-
sion preacutevues en cas de transmission par valeur disparaissent Voyez cet exemple
void f (int amp n) f reccediloit la reacutefeacuterence agrave un entierfloat x f(x) appel illeacutegal
A partir du moment ougrave la fonction reccediloit directement lrsquoadresse drsquoun emplacement qursquoelle
considegravere comme contenant un entier qursquoelle peut eacuteventuellement modifier il va de soi qursquoil
nrsquoest plus possible drsquoeffectuer une quelconque conversion de la valeur qui srsquoy trouve
La transmission par reacutefeacuterence impose donc agrave un argument effectif drsquoecirctre une lvalue du
type preacutevu pour lrsquoargument muet Nous verrons cependant au paragraphe 334 que les
arguments muets constants feront exception agrave cette regravegle et que dans ce cas des conversions
seront possibles
Remarque
Avec le prototype preacuteceacutedent de f lappel fct (ampn) serait rejeteacute En effet une reacutefeacuterence
nrsquoest pas un pointeur mecircme si au bout du compte lrsquoinformation transmise agrave la fonction
est une adresse En particulier lrsquousage qui est fait dans la traduction des instructions de la
fonction nrsquoest pas le mecircme dans le cas drsquoun pointeur on utilise directement sa valeur
quitte agrave mentionner une indirection avec lrsquoopeacuterateur avec une reacutefeacuterence lrsquoindirection
est ajouteacutee automatiquement par le compilateur
333 Cas drsquoun argument effectif constantSupposons quune fonction fct ait pour prototype
void fct (int amp)
Le compilateur refusera alors un appel de la forme suivante (n eacutetant de type int)
fct (3) incorrect f ne peut pas modifier une constante
Il en ira de mecircme pour
const int c = 15 fct (c) incorrect f ne peut pas modifier une constante
Ces refus sont logigues En effet si les appels preacuteceacutedents eacutetaient accepteacutes ils conduiraient agrave
fournir agrave fct ladresse dune constante (3 ou c) dont elle pourrait tregraves bien modifier la valeur1
1 On verra cependant au paragraphe 334 qursquoune telle transmission sera autoriseacutee si la fonction a effectivement preacutevu
dans son en-tecircte de travailler sur une constante
3 - La notion de reacutefeacuterence35
334 Cas drsquoun argument muet constant
En revanche consideacuterons une fonction de prototype
void fct1 (const int amp)
La deacuteclaration const int amp correspond agrave une reacutefeacuterence agrave une constante1 Les appels suivants
seront corrects
const int c = 15 fct1 (3) correct ici fct1 (c) correct ici
Lacceptation de ces instructions se justifie cette fois par le fait que fct a preacutevu de recevoir
une reacutefeacuterence agrave quelque chose de constant le risque de modification eacutevoqueacute preacuteceacutedemment
nexiste donc plus (du moins en theacuteorie car les possibiliteacutes de veacuterification du compilateur ne
sont pas complegravetes)
Qui plus est un appel tel fct1 (exp) (exp deacutesignant une expression quelconque) sera accepteacute
quel que soit le type de exp En effet dans ce cas il y a creacuteation dune variable temporaire (de
type int) qui recevra le reacutesultat de la conversion de exp en int Par exemple
void fct1 (const int amp) float x fct1 (x) correct f reccediloit la reacutefeacuterence agrave une variable temporaire contenant le reacutesultat de la conversion de x en int
En deacutefinitive lrsquoutilisation de const pour un argument muet transmis par reacutefeacuterence est lourde
de conseacutequences Certes comme on srsquoy attend cela amegravene le compilateur agrave verifier la cons-
tance de lrsquoargument concerneacute au sein de la fonction Mais de surcroicirct on autorise la creacuteation
drsquoune copie de lrsquoargument effectif (preacuteceacutedeacutee drsquoune conversion) degraves lors que ce dernier est
constant et drsquoun type diffeacuterent de celui attendu2
Cette remarque prendra encore plus drsquoacuiteacute dans le cas ougrave lrsquoargument en question sera un
objet
34 Transmission par reacutefeacuterence drsquoune valeur de retour
341 Introduction
Le meacutecanisme que nous venons dexposer pour la transmission des arguments sapplique agrave la
valeur de retour dune fonction Il est cependant moins naturel Consideacuterons ce petit
exemple
1 Contrairement agrave un pointeur une reacutefeacuterence est toujours constante de sorte que la deacuteclaration int const amp nrsquoa aucun
sens il nrsquoen allait pas de mecircme pour int const qui deacutesignait un pointeur constant sur un entier
2 Dans le cas drsquoune constante du mecircme type la norme laisse lrsquoimpleacutementation libre drsquoen faire ou non une copie
Geacuteneacuteralement la copie nrsquoest faite que pour les constantes drsquoun type scalaire
Les speacutecificiteacutes du C++CHAPITRE 4
36
int amp f () return n on suppose ici n de type int
Un appel de f provoquera la transmission en retour non plus drsquoune valeur mais de la reacutefeacute-
rence de n Cependant si lrsquoon utilise f drsquoune faccedilon usuelle
int p p = f() affecte agrave p la valeur situeacutee agrave la reacutefeacuterence fournie par f
une telle transmission ne semble guegravere preacutesenter drsquointeacuterecirct par rapport agrave une transmission par
valeur
Qui plus est il est neacutecessaire que n ne soit pas locale agrave la fonction sous peine de reacutecupeacuterer
une reacutefeacuterence (adresse) agrave quelque chose qui nrsquoexiste plus1
Effectivement on conccediloit qursquoon a plus rarement besoin de recevoir une reacutefeacuterence drsquoune
fonction que de lui en fournir
342 On obtient une lvalueDegraves lors qursquoune fonction renvoie une reacutefeacuterence il devient possible drsquoutiliser son appel
comme une lvalue Voyez cet exemple
int amp f () int n float x
f() = 2 n + 5 agrave la reacutefeacuterence fournie par f on range la valeur de lrsquoexpression 2n+5 de type intf() = x agrave la reacutefeacuterence fournie par f on range la valeur de x apregraves conversion en int
Le principal inteacuterecirct de la transmission par reacutefeacuterence drsquoune valeur de retour napparaicirctra que
lorsque nous eacutetudierons la surdeacutefinition dopeacuterateurs En effet dans certains cas il sera indis-
pensable quun opeacuterateur (en fait une fonction) fournisse une lvalue en reacutesultat Ce sera preacute-
ciseacutement le cas de lrsquoopeacuterateur []
343 ConversionContrairement agrave ce qui se produisait pour les arguments aucune contrainte drsquoexactitude de
type ne pegravese sur une valeur de retour car il reste toujours possible de la soumettre agrave une con-
version avant de lrsquoutiliser
1 Cette erreur srsquoapparente agrave celle due agrave la transmission en valeur de retour drsquoun pointeur sur une variable locale Elle
est encore plus difficile agrave deacutetecter dans la mesure ougrave le seul moment ougrave lrsquoon peut utiliser la reacutefeacuterence concerneacutee est
lrsquoappel lui-mecircme (alors qursquoun pointeur peut ecirctre utiliseacute agrave volonteacute) dans un environnement ne modifiant pas la
valeur drsquoune variable lors de sa destruction aucune erreur ne se manifeste ce nrsquoest que lors du portage dans un
environnement ayant un comportement diffeacuterent que les choses deviennent catastrophiques
3 - La notion de reacutefeacuterence37
inf amp f () float x x = f() OK on convertira en int la valeur situeacutee agrave la reacutefeacuterence reccedilue en retour de f
Nous verrons au paragraphe 344 qursquoil nrsquoen va plus de mecircme lorsque la valeur de retour est
une reacutefeacuterence agrave une constante
344 Valeur de retour et constance Si une fonction preacutevoit dans son en-tecircte un retour par reacutefeacuterence elle ne pourra pas mention-
ner de constante dans lrsquoinstruction return En effet si tel eacutetait le cas on prendrait le risque
que la fonction appelante modifie la valeur en question
int n=3 variable globalefloat x=35 idemint amp f1 () return 5 interdit return n OK return x interdit
Une exception a lieu lorsque lrsquoen-tete mentionne une reacutefeacuterence agrave une constante Dans ce cas
si return mentionne une constante on renverra la reacutefeacuterence drsquoune copie de cette constante
preacuteceacutedeacutee drsquoune eacuteventuelle conversion
const int amp f2 () return 5 OK on renvoie la reacutefeacuterence agrave une copie temporaire return n OK return x OK on renvoie la reacutefeacuterence agrave un int temporaire obtenu par conversion de la valeur de x
Mais on notera qursquoune telle reacutefeacuterence agrave une constante ne pourra plus ecirctre utiliseacutee comme une
lvalue
const int amp f () int n float x f() = 2 n + 5 erreur f() nrsquoest pas une lvaluef() = x idem
35 La reacutefeacuterence dune maniegravere geacuteneacuteraleNB Ce paragraphe peut ecirctre ignoreacute dans un premier temps
Lessentiel concernant la notion de reacutefeacuterence a eacuteteacute preacutesenteacute dans les paragraphes preacuteceacutedents
Cependant en toute rigueur la notion de reacutefeacuterence peut intervenir en dehors de la notion
dargument ou de valeur de retour Crsquoest ce que nous allons examiner ici
Les speacutecificiteacutes du C++CHAPITRE 4
38
351 La notion de reacutefeacuterence est plus geacuteneacuterale que celle drsquoargument
Dune maniegravere geacuteneacuterale il est possible de deacuteclarer un identificateur comme reacutefeacuterence dune
autre variable Consideacuterez par exemple ces instructions
int n
int amp p = n
La seconde signifie que p est une reacutefeacuterence agrave la variable n Ainsi dans la suite n et p deacutesigne-
ront le mecircme emplacement meacutemoire Par exemple avec
n = 3
cout ltlt p
nous obtiendrons la valeur 3
Remarque
Il nrsquoest pas possible de deacutefinir des pointeurs sur des reacutefeacuterences ni des tableaux de reacutefeacuteren-
ces
352 Initialisation de reacutefeacuterence
La deacuteclaration
int amp p = n
est en fait une deacuteclaration de reacutefeacuterence (ici p) accompagneacutee dune initialisation (agrave la reacutefeacuterence
de n) Dune faccedilon geacuteneacuterale il nest pas possible de deacuteclarer une reacutefeacuterence sans linitialiser
comme dans
int amp p incorrect car pas dinitialisation
Notez bien quune fois deacuteclareacutee (et initialiseacutee) une reacutefeacuterence ne peut plus ecirctre modifieacutee
Dailleurs aucun meacutecanisme nest preacutevu agrave cet effet si ayant deacuteclareacute int amp p=n vous eacutecri-
vez p=q il sagit obligatoirement de laffectation de la valeur de q agrave lemplacement de reacutefeacute-
rence p et non de la modification de la reacutefeacuterence q
On ne peut pas initialiser une reacutefeacuterence avec une constante La deacuteclaration suivante est
incorrecte
int amp n = 3 incorrecte
Cela est logique puisque si cette instruction eacutetait accepteacutee elle reviendrait agrave initialiser n avec
une reacutefeacuterence agrave la valeur (constante) 3 Dans ces conditions linstruction suivante conduirait
agrave modifier la valeur de la constante 3
n = 5
En revanche il est possible de deacutefinir des reacutefeacuterences constantes1 qui peuvent alors ecirctre initia-
liseacutees par des constantes Ainsi la deacuteclaration suivante est-elle correcte
const int amp n = 3
1 Depuis la version 3 de C++
4 - Les arguments par deacutefaut39
Elle geacutenegravere une variable temporaire (ayant une dureacutee de vie imposeacutee par lemplacement de la
deacuteclaration) contenant la valeur 3 et place sa reacutefeacuterence dans n On peut dire que tout se passe
comme si vous aviez eacutecrit
int temp = 3 int amp n = temp
avec cette diffeacuterence que dans le premier cas vous navez pas explicitement accegraves agrave la varia-
ble temporaire
Enfin les deacuteclarations suivantes sont encore correctes
float x const int amp n = x
Elles conduisent agrave la creacuteation drsquoune variable temporaire contenant le reacutesultat de la conversion
de x en int et placent sa reacutefeacuterence dans n Ici encore tout se passe comme si vous aviez eacutecrit
ceci (sans toutefois pouvoir acceacuteder agrave la variable temporaire temp)
float x int temp = x const int amp n = temp
Remarque
En toute rigueur lrsquoappel drsquoune fonction conduit agrave une initialisation des arguments
muets Dans le cas drsquoune reacutefeacuterence ce sont donc les regravegles que nous venons de deacutecrire qui
sont utiliseacutees Il en va de mecircme pour une valeur de retour On retrouve ainsi le comporte-
ment deacutecrit aux paragraphes 33 et 34
4 Les arguments par deacutefaut
41 ExemplesEn C ANSI il est indispensable que lappel dune fonction contienne autant darguments que
la fonction en attend effectivement C++ permet de saffranchir en partie de cette regravegle gracircce
agrave un meacutecanisme dattribution de valeurs par deacutefaut agrave des arguments
Exemple 1Consideacuterez lexemple suivant
include ltiostreamgtusing namespace std main() int n=10 p=20 void fct (int int=12) proto avec une valeur par deacutefaut fct (n p) appel normal fct (n) appel avec un seul argument fct() serait ici rejeteacute
Les speacutecificiteacutes du C++CHAPITRE 4
40
void fct (int a int b) en-tecircte habituelle
cout ltlt premier argument ltlt a ltlt n
cout ltlt second argument ltlt b ltlt n
premier argument 10
second argument 20
premier argument 10
second argument 12
Exemple de deacutefinition de valeur par deacutefaut pour un argument
La deacuteclaration de fct ici dans la fonction main est reacutealiseacutee par le prototype
void fct (int int = 12)
La deacuteclaration du second argument apparaicirct sous la forme
int = 12
Celle-ci preacutecise au compilateur que en cas dabsence de ce second argument dans un eacuteven-
tuel appel de fct il lui faudra faire comme si lappel avait eacuteteacute effectueacute avec cette valeur
Les deux appels de fct illustrent le pheacutenomegravene Notez quun appel tel que
fct ( )
serait rejeteacute agrave la compilation puisquici il neacutetait pas preacutevu de valeur par deacutefaut pour le pre-
mier argument de fct
Exemple 2
Voici un second exemple dans lequel nous avons preacutevu des valeurs par deacutefaut pour tous les
arguments de fct
include ltiostreamgt
using namespace std
main()
int n=10 p=20
void fct (int=0 int=12) proto avec deux valeurs par deacutefaut
fct (n p) appel normal
fct (n) appel avec un seul argument
fct () appel sans argument
void fct (int a int b) en-tecircte habituelle
cout ltlt premier argument ltlt a ltlt n
cout ltlt second argument ltlt b ltlt n
4 - Les arguments par deacutefaut41
premier argument 10
second argument 20
premier argument 10
second argument 12
premier argument 0
second argument 12
Exemple de deacutefinition de valeurs par deacutefaut pour plusieurs arguments
42 Les proprieacuteteacutes des arguments par deacutefautLorsquune deacuteclaration preacutevoit des valeurs par deacutefaut les arguments concerneacutes doivent obli-
gatoirement ecirctre les derniers de la liste
Par exemple une deacuteclaration telle que
float fexple (int = 5 long int = 3)
est interdite En fait une telle interdiction relegraveve du pur bon sens En effet si cette deacutecla-
ration eacutetait accepteacutee lappel suivant
fexple (10 20)
pourrait ecirctre interpreacuteteacute aussi bien comme
fexple (5 10 20)
que comme
fexple (10 20 3)
Notez bien que le meacutecanisme proposeacute par C++ revient agrave fixer les valeurs par deacutefaut dans la
deacuteclaration de la fonction et non dans sa deacutefinition Autrement dit ce nest pas le concep-
teur de la fonction qui deacutecide des valeurs par deacutefaut mais lutilisateur Une conseacutequence
immeacutediate de cette particulariteacute est que les arguments soumis agrave ce meacutecanisme et les valeurs
correspondantes peuvent varier dune utilisation agrave une autre en pratique toutefois ce point
ne sera guegravere exploiteacute ne serait-ce que parce que les deacuteclarations de fonctions sont en geacuteneacute-
ral figeacutees une fois pour toutes dans un fichier en-tecircte
Nous verrons que les arguments par deacutefaut se reacuteveacuteleront particuliegraverement preacutecieux lorsquil
sagira de fabriquer ce que lon nomme le constructeur dune classe
Remarques
1 Si lon souhaite attribuer une valeur par deacutefaut agrave un argument de type pointeur on prendra
garde de seacuteparer par au moins un espace les caractegraveres et = dans le cas contraire ils
seraient interpreacuteteacutes comme lopeacuterateur daffectation = ce qui conduirait agrave une erreur
Les speacutecificiteacutes du C++CHAPITRE 4
42
2 Les valeurs par deacutefaut ne sont pas neacutecessairement des expressions constantes Elles ne
peuvent toutefois pas faire intervenir de variables locales1
En Java
Les arguments par deacutefaut nrsquoexistent pas en Java
5 Surdeacutefinition de fonctionsDune maniegravere geacuteneacuterale on parle de surdeacutefinition2 lorsquun mecircme symbole possegravede plu-
sieurs significations diffeacuterentes le choix de lune des significations se faisant en fonction du
contexte Cest ainsi que C comme la plupart des langages eacutevolueacutes utilise la surdeacutefinition
dun certain nombre dopeacuterateurs Par exemple dans une expression telle que
a + b
la signification du + deacutepend du type des opeacuterandes a et b suivant le cas il pourra sagir dune
addition dentiers ou dune addition de flottants De mecircme le symbole peut deacutesigner sui-
vant le contexte une multiplication dentiers de flottants ou une indirection3
Un des grands atouts de C++ est de permettre la surdeacutefinition de la plupart des opeacuterateurs
(lorsquils sont associeacutes agrave la notion de classe) Lorsque nous eacutetudierons cet aspect nous ver-
rons quil repose en fait sur la surdeacutefinition de fonctions Cest cette derniegravere possibiliteacute que
nous proposons deacutetudier ici pour elle-mecircme
Pour pouvoir employer plusieurs fonctions de mecircme nom il faut bien sucircr un critegravere (autre
que le nom) permettant de choisir la bonne fonction En C++ ce choix est baseacute (comme pour
les opeacuterateurs citeacutes preacuteceacutedemment en exemple) sur le type des arguments Nous commence-
rons par vous preacutesenter un exemple complet montrant comment mettre en œuvre la surdeacutefini-
tion de fonctions Nous examinerons ensuite diffeacuterentes situations dappel dune fonction
surdeacutefinie avant deacutetudier les regravegles deacutetailleacutees qui preacutesident au choix de la bonne fonction
51 Mise en œuvre de la surdeacutefinition de fonctionsNous allons deacutefinir et utiliser deux fonctions nommeacutees sosie La premiegravere posseacutedera un argu-
ment de type int la seconde un argument de type double ce qui les diffeacuterencie bien lune de
lautre Pour que lrsquoexeacutecution du programme montre clairement la fonction effectivement
appeleacutee nous introduisons dans chacune une instruction daffichage approprieacutee Dans le pro-
1 Ni la valeur this pour les fonctions membres (this sera eacutetudieacute au chapitre 5)
2 De overloading parfois traduit par surcharge
3 On parle parfois de deacutereacutefeacuterence cela correspond agrave des situations telles que
int a
a = 5
5 - Surdeacutefinition de fonctions43
gramme dessai nous nous contentons dappeler successivement la fonction surdeacutefinie sosie
une premiegravere fois avec un argument de type int une seconde fois avec un argument de type
double
include ltiostreamgtusing namespace std
void sosie (int) les prototypesvoid sosie (double)
main() le programme de test int n=5
double x=25 sosie (n) sosie (x) void sosie (int a) la premiegravere fonction cout ltlt sosie numero I a = ltlt a ltlt n void sosie (double a) la deuxiegraveme fonction cout ltlt sosie numero II a = ltlt a ltlt n
sosie numero I a = 5sosie numero II a = 25
Exemple de surdeacutefinition de la fonction sosie
Vous constatez que le compilateur a bien mis en place lappel de la bonne fonction sosie au
vu de la liste darguments (ici reacuteduite agrave un seul)
52 Exemples de choix dune fonction surdeacutefinieNotre preacuteceacutedent exemple eacutetait simple dans la mesure ougrave nous appelions toujours la fonction
sosie avec un argument ayant exactement lun des types preacutevus dans les prototypes (int ou
double) On peut se demander ce qui se produirait si nous lappelions par exemple avec un
argument de type char long ou pointeur ou si lon avait affaire agrave des fonctions comportant
plusieurs arguments
Avant de preacutesenter les regravegles de deacutetermination dune fonction surdeacutefinie examinons tout
dabord quelques situations assez intuitives
Les speacutecificiteacutes du C++CHAPITRE 4
44
Exemple 1void sosie (int) sosie Ivoid sosie (double) sosie IIchar c float y sosie(c) appelle sosie I apregraves conversion de c en intsosie(y) appelle sosie II apregraves conversion de y en doublesosie(d) appelle sosie I apregraves conversion de d en int
Exemple 2void affiche (char ) affiche Ivoid affiche (void ) affiche IIchar ad1 double ad2 affiche (ad1) appelle affiche Iaffiche (ad2) appelle affiche II apregraves conversion de ad2 en void
Exemple 3void essai (int double) essai Ivoid essai (double int) essai IIint n p double z char c essai(nz) appelle essai Iessai(cz) appelle essai I apregraves conversion de c en intessai(np) erreur de compilation
Compte tenu de son ambiguiumlteacute le dernier appel conduit agrave une erreur de compilation En effet
deux possibiliteacutes existent ici convertir p en double sans modifier n et appeler essai I ou au
contraire convertir n en double sans modifier p et appeler essai II
Exemple 4void test (int n=0 double x=0) test Ivoid test (double y=0 int p=0) test IIint n double z test(nz) appelle test Itest(zn) appelle test IItest(n) appelle test Itest(z) appelle test IItest() erreur de compilation compte tenu de lrsquoambiguiumlteacute
Exemple 5
Avec ces deacuteclarations
void truc (int) truc Ivoid truc (const int) truc II
vous obtiendrez une erreur de compilation En effet C++ na pas preacutevu de distinguer int de
const int Cela se justifie par le fait que les deux fonctions truc recevant une copie de linfor-
5 - Surdeacutefinition de fonctions45
mation agrave traiter il nrsquoy a aucun risque de modifier la valeur originale Notez bien qursquoici
lrsquoerreur tient agrave la seule preacutesence des deacuteclarations de chose indeacutependamment drsquoun appel quel-
conque
Exemple 6
En revanche consideacuterez maintenant ces deacuteclarations
void chose (int ) chose Ivoid chose (const int ) chose IIint n = 3 const p = 5
Cette fois la distinction entre int et const int est justifieacutee En effet on peut tregraves bien preacute-
voir que chose I modifie la valeur de la lvalue1 dont elle reccediloit ladresse tandis que chose II
nen fait rien Cette distinction est possible en C+ de sorte que
chose (ampn) appelle chose Ichose (ampp) appelle chose II
Exemple 7
Les reflexions de lrsquoexemple preacuteceacutedent agrave propos des pointeurs srsquoappliquent aux reacutefeacuterences
void chose (int amp) chose Ivoid chose (const int amp) chose IIint n = 3 const int p = 5 chose (n) appelle chose Ichose (p) appelle chose II
Remarque
En dehors de la situation examineacutee ici on notera que le mode de transmission (reacutefeacuterence
ou valeur) nrsquointervient pas dans le choix drsquoune fonction surdeacutefinie Par exemple les
deacuteclarations suivantes conduiraient agrave une erreur de compilation due agrave leur ambiguiumlteacute
(indeacutependamment de tout appel de chose)
void chose (int amp) void chose (int)
Exemple 8
Lrsquoexemple preacuteceacutedent a montreacute comment on pouvait distinguer entre deux fonctions agissant
lrsquoune sur une reacutefeacuterence lrsquoautre sur une reacutefeacuterence constante Mais lrsquoutilisation de reacutefeacuterences
possegravede des conseacutequences plus subtiles comme le montrent ces exemples (revoyez eacuteventuel-
lement le paragraphe 334)
1 Rappelons quon nomme lvalue la reacutefeacuterence agrave quelque chose dont on peut modifier la valeur Ce terme provient de
la contraction de left value qui deacutesigne quelque chose qui peut apparaicirctre agrave gauche dun opeacuterateur daffectation
Les speacutecificiteacutes du C++CHAPITRE 4
46
void chose (int amp) chose Ivoid chose (const int amp) chose IIint n float x chose (n) appelle chose Ichose (2) appelle chose II apregraves copie eacuteventuelle de 2 dans un entier1 temporaire dont la reacutefeacuterence sera transmise agrave chosechose (x) appelle chose II apregraves conversion de la valeur de x en un entier temporaire dont la reacutefeacuterence sera transmise agrave chose
53 Regravegles de recherche dune fonction surdeacutefiniePour linstant nous vous preacutesenterons plutocirct la philosophie geacuteneacuterale ce qui sera suffisant
pour leacutetude des chapitres suivants Au cours de cet ouvrage nous serons ameneacute agrave vous
apporter des informations compleacutementaires De plus lensemble de toutes ces regravegles sont
reprises en Annexe A
531 Cas des fonctions agrave un argument
Le compilateur recherche la meilleure correspondance possible Bien entendu pour pou-
voir deacutefinir ce quest cette meilleure correspondance il faut quil dispose dun critegravere deacuteva-
luation Pour ce faire il est preacutevu diffeacuterents niveaux de correspondance
1) Correspondance exacte on distingue bien les uns des autres les diffeacuterents types de base
en tenant compte de leur eacuteventuel attribut de signe2 de plus comme on la vu dans les
exemples preacuteceacutedents lattribut const peut intervenir dans le cas de pointeurs ou de reacutefeacuteren-
ces
2) Correspondance avec promotions numeacuteriques cest-agrave-dire essentiellement
char et short ndashgt int
float ndashgt double
Rappelons qursquoun argument transmis par reacutefeacuterence ne peut ecirctre soumis agrave aucune conver-
sion sauf lorsqursquoil srsquoagit de la reacutefeacuterence agrave une constante
3) Conversions dites standard il sagit des conversions leacutegales en C++ cest-agrave-dire de celles
qui peuvent ecirctre imposeacutees par une affectation (sans opeacuterateur de cast) cette fois il peut
sagir de conversions deacutegradantes puisque notamment toute conversion dun type numeacute-
rique en un autre type numeacuterique est accepteacutee
Dautres niveaux sont preacutevus en particulier on pourra faire intervenir ce que lon nomme
des conversions deacutefinies par lutilisateur (CDU) qui ne seront eacutetudieacutees qursquoau
chapitre 10
1 Comme lrsquoautorise la norme lrsquoimpleacutementation est libre de faire ou non une copie dans ce cas
2 Attention en C++ char est diffeacuterent de signed char et de unsigned char
5 - Surdeacutefinition de fonctions47
Lagrave encore un argument transmis par reacutefeacuterence ne pourra ecirctre soumis agrave aucune conversion
sauf srsquoil srsquoagit drsquoune reacutefeacuterence agrave une constante
La recherche sarrecircte au premier niveau ayant permis de trouver une correspondance qui doit
alors ecirctre unique Si plusieurs fonctions conviennent au mecircme niveau de correspondance il y
a erreur de compilation due agrave lambiguiumlteacute rencontreacutee Bien entendu si aucune fonction ne
convient agrave aucun niveau il y a aussi erreur de compilation
532 Cas des fonctions agrave plusieurs arguments
Lideacutee geacuteneacuterale est quil doit se deacutegager une fonction meilleure que toutes les autres Pour
ce faire le compilateur seacutelectionne pour chaque argument la ou les fonctions qui reacutealisent
la meilleure correspondance (au sens de la hieacuterarchie deacutefinie ci-dessus) Parmi lensemble des
fonctions ainsi seacutelectionneacutees il choisit celle (si elle existe et si elle est unique) qui reacutealise
pour chaque argument une correspondance au moins eacutegale agrave celle de toutes les autres fonc-
tions1
Si plusieurs fonctions conviennent lagrave encore on aura une erreur de compilation due agrave lambi-
guiumlteacute rencontreacutee De mecircme si aucune fonction ne convient il y aura erreur de compilation
Notez que les fonctions comportant un ou plusieurs arguments par deacutefaut sont traiteacutees
comme si plusieurs fonctions diffeacuterentes avaient eacuteteacute deacutefinies avec un nombre croissant
darguments
533 Le meacutecanisme de la surdeacutefinition de fonctions
Jusquici nous avons examineacute la maniegravere dont le compilateur faisait le choix de la bonne
fonction en raisonnant sur un seul fichier source agrave la fois Mais il serait tout agrave fait
envisageable
bull de compiler dans un premier temps un fichier source contenant la deacutefinition de nos deux
fonctions nommeacutees sosie
bull dutiliser ulteacuterieurement ces fonctions dans un autre fichier source en nous contentant den
fournir les prototypes
Or pour que cela soit possible leacutediteur de liens doit ecirctre en mesure deffectuer le lien entre le
choix opeacutereacute par le compilateur et la bonne fonction figurant dans un autre module objet
Cette reconnaissance est baseacutee sur la modification par le compilateur des noms externes
des fonctions celui-ci fabrique un nouveau nom fondeacute dune part sur le nom interne de la
fonction dautre part sur le nombre et la nature de ses arguments
Il est tregraves important de noter que ce meacutecanisme sapplique agrave toutes les fonctions quelles
soient surdeacutefinies ou non (il est impossible de savoir si une fonction compileacutee dans un fichier
source sera surdeacutefinie dans un autre) On voit donc quun problegraveme se pose degraves que lon sou-
haite utiliser dans un programme C++ une fonction eacutecrite et compileacutee en C (ou dans un autre
1 Ce qui revient agrave dire quil considegravere lintersection des ensembles constitueacutes des fonctions reacutealisant la meilleure
correspondance pour chacun des arguments
Les speacutecificiteacutes du C++CHAPITRE 4
48
langage utilisant les mecircmes conventions dappels de fonction notamment lassembleur ou le
Fortran) En effet une telle fonction ne voit pas son nom modifieacute suivant le meacutecanisme eacutevo-
queacute Une solution existe toutefois deacuteclarer une telle fonction en faisant preacuteceacuteder son proto-
type de la mention extern C Par exemple si nous avons eacutecrit et compileacute en C une fonction
den-tecircte
double fct (int n char c)
et que nous souhaitons lrsquoutiliser dans un programme C++ il nous suffira de fournir son pro-
totype de la faccedilon suivante
extern C double fct (int char)
Remarques
1 Il existe une forme collective de la deacuteclaration extern qui se preacutesente ainsi
extern C void exple (int) double chose (int char float)
2 Le problegraveme eacutevoqueacute pour les fonctions C (assembleur ou Fortran) se pose a priori
pour toutes les fonctions de la bibliothegraveque standard C que lon reacuteutilise en C++ En
fait dans la plupart des environnements cet aspect est automatiquement pris en charge
au niveau des fichiers en-tecircte correspondants (ils contiennent des deacuteclarations extern
conditionnelles)
3 Il est possible drsquoemployer au sein drsquoun mecircme programme C++ une fonction C (assem-
bleur ou Fortran) et une ou plusieurs autres fonctions C++ de mecircme nom (mais drsquoargu-
ments diffeacuterents) Par exemple nous pouvons utiliser dans un programme C++ la
fonction fct preacuteceacutedente et deux fonctions C++ drsquoen-tecircte
void fct (double x) void fct (float y)
en proceacutedant ainsi
extern C void fct (int) void fct (double) void fct (float)
Suivant la nature de lrsquoargument drsquoappel de fct il y aura bien appel de lrsquoune des trois
fonctions fct Notez qursquoil nrsquoest pas possible de mentionner plusieurs fonctions C de
nom fct
En Java
La surdeacutefinition des fonctions existe en Java Les regravegles de recherche de la bonne fonc-
tion sont extrecircmement simples car il existe peu de possibiliteacutes de conversions implicites
6 - Les opeacuterateurs new et delete49
6 Les opeacuterateurs new et deleteEn langage C la gestion dynamique de meacutemoire fait appel agrave des fonctions de la bibliothegraveque
standard telles que malloc et free Bien entendu comme toutes les fonctions standard celles-
ci restent utilisables en C++
Mais dans le contexte de la Programmation Orienteacutee Objet C++ a introduit deux nouveaux
opeacuterateurs new et delete particuliegraverement adapteacutes agrave la gestion dynamique dobjets Ces opeacute-
rateurs peuvent eacutegalement ecirctre utiliseacutes pour des variables classiques1 Dans ces conditions
par souci dhomogeacuteneacuteiteacute et de simpliciteacute il est plus raisonnable en C++ dutiliser systeacutemati-
quement ces opeacuterateurs que lon ait affaire agrave des variables classiques ou agrave des objets Cest
pourquoi nous vous les preacutesentons degraves maintenant
61 Exemples dutilisation de new
Exemple 1Avec la deacuteclaration
int ad
linstruction
ad = new int
permet dallouer lespace meacutemoire neacutecessaire pour un eacuteleacutement de type int et daffecter agrave ad
ladresse correspondante En C vous auriez obtenu le mecircme reacutesultat en eacutecrivant (lopeacuterateur
de cast ici int eacutetant facultatif)
ad = (int ) malloc (sizeof (int))
Comme les deacuteclarations ont un emplacement libre en C++ vous pouvez mecircme deacuteclarer la
variable ad au moment ougrave vous en avez besoin en eacutecrivant par exemple
int ad = new int
Exemple 2Avec la deacuteclaration
char adc
linstruction
adc = new char[100]
alloue lemplacement neacutecessaire pour un tableau de 100 caractegraveres et place ladresse (de
deacutebut) dans adc En C vous auriez obtenu le mecircme reacutesultat en eacutecrivant
adc = (char ) malloc (100)
1 Un objet eacutetant en fait une variable dun type particulier
Les speacutecificiteacutes du C++CHAPITRE 4
50
62 Syntaxe et rocircle de newLrsquoopeacuterateur unaire (agrave un seul opeacuterande) new sutilise ainsi
new type
ougrave type repreacutesente un type absolument quelconque Il fournit comme reacutesultat un pointeur (de
type type) sur lemplacement correspondant lorsque lallocation a reacuteussi
Lrsquoopeacuterateur new accepte eacutegalement une syntaxe de la forme
new type [n]
ougrave n deacutesigne une expression entiegravere quelconque (non neacutegative) Cette instruction alloue alors
lemplacement neacutecessaire pour n eacuteleacutements du type indiqueacute si lrsquoopeacuteration a reacuteussi elle fournit
en reacutesultat un pointeur (toujours de type type ) sur le premier eacuteleacutement de ce tableau
La norme de C++ preacutevoit quen cas deacutechec new deacuteclenche ce que lon nomme une exception
de type bad_alloc Ce meacutecanisme de gestion des exceptions est eacutetudieacute en deacutetail au chapitre
17 Vous verrez que si rien nest preacutevu par le programmeur pour traiter une exception le pro-
gramme sinterrompt
Il est cependant possible de demander agrave new de se comporter diffeacuteremment en cas drsquoeacutechec
comme nous le verrons aux paragraphes 65 et 66
Remarque
En toute rigueur new peut ecirctre utiliseacute pour allouer un emplacement pour un tableau agrave plu-
sieurs dimensions par exemple
new type [n] [10]
Dans ce cas new fournit un pointeur sur des tableaux de 10 entiers (dont le type se note
type () [10]) Drsquoune maniegravere geacuteneacuterale la premiegravere dimension peut ecirctre une expression
entiegravere quelconque les autres doivent obligatoirement ecirctre des expressions constantes
Cette possibiliteacute est rarement utiliseacutee en pratique
En Java
Il existe eacutegalement un opeacuterateur new Mais il ne srsquoapplique pas aux types de base il est
reacuteserveacute aux objets
63 Exemples dutilisation de lopeacuterateur deleteLorsque lon souhaite libeacuterer un emplacement alloueacute preacutealablement par new on doit absolu-
ment utiliser lopeacuterateur delete Ainsi pour libeacuterer les emplacements creacuteeacutes dans les exemples
du paragraphe 61 on eacutecrit
delete ad
6 - Les opeacuterateurs new et delete51
pour lemplacement alloueacute par
ad = new int
et
delete adc
pour lemplacement alloueacute par
adc = new char [100]
64 Syntaxe et rocircle de lopeacuterateur deleteLa syntaxe usuelle de lopeacuterateur delete est la suivante (adresse eacutetant une expression devant
avoir comme valeur un pointeur sur un emplacement alloueacute par new)
delete adresse
Notez bien que le comportement du programme nest absolument pas deacutefini lorsque
bull vous libeacuterez par delete un emplacement deacutejagrave libeacutereacute nous verrons que des preacutecautions
devront ecirctre prises lorsque lon deacutefinit des constructeurs et des destructeurs de certains
objets
bull vous fournissez agrave delete une mauvaise adresse ou un pointeur obtenu autrement que par
new (malloc par exemple)
Remarque
Il existe une autre syntaxe de delete de la forme delete [] adresse qui nintervient que
dans le cas de tableaux dobjets et dont nous parlerons au paragraphe 7 du chapitre 7
65 Lrsquoopeacuterateur new (nothrow)Comme lrsquoa montreacute le paragraphe 62 new deacuteclenche une exception bad_alloc en cas
drsquoeacutechec Dans les versions drsquoavant la norme new fournissait (comme malloc) un pointeur nul
en cas drsquoeacutechec Avec la norme on peut retrouver ce comportement en utilisant au lieu de
new lrsquoopeacuterateur new(stdnothrow) (std est superflu degraves lors qursquoon a bien deacuteclareacute cet
espace de nom par using)
A titre drsquoexemple voici un programme qui alloue des emplacements pour des tableaux
dentiers dont la taille est fournie en donneacutee et ce jusquagrave ce quil ny ait plus suffisam-
ment de place (notez quici nous utilisons toujours la mecircme variable adr pour recevoir
les diffeacuterentes adresses des tableaux ce qui dans un programme reacuteel ne serait probable-
ment pas acceptable)
include ltcstdlibgt ancien ltstdlibhgt pour exitinclude ltiostreamgtusing namespace std
Les speacutecificiteacutes du C++CHAPITRE 4
52
main() long taille int adr int nbloc cout ltlt Taille souhaitee cin gtgt taille for (nbloc=1 nbloc++) adr = new (nothrow) int [taille] if (adr==0) cout ltlt manque de memoire n exit (-1) cout ltlt Allocation bloc numero ltlt nbloc ltlt n
Taille souhaitee 4000000Allocation bloc numero 1Allocation bloc numero 2Allocation bloc numero 3 manque de memoire
Exemple drsquoutilisation de new(nothrow)
66 Gestion des deacutebordements de meacutemoire avec set_new_handler
Comme on lrsquoa vu au paragraphe 62 new deacuteclenche une exception bad_alloc en cas drsquoeacutechec
Mais il est eacutegalement possible de deacutefinir une fonction de votre choix et de demander quelle
soit appeleacutee en cas de manque de meacutemoire Il vous suffit pour cela dappeler la fonction
set_new_handler en lui fournissant en argument ladresse de la fonction que vous avez preacute-
vue pour traiter le cas de manque de meacutemoire Voici comment nous pourrions adapter
lrsquoexemple preacuteceacutedent
include ltcstdlibgt ancien ltstdlibhgt pour exitinclude ltnewgt pour set_new_handler (parfois ltnewhgt)include ltiostreamgtusing namespace std
main() void deborde () proto fonction appeleacutee en cas manque meacutemoire set_new_handler (deborde) long taille int adr int nbloc cout ltlt Taille de bloc souhaitee (en entiers) cin gtgt taille
7 - La speacutecification inline53
for (nbloc=1 nbloc++) adr = new int [taille] cout ltlt Allocation bloc numero ltlt nbloc ltlt n
void deborde () fonction appeleacutee en cas de manque meacutemoire cout ltlt Memoire insuffisanten cout ltlt Abandon de lexecutionn exit (-1)
Taille de bloc souhaitee (en entiers) 4000000Allocation bloc numero 1Allocation bloc numero 2Allocation bloc numero 3Memoire insuffisante pour allouer 16000000 octetsAbandon de lexecutionPress any key to continue
Exemple drsquoutilisation de set_new_handler
7 La speacutecification inline
71 Rappels concernant les macros et les fonctionsEn langage C vous savez quil existe deux notions assez voisines agrave savoir les macros et les
fonctions Une macro et une fonction sutilisent apparemment de la mecircme faccedilon en faisant
suivre leur nom dune liste drsquoarguments entre parenthegraveses Cependant
bull les instructions correspondant agrave une macro sont incorporeacutees agrave votre programme1 chaque
fois que vous lappelez
bull les instructions correspondant agrave une fonction sont geacuteneacutereacutees une seule fois2 agrave chaque ap-
pel il y aura seulement mise en place des instructions neacutecessaires pour eacutetablir la liaison en-
tre le programme3 et la fonction sauvegarde de leacutetat courant (valeurs de certains
registres par exemple) recopie des valeurs des arguments branchement avec conservation
de ladresse de retour recopie de la valeur de retour restauration de leacutetat courant et retour
dans le programme Toutes ces instructions neacutecessaires agrave la mise en œuvre de lappel de la
1 En toute rigueur lincorporation est reacutealiseacutee au niveau du preacuteprocesseur lequel introduit les instructions en langage
C correspondant agrave lexpansion de la macro ces instructions peuvent dailleurs varier dun appel agrave un autre
2 Par le compilateur cette fois sous forme dinstructions en langage machine
3 En toute rigueur il faudrait plutocirct parler de fonction appelante
Les speacutecificiteacutes du C++CHAPITRE 4
54
fonction nexistent pas dans le cas de la macro On peut donc dire que la fonction permet de
gagner de lespace meacutemoire en contrepartie dune perte de temps dexeacutecution Bien entendu
la perte de temps sera dautant plus faible que la fonction sera de taille importante
bull contrairement aux fonctions les macros peuvent entraicircner des effets de bord indeacutesirables
ou du moins impreacutevus Voici deux exemples
- Si une macro introduit de nouvelles variables celles-ci peuvent interfeacuterer avec dautres
variables de mecircme nom Ce risque nexiste pas avec une fonction sauf si lon utilise vo-
lontairement cette fois des variables globales
- Une macro deacutefinie par
carre(x) x x
et appeleacutee par
carre(a++)
geacuteneacuterera les instructions
a++ a++
qui increacutementent deux fois la variable a
En C lorsque lon a besoin dune fonction courte et que le temps drsquoexeacutecution est primordial
on fait geacuteneacuteralement appel agrave une macro malgreacute les inconveacutenients que cela implique En C++
il existe une solution plus satisfaisante utiliser une fonction en ligne (inline)
72 Utilisation de fonctions en ligneUne fonction en ligne se deacutefinit et sutilise comme une fonction ordinaire agrave la seule diffeacute-
rence quon fait preacuteceacuteder son en-tecircte de la speacutecification inline En voici un exemple
include ltcmathgt ancien ltmathhgt pour sqrtinclude ltiostreamgtusing namespace std deacutefinition drsquoune fonction en ligne inline double norme (double vec[3]) int i double s = 0
for (i=0 ilt3 i++) s+= vec[i] vec[i] return sqrt(s) exemple drsquoutilisation main() double v1[3] v2[3] int i for (i=0 ilt3 i++) v1[i] = i v2[i] = 2i-1
7 - La speacutecification inline55
cout ltlt norme de v1 ltlt norme(v1) ltlt n cout ltlt norme de v2 ltlt norme(v2) ltlt n
norme de v1 223607norme de v2 331662
Exemple de deacutefinition et dutilisation dune fonction en ligne
La fonction norme a pour but de calculer la norme dun vecteur agrave trois composantes quon lui
fournit en argument
La preacutesence du mot inline demande au compilateur de traiter la fonction norme diffeacuteremment
dune fonction ordinaire A chaque appel de norme le compilateur devra incorporer au sein
du programme les instructions correspondantes (en langage machine1) Le meacutecanisme habi-
tuel de gestion de lappel et du retour nexistera plus (il ny a plus besoin de sauvegardes
recopies) ce qui permet une eacuteconomie de temps En revanche les instructions correspon-
dantes seront geacuteneacutereacutees agrave chaque appel ce qui consommera une quantiteacute de meacutemoire croissant
avec le nombre dappels
Il est tregraves important de noter que par sa nature mecircme une fonction en ligne doit ecirctre deacutefinie
dans le mecircme fichier source que celui ougrave on lutilise Elle ne peut plus ecirctre compileacutee
seacutepareacutement Cela explique quil nrsquoest pas neacutecessaire de deacuteclarer une telle fonction (sauf si
elle est utiliseacutee au sein dun fichier source avant drsquoecirctre deacutefinie) Ainsi on ne trouve pas dans
notre exemple de deacuteclaration telle que
double norme (double)
Cette absence de possibiliteacute de compilation seacutepareacutee constitue une contrepartie notable aux
avantages offerts par la fonction en ligne En effet pour quune mecircme fonction en ligne
puisse ecirctre partageacutee par diffeacuterents programmes il faudra absolument la placer dans un fichier
en-tecircte2 (comme on le fait avec une macro)
Comparaison entre macro fonction et fonction en ligne
1 Notez quil sagit bien ici dun travail effectueacute par le compilateur lui-mecircme alors que dans le cas dune macro un
travail comparable eacutetait effectueacute par le preacuteprocesseur
2 A moins den eacutecrire plusieurs fois la deacutefinition ce qui ne serait pas raisonnable compte tenu des risques derreurs
que cela comporte
Avantages Inconveacutenients
Macro - Eacuteconomie de temps dexeacutecution
- Perte despace meacutemoire- Risque deffets de bord non deacutesireacutes- Pas de compilation seacutepareacutee possible
Fonction - Eacuteconomie despace meacutemoire- Compilation seacutepareacutee possible
- Perte de temps dexeacutecution
Fonction en ligne - Eacuteconomie de temps dexeacutecution
- Perte despace meacutemoire- Pas de compilation seacutepareacutee possible
Les speacutecificiteacutes du C++CHAPITRE 4
56
Remarque
La deacuteclaration inline constitue une demande effectueacutee aupregraves du compilateur Ce dernier
peut eacuteventuellement (par exemple si la fonction est volumineuse) ne pas lintroduire en
ligne et en faire une fonction ordinaire De mecircme si vous utilisez quelque part (au sein du
fichier source concerneacute) ladresse dune fonction deacuteclareacutee inline le compilateur en fera
une fonction ordinaire (dans le cas contraire il serait incapable de lui attribuer une
adresse et encore moins de mettre en place un eacuteventuel appel dune fonction situeacutee agrave cette
adresse)
8 Les espaces de nomsLorsque lon doit utiliser plusieurs bibliothegraveques dans un programme on peut ecirctre confronteacute
au problegraveme dit de pollution de lespace des noms lieacute agrave ce quun mecircme identificateur peut
tregraves bien avoir eacuteteacute utiliseacute par plusieurs bibliothegraveques Le mecircme problegraveme peut se poser agrave un
degreacute moindre toutefois lors du deacuteveloppement de gros programmes Cest la raison pour
laquelle la norme ANSI du C++ a introduit le concept drsquoespace de noms Il sagit simple-
ment de donner un nom agrave un espace de deacuteclarations en proceacutedant ainsi
namespace une_bibli deacuteclarations usuelles
Pour se reacutefeacuterer agrave des identificateurs deacutefinis dans cet espace de noms on utilisera une instruc-
tion using
using namespace une_bibli ici les identificateurs de une_bibli sont connus
On peut lever lambiguiumlteacute risquant dapparaicirctre lorsquon utilise plusieurs espaces de noms
comportant des identificateurs identiques il suffit pour cela de faire appel agrave lopeacuterateur de
reacutesolution de porteacutee par exemple
une_biblipoint on se reacutefegravere agrave lidentificateur point de lespace de noms une_bibli
On peut aussi utiliser linstruction using pour faire un choix permanent
using une_biblipoint doreacutenavant lidentificateur point employeacute seul correspondra agrave celui deacutefini dans lespace de noms une_bibli
Tous les identificateurs des fichiers en-tecircte standard sont deacutefinis dans lrsquoespace de noms std
aussi est-il neacutecessaire de recourir systeacutematiquement agrave lrsquoinstruction
using namespace std utilisation des fichiers en-tecircte standard
Geacuteneacuteralement cette instruction figurera agrave un niveau global comme nous lrsquoavons fait dans les
exemples preacuteceacutedents En revanche elle ne peut apparaicirctre que si lrsquoespace de noms qursquoelle
mentionne existe deacutejagrave en pratique cela signifie que cette instruction sera placeacutee apregraves
lrsquoinclusion des fichiers en-tecircte
9 - Le type bool57
Ces quelques consideacuterations seront suffisantes pour lrsquoinstant Les espaces de noms seront
eacutetudieacutes en deacutetail au chapitre 24
Remarque
Certains environnements ne respectent pas encore la norme sur le plan des espaces de
noms Lrsquoinstruction using peut alors ecirctre indisponible En geacuteneacuteral dans de tels environne-
ments les fichiers en-tecircte sont encore suffixeacutes par h Certains environnements permettent
drsquoutiliser indiffeacuteremment ltiostreamgt avec using ou ltiostreamhgt sans using1
9 Le type boolCe type est tout naturellement formeacute de deux valeurs noteacutees true et false En theacuteorie les
reacutesultats des comparaisons ou des combinaisons logiques doivent ecirctre de ce type Toutefois
il existe des conversions implicites
bull de bool en numeacuterique true devenant 1 et false devenant 0
bull de numeacuterique (y compris flottant) en bool toute valeur non nulle devenant true et zeacutero de-
venant false
Dans ces conditions tout se passe finalement comme si bool eacutetait un type eacutenumeacutereacute ainsi
deacutefini
typedef enum false=0 true bool
En deacutefinitive ce type bool sert surtout agrave apporter plus de clarteacute aux programmes sans remet-
tre en cause quoi que ce soit
10 Les nouveaux opeacuterateurs de castEn C++ comme en C il est possible de reacutealiser des conversions explicites agrave laide dun opeacutera-
teur de cast Les conversions accepteacutees comportent naturellement toutes les conversions
implicites leacutegales auxquelles sajoutent quelques autres pouvant ecirctre deacutegradantes ou deacutepen-
dantes de limpleacutementation
La norme ANSI de C++ a conserveacute ces possibiliteacutes tout en proposant de nouveaux opeacutera-
teurs de cast plus eacutevocateurs de la nature de la conversion et de sa portabiliteacute eacuteventuelle Ils
sont formeacutes comme les opeacuterateurs classiques agrave laide du type souhaiteacute compleacuteteacute dun mot cleacute
preacutecisant le type de conversion
bull const_cast pour ajouter ou supprimer agrave un type lun des modificateurs const ou volatile (les
types de deacutepart et drsquoarriver ne devant diffeacuterer que par ces modificateurs)
1 Une telle instruction using provoquera une erreur si aucun fichier en-tecircte se reacutefeacuterant agrave lrsquoespace de noms std nrsquoa eacuteteacute
inclus Ce sera notamment le cas si on se limite agrave ltiostreamhgt
Les speacutecificiteacutes du C++CHAPITRE 4
58
bull reinterpret_cast pour les conversions dont le reacutesultat deacutepend de limpleacutementation typique-
ment il sagit des conversions dentier vers pointeur et de pointeur vers entier
bull static_cast pour les conversions indeacutependantes de limpleacutementation En fait les conversions
de pointeur vers pointeur entrent dans cette cateacutegorie malgreacute les diffeacuterences qui peuvent ap-
paraicirctre agrave cause des contraintes dalignement propres agrave chaque impleacutementation
Voici quelques exemples commenteacutes
include ltiostreamgtusing namespace std
main () int n = 12 const int ad1 = ampn int ad2 ad2 = (int ) ad1 ancienne forme conseilleacutee (ad2 = ad1 serait rejeteacutee) ad2 = const_cast ltint gt (ad1) forme ANSI conseilleacutee ad1 = ad2 leacutegale ad1 = (const int ) ad2 forme ancienne conseilleacutee ad1 = const_cast ltconst int gt (ad2) forme ANSI conseilleacutee
const int p = 12 const int const ad3 = ampp int ad4
ad4 = (int ) ad3 ancienne forme conseilleacutee (ad4 = ad3 serait rejeteacutee) ad4 = const_cast ltint gt (ad3) forme ANSI conseilleacutee
Exemples dutilisation de lopeacuterateur const_cast ltgt
include ltiostreamgtusing namespace std
main () long n int adi adi = (int ) n ancienne forme conseilleacutee (adi = n serait rejeteacutee) adi = reinterpret_cast ltint gt (n) forme ANSI conseilleacutee
n = (long) adi ancienne forme conseilleacutee (n = adi serait rejeteacutee)
10 - Les nouveaux opeacuterateurs de cast59
n = reinterpret_cast ltlonggt (adi) forme ANSI conseilleacutee
int p p = n accepteacutee p = (int) n ancienne forme conseilleacutee p = static_cast ltintgt (n) forme ANSI conseilleacutee
Exemples dutilisation des opeacuterateurs reinterpret_cast ltgt et static_cast
Remarque
Ces nouveaux opeacuterateurs napportent aucune possibiliteacute de conversion suppleacutementaire Il
nen ira pas de mecircme de lopeacuterateur dynamic_cast eacutetudieacute au chapitre 15
5
Classes et objets
Avec ce chapitre nous abordons veacuteritablement les possibiliteacutes de POO de C++ Comme
nous lavons dit dans le premier chapitre celles-ci reposent entiegraverement sur le concept de
classe Une classe est la geacuteneacuteralisation de la notion de type deacutefini par lutilisateur1 dans
lequel se trouvent associeacutees agrave la fois des donneacutees (membres donneacutees) et des meacutethodes (fonc-
tions membres) En POO pure les donneacutees sont encapsuleacutees et leur accegraves ne peut se faire
que par le biais des meacutethodes C++ vous autorise agrave nrsquoencapsuler quune partie des donneacutees
dune classe (cette deacutemarche reste cependant fortement deacuteconseilleacutee) Il existe mecircme un type
particulier correspondant agrave la geacuteneacuteralisation du type structure du C dans lequel sont effecti-
vement associeacutees des donneacutees et des meacutethodes mais sans aucune encapsulation
En pratique ce nouveau type structure du C++ sera rarement employeacute sous cette forme geacuteneacute-
raliseacutee En revanche sur le plan conceptuel il correspond agrave un cas particulier de la classe il
sagit en effet dune classe dans laquelle aucune donneacutee nest encapsuleacutee Cest pour cette rai-
son que nous commencerons par preacutesenter le type structure de C++ (mot cleacute struct) ce qui
nous permettra dans un premier temps de nous limiter agrave la faccedilon de mettre en œuvre lasso-
ciation des donneacutees et des meacutethodes Nous ne verrons qursquoensuite comment sexprime lencap-
sulation au sein dune classe (mot cleacute class)
Comme une classe (ou une structure) nest quun simple type deacutefini par lutilisateur les objets
possegravedent les mecircmes caracteacuteristiques que les variables ordinaires en particulier en ce qui
concerne leurs diffeacuterentes classes dallocation (statique automatique dynamique) Cepen-
dant pour rester simple et nous consacrer au concept de classe nous ne consideacutererons dans
1 En C les types deacutefinis par lutilisateur sont les structures les unions et les eacutenumeacuterations
Classes et objetsCHAPITRE 5
62
ce chapitre que des objets automatiques (deacuteclareacutes au sein dune fonction quelconque) ce qui
correspond au cas le plus naturel Ce nest qursquoau chapitre 7 que nous aborderons les autres
classes dallocation des objets
Par ailleurs nous introduirons ici les notions tregraves importantes de constructeur et de destruc-
teur (il ny a guegravere dobjets inteacuteressants qui ny fassent pas appel) Lagrave encore compte tenu de
la richesse de cette notion et de son interfeacuterence avec dautres (comme les classes dalloca-
tion) il vous faudra attendre la fin du chapitre 7 pour en connaicirctre toutes les possibiliteacutes
Nous eacutetudierons ensuite ce qursquoon nomme les membres donneacutees statiques ainsi que la
maniegravere de les intialiser Enfin ce premier des trois chapitres consacreacutes aux classes nous per-
mettra de voir comment exploiter une classe en C++ en recourant aux possibiliteacutes de compi-
lation seacutepareacutee
1 Les structures en C++
11 Rappel les structures en CEn C une deacuteclaration telle que
struct point int x int y
deacutefinit un type structure nommeacute point (on dit aussi un modegravele de structure nommeacute point ou
parfois par abus de langage la structure point1) Quant agrave x et y on dit que ce sont des champs
ou des membres2 de la structure point
On deacuteclare ensuite des variables du type point par des instructions telles que
struct point a b
Celle-ci reacuteserve lemplacement pour deux structures nommeacutees a et b de type point Laccegraves
aux membres (champs) de a ou de b se fait agrave laide de lopeacuterateur point () par exemple ay
deacutesigne le membre y de la structure a
En C++ nous allons pouvoir dans une structure associer aux donneacutees constitueacutees par ses
membres des meacutethodes quon nommera fonctions membres Rappelons que puisque les
donneacutees ne sont pas encapsuleacutees dans la structure une telle association est relativement arti-
ficielle et son principal inteacuterecirct est de preacuteparer agrave la notion de classe
1 Dans ce cas il y a ambiguiumlteacute car le mecircme mot structure deacutesignera agrave la fois un type et des objets dun type structure
Comme le contexte permet geacuteneacuteralement de trancher nous utiliserons souvent ce terme
2 Cest plutocirct ce dernier terme que lon emploiera en C++
1 - Les structures en C++63
12 Deacuteclaration dune structure comportant des fonctions membres
Supposons que nous souhaitions associer agrave la structure point preacuteceacutedente trois fonctions
bull initialise pour attribuer des valeurs aux coordonneacutees dun point
bull deplace pour modifier les coordonneacutees dun point
bull affiche pour afficher un point ici nous nous contenterons par souci de simpliciteacute daffi-
cher les coordonneacutees du point
Voici comment nous pourrions deacuteclarer notre structure point
------------ Deacuteclaration du type point ------------- struct point deacuteclaration classique des donneacutees int x int y deacuteclaration des fonctions membre (meacutethodes) void initialise (int int) void deplace (int int) void affiche ()
Deacuteclaration dune structure comportant des meacutethodes
Outre la deacuteclaration classique des donneacutees1 apparaissent les deacuteclarations (en-tecirctes) de nos
trois fonctions Notez bien que la deacutefinition de ces fonctions ne figure pas agrave ce niveau de sim-
ple deacuteclaration elle sera reacutealiseacutee par ailleurs comme nous le verrons un peu plus loin
Ici nous avons preacutevu que la fonction membre initialise recevra en arguments deux valeurs de
type int A ce niveau rien nrsquoindique dit lusage qui sera fait de ces deux valeurs Ici bien
entendu nous avons eacutecrit len-tecircte de initialise en ayant agrave lesprit lideacutee quelle affecterait aux
membres x et y les valeurs reccedilues en arguments Les mecircmes remarques srsquoappliquent aux deux
autres fonctions membres
Vous vous attendiez peut-ecirctre agrave trouver pour chaque fonction membre un argument suppleacute-
mentaire preacutecisant la structure (variable) sur laquelle elle doit opeacuterer2 Nous verrons com-
ment cette information sera automatiquement fournie agrave la fonction membre lors de son appel
1 On parle parfois de variables par analogie avec les fonctions membres
2 Pour quune telle information ne soit pas neacutecessaire il faudrait dupliquer les fonctions membres en autant
dexemplaires quil y a de structures de type point ce qui serait particuliegraverement inefficace
Classes et objetsCHAPITRE 5
64
13 Deacutefinition des fonctions membresElle se fait par une deacutefinition (presque) classique de fonction Voici ce que pourrait ecirctre la
deacutefinition de initialise
void pointinitialise (int abs int ord)
x = abs
y = ord
Dans len-tecircte le nom de la fonction est
pointinitialise
Le symbole correspond agrave ce que lon nomme lopeacuterateur de reacutesolution de porteacutee lequel
sert agrave modifier la porteacutee drsquoun identificateur Ici il signifie que lidentificateur initialise con-
cerneacute est celui deacutefini dans point En labsence de ce preacutefixe (point) nous deacutefinirions
effectivement une fonction nommeacutee initialise mais celle-ci ne serait plus associeacutee agrave point il
sagirait dune fonction ordinaire nommeacutee initialise et non plus de la fonction membre ini-
tialise de la structure point
Si nous examinons maintenant le corps de la fonction initialise nous trouvons une
affectation
x = abs
Le symbole abs deacutesigne classiquement la valeur reccedilue en premier argument Mais x quant agrave
lui nest ni un argument ni une variable locale En fait x deacutesigne le membre x correspondant
au type point (cette association eacutetant reacutealiseacutee par le point de len-tecircte) Quelle sera preacuteciseacute-
ment la structure1 concerneacutee Lagrave encore nous verrons comment cette information sera trans-
mise automatiquement agrave la fonction initialise lors de son appel
Nous ninsistons pas sur la deacutefinition des deux autres fonctions membres vous trouverez ci-
dessous lensemble des deacutefinitions des trois fonctions
----- Deacutefinition des fonctions membres du type point ----
include ltiostreamgt
using namespace std
void pointinitialise (int abs int ord)
x = abs y = ord
void pointdeplace (int dx int dy)
x += dx y += dy
1 Ici le terme structure est bien synonyme de variable de type structure
1 - Les structures en C++65
void pointaffiche ()
cout ltlt Je suis en ltlt x ltlt ltlt y ltlt n
Deacutefinition des fonctions membres
Les instructions ci-dessus ne peuvent pas ecirctre compileacutees seules Elles neacutecessitent lrsquoincorpora-
tion des instructions de deacuteclaration correspondantes preacutesenteacutees au paragraphe 12 Celles-ci
peuvent figurer dans le mecircme fichier ou mieux faire lrsquoobjet drsquoun fichier en-tecircte seacutepareacute
14 Utilisation dune structure comportant des fonctions membresDisposant du type point tel quil vient drsquoecirctre deacuteclareacute au paragraphe 12 et deacutefini au paragra-
phe 13 nous pouvons deacuteclarer autant de structures de ce type que nous le souhaitons Par
exemple
point a b 1
deacuteclare deux structures nommeacutees a et b chacune posseacutedant des membres x et y et disposant
des trois meacutethodes initialise deplace et affiche A ce propos nous pouvons dores et deacutejagrave
remarquer que si chaque structure dispose en propre de chacun de ses membres il nen va pas
de mecircme des fonctions membres celles-ci ne sont geacuteneacutereacutees2 quune seule fois (le contraire
conduirait manifestement agrave un gaspillage de meacutemoire )
Laccegraves aux membres x et y de nos structures a et b pourrait se deacuterouler comme en C ainsi
pourrions-nous eacutecrire
ax = 5
Ce faisant nous acceacutederions directement aux donneacutees sans passer par lintermeacutediaire des
meacutethodes Certes nous ne respecterions pas le principe dencapsulation mais dans ce cas
preacutecis (de structure et pas encore de classe) ce serait accepteacute en C++3
On procegravede de la mecircme faccedilon pour lappel dune fonction membre Ainsi
ainitialise (52)
signifie appeler la fonction membre initialise pour la structure a en lui transmettant en
arguments les valeurs 5 et 2 Si lon fait abstraction du preacutefixe a cet appel est analogue agrave un
appel classique de fonction Bien entendu cest justement ce preacutefixe qui va preacuteciser agrave la fonc-
tion membre quelle est la structure sur laquelle elle doit opeacuterer Ainsi linstruction
x = abs
1 Ou struct point a b le mot struct est facultatif en C++
2 Exception faite des fonctions en ligne
3 Ici justement les fonctions membres preacutevues pour notre structure point permettent de respecter le principe
dencapsulation
Classes et objetsCHAPITRE 5
66
de pointinitialise placera dans le champ x de la structure a la valeur reccedilue pour abs (cest-agrave-
dire 5)
Remarques
1 Un appel tel que ainitialise (52) pourrait ecirctre remplaceacute par
ax = 5 ay = 2
Nous verrons preacuteciseacutement quil nen ira plus de mecircme dans le cas dune (vraie) classe
pour peu quon y ait convenablement encapsuleacute les donneacutees
2 En jargon POO on dit eacutegalement que ainitialise (5 2) constitue lenvoi dun mes-
sage (initialise accompagneacute des informations 5 et 2) agrave lobjet a
15 Exemple reacutecapitulatifVoici un programme reprenant la deacuteclaration du type point la deacutefinition de ses fonctions
membres et un exemple drsquoutilisation dans la fonction main
include ltiostreamgtusing namespace std ------------ Deacuteclaration du type point ------------- struct point deacuteclaration classique des donneacutees int x int y deacuteclaration des fonctions membres (meacutethodes) void initialise (int int) void deplace (int int) void affiche ()
----- Deacutefinition des fonctions membres du type point ---- void pointinitialise (int abs int ord) x = abs y = ord void pointdeplace (int dx int dy) x += dx y += dy void pointaffiche () cout ltlt Je suis en ltlt x ltlt ltlt y ltlt n main() point a b ainitialise (5 2) aaffiche () adeplace (-2 4) aaffiche () binitialise (1-1) baffiche ()
2 - Notion de classe67
Je suis en 5 2
Je suis en 3 6
Je suis en 1 -1
Exemple de deacutefinition et dutilisation du type point
Remarques
1 La syntaxe mecircme de lappel dune fonction membre fait que celle-ci reccediloit obligatoire-
ment un argument implicite du type de la structure correspondante Une fonction membre
ne peut pas ecirctre appeleacutee comme une fonction ordinaire Par exemple cette instruction
initialise (31)
sera rejeteacutee agrave la compilation (agrave moins quil nexiste par ailleurs une fonction ordinaire
nommeacutee initialise)
2 Dans la deacuteclaration dune structure il est permis (mais geacuteneacuteralement peu conseilleacute)
dintroduire les donneacutees et les fonctions dans un ordre quelconque (nous avons systeacute-
matiquement placeacute les donneacutees avant les fonctions)
3 Dans notre exemple de programme complet nous avons introduit
ndash la deacuteclaration du type point
ndash la deacutefinition des fonctions membres
ndash la fonction (main) utilisant le type point
Mais bien entendu il serait possible de compiler seacutepareacutement le type point crsquoest drsquoailleurs
ainsi que lrsquoon pourra reacuteutiliser un composant logiciel Nous y reviendrons au
paragraphe 6
4 Il reste possible de deacuteclarer des structures geacuteneacuteraliseacutees anonymes mais cela est tregraves peu
utiliseacute
2 Notion de classeComme nous lavons deacutejagrave dit en C++ la structure est un cas particulier de la classe Plus preacute-
ciseacutement une classe sera une structure dans laquelle seulement certains membres etou fonc-
tions membres seront publics cest-agrave-dire accessibles de lexteacuterieur les autres membres
eacutetant dits priveacutes
La deacuteclaration dune classe est voisine de celle dune structure En effet il suffit
bull de remplacer le mot cleacute struct par le mot cleacute class
Classes et objetsCHAPITRE 5
68
bull de preacuteciser quels sont les membres publics (fonctions ou donneacutees) et les membres priveacutes en
utilisant les mots cleacutes public et private
Par exemple faisons de notre preacuteceacutedente structure point une classe dans laquelle tous les
membres donneacutees sont priveacutes et toutes les fonctions membres sont publiques Sa deacuteclaration
serait simplement la suivante
------------ Deacuteclaration de la classe point ------------- class point deacuteclaration des membres priveacutes private facultatif (voir remarque 4) int x int y deacuteclaration des membres publics public void initialise (int int) void deplace (int int) void affiche ()
Deacuteclaration dune classe
Ici les membres nommeacutes x et y sont priveacutes tandis que les fonctions membres nommeacutees ini-
tialise deplace et affiche sont publiques
En ce qui concerne la deacutefinition des fonctions membres dune classe elle se fait exactement
de la mecircme maniegravere que celle des fonctions membres dune structure (quil sagisse de fonc-
tions publiques ou priveacutees) En particulier ces fonctions membres ont accegraves agrave lensemble des
membres (publics ou priveacutes) de la classe
Lutilisation dune classe se fait eacutegalement comme celle dune structure A titre indicatif voici
ce que devient le programme du paragraphe 15 lorsque lon remplace la structure point par la
classe point telle que nous venons de la deacutefinir
include ltiostreamgtusing namespace std ------------ Deacuteclaration de la classe point ------------- class point deacuteclaration des membres priveacutes private int x int y deacuteclaration des membres publics public void initialise (int int) void deplace (int int) void affiche ()
2 - Notion de classe69
----- Deacutefinition des fonctions membres de la classe point ----
void pointinitialise (int abs int ord)
x = abs y = ord
void pointdeplace (int dx int dy)
x = x + dx y = y + dy
void pointaffiche ()
cout ltlt Je suis en ltlt x ltlt ltlt y ltlt n
-------- Utilisation de la classe point --------
main()
point a b
ainitialise (5 2) aaffiche ()
adeplace (-2 4) aaffiche ()
binitialise (1-1) baffiche ()
Exemple de deacutefinition et dutilisation dune classe (point)
Remarques
1 Dans le jargon de la POO on dit que a et b sont des instances de la classe point ou
encore que ce sont des objets de type point crsquoest geacuteneacuteralement ce dernier terme que
nous utiliserons
2 Dans notre exemple tous les membres donneacutees de point sont priveacutes ce qui correspond
agrave une encapsulation complegravete des donneacutees Ainsi une tentative dutilisation directe (ici
au sein de la fonction main) du membre a
ax = 5
conduirait agrave un diagnostic de compilation (bien entendu cette instruction serait accep-
teacutee si nous avions fait de x un membre public)
En geacuteneacuteral on cherchera agrave respecter le principe dencapsulation des donneacutees quitte agrave
preacutevoir des fonctions membres approprieacutees pour y acceacuteder
3 Dans notre exemple toutes les fonctions membres eacutetaient publiques Il est tout agrave fait
possible den rendre certaines priveacutees Dans ce cas de telles fonctions ne seront plus
accessibles de lrsquoexteacuterieur de la classe Elles ne pourront ecirctre appeleacutees que par dautres
fonctions membres
Classes et objetsCHAPITRE 5
70
4 Les mots cleacutes public et private peuvent apparaicirctre agrave plusieurs reprises dans la deacutefinition
dune classe comme dans cet exemple
class X private public private
Si aucun de ces deux mots napparaicirct au deacutebut de la deacutefinition tout se passe comme si
private y avait eacuteteacute placeacute Cest pourquoi la preacutesence de ce mot neacutetait pas indispensable
dans la deacutefinition de notre classe point
Si aucun de ces deux mots napparaicirct dans la deacutefinition dune classe tous ses membres
seront priveacutes donc inaccessibles Cela sera rarement utile
5 Si lon rend publics tous les membres dune classe on obtient leacutequivalent dune struc-
ture Ainsi ces deux deacuteclarations deacutefinissent le mecircme type point
struct point class point int x public int y int x void initialise () int y void initialise ()
6 Par la suite en lrsquoabsence de preacutecisions suppleacutementaires nous utiliserons le mot classe
pour deacutesigner indiffeacuteremment une vraie classe (class) ou une structure (struct) voire
une union (union) dont nous parlerons un peu plus loin1 De mecircme nous utiliserons le
mot objet pour deacutesigner des instances de ces diffeacuterents types
7 En toute rigueur il existe un troisiegraveme mot protected (proteacutegeacute) qui sutilise de la mecircme
maniegravere que les deux autres il sert agrave deacutefinir un statut intermeacutediaire entre public et
priveacute lequel nintervient que dans le cas de classes deacuteriveacutees Nous en reparlerons au
chapitre 13
8 On peut deacutefinir des classes anonymes comme on pouvait deacutefinir des structures anony-
mes
3 Affectation drsquoobjetsEn C il est possible daffecter agrave une structure la valeur dune autre structure de mecircme type
Ainsi avec les deacuteclarations suivantes
1 La situation de loin la plus reacutepandue restant celle du type class
3 - Affectation drsquoobjets71
struct point int x int y struct point a b
vous pouvez tout agrave fait eacutecrire
a = b
Cette instruction recopie lensemble des valeurs des champs de b dans ceux de a Elle joue le
mecircme rocircle que
ax = bx ay = by
Comme on peut srsquoy attendre cette possibiliteacute srsquoeacutetend aux structures geacuteneacuteraliseacutees (avec fonc-
tions membres) preacutesenteacutees preacuteceacutedemment avec la mecircme signification que pour les structures
usuelles Mais elle srsquoeacutetend aussi aux (vrais) objets de mecircme type Elle correspond tout natu-
rellement agrave une recopie des valeurs des membres donneacutees1 que ceux-ci soient publics ou
non Ainsi avec ces deacuteclarations (notez quici nous avons preacutevu artificiellement x priveacute et y
public)
class point int x public int y point a b
linstruction
b = a
provoquera la recopie des valeurs des membres x et y de a dans les membres correspondants
de b
Contrairement agrave ce qui a eacuteteacute dit pour les structures il nest plus possible ici de remplacer cette
instruction par
bx = ax by = ay
En effet si la deuxiegraveme affectation est leacutegale puisque ici y est public la premiegravere ne lest pas
car x est priveacute2 On notera bien que
1 Les fonctions membres nont aucune raison drsquoecirctre concerneacutees
2 Sauf si laffectation bx = ax eacutetait eacutecrite au sein dune fonction membre de la classe point
Lrsquoaffectation a = b est toujours leacutegale quel que soit le statut (public ou priveacute) des membres donneacutees On peut consideacuterer qursquoelle ne viole pas le principe drsquoencapsu-lation dans la mesure ougrave les donneacutees priveacutees de b (les copies de celles de a apregraves affectation) restent toujours inaccessibles de maniegravere directe
Classes et objetsCHAPITRE 5
72
Remarque
Le rocircle de lopeacuterateur = tel que nous venons de le deacutefinir (recopie des membres donneacutees)
peut paraicirctre naturel ici En fait il ne lest que pour des cas simples Nous verrons des cir-
constances ougrave cette banale recopie saveacuterera insuffisante Ce sera notamment le cas degraves
quun objet comportera des pointeurs sur des emplacements dynamiques la recopie en
question ne concernera pas cette partie dynamique de lobjet elle sera superficielle
Nous reviendrons ulteacuterieurement sur ce point fondamental qui ne trouvera de solution
satisfaisante que dans la surdeacutefinition (pour la classe concerneacutee) de lopeacuterateur = (ou
eacuteventuellement dans lrsquointerdiction de son utilisation)
En Java
En C++ on peut dire que la seacutemantique drsquoaffectation drsquoobjets correspond agrave une recopie
de valeur En Java il srsquoagit simplement drsquoune recopie de reacutefeacuterence apregraves affectation on
se retrouve alors en preacutesence de deux reacutefeacuterences sur un mecircme objet
4 Notions de constructeur et de destructeur
41 IntroductionA priori les objets1 suivent les regravegles habituelles concernant leur initialisation par deacutefaut
seuls les objets statiques voient leurs donneacutees initialiseacutees agrave zeacutero En geacuteneacuteral il est donc
neacutecessaire de faire appel agrave une fonction membre pour attribuer des valeurs aux donneacutees dun
objet Cest ce que nous avons fait pour notre type point avec la fonction initialise
Une telle deacutemarche oblige toutefois agrave compter sur lutilisateur de lobjet pour effectuer lappel
voulu au bon moment En outre si le risque ne porte ici que sur des valeurs non deacutefinies il
nen va plus de mecircme dans le cas ougrave avant mecircme drsquoecirctre utiliseacute un objet doit effectuer un cer-
tain nombre drsquoopeacuterations neacutecessaires agrave son bon fonctionnement par exemple allocation
dynamique de meacutemoire2 veacuterification drsquoexistence de fichier ou ouverture connexion agrave un site
Web Labsence de proceacutedure drsquoinitialisation peut alors devenir catastrophique
C++ offre un meacutecanisme tregraves performant pour traiter ces problegravemes le constructeur Il
sagit dune fonction membre (deacutefinie comme les autres fonctions membres) qui sera appeleacutee
automatiquement agrave chaque creacuteation dun objet Ceci aura lieu quelle que soit la classe dallo-
cation de lobjet statique automatique ou dynamique Notez que les objets automatiques
1 Au sens large du terme
2 Ne confondez pas un objet dynamique avec un objet (par exemple automatique) qui salloue dynamiquement de la
meacutemoire Une situation de ce type sera eacutetudieacutee au prochain chapitre
4 - Notions de constructeur et de destructeur73
auxquels nous nous limitons ici sont creacuteeacutes par une deacuteclaration Ceux de classe dynamique
seront creacuteeacutes par new (nous y reviendrons au chapitre 7)
Un objet pourra aussi posseacuteder un destructeur crsquoest-agrave-dire une fonction membre appeleacutee
automatiquement au moment de la destruction de lobjet Dans le cas des objets automati-
ques la destruction de lobjet a lieu lorsque lon quitte le bloc ou la fonction ougrave il a eacuteteacute
deacuteclareacute
Par convention le constructeur se reconnaicirct agrave ce quil porte le mecircme nom que la classe
Quant au destructeur il porte le mecircme nom que la classe preacuteceacutedeacute drsquoun tilde (~)
42 Exemple de classe comportant un constructeurConsideacuterons la classe point preacuteceacutedente et transformons simplement notre fonction membre
initialise en un constructeur en la renommant point (dans sa deacuteclaration et dans sa deacutefinition)
La deacuteclaration de notre nouvelle classe point se preacutesente alors ainsi
class point deacuteclaration des membres priveacutes int x int y public deacuteclaration des membres publics point (int int) constructeur void deplace (int int) void affiche ()
Deacuteclaration drsquoune classe (point) munie dun constructeur
Comment utiliser cette classe A priori vous pourriez penser que la deacuteclaration suivante
convient toujours
point a
En fait agrave partir du moment ougrave un constructeur est deacutefini il doit pouvoir ecirctre appeleacute (automa-
tiquement) lors de la creacuteation de lobjet a Ici notre constructeur a besoin de deux arguments
Ceux-ci doivent obligatoirement ecirctre fournis dans notre deacuteclaration par exemple
point a(13)
Cette contrainte est en fait un excellent garde-fou
A titre dexemple voici comment pourrait ecirctre adapteacute le programme du paragraphe 2 pour
quil utilise maintenant notre nouvelle classe point
Agrave partir du moment ougrave une classe possegravede un constructeur il nrsquoest plus possible de creacuteer un objet sans fournir les arguments requis par son constructeur (sauf si ce dernier ne possegravede aucun argument )
Classes et objetsCHAPITRE 5
74
include ltiostreamgtusing namespace std ------------ Deacuteclaration de la classe point ------------- class point deacuteclaration des membres priveacutes int x int y deacuteclaration des membres publics public point (int int) constructeur void deplace (int int) void affiche () ----- Deacutefinition des fonctions membre de la classe point ---- pointpoint (int abs int ord) x = abs y = ord void pointdeplace (int dx int dy) x = x + dx y = y + dy void pointaffiche () cout ltlt Je suis en ltlt x ltlt ltlt y ltlt n -------- Utilisation de la classe point -------- main() point a(52) aaffiche () adeplace (-2 4) aaffiche () point b(1-1) baffiche ()
Je suis en 5 2Je suis en 3 6Je suis en 1 -1
Exemple dutilisation dune classe (point) munie dun constructeur
Remarques
1 Supposons que lrsquoon deacutefinisse une classe point disposant drsquoun constructeur sans argument
Dans ce cas la deacuteclaration drsquoobjets de type point continuera de srsquoeacutecrire de la mecircme
maniegravere que si la classe ne disposait pas de constructeur
point a deacuteclaration utilisable avec un constructeur sans argument
4 - Notions de constructeur et de destructeur75
Certes la tentation est grande drsquoeacutecrire par analogie avec lrsquoutilisation drsquoun constructeur
comportant des arguments
point a() incorrect
En fait cela repreacutesenterait la deacuteclaration drsquoune fonction nommeacutee a ne recevant aucun
argument et renvoyant un reacutesultat de type point En soi ce ne serait pas une erreur
mais il est eacutevident que toute tentative drsquoutiliser le symbole a comme un objet conduirait
agrave une erreur
2 Nous verrons dans le prochain chapitre que comme toute fonction (membre ou ordi-
naire) un constructeur peut ecirctre surdeacutefini ou posseacuteder des arguments par deacutefaut
3 Lorsqursquoune classe ne deacutefinit aucun constructeur tout se passe en fait comme si elle dis-
posait drsquoun constructeur par deacutefaut ne faisant rien On peut alors dire que lorsqursquoune
classe nrsquoa pas deacutefini de constructeur la creacuteation des objets correspondants se fait en
utilissant ce constructeur par deacutefaut Nous retrouverons drsquoailleurs le mecircme pheacutenomegravene
dans le cas du constructeur de recopie avec cette diffeacuterence toutefois que le construc-
teur par deacutefaut aura alors une action preacutecise
43 Construction et destruction des objetsNous vous proposons ci-dessous un petit programme mettant en eacutevidence les moments ougrave
sont appeleacutes respectivement le constructeur et le destructeur dune classe Nous y deacutefinissons
une classe nommeacutee test ne comportant que ces deux fonctions membres celles-ci affichent
un message nous fournissant ainsi une trace de leur appel En outre le membre donneacutee num
initialiseacute par le constructeur nous permet drsquoidentifier lobjet concerneacute (dans la mesure ougrave
nous nous sommes arrangeacute pour quaucun des objets creacuteeacutes ne contienne la mecircme valeur)
Nous creacuteons des objets automatiques1 de type test agrave deux endroits diffeacuterents dans la fonc-
tion main dune part dans une fonction fct appeleacutee par main dautre part
include ltiostreamgtusing namespace std class test public int num test (int) deacuteclaration constructeur ~test () deacuteclaration destructeur testtest (int n) deacutefinition constructeur num = n cout ltlt ++ Appel constructeur - num = ltlt num ltlt n
1 Rappelons quici nous nous limitons agrave ce cas
Classes et objetsCHAPITRE 5
76
test~test () deacutefinition destructeur cout ltlt -- Appel destructeur - num = ltlt num ltlt n main() void fct (int) test a(1) for (int i=1 ilt=2 i++) fct(i) void fct (int p) test x(2p) notez lexpression (non constante) 2p
++ Appel constructeur - num = 1++ Appel constructeur - num = 2-- Appel destructeur - num = 2++ Appel constructeur - num = 4-- Appel destructeur - num = 4-- Appel destructeur - num = 1
Construction et destruction des objets
44 Rocircles du constructeur et du destructeurDans les exemples preacuteceacutedents le rocircle du constructeur se limitait agrave une initialisation de lobjet
agrave laide des valeurs quil avait reccedilues en arguments Mais le travail reacutealiseacute par le constructeur
peut ecirctre beaucoup plus eacutelaboreacute Voici un programme exploitant une classe nommeacutee hasard
dans laquelle le constructeur fabrique dix valeurs entiegraveres aleacuteatoires quil range dans le mem-
bre donneacutee val (ces valeurs sont comprises entre zeacutero et la valeur qui lui est fournie en
argument)
include ltiostreamgtinclude ltcstdlibgt pour la fonction rand using namespace std class hasard int val[10] public hasard (int) void affiche () hasardhasard (int max) constructeur il tire 10 valeurs au hasard rappel rand fournit un entier entre 0 et RAND_MAX int i for (i=0 ilt10 i++) val[i] = double (rand()) RAND_MAX max void hasardaffiche () pour afficher les 10 valeurs int i for (i=0 ilt10 i++) cout ltlt val[i] ltlt cout ltlt n
4 - Notions de constructeur et de destructeur77
main() hasard suite1 (5) suite1affiche () hasard suite2 (12) suite2affiche ()
0 2 0 4 2 2 1 4 4 32 10 8 6 3 0 1 4 1 1
Un constructeur de valeurs aleacuteatoires
En pratique on preacutefeacuterera drsquoailleurs disposer dune classe dans laquelle le nombre de valeurs
(ici fixeacute agrave dix) pourra ecirctre fourni en argument du constructeur Dans ce cas il est preacutefeacuterable
que lespace (variable) soit alloueacute dynamiquement au lieu decirctre surdimensionneacute Il est alors
tout naturel de faire effectuer cette allocation dynamique par le constructeur lui-mecircme Les
donneacutees de la classe hasard se limiteront ainsi agrave
class hasard int nbval nombre de valeurs int val pointeur sur un tableau de valeurs
Bien sucircr il faudra preacutevoir que le constructeur reccediloive en argument outre la valeur maximale
le nombre de valeurs souhaiteacutees
Par ailleurs agrave partir du moment ougrave un emplacement a eacuteteacute alloueacute dynamiquement il faut se
soucier de sa libeacuteration lorsquil sera devenu inutile Lagrave encore il paraicirct tout naturel de con-
fier ce travail au destructeur de la classe
Voici comment nous pourrions adapter en ce sens lrsquoexemple preacuteceacutedent
include ltiostreamgtinclude ltcstdlibgt pour la fonction rand using namespace std class hasard int nbval nombre de valeurs int val pointeur sur les valeurs public hasard (int int) constructeur ~hasard () destructeur void affiche () hasardhasard (int nb int max) int i val = new int [nbval = nb] for (i=0 iltnb i++) val[i] = double (rand()) RAND_MAX max
Classes et objetsCHAPITRE 5
78
hasard~hasard ()
delete val
void hasardaffiche () pour afficher les nbavl valeurs
int i
for (i=0 iltnbval i++) cout ltlt val[i] ltlt
cout ltlt n
main()
hasard suite1 (10 5) 10 valeurs entre 0 et 5
suite1affiche ()
hasard suite2 (6 12) 6 valeurs entre 0 et 12
suite2affiche ()
0 2 0 4 2 2 1 4 4 3
2 10 8 6 3 0
Exemple de classe dont le constructeur effectue une allocation dynamique de meacutemoire
Dans le constructeur linstruction
val = new [nbval = nb]
joue le mecircme rocircle que
nbval = nb
val = new [nbval]
Remarques
1 Ne confondez pas une allocation dynamique effectueacutee au sein dune fonction membre
dun objet (souvent le constructeur) avec une allocation dynamique dun objet dont nous
parlerons plus tard
2 Lorsquun constructeur se contente dattribuer des valeurs initiales aux donneacutees dun
objet le destructeur est rarement indispensable En revanche il le devient degraves que
comme dans notre exemple lobjet est ameneacute (par le biais de son constructeur ou
dautres fonctions membres) agrave allouer dynamiquement de la meacutemoire
3 Comme nous lavons deacutejagrave mentionneacute degraves quune classe contient comme dans notre
dernier exemple des pointeurs sur des emplacements alloueacutes dynamiquement laffecta-
tion entre objets de mecircme type ne concerne pas ces parties dynamiques geacuteneacuteralement
cela pose problegraveme et la solution passe par la surdeacutefinition de lopeacuterateur = Autrement
dit la classe hasard deacutefinie dans le dernier exemple ne permettrait pas de traiter correc-
tement laffectation dobjets de ce type
4 - Notions de constructeur et de destructeur79
45 Quelques regraveglesUn constructeur peut comporter un nombre quelconque drsquoarguments eacuteventuellement aucun
Par deacutefinition un constructeur ne renvoie pas de valeur aucun type ne peut figurer devant
son nom (dans ce cas preacutecis la preacutesence de void est une erreur)
Par deacutefinition un destructeur ne peut pas disposer drsquoarguments et ne renvoie pas de valeur
Lagrave encore aucun type ne peut figurer devant son nom (et la preacutesence de void est une erreur)
En theacuteorie constructeurs et destructeurs peuvent ecirctre publics ou priveacutes En pratique agrave moins
drsquoavoir de bonnes raisons de faire le contraire il vaut mieux les rendre publics
On notera que si un destructeur est priveacute il ne pourra plus ecirctre appeleacute directement ce qui
nrsquoest geacuteneacuteralement pas grave dans la mesure ougrave cela est rarement utile
En revanche la privatisation drsquoun constructeur a de lourdes conseacutequences puisqursquoil ne sera
plus utilisable sauf par des fonctions membres de la classe elle-mecircme
Informations compleacutementaires
Voici quelques circonstances ougrave un constructeur priveacute peut se justifier
ndash la classe concerneacutee ne sera pas utiliseacutee telle quelle car elle est destineacutee agrave donner nais-
sance par heacuteritage agrave des classes deacuteriveacutees qui quant agrave elles pourront disposer drsquoun
constructeur public (nous reviendrons plus tard sur cette situation dite de classe abs-
traite)
ndash la classe dispose drsquoautres constructeurs (nous verrons bientocirct qursquoun constructeur peut
ecirctre surdeacutefini) dont au moins un est public
ndash on cherche agrave mettre en œuvre un motif de conception1 particulier le singleton il
srsquoagit de faire en sorte qursquoune mecircme classe ne puisse donner naissance qursquoagrave un seul ob-
jet et que toute tentative de creacuteation drsquoun nouvel objet se contente de renvoyer la reacutefeacute-
rence de cet unique objet Dans ce cas on peut preacutevoir un constructeur priveacute (de corps
vide) dont la preacutesence fait qursquoil est impossible de creacuteer explicitement des objets du type
(du moins si ce constructeur nrsquoest pas surdeacutefini) La creacuteation drsquoobjets se fait alors par
appel drsquoune fonction membre qui reacutealise elle-mecircme les allocations neacutecessaires crsquoest-
agrave-dire le travail drsquoun constructeur habituel et qui en outre srsquoassure de lrsquouniciteacute de
lrsquoobjet
En Java
Le constructeur possegravede les mecircmes proprieacuteteacutes qursquoen C++ et une classe peut ne pas com-
porter de constructeur Mais en Java les membres donneacutees sont toujours initialiseacutes par
deacutefaut (valeur nulle) et ils peuvent eacutegalement ecirctre initialiseacutes lors de leur deacuteclaration (la
1 Pattern en anglais
Classes et objetsCHAPITRE 5
80
mecircme valeur eacutetant alors attribueacutee agrave tous les objets du type) Ces deux possiliteacutes (initiali-
sation par deacutefaut et initialisation explicite) nrsquoexistent pas en C++ comme nous le verrons
plus tard de sorte qursquoil est pratiquement toujours neacutecessaire de preacutevoir un constructeur
mecircme dans des situations drsquoinitialisation simple
5 Les membres donneacutees statiques
51 Le qualificatif static pour un membre donneacuteeA priori lorsque dans un mecircme programme on creacutee diffeacuterents objets dune mecircme classe cha-
que objet possegravede ses propres membres donneacutees Par exemple si nous avons deacutefini une
classe exple1 par
class exple1
int n
float x
une deacuteclaration telle que
exple1 a b
conduit agrave une situation que lon peut scheacutematiser ainsi
Une faccedilon (parmi dautres) de permettre agrave plusieurs objets de partager des donneacutees consiste agrave
deacuteclarer avec le qualificatif static les membres donneacutees quon souhaite voir exister en un seul
exemplaire pour tous les objets de la classe Par exemple si nous deacutefinissons une classe
exple2 par
class exple2
static int n
float x
la deacuteclaration
exple2 a b
an
ax
bn
bx
Objet a Objet b
5 - Les membres donneacutees statiques81
conduit agrave une situation que lon peut scheacutematiser ainsi
On peut dire que les membres donneacutees statiques sont des sortes de variables globales dont la
porteacutee est limiteacutee agrave la classe
52 Initialisation des membres donneacutees statiquesPar leur nature mecircme les membres donneacutees statiques nexistent quen un seul exemplaire
indeacutependamment des objets de la classe (mecircme si aucun objet de la classe na encore eacuteteacute
creacuteeacute) Dans ces conditions leur initialisation ne peut plus ecirctre faite par le constructeur de la
classe
On pourrait penser quil est possible dinitialiser un membre statique lors de sa deacuteclaration
comme dans
class exple2 static int n = 2 erreur
En fait cela nest pas permis car compte tenu des possibiliteacutes de compilation seacutepareacutee le
membre statique risquerait de se voir reacuteserver diffeacuterents emplacements1 dans diffeacuterents
modules objet
Un membre statique doit donc ecirctre initialiseacute explicitement (agrave lexteacuterieur de la deacuteclaration de
la classe) par une instruction telle que
int exple2n = 5
Cette deacutemarche est utilisable aussi bien pour les membres statiques priveacutes que publics
Par ailleurs contrairement agrave ce qui se produit pour une variable ordinaire un membre stati-
que nest pas initialiseacute par deacutefaut agrave zeacutero
Remarque
Depuis la norme les membres statiques constants peuvent eacutegalement ecirctre initialiseacutes au
moment de leur deacuteclaration Mais il reste quand mecircme neacutecessaire de les deacuteclarer agrave lrsquoexteacute-
an
ax
Objet a Objet b
bx
bn
1 On retrouve le mecircme pheacutenomegravene pour les variables globales en langage C elles peuvent ecirctre deacuteclareacutees plusieurs
fois mais elles ne doivent ecirctre deacutefinies quune seule fois
Classes et objetsCHAPITRE 5
82
rieur de la classe (sans valeur cette fois) pour provoquer la reacuteservation de lrsquoemplacement
meacutemoire correspondant Par exemple
class exple3 static const int n=5 initialisation OK dpeuis la norme ANSI const int exple3n deacuteclaration indispensable (sans valeur)
53 ExempleVoici un exemple de programme exploitant cette possibiliteacute dans une classe nommeacutee
cpte_obj afin de connaicirctre agrave tout moment le nombre dobjets existants Pour ce faire nous
avons deacuteclareacute avec lattribut statique le membre ctr Sa valeur est increacutementeacutee de 1 agrave chaque
appel du constructeur et deacutecreacutementeacutee de 1 agrave chaque appel du destructeur
include ltiostreamgtusing namespace std
class cpte_obj static int ctr compteur du nombre dobjets creacuteeacutes
public cpte_obj () ~cpte_obj () int cpte_objctr = 0 initialisation du membre statique ctrcpte_objcpte_obj () constructeur cout ltlt ++ construction il y a maintenant ltlt ++ctr ltlt objetsn cpte_obj~cpte_obj () destructeur cout ltlt -- destruction il reste maintenant ltlt --ctr ltlt objetsn main() void fct () cpte_obj a fct () cpte_obj b void fct () cpte_obj u v
++ construction il y a maintenant 1 objets++ construction il y a maintenant 2 objets++ construction il y a maintenant 3 objets-- destruction il reste maintenant 2 objets-- destruction il reste maintenant 1 objets
6 - Exploitation drsquoune classe83
++ construction il y a maintenant 2 objets-- destruction il reste maintenant 1 objets-- destruction il reste maintenant 0 objets
Exemple dutilisation de membre statique
Remarque
En C le terme statique avait deacutejagrave deux significations de classe statique ou de porteacutee
limiteacutee au fichier source1 En C++ lorsquil sapplique aux membres dune classe il en
possegravede donc une troisiegraveme indeacutependant dune quelconque instance de la classe Nous
verrons au prochain chapitre quil pourra sappliquer aux fonctions membres avec la
mecircme signification
En Java
Les membres donneacutees statiques existent eacutegalement en Java et on utilise le mot cleacute static
pour leur deacuteclaration (crsquoest drsquoailleurs la seule signification de ce mot cleacute) Comme en
C++ ils peuvent ecirctre initialiseacutes lors de leur deacuteclaration mais ils peuvent aussi lrsquoecirctre par
le biais drsquoun bloc drsquoinitialisation qui contient alors des instructions exeacutecutables ce que ne
permet pas C++
6 Exploitation drsquoune classe
61 La classe comme composant logicielJusqursquoici nous avions regroupeacute au sein drsquoun mecircme programme trois sortes drsquoinstructions
destineacutees agrave
bull la deacuteclaration de la classe
bull la deacutefinition de la classe
bull lrsquoutilisation de la classe
En pratique on aura souvent inteacuterecirct agrave deacutecoupler la classe de son utilisation Crsquoest tout natu-
rellement ce qui se produira avec une classe drsquointeacuterecirct geacuteneacuteral utiliseacutee comme un composant
seacutepareacute des diffeacuterentes applications
1 Du moins quand on lemployait pour deacutesigner ce qui eacutetait qualifieacute par le mot cleacute static
Classes et objetsCHAPITRE 5
84
On sera alors geacuteneacuteralement ameneacute agrave isoler les seules instructions de deacuteclaration de la classe
dans un fichier en-tecircte (extension h) qursquoil suffira drsquoinclure (par include) pour compiler
lrsquoapplication
Par exemple le concepteur de la classe point du paragraphe 42 pourra creacuteer le fichier en-tecircte
suivant
class point deacuteclaration des membres priveacutes
int x int y
public deacuteclaration des membres publics point (int int) constructeur
void deplace (int int) void affiche ()
Fichier en-tecircte pour la classe point
Si ce fichier se nomme pointh le concepteur fabriquera alors un module objet en compilant
la deacutefinition de la classe point
include ltiostreamgt
include pointh pour introduire les deacuteclarations de la classe pointusing namespace std
----- Deacutefinition des fonctions membre de la classe point ----
pointpoint (int abs int ord) x = abs y = ord
void pointdeplace (int dx int dy) x = x + dx y = y + dy
void pointaffiche ()
cout ltlt Je suis en ltlt x ltlt ltlt y ltlt n
Fichier agrave compiler pour obtenir le module objet de la classe point
Pour faire appel agrave la classe point au sein drsquoun programme lrsquoutilisateur proceacutedera alors ainsi
bull il inclura la deacuteclaration de la classe point dans le fichier source contenant son programme
par une directive telle que
include pointh
6 - Exploitation drsquoune classe85
bull il incorporera le module objet correspondant au moment de lrsquoeacutedition de liens de son propre
programme En principe agrave ce niveau la plupart des eacutediteurs de liens nrsquointroduisent que les
fonctions reacuteellement utiliseacutees de sorte qursquoil ne faut pas craindre de preacutevoir trop de meacutethodes
pour une classe
Parfois on trouvera plusieurs classes diffeacuterentes au sein drsquoun mecircme module objet et drsquoun
mecircme fichier en-tecircte de faccedilon comparable agrave ce qui se produit avec les fonctions de la biblio-
thegraveque standard1 Lagrave encore en geacuteneacuteral seules les fonctions reacuteellement utiliseacutees seront incor-
poreacutees agrave lrsquoeacutedition de liens de sorte qursquoil est toujours possible drsquoeffectuer des regroupements
de classes posseacutedant quelques affiniteacutes
Signalons que bon nombre drsquoenvironnements disposent drsquooutils2 permettant de prendre auto-
matiquement en compte les deacutependances existant entre les diffeacuterents fichiers sources et les
diffeacuterents fichiers objets concerneacutes dans ce cas lors drsquoune modification quelle qursquoelle soit
seules les compilations neacutecessaires sont effectueacutees
Remarque
Comme une fonction ordinaire une fonction membre peut ecirctre deacuteclareacutee sans qursquoon nrsquoen
fournisse de deacutefinition Si le programme fait appel agrave cette fonction membre ce nrsquoest qursquoagrave
lrsquoeacutedition de liens qursquoon srsquoapercevra de son absence En revanche si le programme nrsquouti-
lise pas cette fonction membre lrsquoeacutedition de liens se deacuteroulera normalement car il nrsquointro-
duit que les fonctions effectivement appeleacutees
62 Protection contre les inclusions multiplesPlus tard nous verrons quil existe diffeacuterentes circonstances pouvant amener lutilisateur
dune classe agrave inclure plusieurs fois un mecircme fichier en-tecircte lors de la compilation dun mecircme
fichier source (sans mecircme quil nen ait conscience ) Ce sera notamment le cas dans les
situations dobjets membres et de classes deacuteriveacutees
Dans ces conditions on risque daboutir agrave des erreurs de compilation lieacutees tout simplement agrave
la redeacutefinition de la classe concerneacutee
En geacuteneacuteral on reacuteglera ce problegraveme en proteacutegeant systeacutematiquement tout fichier en-tecircte des
inclusions multiples par une technique de compilation conditionnelle comme dans
ifndef POINT_H
define POINT_H
deacuteclaration de la classe point
endif
1 Avec cette diffeacuterence que dans le cas des fonctions standard on nrsquoa pas agrave speacutecifier les modules objets concerneacutes
au moment de lrsquoeacutedition de liens
2 On parle souvent de projet de fichier projet de fichier make
Classes et objetsCHAPITRE 5
86
Le symbole deacutefini pour chaque fichier en-tecircte sera choisi de faccedilon agrave eacuteviter tout risque de dou-
blons Ici nous avons choisi le nom de la classe (en majuscules) suffixeacute par _H
63 Cas des membres donneacutees statiquesNous avons vu (paragraphe 52) quun membre donneacutee statique doit toujours ecirctre initialiseacute
explicitement Degraves quon est ameneacute agrave consideacuterer une classe comme un composant seacutepareacute le
problegraveme se pose alors de savoir dans quel fichier source placer une telle initialisation
fichier en-tecircte fichier deacutefinition de la classe fichier utilisateur (dans notre exemple du para-
graphe 53 ce problegraveme ne se posait pas car nous navions quun seul fichier source)
On pourrait penser que le fichier en-tecircte est un excellent candidat pour cette initialisation degraves
lors quil est proteacutegeacute contre les inclusions multiples En fait il nen est rien en effet si lutili-
sateur compile seacutepareacutement plusieurs fichiers source utilisant la mecircme classe plusieurs
emplacements seront geacuteneacutereacutes pour le mecircme membre statique et en principe leacutedition de liens
deacutetectera cette erreur
Comme par ailleurs il nest guegravere raisonnable de laisser lutilisateur initialiser lui-mecircme un
membre statique on voit qursquoen deacutefinitive
64 En cas de modification drsquoune classeA priori lorsqursquoune classe est consideacutereacutee comme un composant logiciel crsquoest qursquoelle est au
point et ne devrait plus ecirctre modifieacutee Si une modification srsquoavegravere neacutecessaire malgreacute tout il
faut envisager deux situations assez diffeacuterentes
641 La deacuteclaration des membres publics nrsquoa pas changeacuteCrsquoest ce qui se produit lorsqursquoon se limite agrave des modifications internes nrsquoayant acune reacuteper-
cussion sur la maniegravere drsquoutiliser la classe (son interface avec lrsquoexteacuterieur reste la mecircme) Il
peut srsquoagir de transformation de structures de donneacutees encapsuleacutees (priveacutees) de modification
drsquoalgorithmes de traitement
Dans ce cas les programmes utilisant la classe nrsquoont pas agrave ecirctre modifieacutes Neacuteanmoins il
doivent ecirctre recompileacutes avec le nouveau fichier en-tecircte correspondant1 On proceacutedera
ensuite agrave une eacutedition de liens en incorporant le nouveau module objet
On voit donc que C++ permet une maintenance facile drsquoune classe agrave laquelle on souhaite
apporter des modifications internes (corrections drsquoerreurs ameacutelioration des performances)
nrsquoatteignant pas la speacutecification de son interface
Il est conseilleacute de preacutevoir lrsquoinitialisation des membres donneacutees statiques dans le fichier contenant la deacutefinition de la classe
1 Une telle limitation nrsquoexiste pas dans tous les langages de POO En C++ elle se justifie par le besoin qursquoa le
compilateur de connaicirctre la taille des objets (statiques ou automatiques) pour leur allouer un emplacement
7 - Les classes en geacuteneacuteral87
642 La deacuteclaration des membres publics a changeacute
Ici il est clair que les programmes utilisant la classe risquent de neacutecessiter des modifications
Cette situation devra bien sucircr ecirctre eacuteviteacutee dans la mesure du possible Elle doit ecirctre consideacutereacutee
comme une faute de conception de la classe Nous verrons drsquoailleurs que ces problegravemes pour-
ront souvent ecirctre reacutesolus par lrsquoutilisation du meacutecanisme drsquoheacuteritage qui permet drsquoadapter une
classe (censeacutee ecirctre au point) sans la remettre en cause
7 Les classes en geacuteneacuteralNous apportons ici quelques compleacutements drsquoinformation sur des situations peu usuelles
71 Les autres sortes de classes en C++Nous avons deacutejagrave eu loccasion de dire que C++ qualifiait de classe les types deacutefinis par
struct et class La caracteacuteristique dune classe au sens large que lui donne C++1 est dasso-
cier au sein dun mecircme type des membres donneacutees et des fonctions membres
Pour C++ les unions sont aussi des classes Ce type peut donc disposer de fonctions mem-
bres Notez bien que comme pour le type struct les donneacutees correspondantes ne peuvent pas
se voir attribuer un statut particulier elles sont de fait publiques
Remarque
C++ emploie souvent le mot classe pour deacutesigner indiffeacuteremment un type class struct ou
union De mecircme on parle souvent drsquoobjet pour deacutesigner des variables de lrsquoun de ces trois
types Cet abus de langage semble assez licite dans la mesure ougrave ces trois types jouis-
sent pratiquement des mecircmes proprieacuteteacutes notamment au niveau de lrsquoheacuteritage toutefois
seul le type class permet lrsquoencapsulation des donneacutees Lorsqursquoil sera neacutecessaire drsquoecirctre
plus preacutecis nous parlerons de vraie classe pour deacutesigner le type class
72 Ce quon peut trouver dans la deacuteclaration dune classeEn dehors des deacuteclarations de fonctions membres la plupart des instructions figurant dans
une deacuteclaration de classe seront des deacuteclarations de membres donneacutees dun type quelconque
Neacuteanmoins on peut eacutegalement y rencontrer des deacuteclarations de type y compris dautres types
classes dans ce cas leur porteacutee est limiteacutee agrave la classe (mais on peut recourir agrave lrsquoopeacuterateur de
reacutesolution de porteacutee ) comme dans cet exemple
1 Et non la POO dune maniegravere geacuteneacuterale qui associe lencapsulation des donneacutees agrave la notion de classe
Classes et objetsCHAPITRE 5
88
class A
public
class B classe B deacuteclareacutee dans la classe A
main()
A a
AB b deacuteclaration drsquoun objet b du type de la classe B de A
En pratique cette situation se rencontre peu souvent
Par ailleurs il nest pas possible dinitialiser un membre donneacutee dune classe lors de sa
deacuteclaration Cette interdiction est justifieacutee pour au moins deux raisons
bull une telle initialisation risquerait de faire double emploi avec le constructeur
bull une telle initialisation constituerait une deacutefinition du membre correspondant (et non plus une
simple deacuteclaration) or cette deacutefinition risquerait dapparaicirctre plusieurs fois en cas de com-
pilation seacutepareacutee ce qui est illeacutegal1
En revanche la deacuteclaration de membres donneacutees constants2 est autoriseacutee comme dans
class exple
int n membre donneacutee usuel
const int p membre donneacutee constant - initialisation impossible
agrave ce niveau
Dans ce cas on notera bien que chaque objet du type exple posseacutedera un membre p Crsquoest ce
qui explique qursquoil ne soit pas possible dinitialiser le membre constant au moment de sa
deacuteclaration3 Pour y parvenir la seule solution consistera agrave utiliser une syntaxe particuliegravere
du constructeur (qui devient donc obligatire) telle quelle sera preacutesenteacutee au paragraphe 5 du
chapitre 7 (relatif aux objets membres)
73 Deacuteclaration dune classeLa plupart du temps les classes seront deacuteclareacutees agrave un niveau global Neacuteanmoins il est permis
de deacuteclarer des classes locales agrave une fonction Dans ce cas leur porteacutee est naturellement limi-
teacutee agrave cette fonction (cest bien ce qui en limite linteacuterecirct)
1 On retrouve le mecircme pheacutenomegravene pour les membres donneacutees statiques et pour les variables globales en langage C
ils peuvent ecirctre deacuteclareacutes plusieurs fois mais ils ne doivent ecirctre deacutefinis quune seule fois
2 Ne confondez pas la notion de membre donneacutee constant (chaque objet en possegravede un sa valeur ne peut pas ecirctre
modifieacutee) et la notion de membre donneacutee statique (tous les objets dune mecircme classe partagent le mecircme sa valeur
peut changer)
3 Sauf comme on lrsquoa vu au paragraphe 52 srsquoil srsquoagit drsquoun membre statique constant dans ce cas ce membre est
unique pour tous les objets de la classe
7 - Les classes en geacuteneacuteral89
ExercicesNB les exercices marqueacutes (C) sont corrigeacutes en fin de volume
1 Expeacuterimentez (eacuteventuellement sur un exemple de ce chapitre) la compilation seacutepareacutee
dune classe (creacuteation dun module objet et dun fichier en-tecircte) et son utilisation au
sein dun programme
2 (C) Ecrivez une classe vecteur (de type class et non struct) comportant
bull comme membres donneacutees priveacutes trois composantes de type double
bull comme fonctions membres publiques
ndash initialise pour attribuer des valeurs aux composantes
ndash homothetie pour multiplier les composantes par une valeur fournie en argu-
ment
ndash affiche pour afficher les composantes du vecteur
3 (C) Ecrivez une classe vecteur analogue agrave la preacuteceacutedente dans laquelle la fonction initia-
lise est remplaceacutee par un constructeur
4 Expeacuterimentez la creacuteation drsquoun fichier en-tecircte et drsquoun module objet rassemblant deux
classes diffeacuterentes
5 Veacuterifiez que lorsqursquoune classe comporte un membre donneacutee statique ce dernier peut
ecirctre utiliseacute mecircme lorsquaucun objet de ce type na eacuteteacute deacuteclareacute
6 Mettez en eacutevidence les problegravemes poseacutes par laffectation entre objets comportant une
partie dynamique Pour ce faire utilisez la classe hasard du second exemple du para-
graphe 44 en ajoutant simplement des instructions affichant ladresse contenue dans
val dans le constructeur dune part dans le destructeur dautre part Vous constaterez
quavec ces deacuteclarations
hasard h1(10 3)
hasard h2(20 5)
une instruction telle que
h2 = h1
nentraicircne pas toutes les recopies escompteacutees et que de surcroicirct elle conduit agrave libeacuterer
deux fois le mecircme emplacement (en fin de fonction)
6
Les proprieacuteteacutes desfonctions membres
Le chapitre preacuteceacutedent vous a preacutesenteacute les concepts fondamentaux de classe dobjet de cons-
tructeur et de destructeur Ici nous allons eacutetudier un peu plus en deacutetail lapplication aux fonc-
tions membres des possibiliteacutes offertes par C++ pour les fonctions ordinaires surdeacutefinition
arguments par deacutefaut fonction en ligne transmission par reacutefeacuterence
Nous verrons eacutegalement comment une fonction membre peut recevoir en argument outre
lobjet layant appeleacute (transmis implicitement) un ou plusieurs objets de type classe Ici nous
nous limiterons au cas dobjets de mecircme type que la classe dont la fonction est membre les
autres situations correspondant agrave une violation du principe dencapsulation ne seront exami-
neacutees que plus tard dans le cadre des fonctions amies
Nous verrons ensuite comment acceacuteder au sein dune fonction membre agrave ladresse de lobjet
layant appeleacute en utilisant le mot cleacute this
Enfin nous examinerons les cas particuliers des fonctions membres statiques et des fonctions
membres constantes ainsi que lemploi de pointeurs sur des fonctions membres
1 Surdeacutefinition des fonctions membresNous avons deacutejagrave vu comment C++ nous autorise agrave surdeacutefinir les fonctions ordinaires Cette
possibiliteacute sapplique eacutegalement aux fonctions membres dune classe y compris au construc-
teur (mais pas au destructeur puisquil ne possegravede pas drsquoarguments)
Les proprieacuteteacutes des fonctions membresCHAPITRE 6
92
11 ExempleVoyez cet exemple dans lequel nous surdeacutefinissons
bull le constructeur point le choix du bon constructeur se faisant ici suivant le nombre
drsquoarguments
ndash 0 argument les deux coordonneacutees attribueacutees au point construit sont toutes deux nulles
ndash 1 argument il sert de valeur commune aux deux coordonneacutees
ndash 2 arguments cest le cas usuel que nous avions deacutejagrave rencontreacute
bull la fonction affiche de maniegravere quon puisse lappeler
ndash sans argument comme auparavant
ndash avec un argument de type chaicircne dans ce cas elle affiche le texte correspondant avant
les coordonneacutees du point
include ltiostreamgtusing namespace std class point int x y public point () constructeur 1 (sans arguments) point (int) constructeur 2 (un argument) point (int int) constructeur 3 (deux arguments) void affiche () fonction affiche 1 (sans arguments) void affiche (char ) fonction affiche 2 (un argument chaicircne) pointpoint () constructeur 1 x = 0 y = 0 pointpoint (int abs) constructeur 2 x = y = abs pointpoint (int abs int ord) constructeur 3 x = abs y = ord void pointaffiche () fonction affiche 1 cout ltlt Je suis en ltlt x ltlt ltlt y ltlt n void pointaffiche (char message) fonction affiche 2 cout ltlt message affiche () main() point a appel constructeur 1 aaffiche () appel fonction affiche 1 point b (5) appel constructeur 2 baffiche (Point b - ) appel fonction affiche 2 point c (3 12) appel constructeur 3 caffiche (Hello ---- ) appel fonction affiche 2
1 - Surdeacutefinition des fonctions membres93
Je suis en 0 0Point b - Je suis en 5 5Hello ---- Je suis en 3 12
Exemple de surdeacutefinition de fonctions membres (point et affiche)
Remarques
1 En utilisant les possibiliteacutes drsquoarguments par deacutefaut il est souvent possible de diminuer le
nombre de fonctions surdeacutefinies Cest le cas ici pour la fonction affiche comme nous le
verrons dailleurs dans le paragraphe suivant
2 Ici dans la fonction affiche(char ) nous faisons appel agrave lautre fonction membre affi-
che() En effet une fonction membre peut toujours en appeler une autre (quelle soit
publique ou non) Une fonction membre peut mecircme sappeler elle-mecircme dans la
mesure ougrave lon a preacutevu le moyen de rendre fini le processus de reacutecursiviteacute qui en
deacutecoule
12 Incidence du statut public ou priveacute drsquoune fonction membreMais par rapport agrave la surdeacutefinition des fonctions indeacutependantes il faut maintenant tenir
compte de ce qursquoune fonction membre peut ecirctre priveacutee ou publique Or en C++
Condiseacuterez cet exemple
class A public void f(int n)
private void f(char c)
main()
int n char c A a
af(c)
Lrsquoappel af(c) amegravene le compilateur agrave consideacuterer les deux fonctions f(int) et f(char) et ceci
indeacutependamment de leur statut (public pour la premiegravere priveacute pour la seconde) Lrsquoalgorithme
de recherche de la meilleure fonction conclut alors que f(char) est la meilleure fonction et
qursquoelle est unique Mais comme celle-ci est priveacutee elle ne peut pas ecirctre appeleacutee depuis une
fonction exteacuterieure agrave la classe et lrsquoappel est rejeteacute (et ceci malgreacute lrsquoexistence de f(int) qui
aurait pu convenir) Rappelons que
bull si f(char) est deacutefinie publique elle serait bien appeleacutee par af(c)
bull si f(char) nrsquoest pas deacutefinie du tout af(c) appellerait f(int)
Le statut priveacute ou public drsquoune fonction nrsquointervient pas dans les fonctions consideacute-reacutees En revanche si la meilleure fonction trouveacutee est priveacutee elle ne pourra pas ecirctre appeleacutee (sauf si lrsquoappel figure dans une autre fonction membre de la classe)
Les proprieacuteteacutes des fonctions membresCHAPITRE 6
94
En Java
Contrairement agrave ce qui se passe en C++ le statut priveacute ou public drsquoune fonction membre
est bien pris en compte dans le choix des fonctions acceptables Dans ce dernier exem-
ple af(c) appellerait bien f(int) apregraves conversion de c en int comme si la fonction priveacutee
f(int) nrsquoeacutexistait pas
2 Arguments par deacutefautComme les fonctions ordinaires les fonctions membres peuvent disposer drsquoarguments par
deacutefaut Voici comment nous pourrions modifier lexemple preacuteceacutedent pour que notre classe
point ne possegravede plus quune seule fonction affiche disposant drsquoun seul argument de type
chaicircne Celui-ci indique le message agrave afficher avant les valeurs des coordonneacutees et sa valeur
par deacutefaut est la chaicircne vide
include ltiostreamgtusing namespace std class point int x y public point () constructeur 1 (sans argument)
point (int) constructeur 2 (un argument) point (int int) constructeur 3 (deux arguments) void affiche (char = ) fonction affiche (un argument par deacutefaut) pointpoint () constructeur 1 x = 0 y = 0 pointpoint (int abs) constructeur 2
x = y = abs pointpoint (int abs int ord) constructeur 3 x = abs y = ord void pointaffiche (char message) fonction affiche cout ltlt message ltlt Je suis en ltlt x ltlt ltlt y ltlt n
main() point a appel constructeur 1 aaffiche () point b (5) appel constructeur 2 baffiche (Point b - ) point c (3 12) appel constructeur 3 caffiche (Hello ---- )
3 - Les fonctions membres en ligne95
Je suis en 0 0Point b - Je suis en 5 5Hello ---- Je suis en 3 12
Exemple dutilisation drsquoarguments par deacutefaut dans une fonction membre
Remarque
Ici nous avons remplaceacute deux fonctions surdeacutefinies par une seule fonction ayant un argu-
ment par deacutefaut Bien entendu cette simplification nest pas toujours possible Par exem-
ple ici nous ne pouvons pas lappliquer agrave notre constructeur point En revanche si nous
avions preacutevu que dans le constructeur point agrave un seul argument ce dernier repreacutesente
simplement labscisse du point auquel on aurait alors attribueacute une ordonneacutee nulle nous
aurions pu deacutefinir un seul constructeur
pointpoint (int abs = 0 int ord = 0) x = abs y = ord
3 Les fonctions membres en ligneNous avons vu que C++ permet de deacutefinir des fonctions en ligne Ceci accroicirct lefficience
dun programme dans le cas de fonctions courtes Lagrave encore cette possibiliteacute sapplique aux
fonctions membres moyennant cependant une petite nuance concernant sa mise en œuvre
En effet pour rendre en ligne une fonction membre on peut
bull soit fournir directement la deacutefinition de la fonction dans la deacuteclaration mecircme de la classe
dans ce cas le qualificatif inline na pas agrave ecirctre utiliseacute
bull soit proceacuteder comme pour une fonction ordinaire en fournissant une deacutefinition en dehors de
la deacuteclaration de la classe dans ce cas le qualificatif inline doit apparaicirctre agrave la fois devant
la deacuteclaration et devant len-tecircte
Voici comment nous pourrions rendre en ligne les trois constructeurs de notre preacuteceacutedent
exemple en adoptant la premiegravere maniegravere
include ltiostreamgtusing namespace std class point int x y public point () x = 0 y = 0 constructeur 1 en ligne point (int abs) x = y = abs constructeur 2 en ligne point (int abs int ord) x = abs y = ord constructeur 3 en ligne void affiche (char = )
Les proprieacuteteacutes des fonctions membresCHAPITRE 6
96
void pointaffiche (char message) fonction affiche cout ltlt message ltlt Je suis en ltlt x ltlt ltlt y ltlt n
main() point a appel constructeur 1 aaffiche () point b (5) appel constructeur 2 baffiche (Point b - ) point c (3 12) appel constructeur 3 caffiche (Hello ---- )
Je suis en 0 0Point b - Je suis en 5 5Hello ---- Je suis en 3 12
Exemple de fonctions membres en ligne
Remarques
1 Voici comment se serait preacutesenteacutee la deacuteclaration de notre classe si nous avions deacuteclareacute
nos fonctions membres en ligne agrave la maniegravere des fonctions ordinaires (ici nous navons
mentionneacute quun constructeur)
class point public inline point ()
inline pointpoint() x = 0 y = 0
2 Si nous navions eu besoin que dun seul constructeur avec arguments par deacutefaut
(comme dans la remarque du preacuteceacutedent paragraphe) nous aurions pu tout aussi bien le
rendre en ligne avec la premiegravere deacutemarche (deacutefinition de fonction inteacutegreacutee dans la
deacuteclaration de la classe) nous aurions alors speacutecifieacute les valeurs par deacutefaut directement
dans len-tecircte
class point point (int abs = 0 int ord = 0) x = abs y = ord
Nous utiliserons dailleurs un tel constructeur dans lexemple du paragraphe suivant
4 - Cas des objets transmis en argument drsquoune fonction membre97
3 Par sa nature mecircme la deacutefinition dune fonction en ligne doit obligatoirement ecirctre con-
nue du compilateur lorsquil traduit le programme qui lutilise Cette condition est obli-
gatoirement reacutealiseacutee lorsque lon utilise la premiegravere deacutemarche En revanche ce nest
plus vrai avec la seconde en geacuteneacuteral dans ce cas on placera les deacutefinitions des fonc-
tions en ligne agrave la suite de la deacuteclaration de la classe dans le mecircme fichier en-tecircte
Dans tous les cas on voit toutefois que lutilisateur dune classe (qui disposera obliga-
toirement du fichier en-tecircte relatif agrave une classe) pourra toujours connaicirctre la deacutefinition
des fonctions en ligne le fournisseur dune classe ne pourra jamais avoir la certitude
quun utilisateur de cette classe ne tentera pas de les modifier Ce risque nexiste pas
pour les autres fonctions membres (degraves lors que lutilisateur ne dispose que du module
objet relatif agrave la classe)
4 Cas des objets transmis en argument drsquoune fonction membre
Dans les exemples preacuteceacutedents les fonctions membres recevaient
bull un argument implicite du type de leur classe agrave savoir ladresse de lobjet layant appeleacute
bull un certain nombre drsquoarguments qui eacutetaient dun type ordinaire (cest-agrave-dire autre que classe)
Mais une fonction membre peut outre largument implicite recevoir un ou plusieurs argu-
ments du type de sa classe Par exemple supposez que nous souhaitions au sein dune classe
point introduire une fonction membre nommeacutee coincide chargeacutee de deacutetecter la coiumlncidence
eacuteventuelle de deux points Son appel au sein dun programme se preacutesentera obligatoirement
comme pour toute fonction membre sous la forme
acoincide ()
a eacutetant un objet de type point
Il faudra donc impeacuterativement transmettre le second point en argument en supposant quil se
nomme b cela nous conduira agrave un appel de la forme
acoincide (b)
ou ici compte tenu de la symeacutetrie du problegraveme
bcoincide (a)
Voyons maintenant plus preacuteciseacutement comment eacutecrire la fonction coincide Voici ce que peut
ecirctre son en-tecircte en supposant quelle fournit une valeur de retour entiegravere (1 en cas de coiumlnci-
dence 0 dans le cas contraire)
int pointcoincide (point pt)
Dans coincide nous devons donc comparer les coordonneacutees de lobjet fourni implicitement
lors de son appel (ses membres sont deacutesigneacutes comme dhabitude par x et y) avec celles de
lobjet fourni en argument dont les membres sont deacutesigneacutes par ptx et pty Le corps de coin-
cide se preacutesentera donc ainsi
Les proprieacuteteacutes des fonctions membresCHAPITRE 6
98
if ((ptx == x) ampamp (pty == y)) return 1 else return 0
Voici un exemple complet de programme dans lequel nous avons limiteacute les fonctions mem-
bres de la classe point agrave un constructeur et agrave coincide
include ltiostreamgtusing namespace std class point Une classe point contenant seulement int x y public point (int abs=0 int ord=0) un constructeur (en ligne) x=abs y=ord int coincide (point) une fonction membre coincide int pointcoincide (point pt) if ( (ptx == x) ampamp (pty == y) ) return 1 else return 0 remarquez la dissymeacutetrie des notations ptx et xmain() Un petit programme dessai point a b(1) c(10) cout ltlt a et b ltlt acoincide(b) ltlt ou ltlt bcoincide(a) ltlt n cout ltlt b et c ltlt bcoincide(c) ltlt ou ltlt ccoincide(b) ltlt n
a et b 0 ou 0b et c 1 ou 1
Exemple dobjet transmis en argument agrave une fonction membre
On pourrait penser qursquoon viole le principe drsquoencapsulation dans la mesure ougrave lorsqursquoon
appelle la fonction coincide pour lrsquoobjet a (dans acoincide(b)) elle est autoriseacutee agrave acceacuteder
aux donneacutees de b En fait en C++ nrsquoimporte quelle fonction membre drsquoune classe peut acceacute-
der agrave nrsquoimporte quel membre (public ou priveacute) de nrsquoimporte quel objet de cette classe On
traduit souvent cela en disant que
Remarques
1 Nous aurions pu eacutecrire coincide de la maniegravere suivante
return ((ptx == x) ampamp (pty == y))
En C++ lrsquouniteacute drsquoencapsulation est la classe (et non pas lrsquoobjet )
5 - Mode de transmission des objets en argument99
2 En theacuteorie on peut dire que la coiumlncidence de deux points est symeacutetrique en ce sens
que lordre dans lequel on considegravere les deux points est indiffeacuterent Or cette symeacutetrie ne
se retrouve pas dans la deacutefinition de la fonction coincide pas plus que dans son appel
Cela provient de la transmission en argument implicite de lobjet appelant la fonction
Nous verrons que lrsquoutilisation drsquoune fonction amie permet de retrouver cette symeacute-
trie
3 Notez bien que lrsquouniteacute drsquoencapsulation est la classe concerneacutee pas toutes les classes
existantes Ainsi si A et B sont deux classes diffeacuterentes une fonction membre de A ne
peut heureusement pas acceacuteder aux membres priveacutes drsquoun objet de classe B (pas plus
que ne le pourrait une fonction ordinaire main par exemple) bien entendu elle peut
toujours acceacuteder aux membres publics Nous verrons plus tard qursquoil est possible agrave une
fonction (ordinaire ou membre) de saffranchir de cette interdiction (et donc cette fois
de violer veacuteritablement le principe dencapsulation) par des deacuteclarations damitieacute appro-
prieacutees
En Java
Lrsquouniteacute drsquoencapsulation est eacutegalement la classe
5 Mode de transmission des objets en argument
Dans lrsquoexemple preacuteceacutedent lobjet pt eacutetait transmis classiquement agrave coincide agrave savoir par
valeur Preacuteciseacutement cela signifie donc que lors de lappel
acoincide (b)
les valeurs des donneacutees de b sont recopieacutees dans un emplacement (de type point) local agrave coin-
cide (nommeacute pt)
Comme pour nimporte quel argument ordinaire il est possible de preacutevoir den transmettre
ladresse plutocirct que la valeur ou de mettre en place une transmission par reacutefeacuterence Exami-
nons ces deux possibiliteacutes
51 Transmission de ladresse dun objetIl est possible de transmettre explicitement en argument ladresse dun objet Rappelons que
dans un tel cas on ne change pas le mode de transmission de largument (contrairement agrave ce
qui se produit avec la transmission par reacutefeacuterence) on se contente de transmettre une valeur
qui se trouve ecirctre une adresse et quil faut donc interpreacuteter en conseacutequence dans la fonction
(notamment en employant lopeacuterateur dindirection ) A titre dexemple voici comment nous
pourrions modifier la fonction coincide du paragraphe preacuteceacutedent
Les proprieacuteteacutes des fonctions membresCHAPITRE 6
100
int pointcoincide (point adpt) if (( adpt -gt x == x) ampamp (adpt -gt y == y)) return 1 else return 0
Compte tenu de la dissymeacutetrie naturelle de notre fonction membre cette eacutecriture nest guegravere
choquante Par contre lappel de coincide (au sein de main) le devient davantage
acoincide (ampb)
ou
bcoincide (ampa)
Voici le programme complet ainsi modifieacute
include ltiostreamgtusing namespace std class point Une classe point contenant seulement int x y public point (int abs=0 int ord=0) un constructeur (en ligne) x=abs y=ord int coincide (point ) une fonction membre coincide int pointcoincide (point adpt) if ( (adpt-gtx == x) ampamp (adpt-gty == y) ) return 1 else return 0
main() Un petit programme dessai point a b(1) c(10) cout ltlt a et b ltlt acoincide(ampb) ltlt ou ltlt bcoincide(ampa) ltlt n cout ltlt b et c ltlt bcoincide(ampc) ltlt ou ltlt ccoincide(ampb) ltlt n
a et b 0 ou 0b et c 1 ou 1
Exemple de transmission de ladresse dun objet agrave une fonction membre
Remarque
Noubliez pas quagrave partir du moment ougrave vous fournissez ladresse dun objet agrave une fonction
membre celle-ci peut en modifier les valeurs (elle a accegraves agrave tous les membres sil sagit
dun objet de type de sa classe aux seuls membres publics dans le cas contraire) Si vous
craignez de tels effets de bord au sein de la fonction membre concerneacutee vous pouvez tou-
jours employer le qualificatif const Ainsi ici len-tecircte de coincide aurait pu ecirctre
int pointcoincide (const point adpt)
5 - Mode de transmission des objets en argument101
en modifiant parallegravelement son prototype
int coincide (const point )
Notez toutefois quune telle preacutecaution ne peut pas ecirctre prise avec largument implicite
quest lobjet ayant appeleacute la fonction Ainsi dans coincide muni de len-tecircte ci-dessus
vous ne pourriez plus modifier adpt -gt x mais vous pourriez toujours modifier x Lagrave
encore lrsquoutilisation drsquoune fonction amie permettra drsquoassurer lrsquoeacutegaliteacute de traitement des
deux arguments en particulier au niveau de leur constance
52 Transmission par reacutefeacuterenceComme nous lavons vu lemploi des reacutefeacuterences permet de mettre en place une transmission
par adresse sans avoir agrave en prendre en charge soi-mecircme la gestion Elle simplifie dautant
leacutecriture de la fonction concerneacutee et ses diffeacuterents appels Voici une adaptation de coincide
dans laquelle son argument est transmis par reacutefeacuterence
include ltiostreamgtusing namespace std class point Une classe point contenant seulement int x y public point (int abs=0 int ord=0) un constructeur (en ligne) x=abs y=ord int coincide (point amp) une fonction membre coincide int pointcoincide (point amp pt) if ( (ptx == x) ampamp (pty == y) ) return 1 else return 0 main() Un petit programme dessai point a b(1) c(10) cout ltlt a et b ltlt acoincide(b) ltlt ou ltlt bcoincide(a) ltlt n cout ltlt b et c ltlt bcoincide(c) ltlt ou ltlt ccoincide(b) ltlt n
a et b 0 ou 0b et c 1 ou 1
Exemple de transmission par reacutefeacuterence dun objet agrave une fonction membre
Remarque
La remarque preacuteceacutedente (en fin de paragraphe 51) sur les risques deffets de bord sappli-
que eacutegalement ici Le qualificatif const pourrait y intervenir de maniegravere analogue
int pointcoincide (const point amp pt)
Les proprieacuteteacutes des fonctions membresCHAPITRE 6
102
53 Les problegravemes poseacutes par la transmission par valeurNous avons deacutejagrave vu que laffectation dobjets pouvait poser des problegravemes dans le cas ougrave ces
objets posseacutedaient des pointeurs sur des emplacements alloueacutes dynamiquement Ces poin-
teurs eacutetaient effectivement recopieacutes mais il nen allait pas de mecircme des emplacements poin-
teacutes Le transfert drsquoarguments par valeur preacutesente les mecircmes risques dans la mesure ougrave il
sagit eacutegalement dune simple recopie
De mecircme que le problegraveme poseacute par laffectation peut ecirctre reacutesolu par la surdeacutefinition de cet
opeacuterateur celui poseacute par le transfert par valeur peut ecirctre reacutegleacute par lemploi dun constructeur
particulier nous vous montrerons comment degraves le prochain chapitre
Dune maniegravere geacuteneacuterale dailleurs nous verrons que les problegravemes poseacutes par les objets conte-
nant des pointeurs se ramegravenent effectivement agrave laffectation et agrave lrsquoinitialisation1 dont la
recopie en cas de transmission par valeur constitue un cas particulier
6 Lorsqursquoune fonction renvoie un objetCe que nous avons dit agrave propos des arguments dune fonction membre sapplique eacutegalement agrave
sa valeur de retour Cette derniegravere peut ecirctre un objet et on peut choisir entre
bull transmission par valeur
bull transmission par adresse
bull transmission par reacutefeacuterence
Cet objet pourra ecirctre
bull du mecircme type que la classe auquel cas la fonction aura accegraves agrave ses membres priveacutes
bull dun type diffeacuterent de la classe auquel cas la fonction naura accegraves quagrave ses membres publics
La transmission par valeur suscite la mecircme remarque que preacuteceacutedemment par deacutefaut elle se
fait par simple recopie de lobjet Pour les objets comportant des pointeurs sur des emplace-
ments dynamiques il faudra preacutevoir un constructeur particulier (dinitialisation)
En revanche la transmission dune adresse ou la transmission par reacutefeacuterence risquent de poser
un problegraveme qui nexistait pas pour les arguments Si une fonction transmet ladresse ou la
reacutefeacuterence dun objet il vaut mieux eacuteviter quil sagisse dun objet local agrave la fonction cest-agrave-
dire de classe automatique En effet dans ce cas lemplacement de cet objet sera libeacutereacute2 degraves
la sortie de la fonction la fonction appelante reacutecupeacuterera ladresse de quelque chose nexistant
plus vraiment3 Nous reviendrons plus en deacutetail sur ce point dans le chapitre consacreacute agrave la
surdeacutefinition dopeacuterateurs
1 Bien que cela napparaisse pas toujours clairement en C il est tregraves important de noter qursquoen C++ affectation et
initialisation sont deux choses diffeacuterentes
2 Comme nous le verrons en deacutetail au chapitre suivant il y aura appel du destructeur sil existe
7 - Autoreacutefeacuterence le mot cleacute this103
A titre dexemple voici une fonction membre nommeacutee symetrique qui pourrait ecirctre introduite
dans une classe point pour fournir en retour un point symeacutetrique de celui layant appeleacute
point pointsymetrique ( ) point res resx = -x resy = -y return res
Vous constatez quil a eacuteteacute neacutecessaire de creacuteer un objet automatique res au sein de la fonction
Comme nous lavons expliqueacute ci-dessus il ne serait pas conseilleacute den preacutevoir une transmis-
sion par reacutefeacuterence en utilisant cet en-tecircte
point amp pointsymetrique ( )
7 Autoreacutefeacuterence le mot cleacute thisNous avons eu souvent loccasion de dire quune fonction membre dune classe reccediloit une
information lui permettant dacceacuteder agrave lobjet layant appeleacute Le terme information bien
quil soit relativement flou nous a suffi pour expliquer tous les exemples rencontreacutes jusquici
Mais nous navions pas besoin de manipuler explicitement ladresse de lobjet en question Or
il existe des circonstances ougrave cela devient indispensable Songez par exemple agrave la gestion
dune liste chaicircneacutee dobjets de mecircme nature pour eacutecrire une fonction membre inseacuterant un
nouvel objet (supposeacute transmis en argument implicite) il faudra bien placer son adresse dans
lobjet preacuteceacutedent de la liste
Pour reacutesoudre de tels problegravemes C++ a preacutevu le mot cleacute this Celui-ci utilisable unique-
ment au sein dune fonction membre deacutesigne un pointeur sur lobjet layant appeleacute
Ici il serait preacutematureacute de deacutevelopper lexemple de liste chaicircneacutee dont nous venons de parler
nous vous proposons un exemple deacutecole dans la classe point la fonction affiche fournit
ladresse de lobjet layant appeleacute
include ltiostreamgtusing namespace std class point Une classe point contenant seulement int x y public point (int abs=0 int ord=0) Un constructeur (inline) x=abs y=ord void affiche () Une fonction affiche void pointaffiche () cout ltlt Adresse ltlt this ltlt - Coordonnees ltlt x ltlt ltlt y ltlt n
3 Dans certaines impleacutementations un emplacement libeacutereacute nest pas remis agrave zeacutero Ainsi on peut avoir lillusion que
cela marche si lon se contente dexploiter lobjet immeacutediatement apregraves lappel de la fonction
Les proprieacuteteacutes des fonctions membresCHAPITRE 6
104
main() Un petit programme dessai point a(5) b(315) aaffiche () baffiche ()
Adresse 006AFDF0 - Coordonnees 5 0Adresse 006AFDE8 - Coordonnees 3 15
Exemple dutilisation de this
Remarques
A titre purement indicatif la fonction coincide du paragraphe 51 pourrait seacutecrire
int pointcoincide (point adpt) if ((this -gt x == adpt -gt x) ampamp (this -gt y == adpt -gt y)) return 1 else return 0
La symeacutetrie du problegraveme y apparaicirct plus clairement Ce serait moins le cas si lon eacutecri-
vait ainsi la fonction coincide du paragraphe 4
int pointcoincide (point pt) if ((this -gt x == ptx)) ampamp (this -gt y == pty)) return 1 else return 0
En Java
Le mot cleacute this existe eacutegalement en Java avec une signification voisine il deacutesigne lrsquoobjet
ayant appeleacute une fonction membre au lieu de son adresse en C++ (de toute faccedilon la
notion de pointeur nrsquoexiste pas en Java)
8 Les fonctions membres statiquesNous avons deacutejagrave vu (paragraphe 5 du chapitre 5) comment C++ permet de deacutefinir des mem-
bres donneacutees statiques Ceux-ci existent en un seul exemplaire (pour une classe donneacutee)
indeacutependamment des objets de leur classe
Dune maniegravere analogue on peut imaginer que certaines fonctions membres dune classe
aient un rocircle totalement indeacutependant dun quelconque objet ce serait notamment le cas dune
fonction qui se contenterait dagir sur des membres donneacutees statiques
On peut certes toujours appeler une telle fonction en la faisant porter artificiellement sur un
objet de la classe et ce bien que ladresse de cet objet ne soit absolument pas utile agrave la fonc-
tion En fait il est possible de rendre les choses plus lisibles et plus efficaces en deacuteclarant sta-
8 - Les fonctions membres statiques105
tique (mot cleacute static) la fonction membre concerneacutee Dans ce cas en effet son appel ne
neacutecessite plus que le nom de la classe correspondante (accompagneacute naturellement de lopeacute-
rateur de reacutesolution de porteacutee) Comme pour les membres statiques une telle fonction mem-
bre statique peut mecircme ecirctre appeleacutee lorsquil nexiste aucun objet de sa classe
Voici un exemple de programme illustrant lemploi dune fonction membre statique il sagit
de lexemple preacutesenteacute au paragraphe 53 du chapitre 5 dans lequel nous avons introduit une
fonction membre statique nommeacutee compte affichant simplement le nombre dobjets de sa
classe
include ltiostreamgtusing namespace std class cpte_obj static int ctr compteur (statique) du nombre dobjets creacuteeacutes public cpte_obj () ~cpte_obj() static void compte () pour afficher le nombre dobjets creacuteeacutes int cpte_objctr = 0 initialisation du membre statique ctrcpte_objcpte_obj () constructeur cout ltlt ++ construction il y a maintenant ltlt ++ctr ltlt objetsn cpte_obj~cpte_obj () destructeur cout ltlt -- destruction il reste maintenant ltlt --ctr ltlt objetsn void cpte_objcompte () cout ltlt appel compte il y a ltlt ctr ltlt objetsn main() void fct () cpte_objcompte () appel de la fonction membre statique compte alors quaucun objet de sa classe nexiste cpte_obj a cpte_objcompte () fct () cpte_objcompte () cpte_obj b cpte_objcompte () void fct() cpte_obj u v
appel compte il y a 0 objets++ construction il y a maintenant 1 objets appel compte il y a 1 objets++ construction il y a maintenant 2 objets
Les proprieacuteteacutes des fonctions membresCHAPITRE 6
106
++ construction il y a maintenant 3 objets
-- destruction il reste maintenant 2 objets
-- destruction il reste maintenant 1 objets
appel compte il y a 1 objets
++ construction il y a maintenant 2 objets
appel compte il y a 2 objets
-- destruction il reste maintenant 1 objets
-- destruction il reste maintenant 0 objets
Deacutefinition et utilisation dune fonction membre statique
En Java
Les fonctions membres statiques existent eacutegalement en Java et elles se deacuteclarent agrave lrsquoaide
du mecircme mot cleacute static
9 Les fonctions membres constantes
91 Rappels sur lrsquoutilisation de const en CEn langage C le qualificatif const peut servir agrave deacutesigner une variable dont on souhaite que la
valeur neacutevolue pas Le compilateur est ainsi en mesure de rejeter deacuteventuelles tentatives de
modification de cette variable Par exemple avec cette deacuteclaration
const int n=20
linstruction suivante sera incorrecte
n = 12 incorrecte n nrsquoest pas une lvalue
De la mecircme maniegravere on ne peut modifier la valeur drsquoun argument muet deacuteclareacute constant
dans lrsquoen-tecircte drsquoune fonction
void f(const int n) ou mecircme void f(const int amp n) - voir remarque
n++ incorrect n nrsquoest pas une lvalue
Remarque
Ne confondez pas un argument muet deacuteclareacute const et un argument effectif deacuteclareacute const
Dans le premier cas la deacuteclaration const constitue une sorte de contrat le program-
meur de la fonction srsquoengage agrave ne pas en modifier la valeur et ce mecircme si au bout du
compte la fonction travaille sur une copie de la valeur de lrsquoargument effectif (ce qui est le
cas avec la transmission par valeur avec une reacutefeacuterence agrave une constante )
9 - Les fonctions membres constantes107
92 Deacutefinition drsquoune fonction membre constanteC++ eacutetend ce concept de constance des variables aux classes ce qui signifie quon peut deacutefi-
nir des objets constants Encore faut-il comprendre ce que lon entend par lagrave En effet dans
le cas dune variable ordinaire le compilateur peut assez facilement identifier les opeacuterations
interdites (celles qui peuvent en modifier la valeur) En revanche dans le cas dun objet les
choses sont moins faciles car les opeacuterations sont geacuteneacuteralement reacutealiseacutees par les fonctions
membres Cela signifie que lutilisateur doit preacuteciser parmi ces fonctions membres lesquel-
les sont autoriseacutees agrave opeacuterer sur des objets constants Il le fera en utilisant le mot const dans
leur deacuteclaration comme dans cet exemple de deacutefinition dune classe point
class point int x y public point () void affiche () const void deplace ()
Remarque
La remarque du paragraphe 91 agrave propos des arguments muets constants srsquoapplique
encore ici Il ne faut pas confondre un argument muet deacuteclareacute const et un argument effec-
tif deacuteclareacute const
93 Proprieacuteteacutes drsquoune fonction membre constanteLe fait de speacutecifier que la fonction affiche est constante a deux conseacutequences
1 Elle est utilisable pour un objet deacuteclareacute constant
Ici nous avons speacutecifieacute que la fonction affiche eacutetait utilisable pour un point constant En
revanche la fonction deplace qui na pas fait lobjet dune deacuteclaration const ne le sera pas
Ainsi avec ces deacuteclarations
point a const point c
les instructions suivantes seront correctes
aaffiche () caffiche () adeplace ()
En revanche celle-ci sera rejeteacutee par le compilateur
cdeplace () incorrecte c est constant alors que deplace ne lrsquoest pas
La mecircme remarque srsquoappliquerait agrave un objet reccedilu en argument
Les proprieacuteteacutes des fonctions membresCHAPITRE 6
108
void f (const point p) ou mecircme void f(const point amp p) - voir remarque paffiche () OK pdeplace () incorrecte
2 Les instructions figurant dans sa deacutefinition ne doivent pas modifier la valeur des membres
de lrsquoobjet point
class point int x y public void affiche () const x++ erreur car affiche a eacuteteacute deacuteclareacutee const
Les membres statiques font naturellement exception agrave cette regravegle car ils ne sont pas associeacutes
agrave un objet particulier
class compte static int n public void test() const n++ OK bien que test soit deacuteclareacutee constante car n est un membre statique
Remarques
1 Le meacutecanisme que nous venons dexposer sapplique aux fonctions membres volatiles et
aux objets volatiles (mot cleacute volatile) Il suffit de transposer tout ce qui vient drsquoecirctre dit en
remplaccedilant le mot cleacute const par le mot cleacute volatile
2 Il est possible de surdeacutefinir une fonction membre en se fondant sur la preacutesence ou
labsence du qualificatif const Ainsi dans la classe point preacuteceacutedente nous pouvons
deacutefinir ces deux fonctions
void affiche () const affiche I void affiche () affiche II
Avec ces deacuteclarations
point a const point c
linstruction aaffiche () appellera la fonction II tandis que caffiche () appellera la fonc-
tion I
On notera bien que si seule la fonction void affiche() est deacutefinie elle ne pourra en aucun
cas ecirctre appliqueacutee agrave un objet constant une instruction telle que caffiche() serait alors
rejeteacutee en compilation En revanche si seule la fonction const void affiche() est deacutefinie
10 - Les membres mutables109
elle pourra ecirctre appliqueacutee indiffeacuteremment agrave des objets constants ou non Une telle atti-
tude est logique
ndash on ne court aucun risque en traitant un objet non constant comme sil eacutetait constant
ndash en revanche il serait dangereux de faire agrave un objet constant ce quon a preacutevu de faire agrave
un objet non constant
3 Pour pouvoir deacuteclarer un objet constant il faut ecirctre sucircr que le concepteur de la classe
correspondante a eacuteteacute exhaustif dans le recencement des fonctions membres constantes
(crsquoest-agrave-dire deacuteclareacutees avec le qualificatif const) Dans le cas contraire on risque de ne
plus pouvoir appliquer certaines fonctionnaliteacutes agrave un tel objet constant Par exemple on
ne pourra pas appliquer la meacutethode affiche agrave un objet constant de type point si celle-ci
nrsquoa pas effectivement eacuteteacute deacuteclareacutee constante dans la classe
En Java
La notion de fonction membre constante nrsquoexiste pas en Java
10 Les membres mutablesUne fonction membre constante ne peut pas modifier les valeurs de membres non statiques
La norme a jugeacute que cette restriction pouvait parfois srsquoaveacuterer trop contraignante Elle a intro-
duit le qualificatif mutable pour deacutesigner des champs dont on accepte la modification mecircme
par des fonctions membres constantes Voici un petit exemple
class truc int x y mutable int n n est modifiable par une fonction membre constante void f() x = 5 n++ rien de nouveau ici void f1() const n++ OK car n est deacuteclareacute mutable x = 5 erreur f1 est const et x nrsquoest pas mutable
Comme on peut srsquoy attendre les membres publics deacuteclareacutes avec le qualificatif mutable sont
modifiables par affectation
class truc2 public int n mutable int p const truc c cn = 5 erreur lrsquoobjet c est constant et n nrsquoest pas mutablecp = 5 OK lrsquoobjet c est constant mais p est mutable
Les proprieacuteteacutes des fonctions membresCHAPITRE 6
110
En Java
En Java il nrsquoexiste pas de membres constants a fortiori il ne peut y avoir de membres
mutables
ExercicesNB Les exercices marqueacutes (C) sont corrigeacutes en fin de volume
1 (C) Ecrivez une classe vecteur comportant
bull trois composantes de type double (priveacutees)
bull une fonction affiche
bull deux constructeurs
ndash lun sans arguments initialisant chaque composante agrave 0
ndash lautre avec 3 arguments repreacutesentant les composantes
a) avec des fonctions membres indeacutependantes
b) avec des fonctions membres en ligne
2 (C) Ajoutez agrave la premiegravere classe vecteur preacuteceacutedente une fonction membre nommeacutee
prod_scal fournissant en reacutesultat le produit scalaire de deux vecteurs
3 (C) Ajoutez agrave la classe vecteur preacuteceacutedente (exercice 2) une fonction membre nommeacutee
somme permettant de calculer la somme de deux vecteurs
4 (C) Modifiez la classe vecteur preacuteceacutedente (exercice 3) de maniegravere que toutes les transmis-
sions de valeurs de type vecteur aient lieu
a) par adresse
b) par reacutefeacuterence
7
Construction destructionet initialisation des objets
En langage C une variable peut ecirctre creacuteeacutee de deux faccedilons
bull par une deacuteclaration elle est alors de classe automatique ou statique sa dureacutee de vie est
parfaitement deacutefinie par la nature et lemplacement de sa deacuteclaration
bull en faisant appel agrave des fonctions de gestion dynamique de la meacutemoire (malloc calloc
free) elle est alors dite dynamique sa dureacutee de vie est controcircleacutee par le programme
En langage C++ on retrouvera ces trois classes agrave la fois pour les variables ordinaires et pour
les objets avec cette diffeacuterence que la gestion dynamique fera appel aux opeacuterateurs new et
delete
Dans ce chapitre nous eacutetudierons ces diffeacuterentes possibiliteacutes de creacuteation (donc aussi de des-
truction) des objets Nous commencerons par examiner la creacuteation et la destruction des objets
automatiques et statiques deacutefinis par une deacuteclaration Puis nous montrerons comment creacuteer et
utiliser des objets dynamiques dune maniegravere comparable agrave celle employeacutee pour creacuteer des
variables dynamiques ordinaires en faisant appel agrave une syntaxe eacutelargie de lopeacuterateur new
Nous aborderons ensuite la notion de constructeur de recopie qui intervient dans les situa-
tions dites drsquoinitialisation dun objet cest-agrave-dire lorsquil est neacutecessaire de reacutealiser une
copie dun objet existant Nous verrons quil existe trois situations de ce type transmission
de la valeur dun objet en argument dune fonction transmission de la valeur dun objet en
reacutesultat dune fonction initialisation dun objet lors de sa deacuteclaration par un objet de mecircme
Construction destruction et initialisation des objetsCHAPITRE 7
112
type cette derniegravere possibiliteacute neacutetant quun cas particulier dinitialisation dun objet au
moment de sa deacuteclaration
Puis nous examinerons le cas des objets membres cest-agrave-dire le cas ougrave un type classe pos-
segravede des membres donneacutees eux-mecircme dun type classe Nous aborderons rapidement le cas
du tableau dobjets notion dautant moins importante quun tel tableau nest pas lui-mecircme un
objet
Enfin nous fournirons quelques indications concernant les objets dits temporaires cest-agrave-
dire pouvant ecirctre creacuteeacutes au fil du deacuteroulement du programme1 sans que le programmeur lait
explicitement demandeacute
1 Les objets automatiques et statiquesNous examinons seacutepareacutement
bull leur dureacutee de vie cest-agrave-dire le moment ougrave ils sont creacuteeacutes et celui ougrave ils sont deacutetruits
bull les eacuteventuels appels des constructeurs et des destructeurs
11 Dureacutee de vieLes regravegles sappliquant aux variables ordinaires se transposent tout naturellement aux objets
Les objets automatiques sont ceux creacuteeacutes par une deacuteclaration
bull dans une fonction ceacutetait le cas dans les exemples des chapitres preacuteceacutedents Lrsquoobjet est
creacuteeacute lors de la rencontre de sa deacuteclaration laquelle peut tregraves bien en C++ ecirctre situeacutee apregraves
dautres instructions exeacutecutables2 Il est deacutetruit agrave la fin de lexeacutecution de la fonction
bull dans un bloc lrsquoobjet est aussi creacuteeacute lors de la rencontre de sa deacuteclaration (lagrave encore celle-
ci peut ecirctre preacuteceacutedeacutee au sein de ce bloc dautres instructions exeacutecutables) il est deacutetruit lors
de la sortie du bloc
Les objets statiques sont ceux creacuteeacutes par une deacuteclaration situeacutee
bull en dehors de toute fonction
bull dans une fonction mais assortie du qualificatif static
Les objets statiques sont creacuteeacutes avant le deacutebut de lexeacutecution de la fonction main et deacutetruits
apregraves la fin de son exeacutecution
1 En C il existe deacutejagrave des variables temporaires mais leur existence a moins drsquoimportance que celle des objets
temporaires en C++
2 La distinction entre instruction exeacutecutable et instruction de deacuteclaration nest pas toujours possible dans un langage
comme C++ qui accepte par exemple une instruction telle que
double adr = new double [nelem = 2 n+1]
1 - Les objets automatiques et statiques113
12 Appel des constructeurs et des destructeursRappelons que si un objet possegravede un constructeur sa deacuteclaration (lorsque comme nous le
supposons pour linstant elle ne contient pas drsquoinitialiseur) doit obligatoirement comporter
les arguments correspondants Par exemple si une classe point comporte le constructeur de
prototype
point (int int)
les deacuteclarations suivantes seront incorrectes
point a incorrect le constructeur attend deux argumentspoint b (3) incorrect (mecircme raison)
Celle-ci en revanche conviendra
point a(1 7) correct car le constructeur possegravede deux arguments
Sil existe plusieurs constructeurs il suffit que la deacuteclaration comporte les arguments requis
par lun dentre eux Ainsi si une classe point comporte les constructeurs suivants
point ( ) constructeur 1point (int int) constructeur 2
la deacuteclaration suivante sera rejeteacutee
point a(5) incorrect aucun constructeur agrave un argument
Mais celles-ci conviendront
point a correct appel du constructeur 1point b(1 7) correct appel du constructeur 2
En ce qui concerne la chronologie on peut dire que
bull le constructeur est appeleacute apregraves la creacuteation de lrsquoobjet
bull le destructeur est appeleacute avant la destruction de lobjet
Remarque
Une deacuteclaration telle que
point a attention point a () serait une deacuteclaration drsquoune fonction a
est acceptable dans deux situations fort diffeacuterentes
ndash il nexiste pas de constructeur de point
ndash il existe un constructeur de point sans argument
13 ExempleVoici un exemple de programme mettant en eacutevidence la creacuteation et la destruction dobjets sta-
tiques et automatiques Nous avons deacutefini une classe nommeacutee point dans laquelle le cons-
tructeur et le destructeur affichent un message permettant de repeacuterer
Construction destruction et initialisation des objetsCHAPITRE 7
114
bull le moment de leur appel
bull lrsquoobjet concerneacute (nous avons fait en sorte que chaque objet de type point possegravede des valeurs
diffeacuterentes)
include ltiostreamgt
using namespace std
class point
int x y
public
point (int abs int ord) constructeur (inline)
x = abs y = ord
cout ltlt ++ Construction dun point ltlt x ltlt ltlt y ltlt n
~point () destructeur (inline)
cout ltlt -- Destruction du point ltlt x ltlt ltlt y ltlt n
point a(11) un objet statique de classe point
main()
cout ltlt Debut main n
point b(1010) un objet automatique de classe point
int i
for (i=1 ilt=3 i++)
cout ltlt Boucle tour numero ltlt i ltlt n
point b(i2i) objets creacuteeacutes dans un bloc
cout ltlt Fin main n
++ Construction dun point 1 1
Debut main
++ Construction dun point 10 10
Boucle tour numero 1
++ Construction dun point 1 2
-- Destruction du point 1 2
Boucle tour numero 2
++ Construction dun point 2 4
-- Destruction du point 2 4
Boucle tour numero 3
++ Construction dun point 3 6
-- Destruction du point 3 6
Fin main
2 - Les objets dynamiques115
-- Destruction du point 10 10-- Destruction du point 1 1
Construction et destruction dobjets statiques et automatiques
Remarque
Lexistence de constructeurs et de destructeurs conduit agrave des traitements qui napparais-
sent pas explicitement dans les instructions du programme Par exemple ici une deacuteclara-
tion banale telle que
point b(10 10)
entraicircne laffichage dun message
Qui plus est un certain nombre drsquoopeacuterations se deacuteroulent avant le deacutebut ou apregraves lexeacute-
cution de la fonction main1 On pourrait agrave la limite concevoir une fonction main ne
comportant que des deacuteclarations (ce qui serait le cas de notre exemple si nous suppri-
mions linstruction daffichage du tour de boucle) et reacutealisant malgreacute tout un certain
traitement
2 Les objets dynamiquesNous avons deacutejagrave vu comment creacuteer utiliser et deacutetruire des variables dynamiques scalaires (ou
des tableaux de telles variables) en C++ Bien entendu ces possibiliteacutes srsquoeacutetendent aux struc-
tures et aux objets Nous commencerons par les structures ce qui nous permettra un certain
nombre de rappels sur lutilisation des structures dynamiques en C
21 Les structures dynamiquesSupposez que nous ayons deacutefini la structure suivante
struct chose int x double y int t [5]
et que adr soit un pointeur sur des eacuteleacutements de ce type cest-agrave-dire deacuteclareacute en C par
struct chose adr
1 En toute rigueur il en va deacutejagrave de mecircme dans le cas dun programme C (ouverture ou fermeture de fichiers par
exemple) mais il ne sagit pas alors de tacircches programmeacutees explicitement par lauteur du programme dans le cas de
C++ il sagit de tacircches programmeacutees par le concepteur de la classe concerneacutee
Construction destruction et initialisation des objetsCHAPITRE 7
116
ou plus simplement en C++ par
chose adr
Linstruction
adr = new chose
reacutealise une allocation dynamique despace meacutemoire pour un eacuteleacutement de type chose et affecte
son adresse au pointeur adr
Laccegraves aux diffeacuterents champs de cette structure se fait agrave laide de lopeacuterateur -gt Ainsi adr -
gt y deacutesignera le second champ Rappelons que cette notation est en fait eacutequivalente agrave (adr)
y
Lespace meacutemoire ainsi alloueacute pourra ecirctre libeacutereacute par
delete adr
22 Les objets dynamiquesVoyons tout dabord ce quil y a de commun entre la creacuteation dynamique dobjets et celle de
structures avant deacutetudier les nouvelles possibiliteacutes de lopeacuterateur new
221 Points communs avec les structures dynamiquesLe meacutecanisme que nous venons deacutevoquer sapplique aux objets (au sens large) lorsquils ne
possegravedent pas de constructeur Ainsi si nous deacutefinissons le type point suivant
class point
int x y
public
void initialise (int int)
void deplace (int int)
void affiche ( )
et si nous deacuteclarons
point adr
nous pourrons creacuteer dynamiquement un emplacement de type point (qui contiendra donc ici
la place pour deux entiers) et affecter son adresse agrave adr par
adr = new point
Laccegraves aux fonctions membres de lrsquoobjet pointeacute par adr se fera par des appels de la forme
adr -gt initialise (1 3)
adr -gt affiche ( )
ou eacuteventuellement sans utiliser lopeacuterateur -gt par
( adr)initialise (1 3)
( adr)affiche ( )
Si lrsquoobjet contenait des membres donneacutees publics on y acceacutederait de faccedilon comparable
Quant agrave la suppression de lrsquoobjet en question elle se fera ici encore par
delete adr
2 - Les objets dynamiques117
222 Les nouvelles possibiliteacutes des opeacuterateurs new et delete
Nous avons deacutejagrave vu que la philosophie de C++ consiste agrave faire du constructeur (degraves lors quil
existe) un passage obligeacute lors de la creacuteation dun objet Il en va de mecircme pour le destructeur
lors de la destruction dun objet
Cette philosophie sapplique eacutegalement aux objets dynamiques Plus preacuteciseacutement
bull Apregraves lallocation dynamique de lemplacement meacutemoire requis lopeacuterateur new appelle-
ra un constructeur de lrsquoobjet ce constructeur sera deacutetermineacute par la nature des arguments
qui figurent agrave la suite de son appel comme dans
new point (2 5)
On peut dire que le constructeur appeleacute est le mecircme que celui qui aurait eacuteteacute appeleacute par une
deacuteclaration telle que
a = point (2 5)
Bien entendu sil nexiste pas de constructeur ou sil existe un constructeur sans argument
la syntaxe
new point ou new point ()
sera accepteacutee En revanche si tous les constructeurs possegravedent au moins un argument cette
syntaxe sera rejeteacutee
On retrouve lagrave en deacutefinitive les mecircmes regravegles que celles sappliquant agrave la deacuteclaration dun
objet
bull Avant la libeacuteration de lemplacement meacutemoire correspondant lopeacuterateur delete appellera
le destructeur
223 Exemple
Voici un exemple de programme qui creacutee dynamiquement un objet de type point dans la
fonction main et qui le deacutetruit dans une fonction fct (appeleacutee par main) Les messages affi-
cheacutes permettent de mettre en eacutevidence les moments auxquels sont appeleacutes le constructeur et
le destructeur
include ltiostreamgtusing namespace std class point int x y public point (int abs int ord) constructeur x=abs y=ord cout ltlt ++ Appel Constructeur n ~point () destructeur (en fait inutile ici) cout ltlt -- Appel Destructeur n
Construction destruction et initialisation des objetsCHAPITRE 7
118
main() void fct (point ) prototype fonction fct point adr cout ltlt Debut main n adr = new point (37) creacuteation dynamique dun objet fct (adr) cout ltlt Fin main n void fct (point adp) cout ltlt Debut fct n delete adp destruction de cet objet cout ltlt Fin fct n
Debut main ++ Appel Constructeur Debut fct -- Appel Destructeur Fin fct Fin main
Exemple de creacuteation dynamique dobjets
En Java
Il nrsquoexiste qursquoune seule maniegravere de geacuterer la meacutemoire alloueacutee agrave un objet agrave savoir de
maniegravere dynamique Les emplacements sont alloueacutes explicitement en faisant appel agrave une
meacutethode nommeacutee eacutegalement new En revanche leur libeacuteration se fait automatiquement
gracircce agrave un ramasse-miettes destineacute agrave reacutecupeacuterer les emplacements qui ne sont plus reacutefeacuteren-
ceacutes
3 Le constructeur de recopie
31 PreacutesentationNous avons vu comment C++ garantissait lappel dun constructeur pour un objet creacuteeacute par une
deacuteclaration ou par new Ce point est fondamental puisquil donne la certitude quun objet ne
pourra ecirctre creacuteeacute sans avoir eacuteteacute placeacute dans un eacutetat initial convenable (du moins jugeacute comme
tel par le concepteur de lrsquoobjet)
Mais il existe des circonstances dans lesquelles il est neacutecessaire de construire un objet mecircme
si le programmeur na pas preacutevu de constructeur pour cela La situation la plus freacutequente est
celle ougrave la valeur dun objet doit ecirctre transmise en argument agrave une fonction Dans ce cas il est
3 - Le constructeur de recopie119
neacutecessaire de creacuteer dans un emplacement local agrave la fonction un objet qui soit une copie de
largument effectif Le mecircme problegraveme se pose dans le cas dun objet renvoyeacute par valeur
comme reacutesultat dune fonction il faut alors creacuteer dans un emplacement local agrave la fonction
appelante un objet qui soit une copie du reacutesultat Nous verrons quil existe une troisiegraveme
situation de ce type agrave savoir le cas ougrave un objet est initialiseacute lors de sa deacuteclaration avec un
autre objet de mecircme type
Dune maniegravere geacuteneacuterale on regroupe ces trois situations sous le nom dinitialisation par
recopie1 Une initialisation par recopie dun objet est donc la creacuteation dun objet par recopie
dun objet existant de mecircme type
Pour reacutealiser une telle initialisation C++ a preacutevu dutiliser un constructeur particulier dit
constructeur de recopie2 (nous verrons plus loin la forme exacte quil doit posseacuteder) Si un
tel constructeur nexiste pas un traitement par deacutefaut est preacutevu on peut dire de faccedilon eacutequi-
valente quon utilise un constructeur de recopie par deacutefaut
En deacutefinitive on peut dire que dans toute situation dinitialisation par recopie il y toujours
appel dun constructeur de recopie mais il faut distinguer deux cas principaux et un cas parti-
culier
311 Il nexiste pas de constructeur approprieacute
Il y alors appel dun constructeur de recopie par deacutefaut geacuteneacutereacute automatiquement par le
compilateur Ce constructeur se contente deffectuer une copie de chacun des membres On
retrouve lagrave une situation analogue agrave celle qui est mise en place (par deacutefaut) lors dune affecta-
tion entre objets de mecircme type Elle posera donc les mecircmes problegravemes pour les objets conte-
nant des pointeurs sur des emplacements dynamiques On aura simplement affaire agrave une
copie superficielle cest-agrave-dire que seules les valeurs des pointeurs seront recopieacutees les
emplacements pointeacutes ne le seront pas ils risquent alors par exemple drsquoecirctre deacutetruits deux
fois
312 Il existe un constructeur approprieacute
Vous pouvez fournir explicitement dans votre classe un constructeur de recopie Il doit
alors sagir dun constructeur disposant drsquoun seul argument3 du type de la classe et transmis
obligatoirement par reacutefeacuterence Cela signifie que son en-tecircte doit ecirctre obligatoirement de lune
de ces deux formes (si la classe concerneacutee se nomme point)
point (point amp) point (const point amp)
Dans ce cas ce constructeur est appeleacute de maniegravere habituelle apregraves la creacuteation de lobjet
Bien entendu aucune recopie nest faite de faccedilon automatique pas mecircme une recopie super-
1 Nous aurions pu nous limiter au terme initialisation sil nexistait pas des situations ougrave lon peut initialiser un objet
avec une valeur ou un objet dun type diffeacuterent
2 En anglais copy constructor
3 En toute rigueur la norme ANSI du C++ accepte eacutegalement un constructeur disposant drsquoarguments
suppleacutementaires pourvu qursquoils possegravedent des valeurs par deacutefaut
Construction destruction et initialisation des objetsCHAPITRE 7
120
ficielle contrairement agrave la situation preacuteceacutedente cest agrave ce constructeur de prendre en charge
linteacutegraliteacute du travail (copie superficielle et copie profonde)
313 Lorsqursquoon souhaite interdire la contruction par recopie
On a vu que la copie par deacutefaut des objets contenant des pointeurs nrsquoeacutetait pas satisfaisante
Dans certains cas plutocirct que de munir une classe du constructeur de recopie voulu le con-
cepteur pourra chercher agrave interdire la copie des objets de cette classe Il dispose alors pour
cela de diffeacuterentes possibiteacutes
Par exemple comme nous venons de le voir un constructeur priveacute nrsquoest pas appelable par un
utilisateur de la classe On peut aussi utiliser la possibliteacute offerte par C++ de deacuteclarer une
fonction sans en fournir de deacutefinition dans ce cas toute tentative de copie (mecircme par une
fonction membre cette fois) sera rejeteacutee par lrsquoeacutediteur de liens Drsquoune maniegravere geacuteneacuterale il
peut ecirctre judicieux de combiner les deux possibiliteacutes crsquoest-agrave-dire drsquoeffectuer une deacuteclaration
priveacutee sans deacutefinition Dans ce cas les tentatives de recopie par lrsquoutilisateur resteront deacutetec-
teacutees en compilation (avec un message explicite) et seules les recopies par une fonction mem-
bre se limiteront agrave une erreur drsquoeacutedition de liens (et ce point ne concerne que le concepteur de
la classe pas son utilisateur )
Remarques
1 Notez bien que C++ impose au constructeur par recopie que son unique argument soit
transmis par reacutefeacuterence (ce qui est logique puisque sinon lappel du constructeur de reco-
pie impliquerait une initialisation par recopie de largument donc un appel du construc-
teur de recopie qui lui-mecircme etc)
Quoi quil en soit la forme suivante serait rejeteacutee en compilation
point (point) incorrect
2 Les deux formes preacuteceacutedentes (point (point amp) et point (const point amp)) pourraient exis-
ter au sein dune mecircme classe Dans ce cas la premiegravere serait utiliseacutee en cas dinitialisa-
tion dun objet par un objet quelconque tandis que la seconde serait utiliseacutee en cas
dinitialisation par un objet constant En geacuteneacuteral comme un tel constructeur de recopie
na logiquement aucune raison de vouloir modifier lrsquoobjet reccedilu en argument il est con-
seilleacute de ne deacutefinir que la seconde forme qui restera ainsi applicable aux deux situa-
tions eacutevoqueacutees (une fonction preacutevue pour un objet constant peut toujours sappliquer agrave
un objet variable la reacuteciproque eacutetant naturellement fausse)
3 Nous avons deacutejagrave rencontreacute des situations de recopie dans le cas de laffectation Mais
alors les deux objets concerneacutes existaient deacutejagrave laffectation nest donc pas une situation
dinitialisation par recopie telle que nous venons de la deacutefinir Bien que les deux opeacutera-
tions possegravedent un traitement par deacutefaut semblable (copie superficielle) la prise en
compte dune copie profonde passe par des meacutecanismes diffeacuterents deacutefinition dun
constructeur de recopie pour lrsquoinitialisation surdeacutefinition de lopeacuterateur = pour laffec-
tation (ce que nous apprendrons agrave faire dans le chapitre consacreacute agrave la surdeacutefinition des
opeacuterateurs)
3 - Le constructeur de recopie121
4 Nous verrons que si une classe est destineacutee agrave donner naissance agrave des objets susceptibles
drsquoecirctre introduits dans des conteneurs il ne sera plus possible drsquoen deacutesactiver la reco-
pie (pas plus que lrsquoaffectation)
En Java
Les objets sont manipuleacutes non par valeur mais par reacutefeacuterence La notion de constructeur
de recopie nrsquoexiste pas En cas de besoin il reste possible de creacuteer explicitement une
copie profonde drsquoun objet nommeacutee clone
32 Exemple 1 objet transmis par valeurNous vous proposons de comparer les deux situations que nous venons deacutevoquer constructeur
de recopie par deacutefaut constructeur de recopie deacutefini dans la classe Pour ce faire nous allons
utiliser une classe vect permettant de geacuterer des tableaux dentiers de taille variable (on devrait
plutocirct dire de taille deacutefinissable lors de lexeacutecution car une fois deacutefinie cette taille ne changera
plus) Nous souhaitons que lutilisateur de cette classe deacuteclare un tableau sous la forme
vect t (dim)
dim eacutetant une expression entiegravere repreacutesentant sa taille
Il paraicirct alors naturel de preacutevoir pour vect
bull comme membres donneacutees la taille du tableau et un pointeur sur ses eacuteleacutements lesquels ver-
ront leurs emplacements alloueacutes dynamiquement
bull un constructeur recevant un argument entier chargeacute de cette allocation dynamique
bull un destructeur libeacuterant lemplacement alloueacute par le constructeur
Cela nous conduit agrave une premiegravere eacutebauche
class vect int nelem double adr public vect (int n) ~vect ( )
321 Emploi du constructeur de recopie par deacutefaut
Voici un exemple dutilisation de la classe vect preacuteceacutedente (nous avons ajouteacute des affichages
de messages pour suivre agrave la trace les constructions et destructions dobjets) Ici nous nous
contentons de transmettre par valeur un objet de type vect agrave une fonction ordinaire nommeacutee
fct qui ne fait rien dautre que dafficher un message indiquant son appel
include ltiostreamgtusing namespace std
Construction destruction et initialisation des objetsCHAPITRE 7
122
class vect
int nelem nombre deacuteleacutements
double adr pointeur sur ces eacuteleacutements
public
vect (int n) constructeur usuel
adr = new double [nelem = n]
cout ltlt + const usuel - adr objet ltlt this
ltlt - adr vecteur ltlt adr ltlt n
~vect () destructeur
cout ltlt - Destr objet - adr objet
ltlt this ltlt - adr vecteur ltlt adr ltlt n
delete adr
void fct (vect b)
cout ltlt appel de fct n
main()
vect a(5)
fct (a)
+ const usuel - adr objet 006AFDE4 - adr vecteur 007D0320
appel de fct
- Destr objet - adr objet 006AFD90 - adr vecteur 007D0320
- Destr objet - adr objet 006AFDE4 - adr vecteur 007D0320
Lorsquaucun constructeur de recopie na eacuteteacute deacutefini
Comme vous pouvez le constater lappel
fct (a)
a creacuteeacute un nouvel objet dans lequel on a recopieacute les valeurs des membres nelem et adr de a
La situation peut ecirctre scheacutematiseacutee ainsi (b est le nouvel objet ainsi creacuteeacute)
a
b
5
5
3 - Le constructeur de recopie123
A la fin de lexeacutecution de la fonction fct le destructeur ~point est appeleacute pour b ce qui libegravere
lemplacement pointeacute par adr agrave la fin de lrsquoeacutexeacutecution de la fonction main le destructeur est
appeleacute pour a ce qui libegravere le mecircme emplacement Cette tentative constitue une erreur
dexeacutecution dont les conseacutequences varient avec lrsquoimpleacutementation
322 Deacutefinition dun constructeur de recopie
On peut eacuteviter ce problegraveme en faisant en sorte que lappel
fct (a)
conduise agrave creacuteer inteacutegralement un nouvel objet de type vect avec ses membres donneacutees
nelem et adr mais aussi son propre emplacement de stockage des valeurs du tableau Autre-
ment dit nous souhaitons aboutir agrave cette situation
Pour ce faire nous deacutefinissons au sein de la classe vect un constructeur par recopie de la
forme
vect (const vect amp) ou a la rigueur vect (vect amp)
dont nous savons quil sera appeleacute dans toute situation dinitialisation donc en particulier
lors de lappel de fct
Ce constructeur (appeleacute apregraves la creacuteation dun nouvel objet1) doit
bull creacuteer dynamiquement un nouvel emplacement dans lequel il recopie les valeurs correspon-
dant agrave lrsquoobjet reccedilu en argument
bull renseigner convenablement les membres donneacutees du nouvel objet (nelem = valeur du mem-
bre nelem de lrsquoobjet reccedilu en argument adr = adresse du nouvel emplacement)
1 Notez bien que le constructeur na pas agrave creacuteer lrsquoobjet lui-mecircme cest-agrave-dire ici les membres int et adr mais
simplement les parties soumises agrave la gestion dynamique
a
b
5
5
Construction destruction et initialisation des objetsCHAPITRE 7
124
Introduisons ce constructeur de recopie dans lrsquoexemple preacuteceacutedent
include ltiostreamgt
using namespace std
class vect
int nelem nombre deacuteleacutements
double adr pointeur sur ces eacuteleacutements
public
vect (int n) constructeur usuel
adr = new double [nelem = n]
cout ltlt + const usuel - adr objet ltlt this
ltlt - adr vecteur ltlt adr ltlt n
vect (const vect amp v) constructeur de recopie
adr = new double [nelem = vnelem] creacuteation nouvel objet
int i for (i=0 iltnelem i++) adr[i]=vadr[i] recopie de lancien
cout ltlt + const recopie - adr objet ltlt this
ltlt - adr vecteur ltlt adr ltlt n
~vect () destructeur
cout ltlt - Destr objet - adr objet
ltlt this ltlt - adr vecteur ltlt adr ltlt n
delete adr
void fct (vect b)
cout ltlt appel de fct n
main()
vect a(5) fct (a)
+ const usuel - adr objet 006AFDE4 - adr vecteur 007D0320
+ const recopie - adr objet 006AFD88 - adr vecteur 007D0100
appel de fct
- Destr objet - adr objet 006AFD88 - adr vecteur 007D0100
- Destr objet - adr objet 006AFDE4 - adr vecteur 007D0320
Deacutefinition et utilisation dun constructeur de recopie
Vous constatez cette fois que chaque objet posseacutedant son propre emplacement meacutemoire les
destructions successives se deacuteroulent sans problegraveme
3 - Le constructeur de recopie125
Remarques
1 Si nous avons reacutegleacute le problegraveme de lrsquoinitialisation dun objet de type vect par un autre
objet du mecircme type nous navons pas pour autant reacutegleacute celui qui se poserait en cas
drsquoaffectation entre objets de type vect Comme nous lavons deacutejagrave signaleacute agrave plusieurs repri-
ses ce dernier point ne peut se reacutesoudre que par la surdeacutefinition de lopeacuterateur =
2 Nous avons choisi pour notre constructeur par recopie la deacutemarche la plus naturelle
consistant agrave effectuer une copie profonde en dupliquant la partie dynamique du vecteur
Dans certains cas on pourra chercher agrave eacuteviter cette duplication en la dotant drsquoun comp-
teur de reacutefeacuterences comme lrsquoexplique lrsquoAnnexe E
3 Si notre constructeur de recopie eacutetait deacuteclareacute priveacute lrsquoappel fct(a) entraicircnerait une erreur
de compilation preacutecisant qursquoun constructeur de recopie nrsquoest pas disponible Si le but
est de deacutefinir une classe dans laquelle la recopie est interdite il suffit alors de ne fournir
aucune deacutefinition On notera cependant qursquoil reste neacutecessaire de srsquoassurer qursquoaucune
fonction membre nrsquoaura besoin de ce constructeur ce qui serait par exemple le cas si
notre fonction membre f de la classe vect se preacutesentait ainsi
void f()
void fct (vect) deacuteclaration de la fonction ordinaire fct
vect v1(5)
fct (v1) appel de fct --gt appel contsructeur de recopie
vect v2 = v1 initialisation par appel constructeur de recopie
33 Exemple 2 objet en valeur de retour dune fonctionLorsque la transmission dun argument ou dune valeur de retour dune fonction a lieu par
valeur elle met en œuvre une recopie Lorsqursquoelle concerne un objet cette recopie est
comme nous lavons dit reacutealiseacutee soit par le constructeur de recopie par deacutefaut soit par le
constructeur de recopie preacutevu pour lobjet
Si un objet comporte une partie dynamique lemploi de la recopie par deacutefaut conduit agrave une
copie superficielle ne concernant que les membres de lobjet Les risques de double libeacutera-
tion dun emplacement meacutemoire sont alors les mecircmes que ceux eacutevoqueacutes au paragraphe 32
Mais pour la partie dynamique de lrsquoobjet on perd en outre le beacuteneacutefice de la protection contre
des modifications quoffre la transmission par valeur En effet dans ce cas la fonction con-
cerneacutee reccediloit bien une copie de ladresse de lemplacement mais par le biais de ce pointeur
elle peut tout agrave fait modifier le contenu de lemplacement lui-mecircme (revoyez le scheacutema du
paragraphe 321 dans lequel a jouait le rocircle dun argument et b celui de sa recopie)
Voici un exemple de programme faisant appel agrave une classe point doteacutee dune fonction mem-
bre nommeacutee symetrique fournissant en retour un point symeacutetrique de celui layant appeleacute
Notez bien quici contrairement agrave lexemple preacuteceacutedent le constructeur de recopie nest pas
Construction destruction et initialisation des objetsCHAPITRE 7
126
indispensable au bon fonctionnement de notre classe (qui ne comporte aucune partie
dynamique) il ne sert quagrave illustrer le meacutecanisme de son appel
include ltiostreamgt
using namespace std
class point
int x y
public
point (int abs=0 int ord=0) constructeur usuel
x=abs y=ord
cout ltlt ++ Appel Const usuel ltlt this ltlt ltlt x ltlt ltlt y ltlt n
point (const point amp p) constructeur de recopie
x=px y=py
cout ltlt ++ Appel Const recopie ltlt this ltlt ltlt x ltlt ltlt y ltlt n
~point ()
cout ltlt -- Appel Destr ltlt this ltlt ltlt x ltlt ltlt y ltlt n
point symetrique ()
point pointsymetrique ()
point res resx = -x resy = -y return res
main()
point a(13) b
cout ltlt avant appel de symetriquen
b = asymetrique ()
cout ltlt apres appel de symetriquen
++ Appel Const usuel 006AFDE4 1 3
++ Appel Const usuel 006AFDDC 0 0
avant appel de symetrique
++ Appel Const usuel 006AFD60 0 0
++ Appel Const recopie 006AFDD4 -1 -3
-- Appel Destr 006AFD60 -1 -3
-- Appel Destr 006AFDD4 -1 -3
apres appel de symetrique
-- Appel Destr 006AFDDC -1 -3
-- Appel Destr 006AFDE4 1 3
Appel du constructeur de recopie en cas de transmission par valeur
4 - Initialisation dun objet lors de sa deacuteclaration127
4 Initialisation dun objet lors de sa deacuteclarationNB Ce paragraphe peut ecirctre ignoreacute dans un premier temps
En langage C on peut initialiser une variable au moment de sa deacuteclaration comme dans
int n = 12
En theacuteorie C++ permet de faire de mecircme avec les objets en ajoutant un initialiseur lors de
leur deacuteclaration Mais si le rocircle dun tel initialiseur va de soi dans le cas de variables classi-
ques (il ne sagit que den fournir la ou les valeurs) il nen va plus de mecircme dans le cas dun
objet en effet il ne sagit plus de se contenter dinitialiser simplement ses membres mais
plutocirct de fournir sous une forme peu naturelle des arguments pour un constructeur De plus
C++ nimpose aucune restriction sur le type de linitialiseur qui pourra donc ecirctre du mecircme
type que lrsquoobjet initialiseacute le constructeur utiliseacute sera alors le constructeur de recopie preacute-
senteacute preacuteceacutedemment
Consideacuterons dabord cette classe (munie dun constructeur usuel)
class point
int x y
public
point (int abs) x = abs y = 0
Nous avons deacutejagrave vu quel serait le rocircle dune deacuteclaration telle que
point a(3)
C++ nous autorise eacutegalement agrave eacutecrire
point a = 3
Cette deacuteclaration entraicircne
bull la creacuteation dun objet a
bull lappel du constructeur auquel on transmet en argument la valeur de linitialiseur ici 3
En deacutefinitive les deux deacuteclarations
point a(3)
point a = 3
sont eacutequivalentes
Dune maniegravere geacuteneacuterale lorsque lon deacuteclare un objet avec un initialiseur ce dernier peut
ecirctre une expression dun type quelconque agrave condition quil existe un constructeur agrave un seul
argument de ce type
Cela sapplique donc aussi agrave une situation telle que
point a
point b = a on initialise b avec lrsquoobjet a de mecircme type
Manifestement on aurait obtenu le mecircme reacutesultat en deacuteclarant
point b(a) on creacutee lobjet b en utilisant le constructeur par recopie
de la classe point auquel on transmet lobjet a
Construction destruction et initialisation des objetsCHAPITRE 7
128
Quoi quil en soit ces deux deacuteclarations (point b=a et point b(a)) entraicircnent effectivement la
creacuteation dun objet de type point suivie de lappel du constructeur par recopie de point (celui
par deacutefaut ou le cas eacutecheacuteant celui quon y a deacutefini) auquel on transmet en argument
lobjet a
Remarques
1 Il ne faut pas confondre linitialiseur dune classe avec celui employeacute en C pour donner
des valeurs initiales agrave un tableau
int t[5] = 3 5 11 2 0
ou agrave une structure Celui-ci est toujours utilisable en C++ y compris pour les structures
comportant des fonctions membres Il est mecircme applicable agrave des classes ne disposant
pas de constructeur et dans lesquelles tous les membres sont publics en pratique cette
possibiliteacute ne preacutesente guegravere dinteacuterecirct
2 Supposons quune classe point soit munie dun constructeur agrave deux arguments entiers et
consideacuterons la deacuteclaration
point a = point (1 5)
Il sagit bien dune deacuteclaration comportant un initialiseur constitueacute dune expression de
type point On pourrait logiquement penser quelle entraicircne lappel dun constructeur de
recopie (par deacutefaut ou effectif) en vue dinitialiser lobjet a nouvellement creacuteeacute avec
lrsquoexpression temporaire point (15)
En fait dans ce cas preacutecis dinitialisation dun objet par appel explicite du constructeur
C++ a preacutevu de traiter cette deacuteclaration comme
point a(1 5)
Autrement dit il y a creacuteation dun seul objet a et appel du constructeur (usuel) pour
cet objet Aucun constructeur de recopie nest appeleacute
Cette deacutemarche est assez naturelle et simplificatrice Elle nen demeure pas moins une
exception par opposition agrave celle qui sera mise en œuvre dans
point a = b
ou dans
point a = b + point (1 5)
lorsque nous aurons appris agrave donner un sens agrave une expression telle que b + point (1 5)
(qui suppose la surdeacutefinition de lopeacuterateur + pour la classe point)
5 - Objets membres129
5 Objets membres
51 IntroductionIl est tout agrave fait possible quune classe possegravede un membre donneacutee lui-mecircme de type classe
Par exemple ayant deacutefini
class point
int x y
public
int init (int int)
void affiche ( )
nous pouvons deacutefinir
class cercle
point centre
int rayon
public
void affrayon ( )
Si nous deacuteclarons alors
cercle c
lobjet c possegravede un membre donneacutee priveacute centre de type point Lobjet c peut acceacuteder classi-
quement agrave la meacutethode affrayon par caffrayon En revanche il ne pourra pas acceacuteder agrave la
meacutethode init du membre centre car centre est priveacute Si centre eacutetait public on pourrait acceacuteder
aux meacutethodes de centre par ccentreinit () ou ccentreaffiche ()
Dune maniegravere geacuteneacuterale la situation dobjets membres correspond agrave une relation entre classes
du type relation de possession (on dit aussi relation a ndash du verbe avoir) Effectivement on
peut bien dire ici quun cercle possegravede (a) un centre (de type point) Ce type de relation
soppose agrave la relation qui sera induite par lrsquoheacuteritage de type relation est (du verbe ecirctre)
Voyons maintenant comment sont mis en œuvre les constructeurs des diffeacuterents objets
lorsquils existent
52 Mise en œuvre des constructeurs et des destructeursSupposons cette fois que notre classe point ait eacuteteacute deacutefinie avec un constructeur
class point
int x y
public
point (int int)
Construction destruction et initialisation des objetsCHAPITRE 7
130
Nous ne pouvons plus deacutefinir la classe cercle preacuteceacutedente sans constructeur En effet si nous
le faisions son membre centre se verrait certes attribuer un emplacement (lors dune creacuteation
dun objet de type cercle) mais son constructeur ne pourrait ecirctre appeleacute (quelles valeurs
pourrait-on lui transmettre )
Il faut donc
bull dune part deacutefinir un constructeur pour cercle
bull dautre part speacutecifier les arguments agrave fournir au constructeur de point ceux-ci doivent ecirctre
choisis obligatoirement parmi ceux fournis agrave cercle
Voici ce que pourrait ecirctre la deacutefinition de cercle et de son constructeur
class cercle point centre int rayon public cercle (int int int) cerclecercle (int abs int ord int ray) centre (abs ord)
Vous voyez que len-tecircte de cercle speacutecifie apregraves les deux-points la liste des arguments qui
seront transmis agrave point
Les constructeurs seront appeleacutes dans lordre suivant point cercle Sil existe des destruc-
teurs ils seront appeleacutes dans lordre inverse
Voici un exemple complet
include ltiostreamgtusing namespace std class point int x y public point (int abs=0 int ord=0) x=abs y=ord cout ltlt Constr point ltlt x ltlt ltlt y ltlt n class cercle point centre int rayon public cercle (int int int) cerclecercle (int abs int ord int ray) centre(abs ord) rayon = ray cout ltlt Constr cercle ltlt rayon ltlt n main() cercle a (139)
5 - Objets membres131
Constr point 1 3Constr cercle 9
Appel des diffeacuterents constructeurs dans le cas dobjets membres
Remarques
1 Si point dispose dun constructeur sans argument le constructeur de cercle peut ne pas
speacutecifier dargument agrave destination du constructeur de centre qui sera appeleacute automatique-
ment
2 On pourrait eacutecrire ainsi le constructeur de cercle
cerclecercle (int abs int ord int ray) rayon = ray centre = point (abs ord) cout ltlt Constr cercle ltlt rayon ltlt n
Mais dans ce cas on creacuteerait un objet temporaire de type point suppleacutementaire comme
le montre lrsquoexeacutecution du mecircme programme ainsi modifieacute
Constr point 0 0Constr point 1 3Constr cercle 9
3 Dans le cas dobjets comportant plusieurs objets membres la seacutelection des arguments
destineacutes aux diffeacuterents constructeurs se fait en seacuteparant chaque liste par une virgule En
voici un exemple
class A class B A (int) B (double int) class C A a1 B b A a2 C (int n int p double x int q int r) a1(p) b(xq) a2(r)
Ici pour simplifier leacutecriture nous avons supposeacute que le constructeur de C eacutetait en
ligne Parmi les arguments n p x q et r quil reccediloit p sera transmis au constructeur A
de a1 x et q au constructeur B de b puis r au constructeur A de a2 Notez bien que
lordre dans lequel ces trois constructeurs sont exeacutecuteacutes est en theacuteorie celui de leur
Construction destruction et initialisation des objetsCHAPITRE 7
132
deacuteclaration dans la classe et non pas celui des initialiseurs En pratique on eacutevitera des
situtations ougrave cet ordre pourrait avoir de lrsquoimportance
En revanche comme on peut srsquoy attendre le constructeur C ne sera exeacutecuteacute quapregraves les
trois autres (lordre des imbrications est toujours respecteacute)
53 Le constructeur de recopieNous avons vu que pour toute classe il est preacutevu un constructeur de recopie par deacutefaut qui
est appeleacute en labsence de constructeur de recopie effectif Son rocircle est simple dans le cas
dobjets ne comportant pas dobjets membres puisquil sagit alors de recopier les valeurs des
diffeacuterents membres donneacutees
Lorsque lobjet comporte des objets membres la recopie (par deacutefaut) se fait membre par
membre1 autrement dit si lun des membres est lui-mecircme un objet on le recopiera en appe-
lant son propre constructeur de recopie (qui pourra ecirctre soit un constructeur par deacutefaut
soit un constructeur deacutefini dans la classe correspondante)
Cela signifie que la construction par recopie (par deacutefaut) dun objet sera satisfaisante degraves lors
quil ne contient pas de pointeurs sur des parties dynamiques mecircme si certains de ses objets
membres en comportent (agrave condition quils soient quant agrave eux munis des constructeurs par
recopie approprieacutes)
En revanche si lobjet contient des pointeurs il faudra le munir dun constructeur de recopie
approprieacute Ce dernier devra alors prendre en charge linteacutegraliteacute de la recopie de lobjet
Cependant on pourra pour cela transmettre les informations neacutecessaire aux constructeurs par
recopie (par deacutefaut ou non) de certains de ses membres en utilisant la technique deacutecrite au
paragraphe 52
6 Initialisation de membres dans lrsquoen-tecircte drsquoun constructeur
La syntaxe que nous avons deacutecrite au paragraphe 52 pour transmettre des arguments agrave un
constructeur dun objet membre peut en fait sappliquer agrave nimporte quel membre mecircme sil
ne sagit pas dun objet Par exemple
class point int x y public point (int abs=0 int ord=0) x(abs) y(ord)
1 En anglais on parle de memberwise copy Avant la version 20 de C++ la copie se faisait bit agrave bit (bitwise copy)
ce qui neacutetait pas toujours satisfaisant
7 - Les tableaux drsquoobjets133
Lappel du constructeur point provoquera lrsquoinitialisation des membres x et y avec respective-
ment les valeurs abs et ord Son corps est vide ici puisquil ny a rien de plus agrave faire pour
remplacer notre constructeur classique
point (int abs=0 int ord=0) x=abs y=ord
Cette possibiliteacute peut devenir indispensable en cas
bull drsquoinitialisation dun membre donneacutee constant Par exemple avec cette classe
class truc const int n public truc ()
il nrsquoest pas possible de proceacuteder ainsi pour initialiser n dans le constructeur de truc
tructruc() n = 12 interdit n est constant
En revanche on pourra proceacuteder ainsi
tructruc() n(12)
bull drsquoinitialisation dun membre donneacutee qui est une reacutefeacuterence En effet on ne peut quinitialiser
une telle reacutefeacuterence jamais lui affecter une nouvelle valeur (revoyez eacuteventuellement le para-
graphe 35 du chapitre 4)
7 Les tableaux drsquoobjetsNB Ce paragraphe peut ecirctre ignoreacute dans un premier temps
En C++ un tableau peut posseacuteder des eacuteleacutements de nimporte quel type y compris de type
classe ce qui conduit alors agrave des tableaux dobjets Ce concept ne preacutesente pas de difficulteacutes
particuliegraveres au niveau des notations que nous allons nous contenter de rappeler agrave partir drsquoun
exemple En revanche il nous faudra preacuteciser certains points relatifs agrave lappel des construc-
teurs et aux initialiseurs
71 NotationsSoit une classe point sans constructeur deacutefinie par
class point int x y public void init (int int) void affiche ( )
Construction destruction et initialisation des objetsCHAPITRE 7
134
Nous pouvons deacuteclarer un tableau courbe de vingt objets de type point par
point courbe [20]
Si i est un entier la notation courbe[i] deacutesignera un objet de type point Linstruction
courbe[i]affiche ()
appellera le membre init pour le point courbe[i] (les prioriteacutes relatives des opeacuterateurs et []
permettent de saffranchir de parenthegraveses) De mecircme on pourra afficher tous les points par
for (i = 0 i lt 20 i++) courbe[i]affiche()
Remarque
Un tableau dobjets nest pas un objet Dans lesprit de la POO pure ce concept
nexiste pas puisquon ne manipule que des objets En revanche il reste toujours possible
de deacutefinir une classe dont un des membres est un tableau dobjets Ainsi nous pourrions
deacutefinir un type courbe par
class courbe point p[20]
Notez que la classe vector de la bibliothegraveque standard permettra de deacutefinir des tableaux
dynamiques (dont la taille pourra varier au fil de lrsquoexeacutecution) qui seront de vrais objets
En Java
Non seulement un tableau drsquoobjet est un objet mais mecircme un simple tableau drsquoeacuteleacutements
drsquoun type de base est aussi un objet
72 Constructeurs et initialiseursNous venons de voir la signification de la deacuteclaration
point courbe[20]
dans le cas ougrave point est une classe sans constructeur
Si la classe comporte un constructeur sans argument celui-ci sera appeleacute successivement
pour chacun des eacuteleacutements (de type point) du tableau courbe En revanche si aucun des cons-
tructeurs de point nest un constructeur sans argument la deacuteclaration preacuteceacutedente conduira agrave
une erreur de compilation Dans ce cas en effet C++ nest plus en mesure de garantir le pas-
sage par un constructeur degraves lors que la classe concerneacutee (point) en comporte au moins un
Il est cependant possible de compleacuteter une telle deacuteclaration par un initialiseur comportant une
liste de valeurs chaque valeur sera transmise agrave un constructeur approprieacute (les valeurs peu-
vent donc ecirctre de types quelconques eacuteventuellement diffeacuterents les uns des autres dans la
mesure ougrave il existe le constructeur correspondant) Pour les tableaux de classe automatique
les valeurs de linitialiseur peuvent ecirctre une expression quelconque (pour peu quelle soit cal-
7 - Les tableaux drsquoobjets135
culable au moment ougrave on en a besoin) En outre linitialiseur peut comporter moins de
valeurs que le tableau na deacuteleacutements1 Dans ce cas il y a appel du constructeur sans argument
(qui doit donc exister) pour les eacuteleacutements auxquels ne correspond aucune valeur
Voici un exemple illustrant ces possibiliteacutes (nous avons choisi un constructeur disposant
drsquoarguments par deacutefaut il remplace trois constructeurs agrave zeacutero un et deux arguments)
include ltiostreamgtusing namespace std class point int x y public point (int abs=0 int ord=0) constructeur (0 1 ou 2 arguments) x=abs y =ord cout ltlt ++ Constr point ltlt x ltlt ltlt y ltlt n ~point () cout ltlt -- Destr point ltlt x ltlt ltlt y ltlt n main() int n = 3 point courbe[5] = 7 n 2n+5 cout ltlt fin programme n
++ Constr point 7 0++ Constr point 3 0++ Constr point 11 0++ Constr point 0 0++ Constr point 0 0 fin programme -- Destr point 0 0-- Destr point 0 0-- Destr point 11 0-- Destr point 3 0-- Destr point 7 0
Construction et initialisation dun tableau dobjets (version 20)
73 Cas des tableaux dynamiques dobjetsSi lon dispose dune classe point on peut creacuteer dynamiquement un tableau de points en fai-
sant appel agrave lopeacuterateur new Par exemple
point adcourbe = new point[20]
1 Mais pour linstant les eacuteleacutements manquants doivent obligatoirement ecirctre les derniers
Construction destruction et initialisation des objetsCHAPITRE 7
136
alloue lemplacement meacutemoire neacutecessaire agrave vingt objets (conseacutecutifs) de type point et place
ladresse du premier de ces objets dans adcourbe
Lagrave encore si la classe point comporte un constructeur sans argument ce dernier sera appeleacute
pour chacun des vingt objets En revanche si aucun des constructeurs de point nest un cons-
tructeur sans argument linstruction preacuteceacutedente conduira agrave une erreur de compilation Bien
entendu aucun problegraveme particulier ne se posera si la classe point ne comporte aucun cons-
tructeur
Par contre il nexiste ici aucune possibiliteacute de fournir un initialiseur alors que cela est possi-
ble dans le cas de tableaux automatiques ou statiques (voir paragraphe 62)
Pour deacutetruire notre tableau dobjets il suffira de linstruction (notez la preacutesence des crochets
[] qui preacutecisent que lon a affaire agrave un tableau dobjets)
delete [] adcourbe
Celle-ci provoquera lappel du destructeur de point et la libeacuteration de lespace correspondant
pour chacun des eacuteleacutements du tableau
8 Les objets temporairesNB Ce paragraphe peut ecirctre ignoreacute dans un premier temps
Lorsquune classe dispose dun constructeur ce dernier peut ecirctre appeleacute explicitement (avec
la liste drsquoarguments neacutecessaires) Il y a alors creacuteation dun objet temporaire Par exemple si
nous supposons quune classe point possegravede le constructeur
point (int int)
nous pouvons si a est un objet de type point eacutecrire une affectation telle que
a = point (1 2)
Dans une telle instruction leacutevaluation de lexpression
point (1 2)
conduit agrave
bull la creacuteation dun objet temporaire de type point (il a une adresse preacutecise mais il nest pas ac-
cessible au programme)1
bull lappel du constructeur point pour cet objet temporaire avec transmission des arguments
speacutecifieacutes (ici 1 et 2)
bull la recopie de cet objet temporaire dans a (affectation dun objet agrave un autre de mecircme type)
Quant agrave lobjet temporaire ainsi creacuteeacute il na plus drsquointeacuterecirct degraves que linstruction daffectation est
exeacutecuteacutee La norme preacutevoit qursquoil soit deacutetruit degraves que possible
1 En fait il en va de mecircme lorsque lon reacutealise une affectation telle que y = a x + b Il y a bien creacuteation dun
emplacement temporaire destineacute agrave recueillir le reacutesultat de leacutevaluation de lexpression a x + b
8 - Les objets temporaires137
Voici un exemple de programme montrant lemploi dobjets temporaires Remarquez quici
nous avons preacutevu dans le constructeur et le destructeur de notre classe point dafficher non
seulement les valeurs de lobjet mais eacutegalement son adresse
include ltiostreamgtusing namespace std class point int x y public point (int abs int ord) constructeur (inline) x = abs y = ord cout ltlt ++ Constr point ltlt x ltlt ltlt y ltlt a ladresse ltlt this ltlt n ~point () destructeur (inline) cout ltlt -- Destr point ltlt x ltlt ltlt y ltlt a ladresse ltlt this ltlt n
main() point a(00) un objet automatique de classe point a = point (1 2) un objet temporaire a = point (3 5) un autre objet temporaire cout ltlt Fin main n
+ Constr point 0 0 a ladresse 006AFDE4+ Constr point 1 2 a ladresse 006AFDDC- Destr point 1 2 a ladresse 006AFDDC+ Constr point 3 5 a ladresse 006AFDD4- Destr point 3 5 a ladresse 006AFDD4 Fin main - Destr point 3 5 a ladresse 006AFDE4
Exemple de creacuteation dobjets temporaires
On voit clairement que les deux affectations de la fonction main entraicircnent la creacuteation dun
objet temporaire distinct de a qui se trouve deacutetruit tout de suite apregraves La derniegravere destruc-
tion reacutealiseacutee apregraves la fin de lrsquoexeacutecution concerne lrsquoobjet automatique a
Remarques
1 Reacutepeacutetons que dans une affectation telle que
a = point (1 2)
lobjet a existe deacutejagrave Il na donc pas agrave ecirctre creacuteeacute et il ny a pas dappel de constructeur agrave ce
niveau pour a
Construction destruction et initialisation des objetsCHAPITRE 7
138
2 Les remarques sur les risques que preacutesente une affectation entre objets notamment srsquoils
comportent des parties dynamiques1 restent valables ici On pourra utiliser la mecircme
solution agrave savoir la surdeacutefinition de lopeacuterateur daffectation
3 Il existe dautres circonstances dans lesquelles sont creacuteeacutes des objets temporaires agrave
savoir
ndash transmission de la valeur dun objet en argument dune fonction il y a creacuteation dun
objet temporaire au sein de la fonction concerneacutee
ndash transmission dun objet en valeur de retour dune fonction il y a creacuteation dun objet
temporaire au sein de la fonction appelante
Dans les deux cas lobjet temporaire est initialiseacute par appel du constructeur de recopie
4 La preacutesence dobjets temporaires (dont le moment de destruction nest pas parfaitement
imposeacute par la norme) peut rendre difficile le deacutenombrement exact dobjets dune classe
donneacutee
5 La norme ANSI autorise les compilateurs agrave supprimer certaines creacuteations drsquoobjets tem-
poraires notamment dans des situations telles que
f (point(12) appel drsquoune fonction attendant un point avec un argument qui est un objet temporaire lrsquoimpleacutementation peut ne pas creacuteer point(12) dans la fonction appelantereturn point(35) renvoi de la valeur drsquoun point lrsquoimpleacutementation peut ne pas creacuteer point(35) dans la fonction
ExercicesNB Les exercices marqueacutes (C) sont corrigeacutes en fin de volume
1 Comme le suggegravere la remarque du paragraphe 13 eacutecrivez une fonction main qui bien
que ne contenant que des deacuteclarations (voire une seule deacuteclaration) nen effectue pas
moins un certain traitement (par exemple affichage)
2 Expeacuterimentez le programme du paragraphe 2 pour voir comment sont traiteacutes les objets
temporaires dans votre impleacutementation
3 Cherchez agrave mettre en eacutevidence les problegravemes poseacutes par laffectation dobjets du type
vect tel quil est deacutefini dans lexemple du paragraphe 322
4 Ecrivez un programme permettant de mettre en eacutevidence lordre dappel des construc-
teurs et des destructeurs dans la situation du paragraphe 52 (objets membres) ainsi
que dans celle de la seconde remarque (objet comportant plusieurs objets membres)
Expeacuterimentez eacutegalement la situation dobjets membres dobjets membres
1 Nous incluons dans ce cas les objets dont un membre (lui-mecircme objet) comporte une partie dynamique
8 - Les objets temporaires139
5 (C) Ecrivez une classe nommeacutee pile_entier permettant de geacuterer une pile dentiers Ces der-
niers seront conserveacutes dans un tableau dentiers alloueacutes dynamiquement La classe
comportera les fonctions membres suivantes
bull pile_entier (int n) constructeur allouant dynamiquement un emplacement de n en-
tiers
bull pile_entier ( ) constructeur sans argument allouant par deacutefaut un emplacement de
vingt entiers
bull ~pile_entier ( ) destructeur
bull void empile (int p) ajoute lentier p sur la pile
bull int depile ( ) fournit la valeur de lentier situeacute en haut de la pile en le supprimant
de la pile
bull int pleine ( ) fournit 1 si la pile est pleine 0 sinon
bull int vide ( ) fournit 1 si la pile est vide 0 sinon
6 (C) Ecrivez une fonction main utilisant des objets automatiques et dynamiques du type
pile_entier deacutefini preacuteceacutedemment
7 Mettez en eacutevidence les problegravemes poseacutes par des deacuteclarations de la forme
pile_entier a(10) pile_entier b = a
8 (C) Ajoutez agrave la classe pile_entier le constructeur de recopie permettant de reacutegler les pro-
blegravemes preacuteceacutedents
8
Les fonctions amies
La POO pure impose lrsquoencapsulation des donneacutees Nous avons vu comment la mettre en
œuvre en C++ les membres priveacutes (donneacutees ou fonctions) ne sont accessibles quaux fonc-
tions membres (publiques ou priveacutees1) et seuls les membres publics sont accessibles de
lexteacuterieur
Nous avons aussi vu quen C++ luniteacute de protection est la classe cest-agrave-dire quune mecircme
fonction membre peut acceacuteder agrave tous les objets de sa classe Crsquoest ce qui se produisait dans la
fonction coincide (examen de la coiumlncidence de deux objets de type point) preacutesenteacutee au para-
graphe 4 du chapitre 6
En revanche ce mecircme principe dencapsulation interdit agrave une fonction membre dune classe
dacceacuteder agrave des donneacutees priveacutees dune autre classe Or cette contrainte savegravere gecircnante dans
certaines circonstances Supposez par exemple que vous ayez deacutefini une classe vecteur (de
taille fixe ou variable peu importe ) et une classe matrice Il est probable que vous souhaite-
rez alors deacutefinir une fonction permettant de calculer le produit dune matrice par un vecteur
Or avec ce que nous connaissons actuellement de C++ nous ne pourrions deacutefinir cette fonc-
tion ni comme fonction membre de la classe vecteur ni comme fonction membre de la classe
matrice et encore moins comme fonction indeacutependante (cest-agrave-dire membre daucune
classe)
Bien entendu vous pourriez toujours rendre publiques les donneacutees de vos deux classes mais
vous perdriez alors le beacuteneacutefice de leur protection Vous pourriez eacutegalement introduire dans
1 Le statut proteacutegeacute (protected) nrsquointervient qursquoen cas drsquoheacuteritage nous en parlerons au chapitre 13 Pour lrsquoinstant
vous pouvez consideacuterer que les membres proteacutegeacutes sont traiteacutes comme les membres priveacutes
Les fonctions amiesCHAPITRE 8
142
les deux classes des fonctions publiques permettant dacceacuteder aux donneacutees mais vous seriez
alors peacutenaliseacute en temps dexeacutecution
En fait la notion de fonction amie1 propose une solution inteacuteressante sous la forme dun
compromis entre encapsulation formelle des donneacutees priveacutees et des donneacutees publiques Lors
de la deacutefinition dune classe il est en effet possible de deacuteclarer quune ou plusieurs fonctions
(exteacuterieures agrave la classe) sont des amies une telle deacuteclaration damitieacute les autorise alors agrave
acceacuteder aux donneacutees priveacutees au mecircme titre que nimporte quelle fonction membre
Lavantage de cette meacutethode est de permettre le controcircle des accegraves au niveau de la classe
concerneacutee on ne peut pas simposer comme fonction amie dune classe si cela na pas eacuteteacute
preacutevu dans la classe Nous verrons toutefois quen pratique la protection est un peu moins
efficace quil ny paraicirct dans la mesure ougrave une fonction peut parfois se faire passer pour une
autre
Il existe plusieurs situations damitieacutes
bull fonction indeacutependante amie dune classe
bull fonction membre dune classe amie dune autre classe
bull fonction amie de plusieurs classes
bull toutes les fonctions membres dune classe amies dune autre classe
La premiegravere nous servira agrave preacutesenter les principes geacuteneacuteraux de deacuteclaration deacutefinition et utili-
sation dune fonction amie Nous examinerons ensuite en deacutetail chacune de ces situations
damitieacute Enfin nous verrons lincidence de lexistence de fonctions amies sur lexploitation
dune classe
1 Exemple de fonction indeacutependante amie drsquoune classe
Au paragraphe 4 du chapitre 6 nous avons introduit une fonction coincide examinant la
coiumlncidence de deux objets de type point pour ce faire nous en avons fait une fonction
membre de la classe point Nous vous proposons ici de reacutesoudre le mecircme problegraveme en fai-
sant cette fois de la fonction coincide une fonction indeacutependante amie de la classe point
Tout dabord il nous faut introduire dans la classe point la deacuteclaration damitieacute approprieacutee agrave
savoir
friend int coincide (point point)
Il sagit preacuteciseacutement du prototype de la fonction coincide preacuteceacutedeacute du mot cleacute friend Naturel-
lement nous avons preacutevu que coincide recevrait deux arguments de type point (cette fois il
1 Friend en anglais
1 - Exemple de fonction indeacutependante amie drsquoune classe143
ne sagit plus dune fonction membre elle ne recevra donc pas dargument implicite this cor-
respondant agrave lobjet layant appeleacute)
Leacutecriture de la fonction coincide ne pose aucun problegraveme particulier
Voici un exemple de programme
include ltiostreamgtusing namespace std class point int x y public point (int abs=0 int ord=0) un constructeur (inline) x=abs y=ord deacuteclaration fonction amie (indeacutependante) nommeacutee coincide friend int coincide (point point) int coincide (point p point q) deacutefinition de coincide if ((px == qx) ampamp (py == qy)) return 1 else return 0 main() programme dessai point a(10) b(1) c if (coincide (ab)) cout ltlt a coincide avec b n else cout ltlt a et b sont differents n if (coincide (ac)) cout ltlt a coincide avec c n else cout ltlt a et c sont differents n
a coincide avec b a et c sont differents
Exemple de fonction indeacutependante (coincide) amie de la classe point
Remarques
1 Lemplacement de la deacuteclaration damitieacute au sein de la classe point est absolument indiffeacute-
rent
2 Il nest pas neacutecessaire de deacuteclarer la fonction amie dans la fonction ou dans le fichier
source ougrave on lutilise car elle est deacutejagrave obligatoirement deacuteclareacutee dans la classe concer-
neacutee Cela reste valable dans le cas (usuel) ougrave la classe a eacuteteacute compileacutee seacutepareacutement
puisquil faudra alors en introduire la deacuteclaration (geacuteneacuteralement par include) Neacutean-
moins une deacuteclaration superflue de la fonction amie ne constituerait pas une erreur
3 Comme nous lavons deacutejagrave fait remarquer nous navons plus ici dargument implicite
(this) Ainsi contrairement agrave ce qui se produisait au paragraphe 4 du chapitre 6 notre
fonction coincide est maintenant parfaitement symeacutetrique Nous retrouverons le mecircme
Les fonctions amiesCHAPITRE 8
144
pheacutenomegravene lorsque pour surdeacutefinir un opeacuterateur binaire nous pourrons choisir entre
une fonction membre (dissymeacutetrique) ou une fonction amie (symeacutetrique)
4 Ici les deux arguments de coincide sont transmis par valeur Ils pourraient lecirctre par
reacutefeacuterence notez que dans le cas dune fonction membre lobjet appelant la fonction
est doffice transmis par reacutefeacuterence (sous la forme de this)
5 Geacuteneacuteralement une fonction amie dune classe posseacutedera un ou plusieurs arguments ou
une valeur de retour du type de cette classe (cest ce qui justifiera son besoin daccegraves
aux membres priveacutes des objets correspondants) Ce nest toutefois pas une obligation1
on pourrait imaginer une fonction ayant besoin dacceacuteder aux membres priveacutes dobjets
locaux agrave cette fonction
6 Lorsquune fonction amie dune classe fournit une valeur de retour du type de cette
classe il est freacutequent que cette valeur soit celle dun objet local agrave la fonction Il est alors
impeacuteratif que sa transmission ait lieu par valeur dans le cas dune transmission par
reacutefeacuterence (ou par adresse) la fonction appelante recevrait ladresse dun emplacement
meacutemoire qui aurait eacuteteacute libeacutereacute agrave la sortie de la fonction Ce pheacutenomegravene a deacutejagrave eacuteteacute eacutevoqueacute
au paragraphe 6 du chapitre 6
2 Les diffeacuterentes situations drsquoamitieacuteNous venons dexaminer le cas dune fonction indeacutependante amie dune classe Celle-ci peut
ecirctre reacutesumeacutee par le scheacutema suivant
class point
int coincide (point point )
partie priveacutee on a accegraves ici aux membres pri-
veacutes de tout objet de type point
partie publique
friend int coincide (point point)
Fonction indeacutependante (coincide) amie drsquoune classe (point)
Bien que nous layons placeacutee ici dans la partie publique de point nous vous rappelons que la
deacuteclaration damitieacute peut figurer nimporte ougrave dans la classe
Dautres situations damitieacute sont possibles fondeacutees sur le mecircme principe elles peuvent con-
duire agrave des deacuteclarations damitieacute tregraves leacutegegraverement diffeacuterentes Nous allons maintenant les pas-
ser en revue
1 Mais ce sera obligatoire dans le cas des opeacuterateurs surdeacutefinis
2 - Les diffeacuterentes situations drsquoamitieacute145
21 Fonction membre dune classe amie dune autre classeIl sagit un peu dun cas particulier de la situation preacuteceacutedente En fait il suffit simplement de
preacuteciser dans la deacuteclaration damitieacute la classe agrave laquelle appartient la fonction concerneacutee agrave
laide de lopeacuterateur de reacutesolution de porteacutee ()
Par exemple supposons que nous ayons agrave deacutefinir deux classes nommeacutees A et B et que nous
ayons besoin dans B dune fonction membre f de prototype
int f(char A)
Si comme il est probable f doit pouvoir acceacuteder aux membres priveacutes de A elle sera deacuteclareacutee
amie au sein de la classe par
friend int Bf(char A)
Voici un scheacutema reacutecapitulatif de la situation
class A class B partie priveacutee int f (char A)
partie publique friend int Bf (char A) int Bf (char A )
on a accegraves ici aux membres priveacutes de tout objet de type A
Fonction (f) drsquoune classe (B) amie drsquoune autre classe (A)
Remarques
1 Pour compiler convenablement les deacuteclarations dune classe A contenant une deacuteclaration
damitieacute telle que
friend int Bf(char A)
le compilateur a besoin de connaicirctre les caracteacuteristiques de B cela signifie que la
deacuteclaration de B (mais pas neacutecessairement la deacutefinition de ses fonctions membres)
devra avoir eacuteteacute compileacutee avant celle de A
En revanche pour compiler convenablement la deacuteclaration
int f(char A)
figurant au sein de la classe B le compilateur na pas besoin de connaicirctre preacuteciseacutement
les caracteacuteristiques de A Il lui suffit de savoir quil sagit dune classe Comme dapregraves
ce qui vient drsquoecirctre dit la deacuteclaration de B na pu apparaicirctre avant on fournira linforma-
tion voulue au compilateur en faisant preacuteceacuteder la deacuteclaration de A de
class A
Les fonctions amiesCHAPITRE 8
146
Bien entendu la compilation de la deacutefinition de la fonction f neacutecessite (en geacuteneacuteral1) la
connaissance des caracteacuteristiques des classes A et B leurs deacuteclarations devront donc
apparaicirctre avant
A titre indicatif voici une faccedilon de compiler nos deux classes A et B et la fonction f
class A class B int f(char A) class A friend int Bf(char A) int Bf(char A)
2 Si lon a besoin de deacuteclarations damitieacutes croiseacutees entre fonctions de deux classes dif-
feacuterentes la seule faccedilon dy parvenir consiste agrave deacuteclarer au moins une des classes amie
de lautre (comme nous apprendrons agrave le faire au paragraphe 23)
22 Fonction amie de plusieurs classesRien nempecircche quune mecircme fonction (quelle soit indeacutependante ou fonction membre) fasse
lobjet de deacuteclarations damitieacute dans diffeacuterentes classes Voici un exemple dune fonction
amie de deux classes A et B
class A class B
partie priveacutee partie priveacutee
partie publique partie publique
friend void f(A B) friend void f(A B)
void f(A B)
on a accegraves ici aux membres priveacutes
de nrsquoimporte quel objet de type A ou B
Fonction indeacutependante (f) amie de deux classes (A et B)
1 Une exception aurait lieu pour B si f nacceacutedait agrave aucun de ses membres (ce qui serait surprenant) Il en irait de
mecircme pour A si aucun argument de ce type napparaissait dans f et si cette derniegravere nacceacutedait agrave aucun membre de A
(ce qui serait tout aussi surprenant)
3 - Exemple147
Remarque
Ici la deacuteclaration de A peut ecirctre compileacutee sans celle de B en la faisant preacuteceacuteder de la
deacuteclaration
class B
De mecircme la deacuteclaration de B peut ecirctre compileacutee sans celle de A en la faisant preacuteceacuteder
de la deacuteclaration
class A
Si lon compile en mecircme temps les deux deacuteclarations de A et B il faudra utiliser lune
des deux deacuteclarations citeacutees (class A si B figure avant A class B sinon)
Bien entendu la compilation de la deacutefinition de f neacutecessitera geacuteneacuteralement les deacuteclara-
tions de A et de B
23 Toutes les fonctions dune classe amies dune autre classeCest une geacuteneacuteralisation du cas eacutevoqueacute au paragraphe 21 On pourrait dailleurs effectuer
autant de deacuteclarations damitieacute quil y a de fonctions concerneacutees Mais il est plus simple
deffectuer une deacuteclaration globale Ainsi pour dire que toutes les fonctions membres de la
classe B sont amies de la classe A on placera dans la classe A la deacuteclaration
friend class B
Remarques
1 Cette fois pour compiler la deacuteclaration de la classe A il suffira de la faire preacuteceacuteder de
class B
2 Ce type de deacuteclaration damitieacute eacutevite de fournir les en-tecirctes des fonctions concerneacutees
3 ExempleNous vous proposons ici de reacutesoudre le problegraveme eacutevoqueacute en introduction agrave savoir reacutealiser
une fonction permettant de deacuteterminer le produit dun vecteur (objet de classe vect) par une
matrice (objet de classe matrice) Par souci de simpliciteacute nous avons limiteacute les fonctions
membres agrave
bull un constructeur pour vect et pour matrice
bull une fonction daffichage (affiche) pour matrice
Nous vous fournissons deux solutions fondeacutees sur lemploi dune fonction amie nommeacutee
prod
bull prod est indeacutependante et amie des deux classes vect et matrice
bull prod est membre de matrice et amie de la classe vect
Les fonctions amiesCHAPITRE 8
148
31 Fonction amie indeacutependante
include ltiostreamgtusing namespace std class matrice pour pouvoir compiler la deacuteclaration de vect La classe vect class vect double v[3] vecteur agrave 3 composantes public vect (double v1=0 double v2=0 double v3=0) constructeur v[0] = v1 v[1]=v2 v[2]=v3 friend vect prod (matrice vect) prod = fonction amie indeacutependante void affiche () int i for (i=0 ilt3 i++) cout ltlt v[i] ltlt cout ltlt n La classe matrice class matrice double mat[3] [3] matrice 3 X 3 public matrice (double t[3][3]) constructeur agrave partir dun tableau 3 x 3 int i int j for (i=0 ilt3 i++) for (j=0 jlt3 j++) mat[i] [j] = t[i] [j] friend vect prod (matrice vect) prod = fonction amie indeacutependante La fonction prod vect prod (matrice m vect x) int i j double som vect res pour le reacutesultat du produit for (i=0 ilt3 i++) for (j=0 som=0 jlt3 j++) som += mmat[i] [j] xv[j] resv[i] = som return res Un petit programme de test main() vect w (123) vect res double tb [3][3] = 1 2 3 4 5 6 7 8 9 matrice a = tb res = prod(a w) resaffiche ()
3 - Exemple149
14 32 50
Produit dune matrice par un vecteur agrave laide dune fonction indeacutependante amie des deux classes
32 Fonction amie membre dune classe
include ltiostreamgtusing namespace std Deacuteclaration de la classe matrice class vect pour pouvoir compiler correctementclass matrice double mat[3] [3] matrice 3 X 3 public matrice (double t[3][3]) constructeur agrave partir dun tableau 3 x 3 int i int j for (i=0 ilt3 i++) for (j=0 jlt3 j++) mat[i] [j] = t[i] [j] vect prod (vect) prod = fonction membre (cette fois) Deacuteclaration de la classe vect class vect double v[3] vecteur agrave 3 composantes public vect (double v1=0 double v2=0 double v3=0) constructeur v[0] = v1 v[1]=v2 v[2]=v3 friend vect matriceprod (vect) prod = fonction amie void affiche () int i for (i=0 ilt3 i++) cout ltlt v[i] ltlt cout ltlt n Deacutefinition de la fonction prod vect matriceprod (vect x) int i j double som vect res pour le reacutesultat du produit for (i=0 ilt3 i++) for (j=0 som=0 jlt3 j++) som += mat[i] [j] xv[j] resv[i] = som return res
Les fonctions amiesCHAPITRE 8
150
Un petit programme de test main() vect w (123) vect res double tb [3][3] = 1 2 3 4 5 6 7 8 9 matrice a = tb res = aprod (w) resaffiche ()
14 32 50
Produit dune matrice par un vecteur agrave laide dune fonction membre amie dune autre classe
4 Exploitation de classes disposant de fonctions amies
Comme nous lavons deacutejagrave mentionneacute au chapitre 5 les classes seront geacuteneacuteralement compileacutees
seacutepareacutement Leur utilisation se fera agrave partir dun module objet contenant leurs fonctions
membres et dun fichier en-tecircte contenant leur deacuteclaration Bien entendu il est toujours possi-
ble de regrouper plusieurs classes dans un mecircme module objet et eacuteventuellement dans un
mecircme fichier en-tecircte
Dans tous les cas cette compilation seacutepareacutee des classes permet den assurer la reacuteutilisabiliteacute
le client (qui peut eacuteventuellement ecirctre le concepteur de la classe) ne peut pas intervenir sur
le contenu des objets de cette classe
Que deviennent ces possibiliteacutes lorsque lon utilise des fonctions amies En fait sil sagit de
fonctions amies membres dune classe rien nest changeacute (en dehors des eacuteventuelles deacuteclara-
tions de classes neacutecessaires agrave son emploi) En revanche sil sagit dune fonction indeacutepen-
dante il faudra bien voir que si lon souhaite en faire un module objet seacutepareacute on court le
risque de voir lutilisateur de la classe violer le principe dencapsulation
En effet dans ce cas lutilisateur dune classe disposant dune fonction amie peut toujours ne
pas incorporer la fonction amie agrave leacutedition de liens et fournir lui-mecircme une autre fonction de
mecircme en-tecircte puis acceacuteder comme il lentend aux donneacutees priveacutees
Ce risque drsquoeffet cameacuteleacuteon doit ecirctre nuanceacute par le fait quil sagit dune action deacutelibeacutereacutee
(demandant un certain travail) et non pas dune simple eacutetourderie
9
La surdeacutefinition drsquoopeacuterateurs
Nous avons vu au chapitre 4 que C++ autorise la surdeacutefinition de fonctions quil sagisse de
fonctions membres ou de fonctions indeacutependantes Rappelons que cette technique consiste agrave
attribuer le mecircme nom agrave des fonctions diffeacuterentes lors dun appel le choix de la bonne
fonction est effectueacute par le compilateur suivant le nombre et le type des arguments
Mais C++ permet eacutegalement dans certaines conditions de surdeacutefinir des opeacuterateurs En fait
le langage C comme beaucoup dautres reacutealise deacutejagrave la surdeacutefinition de certains opeacuterateurs
Par exemple dans une expression telle que
a + b
le symbole + peut deacutesigner suivant le type de a et b
bull laddition de deux entiers
bull lrsquoaddition de deux reacuteels (float)
bull lrsquoaddition de deux reacuteels double preacutecision (double)
bull etc
De la mecircme maniegravere le symbole peut suivant le contexte repreacutesenter la multiplication
dentiers ou de reacuteels ou une indirection (comme dans a = adr)
En C++ vous pourrez surdeacutefinir nimporte quel opeacuterateur existant (unaire ou binaire) pour
peu qursquoil porte sur au moins un objet1 Il sagit lagrave dune technique fort puissante puisquelle va
1 Cette restriction signifie simplement quil ne sera pas possible de surdeacutefinir les opeacuterateurs portant sur les diffeacuterents
types de base
La surdeacutefinition drsquoopeacuterateursCHAPITRE 9
152
vous permettre de creacuteer par le biais des classes des types agrave part entiegravere cest-agrave-dire munis
comme les types de base dopeacuterateurs parfaitement inteacutegreacutes La notation opeacuteratoire qui en
deacutecoulera aura lavantage drsquoecirctre beaucoup plus concise et (du moins si lon sy prend
intelligemment ) lisible qursquoune notation fonctionnelle (par appel de fonction)
Par exemple si vous deacutefinissez une classe complexe destineacutee agrave repreacutesenter des nombres com-
plexes il vous sera possible de donner une signification agrave des expressions telles que
a + b a - b a b ab
a et b eacutetant des objets de type complexe1 Pour cela vous surdeacutefinirez les opeacuterateurs + - et
en speacutecifiant le rocircle exact que vous souhaitez leur attribuer Cette deacutefinition se deacuteroulera
comme celle dune fonction agrave laquelle il suffira simplement dattribuer un nom speacutecial per-
mettant de speacutecifier quil sagit en fait dun opeacuterateur Autrement dit la surdeacutefinition dopeacutera-
teurs en C++ consistera simplement en leacutecriture de nouvelles fonctions surdeacutefinies
Apregraves vous avoir preacutesenteacute la surdeacutefinition dopeacuterateurs ses possibiliteacutes et ses limites nous
lrsquoappliquerons aux opeacuterateurs = et [] Certes il ne sagira que dexemples mais ils montreront
quagrave partir du moment ougrave lon souhaite donner agrave ces opeacuterateurs une signification naturelle et
acceptable dans un contexte de classe un certain nombre de preacutecautions doivent ecirctre prises
En particulier nous verrons comment la surdeacutefinition de laffectation permet de reacutegler le pro-
blegraveme deacutejagrave rencontreacute agrave savoir celui des objets comportant des pointeurs sur des emplace-
ments dynamiques
Enfin nous examinerons comment prendre en charge la gestion de la meacutemoire en surdeacutefinis-
sant les opeacuterateurs new et delete
1 Le meacutecanisme de la surdeacutefinition drsquoopeacuterateurs
Consideacuterons une classe point
class point int x y
et supposons que nous souhaitions deacutefinir lopeacuterateur + afin de donner une signification agrave une
expression telle que a + b lorsque a et b sont de type point Ici nous conviendrons que la
somme de deux points est un point dont les coordonneacutees sont la somme de leurs coordon-
neacutees2
1 Une notation fonctionnelle conduirait agrave des choses telles que somme (ab) ou asomme(b) suivant que lon utilise
une fonction amie ou une fonction membre
2 Nous aurions pu tout aussi bien prendre lexemple de la classe complexe eacutevoqueacutee en introduction Nous preacutefeacuterons
cependant choisir un exemple dans lequel la signification de lopeacuterateur na pas un caractegravere aussi eacutevident En effet
noubliez pas que nimporte quel symbole opeacuterateur peut se voir attribuer nimporte quelle signification
1 - Le meacutecanisme de la surdeacutefinition drsquoopeacuterateurs153
La convention adopteacutee par C++ pour surdeacutefinir cet opeacuterateur + consiste agrave deacutefinir une fonc-
tion de nom
operator +
Le mot cleacute operator est suivi de lopeacuterateur concerneacute (dans le cas preacutesent il ne serait pas obli-
gatoire de preacutevoir un espace car en C + sert de seacuteparateur)
Ici notre fonction operator + doit disposer de deux arguments de type point et fournir une
valeur de retour du mecircme type En ce qui concerne sa nature cette fonction peut agrave notre greacute
ecirctre une fonction membre de la classe concerneacutee ou une fonction indeacutependante dans ce der-
nier cas il sagira geacuteneacuteralement dune fonction amie car elle devra pouvoir acceacuteder aux
membres priveacutes de la classe
Examinons ici les deux solutions en commenccedilant par celle qui est la plus naturelle agrave
savoir la fonction amie
11 Surdeacutefinition dopeacuterateur avec une fonction amieLe prototype de notre fonction operator + sera
point operator + (point point)
Ses deux arguments correspondront aux opeacuterandes de lopeacuterateur + lorsquil sera appliqueacute agrave
des valeurs de type point
Le reste du travail est classique
bull deacuteclaration damitieacute au sein de la classe point
bull deacutefinition de la fonction
Voici un exemple de programme montrant la deacutefinition et lutilisation de notre opeacuterateur
daddition de points
include ltiostreamgtusing namespace std
class point int x y public point (int abs=0 int ord=0) x=abs y=ord constructeur friend point operator+ (point point) void affiche () cout ltlt coordonnees ltlt x ltlt ltlt y ltlt n
point operator + (point a point b) point p px = ax + bx py = ay + by return p
La surdeacutefinition drsquoopeacuterateursCHAPITRE 9
154
main() point a(12) aaffiche()
point b(25) baffiche()
point c c = a+b caffiche()
c = a+b+c caffiche()
coordonnees 1 2
coordonnees 2 5
coordonnees 3 7coordonnees 6 14
Surdeacutefinition de lopeacuterateur + pour des objets de type point en employant une fonction amie
Remarques
1 Une expression telle que a + b est en fait interpreacuteteacutee par le compilateur comme lappel
operator + (a b)
Bien que cela ne preacutesente guegravere dinteacuterecirct nous pourrions eacutecrire
c = operator + (a b)
au lieu de c = a + b
2 Une expression telle que a + b + c est eacutevalueacutee en tenant compte des regravegles de prioriteacute
et dassociativiteacute habituelles de lopeacuterateur + Nous reviendrons plus loin sur ce point
Pour linstant notez simplement que cette expression est eacutevalueacutee comme
(a + b) + c
cest-agrave-dire en utilisant la notation fonctionnelle
operator + (operator + (a b) c)
12 Surdeacutefinition dopeacuterateur avec une fonction membreCette fois le premier opeacuterande de notre opeacuterateur correspondant au premier argument de la
fonction operator + preacuteceacutedente va se trouver transmis implicitement ce sera lobjet ayant
appeleacute la fonction membre Par exemple une expression telle que a + b sera alors interpreacuteteacutee
par le compilateur comme
aoperator + (b)
Le prototype de notre fonction membre operator + sera donc
point operator + (point)
Voici comment lrsquoexemple preacuteceacutedent pourrait ecirctre adapteacute
1 - Le meacutecanisme de la surdeacutefinition drsquoopeacuterateurs155
include ltiostreamgtusing namespace std class point int x y public point (int abs=0 int ord=0) x=abs y=ord constructeur point operator + (point) void affiche () cout ltlt coordonnees ltlt x ltlt ltlt y ltlt n point pointoperator + (point a) point p px = x + ax py = y + ay return p main() point a(12) aaffiche() point b(25) baffiche() point c c = a+b caffiche() c = a+b+c caffiche()
coordonnees 1 2coordonnees 2 5coordonnees 3 7coordonnees 6 14
Surdeacutefinition de lopeacuterateur + pour des objets de type point en employant une fonction membre
Remarques
1 Cette fois la deacutefinition de la fonction operator + fait apparaicirctre une dissymeacutetrie entre les
deux opeacuterandes Par exemple le membre x est noteacute x pour le premier opeacuterande (argument
implicite) et ax pour le second Cette dissymeacutetrie peut parfois inciter lutilisateur agrave choisir
une fonction amie plutocirct quune fonction membre Il faut toutefois se garder de deacutecider
trop vite dans ce domaine Nous y reviendrons un peu plus loin
2 Ici laffectation
c = a + b
est interpreacuteteacutee comme
c = aoperator + (b)
Quant agrave laffectation
c = a + b + c
La surdeacutefinition drsquoopeacuterateursCHAPITRE 9
156
le langage C++ ne preacutecise pas exactement son interpreacutetation Certains compilateurs
creacuteeront un objet temporaire t
t = aoperator + (b) c = toperator + (c)
Dautres proceacutederont ainsi en transmettant comme adresse de lobjet appelant operator
+ celle de lobjet renvoyeacute par lappel preacuteceacutedent
c = (aoperator + (b))operator + (c)
On peut deacutetecter le choix fait par un compilateur en affichant toutes les creacuteations
dobjets (en noubliant pas dintroduire un constructeur de recopie prenant la place du
constructeur par deacutefaut)
13 Opeacuterateurs et transmission par reacutefeacuterenceDans les deux exemples preacuteceacutedents la transmission des arguments (deux pour une fonction
amie un pour une fonction membre) et de la valeur de retour de operator + se faisait par
valeur1
Bien entendu on peut envisager de faire appel au transfert par reacutefeacuterence en particulier dans
le cas dobjets de grande taille Par exemple le prototype de la fonction amie operator +
pourrait ecirctre
point operator + (point amp a point amp b)
En revanche la transmission par reacutefeacuterence poserait un problegraveme si on cherchait agrave lappliquer
agrave la valeur de retour En effet le point p est creacuteeacute localement dans la fonction il sera donc
deacutetruit degraves la fin de son exeacutecution Dans ces conditions employer la transmission par reacutefeacute-
rence reviendrait agrave transmettre ladresse dun emplacement de meacutemoire libeacutereacute
Certes nous utilisons ici immeacutediatement la valeur de p degraves le retour dans la fonction main
(ce qui est geacuteneacuteralement le cas avec un opeacuterateur) Neacuteanmoins nous ne pouvons faire aucune
hypothegravese sur la maniegravere dont une impleacutementation donneacutee libegravere un emplacement meacutemoire
elle peut simplement se contenter de noter quil est disponible auquel cas son contenu reste
valable pendant un certain temps elle peut au contraire le mettre agrave zeacutero La premiegravere
situation est certainement la pire puisquelle peut donner lillusion que cela marche
Pour eacuteviter la recopie de cette valeur de retour on pourrait songer agrave allouer dynamiquement
lemplacement de p Geacuteneacuteralement cela prendra plus de temps que sa recopie ulteacuterieure et de
plus compliquera quelque peu le programme (il faudra libeacuterer convenablement lemplace-
ment en question et on ne pourra le faire quen dehors de la fonction )
Si lon cherche agrave proteacuteger contre deacuteventuelles modifications un argument transmis par reacutefeacute-
rence on pourra toujours faire appel au mot cleacute const par exemple len-tecircte de operator +
pourrait ecirctre2
point operator + (const pointamp a const pointamp b)
1 Rappelons que la transmission de lobjet appelant une fonction membre se fait par reacutefeacuterence
2 - La surdeacutefinition drsquoopeacuterateurs en geacuteneacuteral157
Naturellement si lon utilise const dans le cas dobjets comportant des pointeurs sur des par-
ties dynamiques seuls ces pointeurs seront proteacutegeacutes les parties dynamiques resteront
modifiables
2 La surdeacutefinition drsquoopeacuterateurs en geacuteneacuteralNous venons de voir un exemple de surdeacutefinition de lopeacuterateur binaire + lorsquil reccediloit deux
opeacuterandes de type point et ce de deux faccedilons comme fonction amie comme fonction mem-
bre Examinons maintenant ce quil est possible de faire dune maniegravere geacuteneacuterale
21 Se limiter aux opeacuterateurs existantsLe symbole suivant le mot cleacute operator doit obligatoirement ecirctre un opeacuterateur deacutejagrave deacutefini
pour les types de base Il nest donc pas possible de creacuteer de nouveaux symboles Nous ver-
rons dailleurs que certains opeacuterateurs ne peuvent pas ecirctre redeacutefinis du tout (cest le cas de )
et que dautres imposent quelques contraintes suppleacutementaires
Il faut conserver la pluraliteacute (unaire binaire) de lopeacuterateur initial Ainsi vous pourrez surdeacute-
finir un opeacuterateur + unaire ou un opeacuterateur + binaire mais vous ne pourrez pas deacutefinir de =
unaire ou de ++ binaire
Lorsque plusieurs opeacuterateurs sont combineacutes au sein dune mecircme expression (quils soient sur-
deacutefinis ou non) ils conservent leur prioriteacute relative et leur associativiteacute Par exemple si vous
surdeacutefinissez les opeacuterateurs binaires + et pour le type complexe lexpression suivante (a b
et c eacutetant supposeacutes du type complexe)
a b + c
sera interpreacuteteacutee comme
(a b) + c
De telles regravegles peuvent vous paraicirctre restrictives En fait vous verrez agrave lusage quelles sont
encore tregraves larges et quil est facile de rendre un programme incompreacutehensible en abusant de
la surdeacutefinition dopeacuterateurs
Le tableau ci-apregraves preacutecise les opeacuterateurs surdeacutefinissables (en fait tous sauf et )
et rappelle leur prioriteacute relative et leur associativiteacute Notez la preacutesence
bull de lopeacuterateur de cast nous verrons au chapitre 10 quil peut sappliquer agrave la conversion
dune classe dans un type de base ou agrave la conversion dune classe dans une autre classe
bull des opeacuterateurs new et delete avant la version 20 ils ne pouvaient pas ecirctre surdeacutefinis pour
une classe particuliegravere on ne pouvait en modifier la signification que dune faccedilon globale
Depuis la version 20 ils sont surdeacutefinissables au mecircme titre que les autres Nous en parle-
rons au paragraphe 7
2 Cependant comme on le verra au chapitre 10 la preacutesence de cet attribut const pourra autoriser certaines
conversions de lrsquoargument
La surdeacutefinition drsquoopeacuterateursCHAPITRE 9
158
bull des opeacuterateurs -gt et introduits par la norme ils sont drsquoun usage restreint et ils srsquoappli-
quent aux pointeurs sur des membres Leur rocircle est deacutecrit en Annexe F
Les opeacuterateurs surdeacutefinissables en C++ (classeacutes par prioriteacute deacutecroissante)
(1) Srsquoil nrsquoest pas surdeacutefini il possegravede une signification par deacutefaut
(2) Depuis la version 20 seulement
(3) Doit ecirctre deacutefini comme fonction membre
(4) Soit agrave un niveau global (fonction indeacutependante) avant la version 20 Depuis la version 20 il peut en outre ecirctre
surdeacutefini pour une classe dans ce cas il doit lrsquoecirctre comme fonction membre
(5) Jusqursquoagrave la version 3 on ne pouvait pas distinguer entre les notations preacute et post Depuis la version 3 lorsqursquoils
sont deacutefinis de faccedilon unaire ces opeacuterateurs correspondent agrave la notation preacute mais il en existe une deacutefinition binaire
(avec deuxiegraveme opeacuterande fictif de type int) qui correspond agrave la notation post
(6) On distingue bien new de new[] et delete de delete[]
22 Se placer dans un contexte de classeOn ne peut surdeacutefinir un opeacuterateur que sil comporte au moins un argument (implicite ou
non) de type classe Autrement dit il doit sagir
Pluraliteacute Opeacuterateurs Associativiteacute
Binaire ()(3) [](3) -gt(1) (2) (3) -gt
Unaire + - ++(5) --(5) ~ amp(1)
new(1)(4)(6) new[](1)(4)(6) delete(1)(4)(6) delete[](1)(4)(6)
(cast)
lt-
Binaire -gt
Binaire -gt(1) (1) -gt
Binaire + - -gt
Binaire ltlt gtgt -gt
Binaire lt lt= gt gt= -gt
Binaire == = -gt
Binaire amp -gt
Binaire ^ -gt
Binaire || -gt
Binaire ampamp -gt
Binaire | -gt
Binaire =(1)(3) += -= = = =
amp= ^= |= ltlt= gtgt=
lt-
Binaire (2) -gt
2 - La surdeacutefinition drsquoopeacuterateurs en geacuteneacuteral159
bull Soit dune fonction membre dans ce cas elle comporte agrave coup sucircr un argument (implicite)
de type classe agrave savoir lobjet layant appeleacute Sil sagit dun opeacuterateur unaire elle ne com-
portera aucun argument explicite Sil sagit dun opeacuterateur binaire elle comportera un argu-
ment explicite auquel aucune contrainte de type nest imposeacutee (dans les exemples
preacuteceacutedents il sagissait du mecircme type que la classe elle-mecircme mais il pourrait sagir dun
autre type classe ou mecircme dun type de base)
bull Soit dune fonction indeacutependante ayant au moins un argument de type classe En geacuteneacuteral il
sagira dune fonction amie
Cette regravegle garantit limpossibiliteacute de surdeacutefinir un opeacuterateur portant sur des types de base
(imaginez ce que serait un programme dans lequel on pourrait changer la signification de
3 + 5 ou de adr ) Une exception a lieu cependant pour les seuls opeacuterateurs new et delete
dont la signification peut ecirctre modifieacutee de maniegravere globale (pour tous les objets et les types
de base) nous en reparlerons au paragraphe 7
De plus certains opeacuterateurs doivent obligatoirement ecirctre deacutefinis comme membres dune
classe Il sagit de [] ( ) -gt1 ainsi que de new et delete (dans le seul cas ougrave ils portent sur une
classe particuliegravere)
23 Eviter les hypothegraveses sur le rocircle drsquoun opeacuterateurComme nous avons deacutejagrave eu loccasion de lindiquer vous ecirctes totalement libre dattribuer agrave un
opeacuterateur surdeacutefini la signification que vous deacutesirez Cette liberteacute nest limiteacutee que par le bon
sens qui doit vous inciter agrave donner agrave un symbole une signification relativement naturelle
par exemple + pour la somme de deux complexes plutocirct que - ou []
Cela dit vous ne retrouverez pas pour les opeacuterateurs surdeacutefinis les liens qui existent entre
certains opeacuterateurs de base Par exemple si a et b sont de type int
a += b
est eacutequivalent agrave
a = a + b
Autrement dit le rocircle de lopeacuterateur de base += se deacuteduit du rocircle de lopeacuterateur + et de celui
de lopeacuterateur = En revanche si vous surdeacutefinissez lopeacuterateur + et lopeacuterateur = lorsque leurs
deux opeacuterandes sont de type complexe vous naurez pas pour autant deacutefini la signification de
+= lorsquil aura deux opeacuterandes de type complexe De plus vous pourrez tregraves bien surdeacutefinir
+= pour quil ait une signification diffeacuterente de celle attendue naturellement cela nest pas
conseilleacute
De mecircme et de faccedilon peut-ecirctre plus surprenante C++ ne fait aucune hypothegravese sur la com-
mutativiteacute eacuteventuelle dun opeacuterateur surdeacutefini (contrairement agrave ce qui se passe pour sa prio-
riteacute relative ou son associativiteacute) Cette remarque est lourde de conseacutequences Supposez par
1 Il nest surdeacutefinissable que depuis la version 20
La surdeacutefinition drsquoopeacuterateursCHAPITRE 9
160
exemple que vous ayez surdeacutefini lopeacuterateur + lorsquil a comme opeacuterandes un complexe et
un double (dans cet ordre) son prototype pourrait ecirctre
complexe operator + (complexe double)
Si ceci vous permet de donner un sens agrave une expression telle que (a eacutetant complexe)
a + 35
cela ne permet pas pour autant dinterpreacuteter
35 + a
Pour ce faire il aurait fallu surdeacutefinir lopeacuterateur + lorsquil a comme opeacuterandes un double et
un complexe avec par exemple1 comme prototype
complexe operator + (double complexe)
Nous verrons cependant au chapitre 10 que les possibiliteacutes de conversions deacutefinies par lutili-
sateur permettront de simplifier quelque peu les choses Par exemple il suffira dans ce cas
preacutecis de deacutefinir lopeacuterateur + lorsquil porte sur deux complexes ainsi que la conversion de
double en complexe pour que les expressions de lune de ces formes aient un sens
double + complexecomplexe + doublefloat + complexecomplexe + float
24 Cas des opeacuterateurs ++ et --Jusquagrave la version 20 de C++ on ne pouvait pas distinguer lopeacuterateur ++ en notation preacute-
fixeacutee (comme dans ++a) de ce mecircme opeacuterateur en notation postfixeacutee (comme dans a++)
Autrement dit pour un type classe donneacute on ne pouvait deacutefinir quun seul opeacuterateur ++ (ope-
rator ++) qui eacutetait utiliseacute dans les deux cas
Depuis la version 3 on peut deacutefinir agrave la fois un opeacuterateur ++ utilisable en notation preacutefixeacutee et
un autre utilisable en notation postfixeacutee Pour ce faire on utilise une convention qui consiste
agrave ajouter un argument fictif suppleacutementaire agrave la version postfixeacutee Par exemple si T deacutesigne
un type classe et que ++ est deacutefini sous la forme drsquoune fonction membre
bull lopeacuterateur (usuel) den-tecircte T operator ++ () est utiliseacute en cas de notation preacutefixeacutee
bull lopeacuterateur den-tecircte T operator ++ (int) est utiliseacute en cas de notation postfixeacutee Notez bien
la preacutesence dun second opeacuterande de type int Celui-ci est totalement fictif en ce sens quil
permet au compilateur de choisir lopeacuterateur agrave utiliser mais quaucune valeur ne sera reacuteelle-
ment transmise lors de lappel
De mecircme si ++ est deacutefini sous forme de fonction amie
bull lrsquoopeacuterateur (usuel) drsquoen-tecircte T operator (T) est utiliseacute en cas de notation preacutefixeacutee
bull lrsquoopeacuterateur drsquoen-tecircte T operator (T int) est utiliseacute en cas de notation postfixeacutee
1 Nous verrons dailleurs un peu plus loin que dans ce cas on ne pourra pas surdeacutefinir cet opeacuterateur comme une
fonction membre (puisque son premier opeacuterande nest plus de type classe)
2 - La surdeacutefinition drsquoopeacuterateurs en geacuteneacuteral161
Les mecircmes consideacuterations sappliquent agrave lopeacuterateur --
Voici un exemple dans lequel nous avons deacutefini ++ pour quil increacutemente dune uniteacute les deux
coordonneacutees dun point et fournisse comme valeur soit celle du point avant increacutementation
dans le cas de la notation postfixeacutee soit celle du point apregraves increacutementation dans le cas de la
notation preacutefixeacutee
include ltiostreamgtusing namespace std class point int x y public point (int abs=0 int ord=0) x=abs y=ord point operator ++ () notation preacutefixeacutee x++ y++ return this point operator ++ (int n) notation postfixeacutee point p = this x++ y++ return p void affiche () cout ltlt x ltlt ltlt y ltlt n
main() point a1 (2 5) a2(2 5) b b = ++a1 cout ltlt a1 a1affiche () affiche a1 3 6 cout ltlt b baffiche () affiche b 3 6 b = a2++ cout ltlt a2 a2affiche () affiche a2 3 6 cout ltlt b baffiche () affiche b 2 5
Exemple de surdeacutefinition de ++ en notation preacutefixeacutee et postfixeacutee
Remarque
Theacuteoriquement depuis la version 3 et donc depuis la norme ANSI il nrsquoest plus possible
de ne deacutefinir quun seul opeacuterateur ++ quon utiliserait agrave la fois en notation preacutefixeacutee et post-
fixeacutee En fait la plupart des compilateurs acceptent que lrsquoon ne fournisse que la version
preacutefixeacutee qui se trouve alors utiliseacutee dans les deux cas
25 Les opeacuterateurs = et amp ont une signification preacutedeacutefinieDans notre exemple drsquointroduction nous avions surdeacutefini lrsquoopeacuterateur + pour des opeacuterandes
de type point Comme on srsquoen doute en lrsquoabsence drsquoune telle surdeacutefinition lrsquoopeacuterateur
nrsquoaurait aucun sens dans ce contexte et son utilisation conduirait agrave une erreur de compilation
La surdeacutefinition drsquoopeacuterateursCHAPITRE 9
162
Il en va ainsi pour la plupart des opeacuterateurs qui nrsquoont donc pas de signification preacutedeacutefinie
pour un type classe Il existe toutefois quelques exceptions qui vont geacuteneacuteralement de soi (par
exemple on srsquoattend bien agrave ce que amp repreacutesente lrsquoadresse drsquoun objet )
Lrsquoopeacuterateur = fait lui aussi exception Nous avons deacutejagrave eu lrsquooccasion de lrsquoemployer avec
deux opeacuterandes du mecircme type classe et nous nrsquoavions pas eu besoin de le surdeacutefinir Effecti-
vement en lrsquoabsence de surdeacutefinition explicite cet opeacuterateur correspond agrave la recopie des
valeurs de son second opeacuterande dans le premier Nous avons drsquoailleurs constateacute que cette
simple recopie pouvait srsquoaveacuterer insatisfaisante degraves lors que les objets concerneacutes comportaient
des pointeurs sur des emplacements dynamiques Il srsquoagit lagrave typiquement drsquoune situation qui
neacutecessite la surdeacutefinition de lrsquoopeacuterateur = dont nous donnerons un exemple dans le paragra-
phe suivant
On notera la grande analogie existant entre
bull le constructeur de recopie srsquoil nrsquoen existe pas drsquoexplicite il y a appel drsquoun constructeur de
recopie par deacutefaut
bull lrsquoopeacuterateur drsquoaffectation srsquoil nrsquoen existe pas drsquoexplicite il y a emploi drsquoun opeacuterateur drsquoaf-
fectation par deacutefaut
Constructeur de recopie par deacutefaut et opeacuterateur drsquoaffectation par deacutefaut effectuent le mecircme
travail la recopie des valeurs de lrsquoobjet Au chapitre 7 nous avons signaleacute que dans le cas
drsquoobjets dont certains membres sont eux-mecircmes des objets le constructeur de recopie par
deacutefaut travaillait membre par membre La mecircme remarque srsquoapplique agrave lrsquoopeacuterateur drsquoaffecta-
tion par deacutefaut il opegravere membre par membre1 ce qui laisse la possibiliteacute drsquoappeler un opeacutera-
teur drsquoaffectation explicite dans le cas ougrave lrsquoun des membres en posseacutederait un Cela peut
eacuteviter drsquoavoir agrave eacutecrire explicitement un opeacuterateur drsquoaffectation pour des objets sans pointeurs
(apparents) mais dont un ou plusieurs membres possegravedent quant agrave eux des parties dynami-
ques
26 Les conversionsC et C++ autorisent freacutequemment les conversions entre types de base de faccedilon explicite ou
implicite Ces possibiliteacutes seacutetendent aux objets Par exemple comme nous lavons deacutejagrave eacutevo-
queacute si a est de type complexe et si lopeacuterateur + a eacuteteacute surdeacutefini pour deux complexes une
expression telle que a + 35 pourra prendre un sens
bull soit si lon a surdeacutefini lopeacuterateur + lorsquil a un opeacuterande de type complexe et un opeacuterande
de type double
bull soit si lon a deacutefini une conversion de type double en complexe
Nous avons toutefois preacutefeacutereacute regrouper au chapitre 10 tout ce qui concerne les problegravemes de
conversion cest lagrave que nous parlerons de la surdeacutefinition dun opeacuterateur de cast
1 Lagrave encore depuis la version 20 de C++ Auparavant il opeacuterait de faccedilon globale (memberwise copy)
3 - Exemple de surdeacutefinition de lrsquoopeacuterateur =163
27 Choix entre fonction membre et fonction amieC++ vous laisse libre de surdeacutefinir un opeacuterateur agrave laide dune fonction membre ou dune
fonction indeacutependante (en geacuteneacuteral amie) Vous pouvez donc parfois vous demander sur quels
critegraveres effectuer le choix Certes il semble qursquoon puisse eacutennoncer la regravegle suivante si un
opeacuterateur doit absolument recevoir un type de base en premier argument il ne peut pas
ecirctre deacutefini comme fonction membre (puisque celle-ci reccediloit implicitement un premier argu-ment du type de sa classe)
Mais il faudra tenir compte des possibliteacutes exposeacutees au prochain chapitre de conversion en unobjet drsquoun opeacuterande drsquoun type de base Par exemple lrsquoaddition dun double (type de base) etdun complexe (type classe) dans cet ordre1 semble correspondre agrave la situation eacutevoqueacutee (pre-mier opeacuterande drsquoun type de base) et donc imposer le recours agrave une fonction amie de la classecomplexe En fait nous verrons qursquoil peut aussi se traiter par surdeacutefinition drsquoune fonctionmembre de la classe complexe effectuant laddition de deux complexes compleacuteteacutee par ladeacutefinition de la conversion double -gt complexe
3 Exemple de surdeacutefinition de lrsquoopeacuterateur =
31 Rappels concernant le constructeur par recopie Nous avons deacutejagrave eu loccasion dutiliser une classe vect correspondant agrave des vecteurs dyna-miques (voir au paragraphe 3 du chapitre 7)
class vect int nelem nombre deacuteleacutements int adr adresse public vect (int n) constructeur
Si fct eacutetait une fonction agrave un argument de type vect les instructions suivantes vect a(5) fct (a)
1 Il ne suffira pas davoir surdeacutefini laddition dun complexe et dun double (qui peut se faire par une fonction
membre) En effet comme nous lavons dit aucune hypothegravese nest faite par C++ sur lopeacuterateur surdeacutefini en
particulier sur sa commutativiteacute
La surdeacutefinition drsquoopeacuterateursCHAPITRE 9
164
posaient problegraveme lappel de fct conduisait agrave la creacuteation par recopie de a dun nouvel objetb Nous eacutetions alors en preacutesence de deux objets a et b comportant un pointeur (adr) vers lemecircme emplacement
En particulier si la classe vect posseacutedait (comme cest souhaitable ) un destructeur chargeacute delibeacuterer lemplacement dynamique associeacute on risquait daboutir agrave deux demandes de libeacuterationdu mecircme emplacement meacutemoire
Une solution consistait agrave deacutefinir un constructeur de recopie chargeacute deffectuer non seulementla recopie de lobjet lui-mecircme mais aussi celle de sa partie dynamique dans un nouvel empla-cement (ou agrave interdire la recopie)
32 Cas de lrsquoaffectationLaffectation dobjets de type vect pose les mecircmes problegravemes Ainsi avec cette deacuteclaration
vect a(5) b(3)
qui correspond au scheacutema
a
b
5
5
a
b
5
3
x
y
z
t
u
f
g
h
3 - Exemple de surdeacutefinition de lrsquoopeacuterateur =165
Laffectation b = a
conduit agrave
Le problegraveme est effectivement voisin de celui de la construction par recopie Voisin maisnon identique car quelques difffeacuterences apparaissent
bull On peut se trouver en preacutesence dune affectation dun objet agrave lui-mecircme
bull Avant affectation il existe ici deux objets complets (cest-agrave-dire avec leur partie dynami-que) Dans le cas de la construction par recopie il nexistait quun seul emplacement dyna-mique le second eacutetant agrave creacuteer On va donc se retrouver ici avec lancien emplacementdynamique de b Or srsquoil nest plus reacutefeacuterenceacute par b est-on sucircr quil nest pas reacutefeacuterenceacute parailleurs
33 Algorithme proposeacuteNous pouvons reacutegler les diffeacuterents points en surdeacutefinissant lopeacuterateur daffectation demaniegravere que chaque objet de type vect comporte son propre emplacement dynamique Dansce cas on est sucircr quil nest reacutefeacuterenceacute quune seule fois et son eacuteventuelle libeacuteration peut sefaire sans problegraveme Notez cependant que cette deacutemarche ne convient totalement que si elleest associeacutee agrave la deacutefinition conjointe du constructeur de recopie
Voici donc comment nous pourrions traiter une affectation telle que b = a lorsque a est dif-feacuterent de b
bull libeacuteration de lemplacement pointeacute par b
bull creacuteation dynamique dun nouvel emplacement dans lequel on recopie les valeurs de lempla-cement pointeacute par a
bull mise en place des valeurs des membres donneacutees de b
a
b
5
3
x
y
z
t
u
f
g
h
La surdeacutefinition drsquoopeacuterateursCHAPITRE 9
166
Voici un scheacutema illustrant la situation agrave laquelle on aboutit
Il reste agrave reacutegler le cas ougrave a et b correspondent au mecircme objet
Si la transmission de a agrave lrsquoopeacuterateur drsquoaffectation a lieu par valeur et si le constructeur parrecopie a eacuteteacute redeacutefini de faccedilon approprieacutee (par creacuteation drsquoun nouvel emplacement dynami-que) lrsquoalgorithme proposeacute fonctionnera sans problegraveme
En revanche si la transmission de a a lieu par reacutefeacuterence on abordera lrsquoalgorithme avec cettesituation
Lrsquoemplacement dynamique associeacute agrave b (donc aussi agrave a) sera libeacutereacute avant qursquoon tente de lrsquouti-liser pour le recopier dans un nouvel emplacement La situation sera alors catastrophique1
a
b
5
5
x
y
z
t
u
x
y
z
t
u
5z
t
ua et b
y
1 Dans beaucoup drsquoenvironnements les valeurs drsquoun emplacement libeacutereacute ne sont pas modifieacutees Lrsquoalgorithme peut
alors donner lrsquoillusion qursquoil fonctionne
3 - Exemple de surdeacutefinition de lrsquoopeacuterateur =167
34 Valeur de retourEnfin il faut deacutecider de la valeur de retour fournie par lrsquoopeacuterateur Agrave ce niveau tout deacutependde lrsquousage que nous souhaitons en faire
bull Si nous nous contentons drsquoaffectations simples (b=a) nous nrsquoavons besoin drsquoaucune va-leur de retour (void)
bull En revanche si nous souhaitons pouvoir traiter une affectation multiple ou plus geacuteneacuterale-ment faire en sorte que (comme on peut srsquoy attendre ) lrsquoexpression b=a ait une valeur(probablement celle de b ) il est neacutecessaire que lrsquoopeacuterateur fournisse une valeur de retour
Nous choisissons ici la seconde possibiliteacute qui a le meacuterite drsquoecirctre plus geacuteneacuterale1
35 En deacutefinitiveVoici finalement ce que pourrait ecirctre la deacutefinition de lrsquoopeacuterateur = (C++ impose de le deacutefinircomme une fonction membre) b devient le premier opeacuterande ndash ici this ndash et a devient lesecond opeacuterande ndash ici v De plus nous preacutevoyons de transmettre le second opeacuterande parreacutefeacuterence
vect vectoperator = (const vect amp v) notez const if (this = ampv) delete adr adr = new int [nelem = vnelem] for (int i=0 iltnelem i++) adr[i] = vadr[i] return this
Comme lrsquoargument de la fonction membre operator= est transmis par reacutefeacuterence il est neacuteces-saire de lui associer le qualificatif const si lrsquoon souhaite pouvoir affecter un vecteur constantagrave un vecteur quelconque2
36 Exemple de programme completNous vous proposons dinteacutegrer cette deacutefinition dans un programme complet servant agrave illus-trer le fonctionnement de lopeacuterateur Pour ce faire nous ajoutons comme dhabitude un cer-tain nombre dinstructions daffichage (en particulier nous suivons les adresses des objets etdes emplacements dynamiques qui leur sont associeacutes) Mais pour que le programme ne soitpas trop long nous avons reacuteduit la classe vect au strict minimum en particulier nousnrsquoavons pas preacutevu de constructeur de recopie or celui-ci deviendrait naturellement
indispensable dans une application reacuteelle
1 Bien entendu C++ vous laisse libre de faire ce que vous voulez y compris de renvoyer une valeur autre que celle
de b (avec tous les risques de manque de lisibiliteacute que cela suppose )
2 Cependant comme on le verra au chapitre 10 la preacutesence de cet attribut const pourra autoriser certaines
conversions de lrsquoargument
La surdeacutefinition drsquoopeacuterateursCHAPITRE 9
168
En outre bien qursquoici notre fonction main se limite agrave lemploi de lopeacuterateur = nous avons ducircpreacutevoir une transmission par reacutefeacuterence pour largument et la valeur de retour de operator=En effet si nous ne lavions pas fait lappel de cet opeacuterateur ndash traiteacute comme une fonction ndashaurait entraicircneacute un appel de constructeur de recopie (a = b est eacutequivalent ici agrave aoperator =
(b)) il se serait alors agi du constructeur de recopie par deacutefaut ce qui aurait entraicircneacute les pro-
blegravemes deacutejagrave eacutevoqueacutes de double libeacuteration dun emplacement1
include ltiostreamgtusing namespace std class vect int nelem nombre deacuteleacutements int adr pointeur sur ces eacuteleacutements public vect (int n) constructeur adr = new int [nelem = n] for (int i=0 iltnelem i++) adr[i] = 0 cout ltlt ++ obj taille ltlt nelem ltlt en ltlt this ltlt - v dyn en ltlt adr ltlt n ~vect () destructeur cout ltlt -- obj taille ltlt nelem ltlt en ltlt this ltlt - v dyn en ltlt adr ltlt n delete adr vect amp operator = (const vect amp) surdeacutefinition opeacuterateur = vect amp vectoperator = (const vect amp v) cout ltlt == appel operateur = avec adresses ltlt this ltlt ltlt ampv ltlt n if (this = ampv) cout ltlt effacement vecteur dynamique en ltlt adr ltlt n delete adr adr = new int [nelem = vnelem] cout ltlt nouveau vecteur dynamique en ltlt adr ltlt n for (int i=0 iltnelem i++) adr[i] = vadr[i] else cout ltlt on ne fait rien n return this main() vect a(5) b(3) c(4) cout ltlt affectation a=b n a = b cout ltlt affectation c=c n c = c cout ltlt affectation a=b=c n a = b = c
1 Un des exercices de ce chapitre vous propose de le veacuterifier
3 - Exemple de surdeacutefinition de lrsquoopeacuterateur =169
++ obj taille 5 en 006AFDE4 - v dyn en 007D0340
++ obj taille 3 en 006AFDDC - v dyn en 007D00D0
++ obj taille 4 en 006AFDD4 - v dyn en 007D0090
affectation a=b
== appel operateur = avec adresses 006AFDE4 006AFDDC
effacement vecteur dynamique en 007D0340
nouveau vecteur dynamique en 007D0340
affectation c=c
== appel operateur = avec adresses 006AFDD4 006AFDD4
on ne fait rien
affectation a=b=c
== appel operateur = avec adresses 006AFDDC 006AFDD4
effacement vecteur dynamique en 007D00D0
nouveau vecteur dynamique en 007D00D0
== appel operateur = avec adresses 006AFDE4 006AFDDC
effacement vecteur dynamique en 007D0340
nouveau vecteur dynamique en 007D0340
-- obj taille 4 en 006AFDD4 - v dyn en 007D0090
-- obj taille 4 en 006AFDDC - v dyn en 007D00D0
-- obj taille 4 en 006AFDE4 - v dyn en 007D0340
Exemple dutilisation dune classe vect avec un opeacuterateur daffectation surdeacutefini
37 Lorsqursquoon souhaite interdire lrsquoaffectationNous avons deacutejagrave vu (paragraphe 313 du chapitre 7) que dans certains cas on pouvait avoir
inteacuterecirct agrave interdire la recopie dobjets Les mecircmes consideacuterations sappliquent agrave laffectation
Ainsi une redeacutefinition de laffectation sous forme priveacutee en interdit lemploi par des fonc-
tions autres que les fonctions membres de la classe concerneacutee On peut eacutegalement exploiter la
possibiliteacute quoffre C++ de deacuteclarer une fonction sans en fournir de deacutefinition dans ce cas
toute tentative daffectation (mecircme au sein dune fonction membre) sera rejeteacutee par leacutediteur
de liens Dune maniegravere geacuteneacuterale il peut ecirctre judicieux de combiner les deux possibiliteacutes
cest-agrave-dire deffecteur une deacuteclaration priveacutee sans deacutefinition dans ces cas les tentatives
daffectation de la part de lutilisateur seront deacutetecteacutees en compilation et seules les tentatives
daffectation par une fonction membre produiront une erreur de leacutedition de liens (et ce point
ne concerne que le concepteur de la classe et non son utilisateur)
Remarques
1 Comme dans le cas de la deacutefinition du constructeur de recopie nous avons utiliseacute la
deacutemarche la plus naturelle consistant agrave effectuer une copie profonde en dupliquant la par-
tie dynamique de lobjet Dans certains cas on pourra chercher agrave eacuteviter cette duplication
en la dotant dun compteur de reacutefeacuterences comme lexplique lAnnexe E
La surdeacutefinition drsquoopeacuterateursCHAPITRE 9
170
2 Nous verrons plus tard que si une classe est destineacutee agrave donner naissance agrave des objets
susceptibles drsquoecirctre introduits dans des conteneurs il nrsquoest plus possible de deacutesactiver
lrsquoaffectation (pas plus que la recopie)
En Java
Java ne permet pas la surdeacutefinition dopeacuterateur On ne peut donc pas modifier la seacutemanti-
que de laffectation qui rappelons-le est tregraves diffeacuterente de celle agrave laquelle on est habitueacute
en C++ (les objets eacutetant manipuleacutes par reacutefeacuterence on aboutit apregraves affectation agrave deux reacutefeacute-
rences eacutegales agrave un unique objet)
4 La forme canonique dune classeDegraves lors quune classe dispose de pointeurs sur des parties dynamiques la copie dobjets de la
classe (aussi bien par le constructeur de recopie par deacutefaut que par lopeacuterateur daffectation
par deacutefaut) nest pas satisfaisante Dans ces conditions si lon souhaite que cette recopie fonc-
tionne convenablement il est neacutecessaire de munir la classe des quatre fonctions membres
suivantes au moins
bull constructeur (il sera geacuteneacuteralement chargeacute de lallocation de certaines parties de lobjet)
bull destructeur (il devra libeacuterer correctement tous les emplacements dynamiques creacuteeacutes par lobjet)
bull constructeur de recopie
bull opeacuterateur daffectation
Voici un canevas reacutecapitulatif correspondant agrave ce minimum quon nomme souvent classe
canonique
class T public
T () constructeurs autres que par recopie
T (const T amp) constructeur de recopie (forme conseilleacutee)
(deacuteclaration priveacutee pour lrsquointerdire) ~T () destructeur
T amp operator = (const T amp) affectation (forme conseilleacutee)
(deacuteclaration priveacutee pour lrsquointerdire)
La forme canonique dune classe
Bien que ce ne soit pas obligatoire nous vous conseillons
bull demployer le qualificatif const pour largument du constructeur de recopie et celui de laffec-
tation dans la mesure ougrave ces fonctions membres nont aucune raison de modifier les valeurs
5 - Exemple de surdeacutefinition de lopeacuterateur [ ]171
des objets correspondants On verra toutefois au chapitre 10 que cette faccedilon de proceacuteder peut
autoriser lintroduction de certaines conversions de lopeacuterande de droite de laffectation
bull de preacutevoir (agrave moins davoir de bonnes raisons de faire le contraire) une valeur de retour agrave
lopeacuterateur daffectation seul moyen de geacuterer correctement les affectations multiples
En revanche largument de lopeacuterateur daffectation et sa valeur de retour peuvent ecirctre indif-
feacuteremment transmis par reacutefeacuterence ou par valeur Cependant on ne perdra pas de vue que les
transmissions par valeur entraicircnent lappel du constructeur de recopie Dautre part degraves lors
que les objets sont de taille respectable la transmission par reacutefeacuterence savegravere plus efficace
Si vous creacuteez une classe comportant des pointeurs sans la doter de ce minimum vital et sans
prendre de preacutecautions particuliegraveres lutilisateur ne se verra nullement interdire la recopie ou
laffectation dobjets
Il peut arriver de creacuteer une classe qui nrsquoa pas besoin de disposer de ces possibiliteacutes de recopie
et drsquoaffectation par exemple parce qursquoelles nrsquoont pas de sens (cas drsquoune classe fenecirctre drsquoun
systegraveme graphique) Il se peut aussi que vous souhaitiez tout simplement ne pas offrir ces
possibliteacutes agrave lrsquoutilisateur de la classe Dans ce cas plutocirct que de compter sur la bonne
volonteacute de lrsquoutilisateur il est preacutefeacuterable drsquoutiliser quand mecircme la forme canonique en
srsquoarrangeant pour interdire ces actions Nous vous avons fourni des pistes dans ce sens au
paragraphe 37 ainsi qursquoau paragraphe 313 du chapitre 7 et nous avons vu qursquoune solution
simple agrave mettre en place consistait agrave fournir des deacuteclarations priveacutees de ces deux meacutethodes
sans en fournir de deacutefinition
Remarque
Ce scheacutema sera compleacuteteacute au chapitre 13 afin de prendre en compte la situation dheacuteritage
5 Exemple de surdeacutefinition de lopeacuterateur [ ] Consideacuterons agrave nouveau notre classe vect
class vect int nelem int adr
Cherchons agrave la munir doutils permettant dacceacuteder agrave un eacuteleacutement de lemplacement pointeacute par
adr agrave partir de sa position que lon repeacuterera par un entier compris entre 0 et nelem-1
Nous pourrions bien sucircr eacutecrire des fonctions membres comme
void range (int valeur int position)
pour introduire une valeur agrave une position donneacutee et
int trouve (int position)
pour fournir la valeur situeacutee agrave une position donneacutee
La surdeacutefinition drsquoopeacuterateursCHAPITRE 9
172
La manipulation de nos vecteurs ne serait alors guegravere aiseacutee Elle ressemblerait agrave ceci
vect a(5) arange (15 0) place 15 en position 0 de aarange (25 1) 25 en position 1for (int i = 2 i lt 5 i++) arange (0 i) et O ailleursfor i = 0 i lt 5 i++) pour afficher les valeurs de a cout ltlt atrouve (i)
En fait nous pouvons chercher agrave surdeacutefinir lopeacuterateur [] de maniegravere que a[i] deacutesigne leacuteleacute-
ment demplacement i de a La seule preacutecaution agrave prendre consiste agrave faire en sorte que cette
notation puisse ecirctre utiliseacutee non seulement dans une expression (cas qui ne preacutesente aucune
difficulteacute) mais eacutegalement agrave gauche dune affectation cest-agrave-dire comme lvalue Notez que
le problegraveme ne se posait pas dans lexemple ci-dessus puisque chaque cas eacutetait traiteacute par une
fonction membre diffeacuterente
Pour que a[i] soit une lvalue il est donc neacutecessaire que la valeur de retour fournie par lopeacute-
rateur [] soit transmise par reacutefeacuterence
Par ailleurs C++ impose de surdeacutefinir cet opeacuterateur sous la forme dune fonction membre ce
qui implique que son premier opeacuterande (le premier opeacuterande de a[i] est a) soit de type classe
(ce qui semble raisonnable ) Son prototype sera donc
int amp operator [] (int)
Si nous nous contentons de renvoyer leacuteleacutement chercheacute sans effectuer de controcircle sur la vali-
diteacute de la position le corps de la fonction operator[] peut se reacuteduire agrave
return adr[i]
Voici un exemple simple dutilisation dune classe vect reacuteduite agrave son strict minimum cons-
tructeur destructeur et opeacuterateur [] Bien entendu en pratique il faudrait au moins lui ajouter
un constructeur de recopie et un opeacuterateur daffectation
include ltiostreamgtusing namespace std class vect int nelem int adr public vect (int n) adr = new int [nelem=n] ~vect () delete adr int amp operator [] (int) int amp vectoperator [] (int i) return adr[i] main() int i vect a(3) b(3) c(3) for (i=0 ilt3 i++) a[i] = i b[i] = 2i for (i=0 ilt3 i++) c[i] = a[i]+b[i] for (i=0 ilt3 i++) cout ltlt c[i] ltlt
6 - Surdeacutefinition de lopeacuterateur ()173
0 3 6
Exemple de surdeacutefinition de lopeacuterateur[]
Remarques
1 Nous pourrions bien sucircr transmettre le second opeacuterande par reacutefeacuterence mais cela ne preacute-
senterait guegravere dinteacuterecirct compte tenu de la petite taille des variables du type int
2 C++ interdit de deacutefinir lopeacuterateur [] sous la forme dune fonction amie il en allait deacutejagrave
de mecircme pour lopeacuterateur = De toute faccedilon nous verrons au prochain chapitre quil
nest pas conseilleacute de deacutefinir par une fonction amie un opeacuterateur susceptible de modifier
un objet compte tenu des conversions implicites pouvant apparaicirctre
3 Seules les fonctions membres doteacutees du qualificatif const peuvent ecirctre appliqueacutees agrave un
objet constant Tel que nous lavons conccedilu lopeacuterateur [] ne permet donc pas dacceacuteder
agrave un objet constant mecircme sil ne sagit que drsquoutiliser la valeur de ses eacutelements sans la
modifier Certes on pourrait ajouter ce qualificatif const agrave lrsquoopeacuterateur [] mais il la
modification des valeurs drsquoun objet constant deviendrait alors possible ce qui nest
guegravere souhaitable En geacuteneacuteral on preacutefeacuterera deacutefinir un second opeacuterateur destineacute unique-
ment aux objets constants en faisant en sorte quil puisse consulter lobjet en question
mais non le modifier Dans notre cas voici ce que pourrait ecirctre ce second opeacuterateur
int vectoperator [] (int i) const return adr[i]
Une affectation telle que v[i] = v eacutetant un vecteur constant sera bien rejeteacutee en com-
pilation puisque notre opeacuterateur transmet son reacutesultat par valeur et non plus par reacutefeacute-
rence
4 Lopeacuterateur [] eacutetait ici dicteacute par le bon sens mais nullement imposeacute par C++ Nous
aurions pu tout aussi bien utiliser
ndash lopeacuterateur () la notation a(i) aurait encore eacuteteacute compreacutehensible
ndash lopeacuterateur lt que penser alors de la notation a lt i
ndash lopeacuterateur notation a i
ndash etc
6 Surdeacutefinition de lopeacuterateur ()Lorsquune classe surdeacutefinit lopeacuterateur () on dit que les objets auxquels elle donne naissance
sont des objets fonctions car ils peuvent ecirctre utiliseacutes de la mecircme maniegravere quune fonction
La surdeacutefinition drsquoopeacuterateursCHAPITRE 9
174
ordinaire En voici un exemple simple dans lequel nous surdeacutefinissons lopeacuterateur () pour
quil corresponde agrave une fonction agrave deux arguments de type int et renvoyant un int
class cl_fct public cl_fct(float x) constructeur int operator() (int n int p ) opeacuterateur ()
Dans ces conditions une deacuteclaration telle que
cl_fct obj_fct1(25)
construit bien sucircr un objet nommeacute obj_fct1 de type cl_fct en transmettant le paramegravetre 25 agrave
son constructeur En revanche la notation suivante reacutealise lappel de lopeacuterateur ( ) de lobjet
obj_fct1 en lui transmettant les valeurs 3 et 5
obj_fct1(3 5)
Ces possibiliteacutes peuvent servir lorsquil est neacutecessaire deffectuer certaines opeacuterations dini-
tialisation dune fonction ou de parameacutetrer son travail (par le biais des arguments passeacutes agrave son
constructeur) Mais elles saveacutereront encore plus inteacuteressantes dans le cas des fonctions dites
de rappel crsquoest-agrave-dire transmises en argument agrave une autre fonction
7 Surdeacutefinition des opeacuterateurs new et deleteNB Ce paragraphe peut ecirctre ignoreacute dans un premier temps
Tout drsquoabord il faut bien noter que les opeacuterateurs new et delete peuvent srsquoappliquer agrave des
types de base agrave des structures usuelles ou agrave des objets Par ailleurs il existe drsquoautres opeacutera-
teurs new[] et delete[] srsquoappliquant agrave des tableaux (drsquoeacuteleacutements de type de base structure ou
objet) Cette remarque a des conseacutequences au niveau de leur redeacutefinition
bull Vous pourrez redeacutefinir new et delete seacutelectivement pour une classe donneacutee bien entendu
vous pourrez toujours redeacutefinir ces opeacuterateurs dans autant de classes que vous le
souhaiterez dans ce cas les opeacuterateurs preacutedeacutefinis (on parle aussi drsquoopeacuterateurs globaux)
continueront drsquoecirctre utiliseacutes pour les classes ougrave aucune surdeacutefinition nrsquoaura eacuteteacute preacutevue
bull Vous pourrez eacutegalement redeacutefinir ces opeacuterateurs de faccedilon globale il seront alors utiliseacutes
pour les types de base pour les structures usuelles et pour les types classe nrsquoayant opeacutereacute
aucune surdeacutefinition
bull Enfin il ne faudra pas perdre de vue que les surdeacutefinitions de new drsquoune part et de new[]
drsquoautre part sont deux choses diffeacuterentes lrsquoune nrsquoentraicircnant pas automatiquement lrsquoautre
la mecircme remarque srsquoapplique agrave delete et delete[]
Voyons cela plus en deacutetail en commenccedilant par la situation la plus usuelle agrave savoir la surdeacute-
finition de new et delete au sein drsquoune classe
7 - Surdeacutefinition des opeacuterateurs new et delete175
71 Surdeacutefinition de new et delete pour une classe donneacuteeLa surdeacutefinition de new se fait obligatoirement par une fonction membre qui doit
bull Posseacuteder un argument de type size_t correspondant agrave la taille en octets de lrsquoobjet agrave allouer
Bien qursquoil figure dans la deacutefinition de new il nrsquoa pas agrave ecirctre speacutecifieacute lors de son appel car
crsquoest le compilateur qui le geacuteneacuterera automatiquement en fonction de la taille de lrsquoobjet con-
cerneacute (Rappelons que size_t est un synonyme drsquoun type entier deacutefini dans le fichier en-
tecircte cstddef)
bull Fournir en retour une valeur de type void correspondant agrave lrsquoadresse de lrsquoemplacement al-
loueacute pour lrsquoobjet
Quant agrave la deacutefinition de la fonction membre correspondant agrave lrsquoopeacuterateur delete elle doit
bull Recevoir un argument du type pointeur sur la classe correspondante il repreacutesente lrsquoadresse
de lrsquoemplacement alloueacute agrave lrsquoobjet agrave deacutetruire
bull Ne fournir aucune valeur de retour (void)
Remarques
1 Mecircme lorsque lrsquoopeacuterateur new a eacuteteacute surdeacutefini pour une classe il reste possible de faire
appel agrave lrsquoopeacuterateur preacutedeacutefini en utilisant lrsquoopeacuterateur de reacutesolution de porteacutee il en va de
mecircme pour delete
2 Les opeacuterateurs new et delete sont des fonctions membres statiques de leur classe (voir
le paragraphe 8 du chapitre 6) En tant que tels ils nrsquoont donc accegraves qursquoaux membres
statiques de la classe ougrave ils sont deacutefinis et ne reccediloivent pas drsquoargument implicite (this)
72 ExempleVoici un programme dans lequel la classe point surdeacutefinit les opeacuterateurs new et delete dans
le seul but drsquoen comptabiliser les appels1 Ils font drsquoailleurs appel aux opeacuterateurs preacutedeacutefinis
(par emploi de ) pour ce qui concerne la gestion de la meacutemoire
include ltiostreamgtinclude ltcstddefgt pour size_tusing namespace std
class point static int npt nombre total de points static int npt_dyn nombre de points dynamiques int x y
1 Bien entendu dans un programme reacuteel lrsquoopeacuterateur new accomplira en geacuteneacuteral une tacircche plus eacutelaboreacutee
La surdeacutefinition drsquoopeacuterateursCHAPITRE 9
176
public point (int abs=0 int ord=0) constructeur x=abs y=ord npt++ cout ltlt ++ nombre total de points ltlt npt ltlt n ~point () destructeur npt-- cout ltlt -- nombre total de points ltlt npt ltlt n void operator new (size_t sz) new surdeacutefini npt_dyn++ cout ltlt il y a ltlt npt_dyn ltlt points dynamiques sur un n return new char[sz] void operator delete (void dp) npt_dyn-- cout ltlt il y a ltlt npt_dyn ltlt points dynamiques sur un n delete (dp) int pointnpt = 0 initialisation membre statique nptint pointnpt_dyn = 0 initialisation membre statique npt_dynmain() point ad1 ad2 point a(35) ad1 = new point (13) point b ad2 = new point (20) delete ad1 point c(2) delete ad2
++ nombre total de points 1 il y a 1 points dynamiques sur un ++ nombre total de points 2++ nombre total de points 3 il y a 2 points dynamiques sur un ++ nombre total de points 4-- nombre total de points 3 il y a 1 points dynamiques sur un ++ nombre total de points 4-- nombre total de points 3 il y a 0 points dynamiques sur un -- nombre total de points 2-- nombre total de points 1-- nombre total de points 0
Exemple de surdeacutefinition de lrsquoopeacuterateur new pour la classe point
7 - Surdeacutefinition des opeacuterateurs new et delete177
Remarques
1 Comme le montre cet exemple et comme on peut srsquoy attendre la surdeacutefinition des opeacutera-
teurs new et delete nrsquoa drsquoincidence que sur les objets alloueacutes dynamiquement Les objets
statiques (alloueacutes agrave la compilation) et les objets dynamiques (alloueacutes lors de lrsquoexeacutecution
mais sur la pile) ne sont toujours pas concerneacutes
2 Que new soit surdeacutefini ou preacutedeacutefini son appel est toujours (heureusement) suivi de
celui du constructeur (lorsqursquoil existe) De mecircme que delete soit surdeacutefini ou preacutedeacutefini
son appel est toujours preacuteceacutedeacute de celui du destructeur (lorsqursquoil existe)
3 Nrsquooubliez pas qursquoil est neacutecessaire de distinguer new de new[] delete de delete[] Ainsi
dans lrsquoexemple de programme preacuteceacutedent une instruction telle que
point ad = new point [50]
ferait appel agrave lrsquoopeacuterateur new preacutedeacutefini (et 50 fois agrave lrsquoappel du constructeur sans argu-
ment) En geacuteneacuteral on surdeacutefinira eacutegalement new[] et delete[] comme nous allons le
voir ci-apregraves
73 Drsquoune maniegravere geacuteneacuteralePour surdeacutefinir new[] au sein drsquoune classe il suffit de proceacuteder comme pour new le nom
mecircme de lrsquoopeacuterateur (new[] au lieu de new) servant agrave effectuer la distinction Par exemple
dans notre classe point de lrsquoexemple preacuteceacutedent nous pourrons ajouter
void operator new [](size_t sz)
return new char[sz]
La valeur fournie en argument correspondra bien agrave la taille totale agrave allouer pour le tableau (et
non agrave la taille drsquoun seul eacuteleacutement) Cette fois dans notre preacuteceacutedent exemple de programme
lrsquoinstruction
point adp = new point[50]
effectuera bien un appel de lrsquoopeacuterateur new[] ainsi surdeacutefini (et toujours les 50 appels du
constructeur sans arguments de point)
De mecircme on surdeacutefinira delete[] de cette faccedilon
void operator delete (void dp) dp adresse de lrsquoemplacement agrave libeacuterer
Enfin pour surdeacutefinir les opeacuterateurs new et delete de maniegravere globale il suffit de deacutefinir
lrsquoopeacuterateur correspondant sous la forme drsquoune fonction indeacutependante comme dans cet
exemple
void operator new (size_t sz)
La surdeacutefinition drsquoopeacuterateursCHAPITRE 9
178
Notez bien qursquoalors
bull Cet opeacuterateur sera appeleacute pour tous les types pour lesquels aucun opeacuterateur new nrsquoa eacuteteacute sur-
deacutefini y compris pour les types de base Crsquoest le cas de la deacuteclaration suivante
int adi = new int
bull Dans la surdeacutefinition de cet opeacuterateur il nrsquoest plus possible de faire appel agrave lrsquoopeacuterateur new
preacutedeacutefini Toute tentative drsquoappel de new ou mecircme de new fera entrer dans un processus
reacutecursif
Ce dernier point limite lrsquointeacuterecirct de la surdeacutefinition globale de new de delete puisque le pro-
grammeur doit prendre complegravetement agrave sa charge la gestion dynamique de meacutemoire (par
exemple en reacutealisant les appels au systegraveme neacutecessaires)
ExercicesNB Les exercices marqueacutes (C) sont corrigeacutes en fin de volume
1) Dans les deux exemples de programme des paragraphes 11 et 12 mettez en eacutevidence
les appels dun constructeur de recopie Pour ce faire introduisez un constructeur sup-
pleacutementaire de la forme point (pointamp) dans la classe point Voyez ce qui se produit
lorsque vous employez la transmission par reacutefeacuterence pour le ou les arguments de ope-
rator+
2) Dans lexemple du paragraphe 3 introduisez un constructeur de recopie pour la classe
vect Constatez que le remplacement de la transmission par reacutefeacuterence par une trans-
mission par valeur entraicircne la creacuteation de nombreux objets suppleacutementaires
3 (C) Dans une classe point de la forme
class point
int x y
public
point (int abs = 0 int ord = 0)
introduisez un opeacuterateur == tel que si a et b sont deux points a==b fournisse la
valeur 1 lorsque a et b ont les mecircmes coordonneacutees et la valeur 0 dans le cas contraire
On preacutevoira les deux situations
a) fonction membre
b) fonction amie
7 - Surdeacutefinition des opeacuterateurs new et delete179
4 (C) Dans une classe pile_entier de la forme1
class pile_entier int dim nombre maxi deacuteleacutements de la pile int adr adresse du tableau repreacutesentant la pile int nelem nombre deacuteleacutements courant de la pile public pile_entier (int n) ~pile_entier ()
introduisez les opeacuterateurs gt et lt tels que si p est un objet de type pile_entier et n une
variable entiegravere
p lt n ajoute la valeur de n sur la pile p (en ne renvoyant aucune valeur)
p gt n supprime la valeur du haut de la pile et la place dans n
On preacutevoira les deux situations
a) fonctions membres
b) fonctions amies
5 Veacuterifiez si votre impleacutementation accepte qursquoon ne deacutefinisse que la version preacute de
lrsquoopeacuterateur ++ en vous inspirant de lrsquoexemple du paragraphe 24
6 (C) En langage C il nexiste pas de veacuteritable type chaicircne mais simplement une conven-
tion de repreacutesentation des chaicircnes (suite de caractegraveres termineacutee par un caractegravere de
code nul) Un certain nombre de fonctions utilisant cette convention permettent les
manipulations classiques (copie concateacutenation)
Cet exercice vous demande de deacutefinir une classe nommeacutee chaine offrant des possibili-
teacutes plus proches dun veacuteritable type chaicircne (tel que celui du Basic ou du Pascal) Pour
ce faire on preacutevoira comme membres donneacutees
bull la longueur courante de la chaicircne
bull ladresse dune zone alloueacutee dynamiquement destineacutee agrave recevoir la suite de carac-
tegraveres (il ne sera pas neacutecessaire dy ranger le caractegravere nul de fin puisque la longueur
de la chaicircne est deacutefinie par ailleurs)
Le contenu dun objet de type chaine pourra donc eacutevoluer par un simple jeu de gestion
dynamique
On munira la classe chaine des constructeurs suivants
bull chaine() initialise une chaicircne vide
bull chaine (char) initialise la chaicircne avec la chaicircne (au sens du C) dont on fournit
ladresse en argument
bull chaine (chaineamp) constructeur de recopie
1 Revoyez eacuteventuellement lexercice 5 du chapitre 7
On deacutefinira les opeacuterateurs
bull = pour laffectation entre objets de type chaine (penser agrave laffectation multiple)
bull == pour examiner leacutegaliteacute de deux chaicircnes
bull + pour reacutealiser la concateacutenation de deux chaicircnes Si a et b sont de type chaine a + b
sera une (nouvelle) chaicircne formeacutee de la concateacutenation de a et b (les chaicircnes a et b
devront ecirctre inchangeacutees)
bull [] pour acceacuteder agrave un caractegravere de rang donneacute dune chaicircne (les affectations de la for-
me a[i] = x devront pouvoir fonctionner
On pourra ajouter une fonction daffichage On ne preacutevoira pas demployer de comp-
teur par reacutefeacuterence ce qui signifie quon acceptera de dupliquer les chaicircnes identiques
NB On trouvera dans la bibliothegraveque standard preacutevue par la norme une classe string
offrant entre autres les fonctionnaliteacutes eacutevoqueacutees ici Pour conserver son inteacuterecirct agrave
lrsquoexercice il ne faut pas lrsquoutiliser ici
La surdeacutefinition drsquoopeacuterateursCHAPITRE 9
180
10
Les conversions de typedeacutefinies par lrsquoutilisateur
En matiegravere de conversion dun type de base en un autre type de base C++ offre naturellement
les mecircmes possibiliteacutes que le langage C qui rappelons-le fait intervenir des conversions
explicites et des conversions implicites
Les conversions sont explicites lorsque lon fait appel agrave un opeacuterateur de cast comme dans
int n double z
z = double(n) conversion de int en double
ou dans
n = int(z) conversion de double en int
Les conversions implicites ne sont pas mentionneacutees par lutilisateur1 mais elles sont mises
en place par le compilateur en fonction du contexte elles se rencontrent agrave diffeacuterents
niveaux
bull dans les affectations il y a alors conversion forceacutee dans le type de la variable reacuteceptrice
bull dans les appels de fonction comme le prototype est obligatoire en C++ il y a eacutegalement
conversion forceacutee dun argument dans le type deacuteclareacute dans le prototype
1 Cest-agrave-dire en fait lauteur du programme Nous avons toutefois conserveacute le terme reacutepandu dutilisateur qui
soppose ici agrave compilateur
Les conversions de type deacutefinies par lrsquoutilisateurCHAPITRE 10
182
bull dans les expressions pour chaque opeacuterateur il y a conversion eacuteventuelle de lun des opeacute-
randes dans le type de lautre suivant des regravegles preacutecises1 qui font intervenir
ndash des conversions systeacutematiques char et short en int
ndash des conversions dajustement de type par exemple int en long pour une addition de
deux valeurs de type long
Mais C++ permet aussi de deacutefinir des conversions faisant intervenir des types classe creacuteeacutes
par lutilisateur Par exemple pour un type complexe on pourra en eacutecrivant des fonctions
approprieacutees donner une signification aux conversions
complexe -gt doubledouble -gt complexe
Qui plus est nous verrons que lexistence de telles conversions permettra de donner un sens agrave
laddition dun complexe et dun double ou mecircme celle dun complexe et dun int
Cependant sil paraicirct logique de disposer de conversions entre une classe complexe et les
types numeacuteriques il nen ira plus neacutecessairement de mecircme pour des classes nayant pas une
connotation matheacutematique aussi forte ce qui nempecircchera pas le compilateur de mettre en
place le mecircme genre de conversions
Ce chapitre fait le point sur ces diffeacuterentes possibiliteacutes Consideacutereacutees comme assez dangereu-
ses il est bon de ne les employer qursquoen toute connaissance de cause Pour vous eacuteviter une
conclusion hacirctive nous avons volontairement utiliseacute des exemples de conversions tantocirct
signifiantes (agrave connotation matheacutematique) tantocirct non signifiantes
Au passage nous en profiterons pour insister sur le rocircle important du qualificatif const appli-
queacute agrave un argument muet transmis par reacutefeacuterence
1 Les diffeacuterentes sortes de conversions deacutefinies par lrsquoutilisateur
Consideacuterons une classe point posseacutedant un constructeur agrave un argument comme
point (int abs) x = abs y = 0
On peut dire que ce constructeur reacutealise une conversion dun int en un objet de type point
Nous avons dailleurs deacutejagrave vu comment appeler explicitement ce constructeur par exemple
point a a = point(3)
Comme nous le verrons agrave moins de linterdire au moment de la deacutefinition de la classe ce
constructeur peut ecirctre appeleacute implicitement dans des affectations des appels de fonction ou
1 Ces regravegles sont deacutetailleacutees dans La reacutefeacuterence du C norme ANSIISO du mecircme auteur
1 - Les diffeacuterentes sortes de conversions deacutefinies par lrsquoutilisateur183
des calculs dexpression au mecircme titre quune conversion usuelle (on parle aussi de con-
version standard)
Plus geacuteneacuteralement si lon considegravere deux classes nommeacutees point et complexe on peut dire
qursquoun constructeur de la classe complexe agrave un argument de type point
complexe (point)
permet de convertir un point en complexe Nous verrons que cette conversion pourra elle
aussi ecirctre utiliseacutee implicitement dans les diffeacuterentes situations eacutevoqueacutees (agrave moins quon lait
interdit explicitement)
En revanche un constructeur (qui fournit un objet du type de sa classe) ne peut en aucun cas
permettre de reacutealiser une conversion dun objet en une valeur dun type simple (type de base
ou pointeur) par exemple un point en int ou un complexe en double Comme nous le verrons
ce type de conversion pourra ecirctre traiteacute en deacutefinissant au sein de la classe concerneacutee un opeacute-
rateur de cast approprieacute par exemple pour les deux cas citeacutes
operator int()
au sein de la classe point
operator double()
au sein de la classe complexe
Cette derniegravere deacutemarche de deacutefinition dun opeacuterateur de cast pourra aussi ecirctre employeacutee pour
deacutefinir une conversion dun type classe en un autre type classe Par exemple avec
operator complexe()
au sein de la classe point on deacutefinira la conversion dun point en complexe au mecircme titre
quavec le constructeur
complexe (point)
situeacute cette fois dans la classe complexe
Voici un scheacutema reacutecapitulant les diffeacuterentes possibiliteacutes que nous venons deacutevoquer A et B
deacutesignent deux classes b un type de base quelconque
Les quatre sortes de conversions deacutefinies par lrsquoutilisateur
Constructeur agrave un argument
A(b)
Opeacuterateur de cast dans A
operator (b)
Constructeur de D agrave un argument
D(C)
Opeacuterateur de cast dans C
operator D()
b A
C D
Les conversions de type deacutefinies par lrsquoutilisateurCHAPITRE 10
184
Parmi les diffeacuterentes possibiliteacutes de conversion que nous venons deacutevoquer seul lopeacuterateur
de cast appliqueacute agrave une classe apparaicirct comme nouveau Nous allons donc le preacutesenter mais
aussi expliquer quand et comment les diffeacuterentes conversions implicites sont mises en œuvre
et examiner les cas rejeteacutes par le compilateur
2 Lopeacuterateur de cast pour la conversion type classe ndashgt type de base
21 Deacutefinition de lopeacuterateur de castConsideacuterons une classe point
class point int x y
Supposez que nous souhaitions la munir dun opeacuterateur de cast permettant la conversion de
point en int Nous le noterons simplement
operator int()
Il sagit lagrave du meacutecanisme habituel de surdeacutefinition dopeacuterateur eacutetudieacute au chapitre preacuteceacutedent
lopeacuterateur se nomme ici int il est unaire (un seul argument) et comme il sagit dune fonc-
tion membre aucun argument napparaicirct dans son en-tecircte ou son prototype Reste la valeur de
retour en principe cet opeacuterateur fournit un int de sorte quon aurait pu penser agrave len-tecircte
int operator int()
En fait en C++ un opeacuterateur de cast doit toujours ecirctre deacutefini comme une fonction mem-
bre et le type de la valeur de retour (qui est alors celui deacutefini par le nom de lopeacuterateur) ne
doit pas ecirctre mentionneacute
En deacutefinitive voici comment nous pourrions deacutefinir notre opeacuterateur de cast (ici en ligne) en
supposant que le reacutesultat souhaiteacute pour la conversion en int soit labscisse du point
operator int() return x
Bien entendu pour ecirctre utilisable agrave lrsquoexteacuterieur de la classe cet opeacuterateur devra ecirctre public
22 Exemple dutilisationVoici un premier exemple de programme montrant agrave la fois un appel explicite de lopeacuterateur
int que nous venons de deacutefinir et un appel implicite entraicircneacute par une affectation1 Comme agrave
1 Srsquoil nrsquoeacutetait pas deacuteclareacute public on obtiendrait une erreur de compilation dans les deux appels
2 - Lopeacuterateur de cast pour la conversion type classe ndashgt type de base185
laccoutumeacutee nous avons introduit une instruction daffichage dans lopeacuterateur lui-mecircme
pour obtenir une trace de son appel
include ltiostreamgtusing namespace std class point int x y public point (int abs=0 int ord=0) constructeur 0 1 ou 2 arguments x = abs y = ord cout ltlt ++ construction point ltlt x ltlt ltlt y ltlt n operator int() cast point --gt int cout ltlt == appel int() pour le point ltlt x ltlt ltlt y ltlt n return x main() point a(34) b(57) int n1 n2 n1 = int(a) ou n1 = (int) a appel explicite de int () on peut aussi eacutecrire n1 = (int) a ou n1 = static_castltintgt (a) cout ltlt n1 = ltlt n1 ltlt n n2 = b appel implicite de int() cout ltlt n2 = ltlt n2 ltlt n
++ construction point 3 4++ construction point 5 7== appel int() pour le point 3 4n1 = 3== appel int() pour le point 5 7n2 = 5
Exemple dutilisation dun opeacuterateur de cast pour la conversion point -gt int
Nous voyons clairement que laffectation
n2 = b
a eacuteteacute traduite par le compilateur en
bull une conversion du point b en int
bull une affectation (classique) de la valeur obtenue agrave n2
23 Appel implicite de lopeacuterateur de cast lors drsquoun appel de fonctionDeacutefinissons une fonction fct recevant un argument de type entier que nous appelons
bull une premiegravere fois avec un argument entier (6)
Les conversions de type deacutefinies par lrsquoutilisateurCHAPITRE 10
186
bull une deuxiegraveme fois avec un argument de type point (a)
En outre nous introduisons (artificiellement) dans la classe point un constructeur de recopie
afin de montrer quici il nest pas appeleacute
include ltiostreamgtusing namespace std class point int x y public point (int abs=0 int ord=0) constructeur 0 1 ou 2 arguments x = abs y = ord cout ltlt ++ construction point ltlt x ltlt ltlt y ltlt n point (const point amp p) constructeur de recopie cout ltlt appel constructeur de recopie n x = px y = py operator int() cast point --gt int cout ltlt == appel int() pour le point ltlt x ltlt ltlt y ltlt n return x void fct (int n) fonction cout ltlt appel fct avec argument ltlt n ltlt n
main() void fct (int) point a(34) fct (6) appel normal de fct fct (a) appel avec conversion implicite de a en int
++ construction point 3 4 appel fct avec argument 6== appel int() pour le point 3 4 appel fct avec argument 3
Appel de lopeacuterateur de cast lors dun appel de fonction
On voit que lappel
fct(a)
a eacuteteacute traduit par le compilateur en
bull une conversion de a en int
bull un appel de fct agrave laquelle on fournit en argument la valeur ainsi obtenue
2 - Lopeacuterateur de cast pour la conversion type classe ndashgt type de base187
Comme on pouvait sy attendre la conversion est bien reacutealiseacutee avant lappel de la fonction et
il ny a pas de creacuteation par recopie dun objet de type point
24 Appel implicite de lopeacuterateur de cast dans leacutevaluation dune expression
Les reacutesultats de ce programme illustrent la maniegravere dont sont eacutevalueacutees des expressions telles
que a + 3 ou a + b lorsque a et b sont de type point
include ltiostreamgtusing namespace std class point int x y public point (int abs=0 int ord=0) constructeur 0 1 ou 2 arguments x = abs y = ord cout ltlt ++ construction point ltlt x ltlt ltlt y ltlt n operator int() cast point --gt int cout ltlt == appel int() pour le point ltlt x ltlt ltlt y ltlt n return x main() point a(34) b(57) int n1 n2 n1 = a + 3 cout ltlt n1 = ltlt n1 ltlt n n2 = a + b cout ltlt n2 = ltlt n2 ltlt n
double z1 z2 z1 = a + 3 cout ltlt z1 = ltlt z1 ltlt n z2 = a + b cout ltlt z2 = ltlt z2 ltlt n
++ construction point 3 4++ construction point 5 7== appel int() pour le point 3 4n1 = 6== appel int() pour le point 3 4== appel int() pour le point 5 7n2 = 8== appel int() pour le point 3 4z1 = 6== appel int() pour le point 3 4== appel int() pour le point 5 7z2 = 8
Utilisation de lopeacuterateur de cast dans leacutevaluation dune expression
Les conversions de type deacutefinies par lrsquoutilisateurCHAPITRE 10
188
Lorsquil rencontre une expression comme a + 3 avec un opeacuterateur portant sur un eacuteleacutement de
type point et un entier le compilateur recherche tout dabord sil existe un opeacuterateur + surdeacute-
fini correspondant agrave ces types dopeacuterandes Ici il nen trouve pas Il cherche alors agrave mettre en
place des conversions des opeacuterandes permettant daboutir agrave une opeacuteration existante Dans
notre cas il preacutevoit la conversion de a en int de maniegravere agrave se ramener agrave la somme de deux
entiers suivant le scheacutema
point int | | int | |___ + ___| | int
Certes une telle deacutemarche peut choquer Quelques remarques srsquoimposent
bull Ici aucune autre conversion nest envisageable Il nen irait pas de mecircme sil existait un opeacute-
rateur (surdeacutefini) daddition de deux points
bull La deacutemarche paraicirct moins choquante si lon ne cherche pas agrave donner une veacuteritable significa-
tion agrave lopeacuteration a + 3
bull Nous cherchons agrave preacutesenter les diffeacuterentes situations que lon risque de rencontrer non pas
pour vous encourager agrave les employer toutes mais plutocirct pour vous mettre en garde
Quant agrave leacutevaluation de a + b elle se fait suivant le scheacutema suivant
point point | | int int |___ + ___| | int
Pour chacune des deux expressions nous avons preacutevu deux sortes drsquoaffectation
bull agrave une variable entiegravere
bull agrave une variable de type double dans ce cas il y a conversion forceacutee du reacutesultat de lexpres-
sion en double
Notez bien que le type de la variable reacuteceptrice nagit aucunement sur la maniegravere dont
lexpression est eacutevalueacutee pas plus que sur son type final
25 Conversions en chaicircneConsideacuterez cet exemple
include ltiostreamgtusing namespace std
2 - Lopeacuterateur de cast pour la conversion type classe ndashgt type de base189
class point int x y public point (int abs=0 int ord=0) constructeur 0 1 ou 2 arguments x = abs y = ord cout ltlt ++ construction point ltlt x ltlt ltlt y ltlt n operator int() cast point --gt int cout ltlt == appel int() pour le point ltlt x ltlt ltlt y ltlt n return x void fct (double v) cout ltlt appel fct avec argument ltlt v ltlt n
main() point a(34) int n1 double z1 z2 n1 = a + 385 cout ltlt n1 = ltlt n1 ltlt n z1 = a + 385 cout ltlt z1 = ltlt z1 ltlt n z2 = a cout ltlt z2 = ltlt z2 ltlt n fct (a)
++ construction point 3 4== appel int() pour le point 3 4n1 = 6== appel int() pour le point 3 4z1 = 685== appel int() pour le point 3 4z2 = 3== appel int() pour le point 3 4 appel fct avec argument 3
Conversions en chaicircne
Cette fois nous avons agrave eacutevaluer agrave deux reprises la valeur de lexpression
a + 385
La diffeacuterence avec les situations preacuteceacutedentes est que la constante 385 est de type double et
non plus de type int Par analogie avec ce qui preacutecegravede on pourrait supposer que le compila-
teur preacutevoie la conversion de 385 en int Or il sagirait dune conversion dun type de base
double en un autre type de base int qui risquerait drsquoecirctre deacutegradante et qui comme dhabi-
tude nest jamais mise en œuvre de maniegravere implicite dans un calcul dexpression1
1 Elle pourrait lecirctre dans une affectation ou un appel de fonction en tant que conversion forceacutee
Les conversions de type deacutefinies par lrsquoutilisateurCHAPITRE 10
190
En fait leacutevaluation se fera suivant le scheacutema
point double | | int | | | double | |___ + ___| | double
La valeur afficheacutee pour z1 confirme le type double de lexpression
La valeur de ai a donc eacuteteacute soumise agrave deux conversions successives avant drsquoecirctre transmise agrave
un opeacuterateur Ceci est indeacutependant de lusage qui doit ecirctre fait ulteacuterieurement de la valeur de
lexpression agrave savoir
bull conversion en int pour affectation agrave n1 dans le premier cas
bull affectation agrave z2 dans le second cas
Quant agrave laffectation z2 = a elle entraicircne une double conversion de point en int puis de int en
double
Il en va de mecircme pour lappel
fct (a)
Dune maniegravere geacuteneacuterale
Remarque
Nous avons deacutejagrave rencontreacute ce meacutecanisme dans le cas des fonctions surdeacutefinies Ici il
sagit dun meacutecanisme comparable appliqueacute agrave un opeacuterateur preacutedeacutefini et non plus agrave une
fonction deacutefinie par lutilisateur Nous retrouverons des situations semblables par la
suite relatives cette fois agrave un opeacuterateur deacutefini par lutilisateur (donc agrave une fonction)
les regravegles appliqueacutees seront alors bien celles que nous avons eacutevoqueacutees dans la recher-
che de la bonne fonction surdeacutefinie
26 En cas dambiguiumlteacuteA partir du moment ougrave le compilateur accepte de mettre en place une chaicircne de conversions
certaines ambiguiumlteacutes peuvent apparaicirctre Reprenons lexemple de la classe point en suppo-
sant cette fois que nous lavons munie de deux opeacuterateurs de cast
En cas de besoin C++ peut ainsi mettre en œuvre une chaicircne de conver-sions agrave condition toutefois que celle-ci ne fasse intervenir qursquoune seule CDU (Conversion Deacutefinie par lrsquoUtilisateur) Plus preacuteciseacutement cette chaicircne peut ecirctre formeacutee drsquoau maximum trois conversions agrave savoir une conversion standard suivie drsquoune CDU suivie drsquoune conversion standard
3 - Le constructeur pour la conversion type de base -gt type classe191
operator int()operator double()
Supposons que nous utilisions de nouveau une expression telle que (a eacutetant de type point)
a + 385
Dans ce cas le compilateur se trouve en preacutesence de deux scheacutemas possibles de conversion
point double point double | | | | double | int | |____+____| | | | double | double |___ + ___| | double
Ici il refuse lexpression en fournissant un diagnostic dambiguiumlteacute
Cette ambiguiumlteacute reacuteside dans le fait que deux chaicircnes de conversions permettent de passer du
type point au type double Sil sagissait dune ambiguiumlteacute concernant le choix de lopeacuterateur agrave
appliquer (ce qui neacutetait pas le cas ici) le compilateur appliquerait alors les regravegles habituelles
de choix dune fonction surdeacutefinie1
3 Le constructeur pour la conversion type de base -gt type classe
31 ExempleNous avons deacutejagrave vu comment appeler explicitement un constructeur Par exemple avec la
classe point preacuteceacutedente si a est de type point nous pouvons eacutecrire
a = point (12)
Cette instruction provoque
bull la creacuteation dun objet temporaire de type point
bull lrsquoaffectation de cet objet agrave a
On peut donc dire que lexpression
point (12)
exprime la conversion de lentier 12 en un point
Dune maniegravere geacuteneacuterale tout constructeur agrave un seul argument dun type de base2 reacutealise une
conversion de ce type de base dans le type de sa classe
1 En toute rigueur il faudrait consideacuterer que les opeacuterateurs sur les types de base correspondent eux aussi agrave des
fonctions de la forme operator +
2 Ou eacuteventuellement comme cest le cas ici agrave plusieurs arguments ayant des valeurs par deacutefaut agrave partir du moment
ougrave il peut ecirctre appeleacute avec un seul argument
Les conversions de type deacutefinies par lrsquoutilisateurCHAPITRE 10
192
Or tout comme lopeacuterateur de cast ce constructeur peut eacutegalement ecirctre appeleacute implicitement
Ainsi laffectation
a = 12
provoque exactement le mecircme reacutesultat que
a = point (12)
A sa rencontre en effet le compilateur cherche sil existe une conversion (voire une chaicircne de con-
versions) unique permettant de passer du type int au type point Ici le constructeur fait laffaire
De la mecircme faccedilon si fct a pour prototype
void fct (point)
un appel tel que
fct (4)
entraicircne une conversion de lentier 4 en un point temporaire qui est alors transmis agrave fct
Voici un petit programme illustrant ces premiegraveres possibiliteacutes de conversion par un constructeur
include ltiostreamgt
using namespace std
class point
int x y
public
point (int abs=0 int ord=0) constructeur 0 1 ou 2 arguments
x = abs y = ord
cout ltlt ++ construction point ltlt x ltlt ltlt y
ltlt en ltlt this ltlt n
point (const point amp p) constructeur de recopie
x = px y = py
cout ltlt constr recopie de ltlt ampp ltlt en ltlt this ltlt n
void fct (point p) fonction simple
cout ltlt appel fct ltlt n
main()
void fct (point)
point a(34)
a = point (12) appel explicite constructeur
a = 12 appel implicite
fct(4)
++ construction point 3 4 en 006AFDF0
++ construction point 12 0 en 006AFDE8
++ construction point 12 0 en 006AFDE0
3 - Le constructeur pour la conversion type de base -gt type classe193
++ construction point 4 0 en 006AFD88
appel fct
Utilisation dun constructeur pour reacutealiser des conversions int ndashgt point
Remarques
1 Bien entendu si fct est surdeacutefinie le choix de la bonne fonction se fera suivant les regravegles
deacutejagrave rencontreacutees au chapitre 4 Cette fonction devra ecirctre unique de mecircme que les chaicircnes
de conversions mises en œuvre pour chaque argument
2 Si nous avions deacuteclareacute fct sous la forme void fct (point amp) lrsquoappel fct(4) serait rejeteacute
En revanche avec la deacuteclaration void fct (const point amp) ce mecircme appel serait
accepteacute il conduirait agrave la creacuteation drsquoun point temporaire obtenu par conversion de 4 en
point et agrave la transmission de sa reacutefeacuterence agrave la fonction fct Lrsquoexeacutecution se preacutesenterait
exactement comme ci-dessus
32 Le constructeur dans une chaicircne de conversionsSupposons que nous disposions dune classe complexe
class complexe
double reel imag
public
complexe (double r = 0 double i = 0)
Son constructeur permet des conversions double -gt complexe Mais compte tenu des possibi-
liteacutes de conversion implicite int -gt double ce constructeur peut intervenir dans une chaicircne de
conversions
int -gt double -gt complexe
Ce sera le cas dans une affectation telle que (c eacutetant de type complexe)
c = 3
Cette possibiliteacute de chaicircne de conversions rejoint ici les regravegles concernant les conversions
habituelles agrave propos des fonctions (surdeacutefinies ou non) En effet on peut consideacuterer ici que
lentier 3 est converti en double compte tenu du prototype de complexe Cette double inter-
preacutetation dune mecircme possibiliteacute nest pas gecircnante dans la mesure ougrave elle conduit dans les
deux cas agrave la mecircme conclusion concernant la faisabiliteacute de la conversion
33 Choix entre constructeur ou opeacuterateur daffectationDans lexemple daffectation
a = 12
Les conversions de type deacutefinies par lrsquoutilisateurCHAPITRE 10
194
du paragraphe 31 il nexistait pas dopeacuterateur drsquoaffectation dun int agrave un point Si tel est le
cas on peut penser que le compilateur doit alors choisir entre
bull utiliser la conversion int -gt point offerte par le constructeur suivie drsquoune affectation point -
gt point
bull sup2utiliser lopeacuterateur daffectation int -gt point
En fait une regravegle permet de trancher
Cest donc la seconde solution qui sera choisie ici par le compilateur comme le montre le
programme suivant Nous avons surdeacutefini lopeacuterateur drsquoaffectation non seulement dans le cas
intndashgt point mais aussi dans le cas point -gt point afin de bien montrer que cette derniegravere ver-
sion nest pas employeacutee dans laffectation a = 12
include ltiostreamgt
using namespace std
class point
int x y
public
point (int abs=0 int ord=0) constructeur 0 1 ou 2 arguments
x = abs y = ord
cout ltlt ++ construction point ltlt x ltlt ltlt y
ltlt en ltlt this ltlt n
point amp operator = (const point amp p) surdeacutef affectation point -gt point
x = px y = py
cout ltlt == affectation point --gt point de ltlt ampp ltlt en ltlt this
return this
point amp operator = (const int n) surdeacutef affectation int -gt point
x = n y = 0
cout ltlt == affectation int --gt point de ltlt x ltlt ltlt y
ltlt en ltlt this ltlt n
return this
main()
point a(34)
a = 12
++ construction point 3 4 en 006AFDF0
== affectation int --gt point de 12 0 en 006AFDF0
Les conversions deacutefinies par lutilisateur ne sont mises en œuvre que si neacutecessaire
Les conversions deacutefinies par lrsquoutilisateur (cast ou constructeur) ne sont mises en œuvre que lorsque cela est neacutecessaire
3 - Le constructeur pour la conversion type de base -gt type classe195
34 Emploi dun constructeur pour eacutelargir la signification dun opeacuterateur
Consideacuterons une classe point munie dun constructeur agrave un argument entier et dun opeacuterateur
daddition fourni sous la forme dune fonction amie (nous verrons un peu plus loin ce qui se
passerait dans le cas dune fonction membre)
class point int x y public point (int) friend point operator + (point point)
Dans ces conditions si a est de type point une expression telle que
a + 3
a une signification En effet dans ce cas le compilateur met en œuvre
bull une conversion de lentier 3 en point (par appel du constructeur)
bull laddition de la valeur obtenue avec celle de a (par appel de operator +)
Le reacutesultat sera du type point Le scheacutema suivant reacutecapitule la situation
point int | | | point |___ + ___| | point
On peut dire eacutegalement que notre expression a + 3 est eacutequivalente agrave
operator + (a point (3))
Le mecircme meacutecanisme sapplique agrave une expression telle que
5 + a
qui sera donc eacutequivalente agrave
operator + (5 a)
Toutefois dans ce dernier cas il nen serait pas alleacute de mecircme si notre opeacuterateur + avait eacuteteacute
deacutefini par une fonction membre En effet son premier opeacuterande aurait alors ducirc ecirctre de type
point aucune conversion implicite naurait pu ecirctre mise en place1
Voici un petit programme illustrant les possibiliteacutes que nous venons deacutevoquer
include ltiostreamgtusing namespace std class point int x y
1 Des appels tels que 5operator + (a) ou noperator + (a) (n eacutetant de type int) seront rejeteacutes
Les conversions de type deacutefinies par lrsquoutilisateurCHAPITRE 10
196
public point (int abs=0 int ord=0) constructeur 0 1 ou 2 arguments x = abs y = ord cout ltlt ++ construction point ltlt x ltlt ltlt y ltlt n friend point operator + (point point) point + point --gt point void affiche () cout ltlt Coordonnees ltlt x ltlt ltlt y ltlt n point operator+ (point a point b) point r rx = ax + bx ry = ay + by return r
main() point a b(94) a = b + 5 aaffiche() a = 2 + b baffiche()
++ construction point 0 0++ construction point 9 4++ construction point 5 0++ construction point 0 0Coordonneacutees 14 4++ construction point 2 0++ construction point 0 0Coordonneacutees 9 4
Elargissement de la signification de lopeacuterateur +
Remarques
1 On peut envisager de transmettre par reacutefeacuterence les arguments de operator Dans ce cas il
est neacutecessaire de preacutevoir le qualificatif const pour autoriser la conversion de 5 en un point
temporaire dans lrsquoaffectation a = b + 5 ou de 2 en un point temporaire dans a = 2 + b
2 Lrsquoutilisation du constructeur dans une chaicircne de conversions peut rendre de grands ser-
vices dans une situation reacuteelle puisquelle permet de donner un sens agrave des expressions
mixtes Lexemple le plus caracteacuteristique est celui dune classe de nombres complexes
(supposeacutes constitueacutes ici de deux valeurs de type double) Il suffit en effet de deacutefinir la
somme de deux complexes et un constructeur agrave un argument de type double
3 - Le constructeur pour la conversion type de base -gt type classe197
class complexe double reel imag public complexe (double v) reel = v imag = 0 friend complexe operator + (complexe complexe)
Les expressions de la forme
ndash complexe + double
ndash double + complexe
auront alors une signification (et ici ce sera bien celle que lon souhaite)
Compte tenu des possibiliteacutes de conversions il en ira de mecircme de nimporte quelle
addition dun complexe et dun float dun long dun short ou dun char
Ici encore ces conversions ne seront plus possibles si les opeacuterandes sont transmis par
reacutefeacuterence Elles le redeviendront avec des reacutefeacuterences agrave des constantes
3 Si nous avions deacutefini
class complexe float reel imag public complexe (float v) friend complexe operator + (complexe complexe)
laddition dun complexe et dun double ne serait pas possible Elle le deviendrait en
remplaccedilant le constructeur par
complexe (double v)
(ce qui ne signifie pas pour autant que le reacutesultat de la conversion forceacutee de double en
float qui y figurera sera acceptable )
35 Interdire les conversions implicites par le constructeur le rocircle drsquoexplicit
La norme ANSI de C++ preacutevoit quon puisse interdire lutilisation du constructeur dans des
conversions implicites (simples ou en chaicircne) en utilisant le mot cleacute explicit lors de sa deacutecla-
ration Par exemple avec
class point public explicit point (int) friend operator + (point point)
les instructions suivantes seraient rejeteacutees (a et b eacutetant de type point)
Les conversions de type deacutefinies par lrsquoutilisateurCHAPITRE 10
198
a = 12 illeacutegal car le constructeur possegravede le qualificatif explicita = b + 5 idem
En revanche la conversion pourrait toujours se faire par un appel explicite comme dans
a = point (3) OK conversion explicite par le constructeura = b + point (5) idem
4 Les conversions drsquoun type classe en un autre type classe
Les possibiliteacutes de conversions dun type de base en un type classe que nous venons drsquoeacutetudier
se geacuteneacuteralisent ainsi
bull Au sein dune classe A on peut deacutefinir un opeacuterateur de cast reacutealisant la conversion dans le
type A dun autre type de classe B
bull Un constructeur de la classe A recevant un argument de type B reacutealise une conversion de B
en A
41 Exemple simple dopeacuterateur de castLe programme suivant illustre la premiegravere situation lopeacuterateur complexe de la classe point
permet des conversions dun objet de type point en un objet de type complexe
include ltiostreamgtusing namespace std class complexe
class point int x y public point (int abs=0 int ord=0) x=abs y=ord operator complexe () conversion point --gt complexe
class complexe double reel imag public complexe (double r=0 double i=0) reel=r imag=i friend pointoperator complexe () void affiche () cout ltlt reel ltlt + ltlt imag ltltin pointoperator complexe () complexe r rreel=x rimag=y cout ltlt cast ltltxltlt ltltyltlt en ltltrreelltlt + ltltrimagltltin return r
4 - Les conversions drsquoun type classe en un autre type classe199
main() point a(25) complexe c c = (complexe) a caffiche () conversion explicite point b (912) c = b caffiche () conversion implicite
cast 2 5 en 2 + 5i2 + 5icast 9 12 en 9 + 12i9 + 12i
Exemple dutilisation dun opeacuterateur de cast pour des conversions point -gt complexe
Remarque
La conversion point ndashgt complexe eacutequivalente ici agrave la conversion de deux entiers en reacuteel
est assez naturelle et de toute faccedilon non deacutegradante Mais bien entendu C++ vous laisse
seul juge de la qualiteacute des conversions que vous pouvez deacutefinir de cette maniegravere
42 Exemple de conversion par un constructeurLe programme suivant illustre la seconde situation le constructeur complexe (point) repreacute-
sente une autre faccedilon de reacutealiser des conversions dun objet de type point en un objet de type
complexe
include ltiostreamgtusing namespace std class point class complexe double reel imag public complexe (double r=0 double i=0) reel=r imag=i complexe (point) void affiche () cout ltlt reel ltlt + ltlt imag ltlt in class point int x y public point (int abs=0 int ord=0) x=abs y=ord friend complexecomplexe (point) complexecomplexe (point p) reel = px imag = py
Les conversions de type deacutefinies par lrsquoutilisateurCHAPITRE 10
200
main()
point a(35)
complexe c (a) caffiche ()
3 + 5i
Exemple dutilisation dun constructeur pour des conversions point ndashgt complexe
Remarques
1 La remarque faite preacuteceacutedemment agrave propos de la qualiteacute des conversions sapplique tout
aussi bien ici Par exemple nous aurions pu introduire dans la classe point un construc-
teur de la forme point (complexe)
2 En ce qui concerne les conversions dun type de base en une classe la seule possibiliteacute
qui nous eacutetait offerte consistait agrave preacutevoir un constructeur approprieacute au sein de la classe
En revanche pour les conversions A ndashgt B (ougrave A et B sont deux classes) nous avons le
choix entre placer dans B un constructeur B(A) ou placer dans A un opeacuterateur de cast
B()
3 Il nest pas possible de deacutefinir simultaneacutement la mecircme conversion A ndashgt B en preacutevoyant
agrave la fois un constructeur B(A) dans B et un cast B() dans A En effet cela conduirait le
compilateur agrave deacuteceler une ambiguiumlteacute degraves quune conversion de A en B serait neacutecessaire
Il faut signaler cependant quune telle anomalie peut rester cacheacutee tant que le besoin
dune telle conversion ne se fait pas sentir (en particulier les classes A et B seront com-
pileacutees sans problegraveme y compris si elles figurent dans le mecircme fichier source)
43 Pour donner une signification agrave un opeacuterateur deacutefini dans une autre classe
Consideacuterons une classe complexe pour laquelle lopeacuterateur + a eacuteteacute surdeacutefini par une fonction
amie1 ainsi quune classe point munie dun opeacuterateur de cast complexe() Supposons a de
type point x de type complexe et consideacuterons lexpression
x + a
Compte tenu des regravegles habituelles relatives aux fonctions surdeacutefinies (mise en œuvre dune
chaicircne unique de conversions ne contenant pas plus dune CDU) le compilateur est conduit
agrave eacutevaluer cette expression suivant le scheacutema
1 Nous verrons que ce point est important on nobtiendrait pas les mecircmes possibiliteacutes avec une fonction membre
4 - Les conversions drsquoun type classe en un autre type classe201
complexe point | | | complexe |___ + ___| | complexe
Celui-ci fait intervenir lopeacuterateur + surdeacutefini par la fonction indeacutependante operator+ On
peut dire que lrsquoexpression x + a est en fait eacutequivalente agrave
operator + (x a)
Le mecircme raisonnement sapplique agrave lexpression a + x Quant agrave lexpression
a + b
ougrave a et b sont de type point elle est eacutequivalente agrave
operator + (a b)
et eacutevalueacutee suivant le scheacutema
point point | | complexe complexe |____ + ____| | complexe
Voici un exemple complet de programme illustrant ces possibiliteacutes
include ltiostreamgtusing namespace std class complexe class point int x y public point (int abs=0 int ord=0) x=abs y=ord operator complexe () void affiche () cout ltlt point ltlt x ltlt ltlt y ltlt n class complexe double reel imag public complexe (double r=0 double i=0) reel=r imag=i void affiche () cout ltlt reel ltlt + ltlt imag ltlt i n friend pointoperator complexe () friend complexe operator + (complexe complexe) pointoperator complexe () complexe r rreel = x rimag = y return r complexe operator + (complexe a complexe b) complexe r rreel = areel + breel rimag = aimag + bimag return r
Les conversions de type deacutefinies par lrsquoutilisateurCHAPITRE 10
202
main() point a(34) b(79) c complexe x(3528) y y = x + a yaffiche () marcherait encore si + eacutetait fct membre y = a + x yaffiche () ne marcherait pas si + eacutetait fonction membre y = a + b yaffiche () ne marcherait pas si + eacutetait fonction membre (voir remarque) NB c = a + b naurait pas de sens ici
65 + 68i 65 + 68i 10 + 13i
Elargissement de la signification de lopeacuterateur + de la classe complexe
Remarques
1 Sil est effectivement possible ici deacutecrire
y = a + b
il nest pas possible deacutecrire
c = a + b
car il nexiste pas de conversion de complexe (type de lexpression a + b) en point
Pour que cela soit possible il suffirait par exemple dintroduire dans la classe point un
constructeur de la forme point (complexe) Bien entendu cela ne preacutejuge nullement de
la signification dune telle opeacuteration et en particulier de son aspect deacutegradant
2 Si lopeacuterateur + de la classe complexe avait eacuteteacute deacutefini par une fonction membre de
prototype
complexe complexeoperator + (complexe)
lexpression a + x naurait pas eu de sens pas plus que a + b En effet dans le premier
cas lappel de operator + naurait pu ecirctre que
aoperator + (x)
Cela naurait pas eacuteteacute permis En revanche lexpression x + a aurait pu correctement ecirctre
eacutevalueacutee comme
xoperator + (a)
3 Il nest pas toujours aussi avantageux que dans cet exemple de deacutefinir un opeacuterateur sous
la forme dune fonction amie En particulier si un opeacuterateur modifie son premier opeacute-
rande (supposeacute ecirctre un objet) il est preacutefeacuterable den faire une fonction membre Dans le
cas contraire en effet on risque de voir cet opeacuterateur agir non pas sur lobjet concerneacute
mais sur un objet (ou une variable) temporaire dun autre type creacuteeacute par une conversion
5 - Quelques conseils203
implicite1 Crsquoest dailleurs pour cette raison que C++ impose que les opeacuterateurs = [] ()
et -gt soient toujours surdeacutefinis par des fonctions membres
4 On peut envisager de transmettre les arguments de operator+ par reacutefeacuterence Dans ce
cas si lrsquoon nrsquoa pas preacutevu le qualificatif const dans leur deacuteclaration dans lrsquoen-tecircte les
trois expressions x+a a+x et a+b conduisent agrave une erreur de compilation
5 Quelques conseilsLes possibiliteacutes de conversions implicites ne sont certes pas infinies puisquelles sont limi-
teacutees agrave une chaicircne dau maximum trois conversions (standard CDU standard) et que la
CDU nest mise en œuvre que si elle est utile
Elles nen restent pas moins tregraves (trop ) riches Une telle richesse peut laisser craindre que
certaines conversions soient mises en place sans que le concepteur des classes concerneacutees ne
lait souhaiteacute
En fait il faut bien voir que
bull lopeacuterateur de cast doit ecirctre introduit deacutelibeacutereacutement par le concepteur de la classe
bull le concepteur dune classe peut interdire lusage implicite du constructeur dans une conver-
sion en faisant appel au mot cleacute explicit
Il est donc possible de se proteacuteger totalement contre lusage des conversions implicites relati-
ves aux classes il suffira de qualifier tous les constructeurs avec explicit et de ne pas intro-
duire dopeacuterateur de cast
Dune maniegravere geacuteneacuterale on aura inteacuterecirct agrave reacuteserver ces possibiliteacutes de conversions implicites agrave
des classes ayant une forte connotation matheacutematique dans lesquelles on aura probable-
ment surdeacutefini un certain nombre dopeacuterateurs (+ - etc)
Lexemple le plus classique est certainement celui de la classe complexe (que nous avons ren-
contreacutee dans ce chapitre) Dans ce cas il paraicirct naturel de disposer de conversions de com-
plexe en float de float en complexe de int en complexe (par le biais de float) etc
De mecircme il paraicirct naturel de pouvoir reacutealiser aussi bien la somme dun complexe et dun float
que celle de deux complexes et donc de profiter des possibiliteacutes de conversions implicites
pour ne deacutefinir quun seul opeacuterateur daddition (celle de deux complexes)
1 Aucune conversion implicite ne peut avoir lieu sur lobjet appelant une fonction membre
11
Les patrons de fonctions
Nous avons deacutejagrave vu que la surdeacutefinition de fonctions permettait de donner un nom unique agrave
plusieurs fonctions reacutealisant un travail diffeacuterent La notion de patron de fonction (on parle
aussi de fonction geacuteneacuterique ou de modegravele de fonction) introduite par la version 3 est agrave la
fois plus puissante et plus restrictive plus puissante car il suffit deacutecrire une seule fois la
deacutefinition dune fonction pour que le compilateur puisse automatiquement ladapter agrave
nimporte quel type plus restrictive puisque toutes les fonctions ainsi fabriqueacutees par le com-
pilateur doivent correspondre agrave la mecircme deacutefinition donc au mecircme algorithme
Nous commencerons par vous preacutesenter cette nouvelle notion agrave partir drsquoun exemple simple
ne faisant intervenir quun seul paramegravetre de type Nous verrons ensuite quelle se geacuteneacutera-
lise agrave un nombre quelconque de paramegravetres et quon peut eacutegalement faire intervenir des
paramegravetres expressions Puis nous montrerons comment un patron de fonctions peut agrave son
tour ecirctre surdeacutefini Enfin nous verrons que toutes ces possibiliteacutes peuvent encore ecirctre affi-
neacutees en speacutecialisant une ou plusieurs des fonctions dun patron
NB On rencontre souvent le terme anglais template au lieu de celui de patron On parle alors
de fonctions template ou de classes template mais dans ce cas il est difficile de distin-
guer comme nous serons ameneacutes agrave le faire un patron de fonctions drsquoune fonction patron ou
un patron de classes drsquoune classe patron
Les patrons de fonctionsCHAPITRE 11
206
1 Exemple de creacuteation et drsquoutilisation drsquoun patron de fonctions
11 Creacuteation dun patron de fonctionsSupposons que nous ayons besoin deacutecrire une fonction fournissant le minimum de deux valeurs
de mecircme type reccedilues en arguments Nous pourrions eacutecrire une deacutefinition pour le type int
int min (int a int b) if (a lt b) return a ou return a lt b a b else return b
Bien entendu il nous faudrait probablement eacutecrire une autre deacutefinition pour le type float
cest-agrave-dire (en supposant que nous lui donnions le mecircme nom min ce que nous avons tout
inteacuterecirct agrave faire)
float min (float a float b) if (a lt b) return a ou return a lt b a b else return b
Nous aurions ainsi agrave eacutecrire de nombreuses deacutefinitions tregraves proches les unes des autres En
effet seul le type concerneacute serait ameneacute agrave ecirctre modifieacute
En fait nous pouvons simplifier consideacuterablement les choses en deacutefinissant un seul patron
de fonctions de la maniegravere suivante
creacuteation dun patron de fonctionstemplate ltclass Tgt T min (T a T b) if (a lt b) return a ou return a lt b a b else return b
Creacuteation dun patron de fonctions
Comme vous le constatez seul len-tecircte de notre fonction a changeacute (il nen ira pas toujours
ainsi)
template ltclass Tgt T min (T a T b)
La mention template ltclass Tgt preacutecise que lon a affaire agrave un patron (template) dans lequel
apparaicirct un paramegravetre1 de type nommeacute T Notez que C++ a deacutecideacute demployer le mot cleacute
1 Ou argument ici nous avons convenu demployer le terme paramegravetre pour les patrons et le terme argument pour
les fonctions mais il ne sagit aucunement dune convention universelle
1 - Exemple de creacuteation et drsquoutilisation drsquoun patron de fonctions207
class pour preacuteciser que T est un paramegravetre de type (on aurait preacutefeacutereacute le mot cleacute type ) Autre-
ment dit dans la deacutefinition de notre fonction T repreacutesente un type quelconque
Le reste de len-tecircte
T min (T a T b)
preacutecise que min est une fonction recevant deux arguments de type T et fournissant un reacutesultat
du mecircme type
Remarque
Dans la deacutefinition drsquoun patron on utilise le mot cleacute class pour indiquer en fait un type
quelconque classe ou non La norme a introduit le mot cleacute typename qui peut se subtituer
agrave class dans la deacutefinition
template lttypename Tgt T min (T a T b) idem template ltclass Tgt
Cependant son arriveacutee tardive fait que la plupart des programmes continuent drsquoutiliser
le mot cleacute class dans ce cas
12 Premiegraveres utilisations du patron de fonctionsPour utiliser le patron min que nous venons de creacuteer il suffit dutiliser la fonction min dans
des conditions approprieacutees (cest-agrave-dire ici deux arguments de mecircme type) Ainsi si dans un
programme dans lequel n et p sont de type int nous faisons intervenir lexpression min (n p)
le compilateur fabriquera (on dit aussi instanciera) automatiquement la fonction min (dite
fonction patron1) correspondant agrave des arguments de type int Si nous appelons min avec
deux arguments de type float le compilateur fabriquera automatiquement une autre fonc-
tion patron min correspondant agrave des arguments de type float et ainsi de suite
Comme on peut sy attendre il est neacutecessaire que le compilateur dispose de la deacutefinition du
patron en question autrement dit que les instructions preacuteceacutedentes apparaissent avant une
quelconque utilisation de min Voici un exemple complet illustrant cela
include ltiostreamgtusing namespace std creacuteation dun patron de fonctionstemplate ltclass Tgt T min (T a T b) if (a lt b) return a ou return a lt b a b else return b
1 Attention au vocabulaire patron de fonction pour la fonction geacuteneacuterique fonction patron pour une instance
donneacutee
Les patrons de fonctionsCHAPITRE 11
208
exemple dutilisation du patron de fonctions minmain() int n=4 p=12 float x=25 y=325 cout ltlt min (n p) = ltlt min (n p) ltlt n int min(int int) cout ltlt min (x y) = ltlt min (x y) ltlt n float min (float float)
min (n p) = 4min (x y) = 25
Deacutefinition et utilisation dun patron de fonctions
13 Autres utilisations du patron de fonctionsLe patron min peut ecirctre utiliseacute pour des arguments de nimporte quel type quil sagisse dun
type preacutedeacutefini (short char double int char int etc) ou dun type deacutefini par lutilisa-
teur (notamment structure ou classe)
Par exemple si n et p sont de type int un appel tel que min (ampn ampp) conduit le compilateur
agrave instancier une fonction int min (int int )
Examinons plus en deacutetail deux situations preacutecises
bull arguments de type char
bull arguments de type classe
131 Application au type char Voici un premier exemple dans lequel nous exploitons le patron min pour fabriquer une fonc-
tion portant sur des chaicircnes de caractegraveres
include ltiostreamgtusing namespace std template ltclass Tgt T min (T a T b) if (a lt b) return a ou return a lt b a b else return b main() char adr1 = monsieur adr2 = bonjour cout ltlt min (adr1 adr2) = ltlt min (adr1 adr2)
min (adr1 adr2) = monsieur
Application du patron min au type char
1 - Exemple de creacuteation et drsquoutilisation drsquoun patron de fonctions209
Le reacutesultat peut surprendre si vous vous attendiez agrave ce que min fournisse la chaicircne bon-
jour En fait agrave la rencontre de lexpression min (adr1 adr2) le compilateur a geacuteneacutereacute la fonc-
tion suivante
char min (char a char b)
if (a lt b) return a
else return b
La comparaison altb porte donc sur les valeurs des pointeurs reccedilus en argument (ici a eacutetait
infeacuterieur agrave b mais il peut en aller autrement dans dautres impleacutementations) En revanche
laffichage obtenu par lopeacuterateur ltlt porte non plus sur ces adresses mais sur les chaicircnes
situeacutees agrave ces adresses
132 Application agrave un type classe
Pour pouvoir appliquer le patron min agrave une classe il est bien sucircr neacutecessaire que lopeacuterateur lt
puisse sappliquer agrave deux opeacuterandes de ce type classe Voici un exemple dans lequel nous
appliquons min agrave deux objets de type vect classe munie drsquoun opeacuterateur lt fournissant un
reacutesultat baseacute sur le module des vecteurs
include ltiostreamgt
using namespace std
le patron de fonctions mintemplate ltclass Tgt T min (T a T b)
if (a lt b) return a
else return b
la classe vect
class vect int x y
public vect (int abs=0 int ord=0) x=abs y=ord
void affiche () cout ltlt x ltlt ltlt y friend int operator lt (vect vect)
int operator lt (vect a vect b) return axax + ayay lt bxbx + byby
un exemple dutilisation de minmain()
vect u (3 2) v (4 1) w w = min (u v)
cout ltlt min (u v) = waffiche()
Les patrons de fonctionsCHAPITRE 11
210
min (u v) = 3 2
Utilisation du patron min pour la classe vect
Naturellement si nous cherchons agrave appliquer notre patron min agrave une classe pour laquelle
lopeacuterateur lt nest pas deacutefini le compilateur le signalera exactement de la mecircme maniegravere que
si nous avions eacutecrit nous-mecircme la fonction min pour ce type
Remarque
Un patron de fonctions pourra sappliquer agrave des classes patrons cest-agrave-dire agrave un type de
classe instancieacute par un patron de classe Nous en verrons des exemples dans le prochain
chapitre
14 Contraintes drsquoutilisation drsquoun patronLes instructions de deacutefinition dun patron ressemblent agrave des instructions exeacutecutables de deacutefi-
nition de fonction Neacuteanmoins le meacutecanisme mecircme des patrons fait que ces instructions sont
utiliseacutees par le compilateur pour fabriquer (instancier) chaque fois quil est neacutecessaire les ins-
tructions correspondant agrave la fonction requise en ce sens ce sont donc des deacuteclarations leur
preacutesence est toujours neacutecessaire et il nest pas possible de creacuteer un module objet correspon-
dant agrave un patron de fonctions
Tout se passe en fait comme si avec la notion de patron de fonctions apparaissaient deux
niveaux de deacuteclarations On retrouvera le mecircme pheacutenomegravene pour les patrons de classes Par
la suite nous continuerons agrave parler de deacutefinition dun patron
En pratique on placera les deacutefinitions de patron dans un fichier approprieacute dextension h
Remarque
Les consideacuterations preacuteceacutedentes doivent en fait ecirctre pondeacutereacutees par le fait que la norme a
introduit le mot cleacute export Appliqueacute agrave la deacutefinition drsquoun patron il preacutecise que celle-ci
sera accessible depuis un autre fichier source Par exemple en eacutecrivant ainsi notre patron
de fonctions min du paragraphe 11
export template ltclass Tgt T min (T a T b) if (a lt b) return a ou return a lt b a b else return b
on peut alors utiliser ce patron depuis un autre fichier source en se contentant de men-
tionner sa deacuteclaration (cette fois il srsquoagit bien drsquoune veacuteritable deacuteclaration et non plus
drsquoune deacutefinition)
2 - Les paramegravetres de type drsquoun patron de fonctions211
template ltclass Tgt T min (T a T b) deacuteclaration seule de min
min (x y)
En pratique on aura alors inteacuterecirct agrave preacutevoir deux fichiers en-tecirctes distincts un pour la
deacuteclaration un pour la deacutefinition On pourra agrave volonteacute inclure le premier dans la deacutefi-
nition du patron ou dans son utilisation
Ce meacutecanisme met en jeu une sorte de preacutecompilation des deacutefinitions de patrons
2 Les paramegravetres de type drsquoun patron de fonctions
Ce paragraphe fait le point sur la maniegravere dont les paramegravetres de type peuvent intervenir dans
un patron de fonctions sur lalgorithme qui permet au compilateur dinstancier la fonction
voulue et sur les problegravemes particuliers quil peut poser
Notez quun patron de fonctions peut eacutegalement comporter ce que lon nomme des paramegrave-
tres expressions qui correspondent en fait agrave la notion usuelle dargument dune fonction Ils
seront eacutetudieacutes au paragraphe suivant
21 Utilisation des paramegravetres de type dans la deacutefinition dun patron
Un patron de fonctions peut donc comporter un ou plusieurs paramegravetres de type chacun
devant ecirctre preacuteceacutedeacute du mot cleacute class par exemple
template ltclass T class Ugt fct (T a T b U c)
Ces paramegravetres peuvent intervenir agrave nimporte quel endroit de la deacutefinition dun patron1
bull dans len-tecircte (ceacutetait le cas des exemples preacuteceacutedents)
bull dans des deacuteclarations2 de variables locales (de lun des types des paramegravetres)
bull dans les instructions exeacutecutables3 (par exemple new sizeof ())
1 De la mecircme maniegravere quun nom de type peut intervenir dans la deacutefinition dune fonction
2 Il sagit alors de deacuteclarations au sein de la deacutefinition du patron cest-agrave-dire finalement de deacuteclarations au sein de
deacuteclarations
3 Nous parlons dinstructions exeacutecutables bien quil sagisse toujours de deacuteclarations (puisque la deacutefinition dun
patron est une deacuteclaration) En toute rigueur ces instructions donneront naissance agrave des instructions exeacutecutables agrave
chaque instanciation dune nouvelle fonction
Les patrons de fonctionsCHAPITRE 11
212
En voici un exemple
template ltclass T class Ugt fct (T a T b U c)
T x variable locale x de type T
U adr variable locale adr de type U
adr = new T [10] allocation tableau de 10 eacuteleacutements de type T
n = sizeof (T)
Dans tous les cas il est neacutecessaire que chaque paramegravetre de type apparaisse au moins une
fois dans len-tecircte du patron comme nous le verrons cette condition est parfaitement logi-
que puisque cest preacuteciseacutement gracircce agrave la nature de ces arguments que le compilateur est en
mesure dinstancier correctement la fonction neacutecessaire
22 Identification des paramegravetres de type dune fonction patronLes exemples preacuteceacutedents eacutetaient suffisamment simples pour que lon devine quelle eacutetait la
fonction instancieacutee pour un appel donneacute Mais reprenons le patron min
template ltclass Tgt T min (T a T b)
if (a lt b) return a else return b
avec ces deacuteclarations
int n char c
Que va faire le compilateur en preacutesence dun appel tel que min (nc) ou min(cn) En fait la
regravegle preacutevue par C++ dans ce cas est quil doit y avoir correspondance absolue des types
Cela signifie que nous ne pouvons utiliser le patron min que pour des appels dans lesquels les
deux arguments ont le mecircme type Manifestement ce nest pas le cas dans nos deux appels
qui aboutiront agrave une erreur de compilation On notera que dans cette correspondance abso-
lue les eacuteventuels qualifieurs const ou volatile interviennent
Voici quelques exemples dappels de min qui preacutecisent quelle sera la fonction instancieacutee lors-
que lappel est correct
int n char c unsigned int q
const int ci1 = 10 ci2 = 12
int t[10]
int adi
min (n c) erreur
min (n q) erreurmin (n ci1) erreur const int et int ne correspondent pas
min (ci1 ci2) min (const int const int)
min (t adi) min (int int ) car ici t est converti
en int avant appel
2 - Les paramegravetres de type drsquoun patron de fonctions213
Il est cependant possible drsquointervenir sur ce meacutecanisme drsquoidentification de type En effet
C++ vous autorise agrave speacutecifier un ou plusieurs paramegravetres de type au moment de lrsquoappel du
patron Voici quelques exemples utilisant les deacuteclarations preacuteceacutedentes
minltintgt (c n) force lutilisation de minltintgt et donc la conversion
de c en int le reacutesultat sera de type int
minltchargt (q n) force lutilisation de minltchargt et donc la conversion de q et de n en char le reacutesultat sera de type char
Voici un autre exemple faisant intervenir plusieurs paramegravetres de type
template ltclass T class Ugt T fct (T x U y T z) return x + y + z
main () int n = 1 p = 2 q = 3
float x = 25 y = 50
cout ltlt fct (n x p) ltlt n affiche la valeur (int) 5 cout ltlt fct (x n y) ltlt n affiche la valeur (float) 85
cout ltlt fct (n p q) ltlt n affiche la valeur (int) 6
cout ltlt fct (n p x) ltlt n erreur pas de correspondance
Ici encore on peut forcer certains des paramegravetres de type comme dans ces exemples
fctltintfloatgt (n p x) force lutilisation de fctltintfloatgt et donc la conversion de p en float et de x en int
fctltfloatgt (n p x ) force lutilisation de float pour T U est deacutetermineacute par les regravegles habituelles
crsquoest-agrave-dire int (type de p)
n sera converti en float
Remarque
Le mode de transmission drsquoun paramegravetre (par valeur ou par reacutefeacuterence) ne joue aucun rocircle
dans lrsquoidentification des paramegravetres de type Cela va de soi puisque
bull drsquoune part ce mode ne peut pas ecirctre deacuteduit de la forme de lrsquoappel
bull drsquoautre part la notion de conversion nrsquoa aucune signfication ici elle ne peut donc pas in-
tervenir pour trancher entre une reacutefeacuterence et une reacutefeacuterence agrave une constante
23 Nouvelle syntaxe dinitialisation des variables des types standard
Dans un patron de fonctions un paramegravetre de type est susceptible de correspondre tantocirct agrave un
type standard tantocirct agrave un type classe Un problegraveme apparaicirct donc si lon doit deacuteclarer au sein
du patron un objet de ce type en transmettant un ou plusieurs arguments agrave son constructeur
Les patrons de fonctionsCHAPITRE 11
214
Consideacuterons cet exemple
template ltclass Tgt fct (T a)
T x (3) x est un objet local de type T quon construit
en transmettant la valeur 3 agrave son constructeur
Tant que lon utilise une fonction fct pour un type classe tout va bien En revanche si lon
cherche agrave lrsquoutiliser pour un type standard par exemple int le compilateur geacuteneacutere la fonction
suivante
fct (int a)
int x (3)
Pour que linstruction int x(3) ne pose pas de problegraveme C++ a preacutevu quelle soit simplement
interpreacuteteacutee comme une initialisation de x avec la valeur 3 cest-agrave-dire comme
int x = 3
En theacuteorie cette possibiliteacute est utilisable dans nimporte quelle instruction C++ de sorte que
vous pouvez tregraves bien eacutecrire
double x(35) au lieu de double x = 35
char c(e) au lieu de char c = e
En pratique cela sera rarement utiliseacute de cette faccedilon
24 Limitations des patrons de fonctionsLorsque lon deacutefinit un patron de classe agrave un paramegravetre de type peut theacuteoriquement corres-
pondre nimporte quel type effectif (standard ou classe) Il nexiste a priori aucun meacutecanisme
intrinsegraveque permettant dinterdire linstanciation pour certains types
Ainsi si un patron a un en-tecircte de la forme
template ltclass Tgt void fct (T)
on pourra appeler fct avec un argument de nimporte quel type int float int int t t ou
mecircme t (t deacutesignant un type classe quelconque)
Cependant un certain nombre deacuteleacutements peuvent intervenir indirectement pour faire
eacutechouer linstanciation
Tout dabord on peut imposer quun paramegravetre de type corresponde agrave un pointeur Ainsi avec
un patron den-tecircte
template ltclass Tgt void fct (T )
on ne pourra appeler fct quavec un pointeur sur un type quelconque int int t ou t
Dans les autres cas on aboutira agrave une erreur de compilation
Par ailleurs dans la deacutefinition dun patron peuvent apparaicirctre des instructions qui saveacutereront
incorrectes lors de la tentative dinstanciation pour certains types
3 - Les paramegravetres expressions drsquoun patron de fonctions215
Par exemple le patron min
template ltclass Tgt T min (T a T b) if (a lt b) return a else return b
ne pourra pas sappliquer si T correspond agrave un type classe dans lequel lopeacuterateur lt na pas eacuteteacute
surdeacutefini
De mecircme un patron comme
template ltclass Tgt void fct (T) T x (2 5) objet local de type T initialiseacute par un constructeur agrave 2 arguments
ne pourra pas sappliquer agrave un type classe pour lequel nexiste pas un constructeur agrave deux
arguments
En deacutefinitive bien quil nexiste pas de meacutecanisme formel de limitation les patrons de fonc-
tions peuvent neacuteanmoins comporter dans leur deacutefinition mecircme un certain nombre deacuteleacutements
qui en limiteront la porteacutee
3 Les paramegravetres expressions drsquoun patron de fonctions
Comme nous lavons deacutejagrave eacutevoqueacute un patron de fonctions peut comporter des paramegravetres
expressions1 crsquoest-agrave-dire des paramegravetres (muets) ordinaires analogues agrave ceux quon
trouve dans la deacutefinition dune fonction Consideacuterons cet exemple dans lequel nous deacutefinis-
sons un patron nommeacute compte permettant de fabriquer des fonctions comptabilisant le nom-
bre deacuteleacutements nuls dun tableau de type et de taille quelconques
include ltiostreamgtusing namespace std template ltclass Tgt int compte (T tab int n) int i nz=0 for (i=0 iltn i++) if (tab[i]) nz++ return nz
main () int t [5] = 5 2 0 2 0 char c[6] = 0 12 0 0 0 5 cout ltlt compte (t) = ltlt compte (t 5) ltlt n cout ltlt compte (c) = ltlt compte (c 6) ltlt n
1 Cette possibiliteacute a eacuteteacute introduite par la norme ANSI
Les patrons de fonctionsCHAPITRE 11
216
compte (t) = 2
compte (c) = 4
Exemple de patron de fonctions comportant un paramegravetre expression (n)
On peut dire que le patron compte deacutefinit une famille de fonctions compte dans laquelle le
type du premier argument est variable (et donc deacutefini par lappel) tandis que le second est de
type imposeacute (ici int) Comme on peut sy attendre dans un appel de compte seul le type du
premier argument intervient dans le code de la fonction instancieacutee
Dune maniegravere geacuteneacuterale un patron de fonctions peut disposer dun ou de plusieurs paramegravetres
expressions Lors de lappel leur type na plus besoin de correspondre exactement agrave celui
attendu il suffit quil soit acceptable par affectation comme dans nimporte quel appel dune
fonction ordinaire
4 Surdeacutefinition de patronsDe mecircme quil est possible de surdeacutefinir une fonction classique il est possible de surdeacutefinir
un patron de fonctions cest-agrave-dire de deacutefinir plusieurs patrons posseacutedant des arguments dif-
feacuterents On notera que cette situation conduit en fait agrave deacutefinir plusieurs familles de fonc-
tions (il y a bien plusieurs deacutefinitions de familles et non plus simplement plusieurs
deacutefinitions de fonctions) Elle ne doit pas ecirctre confondue avec la speacutecialisation dun patron de
fonctions qui consiste agrave surdeacutefinir une ou plusieurs des fonctions de la famille et que nous
eacutetudierons au paragraphe suivant
41 Exemples ne comportant que des paramegravetres de typeConsideacuterons cet exemple dans lequel nous avons surdeacutefini deux patrons de fonctions min de
faccedilon agrave disposer
bull dune premiegravere famille de fonctions agrave deux arguments de mecircme type quelconque (comme
dans les exemples preacuteceacutedents)
bull dune seconde famille de fonctions agrave trois arguments de mecircme type quelconque
include ltiostreamgt
using namespace std
patron numero I
template ltclass Tgt T min (T a T b)
if (a lt b) return a
else return b
4 - Surdeacutefinition de patrons217
patron numero IItemplate ltclass Tgt T min (T a T b T c) return min (min (a b) c) main() int n=12 p=15 q=2 float x=35 y=425 z=025 cout ltlt min (n p) ltlt n patron I int min (int int) cout ltlt min (n p q) ltlt n patron II int min (int int int) cout ltlt min (x y z) ltlt n patron II float min (float float float)
122025
Exemple de surdeacutefinition de patron de fonctions (1)
Dune maniegravere geacuteneacuterale on peut surdeacutefinir des patrons posseacutedant un nombre diffeacuterent de
paramegravetres de type (dans notre exemple il ny en avait quun dans chaque patron min) les
en-tecirctes des fonctions correspondantes peuvent donc ecirctre aussi varieacutes quon le deacutesire Mais il
est souhaitable quil ny ait aucun recoupement entre les diffeacuterentes familles de fonctions cor-
respondant agrave chaque patron Si tel nest pas le cas une ambiguiumlteacute risque dapparaicirctre avec cer-
tains appels
Voici un autre exemple dans lequel nous avons deacutefini plusieurs patrons de fonctions min agrave
deux arguments afin de traiter convenablement les trois situations suivantes
bull deux valeurs de mecircme type (comme dans les paragraphes preacuteceacutedents)
bull un pointeur sur une valeur dun type donneacute et une valeur de ce mecircme type
bull une valeur dun type donneacute et un pointeur sur une valeur de ce mecircme type
include ltiostreamgtusing namespace std
patron numeacutero Itemplate ltclass Tgt T min (T a T b) if (a lt b) return a else return b
patron numeacutero IItemplate ltclass Tgt T min (T a T b) if (a lt b) return a else return b
Les patrons de fonctionsCHAPITRE 11
218
patron numeacutero III
template ltclass Tgt T min (T a T b)
if (a lt b) return a
else return b
main()
int n=12 p=15
float x=25 y=52
cout ltlt min (n p) ltlt n patron numeacutero I int min (int int)
cout ltlt min (ampn p) ltlt n patron numeacutero II int min (int int)
cout ltlt min (x ampy) ltltn patron numeacutero III float min (float float )
cout ltlt min (ampn ampp) ltlt n patron numeacutero I int min (int int )
12
12
25
006AFDF0
Exemple de surdeacutefinition de patron de fonctions (2)
Les trois premiers appels ne posent pas de problegraveme En revanche un appel tel que min (ampn
ampp) conduit agrave instancier agrave laide du patron numeacutero I la fonction
int min (int int )
La valeur fournie alors par lappel est la plus petite des deux valeurs (de type int ) ampn et ampp
Il est probable que ce ne soit pas le reacutesultat attendu par lutilisateur (nous avons deacutejagrave rencon-
treacute ce genre de problegraveme dans le paragraphe 1 en appliquant min agrave des chaicircnes1)
Pour linstant notez quil ne faut pas espeacuterer ameacuteliorer la situation en deacutefinissant un patron
suppleacutementaire de la forme
template ltclass Tgt T min (T a T b)
if (a lt b) return a
else return b
En effet les quatre familles de fonctions ne seraient plus totalement indeacutependantes Plus preacute-
ciseacutement si les trois premiers appels fonctionnent toujours convenablement lappel min (ampn
ampp) conduit agrave une ambiguiumlteacute puisque deux patrons conviennent maintenant (celui que nous
venons dintroduire et le premier)
1 Mais ce problegraveme pourra se reacutegler convenablement avec la speacutecialisation de patron ce qui nest pas le cas du
problegraveme que nous exposons ici
4 - Surdeacutefinition de patrons219
Remarque
Nous avons deacutejagrave vu que le mode de transmission drsquoun paramegravetre de type (par valeur ou
par expression) ne jouait aucun rocircle dans lrsquoidentification des paramegravetres de type drsquoun
patron Il en va de mecircme pour le choix du bon patron en cas de surdeacutefinition La raison en
est la mecircme ce mode de transmission nrsquoest pas deacutefini par lrsquoappel de la fonction mais uni-
quement suivant la fonction choisie pour satisfaire agrave lrsquoappel Comme dans le cas des
patrons la correspondance de type doit ecirctre exacte il nrsquoest mecircme plus question de trou-
ver deux patrons lrsquoun correspondant agrave une transmission par valeur lrsquoautre agrave une trans-
mission par reacutefeacuterence1
template ltclass Tgt f(T a) template ltclass Tgt f(T amp a) main() int n f(n) ambiguiumlteacute f(T) avec T=int ou f(Tamp) avec T=int
Cela restait possible dans le cas des fonctions surdeacutefinies dans la mesure ougrave la reacutefeacute-
rence ne pouvait ecirctre employeacutee qursquoavec une correspondance exacte la transmission par
valeur autorisant des conversions (mais lrsquoambiguiumlteacute existait quand mecircme en cas de cor-
respondance exacte)
42 Exemples comportant des paramegravetres expressionsLa preacutesence de paramegravetres expressions donne agrave la surdeacutefinition de patron un caractegravere plus
geacuteneacuteral Dans lexemple suivant nous avons deacutefini deux familles de fonctions min
bull lune pour deacuteterminer le minimum de deux valeurs de mecircme type quelconque
bull lautre pour deacuteterminer le minimum des valeurs dun tableau de type quelconque et de taille
quelconque (fournie en argument sous la forme dun entier)include ltiostreamgt
using namespace std patron Itemplate ltclass Tgt T min (T a T b) if (a lt b) return a else return b patron IItemplate ltclass Tgt T min (T t int n) int i T min = t[0] for (i=1 iltn i++) if (t[i] lt min) min=t[i] return min
1 Nous reviendrons au paragraphe 5 sur la distinction entre f(Tamp) et f(const Tamp)
Les patrons de fonctionsCHAPITRE 11
220
main() long n=2 p=12
float t[6] = 25 32 15 38 11 28 cout ltlt min (n p) ltlt n patron I long min (long long)
cout ltlt min (t 6) ltlt n patron II float min (float int)
211
Exemple de surdeacutefinition de patrons comportant un paramegravetre expression
Notez que si plusieurs patrons sont susceptibles drsquoecirctre employeacutes et quils ne se distinguent
que par le type de leurs paramegravetres expressions ce sont alors les regravegles de choix dune fonc-
tion surdeacutefinie ordinaire qui sappliquent
5 Speacutecialisation de fonctions de patron
51 GeacuteneacuteraliteacutesUn patron de fonctions deacutefinit une famille de fonctions agrave partir dune seule deacutefinition Autre-
ment dit toutes les fonctions de la famille reacutealisent le mecircme algorithme Dans certains cas
cela peut saveacuterer peacutenalisant Nous lavons dailleurs deacutejagrave remarqueacute dans le cas du patron min
du paragraphe 1 le comportement obtenu lorsquon lappliquait au type char ne nous satis-
faisait pas
La notion de speacutecialisation offre une solution agrave ce problegraveme En effet C++ vous autorise agrave
fournir outre la deacutefinition dun patron la deacutefinition dune ou de plusieurs fonctions pour cer-
tains types darguments Voici par exemple comment ameacuteliorer notre patron min du paragra-
phe 1 en fournissant une version speacutecialiseacutee pour les chaicircnes
include ltiostreamgt
using namespace std include ltcstringgt pour strcmp (ancien stringh)
patron mintemplate ltclass Tgt T min (T a T b) if (a lt b) return a else return b
fonction min pour les chaineschar min (char cha char chb)
if (strcmp (cha chb) lt 0) return cha else return chb
5 - Speacutecialisation de fonctions de patron221
main() int n=12 p=15 char adr1 = monsieur adr2 = bonjour cout ltlt min (n p) ltlt n patron int min (int int) cout ltlt min (adr1 adr2) fonction char min (char char )
12bonjour
Exemple de speacutecialisation dune fonction dun patron
52 Les speacutecialisations partiellesIl est theacuteoriquement possible deffectuer ce que lon nomme des speacutecialisations partielles1
crsquoest-agrave-dire de deacutefinir des familles de fonctions certaines eacutetant plus geacuteneacuterales que dautres
comme dans
template ltclass T class Ugt void fct (T a U b) template ltclass Tgt void fct (T a T b)
Manifestement la seconde deacutefinition est plus speacutecialiseacutee que la premiegravere et devrait ecirctre utili-
seacutee dans des appels de fct dans lesquels les deux arguments sont de mecircme type
Ces possibiliteacutes de speacutecialisation partielle srsquoavegraverent tregraves utiles dans les situations suivantes
bull traitement particulier pour un pointeur en speacutecialisant partiellement T en T
template ltclass Tgt void f(T t) patron I template ltclass Tgt void f(T t) patron II int n int adc f(n) f(int) en utilisant patron I avec T = int f(adi) f(int ) en utilisant patron II avec T = int car il est plus speacutecialiseacute que patron I (avec T = int )
bull distinction entre pointeur ou reacutefeacuterence sur une variable de pointeur ou reacutefeacuterence sur une
constante
template ltclass Tgt void f(T amp t) patron I template ltclass Tgt void f(const T amp t) patron II int n const int cn=12 f(n) f(int amp) en utilisant patron I avec T = int f(cn) f(const int amp) en utilisant patron II avec T = int car il est plus speacutecialiseacute que patron I (avec T = const int)
1 Cette possibiliteacute a eacuteteacute introduite par la norme ANSI
Les patrons de fonctionsCHAPITRE 11
222
Drsquoune maniegravere geacuteneacuterale la norme deacutefinit une relation drsquoordre partiel permettant de dire
qursquoun patron est plus speacutecialiseacute qursquoun autre Comme on peut srsquoy attendre il existe des situa-
tions ambigueumls dans lesquelles aucun patron nrsquoest plus speacutecialiseacute qursquoun autre
6 Algorithme drsquoinstanciation drsquoune fonction patron
Nous avons donc vu qursquoon peut deacutefinir un ou plusieurs patrons de mecircme nom (surdeacutefinition)
chacun posseacutedant ses propres paramegravetres de type et eacuteventuellement des paramegravetres expres-
sions De plus il est possible de fournir des fonctions ordinaires portant le mecircme nom quun
patron (speacutecialisation dune fonction de patron)
Lorsque lrsquoon combine ces diffeacuterentes possibiliteacutes le choix de la fonction agrave instancier peut
srsquoaveacuterer moins eacutevident que dans nos preacuteceacutedents exemples Nous allons donc preacuteciser ici
lrsquoalgorithme utiliseacute par le compilateur dans linstanciation de la fonction correspondant agrave un
appel donneacute
Dans un premier temps on examine toutes les fonctions ordinaires ayant le nom voulu et on
sinteacuteresse aux correspondances exactes Si une seule convient le problegraveme est reacutesolu Sil en
existe plusieurs il y a ambiguiumlteacute une erreur de compilation est deacutetecteacutee et la recherche est
interrompue
Si aucune fonction ordinaire ne reacutealise de correspondance exacte on examine alors tous les
patrons ayant le nom voulu en ne consideacuterant que les paramegravetres de type Si une seule
correspondance exacte est trouveacutee on cherche agrave instancier la fonction correspondante1 agrave
condition que cela soit possible Cela signifie que si cette derniegravere dispose de paramegravetres
expressions il doit exister des conversions valides des arguments correspondants dans le type
voulu Si tel est le cas le problegraveme est reacutesolu
Si plusieurs patrons assurent une correspondance exacte de type on examine tout dabord si
lon est en preacutesence dune speacutecialisation partielle auquel cas on choisit le patron le plus speacute-
cialiseacute2 Si cela ne suffit pas agrave lever lrsquoambiguiumlteacute on examine les eacuteventuels paramegravetres expres-
sions qursquoon traite de la mecircme maniegravere que pour une surdeacutefinition usuelle Si plusieurs
fonctions restent utilisables on aboutit agrave une erreur de compilation et la recherche est inter-
rompue
En revanche si aucun patron de fonctions ne convient3 on examine agrave nouveau toutes les
fonctions ordinaires en les traitant cette fois comme de simples fonctions surdeacutefinies (promo-
tions numeacuteriques conversions standard4)
1 Du moins si elle na pas deacutejagrave eacuteteacute instancieacutee
2 Rappelons que la possibiliteacute de speacutecialisation partielle des patrons de fonctions nest pas correctement geacutereacutee par
toutes les impleacutementations
3 Y compris si un seul reacutealisait les correspondances exactes des paramegravetres de type sans qursquoil existe de conversions
leacutegales pour les eacuteventuels paramegravetres expressions
4 Voir au paragraphe 5 du chapitre 4