Vous êtes sur la page 1sur 872

Bien dbuter en C++

Anatomie dun programme C++


Variables et constantes
Expressions et instructions
Fonctions
Programmation oriente objet
Droulement dun programme
Pointeurs
Rfrences
Fonctions avances
Analyse et conception oriente objet
Hritage
Tableaux et chanes
Polymorphisme
Classes et fonctions spciales
Concepts avancs dhritage
Les ux
Espaces de noms
Les modles
Gestion des erreurs et exceptions
Et maintenant ?
Mots-cls C++
Priorit des oprateurs
Solutions des exercices
tude des listes chanes
O
Un
ma
Matrisez rapidement les principes fondamentaux de
la programmation oriente objet !
Grce cet ouvrage, initiez-vous rapidement au C++
en dcouvrant les principaux concepts du langage
et de la programmation oriente objet : gestion des
entres-sorties, boucles et tableaux, modles, etc.
Ces concepts sont mis en uvre dans de nombreux
exemples de codes, qui sont analyss en dtail pour
vous permettre ensuite dcrire vos propres program-
mes. Vous pourrez galement vous exercer et tester
vos connaissances avec les quizz et exercices pr-
sents dans chaque chapitre, dont vous trouverez les
rponses la n de louvrage.
Entirement rvis pour cette nouvelle dition, ce
best-seller vous guidera pas pas dans votre appren-
tissage du C++.
Les codes sources du livre sont disponibles
sur www.pearson.fr.
Niveau : Dbutant / Intermdiaire
Catgorie : Programmation
Conguration : Multiplate-forme
ISBN : 978-2-7440-2359-0
2359 0609 32
L
e

l
a
n
g
a
g
e


Le langage C++
Jesse Liberty
et Bradley Jones
Initiez-vous
la programmation
en C++
C
++
Le langage
propos de lauteur
Jesse Liberty est lauteur de nombreux best-sellers sur ASP.NET, C# et C++. Il
a travaill pour AT&T et Citibank avant dintgrer les quipes de Microsoft.
Bradley L. Jones dirige de nombreux sites web ddis la programmation,
dont CodeGuru.com, Developer.com et Javascripts.com.
Pearson Education France
47 bis, rue des Vinaigriers 75010 Paris
Tl. : 01 72 74 90 00
Fax : 01 42 05 22 17
www.pearson.fr
C
+
+
2359-prog C++.indd 1 18/05/09 17:29:15
Jesse Liberty et Bradley Jones
Le langage C++


L E P R O G R A M M E U R
C++ Livre Page I Jeudi, 7. mai 2009 9:45 09
Pearson Education France a apport le plus grand soin la ralisation de ce livre an de vous four-
nir une information complte et able. Cependant, Pearson Education France nassume de respon-
sabilits, ni pour son utilisation, ni pour les contrefaons de brevets ou atteintes aux droits de tierces
personnes qui pourraient rsulter de cette utilisation.
Les exemples ou les programmes prsents dans cet ouvrage sont fournis pour illustrer les descriptions
thoriques. Ils ne sont en aucun cas destins une utilisation commerciale ou professionnelle.
Pearson Education France ne pourra en aucun cas tre tenu pour responsable des prjudices
ou dommages de quelque nature que ce soit pouvant rsulter de lutilisation de ces exemples ou
programmes.
Tous les noms de produits ou marques cits dans ce livre sont des marques dposes par leurs
propritaires respectifs.
Aucune reprsentation ou reproduction, mme partielle, autre que celles prvues larticle L. 122-5 2 et 3 a) du code de la
proprit intellectuelle ne peut tre faite sans lautorisation expresse de Pearson Education France ou, le cas chant, sans le
respect des modalits prvues larticle L. 122-10 dudit code.
No part of this book shall be reproduced, stored in a retrieval system, or transmitted by any means, electronic, mechanical,
photocopying, recording, or otherwise, without written permission from the publisher.
Publi par Pearson Education France
47 bis, rue des Vinaigriers
75010 PARIS
Tl. : 01 72 74 90 00
www.pearson.fr
Mise en pages : TyPAO
Tous droits rservs
Titre original : Teach Yourself C++ in 21 Days,
fifth edition
Traduit de lamricain par Nathalie Le Guillou de Penanros
Nouvelle dition franaise revue, corrige
et complte par Eric Jacoboni
ISBN original : 0-672-32711-2
Copyright 2004 Sams Publishing
All rights reserved
C++ Livre Page II Jeudi, 7. mai 2009 9:45 09
ISBN : 978-2-7440-4023-8
Copyright 2009 Pearson Education France
Sommaire
Introduction ............................................ 1
Partie I .................................................... 3
1. Bien dbuter en C++ .......................... 5
2. Anatomie dun programme C++ ...... 23
3. Variables et constantes ...................... 39
4. Expressions et instructions ................ 65
5. Fonctions ............................................. 97
6. Programmation oriente objet .......... 135
7. Droulement dun programme ......... 173
Partie II ................................................... 207
8. Pointeurs ............................................. 209
9. Rfrences .......................................... 243
10. Fonctions avances ........................... 279
11. Analyse et conception oriente objet 321
12. Hritage ............................................ 357
13. Tableaux et chanes .......................... 395
14. Polymorphisme ................................ 437
Partie III ................................................. 481
15. Classes et fonctions spciales .......... 483
16. Concepts avancs dhritage .......... 515
17. Les ux ............................................. 575
18. Espaces de noms .............................. 619
19. Les modles ...................................... 641
20. Gestion des erreurs et exceptions ... 699
21. Et maintenant ? ............................... 737
A. Mots-cls C++ ................................... 775
B. Priorit des oprateurs ..................... 777
C. Solutions des exercices ..................... 779
D. tude des listes chanes .................. 837
Index ....................................................... 849
C++Livre Page III J eudi, 7. mai 2009 9:45 09
C++Livre Page IV J eudi, 7. mai 2009 9:45 09
Table des matires
Introduction ............................................ 1
Public vis ........................................... 1
Conventions typographiques .............. 2
Partie I .................................................... 3
1. Bien dbuter en C++ .......................... 5
Introduction ........................................ 5
Un peu dhistoire... ............................. 5
La programmation oriente objet (POO) 9
volution de C++ ............................... 11
Est-il ncessaire dapprendre dabord
le langage C ? .................................... 11
C++, Java et C# .................................. 11
Norme ANSI ...................................... 12
Prparation la programmation ......... 13
Votre environnement de dveloppement 13
Cration du programme ...................... 14
Cycle de dveloppement ..................... 15
Votre premier programme C++ :
BONJOUR.cpp ................................... 16
Comment tirer parti de votre compilateur 19
Erreurs de compilation ........................ 20
Questions-rponses ............................. 21
Testez vos connaissances .................... 22
Exercices ............................................ 22
2. Anatomie dun programme C++ ...... 23
Un programme simple ........................ 24
tude rapide de lobjet cout ............... 26
Utilisation de lespace de nom standard 28
Commentaires ..................................... 30
Les fonctions ..................................... 33
Questions-rponses ............................. 36
Testez vos connaissances .................... 37
Exercices ............................................ 37
3. Variables et constantes ...................... 39
Quest-ce quune variable ? ............... 39
Dnition dune variable .................... 44
Cration de plusieurs variables la fois 48
Affectation de valeurs aux variables .. 48
Cration dalias avec typedef ............. 50
short ou long : que choisir ? ............... 51
Variables caractre .............................. 54
Constantes .......................................... 57
Constantes numres ........................ 59
Questions-rponses ............................. 62
Testez vos connaissances .................... 63
Exercices ............................................ 64
C++Livre Page V J eudi, 7. mai 2009 9:45 09
VI Le langage C++
4. Expressions et instructions ............... 65
Instructions ......................................... 66
Expressions ........................................ 67
Oprateurs .......................................... 69
Combinaison doprateurs daffectation
et doprateurs mathmatiques .......... 72
Incrmentation et dcrmentation ...... 72
Priorit des oprateurs ....................... 75
Parenthses imbriques ..................... 76
Vrai ou faux ....................................... 77
Linstruction if .................................... 78
Utilisation daccolades
dans les instructions if imbriques ..... 86
Oprateurs logiques ........................... 89
valuation en court-circuit ................. 90
Priorit des oprateurs relationnels .... 90
Vrai ou faux ....................................... 91
Oprateur conditionnel (ternaire) ....... 92
Questions-rponses ............................ 93
Testez vos connaissances ................... 94
Exercices ............................................ 95
5. Fonctions ............................................ 97
Quest-ce quune fonction ? ............... 98
Valeurs renvoyes, paramtres formels
et effectifs ........................................... 99
Dclaration et dnition de fonctions 99
Excution des fonctions ..................... 103
Porte des variables ............................ 104
Instructions des fonctions .................. 111
Retour sur les paramtres des fonctions 111
Retour sur les valeurs renvoyes ....... 112
Paramtres par dfaut ......................... 115
Surcharge de fonctions ....................... 117
Pour en savoir plus sur les fonctions .. 121
Principe des fonctions ........................ 128
Questions-rponses ............................ 132
Testez vos connaissances ................... 133
Exercices ............................................ 133
6. Programmation oriente objet ......... 135
C++ est-il orient objet ? ................... 135
Cration de nouveaux types ................ 137
Prsentation des classes et des membres 138
Accs aux membres dune classe ....... 141
Accs priv et accs public ................ 142
Implmentations des mthodes
de la classe .......................................... 148
Ajout de constructeurs et de destructeurs 151
Inclure des fonctions membres const .. 156
Interface et implmentation ................ 156
O placer les dclarations de classes
et les dnitions de mthodes ? .......... 159
Implmentation en ligne ..................... 161
Classes imbriques ............................. 163
Les structures ...................................... 168
Questions-rponses ............................. 169
Testez vos connaissances .................... 170
Exercices ............................................. 171
7. Droulement dun programme ......... 173
Les boucles ......................................... 173
Les boucles while ............................... 175
Les boucles do...while ........................ 183
Linstruction do...while ....................... 184
Les boucles for ................................... 186
Rsum des boucles ............................ 195
Contrler le ux avec des instructions
switch .................................................. 198
Utilisation de switch dans un menu ... 201
Questions-rponses ............................. 204
Testez vos connaissances .................... 205
Exercices ............................................. 206
Partie II ................................................... 207
8. Pointeurs ............................................. 209
Quest-ce quun pointeur ? ................. 210
Utilit des pointeurs ............................ 220
La pile et lespace mmoire adressable
(tas) ..................................................... 220
En savoir plus sur les fuites mmoire . 225
Cration dobjets sur le tas ................. 226
Suppression dobjets du tas ................ 226
C++Livre Page VI J eudi, 7. mai 2009 9:45 09
Table des matires VII
Accs aux donnes membres .............. 228
Cration de donnes membres dans le tas 229
Le pointeur this ................................... 232
Pointeurs perdus, incontrlables
ou pointeurs fous ................................ 233
Pointeurs const .................................... 237
Questions-rponses ............................. 240
Testez vos connaissances .................... 241
Exercices ............................................. 241
9. Rfrences .......................................... 243
Quest-ce quune rfrence ? .............. 244
Utilisation de loprateur adresse de (&)
sur des rfrences ................................ 245
Rfrencement des objets ................... 248
Pointeurs nuls et rfrences nulles ..... 250
Passage de paramtres par rfrence .. 251
En-ttes et prototypes de fonctions ..... 256
Renvoi de plusieurs valeurs ............... 257
Efcacit du passage de paramtres
par rfrence ....................................... 260
Choisir entre rfrences et pointeurs .. 269
Mlanger les rfrences et les pointeurs
dans une liste de paramtres ............... 269
Renvoi de rfrences dobjet hors
de porte ............................................. 271
Renvoi dune rfrence un objet
dans le tas ............................................ 272
O le pointeur est-il pass ? ................ 275
Questions-rponses ............................. 275
Testez vos connaissances .................... 276
Exercices ............................................. 276
10. Fonctions avances ........................... 279
Fonctions membres surcharges ......... 279
Utilisation de valeurs par dfaut ......... 282
Choisir entre des valeurs par dfaut
et des fonctions surcharges ............... 284
Le constructeur par dfaut .................. 285
Surcharge des constructeurs ............... 285
Initialisation des objets ....................... 287
Le constructeur de copie ..................... 288
Surcharge des oprateurs .................... 293
Conversion de type (transtypage) ....... 312
Questions-rponses ............................. 317
Testez vos connaissances .................... 318
Exercices ............................................ 319
11. Analyse et conception oriente objet 321
Les modles ........................................ 322
Conception : le langage de modlisation 322
Conception : le processus .................. 324
tape 1 La conceptualisation :
commencer par la vision .................... 327
tape 2 Lanalyse : collecter les besoins 327
tape 3 La conception .................... 341
tapes 4 6 Implmentation, tests
et dploiement .................................... 355
Itrations ............................................. 355
Questions-rponses ............................. 355
Testez vos connaissances .................... 356
Exercices ............................................ 356
12. Hritage ............................................ 357
Quest-ce que lhritage ? .................. 357
Priv ou protg ? ............................... 362
Lhritage avec les constructeurs
et les destructeurs ............................... 365
Rednition des mthodes
de la classe de base ............................. 372
Mthodes virtuelles ............................ 378
Questions-rponses ............................. 392
Testez vos connaissances .................... 393
Exercices ............................................ 393
13. Tableaux et chanes .......................... 395
Quest-ce quun tableau ? ................... 395
Accs aux lments de tableau ........... 396
criture dlments hors limite .......... 398
Erreurs dintervalle ............................ 401
Initialisation des tableaux ................... 402
Dclaration de tableaux ...................... 402
Tableaux dobjets ............................... 404
Tableaux multidimensionnels ............ 406
C++Livre Page VII J eudi, 7. mai 2009 9:45 09
VIII Le langage C++
Initialisation de tableaux plusieurs
dimensions ........................................ 407
Construire des tableaux de pointeurs . 409
Larithmtique des pointeurs,
un sujet complexe ............................... 411
Dclaration de tableaux sur le tas ...... 414
Pointeurs sur tableau et tableaux
de pointeurs ........................................ 415
Pointeurs et noms de tableaux ........... 415
Suppression des tableaux stocks
sur le tas ............................................. 417
Redimensionner les tableaux en cours
dexcution ......................................... 418
Tableaux de caractres et chanes ..... 421
Les fonctions strcpy() et strncpy() ..... 423
La classe String .................................. 425
Listes chanes et autres structures .... 433
Cration de classes tableaux ............. 434
Questions-rponses ............................ 434
Testez vos connaissances ................... 435
Exercices ............................................ 436
14. Polymorphisme ................................ 437
Problmes lis lhritage simple ..... 437
Hritage multiple ............................... 445
Types abstraits de donnes (TAD) ..... 463
Questions-rponses ............................ 477
Testez vos connaissances ................... 478
Exercices ............................................ 479
Partie III ................................................ 481
15. Classes et fonctions spciales .......... 483
Partage dinformations entre objets de
mme type : donnes membres statiques 484
Mthodes membres statiques ............. 489
Pointeurs sur des fonctions ................ 492
Pointeurs sur des fonctions membres . 507
Questions-rponses ............................ 512
Testez vos connaissances ................... 513
Exercices ............................................ 513
16. Concepts avancs dhritage ........... 515
Agrgation ......................................... 515
Hritage et Dlgation/Agrgation .... 531
Hritage priv ..................................... 541
Classes amies ...................................... 550
Fonctions amies .................................. 560
Fonctions amies et surcharge
doprateur .......................................... 560
Surcharge de operator<<() ............... 566
Questions-rponses ............................. 570
Testez vos connaissances .................... 571
Exercices ............................................. 571
17. Les ux .............................................. 575
Prsentation des ux ........................... 575
Flux et tampons .................................. 578
Les objets E/S standard ...................... 579
Redirection des ux standard ............. 579
Les entres avec cin ............................ 580
Autres fonctions membres de cin ....... 585
Les sorties avec cout ........................... 594
Manipulateurs, indicateurs dtat
et instructions de formatage ................ 597
Flux ou fonction printf() ..................... 601
Entres/sorties sur chier ................... 604
ofstream et ifstream ............................ 604
Fichiers binaires ou chiers texte ....... 610
Traitement de la ligne de commande .. 612
Questions-rponses ............................. 616
Testez vos connaissances .................... 617
Exercices ............................................. 617
18. Espaces de noms ............................... 619
Utilit des espaces de noms ............... 619
Rsolution des fonctions et des classes
par leurs noms ...................................... 620
Cration dun espace de noms ............ 625
Utilisation dun espace de noms ........ 628
Mot-cl using ..................................... 630
Alias despace de noms ..................... 634
Espace de noms anonyme ................... 634
Lespace de noms standard std .......... 636
C++Livre Page VIII J eudi, 7. mai 2009 9:45 09
Table des matires IX
Questions-rponses ............................. 637
Testez vos connaissances .................... 638
Exercices ............................................. 639
19. Les modles ...................................... 641
Quest-ce quun modle ...................... 641
Construire une dnition de modle ... 643
Passer des objets modles instancis
aux fonctions ....................................... 651
Modles et amis .................................. 652
Utilisation des des modles ................ 660
La bibliothque des modles standard 676
Les conteneurs .................................... 676
Les conteneurs squentiels ................. 677
Les conteneurs associatifs .................. 687
Les classes dalgorithmes ................... 692
Questions-rponses ............................. 695
Testez vos connaissances .................... 696
Exercices ............................................. 697
20. Gestion des erreurs et exceptions ... 699
Erreurs logiques et fautes de syntaxe .. 700
Les coulisses des exceptions ............... 702
Produire ses propres exceptions .......... 706
Crer une classe dexception .............. 708
Placement des blocs try et catch ......... 712
Donnes dans les exceptions et noms
des objets exceptions .......................... 719
Exceptions et modles ........................ 727
Exceptions et erreurs .......................... 730
Recherche des erreurs ......................... 731
Questions-rponses ............................. 733
Testez vos connaissances .................... 734
Exercices ............................................. 734
21. Et maintenant ? ................................ 737
Le prprocesseur et le compilateur ..... 738
La directive de prprocesseur #dene . 738
Inclusions et clauses dinclusion ........ 741
Les macros ......................................... 742
Manipulation des chanes ................... 745
Les macros prdnies ....................... 747
La macro assert() ............................... 747
Les fonctions inline ............................ 757
Oprations sur les bits ........................ 758
Styles de programmation .................... 765
Commentaires ..................................... 768
Comment poursuivre
votre dveloppement C++ .................. 770
Questions-rponses ............................. 772
Testez vos connaissances .................... 773
Exercices ............................................ 774
A. Mots-cls C++ ................................... 775
B. Priorit des oprateurs ..................... 777
C. Solutions des exercices ..................... 779
Chapitre 1 ........................................... 779
Chapitre 2 ........................................... 780
Chapitre 3 ........................................... 782
Chapitre 4 ........................................... 784
Chapitre 5 ........................................... 784
Chapitre 6 ........................................... 787
Chapitre 7 ........................................... 791
Chapitre 8 ........................................... 793
Chapitre 9 ........................................... 794
Chapitre 10 ......................................... 797
Chapitre 11 ........................................ 802
Chapitre 12 ......................................... 804
Chapitre 13 ........................................ 805
Chapitre 14 ........................................ 806
Chapitre 15 ........................................ 808
Chapitre 16 ......................................... 815
Chapitre 17 ......................................... 819
Chapitre 18 ........................................ 822
Chapitre 19 ......................................... 823
Chapitre 20 ......................................... 828
Chapitre 21 ......................................... 834
D. tude des listes chanes .................. 837
Les composants dune liste chane ... 838
Index ....................................................... 849
C++Livre Page IX J eudi, 7. mai 2009 9:45 09
C++Livre Page X J eudi, 7. mai 2009 9:45 09
Introduction

Ce livre a t conu pour vous initier la programmation en C++. Vous y apprendrez les
principaux concepts du langage (gestion des entres-sorties, boucles et tableaux, modles,
etc.) et de la programmation oriente objet. Chaque chapitre contient des exemples de
programmes qui mettent en uvre les concepts tudis et qui sont analyss en dtail.
Tous les chapitres se terminent par un jeu de questions-rponses, un quiz et des exercices
dont vous trouverez les rponses lAnnexe D.
Les listings de code de chacun des chapitres sont disponibles sur le site www.pearson.fr,
la page consacre cet ouvrage.
Public vis
Il nest pas ncessaire davoir une exprience pralable de la programmation pour tirer
parti de ce livre car il part de zro et vous apprend la fois le langage et les concepts de
programmation C++. Les nombreux exemples de syntaxe et les analyses dtailles des
extraits de code constituent un excellent guide touristique pour votre voyage dans cet envi-
ronnement si riche. Que vous soyez un programmeur dbutant ou expriment, vous pour-
rez constater que cet ouvrage vous permettra dapprendre rapidement et simplement le
langage C++.
C++Livre Page 1 J eudi, 7. mai 2009 9:45 09
2 Le langage C++
Conventions typographiques
Ces paragraphes soulignent des informations qui amlioreront lefcacit de
vos programmes C++.
Ces notes fournissent des prcisions sur le sujet abord.
Ces avertissements vous vitent de tomber dans les piges les plus communs
qui peuvent vous gcher lexistence.
Les encadrs
Ils prcisent la syntaxe dune instruction.
Il peut aussi sagir daparts destins attirer votre attention sur un cas particulier ou une
situation exceptionnelle.
Cet ouvrage utilise galement des polices de caractres diffrentes pour distinguer le code
C++ du texte classique :
Les commandes, variables et autres lments de code apparaissent dans le texte dans
une police chasse fixe spciale.
Les termes nouveaux ou importants sont crits en italique.
Les parties variables dans les descriptions de syntaxe apparaissent en police
chasse fixe italique. Ce style signale que vous devez remplacer lespace rserv
par le nom rel du chier, du paramtre ou de tout autre lment quil reprsente.
En outre, toutes les lignes de code sont numrotes. Vous constaterez parfois que certaines
lignes trop longues pour le format de ce livre ont t dcoupes en plusieurs lignes : en ce
cas, les lignes de continuation ne seront pas numrotes et vous devrez saisir ces lignes
comme une seule ligne dans votre diteur de texte.
Faire Ne pas faire
Un rappel des conseils suivre. Les erreurs ou confusions viter.
A
s
t
u
c
e
In
f
o
A
tte
n
tio
n
C++Livre Page 2 J eudi, 7. mai 2009 9:45 09
Partie I
Dans les sept chapitres qui constituent cette partie, vous ferez vos premiers pas en
programmation en gnral et en C++ en particulier. Les Chapitres 1 et 2 abordent les
notions de base de la programmation et le droulement dune application. Au Chapi-
tre 3, vous apprendrez grer des variables et des constantes dans un programme. Le
Chapitre 4 est consacr aux branchements conditionnels raliss laide dinstruc-
tions et dexpressions. Vous vous initierez, au Chapitre 5, lutilisation des fonctions,
et celle des classes et des objets au Chapitre 6. Enn, partir du Chapitre 7, vous
serez en mesure de comprendre le droulement dune application et dcrire vos
premiers programmes orients objet.
C++Livre Page 3 J eudi, 7. mai 2009 9:45 09
C++Livre Page 4 J eudi, 7. mai 2009 9:45 09
1
Bien dbuter en C++
Au sommaire de ce chapitre
Pourquoi choisir le langage C++ ?
Les tapes du cycle de dveloppement dun programme
crire, compiler et lancer votre premier programme C++
Introduction
Bienvenue dans Le Langage C++ de la collection Le Programmeur ! Cet ouvrage a pour
objectif de vous initier efcacement la programmation en langage C++.
Un peu dhistoire...
Les langages de programmation ont considrablement volu depuis les premiers calcula-
teurs qui avaient t conus pour les calculs de trajectoires dartillerie durant la seconde
guerre mondiale. cette poque, les programmeurs travaillaient avec le langage informa-
tique le plus primitif qui soit le langage machine ce qui les obligeait grer de longues
C++Livre Page 5 J eudi, 7. mai 2009 9:45 09
6 Le langage C++
chanes de 1 et de 0. Puis, les premiers assembleurs apparurent, an de rendre les instruc-
tions machine plus comprhensibles et plus faciles utiliser puisquelles taient dsormais
reprsentes par des instructions mnmoniques comme MOV et ADD.
Dans les annes 60 apparurent des langages plus volus comme BASIC et COBOL, qui
permettaient aux programmeurs dutiliser une syntaxe proche de la langue anglaise (le
code source), avec des instructions et des mots comme Let I = 100. Les lignes dinstructions
taient ensuite traduites en langage machine par des interprteurs ou des compilateurs.
Un interprteur traduit et excute une une les instructions du programme (ou code
source) et les transforme directement en actions.
Un compilateur passe par une tape intermdiaire (la compilation) qui produit dabord un
chier objet. Le compilateur fait ensuite appel un diteur de liens (ou linker) qui trans-
forme ce chier objet en programme excutable.
Les interprteurs lisent les instructions ligne ligne et excutent le code immdiatement,
ce qui en simplie lutilisation. Aujourdhui, les programmes interprts sont gnralement
appels scripts.
Certains langages, comme Visual Basic 6, dsignent linterprteur sous le nom de biblio-
thque dexcution. Dautres langages, comme Visual Basic .NET et Java, disposent dun autre
composant, appel "machine virtuelle" (VM, Virtual Machine) ou excuteur. Bien que la VM
soit aussi un interprteur, il ne sagit plus dun interprteur de code source qui se contente
de traduire un langage lisible en code objet : la VM interprte et excute un "langage
machine indpendant de lordinateur" compil, parfois appel "langage intermdiaire".
Les compilateurs ajoutent les tapes supplmentaires de compilation du code source
(comprhensible par lhomme) en code objet (lisible par la machine). Ceci pourrait appa-
ratre comme un inconvnient mais, en ralit, cette tape permet de crer un programme
dont la vitesse dexcution est optimise puisque la traduction du chier source en langage
machine a dj t ralis une fois pour toutes, au moment de la compilation. Il nest donc
plus ncessaire de retraduire le programme chaque fois quon lexcute.
Lautre avantage des langages compils comme C++ tient la diffusion des programmes
puisque lon peut distribuer un chier excutable des personnes qui ne disposent pas du
compilateur. Avec un langage interprt, par contre, lutilisateur doit ncessairement
possder linterprteur pour pouvoir excuter le programme.
C++ est gnralement un langage compil, mme sil existe quelques interprteurs C++.
linstar de nombreux langages compils, il a la rputation de produire des programmes
rapides et performants.
En fait, pendant longtemps, la principale proccupation des programmeurs tait de conce-
voir des applications trs courtes pouvant sexcuter rapidement, car la mmoire et le
temps de calcul cotaient cher. Avec la miniaturisation des ordinateurs, laugmentation de
C++Livre Page 6 J eudi, 7. mai 2009 9:45 09
Chapitre 1 Bien dbuter en C++ 7
leurs performances et la chute des prix, les priorits ont chang. Dsormais, le cot de
dveloppement dpasse largement celui dun ordinateur de type PC ou mini. Limportant
est dcrire des programmes performants, bien construits et faciles mettre jour (cest--
dire sans surcot excessif).
Le mot programme a deux sens. Il dsigne les instructions (ou code source)
crites par un dveloppeur, mais galement lensemble dun logiciel excuta-
ble. Cette homonymie peut tre une source de confusion et il est important de
faire la distinction entre le chier source et le programme excutable.
Comment rsoudre les problmes
Les problmes auxquels sont confronts les programmeurs sont totalement diffrents de
ceux quils devaient rsoudre il y a une vingtaine dannes. Dans les annes 80, les
programmes traitaient des volumes importants de donnes brutes. Le programmeur et
lutilisateur nal taient tous deux des spcialistes de linformatique. De nos jours, les
utilisateurs sont bien plus nombreux et peu connaissent tous les dtails des ordinateurs et du
fonctionnement des programmes. Lutilisateur nal actuel recherche des solutions prtes
lemploi et capables deffectuer des oprations de gestion courantes ou ponctuelles.
Linformatique est devenue plus conviviale, mais ce processus a galement conduit la
mise en uvre de programmes de plus en plus complexes. Dans les annes 70, les utilisa-
teurs taient contraints de saisir des commandes nigmatiques pour voir dler lcran
des volumes impressionnants de donnes brutes. Cette poque est rvolue ! Une applica-
tion se compose dsormais de fentres, de menus et de botes de dialogue intgrs une
interface conviviale.
Avec le dveloppement du Web, les ordinateurs ont abord une re nouvelle de pntration
du march ; les utilisateurs dordinateurs sont plus nombreux que jamais, et ils sont trs
exigeants.
Au cours des dernires annes, les applications se sont galement tendues dautres pri-
phriques : lordinateur de bureau nest plus la seule cible des applications. Les tlphones
portables, les assistants personnels (PDA), les PC de poche et autres priphriques consti-
tuent des cibles toutes trouves pour les applications modernes.
Depuis la premire dition de ce livre, les programmeurs ont rpondu aux demandes des
utilisateurs et les programmes sont devenus plus volumineux et plus complexes. La nces-
sit de dvelopper des techniques de programmation permettant de grer cette complexit
est devenue vidente.
Les besoins changeant, les techniques et les langages voluent galement pour aider les
programmeurs grer la complexit des demandes. Dans cet ouvrage, nous nous
In
f
o
C++Livre Page 7 J eudi, 7. mai 2009 9:45 09
8 Le langage C++
concentrerons uniquement sur une partie essentielle de cette volution : le passage de la
programmation procdurale la programmation oriente objet.
La programmation procdurale, structure et oriente objet
Il y a quelques annes encore, les programmes taient conus comme des suites de proc-
dures destines traiter les donnes. Une procdure galement appele fonction ou
mthode est un ensemble dinstructions sexcutant lune aprs lautre. Les donnes et
les procdures taient totalement dissocies et le travail du programmeur consistait
connatre les fonctions appeles par dautres fonctions et les donnes qui taient modi-
es. Pour faire face ce niveau de complexit, on a donc invent la programmation
structure.
Le principe gnral de la programmation structure consiste diviser pour mieux rgner.
Un programme peut alors tre considr comme un ensemble de tches. Toute opration
trop complexe pour tre dcrite simplement est dcompose en un ensemble doprations
plus simples, jusqu nobtenir que des tches sufsamment triviales pour tre aisment
comprhensibles.
Le calcul du salaire moyen de chaque employ dune entreprise est, par exemple, une
tche assez complexe. Toutefois, il est possible de diviser le traitement en plusieurs tches
secondaires :
1. Compter le nombre demploys.
2. Dterminer le revenu de chaque employ.
3. Faire le total de tous les salaires.
4. Diviser cette valeur par le nombre demploys.
La troisime tape (total des salaires) peut galement se diviser en tches plus simples :
1. Lire lenregistrement de chaque salari.
2. Extraire le salaire de chaque employ.
3. Ajouter cette valeur au total gnral.
4. Accder lenregistrement suivant.
La lecture de chaque enregistrement peut, de la mme faon, tre dcompose en oprations
plus lmentaires :
1. Ouvrir le chier des employs.
2. Rechercher le bon enregistrement.
3. Lire les donnes.
C++Livre Page 8 J eudi, 7. mai 2009 9:45 09
Chapitre 1 Bien dbuter en C++ 9
La programmation structure rsout des problmes complexes de manire trs able.
Toutefois, la n des annes 80, cette mthode a montr ses limites.
Dune part, il est naturel dassocier les donnes (les enregistrements des employs, par
exemple) et leur traitement (tri, modication, etc.). Malheureusement, la programmation
structure spare les donnes des fonctions qui les manipulent et ne propose pas de moyen
naturel de les regrouper. La programmation structure est donc souvent dsigne par le
terme de programmation procdurale, car elle met laccent sur les procdures (plutt que
sur les objets).
Dautre part, les programmeurs devaient souvent rutiliser des fonctions. Or, certaines
fonctions qui convenaient un type de donnes ne pouvaient pas toujours tre rutilises
avec dautres, ce qui limitait leurs avantages.
La programmation oriente objet (POO)
La programmation oriente objet (POO) rpond ces besoins. Elle fournit les techniques
permettant de traiter des applications trs complexes, exploite des composants logiciels
rutilisables et associe les donnes aux tches qui les manipulent.
La caractristique essentielle de la programmation oriente objet est de modliser des
"objets" (cest--dire des concepts) plutt que des "donnes". Ces objets peuvent tre
des lments graphiques afchables, comme des boutons ou des zones de liste, ou des
objets rels, comme des clients, des bicyclettes, des avions, des chats ou de leau.
Les objets possdent des caractristiques, galement appeles proprits ou attributs,
comme ge, rapidit, volume, noir, humide. Ils ont aussi des fonctionnalits, appeles
oprations ou fonctions, comme acclrer, voler, miauler ou couler. Le rle de la program-
mation oriente objet est de reprsenter ces objets dans le langage de programmation.
C++ et la programmation oriente objet
Le langage C++ permet dutiliser toutes les possibilits de la programmation oriente
objet, notamment ses trois piliers que sont lencapsulation, lhritage et le polymorphisme.
Encapsulation
Un technicien ne fabrique pas les composants quil assemble. Il les choisit selon leurs
spcications sans se proccuper de leur fonctionnement interne.
Lautonomie dun objet est une proprit rsultant dun processus appel encapsulation.
Celle-ci permet de masquer les donnes internes dun objet et de lutiliser sans connatre
les dtails de son fonctionnement, exactement comme vous utilisez votre rfrigrateur
C++Livre Page 9 J eudi, 7. mai 2009 9:45 09
10 Le langage C++
sans comprendre le principe des compresseurs. Il reste possible de modier ce fonction-
nement interne sans affecter celui du programme, condition toutefois que les spcica-
tions soient respectes (le compresseur du rfrigrateur peut tre remplac par un autre de
conception similaire).
Lorsque notre technicien veut utiliser un composant lectronique, il na pas besoin den
connatre les rouages internes. Toutes les proprits de ce composant sont encapsules
dans lobjet composant, elles ne sont pas communiques au circuit mont. Il nest pas
ncessaire de connatre le fonctionnement du composant pour lutiliser efcacement.
Ce fonctionnement est masqu par son botier.
Le langage C++ gre lencapsulation laide de types dnis par lutilisateur : les classes.
Pour en savoir plus sur la conception dune classe, reportez-vous au Chapitre 6. Si elle est
correctement dnie, une classe agit comme une entit encapsule elle fonctionne
comme un composant autonome. Comme pour un objet du monde rel, son fonctionne-
ment interne peut tre masqu. Les utilisateurs dune classe bien conue nont pas besoin
de savoir comment elle fonctionne, mais uniquement comment lutiliser.
Hritage et rutilisabilit
Lorsquils souhaitent concevoir une nouvelle voiture, les ingnieurs de Superauto ont le
choix entre monter un projet partir de zro et modier un modle existant. Le modle
Pipo est peut-tre parfait pour la ville, mais cette voiture manque de nervosit sur auto-
route. Les ingnieurs ont donc dcid de lui adjoindre un turbo-compresseur et une bote
six vitesses. Le responsable du projet prfre donc partir dun modle existant, le perfec-
tionner, le tester, et lappeler Star. La Star sera donc une sorte de Pipo, mais il sagira
dune version spciale, disposant de nouvelles fonctionnalits.
Le langage C++ implmente la notion dhritage. Grce lhritage, vous pouvez dclarer
un nouveau type partir dun type existant. On dit que la sous-classe obtenue drive du
type existant ; on la nomme quelquefois "type driv". Si la Star est drive de la Pipo et
hrite donc de toutes ses qualits, certaines proprits peuvent lui tre ajoutes, dautres
modies. Pour en savoir plus sur lhritage et son application en C++, reportez-vous aux
Chapitres 12 et 16.
Polymorphisme
La Star ne rpondra pas ncessairement de la mme faon que la Pipo un appui sur
lacclrateur. En effet, la premire peut utiliser son injection et son turbo alors que la
seconde devra se contenter de sa carburation classique. Quoi quil en soit, il suft au
conducteur de dmarrer son vhicule et de se dplacer o bon lui semble. Il nest pas
oblig de connatre les dtails du moteur.
C++Livre Page 10 J eudi, 7. mai 2009 9:45 09
Chapitre 1 Bien dbuter en C++ 11
En C++, des objets diffrents peuvent avoir des comportements adquats diffrents en
rponse la mme action grce au polymorphisme de fonction et de classe. Poly signie
plusieurs et morphe signie forme. Le polymorphisme se traduit donc par un nom unique
pouvant prendre plusieurs formes, et il est trait aux Chapitres 10 et 14.
volution de C++
Lorsque les qualits de la programmation, de la conception et de lanalyse orientes objet
commencrent tre reconnues, Bjarne Stroustrup cra C++ partir du langage le plus
utilis pour le dveloppement des logiciels professionnels, le langage C. Il lui ajouta tous
les lments ncessaires la programmation oriente objet.
On a coutume de dire que C++ est un surensemble de langage C et que, par consquent,
tout programme C est virtuellement un programme C++. Pourtant, ces deux langages sont
trs diffrents. Pendant longtemps, C++ a attir les programmeurs C car ces derniers trou-
vaient sa syntaxe familire. Toutefois, pour tirer le meilleur prot des fonctionnalits de
C++, de nombreux dveloppeurs ont compris quils devaient laisser de ct une partie
de leurs acquis en C et aborder les problmes diffremment.
Est-il ncessaire dapprendre dabord le langage C ?
Nombreux parmi vous sont ceux qui vont se poser cette question, puisque C++ est un
surensemble du langage C. La rponse de son crateur et de la plupart des program-
meurs C++ est la suivante : il est inutile dapprendre le langage C, voire prfrable de
commencer directement par le langage C++.
La programmation C est fonde sur les concepts de programmation structure, alors que la
programmation C++ repose sur ceux de la programmation oriente objet. Si vous apprenez
dabord le langage C, vous devrez alors vous dfaire des habitudes nfastes lies ce
langage.
Ce livre ne sadresse pas obligatoirement un public ayant une exprience pralable de la
programmation. Si vous tes programmeur C, vous pouvez vous contenter de survoler les
premiers chapitres du livre. Le dveloppement orient objet nest rellement abord qu
partir du Chapitre 6.
C++, Java et C#
C++ est lun des principaux langages pour le dveloppement de logiciels professionnels.
Ces dernires annes, Java a eu un temps la faveur des programmeurs. Toutefois, nombre
C++Livre Page 11 J eudi, 7. mai 2009 9:45 09
12 Le langage C++
dentre eux, qui avaient abandonn C++ au prot de Java, ont depuis fait marche arrire.
En tout tat de cause, les deux langages sont si semblables quapprendre lun revient
connatre 90 % de lautre.
C# (prononcez C sharp) est un langage plus rcent dvelopp par Microsoft pour la plate-
forme .NET. Il utilise la mme syntaxe que C++ et, bien que ces deux langages diffrent
en quelques points importants, lapprentissage de C++ apporte la majorit des connaissan-
ces ncessaires lutilisation de C#. Si vous dcidiez par la suite dapprendre C#, linves-
tissement ralis dans lapprentissage de C++ vous sera trs bnque.
Extensions gres de Microsoft pour C++
Avec larrive de .NET, Microsoft a introduit les extensions gres pour C++ (Managed
C++). Il sagit dune extension du langage C++ lui permettant dutiliser la nouvelle plate-
forme Microsoft et ses bibliothques. Managed C++ permet surtout un programmeur
C++ de proter des fonctionnalits avances de lenvironnement .NET. Au cas o vous
dcideriez de crer des applications spciquement conues pour la plate-forme .NET,
vous devrez tendre votre connaissance du C++ standard pour y inclure ces extensions.
Norme ANSI
Le comit daccrditation des standard, qui dpend de lANSI (American National Standards
Institute), a labor un standard international pour C++.
Le standard C++ est galement appel norme ISO (International Organization for Stan-
dardization), norme NCITS (National Committee for Information Technology Standards),
norme X3 (ancienne appellation de NCITS), ou norme ANSI/ISO. Dans ce livre, nous
continuerons faire rfrence la norme ANSI car cest le terme le plus utilis.
Lobjectif du standard ANSI est de garantir la portabilit du C++ an que le code que vous
allez crire pour le compilateur de Microsoft, par exemple, ne gnre pas derreur avec un
autre compilateur. Le code prsent dans ce livre tant compatible ANSI, il pourra tre
compil sans erreur sur des plates-formes Macintosh, Windows ou Unix.
Pour la plupart des utilisateurs C++, le standard ANSI est transparent. La version la plus
rcente de cette norme est lISO/IEC 14882-2003. La version prcdente, lISO 14882-1998,
a bnci dune stabilit durable et tous les fournisseurs de renom la prennent en charge.
Nous nous sommes efforcs dassurer une totale compatibilit ANSI du code dans cette
dition.
Toutefois, noubliez pas que les compilateurs ne sont pas tous totalement compatibles avec
la norme. En outre, certaines parties de la norme ont t laisses au choix du concepteur
C++Livre Page 12 J eudi, 7. mai 2009 9:45 09
Chapitre 1 Bien dbuter en C++ 13
du compilateur : il nest donc pas garanti que ces parties se compilent ou fonctionnent de
la mme manire avec des compilateurs diffrents.
Les extensions gres pour C++ ne sappliquant qu la plate-forme .NET et,
ne faisant pas partie de la norme ANSI, elles ne sont pas traites dans cet
ouvrage.
Prparation la programmation
Plus que tout autre langage, C++ oblige le dveloppeur concevoir soigneusement une
application avant de lcrire. Les programmes gurant dans les premiers chapitres de cet
ouvrage ne ncessitent pas danalyse car ils sont triviaux. En revanche, les problmes
complexes rencontrs en programmation professionnelle ne peuvent tre rsolus quavec
cette approche. La conception et lanalyse permettent de mieux cerner les diffrents
aspects du problme. Un programme bien construit ne contient pas derreurs et peut ais-
ment tre mis jour. Daprs des tudes rcentes, on estime que le cot dun programme
repose 90 % sur la mise au point et la maintenance. La phase de conception permet de
rduire les cots et, par l mme, le prix de revient du logiciel.
Avant de passer la conception dun programme, vous devez connatre parfaitement le
problme rsoudre. Les programmes les plus simples comme les plus complexes sarti-
culent autour dun droulement clair et logique.
Il convient galement de dterminer si le problme peut tre rsolu laide dun programme
existant qui sera modi, ou laide dun logiciel du commerce. Quil choisisse lune ou
lautre solution, le programmeur ne manquera de toutes faons pas de travail : trouver des
solutions moins coteuses des problmes actuels produira toujours de nouvelles oppor-
tunits un peu plus tard.
En supposant que le problme rsoudre ait t bien compris et quil faille crire un
nouveau programme, vous tes prt commencer votre conception.
Le processus dapprhension totale du problme (analyse) et dlaboration dun plan pour
une solution (conception) est indispensable lobtention dune application professionnelle
de carrure internationale.
Votre environnement de dveloppement
Pour utiliser ce livre, nous supposons que vous disposez dun compilateur permettant de
saisir des donnes directement sur une "console" (par exemple une fentre de commande
MS-DOS ou une fentre shell), cest--dire sans vous proccuper dun environnement
In
f
o
C++Livre Page 13 J eudi, 7. mai 2009 9:45 09
14 Le langage C++
graphique comme Windows ou Macintosh. Recherchez une option comme console ou
easy window ou consultez la documentation de votre compilateur.
Votre compilateur peut faire partie dun environnement de dveloppement intgr (IDE)
ou possder son propre diteur de texte pour saisir le code source des programmes. Vous
pouvez galement utiliser un diteur de texte spar ou un logiciel de traitement de texte
du moment que vous produisez des chiers texte sans formatage ni style particuliers. Le
Bloc-notes de Windows, lditeur Edit de DOS, les diteurs Brief, Epsilon, Emacs et vi
sont particulirement bien adapts la saisie des codes sources. Si vous disposez dun trai-
tement de texte tel que WordPerfect ou Word (ou autre), utilisez la commande denregis-
trement au format texte simple.
Les documents crs partir dun diteur de texte sappellent des chiers sources. En C++,
ils portent traditionnellement lextension .cpp, .cp ou .c. Dans cet ouvrage, nous avons
choisi lextension .cpp. Vriez que votre compilateur la prend en charge.
La plupart des compilateurs C++ acceptent toutes les extensions et affectent
par dfaut lextension .cpp aux chiers sources. Toutefois, soyez prudents, car
certains compilateurs traitent les chiers .c comme du code C et les chiers
.cpp comme du code C++. Vriez la documentation du compilateur. Dans
tous les cas, une utilisation cohrente des .cpp pour les chiers du code source
C++ facilitera la tche des programmeurs qui devront comprendre votre code.
Cration du programme
La premire tape de la cration dun programme consiste crire les commandes adap-
tes (instructions) dans un chier source. Mme si les instructions du chier source
semblent quelque peu mystrieuses pour ceux qui ne connaissent pas C++, il sagit quand
mme dun format lisible. Le chier source nest pas un programme : il vous sera impossible
de le lancer ou de lexcuter comme vous le feriez avec un programme excutable.
Faire Ne pas faire
crire le chier source laide de lditeur de
texte fourni avec le compilateur ou dun
diteur externe.
Enregistrer votre chier source avec lexten-
sion .c, .cp ou .cpp.
Consulter la documentation du compilateur et
de lditeur de liens pour connatre les diff-
rentes tapes de la cration du programme.
Utiliser les fonctions de mise en forme dun
traitement de texte. Si vous utilisez un traite-
ment de texte, sauvegardez le chier source
au format texte ASCII.
Utiliser une extension .c si votre compila-
teur considre ces chiers comme du code C
et non du code C++.
In
f
o
C++Livre Page 14 J eudi, 7. mai 2009 9:45 09
Chapitre 1 Bien dbuter en C++ 15
Cration dun chier objet laide du compilateur
Pour transformer le code source en programme, vous allez utiliser un compilateur. Linvo-
cation et la conguration de cet outil variant dun compilateur un autre, il est vivement
recommand de consulter la documentation.
La compilation produit un chier objet. Le plus souvent, il porte lune des extensions .obj
ou .o. Toutefois, il ne sagit pas encore dun programme excutable. Pour crer le chier
.exe, vous devez utiliser lditeur de liens (linker).
Cration dun chier excutable laide de lditeur de liens
Les programmes C++ sont produits en liant un ou plusieurs chiers objet (.obj ou .o)
avec une ou plusieurs bibliothques. Une bibliothque regroupe des chiers fournis avec le
compilateur, achets sparment ou que vous crez et compilez vous-mme. Tous les
compilateurs C++ sont livrs avec une bibliothque de fonctions et de classes trs utiles,
que vous pouvez inclure dans vos programmes. Nous traiterons les classes et les fonctions
plus en dtail ultrieurement.
La cration dun chier excutable se dcompose en trois tapes :
1. Cration dun chier source portant lextension .cpp.
2. Compilation du code source en un chier objet portant lune des extensions .obj ou .o.
3. Liaison du chier objet avec les diffrentes bibliothques, an dobtenir le programme
excutable.
Cycle de dveloppement
Si chaque programme fonctionnait parfaitement ds le premier coup, le cycle de dvelop-
pement complet serait : criture du programme, compilation du code source, dition des
liens et lancement de lapplication. Malheureusement, rares sont les programmes qui ne
contiennent aucune erreur. Certaines feront chouer la compilation du programme,
dautres ldition des liens, dautres encore ne se dclareront quau moment de lexcution
(celles-ci sont souvent appeles "bogues").
Toute erreur, quelle quelle soit doit tre corrige. Ceci implique de modier le code
source, de le compiler nouveau, de recrer les liens et de relancer le programme.
La Figure 1.1 illustre les diffrentes tapes de ce cycle de dveloppement.
C++Livre Page 15 J eudi, 7. mai 2009 9:45 09
16 Le langage C++
Votre premier programme C++ : BONJOUR.cpp
Les ouvrages traitant de programmation proposent traditionnellement un programme qui
permet dafcher "Bonjour" lcran. Nous ne drogerons donc pas cette rgle !
Ouvrez lditeur de texte, puis tapez le code source du Listing 1.1 en respectant scrupuleu-
sement la syntaxe (ne tenez pas compte de la numrotation des lignes qui ne sert ici qu
clarier les choses). Aprs vrication, enregistrez le chier, compilez-le, ditez les liens,
puis lancez-le. Si tout se passe correctement, "Bonjour !" apparat lcran. Ne vous souciez
pas des instructions utilises ici ; pour le moment, nous nous contentons de vous prsenter
Figure 1.1
Les tapes du cycle
de dveloppement
dun programme C++.
Dbut
Ecriture/
modification
du code
source
Compilation
Erreurs de
compilation ?
Edition
de liens
Erreurs
d'dition
de liens ?
Lancement
du programme
Erreurs
d'excution ?
Oui
Oui
Oui
Non
Non
Non
Fin
C++Livre Page 16 J eudi, 7. mai 2009 9:45 09
Chapitre 1 Bien dbuter en C++ 17
le cycle de dveloppement. En fait, les diffrents aspects de ce programme seront traits
dans les deux prochains chapitres.
Le listing suivant est numrot gauche an de vous permettre de suivre pas pas
son droulement. Vous ne devez pas taper ces numros. Dans le premier exemple,
vous devez uniquement taper pour la premire ligne :
#include <iostream>
Listing 1.1 : Programme bonjour.cpp
1: #include <iostream>
2:
3: int main()
4: {
5: std::cout << "Bonjour!\n";
6: return 0;
7: }
Vriez que ce que vous avez tap est conforme ce chier source. Attention la ponctua-
tion ! Pour obtenir le symbole de redirection (<<) la ligne 5, appuyez deux fois sur la
touche <. Les termes std et cout sont spars par deux caractres deux-points (:).
Noubliez pas non plus le point-virgule qui termine les lignes 5 et 6.
La plupart des compilateurs compilent les chiers et ralisent ldition de liens dans la
foule. Reportez-vous votre documentation pour voir si vous devez fournir une option
particulire ou excuter une commande pour effectuer cette dition des liens. En cas
derreur, comparez votre chier source avec celui du Listing 1.1. Si le compilateur signale
que le chier iostream est introuvable, vous devrez peut-tre consulter votre documenta-
tion pour savoir comment votre compilateur prconise de congurer les chemins daccs
des chiers inclure et les variables denvironnement.
Si un message vous informe quil nexiste pas de prototype pour la fonction main, tapez
linstruction int main(); avant la ligne 3 (cest lune de ces vilaines variations entre
compilateurs). Si tel est le cas, vous devrez inclure cette instruction dans tous les programmes
de cet ouvrage, bien que la plupart des compilateurs nen auront pas besoin.
Votre programme, dans ce cas, ressemblera ceci :
#include <iostream>
int main(); // ligne inutile pour la plupart des compilateurs
int main()
{
std::cout <<"Bonjour!\n";
return 0;
}
A
tte
n
tio
n
C++Livre Page 17 J eudi, 7. mai 2009 9:45 09
18 Le langage C++
Sous Windows, essayez de lancer bonjour.exe (en remplaant lextension de lexcuta-
ble par celle de votre systme dexploitation ; sous Unix, par exemple, excutez bonjour,
car les excutables ne possdent pas dextension). Lexcution du programme doit
afcher :
Bonjour!
Si cest le cas, bravo ! Tout va pour le mieux. Vous venez de crer votre premier
programme C++. Il nest peut-tre pas spectaculaire, mais cest ainsi quont dbut la
plupart des programmeurs professionnels.
Certains programmeurs utilisant des IDE (comme Visual Studio ou Borland C++ Builder)
risquent de voir apparatre brivement le programme dans une fentre qui disparatra rapi-
dement, sans laisser le temps de lire ce quelle contient. Dans ce cas, ajoutez ces lignes
votre source, juste avant linstruction return:
char reponse;
std::cin >> reponse;
Ces lignes forcent le programme faire une pause jusqu ce que vous ayez saisi un carac-
tre suivi dun appui sur la touche Entre. Elles permettent donc de voir les rsultats de
votre test. Si elles sont ncessaires pour le chier bonjour.cpp, elles le seront srement
pour la plupart des programmes de cet ouvrage.
Utilisation des bibliothques standard
Si votre compilateur est trs ancien, il peut refuser de compiler le code ci-dessus car il ne
trouvera pas les nouveaux chier enttes du standard ANSI. Dans ce cas, modiez votre
programme de la faon suivante :
1: #include <iostream.h>
2:
3: int main()
4: {
5: cout << "Bonjour!\n";
6: return 0;
7: }
Vous remarquerez que le nom du chier entte se termine prsent par .h (point-h) et
que nous nutilisons plus std:: devant cout la ligne 5. Ceci correspond lancien style
(pr-ANSI) de chiers entte et signie que votre compilateur est trop ancien ; il convien-
dra pour les premiers chapitres, mais lorsque nous aborderons les modles et les exceptions,
il risque dtre inutilisable.
C++Livre Page 18 J eudi, 7. mai 2009 9:45 09
Chapitre 1 Bien dbuter en C++ 19
Comment tirer parti de votre compilateur
Ce livre nest pas spcique un compilateur donn ; les programmes inclus fonctionne-
ront donc avec nimporte quel compilateur C++ compatible ANSI, sur nimporte quelle
plate-forme (Windows, Macintosh, Unix, Linux, etc.).
Ceci dit, une grande majorit de programmeurs travaillent sous lenvironnement Windows
et la plupart des dveloppeurs professionnels ont recours aux compilateurs Microsoft. Il
nest pas possible dindiquer ici les dtails de la compilation et de ldition de liens avec
tous les compilateurs possibles, mais nous pouvons vous montrer comment dbuter avec
Microsoft Visual C++ ; cela ne devrait pas tre trs diffrent avec votre compilateur, bien
quil soit conseill de vous reporter sa documentation spcique.
Construction du projet Bonjour
Les tapes ncessaires la cration et au test du programme Bonjour sont les suivantes :
1. Lancez le compilateur.
2. Choisissez Nouveau/Projet dans le menu Fichier.
3. Choisissez Application Console Win32 et entrez un nom pour le projet, Bonjour, par
exemple, puis cliquez sur Terminer.
4. Slectionnez Projet vide dans les choix proposs et cliquez sur OK. Une bote de
dialogue contenant des informations sur le nouveau projet safche.
5. Cliquez sur OK. Vous revenez la fentre principale de lditeur.
6. Choisissez Nouveau dans le menu Fichier.
7. Choisissez Fichier C++ et donnez-lui un nom : bonjour, par exemple, que vous saisirez
dans la zone de texte Nom du chier.
8. Cliquez sur OK. Vous revenez la fentre principale de lditeur.
9. Saisissez le code du Listing prcdent.
10. Choisissez Gnrer bonjour dans le menu Gnrer.
11. Vriez que vous navez pas derreurs de compilation. Vous trouverez ces informations
au bas de lditeur.
12. Excutez le programme en appuyant sur Ctrl+F5.
13. Appuyez sur la barre despacement pour terminer le programme.
C++Livre Page 19 J eudi, 7. mai 2009 9:45 09
20 Le langage C++
Le programme sexcute, mais lafchage est si rapide que je nai pas le temps de lire.
Quest-ce qui ne va pas ?
Consultez la documentation de votre compilateur ; vous devez pouvoir contraindre le
programme respecter une pause aprs son excution. Avec les compilateurs Microsoft il
suft dutiliser la combinaison de touches Ctrl+F5.
Avec nimporte quel compilateur, vous pouvez insrer les lignes suivantes immdiatement
avant linstruction return (cest--dire entre les lignes 5 et 6 du Listing 1.1) :
char reponse;
std::cin >> reponse;
Le programme fera une pause pour attendre que vous entriez une valeur. Pour terminer
le programme, entrez un nombre (par exemple 1) puis appuyez sur Entre (si ncessaire).
La signication des termes std::cin et std::cout sera prsente dans les prochains
chapitres ; pour linstant, utilisez-les sans vous poser de questions.
Erreurs de compilation
Les erreurs de compilation peuvent survenir dans un certain nombre de cas. Le plus
souvent, il sagit dune faute de frappe ou dun oubli. Les bons compilateurs mettront la
ligne incrimine en vidence et vous indiqueront lerreur que vous avez commise. Les plus
performants proposeront mme une solution au problme rencontr !
Pour tester les fonctionnalits de votre compilateur, il suft dinsrer dlibrment une
erreur dans le programme bonjour.cpp, en tant la ligne 7 o doit se trouver laccolade
fermante.
Listing 1.2 : Erreur de compilation
1: #include <iostream>
2:
3: int main()
4: {
5: std::cout << "Bonjour!\n";
6: return 0;
Compilez de nouveau le programme. Votre compilateur afche une erreur similaire
celle-ci :
bonjour.cpp, line 7: fatal error C1004: unexpected end of file found.
C++Livre Page 20 J eudi, 7. mai 2009 9:45 09
Chapitre 1 Bien dbuter en C++ 21
Vous constatez que le message est un petit peu nigmatique et quil y est question dune
erreur qui correspond la ligne 7. Ici, le compilateur indique quil manque des lignes
sources et quil a atteint la n du chier sans dtecter laccolade fermante. En rgle gn-
rale, les messages derreur contiennent un numro de ligne, ce qui permet de rsoudre le
problme plus aisment.
Parfois, ces messages ne vous indiqueront que la proximit du problme : si un compilateur
pouvait parfaitement identier toutes les erreurs, il les corrigerait lui-mme.
Questions-rponses
Q Quelle est la diffrence entre un diteur de texte et un traitement de texte ?
R Contrairement un traitement de texte, un diteur de texte gnre des chiers texte
sans code de formatage ni de mise en page. Un simple diteur de texte contient du
texte brut, sans caractres gras, italiques, etc.
Q Mon compilateur comprend un diteur intgr. Dois-je lutiliser ?
R La plupart des compilateurs seront capables de traiter du code tap laide dun di-
teur de texte. Un diteur intgr permet de se dplacer rapidement dans le chier
source et de suivre pas pas le cycle de dveloppement avant et aprs compilation. Les
compilateurs modernes prsentent lavantage de pouvoir consulter laide en ligne, de
compiler le code en direct et de rsoudre les problmes sans quitter lenvironnement
de dveloppement.
Q Le compilateur a produit des messages davertissement. Puis-je les ignorer ?
R Les compilateurs produisent gnralement des avertissements et des erreurs. En cas
derreurs, le programme ne pourra pas tre totalement construit. En ce qui concerne
les avertissements, le compilateur poursuivra son travail et crera tout de mme le
programme.
Bien que de nombreux livres ne donnent pas de rponse cette question, la ntre est
non ! Ds maintenant, considrez les messages davertissement comme des messages
derreur. Le compilateur vous informe que quelque chose danormal ou de suspect a
t dtect. Lisez le message lcran, puis corrigez lerreur. Certains compilateurs
disposent mme dun paramtre qui permet de traiter tous les avertissements comme
des erreurs et donc dempcher que le programme ne devienne un excutable.
Q Quest-ce que la compilation ?
R La vritable compilation intervient lorsque vous lancez le compilateur. Il sagit de ltape
qui prcde ldition de liens et le lancement du programme. Les programmeurs ont
C++Livre Page 21 J eudi, 7. mai 2009 9:45 09
22 Le langage C++
tendance utiliser ce terme comme un raccourci dsignant la vritable tape de compi-
lation suivie de ldition de liens.
Testez vos connaissances
1. Quelle est la diffrence entre un interprteur et un compilateur ?
2. Comment compiler un chier source ?
3. quoi sert lditeur de liens ?
4. Quelles sont les diffrentes tapes du cycle de dveloppement ?
Exercices
1. Examinez le listing qui suit et, sans lexcuter, devinez ce quil permet de faire.
1: #include <iostream>
2: int main()
3: {
4: int x = 5;
5: int y = 7;
6: std::cout << end1;
7: std::cout << x+y << " " << x * y;
8: std::cout << end;
9: return 0;
10: }
2. Tapez le programme de lexercice prcdent. Compilez-le et ditez les liens. Que fait-
il ? Vous tiez-vous tromp en rpondant la question prcdente ?
3. Tapez le programme suivant, puis compilez-le. Quelle erreur produit-il ?
1: include <iostream>
2: int main()
3: {
4: std::cout << "Salut \n";
5: return 0;
6: }
C++Livre Page 22 J eudi, 7. mai 2009 9:45 09
2
Anatomie dun
programme C++
Au sommaire de ce chapitre
La structure dun programme C++
Les interactions entre ses composants
La nature et le rle dune fonction
Un programme C++ se compose dobjets, de fonctions, de variables et dun certain
nombre dautres composants. Lessentiel de cet ouvrage est consacr lexplication
dtaille de ces diffrents lments mais, pour comprendre comment ils sarticulent
ensemble, il convient dtudier un programme complet.
C++Livre Page 23 J eudi, 7. mai 2009 9:45 09
24 Le langage C++
Un programme simple
Le programme bonjour.cpp du Chapitre 1 est intressant bien des gards. Pour information,
le Listing 2.1 reproduit la version originale du chier source.
Listing 2.1 : Le programme bonjour.cpp prsente une structure type dapplication C++
1: #include <iostream>
2:
3: int main()
4: {
5: std::cout << "Bonjour!\n";
6: return 0;
7: }
Ce programme afche le rsultat suivant :
Bonjour!
La ligne 1 inclut le chier iostream dans le chier courant.
Le principe est le suivant : le premier caractre est le signe dise (#) qui indique une direc-
tive adresse au prprocesseur. Chaque fois que vous lancez le compilateur, le prproces-
seur sexcute en premier : son rle est de dtecter les lignes commenant par le signe
dise et deffectuer certaines oprations en fonction de ce prxe. Le prprocesseur sera
tudi en dtail au Chapitre 21.
La commande #include est une instruction du prprocesseur qui signie : "Le terme qui
suit est un nom de chier. Trouver ce chier et insrer ici son contenu". Les chevrons (< et
>) placs autour du nom du chier demandent au prprocesseur de rechercher ce chier
aux emplacements habituels. Si linstallation a t ralise correctement, le prprocesseur
trouvera le chier iostream dans le rpertoire contenant tous les chiers include. Ce
chier (dont le nom est labrviation de Input-Output-Stream) est utilis par cout, qui
permet dafcher le rsultat lcran. Le rle de la ligne 1 est dinclure le chier
iostream dans ce programme, comme si vous laviez vous-mme saisi.
Le prprocesseur est lanc avant le compilateur. Il traduit toutes les lignes
commenant par le signe dise (#) en commandes spciales qui rendent votre
code directement exploitable par le compilateur.
Les compilateurs ne sont pas tous cohrents dans leur prise en charge de
#include lorsque lon omet lextension des chiers. Si vous obtenez des messa-
ges derreur, vous devrez peut-tre modier le chemin de recherche de include
pour votre compilateur ou ajouter lextension au chier inclus.
In
f
o
In
f
o
C++Livre Page 24 J eudi, 7. mai 2009 9:45 09
Chapitre 2 Anatomie dun programme C++ 25
Le programme commence vraiment en ligne 3, avec la fonction main(). Cette fonction est
obligatoire dans tous les programmes C++. Une fonction est un bloc dinstructions qui
effectue une ou plusieurs actions et qui peut tre appele par une autre fonction, mais
main() est spciale car elle est appele automatiquement au lancement du programme.
Comme toutes les fonctions, main() doit indiquer le type de la valeur quelle renvoie.
Dans le programme bonjour.cpp, il sagit de int, ce qui signie que la fonction renverra
une valeur entire au systme dexploitation lorsquelle se terminera. Ici, elle renvoie la
valeur 0, comme le montre la ligne 6. Cette valeur renvoye a relativement peu dimpor-
tance et est assez rarement utilise, mais le standard C++ exige que la fonction main() soit
dclare ainsi.
Certains compilateurs permettent de dclarer main() comme renvoyant void,
ce qui signie quaucune valeur nest renvoye. Ceci nest plus autoris en
C++ et il est prfrable de ne pas prendre cette mauvaise habitude. Dclarez
main() comme renvoyant int et renvoyez simplement la valeur 0 la dernire
ligne de la fonction main().
Tous les systmes dexploitation permettent de tester la valeur renvoye par un
programme. La convention consiste renvoyer 0 pour indiquer que le programme
sest termin correctement, et une valeur diffrente de zro dans le cas
contraire.
Toutes les fonctions commencent par une accolade ouvrante ({) et se terminent par une
accolade fermante (}). Dans notre programme, les accolades de la fonction main() gu-
rent aux lignes 4 et 7. Toute instruction comprise entre ces deux accolades fait partie de la
fonction.
Le plat de rsistance du programme se trouve la ligne 5.
Lobjet cout permet dafcher un message lcran. Les objets et leurs particularits
seront prsents au Chapitre 6 et lobjet cout et son alter ego cin seront dcrits au Chapi-
tre 17. En C++, ces deux objets permettent de grer respectivement les sorties (notamment
lcran) et les entres (au clavier, par exemple).
cout est un objet fourni par la bibliothque standard. Une bibliothque est une collection
de classes. La bibliothque standard est fournie avec tout compilateur compatible ANSI.
On indique au compilateur que lobjet cout que lon souhaite utiliser fait partie de la
bibliothque standard laide du spcicateur despace de nom std. Vous pouvez en effet
avoir des objets portant le mme nom, mais provenant de fournisseurs diffrents et cest la
raison pour laquelle C++ prvoit lutilisation des "espaces de noms". Un espace de nom
est une faon de dire : "Lorsque je dis std::cout, je fais rfrence lobjet cout faisant
A
tte
n
tio
n
In
f
o
C++Livre Page 25 J eudi, 7. mai 2009 9:45 09
26 Le langage C++
partie de lespace de nom standard, et daucun autre". Vous le faites savoir au compilateur
en indiquant le terme std suivi de deux caractres deux-points avant lobjet cout. Nous
reparlerons des espaces de noms ultrieurement.
La syntaxe de cout est relativement simple. Ce terme est suivi de loprateur de redirec-
tion de sortie (<<). La variable ou la chane de caractres gurant immdiatement aprs <<
sera crite lcran. Si vous voulez produire un message directement lcran, noubliez
pas de le mettre entre guillemets ("), comme la ligne 5.
Loprateur de redirection est reprsent par deux signes "plus grand que"
sans espace entre eux.
Une chane de caractres est une suite de caractres imprimables.
Les deux derniers caractres, \n, correspondent un saut de ligne qui sera produit aprs le
message. Pour en savoir plus sur les retours chariot et les sauts de ligne, reportez-vous au
Chapitre 18.
La fonction main() se termine la ligne 7 avec laccolade fermante.
tude rapide de lobjet cout
Au Chapitre 17, vous apprendrez afcher des donnes lcran laide de lobjet cout.
Pour le moment, vous pouvez lutiliser sans connatre parfaitement son mode de fonction-
nement. Pour afcher une valeur sur votre moniteur, tapez le mot cout, suivi de lopra-
teur dinsertion (<<), cest--dire de deux signes "infrieur ". Mme si le signe est
constitu de deux caractres, C++ considre quil ny en a quun.
Tapez les donnes aprs cet oprateur. Le Listing 2.2 est un exemple dutilisation de
loprateur dinsertion. Recopiez le chier source de ce petit programme en indiquant vos
propres prnom et nom.
Listing 2.2 : Exemple dutilisation de cout
1: // Listing2.2 utilisation de std::cout
2: #include <iostream>
3: int main()
4: {
5: std::cout << "Salut.\n";
6: std::cout << "Je tape 5: " << 5 << "\n";
7: std::cout << "Loprateur std::endl ";
8: std::cout << "provoque un saut de ligne lcran.";
In
f
o
C++Livre Page 26 J eudi, 7. mai 2009 9:45 09
Chapitre 2 Anatomie dun programme C++ 27
9: std::cout << std::endl;
10: std::cout << "Voici un tres grand nombre:\t" << 70000;
11: std::cout << std::endl;
12: std::cout << "8 et 5 font:\t";
13: std::cout << 8+5 << std::endl;
14: std::cout << "Voici une fraction: \t\t";
15: std::cout << (float) 5/8 << std::endl;
16: std::cout << "Et un nombre astronomique: \t";
17: std::cout << (double) 7000 * 7000 << std::endl;
18: std::cout << "Remplacez le nom ";
19: std::cout << "par le votre...\n";
20: std::cout << "Elie Kopter est un programmeur C++! \n";
21: return 0;
22: }
Ce programme produit le rsultat suivant :
Salut.
Je tape 5: 5
Loprateur std::endl provoque un saut de ligne lcran.
Voici un trs grand nombre: 70000
8 et 5 font: 13
Voici une fraction: 0.625
Et un nombre astronomique: 4.9e+07
Remplacez le nom par le votre ...
Elie Kopter est un programmeurC++!
Certains compilateurs ont un bogue qui exige que des parenthses soit places
autour de laddition avant de la passer cout. La ligne 13 devient alors :
13: cout << (8+5) << std::endl;
la ligne 2, linstruction #include <iostream> intgre le chier iostream au code
source. Celui-ci est ncessaire cout et ses fonctions associes.
la ligne 5, cout est utilis dans sa version la plus simple pour afcher une chane de
caractres. Le symbole \n est un caractre de formatage spcial qui demande cout
dafcher un caractre "nouvelle ligne".
Trois valeurs sont ensuite passes cout la ligne 6. Chacune dentre elles est spare par
loprateur dinsertion. La premire valeur correspond "Je tape 5: ". Notez que
lespace situ aprs le caractre deux-points fait partie de la chane. Le chiffre 5 est alors
pass loprateur dinsertion, puis le programme saute une ligne (entre guillemets ou
apostrophes). La ligne suivante apparat lcran :
Je tape 5: 5
A
tte
n
tio
n
C++Livre Page 27 J eudi, 7. mai 2009 9:45 09
28 Le langage C++
Le chiffre suit immdiatement la chane de caractres, car vous navez pas insr de saut
de ligne. Sans le savoir, vous avez concatn deux valeurs.
La ligne 7 afche un message puis utilise le manipulateur std::endl qui a pour but
dcrire une nouvelle ligne lcran (Pour en savoir plus sur endl, reportez-vous au
Chapitre 16). endl faisant aussi partie de la bibliothque standard, on utilise le prxe
std::, comme pour cout.
endl signie end line (ligne nale). La dernire lettre est bien la lettre "l" et non le
chiffre 1.
Lutilisation de endl est prfrable celle de \n, car il est adapt au systme
dexploitation utilis. En revanche, \n risque de ne pas reprsenter le caractre de
nouvelle ligne complet exig sur un systme dexploitation ou une plate-forme
particuliers.
la ligne suivante, vous dcouvrez un nouveau caractre de formatage : \t. Il permet
dinsrer une tabulation pour aligner les chanes de caractres (lignes 10 16). La ligne 10
montre que lon peut afcher tous les nombres entiers, quel que soit leur type. la
ligne 14, on passe une addition (8 + 5) cout, qui afche 13.
la ligne 15, la fraction 5/8 est transmise cout. Le terme (float) indique quune valeur
dcimale ( virgule ottante) doit safcher. Lexpression 7000 * 7000 est ensuite trans-
mise cout (ligne 17) et le terme double indique cout quil sagit dune valeur
virgule ottante. Pour obtenir des informations dtailles sur les types de donnes, reportez-
vous au Chapitre 3.
Aux lignes 18 et 20, vous avez remplac le nom du programmeur par le vtre. Si cest le
cas, la sortie conrmera que vous tes un programmeur C++, ce qui doit tre vrai puisque
cest lordinateur qui le dit !
Utilisation de lespace de nom standard
Lutilisation systmatique de std:: devant cout et endl pouvant devenir plutt fastidieuse
la longue, la norme ANSI propose deux solutions ce petit problme.
La premire consiste indiquer au compilateur, au dbut du code, que vous allez utiliser
les objets cout et endl de la bibliothque standard, comme au Listing 2.3 (lignes 5 et 6).
Listing 2.3 : Utilisation du mot-cl using
1: // Listing2.3 - Utilisation du mot-cl using
2: #include <iostream>
In
f
o
C++Livre Page 28 J eudi, 7. mai 2009 9:45 09
Chapitre 2 Anatomie dun programme C++ 29
3: int main()
4: {
5: using std::cout;
6: using std::endl;
7:
8: cout << "Salut.\n";
9: cout << "Je tape 5: " << 5 << "\n";
10: cout << "Loprateur endl ";
11: cout << "provoque un saut de ligne a lcran.";
12: cout << endl;
13: cout << "Voici un trs grand nombre:\t" << 70000;
14: cout << endl;
15: cout << "8 et 5 font:\t";
16: cout << 8+5 << endl;
17: cout << "Voici une fraction:\t\t";
18: cout << (float) 5/8 << endl;
19: cout << "Et un nombre astronomique:\t";
20: cout << (double) 7000 * 7000 << endl;
21: cout << "Remplacez le nom ";
22: cout << "par le votre...\n";
23: cout << "Elie Kopter est un programmeur C++!\n";
24: return 0;
25: }
Ce qui produit le rsultat suivant :
Salut.
Je tape 5: 5
Loprateur endl provoque un saut de ligne lcran.
Voici un tres grand nombre: 70000
8 et 5 font: 13
Voici une fraction: 0.625
Et un nombre astronomique: 4.9e+07
Remplacez le nom par le vtre ...
Elie Kopter est un programmeurC++!
La sortie est identique. Les lignes 5 et 6 du Listing 2.3 informent le compilateur laide
du mot-cl using, que nous allons utiliser deux objets de la bibliothque standard. Il nest
dans ce cas plus ncessaire de qualier les objets cout et endl.
Une autre mthode consiste informer le compilateur que nous allons utiliser lespace de
nom standard en totalit ; cest--dire que tout objet non explicitement quali sera consi-
dr comme provenant de lespace de nom standard. Au lieu dcrire std::cout; nous
allons simplement prciser, comme au Listing 2.4 : using namespace std;.
C++Livre Page 29 J eudi, 7. mai 2009 9:45 09
30 Le langage C++
Listing 2.4 : Utilisation du mot-cl namespace
1: // Listing2.4 - Utilisation de lespace de nom standard
2: #include <iostream>
3: int main()
4: {
5: using namespace std;
6:
7: cout << "Salut.\n";
8: cout << "Je tape 5: " << 5 << "\n";
9: cout << "Loprateur endl ";
10: cout << "provoque un saut de ligne lcran.";
11: cout << endl;
12: cout << "Voici un trs grand nombre:\t" << 70000;
13: cout << endl;
14: cout << "8 et 5 font:\t";
15: cout << 8+5 << endl;
16: cout << "Voici une fraction:\t\t";
17: cout << (float) 5/8 << endl;
18: cout << "Et un nombre astronomique:\t";
19: cout << (double) 7000 * 7000 << endl;
20: cout << "Remplacez le nom indique";
21: cout << "par le votre...\n";
22: cout << "Elie Kopter est un programmeur C++!\n";
23: return 0;
24: }
La sortie sera ici encore identique celle des prcdentes versions de ce programme.
Lintrt dcrire using namespace std; est quil nest plus ncessaire de dsigner
nommment les objets que vous allez utiliser (cout et endl). Vous prenez par contre le
risque dutiliser par inadvertance des objets dune bibliothque inadquate.
Les puristes prfrent crire std:: devant chaque instance de cout et endl. Les paresseux
prfreront using namespace std;.
Commentaires
Lors de la saisie dun programme, vous savez avec prcision ce que vous faites. Mais si
vous rouvrez le chier source un mois plus tard, certains passages du listing risquent de
vous paratre difciles comprendre.
Pour viter ce dsagrment et permettre aux autres de comprendre vos chiers, nhsitez
pas insrer des commentaires. Les commentaires sont du texte simple qui nest pas trait
C++Livre Page 30 J eudi, 7. mai 2009 9:45 09
Chapitre 2 Anatomie dun programme C++ 31
par le compilateur. Ils ont pour but dinformer le lecteur sur telle ou telle action du
programme.
Les diffrents types de commentaires
C++ utilise deux types de commentaires : les commentaires sur une seule ligne et les
commentaires multilignes.
Les premiers sont introduits par deux caractres slashs (//) qui indiquent au compilateur
quil doit ignorer tout ce qui suit jusqu la n de la ligne.
Les seconds sont introduits par les caractres slash-toile (/*) et demandent au compi-
lateur de ne pas traiter les caractres qui suivent, jusquau symbole de n de commen-
taires toile-slash (*/). Ces marques peuvent se trouver sur la mme ligne ou tre
spares par plusieurs lignes. chaque symbole /* doit correspondre un symbole de n
de commentaires */.
En gnral, les programmeurs C++ utilisent le double slash, puis une ligne de commen-
taire. Ils rservent les commentaires de plusieurs lignes pour dlimiter de grandes parties
dun programme. Vous pouvez inclure des commentaires dune ligne dans un bloc
"comment" : ce qui gure entre les symboles de commentaires multilignes nest pas pris
en compte, y compris les doubles slashs.
Les commentaires sur plusieurs lignes sont connus sous le nom de "style C" car
ils ont t introduits par le langage C. Les commentaires dune ligne sont appa-
rus avec le C++ et sont donc devenus le "style C++". Les normes actuelles de
ces deux langages intgrent les deux styles.
Utilisation des commentaires
Certains prconisent dcrire des commentaires au dbut de chaque fonction pour indiquer
son rle et les valeurs quelle renvoie.
Cette recommandation est discutable, car ce type de commentaire mis en en-tte nest
pratiquement jamais remis jour lors des diffrentes volutions du programme. Les noms
de fonctions doivent tre sufsamment explicites pour viter toute ambigut et les
portions de code trop nigmatiques doivent tre rcrites pour tre sufsamment claires
par elles-mmes. Les commentaires ne doivent pas tre une excuse dun code obscur.
Ceci ne veut pas dire que les commentaires ne servent rien, mais simplement quils ne
doivent pas se substituer un code lisible : il est toujours prfrable de clarier un code
plutt que dexpliquer ce quil fait. Bref, crivez du code lisible et nutilisez les commen-
taires que pour fournir des informations supplmentaires.
In
f
o
C++Livre Page 31 J eudi, 7. mai 2009 9:45 09
32 Le langage C++
Le Listing 2.5 va vous familiariser avec lusage des commentaires. Il dmontre que ceux-
ci ninuent en rien sur le traitement et sur lafchage des donnes.
Listing 2.5 : aide.cpp montre lusage des commentaires
1: #include <iostream>
2:
3: int main()
4: {
5: using std::cout;
6:
7: /* Ceci est un commentaire qui se termine
8: lorsque le symbole toile-slash
9: est rencontr */
10: cout << "Bonjour!\n";
11: // Ce commentaire se termine la fin de la ligne
12: cout << "Exemples de commentaires!\n";
13:
14: // Les commentaires double slash peuvent occuper une seule
15: /* ligne tout comme les commentaires de type slash-toile */
16: return 0;
17: }
Ce programme produit le rsultat suivant :
Bonjour!
Exemples de commentaires!
Les commentaires des lignes 7 9 sont ignors par le compilateur, tout comme ceux des
lignes 11, 14 et 15. Les commentaires de la ligne 11 sont compris entre le symbole // et le
retour la ligne, alors que ceux des lignes 7 et 15 requirent un symbole de fermeture.
Certains compilateurs C++ reconnaissent un troisime type de commentaires :
les commentaires de documentation, signals par trois slashes (///). Les
compilateurs qui les acceptent permettent de produire de la documentation
partir de ces commentaires. Cependant, cette syntaxe ne fait pas encore partie
de la norme C++ et ne sera donc pas dtaille ici.
Quelques conseils...
vitez dinsrer des commentaires lorsque lopration effectue est vidente. En effet, ils
surchargent le chier source et risquent dtre oublis par le programmeur lors de la
prochaine mise jour. Cela tant dit, ce qui peut sembler vident une personne peut
paratre obscur une autre : vous devez donc faire preuve de discernement lorsque vous
placez des commentaires.
In
f
o
C++Livre Page 32 J eudi, 7. mai 2009 9:45 09
Chapitre 2 Anatomie dun programme C++ 33
En rsum, les commentaires ne doivent pas dire ce qui se passe, mais expliquer pourquoi
cela arrive.
Les fonctions
Bien que main() soit une fonction, ses caractristiques la distinguent des autres. En effet,
lutilit des fonctions standard est quelles peuvent tre appeles ou invoques tout moment
dans un programme ; or la fonction main() est appele par le systme dexploitation.
Le droulement dun programme est squentiel (cest--dire quil suit lordre des lignes de
code). Quand il rencontre une instruction dappel dune fonction, il se droute pour excuter
cette dernire. Lorsque la fonction se termine, le programme continue son excution avec
la ligne place immdiatement aprs lappel de la fonction.
Comparons lappel dune fonction une action de la vie courante : alors que vous dessinez
un paysage, la mine de votre crayon se casse. Vous cessez immdiatement de dessiner an
de tailler votre crayon. Puis vous reprenez votre activit artistique l o vous laviez inter-
rompue. Il en est de mme dans un programme. Lorsque celui-ci a besoin dun service, il
fait appel une fonction, puis reprend son droulement une fois que celle-ci est termine.
Le Listing 2.6 illustre ce concept.
Pour plus dinformations sur les fonctions, reportez-vous au Chapitre 5. Les
types de donnes prises en charge seront prsentes au Chapitre 3. Ce chapi-
tre-ci donne uniquement un aperu des fonctions, car on les retrouvera dans la
plupart des programmes C++.
Listing 2.6 : Appel de fonction dans un programme
1: #include <iostream>
2:
3: // Fonction DemoFonction
4: // affiche un message dinformation
5: void DemoFonction()
6: {
7: std::cout << "Dans la fonction DemoFonction \n";
8: }
9:
10: // Fonction main - affiche un message,
11: // appelle DemoFonction, puis affiche
12: // un second message.
13: int main()
14: {
15: std::cout << "Dans la fonction main\n";
In
f
o
C++Livre Page 33 J eudi, 7. mai 2009 9:45 09
34 Le langage C++
16: DemoFonction();
17: std::cout << "Retour dans main\n";
18: return 0;
19: }
Ce programme produit le rsultat suivant :
Dans la fonction main
Dans la fonction DemoFonction
Retour dans main
La fonction DemoFonction() est dnie aux lignes 6 8. Lorsquelle est appele, elle afche
un message, puis rend la main au programme.
Le vritable programme commence la ligne 13. la ligne 15, la fonction main() afche
un message indiquant que lon est dans la fonction principale. la ligne suivante, la fonc-
tion DemoFonction() est appele, ce qui signie que les instructions de cette dernire
vont sexcuter. Dans le cas prsent, la fonction se rsume un message (ligne 7). la
ligne 8, DemoFonction() se termine. Le programme reprend son excution la ligne 17 et
la fonction main() afche son dernier message.
Utilisation des fonctions
Lintrt dune fonction est de transmettre au programme une valeur dont il a besoin. Par
exemple, une fonction qui additionne deux entiers renverra la somme obtenue sous la
forme dune valeur entire. En revanche, une fonction qui afche simplement un message
ne transmet pas de valeur au programme et doit donc tre dclare comme renvoyant une
valeur void.
Une fonction se compose dun en-tte et dun corps. Len-tte se divise lui-mme en
plusieurs parties : le type de la valeur renvoye, le nom de la fonction et les paramtres
quelle attend. Ces derniers permettent de transmettre des valeurs la fonction. Par exem-
ple, si la fonction additionne deux nombres, ceux-ci devront lui tre passs en paramtre.
La ligne ci-dessous est un en-tte caractristique dune fonction Somme qui attend deux
valeurs entires (a et b), puis renvoie une valeur entire :
int Somme(int a, int b)
Un paramtre formel est une dclaration du type de la valeur transmise la fonction. La
valeur relle passe par la fonction appelante sappelle le paramtre rel (certains
programmeurs utilisent galement le terme dargument).
Le corps dune fonction se compose dune accolade ouvrante, de zro ou plusieurs instruc-
tions et dune accolade fermante. Les instructions constituent le travail rel de la fonction.
C++Livre Page 34 J eudi, 7. mai 2009 9:45 09
Chapitre 2 Anatomie dun programme C++ 35
Une fonction peut renvoyer une valeur laide de linstruction return. Le type de cette
valeur doit correspondre celui qui a t dclar dans len-tte de la fonction. En outre,
lexcution de linstruction return provoque galement la n de lexcution de la fonc-
tion. Une fonction qui se termine sans passer par une instruction return renvoie void
(rien) lorsquelle atteint son accodade fermante. En outre, une fonction cense renvoyer
une valeur mais se terminant sans instruction return peut provoquer un message daver-
tissement ou une erreur avec certains compilateurs.
Dans le Listing 2.7, la fonction attend deux paramtres entiers et renvoie une valeur entire.
Pour le moment, ne vous attardez pas sur la syntaxe et sur le traitement des valeurs
entires (comme int x). Vous dcouvrirez ces sujets au Chapitre 3.
Listing 2.7 : Mise en uvre dune fonction simple
1: #include <iostream>
2: int Somme (int x, int y)
3: {
4: std::cout << "Somme() a recu " << x << " et " << y << "\n";
5: return (x + y);
6: }
7:
8: int main()
9: {
10: using std::cout;
11: using std::cin;
12:
13:
14: cout << "Vous etes dans main()!\n";
15: int a, b, c;
16: cout << "Entrez deux nombres: ";
17: cin >> a;
18: cin >> b;
19: cout << "\nAppel de Somme()\n";
20: c = Somme(a, b);
21: cout << "\nRetour dans main().\n";
22: cout << "c vaut maintenant " << c;
23: cout << "\nFin du traitement...\n\n";
24: return 0;
25: }
Ce programme produit le rsultat suivant :
Vous etes dans main()!
Entrez deux nombres: 35
Appel de Somme()
C++Livre Page 35 J eudi, 7. mai 2009 9:45 09
36 Le langage C++
Somme() a recu 3 et 5
Retour dans main().
c vaut maintenant 8
Fin du traitement...
Dnie la ligne 2, la fonction Somme() reoit deux paramtres entiers et renvoie une
valeur entire. Le programme commence la ligne 8 ; il afche un message invitant
lutilisateur entrer deux nombres (ligne 16), spars par un espace. Ces nombres sont
placs dans les variables a et b aux lignes 17 et 18. Pour valider, tapez sur Entre. La
fonction main() passe ces deux nombres en paramtres la fonction Somme()
(ligne 20).
Le programme se droute dans la fonction Somme() qui commence la ligne 2. Les
valeurs de a et b sont reues, respectivement, par les paramtres x et y. La fonction afche
ces valeurs puis les additionne ; le rsultat obtenu est renvoy la ligne 5, puis la fonction
rend la main la fonction qui la appele la fonction main(), ici.
Dans les lignes 17 et 18, lobjet cin permet dobtenir des valeurs pour les variables a et b,
qui sont ensuite afches laide de lobjet cout. Nous traiterons en dtail les variables et
les diffrents aspects dun programme dans les chapitres suivants.
Mthodes ou fonctions
Il faut noter ici que les diffrents langages de programmation et les diffrentes mtho-
dologies peuvent dsigner les fonctions par un autre nom. Lun des termes les plus
habituels est celui de mthode. Une mthode est simplement une fonction qui fait partie
dune classe.
Questions-rponses
Q Quelle est la signication du terme #include ?
R Il sagit dune directive du prprocesseur qui sexcute lorsque vous appelez le compi-
lateur. Cette directive insre le chier dont le nom est indiqu entre chevrons, comme
si vous laviez saisi en totalit, dans le code source du programme.
Q Quelle est la diffrence entre les commentaires // et les commentaires de type /* ?
R Les commentaires double slash (//) (commentaires sur une ligne) prennent n avec le
retour la ligne, alors que les commentaires slash-toile (/*) (commentaires sur plu-
sieurs lignes) se terminent avec un symbole de fermeture (*/). Noubliez pas de fermer
tous les commentaires multilignes an dviter toute erreur de compilation.
C++Livre Page 36 J eudi, 7. mai 2009 9:45 09
Chapitre 2 Anatomie dun programme C++ 37
Q Quest-ce qui distingue un bon commentaire dun mauvais ?
R Un bon commentaire indique les actions qui vont tre ralises par une fonction, une
instruction ou un bloc dinstructions. Un mauvais commentaire surcharge le code de
formules redondantes et dexplications inutiles. Le code des programmes doit tre
crit de faon se comprendre aisment par lui-mme. Une ligne bien crite ne devrait
pas avoir besoin dun commentaire pour expliquer ce quelle fait, mais cest vous de
distinguer ce qui mrite commentaire et ce qui est vident pour tous.
Testez vos connaissances
1. Quelle est la diffrence entre un compilateur et un prprocesseur ?
2. Quelles sont les particularits de la fonction main() ?
3. Quels sont les deux types de commentaires admis en C++ ? En quoi diffrent-ils ?
4. Est-il possible dimbriquer des commentaires ?
5. Un commentaire peut-il tre plus long quune ligne ?
Exercices
1. crivez un programme qui afche "Jadore C++" lcran.
2. crivez le plus petit programme pouvant tre compil, li, puis lanc.
3. CHERCHEZ LERREUR : compilez ce programme. Pourquoi choue-t-il ? Corrigez
lerreur.
1: #include <iostream>
2: main()
3: {
4: std::cout << Y a-t-il un bogue dans la salle?";
5: }
4. Aprs avoir corrig ce bogue, recompilez le programme puis lancez-le.
5. Incluez dans le Listing 2.7 une fonction de soustraction, nomme Difference() et
utilisez-la de la mme manire que la fonction Somme(). Passez-lui les valeurs qui ont
t passes la fonction Somme().
C++Livre Page 37 J eudi, 7. mai 2009 9:45 09
C++Livre Page 38 J eudi, 7. mai 2009 9:45 09
3
Variables et constantes
Au sommaire de ce chapitre
Dclarer et dnir des variables et des constantes
Affecter des valeurs aux variables, puis grer ces valeurs
Afcher le contenu dune variable lcran
Les programmes doivent mmoriser les donnes quils utilisent ou les crer pour les ruti-
liser lors de lexcution du programme. Pour cela, les variables et les constantes proposent
plusieurs mthodes pour reprsenter, stocker et manipuler des informations.
Quest-ce quune variable ?
En C++, une variable est un emplacement destin recevoir des donnes. Cette zone se
situe dans la mmoire vive de votre ordinateur et reoit une valeur qui peut ensuite tre
relue.
Les variables sont utilises pour un stockage temporaire. Si vous quittez un programme et
arrtez votre ordinateur, linformation stocke dans ces variables est perdue. Un stockage
C++Livre Page 39 J eudi, 7. mai 2009 9:45 09
40 Le langage C++
permanent est un autre problme. Gnralement, pour stocker des valeurs de faon persis-
tante, vous devez les enregistrer soit dans une base de donnes, soit dans un chier sur le
disque. Cette dernire mthode est prsente au Chapitre 16.
Stockage des donnes en mmoire
La mmoire de votre ordinateur peut tre compare un ensemble de cases alignes les
unes ct des autres. Ces emplacements sont numrots squentiellement par des
nombres appels adresses mmoire. Une variable peut occuper une ou plusieurs cases an
de stocker la valeur quelle contient.
Le nom de la variable (par exemple, maVariable) est une tiquette place sur lune des
cases. Il identie la variable et vous pargne de devoir connatre son adresse relle en
mmoire. La Figure 3.1 est une reprsentation symbolique de lagencement de la
mmoire. Comme vous pouvez le constater, maVariable commence ladresse mmoire
103. En fonction de sa taille, elle peut occuper une ou plusieurs adresses.
RAM est lacronyme de Random Access Memory (mmoire accs direct).
Lorsque vous lancez un programme, il est charg du disque vers la RAM.
La mmoire vive est galement le sige de toutes les variables du
programme. En programmation, le terme mmoire dsigne le plus souvent la
mmoire vive.
Gnralits sur la mmoire
En C++, vous devez attribuer un type chaque variable que vous dnissez. Il peut sagir
dune variable de type entier, dun nombre virgule ottante, dun caractre, etc. Grce
ces informations, le compilateur dtermine lespace attribuer la variable en mmoire,
ainsi que le type des donnes quelle pourra contenir. Cela permet aussi au compilateur de
vous avertir ou de produire un message derreur si vous tentez, par inadvertance, de stocker
une valeur dun type erron dans votre variable (cette caractristique de certains langages
de programmation sappelle typage fort).
Figure 3.1
Reprsentation
schmatique
de la mmoire.
maVariable
100 101 102 103 104 105 106
Nom de la variable
RAM
Adresse
In
f
o
C++Livre Page 40 J eudi, 7. mai 2009 9:45 09
Chapitre 3 Variables et constantes 41
Chaque emplacement de la RAM occupe un octet. Si le type de variable cr a une taille
de 4 octets, celle-ci occupera donc quatre emplacements en mmoire. Le type de la variable
(int, par exemple) indique au compilateur combien despace mmoire (ou demplacements)
rserver dans la RAM.
une poque, les programmeurs devaient imprativement comprendre la notion de bits et
doctets, qui sont en fait les units de base de stockage. Bien que les langages actuels
permettent de faire abstraction de ces dtails, il demeure intressant de comprendre
comment sont stockes les donnes. Pour en savoir plus, reportez-vous lAnnexe A.
Taille des entiers
Dun ordinateur un autre, les tailles occupes par les diffrents types de variables
peuvent tre diffrentes, mais elles seront toujours les mmes sur un ordinateur donn.
Ainsi, un entier peut occuper deux octets sur une machine ou quatre sur une autre, mais
cette proprit ne variera jamais sur lune ou lautre de ces machines.
Les caractres simples, comme les lettres, les chiffres ou les symboles, sont stocks dans
des variables de type char, qui occupent gnralement un octet.
Pour les nombres entiers les plus petits, on peut employer une variable de type short qui,
sur la plupart des ordinateurs, occupera deux octets, alors quune variable de type long
occupe gnralement quatre octets. Si vous nindiquez pas le mot-cl short ou long, la
taille dun entier varie de deux quatre octets selon les ordinateurs.
En fait, contrairement ce que lon pourrait attendre, le langage ne le prcise pas exacte-
ment. Tout ce que nous savons est que la taille dun entier short doit tre infrieure ou
gale celle dun int qui, son tour, doit tre infrieure ou gale celle dun entier long.
Ceci dit, vous travaillez srement sur un ordinateur ayant des short sur deux octets et des
int et des long de quatre octets.
La taille dun entier est dtermine par le processeur (16 bits, 32 bits ou 64 bits) et le
compilateur que vous utilisez. Sur une machine 32 bits (Pentium) utilisant des compilateurs
modernes, les entiers occupent quatre octets.
Lorsque vous crivez des programmes, ne faites jamais de supposition sur la
taille mmoire utilise pour reprsenter un type particulier.
Pour dterminer la taille exacte des types pris en charge par votre ordinateur, compilez et
excutez le programme du Listing 3.1.
A
tte
n
tio
n
C++Livre Page 41 J eudi, 7. mai 2009 9:45 09
42 Le langage C++
Listing 3.1 : Dterminer la taille des types de variables sur votre ordinateur
1: #include <iostream>
2:
3: int main()
4: {
5: using std::cout;
6:
7: cout << "La taille dun entier est:\t\t"
8: << sizeof(int) << " octets.\n";
9: cout << "La taille dun entier court est:\t"
10: << sizeof(short) << " octets.\n";
11: cout << "La taille dun entier long est:\t"
12: << sizeof(long) << " octets.\n";
13: cout << "La taille dun type char est:\t\t"
14: << sizeof(char) << " octets.\n";
15: cout << "La taille dun type float est:\t\t"
16: << sizeof(float) << " octets.\n";
17: cout << "La taille dun type double est:\t"
18: << sizeof(double) << " octets.\n";
19: cout << "La taille dun booleen est:\t\t"
20: << sizeof(bool) << " octets.\n";
21:
22: return 0;
23: }
Ce programme produit le rsultat suivant :
La taille dun entier est: 4octets.
La taille dun entier court est: 2octets.
La taille dun entier long est: 4octets.
La taille dun type char est: 1octets.
La taille dun type float est: 4octets.
La taille dun type double est: 8octets.
La taille dun booleen est: 1 octets.
Sur votre ordinateur, le nombre doctets annonc peut tre diffrent !
Le Listing 3.1 ne prsente pas de difcults majeures. Certaines lignes ont t scindes en
deux pour des raisons ddition. Par exemple, les lignes 7 et 8 pourraient normalement
tenir sur une seule ligne. Le compilateur ignore les blancs (espace, tabulation, saut de
ligne) et considre ces instructions comme une seule ligne. Cest pourquoi vous devez
terminer la plupart des lignes par un signe ";".
In
f
o
C++Livre Page 42 J eudi, 7. mai 2009 9:45 09
Chapitre 3 Variables et constantes 43
Les lignes 7 20 font appel loprateur sizeof, qui est utilis comme une fonction.
Il renvoie la taille de lobjet qui lui a t pass en paramtre. Par exemple, la ligne 8, le mot
cl int est pass sizeof. Vous verrez plus loin dans ce chapitre que int permet de dcrire
une variable entire standard. Si lon utilise sizeof sur un Pentium 4 sous Windows XP, la
taille dune valeur int quivaut celle dun entier long, soit quatre octets.
Les autres lignes du Listing 3.1 afchent les tailles des autres types de donnes. Vous en
saurez plus sur les valeurs que ces types de donnes peuvent stocker et ce qui les diffrencie
un peu plus loin.
signed et unsigned
Toutes les variables entires ont deux variantes : signe (signed) ou non signe (unsi-
gned). En effet, on a parfois besoin de nombre ngatifs, parfois non. Les entiers dclars
sans le mot-cl unsigned sont considrs comme signs. Les entiers signs sont soit ngatifs,
soit positifs, alors que les entiers non signs sont toujours positifs.
Les entiers, quils soient signs ou non, occupent le mme espace mmoire. De ce fait, une
partie de la place de stockage dun entier sign sera donc utilise pour indiquer sil est positif
ou ngatif. La valeur la plus leve que vous pouvez stocker dans un entier non sign corres-
pond donc au double du nombre positif le plus lev contenu dans une variable entire signe.
Par exemple, si un entier short est stock sur deux octets, un entier short non sign peut
contenir une valeur comprise entre 0 et 65 535. Pour un entier short sign, la moiti des
valeurs admises correspond des nombres ngatifs. Lintervalle autoris se situe donc
entre 32 768 et +32 767.
Types fondamentaux
C++ dispose de plusieurs types prdnis, qui peuvent tre diviss en types entiers (voir
plus haut), en types ottants et en type caractre.
Les variables virgule ottante peuvent tre exprimes sous la forme de fractions, ce sont
des nombres rels. Les variables de type caractre noccupent gnralement quun seul
octet et servent souvent stocker les 256 caractres et symboles du jeu ASCII standard et
des jeux ASCII tendus (en vigueur dans certaines langues).
Le jeu de caractres ASCII est un ensemble normalis de lettres, de nombres et
de symboles reconnu par tous les ordinateurs. ASCII est lacronyme de Ameri-
can Standard Code for Information Interchange. Cette norme de codage est
prise en charge par la plupart des systmes dexploitation, bien que nombre
dentre eux prennent galement en charge dautres jeux de caractres interna-
tionaux.
In
f
o
C++Livre Page 43 J eudi, 7. mai 2009 9:45 09
44 Le langage C++
Les types de variables utilises en C++ sont dcrits dans le Tableau 3.1. En face du libell
du type C++ gurent la traduction franaise ainsi que sa taille et les valeurs qui peuvent
tre stockes. Ces valeurs sont dtermines par la taille des types de variables, conform-
ment la sortie produite par le programme du Listing 3.1 ; consultez son rsultat pour voir
si vos types de variables sont de la mme taille. Il est trs probable que ce sera le cas,
moins que vous nutilisiez un ordinateur ayant un processeur 64 bits.
La taille des variables peut varier en fonction du compilateur et du processeur
de votre ordinateur. En fait, le Tableau 3.1 et le listing qui prcde se
compltent, car les valeurs ont t extraites de la mme conguration. Si le
programme afche un rsultat diffrent, reportez-vous au manuel de votre
compilateur.
Dnition dune variable
Pour linstant, vous avez vu la cration et lutilisation de plusieurs variables. Il est temps
maintenant dapprendre crer les vtres.
Tableau 3.1 : Types de variables
Type C++ Traduction franaise Taille Valeurs
bool boolen 1 octet true (vrai) ou false (faux)
unsigned short int entier court non sign 2 octets de 0 65 535
short int entier court 2 octets de 32 768 32 767
unsigned long int entier long non sign 4 octets de 0 4 294 967 295
long int entier long 4 octets de 2 147 483 648 2 147 483 647
int entier (16 bits) 2 octets de 32 768 32 767
int entier (32 bits) 4 octets de 2 147 483 648 2 147 483 647
unsigned int entier non sign (16 bits) 2 octets de 0 65 535
unsigned int entier non sign (32 bits) 2 octets de 0 4 294 967 295
char texte 1 octet 256 caractres
float nombre dcimal 4 octets de 1,2e38 3,4e38
double nombre double 8 octets de 2,2e308 1,8e308
In
f
o
C++Livre Page 44 J eudi, 7. mai 2009 9:45 09
Chapitre 3 Variables et constantes 45
Pour crer ou dnir une variable, vous devez indiquer son type suivi de un ou plusieurs
espaces, puis le nom de la variable suivi dun point-virgule. Un nom de variable peut tre
compos de nimporte quelle combinaison de lettres, de chiffres et de symboles, mais ne
peut pas contenir despace. Exemple : x, J23qrsnf, monAge. Le nom de la variable doit
reter son rle, ce qui permet de suivre plus aisment le droulement du programme.
Linstruction suivante dnit la variable entire monAge :
int monAge;
Lorsque vous dclarez une variable, la mmoire correspondante est alloue
(rserve) cette variable. La valeur de cette variable sera alors toute valeur
se trouvant cet emplacement mmoire ce moment. Vous verrez bientt
comment affecter une nouvelle valeur cet emplacement mmoire.
vitez dutiliser des noms barbares qui nvoquent rien comme J23qrsnf et rservez les
noms dune seule lettre comme x ou i, des variables qui ne seront utilises quun bref
instant. Les noms expressifs comme Total ou monAge sont bien plus faciles comprendre
et mettre jour.
Nous allons illustrer ces propos par deux exemples. Pour cela, tapez les deux blocs
dinstructions suivants :
Exemple 1
int main()
{
unsigned short x;
unsigned short y;
unsigned short z;
z = x * y;
return 0;
}
Exemple 2
int main()
{
unsigned short Largeur;
unsigned short Longueur;
unsigned short Surface;
Surface = Largeur * Longueur;
return 0;
}
Si vous compilez ces programmes, votre compilateur vous signalera que ces
valeurs ne sont pas initialises. Nous allons bientt voir comment rsoudre ce
problme.
In
f
o
In
f
o
C++Livre Page 45 J eudi, 7. mai 2009 9:45 09
46 Le langage C++
Il est vident que le second programme est plus facile comprendre. En outre, les noms
longs donnent un sens au code et facilitent dventuelles modications.
Respect de la casse des caractres
Le langage C++ fait la diffrence entre les majuscules et les minuscules, ce qui signie
quune variable appele age est diffrente dune variable appele Age, qui est elle-mme
diffrente de la variable AGE.
Certains compilateurs permettent de dsactiver cette fonctionnalit. Cette
pratique est dconseille car vous risquez de ne pas pouvoir compiler vos
programmes avec dautres compilateurs et den rendre la lecture plus difcile
pour les autres programmeurs.
Conventions de nommage
Il existe plusieurs conventions pour les noms de variables. Peu importe celle que vous
choisirez, mais il convient de rester homogne dans lattribution des noms tout au long du
programme de manire faciliter la lecture du code par dautres programmeurs.
Certains programmeurs dtestent les majuscules et tapent tout le code en minuscules. Si le
nom de la variable requiert deux lments (par exemple, sous total), il est possible de
les sparer laide dun caractre de soulignement, ou de les accoler. Exemple :
sous_total ou sousTotal. La prsence dune lettre majuscule au milieu du nom de la
variable facilite la lecture ; elle fait penser une bosse et cest la raison pour laquelle on
lappelle camel case.
Lutilisation des caractres de soulignement ne fait pas lunanimit. Certains estiment
que le code source est plus facile lire, alors que dautres pensent que la touche nest
pas bien situe sur le clavier. Dans cet ouvrage, nous avons adopt la notation "droma-
daire". Pour une meilleure lisibilit du texte, tous les lments du nom (sauf le premier)
commencent par une majuscule. Exemple : monAge, totGeneral, etc. Notez que nous
nutilisons pas les caractres accentus, la plupart des versions ne les prenant pas en
charge.
En programmation avance, on rencontre souvent la notation dite hongroise. Cette prati-
que consiste utiliser un prxe correspondant au type de la variable. Par exemple, les
variables entires peuvent commencer par un i minuscule. Les variables de type long
peuvent commencer par un l. Bien entendu, il existe dautres prxes signalant diffren-
tes constructions en C++ pour les pointeurs, les constantes, etc., que nous verrons par la
suite.
A
tte
n
tio
n
C++Livre Page 46 J eudi, 7. mai 2009 9:45 09
Chapitre 3 Variables et constantes 47
Lorigine du nom de la notation hongroise vient de son inventeur, un hongrois
du nom de Charles Simonyi, de Microsoft. Vous pouvez trouver sa monographie
originale ladresse : http://www.strangecreations.com//library/c/naming.txt.
Microsoft a abandonn rcemment cette notation, et recommande de ne pas lutiliser en
C#. Cette recommandation est galement valable pour C++.
Mots-cls
Certains mots sont rservs par C++ et ne peuvent donc pas tre utiliss comme noms de
variables. Il sagit de mots-cls ayant une signication particulire pour le compilateur
C++, par exemple if, while, for et main. Vous en trouverez une liste complte au
Tableau 3.2 ainsi qu lAnnexe B. Il est possible que votre compilateur dispose dautres
mots rservs : vous devrez consulter son manuel pour en avoir la liste complte.
Tableau 3.2 : Les mots-cls de C++
asm else new this
auto enum operator throw
bool explicit private true
break export protected try
case extern public typedef
catch false register typeid
char float reinterpret_cast typename
class for return union
const friend short unsigned
const_cast goto signed using
continue if sizeof virtual
default inline static void
delete int static_cast volatile
do long struct wchar_t
double mutable switch while
dynamic_cast namespace template
In
f
o
C++Livre Page 47 J eudi, 7. mai 2009 9:45 09
48 Le langage C++
Les mots suivants sont rservs :
Cration de plusieurs variables la fois
Vous pouvez crer plusieurs variables de type identique dans la mme instruction en sparant
les noms par des virgules :
unsigned int monAge, monPoids; // 2 vars entires non signes
long int surface, largeur, longueur; // 3 variables entires longues
Comme vous pouvez le constater, monAge et monPoids sont des variables entires non
signes car il est inconcevable quun ge ou un poids soient ngatifs. Sur la deuxime
ligne, les variables surface, largeur et longueur sont de type long. Une mme instruction
de dnition ne peut comprendre des types de variables diffrents.
Affectation de valeurs aux variables
Pour affecter une valeur une variable, on utilise loprateur daffectation (=). Dans
lexpression suivante, on affecte la valeur 5 la variable largeur :
unsigned short largeur;
largeur = 5;
And bitor not_eq xor
and_eq compl or xor_eq
bitand not or_eq
Faire Ne pas faire
Dnir une variable en indiquant son type et
son nom.
Utiliser des noms de variables signicatifs.
Ne pas oublier que C++ respecte la casse des
caractres.
Tenir compte de lespace occup par la varia-
ble en mmoire en fonction de son type, pour
affecter des valeurs adquates.
Utiliser des mots-cls de C++ comme noms
de variables.
Prsumer du nombre doctets utiliss pour le
stockage dune variable.
Utiliser des variables non signes pour
stocker des nombres ngatifs.
C++Livre Page 48 J eudi, 7. mai 2009 9:45 09
Chapitre 3 Variables et constantes 49
long est un raccourci pour long int, comme short pour short int.
Il est possible de combiner les instructions de cration dune variable celles daffectation
dune valeur. Vous pouvez par exemple combiner ces deux tapes pour la variable largeur
en crivant :
unsigned short largeur = 5;
Linitialisation ressemble beaucoup laffectation prcdente. Les diffrences entre les
deux oprations sont mineures en ce qui concerne les entiers. Dans ce chapitre, vous allez
apprendre que certaines variables doivent tre initialises, car il nest plus possible de leur
attribuer de valeur par la suite.
On peut galement initialiser plusieurs variables simultanment. Linstruction suivante,
par exemple, cre deux variables du type long et les initialise :
long largeur = 5, hauteur = 7;
Vous pouvez mlanger des dnitions et des initialisations :
int monAge = 39, tonAge, sonAge = 40;
Dans cet exemple, trois variables entires sont cres, mais seules la premire et la
dernire sont initialises.
Vous pouvez compiler le chier source du Listing 3.2. Ce programme calcule la surface
dun rectangle, puis afche le rsultat lcran.
Listing 3.2 : Utilisation de variables
1: // Dmonstration de variables
2: #include <iostream>
3:
4: int main()
5: {
6: using std::cout;
7: using std::endl;
8:
9: unsigned short int Largeur = 5, Longueur;
10: Longueur = 10;
11:
12: // Cre un entier court non sign
13: // et linitialise avec le rsultat de la
In
f
o
C++Livre Page 49 J eudi, 7. mai 2009 9:45 09
50 Le langage C++
13a: // multiplication de Largeur par Longueur
14: unsigned short int Surface = (Largeur * Longueur);
15:
16: cout << "Largeur:" << Largeur << endl;
17: cout << "Longueur: " << Longueur << endl;
18: cout << "Surface: " << Surface << endl;
19: return 0;
20: }
Ce programme produit le rsultat suivant :
Largeur: 5
Longueur: 10
Surface: 50
Comme vous laurez constat dans le prcdent listing, la ligne 2, linstruction include
insre la bibliothque iostream, ce qui permet cout de fonctionner. Le programme
commence la ligne 4 par la fonction main(). Les lignes 6 et 7 dnissent cout et endl
comme faisant partie de lespace de nom standard (std).
La ligne 9 dnit les premires variables. Largeur est un entier court non sign, initialis
5. En revanche, la variable Longueur est dnie et non initialise, elle reoit la valeur 10
la ligne 10.
La ligne 14 dnit lentier court non sign Surface, qui reoit le produit de Largeur par
Longueur. Les diffrentes valeurs sont ensuite afches lcran (lignes 16 18). Le mot-
cl endl permet de passer la ligne.
Cration dalias avec typedef
la longue, linstruction unsigned short int peut devenir contraignante. Pourtant, elle
prsente lavantage dviter les erreurs de type lors de la compilation. Le C++ permet de
crer un alias en utilisant le mot-cl typedef (qui signie dnition de type).
En ralit, ce mot-cl permet de crer un synonyme, ce qui est diffrent de la cration dun
nouveau type (voir Chapitre 6). typedef est suivi du nom du type existant puis de lalias
et dun point-virgule. Exemple :
typedef unsigned short int USHORT
Le nouveau nom USHORT fait rfrence au type entier court non sign et peut tre utilis
chaque fois que vous auriez crit unsigned short int. Le Listing 3.3 ressemble au
Listing 3.2, mais nous avons utilis le nouveau type USHORT.
C++Livre Page 50 J eudi, 7. mai 2009 9:45 09
Chapitre 3 Variables et constantes 51
Listing 3.3 : Utilisation dun alias de type
1: // Dmonstration de lutilisation du mot cl typedef
2: #include <iostream>
3:
4: typedef unsigned short int USHORT; //dfinition dalias
5:
6: int main()
7: {
8:
9: using std::cout;
10: using std::endl;
11:
12: USHORT Largeur = 5;
13: USHORT Longueur;
14: Longueur = 10;
15: USHORT Surface = Largeur * Longueur;
16: cout << "Largeur:" << Largeur << endl;
17: cout << "Longueur: " << Longueur << endl;
18: cout << "Surface: " << Surface <<endl;
19: return 0;
20: }
Ce programme produit le rsultat suivant :
Largeur: 5
Longueur: 10
Surface: 50
Lastrisque (*) reprsente une multiplication.
la ligne 4, USHORT est dni comme synonyme de unsigned short int. Par ailleurs, le
programme est pratiquement identique au Listing 3.2 et la sortie est la mme.
short ou long : que choisir ?
Les programmeurs qui dbutent en C++ hsitent souvent entre ces deux types. La solution
est simple : il est prfrable de choisir le type long lorsque le contenu risque dexcder la
taille maximale autorise pour une variable de type short.
Comme le montre le Tableau 3.1, les entiers courts non signs, supposs tre dune taille
de deux octets, acceptent une valeur maximale de 65 535. Pour les entiers signs courts,
In
f
o
C++Livre Page 51 J eudi, 7. mai 2009 9:45 09
52 Le langage C++
cette valeur est rpartie entre les valeurs positives et ngatives, et leur valeur maximale est
donc la moiti de celle dun entier court non sign.
En revanche, les entiers longs non signs prennent en charge une valeur comprise entre 0
et 4 294 967 295. Si vous envisagez de grer une valeur suprieure, vous avez le choix
entre le type float et le type double, au dtriment de la prcision numrique. Sur la
plupart des ordinateurs, seules les 7 ou 9 premires positions sont prises en compte, ce qui
signie que le rsultat sera arrondi aprs ces chiffres.
Les variables courtes occupent moins de mmoire mais, de nos jours, la mmoire nest
pas chre et la vie est courte, nhsitez donc pas utiliser des variables de type int qui
occuperont probablement quatre octets sur votre machine.
Rebouclage des entiers non signs
Les entiers non signs acceptent une valeur limite. Quen est-il si vous la dpassez ?
Quand la valeur maximale dune variable entire non signe est atteinte, le contenu de
cette dernire est remis zro, un peu comme le compteur dune voiture. Examinez
le Listing 3.4.
Listing 3.4 : Incrmentation de un de la valeur maximale autorise pour un entier short
non sign
1: #include <iostream>
2: int main()
3: {
4: using std::cout;
5: using std::endl;
6:
7: unsigned short int Nombre;
8: Nombre = 65535;
9: cout << "Nombre:" << Nombre << endl;
10: Nombre++;
11: cout << "Nombre:" << Nombre << endl;
12: Nombre++;
13: cout << "Nombre:" << Nombre << endl;
14: return 0;
15: }
Ce programme produit le rsultat suivant :
Nombre:65535
Nombre:0
Nombre:1
C++Livre Page 52 J eudi, 7. mai 2009 9:45 09
Chapitre 3 Variables et constantes 53
la ligne 7, Nombre est une variable de type entier court non sign qui, sur un Pentium 4
sous Windows XP, est dune longueur de deux octets et peut donc contenir une valeur
comprise entre 0 et 65 535. la ligne suivante, cette variable reoit la valeur maximale et
le rsultat safche correctement (ligne 9).
la ligne 10, la variable est incrmente de un, comme le montre le symbole ++. Au
passage, vous pouvez constater que le nom de C++ semble indiquer quil sagit dun
incrment du langage C. La valeur de Nombre est donc gale 65 536, valeur refuse
pour un entier court non sign. La variable est remise zro et son contenu apparat
lcran.
la ligne 12, Nombre est incrment de nouveau. Sa nouvelle valeur (1) safche.
Rebouclage des entiers signs
Un entier sign se distingue dun entier non sign puisque la moiti des valeurs admises
sont ngatives. Au lieu de vous reprsenter un compteur de voiture, vous pourriez imagi-
ner une horloge comme celle de la Figure 3.2, o les chiffres augmentent dans les sens
des aiguilles, et diminuent dans le sens inverse. Ils se croisent en bas du cadran (
6 heures).
Lorsque vous ne disposez plus de nombres positifs, vous atteignez le plus grand nombre
ngatif puis dcomptez jusqu la valeur 0.
Dans le programme du Listing 3.5, le nombre positif maximal est incrment de un.
Figure 3.2
Si les pendules utilisaient
des nombres signs.
C++Livre Page 53 J eudi, 7. mai 2009 9:45 09
54 Le langage C++
Listing 3.5 : Incrmentation de la valeur maximale admise pour un entier sign
1: #include <iostream>
2: int main()
3: {
4: short int Nombre;
5: Nombre = 32767;
6: std::cout << "Nombre:" << Nombre << std::endl;
7: Nombre++;
8: std::cout << "Nombre:" << Nombre << std::endl;
9: Nombre++;
10: std::cout << "Nombre:" << Nombre << std::endl;
11: return 0;
12: }
Ce programme produit le rsultat suivant :
Nombre: 32767
Nombre: -32768
Nombre: -32767
la ligne 3, Nombre est dclar comme entier court sign (par dfaut, tout entier court est
sign, pour dnir un entier non sign, vous devez crire explicitement le mot-cl unsi-
gned). Le programme fonctionne sensiblement comme le prcdent. Bien entendu, les
rsultats obtenus sont diffrents. Pour bien assimiler cet exemple, vous devez comprendre
comment les nombres signs sont reprsents sous forme binaire dans un entier dune
longueur de deux octets.
Comme pour un entier non sign, lentier sign reboucle de la plus haute valeur positive
la plus haute valeur ngative.
Variables caractre
Les variables caractre (type char) occupent gnralement 1 octet et acceptent jusqu
256 valeurs (voir Annexe C). Ce type char peut tre interprt comme un nombre court
(0 255) ou comme un membre du jeu de caractres ASCII. Le jeu de caractres ASCII et
son quivalent ISO permettent de coder toutes les lettres, les chiffres et les signes de ponc-
tuation.
Les ordinateurs sont incapables de traiter des lettres, des signes de ponctuation
ou des phrases. Ils ne comprennent que les nombres. En fait, leur rle se limite
examiner une jonction de cbles particulire an de dterminer si une charge
In
f
o
C++Livre Page 54 J eudi, 7. mai 2009 9:45 09
Chapitre 3 Variables et constantes 55
lectrique sufsante peut y tre dtecte. Cette charge est alors reprsente en
interne comme un 1, ou comme un 0 si elle est absente. En regroupant lensem-
ble de ces 0 et 1, lordinateur est capable de gnrer des structures interprtes
comme des nombres, qui leur tour correspondent des lettres ou des signes
de ponctuation.
En code ASCII, la valeur 97 correspond la lettre "a" minuscule. Les majuscules, les
minuscules, les chiffres et les signes de ponctuation sont associs aux valeurs ASCII
comprises entre 1 et 128. Les 128 valeurs restantes sont rserves au constructeur infor-
matique, bien que le jeu IBM tendu soit devenu une norme utilise par de nombreux
programmeurs.
ASCII se prononce "A-ski".
Caractres et nombres
Tout caractre affect une variable char (comme a) correspond en fait un nombre
entre 0 et 255. Le compilateur est capable didentier, de coder et de dcoder tout carac-
tre, nombre ou signe de ponctuation gurant entre des guillemets simples.
La relation valeur/lettre est arbitraire. Il sagit dune convention prise en charge par le
clavier, le compilateur et lcran. Attention : la valeur 5 et le caractre 5 ne sont pas
gaux, ce dernier ayant une valeur ASCII de 53. Cette notion est illustre dans le
Listing 3.6.
Listing 3.6 : Impression de caractres partir de leurs cSCII
1: #include <iostream>
2: int main()
3: {
4: for (int i = 32; i<128; i++)
5: std::cout << (char) i;
6: return 0;
7: }
Ce programme produit le rsultat suivant :
!"#$%&()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMN
OPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
In
f
o
C++Livre Page 55 J eudi, 7. mai 2009 9:45 09
56 Le langage C++
Ce programme permet dafcher les valeurs caractres des codes ASCII compris entre 32
et 127. Pour accomplir cette tche, ce listing utilise une variable entire, i, la ligne 4.
En ligne 5, le nombre de la variable i est oblig de safcher sous forme de caractre.
On aurait aussi pu utiliser une variable caractre, comme dans le Listing 3.7, pour arriver
au mme rsultat.
Listing 3.7 : Impression de caractres partir de leurs codes ASCII version 2
1: #include <iostream>
2: int main()
3: {
4: for (unsigned char i = 32; i<128; i++)
5: std::cout << i;
6: return 0;
7: }
Comme vous le pouvez le constater la ligne 4, on utilise ici un caractre non sign. Une
variable de caractre tant utilise la place dune variable numrique, le cout de la
ligne 5 sait afcher la valeur du caractre.
Caractres spciaux pour limpression
Le compilateur C++ reconnat certains caractres spciaux de formatage. Le Tableau 3.3
regroupe les codes de mise en forme les plus courants. Pour les insrer dans un code
source, il suft de les faire prcder dune barre oblique inverse (appele caractre
dchappement). Pour insrer une tabulation, tapez la ligne suivante :
char carTab = \t;
Cet exemple dclare une variable de type char (carTab) qui est initialise avec la valeur
\t reconnue comme une tabulation. Les caractres de formatage sont utiliss sur
nimporte quel priphrique de sortie : cran, imprimante et chier disque.
Le caractre dchappement (\) modie la signication du caractre quil prcde. Par
exemple, si vous tapez n, vous afchez la 14
e
lettre de lalphabet, alors que prcd du
caractre dchappement, il correspond une nouvelle ligne.
Tableau 3.3 : Caractres dchappement
Caractre Signication
\a alerte sonore
\b retour arrire
C++Livre Page 56 J eudi, 7. mai 2009 9:45 09
Chapitre 3 Variables et constantes 57
Constantes
Comme les variables, les constantes sont des emplacements de stockage dinformation.
Mais, la diffrence des variables, elles ne sont pas modiables : vous devez donc initiali-
ser une constante lorsque vous la dclarez et vous ne pourrez plus ensuite lui affecter de
nouvelle valeur.
C++ distingue deux types de constantes : les constantes littrales et les constantes symbo-
liques.
Constantes littrales
Une constante littrale est une valeur apparaissant directement dans le code du programme
au moment o lon en a besoin. Exemple :
int monAge = 32;
monAge est une variable de type int, alors que 32 est une constante littrale. Vous ne
pouvez pas affecter de valeur 32 et sa valeur ne peut pas tre modie.
\f saut de page
\n nouvelle ligne
\r retour chariot
\t tabulation horizontale
\v tabulation verticale
\ guillemet simple
\" guillemet double
\? point dinterrogation
\\ barre oblique inverse
\000 notation octale
\xhhh notation hexadcimale
Tableau 3.3 : Caractres dchappement (suite)
Caractre Signication
C++Livre Page 57 J eudi, 7. mai 2009 9:45 09
58 Le langage C++
Constantes symboliques
Comme une variable, une constante symbolique est reprsente par un nom. Toutefois, son
contenu ne peut tre modi aprs son initialisation.
Supposons que vous ayez dclar deux variables de type entier Eleves et Classes et que
souhaitiez connatre le nombre dlves dune cole. Si vous savez que chaque classe est
forme de 15 lves, le calcul se prsentera ainsi :
Eleves = Classes * 15;
Dans cet exemple, 15 est une constante littrale. Votre programme serait plus facile lire
et mettre jour si cette valeur tait reprsente par une constante symbolique :
Eleves = Classes * elevesParClasse
Si vous dcidez plus tard de modier le nombre dlves par classe, il vous sufra de
changer la valeur dinitialisation de la constante elevesParClasse : vous naurez pas
besoin de modier toutes les instructions qui utilisent cette valeur.
Il existe deux faons de dclarer une constante symbolique en C++. La plus ancienne,
dsormais obsolte, consiste utiliser la directive #define du prprocesseur. La seconde,
plus approprie, consiste utiliser le mot-cl const.
Dnition de constantes avec #define
De nombreux programmes faisant appel la directive #define, vous devez bien en
comprendre le fonctionnement. Pour dnir une constante de cette manire (obsolte),
vous pouvez taper :
#define elevesParClasse 15
La constante elevesParClasse na pas de type particulier (int, char, etc.). En fait, le
prprocesseur effectuera une simple substitution de texte : chaque fois quil rencontrera
le mot elevesParClasse dans le texte du programme, il le remplacera 15.
Le prprocesseur sexcutant avant le compilateur, ce dernier ne verra donc jamais votre
constante, mais uniquement le nombre 15.
Mme si #define semble trs simple utiliser, il est prfrable de lviter car
la norme C++ la dclar obsolte. A
tte
n
tio
n
C++Livre Page 58 J eudi, 7. mai 2009 9:45 09
Chapitre 3 Variables et constantes 59
Dnition de constantes laide de const
Bien que la directive #define fonctionne, le mot-cl const permet de crer des constan-
tes C++ plus proprement :
const unsigned short int elevesParClasse = 15;
Cet exemple cre galement la constante symbolique elevesParClasse mais, cette fois-
ci, elle est de type entier court non sign. Grce cette mthode, les erreurs sont moins
frquentes et le code peut tre mis jour facilement car le mot-cl const permet au
compilateur de connatre le type de la constante et de vrier quelle est correctement
utilise.
Il est impossible de modier le contenu dune constante pendant lexcution
dun programme. Pour affecter une autre valeur une constante, vous devez
intervenir au niveau du chier source, puis le recompiler.
Constantes numres
Ces constantes permettent de crer de nouveaux types, puis de dnir des variables dont
les valeurs seront limites un ensemble prcis. Vous pourriez par exemple crer une
numration pour y stocker des couleurs. En ce cas, il faudrait dclarer lnumration
COULEURS, qui serait associe aux cinq valeurs ROUGE, BLEU, VERT, BLANC et NOIR.
La syntaxe consiste crire le mot-cl enum, suivi du nom du type, dune accolade
ouvrante, des valeurs admises spares par des virgules, dune accolade fermante et dun
point-virgule. Exemple :
enum COULEURS { ROUGE, BLEU, VERT, BLANC, NOIR };
Cette instruction effectue deux actions :
1. Elle cre le nom dnumration COULEURS, cest--dire un nouveau type.
Faire Ne pas faire
Vrier que la valeur affecte dun entier de
dpasse pas ses limites an quil ny ait pas
de rebouclage.
Attribuer des noms signicatifs aux
constantes.
Utiliser des mots-cls comme noms de
constantes.
Utiliser la directive #define du prproces-
seur pour dclarer des constantes. Prfrer
const.
In
f
o
C++Livre Page 59 J eudi, 7. mai 2009 9:45 09
60 Le langage C++
2. Elle cre des constantes symbolique comme ROUGE, BLEU, VERT, etc. en les initialisant
respectivement avec les valeurs 0, 1, 2, etc.
chaque constante correspond une valeur entire. En labsence dindication contraire, la
premire constante sera initialise 0, la seconde 1, etc. Vous pouvez toutefois affecter
des valeurs explicites ; celles qui nen nont pas recevront la valeur de la constante prc-
dente, plus un. Ici, par exemple :
enum Couleurs { ROUGE=100, BLEU, VERT=500, BLANC, NOIR=700 };
ROUGE ayant la valeur 100, BLEU sera associe la valeur 101 ; selon le mme principe,
BLANC vaudra 501.
Vous pouvez dnir des variables du type COULEURS, mais elles ne pourront recevoir que
des valeurs de lnumration (ici, ROUGE, BLEU, VERT, BLANC ou NOIR).
Il faut bien raliser que les variables numres sont gnralement de type unsigned int
et que les constantes de lnumration sont, en fait, des valeurs entires. Il est toutefois trs
pratique de pouvoir utiliser des noms vocateurs pour ces valeurs lorsque lon travaille sur
des couleurs, des jours de la semaine, ou tout autre ensemble ni de valeurs. Le Listing 3.8
utilise une numration pour reprsenter des jours :
Listing 3.8 : Exemple dnumration
1: #include <iostream>
2: int main()
3: {
4: enum Jours { Dimanche, Lundi, Mardi,
5: Mercredi, Jeudi, Vendredi, Samedi };
6:
7: Jours aujourdhui;
8: aujourdhui = Lundi;
9:
10: if (aujourdhui == Dimanche || aujourdhui == Samedi)
11: std::cout << "\nJadore les week-ends!\n";
12: else
13: std::cout << "\nAu boulot!\n";
14:
15: return 0;
16: }
Ce programme produit le rsultat suivant :
Au boulot!
C++Livre Page 60 J eudi, 7. mai 2009 9:45 09
Chapitre 3 Variables et constantes 61
Aux lignes 4 et 5, on dnit la constante numre Jours avec sept valeurs quivalant
chacune un entier en partant de 0 ; la valeur de Lundi est donc 1 (Dimanche valait 0).
La ligne 7 cre une variable de type Jours qui contiendra une valeur valide partir de la
liste des constantes numres dnies aux lignes 4 et 5. La valeur Lundi est affecte
cette variable en ligne 8 et le contenu de la variable est teste en ligne 10.
La constante numre de la ligne 8 pourrait tre remplace par une srie de constantes
entires, comme dans le Listing 3.9.
Listing 3.9 : Le mme programme utilisant des constantes entires
1: #include <iostream>
2: int main()
3: {
4: const int Dimanche = 0;
5: const int Lundi = 1;
6: const int Mardi = 2;
7: const int Mercredi = 3;
8: const int Jeudi = 4;
9: const int Vendredi = 5;
10: const int Samedi = 6;
11:
12: int aujourdhui;
13: aujourdhui = Lundi;
14:
15: if (aujourdhui == Dimanche || aujourdhui == Samedi)
16: std::cout << "\nJadore les week-ends!\n";
17: else
18: std::cout << "\nAu boulot!\n";
19:
20: return 0;
21: }
Ce programme produit le rsultat suivant :
Au boulot!
Plusieurs variables dclares dans ce programme ntant pas utilises, votre
compilateur risque de vous en avertir au moment de la compilation.
La sortie produite est identique celle du Listing 3.8. Chacune des constantes (Dimanche,
Lundi, etc.) a t ici dnie explicitement et il nexiste pas de type numr Jours.
A
tte
n
tio
n
C++Livre Page 61 J eudi, 7. mai 2009 9:45 09
62 Le langage C++
Les constantes numres prsentent lavantage dtre parfaitement claires, lutilit du
type numr Jours est vidente.
Questions-rponses
Q Si un entier court peut tre remis zro en cas de dpassement de capacit, pourquoi
ne pas toujours utiliser des entiers longs ?
R Tous les types dentiers pourront renvoyer des valeurs errones en cas de dpassement
de capacit. Toutefois, les valeurs maximales acceptes seront de 65 535 pour un
unsigned short int de deux octets, et de 4 294 967 295 pour un unsigned long
int de quatre octets. Cependant, sur la plupart des machines, un entier long occupera
davantage de mmoire (4 octets contre 2) et, dans ce cas, un programme contenant
100 variables de ce type occupera donc 200 octets de mmoire vive supplmentaires.
Dsormais ce problme nest plus crucial, car les PC disposent maintenant de quantits de
mmoire considrables.
Utiliser des types suprieurs vos besoins peut galement demander plus de traitement
de la part du processeur.
Q Que se passe-t-il si jaffecte un nombre virgule un entier alors que jaurai d
utiliser un float ?
Par exemple :
int unNombre = 5.4;
R Dans ce cas, un compilateur efcace enverra un avertissement, mais cette affectation
est autorise. Le nombre affect sera tronqu : la valeur 5,4 deviendra la valeur entire
5, ce qui entranera une perte dinformation.
Q Pourquoi doit-on utiliser des constantes symboliques et ne pas se contenter des
constantes littrale ?
R Si vous utilisez la mme valeur en de nombreux endroits de votre programme, vous
pourrez la modier en une seule fois si elle est dsigne par une constante symbolique.
Par ailleurs une constante symbolique, de par son nom, est plus explicite. Il peut tre
difcile de comprendre pourquoi un nombre est multipli par la constante 60, alors que
cela deviendra vident sil sagit de la constante secParMinute.
Q Que se passe-t-il si jaffecte un nombre ngatif une variable non signe ?
Par exemple :
unsigned int unNombrePositif = -1;
C++Livre Page 62 J eudi, 7. mai 2009 9:45 09
Chapitre 3 Variables et constantes 63
R Un bon compilateur renverra un avertissement, mais cette affectation est autorise. La
valeur sera alors considre comme une suite de bit reprsentant un nombre non sign
et sera affecte la variable. Par exemple -1 dont la reprsentation binaire est
1111111111111111 (0xFF en code hexadcimal) sera interprt comme la valeur
65 535 non signe.
Q Est-il possible de travailler en C++ sans comprendre les structures binaires,
larithmtique binaire et le systme hexadcimal ?
R Oui, mais moins efcacement que si vous matrisez ces notions. Le langage C++ ne
masque pas autant que dautres langages les dtails internes de lordinateur. Ceci peut
tre un avantage puisque vous disposez ainsi dune bien plus grande marge de manu-
vre, mais cela peut aussi entraner des rsultats non souhaits en cas derreur de pro-
grammation. Bien comprendre ces concepts permet den exploiter pleinement les
possibilits. Les programmeurs C++ qui ne matrisent pas les bases du systme binaire
sont souvent dconcerts par certains rsultats.
Testez vos connaissances
1. Quelle est la diffrence entre une variable entire et une variable virgule ottante ?
2. Quelles sont les diffrences entre un unsigned short int et un longint ?
3. Pourquoi prfrer les constantes symboliques aux constantes littrales ?
4. Pourquoi prfrer le mot-cl const la directive #define ?
5. Quest-ce qui caractrise un "bon" nom de variable ou un "mauvais" nom ?
6. Dans cette numration, quelle est la valeur de BLEU ?
enum COLOR { BLANC, NOIR = 100, ROUGE, BLEU, VERT = 300 };
7. Parmi ces noms de variables, quels sont ceux qui sont bons et mauvais, quels sont
ceux qui ne sont pas corrects ?
a. Age
b. !ex
c. R79J
d. revenuTotal
e. __Invalide
C++Livre Page 63 J eudi, 7. mai 2009 9:45 09
64 Le langage C++
Exercices
1. Quel doivent tre les types des variables destines recevoir les informations suivantes ?
a. Votre ge
b. La surface de votre jardin
c. Le nombre dtoiles de la galaxie
d. La pluviomtrie moyenne de janvier
2. Proposez des noms de variables vocateurs pour ces informations.
3. Dclarez une constante pour pi = 3.14159.
4. Dclarez une variable de type float, puis initialisez-la avec la constante pi.
C++Livre Page 64 J eudi, 7. mai 2009 9:45 09
4
Expressions
et instructions
Au sommaire de ce chapitre
Les instructions
Les blocs dinstructions
Les expressions
Les branchements conditionnels
Les conditions logiques
Un programme est, la base, un ensemble de commandes qui sexcutent squentiel-
lement. Il est possible de modier cette squence et dexcuter un jeu de commandes ou
un autre selon quune condition particulire est ou nest pas satisfaite.
C++Livre Page 65 J eudi, 7. mai 2009 9:45 09
66 Le langage C++
Instructions
En C++, une instruction a pour rle de contrler la squence dexcution, dvaluer une
expression ou de ne rien faire (instruction nulle). Toutes les instructions C++, y compris
linstruction nulle, se terminent par un point-virgule.
Lune des instructions les plus courantes, laffectation, ressemble ceci :
x = a+b;
la diffrence de lalgbre, cette instruction ne signie pas que x est gal a+b, mais
quil faut "affecter la valeur de la somme a plus b x".
Cette instruction effectue deux oprations : elle ajoute a et b, puis affecte le rsultat x
laide de loprateur daffectation (=). Bien quelle ralise deux oprations, il ne sagit que
dune seule instruction, qui se termine donc par un point-virgule.
Loprateur daffectation attribue la valeur droite du signe gal la variable
situe sa gauche.
Espace ou blanc
Un espace correspond un caractre invisible (tabulation, espace, saut de ligne). On les
appelle "caractres blancs", car ils sont invisibles limpression.
Gnralement, les instructions ignorent les espaces. Linstruction daffectation ci-dessus,
par exemple, pourrait scrire ainsi :
x=a+b;
ou ainsi :
x =a
+ b ;
Bien que correcte, cette dernire prsentation nest pas trs convaincante. Les espaces sont
destins rendre le programme plus lisible, non lparpiller dans le listing. Dans tous les
cas, le C++ propose, le programmeur dispose !
Blocs et instructions composes
Partout o vous pouvez placer une instruction simple, vous pouvez placer une instruction
compose, appele bloc. Un bloc commence par une accolade ouvrante et se termine par
In
f
o
C++Livre Page 66 J eudi, 7. mai 2009 9:45 09
Chapitre 4 Expressions et instructions 67
une accolade fermante. Bien que chaque instruction du bloc doive se terminer par un
point-virgule, le bloc lui-mme se termine simplement par laccolade fermante. Exemple :
{
temp = a;
a = b;
b = temp;
}
Ce bloc agit comme une seule instruction, en intervertissant les valeurs de a et de b.
Expressions
En C++, tout ce qui correspond une valeur est une expression. On dit quune expression
renvoie une valeur : 3+2, par exemple, est une expression qui renvoie la valeur 5. Toutes
les expressions sont des instructions.
Les expressions sont nombreuses et de formats diffrents. Voici quelques exemples :
3.2 // renvoie la valeur 3,2
PI // constante "float" renvoyant la valeur 3,14
SecondesParMinute // constante entire renvoyant 60
En admettant que PI et SecondesParMinute soient des constantes initialises respectivement
3,14 et 60, ces trois instructions sont des expressions.
Voici une expression un peu plus complexe :
x = a+b;
Elle additionne a et b, affecte le rsultat x, mais renvoie aussi la valeur qui vient dtre
affecte (celle de x) : cette instruction daffectation est donc une expression.
Nimporte quelle expression peut apparatre droite dun oprateur daffectation, ce qui
inclut donc linstruction daffectation que nous venons de voir. On peut donc galement
crire :
y = x = a+b;
Faire Ne pas faire
Terminer chaque instruction par un point-
virgule.
Utiliser judicieusement des espaces pour
rendre le code plus lisible.
Oublier dinsrer une accolade fermante
correspondant une accolade ouvrante.
C++Livre Page 67 J eudi, 7. mai 2009 9:45 09
68 Le langage C++
Cette ligne est value dans lordre suivant :
1. Ajouter a b.
2. Affecter le rsultat de lexpression a+b x.
3. Affecter le rsultat de lexpression x = a+b y.
Si a, b, x et y sont des entiers et que a et b valent respectivement 9 et 7, x et y recevront
toutes les deux la valeur 16, comme le montre le Listing 4.1.
Listing 4.1 : valuation dexpressions
1: #include <iostream>
2: int main()
3: {
4: using std::cout;
5: using std::endl;
6:
7: int a=0, b=0, x=0, y=35;
8: cout << "a: " << a << " b: " << b;
9: cout << " x: " << x << " y: " << y << endl;
10: a = 9;
11: b = 7;
12: y = x = a+b;
13: cout << "a: " << a << " b: " << b;
14: cout << " x: " << x << " y: " << y << endl;
15: return 0;
16: }
Ce programme produit le rsultat suivant : ,
a: 0 b: 0 x: 0 y: 35
a: 9 b: 7 x: 16 y: 16
La ligne 7 dclare et initialise quatre variables dont les valeurs sont afches aux
lignes 8 et 9. La ligne 10 affecte la valeur 9 a, la ligne 11, la valeur 7 b. La ligne 12
additionne a et b et affecte le rsultat x. En consquence, lexpression (x = a+b) est
value pour produire une valeur (la somme de a et b), qui est son tour affecte y.
Aux lignes 13 et 14, ces rsultats sont conrms par lafchage des valeurs des quatre
variables.
C++Livre Page 68 J eudi, 7. mai 2009 9:45 09
Chapitre 4 Expressions et instructions 69
Oprateurs
Un oprateur est un symbole qui demande au compilateur deffectuer une action. Les
oprateurs agissent sur les oprandes qui sont des expressions en langage C++. Il existe
plusieurs catgories doprateurs ; les deux premires que nous tudierons ici sont :
les oprateurs daffectation ;
les oprateurs mathmatiques.
Oprateurs daffectation
Vous avez dj vu loprateur daffectation (=). Il transfre la valeur situe droite de
loprateur dans loprande de gauche. Lexpression suivante :
x = a+b;
affecte le rsultat de laddition loprande x.
lvalues et rvalues
Tout oprande situ gauche de loprateur daffectation est appel lvalue (left value),
alors que loprande situ droite sappelle rvalue (right value).
Toutes les lvalues sont des rvalues, mais linverse nest pas toujours vrai. Une rvalue litt-
rale, par exemple, nest pas un lvalue. Vous pouvez donc crire :
x = 5;
mais pas :
5 = x;
x peut tre une lvalue ou une rvalue, alors que 5 ne peut tre quune rvalue.
Les constantes sont des rvalues : leurs valeurs ne pouvant tre modies, elles
ne peuvent pas se trouver du ct gauche de loprateur daffectation, ce qui
signie quelles ne peuvent pas tre des lvalues.
Oprateurs mathmatiques
Lautre catgorie doprateurs regroupe les oprateurs mathmatiques. Il existe cinq
oprateurs mathmatiques : addition (+), soustraction (-), multiplication (*), division (/)
et modulo (%).
In
f
o
C++Livre Page 69 J eudi, 7. mai 2009 9:45 09
70 Le langage C++
Les oprateurs + et fonctionnent comme lon sy attend. La multiplication utilise le
signe astrisque (*) et la division la barre oblique. Les exemples suivants illustrent
chacun de ces oprateurs. Dans chaque cas, le rsultat est affect la variable resultat.
Les commentaires droite montrent la valeur attendue.
resultat = 56 + 32 // resultat = 88
resultat = 12 10 // resultat = 2
resultat = 21 / 7 // resultat = 3
resultat = 12 * 4 // resultat = 48
Problmes de soustraction
La soustraction dentiers non signs (unsigned) peut conduire des rsultats tonnants
lorsque le rsultat est un nombre ngatif. Dans le chapitre prcdent, nous avons
abord le dpassement de capacit dune variable ngative. Dans le Listing 4.2, nous
allons soustraire dun nombre non sign un autre nombre non sign de valeur sup-
rieure.
Listing 4.2 : Soustraction dentiers et dpassement de capacit
1: // Listing4.2 - soustraction et
2: // dpassement de capacit
3: #include <iostream>
4:
5: int main()
6: {
7: using std::cout;
8: using std::endl;
9:
10: unsigned int difference;
11: unsigned int grandNombre = 100;
12: unsigned int petitNombre = 50;
13:
14: difference = grandNombre - petitNombre;
15: cout << "La difference est: " << difference;
16:
17: difference = petitNombre - grandNombre;
18: cout << "\nMaintenant la difference est: " << difference <<endl;
19: return 0;
20: }
C++Livre Page 70 J eudi, 7. mai 2009 9:45 09
Chapitre 4 Expressions et instructions 71
Ce programme produit le rsultat suivant :
La difference est: 50
Maintenant la difference est: 4294967246
Le rsultat de la soustraction effectue la ligne 14 safche pour la premire fois la
ligne 15. Loprateur est appel de nouveau la ligne 17 mais, comme on a retranch un
grand nombre dun nombre plus petit, le rsultat est ngatif. Comme ce rsultat est valu
(et afch) comme un nombre non sign, il y a dpassement de capacit, comme on la
expliqu au Chapitre 3. Pour plus de dtails, consultez lAnnexe C.
Division entire et modulo
La division entire est celle que vous avez tudie lcole primaire. Si vous divisez 21
par 4 (21 / 4), la rponse est 5 (avec un reste).
Le cinquime oprateur mathmatique vous est peut-tre inconnu. Il sagit de loprateur
modulo (%) qui donne la valeur du reste de la division entire. Pour obtenir le reste de la
division de 21 par 4, on calcule 21 modulo 4 (21%4), ce qui donne 1.
Extraire le reste peut tre trs utile pour, par exemple, afcher une instruction toutes les
dix actions. En effet, si le reste de la division dun nombre par 10 est gal 0, ce nombre
est un multiple de 10. Ainsi, 1%10 vaut 1, 2%10 vaut 2, etc., jusqu 10%10 qui vaut
0. 11%10 repasse 1 et cette srie se continue jusquau prochain multiple de 10, qui est
20 (20%10 vaut nouveau 0). Vous utiliserez cette technique avec les boucles au Chapi-
tre 7.
Si je divise 5 par 3 jobtiens 1. Quest-ce qui ne va pas ?
Si vous divisez un entier par un autre, vous obtenez un entier comme rsultat.
Par consquent 5/3 donne 1 (en ralit la rponse est 1 avec un reste de 2, pour obtenir
le reste, faites 5%3 et vous obtiendrez 2).
Pour obtenir une valeur fractionnaire, utilisez des chiffres virgule ottante (types float,
double ou long double).
5.0/3.0 vous donnera un rsultat fractionnaire : 1,66667
Si, soit le diviseur, soit le dividende est une valeur virgule ottante, le compilateur
produit un quotient virgule ottante. Toutefois, sil est affect une lvalue entire, la
valeur sera nouveau tronque.
C++Livre Page 71 J eudi, 7. mai 2009 9:45 09
72 Le langage C++
Combinaison doprateurs daffectation
et doprateurs mathmatiques
Il est courant dajouter une valeur une variable, puis de renvoyer le rsultat dans cette
dernire. Par exemple, prenons la variable age, laquelle nous allons ajouter 2 :
int age = 5;
int temp;
temp = age+2; // 5+2 dans temp
age = temp; // affectation de temp age
Les deux premires lignes crent la variable age ainsi quune variable temporaire. Comme
on le voit la troisime ligne, la valeur age se voit ajouter 2 et le rsultat est affect
temp. La ligne suivante, cette valeur est replace dans age, an de la mettre jour.
Cette mthode est terriblement complique et un peu difcile grer. En C++, vous
pouvez utiliser la mme variable des deux cts de loprateur daffectation, ce qui permet
dcrire :
age = age+2;
Cette expression est bien plus claire, mme si elle nest pas conforme la notation alg-
brique. Le C++ linterprte comme : "ajouter 2 la variable age, puis affecter le rsultat
cette mme variable".
Il est galement possible de faire plus court :
age += 2;
Le rsultat est identique, mais peut-tre un peu moins lisible.
Cette ligne utilise loprateur daddition combin laffectation (+=) qui ajoute la valeur
droite la valeur gauche puis affecte nouveau le rsultat la valeur gauche. Si age valait
24 au dpart, il vaudra donc 26 aprs cette instruction.
Il existe dautres oprateurs combins laffectation pour la soustraction (-=), la division
(/=), la multiplication (*=) et le modulo (%=).
Incrmentation et dcrmentation
La valeur que lon ajoute (ou soustrait) le plus souvent est 1. En C++, augmenter une
valeur de 1 est appel incrmentation, diminuer une valeur de 1 est appel dcrmentation.
Pour effectuer ces actions, le langage C++ propose deux oprateurs spciaux.
C++Livre Page 72 J eudi, 7. mai 2009 9:45 09
Chapitre 4 Expressions et instructions 73
Loprateur dincrmentation (++) augmente de 1 la valeur dune variable, tandis que
loprateur de dcrmentation (--) la diminue de 1. Dans lexemple ci-dessous, nous
allons incrmenter la variable compteur :
compteur++; // compteur est gal compteur plus 1.
Ce qui revient crire :
compteur = compteur+1;
ou encore :
compteur += 1;
Comme vous lavez peut-tre devin, le nom C++ provient de lapplication de
loprateur dincrmentation au nom de son prdcesseur, le langage C. Lide
est que le C++ est une version suprieure de C.
Prxe et sufxe
Les oprateurs dincrmentation et de dcrmentation existent en deux versions : prxe
et sufxe. Un prxe prcde le nom de la variable (++age), alors que le sufxe le suit
(age++).
Dans une instruction simple, vous pouvez utiliser lun ou lautre, mais dans une instruc-
tion complexe o vous incrmentez (ou dcrmentez) une variable puis affectez le rsultat
une autre variable, la nuance est importante.
Le principe du prxe est que lon effectue lincrmentation avant de renvoyer la valeur
alors que, dans le cas du sufxe, on incrmente aprs avoir renvoy la valeur.
Un exemple permettra dclaircir ce concept. Supposons que x soit un entier dont la valeur
est 5 et que vous utilisiez un oprateur dincrmentation prxe. Si vous crivez :
int a = ++x;
le compilateur va incrmenter x (qui prendra la valeur 6) avant den affecter la valeur a.
Ces deux variables auront donc la mme valeur 6.
Si vous utilisez ensuite loprateur sufxe pour crire :
int b = x++;
le compilateur va attribuer la valeur de x (6) b, puis incrmenter x qui prendra la valeur
7. b vaut donc 6 et x vaut 7. Le Listing 4.3 est un exemple dutilisation des prxes et des
sufxes.
In
f
o
C++Livre Page 73 J eudi, 7. mai 2009 9:45 09
74 Le langage C++
Listing 4.3 : Oprateurs prxe et sufxe
1: // Listing4.3 - Utilisation des oprateurs
2: // dincrmentation et de dcrmentation
3: // en prfixe et suffixe
4: #include <iostream>
5: int main()
6: {
7: using std::cout;
8:
9: int monAge = 39; // initialise deux entiers
10: int tonAge = 39;
11: cout << "Jai: " << monAge << " ans.\n";
12: cout << "Tu as: " << tonAge << " ans\n";
13: monAge++; // suffixe
14: ++tonAge; // prfixe
15: cout << "Un an plus tard...\n";
16: cout << "Jai: " << monAge << " ans.\n";
17: cout << "Tu as: " << tonAge << " ans\n";
18: cout << "Encore une annee de plus\n";
19: cout << "Jai: " << monAge++ << " ans.\n";
20: cout << "Tu as: " << ++tonAge << " ans\n";
21: cout << "Reaffichons le resultat\n";
22: cout << "Jai: " << monAge << " ans.\n";
23: cout << "Tu as: " << tonAge << " ans\n";
24: return 0;
25: }
Ce programme produit le rsultat suivant :
Jai: 39 ans
Tu as: 39 ans
Un an plus tard...
Jai: 40 ans
Tu as: 40 ans
Encore une annee de plus
Jai: 40 ans
Tu as: 41 ans
Reaffichons le resultat
Jai: 41 ans
Tu as: 41 ans
Les lignes 9 et 10 dclarent deux variables entires initialises 39. Leurs valeurs safchent
aux lignes suivantes.
Aux lignes 13 et 14, monAge et tonAge sont incrmentes respectivement laide dun
oprateur sufxe et dun prxe. Le rsultat est gal 40 (lignes 16 et 17).
la ligne 19, monAge est incrment dans linstruction dafchage, laide dun oprateur
sufxe ; lincrmentation seffectue donc aprs lafchage qui produit nouveau 40, puis
C++Livre Page 74 J eudi, 7. mai 2009 9:45 09
Chapitre 4 Expressions et instructions 75
la variable monAge est incrmente. la ligne 20, tonAge est incrment par prxe, donc
avant lafchage qui produit 41.
Aux lignes 22 et 23, le rsultat est rafch. Les deux valeurs sont gales puisque
linstruction dincrmentation a t excute.
Priorit des oprateurs
Dans linstruction compose suivante :
x = 5+3 * 8;
quelle sera lopration effectue en premier, laddition ou la multiplication ? Si cest laddi-
tion, le rsultat sera 8 * 8, soit 64. Si cest la multiplication, le rsultat sera 5 + 24, soit 29.
La norme C++ ne laisse pas de place au hasard dans lapplication des oprations. Tout
oprateur a un niveau de priorit par rapport aux autres (voir lAnnexe C). La multiplica-
tion ayant une priorit suprieure laddition, cest elle qui sera excute en premier et la
valeur de lexpression sera donc gale 29.
Lorsque deux oprateurs sont de priorit identique, les oprations seffectuent de gauche
droite. Dans lexpression suivante :
x = 5+3+8 * 9+6 * 4;
La multiplication est prioritaire, de gauche droite. Do : 8 * 9 = 72, et 6 * 4 = 24. Ce qui
revient :
x = 5+3+72+24;
Laddition seffectue ensuite de gauche droite : 5 + 3 = 8 ; 8 + 72 = 80 ; 80 + 24 = 104.
Attention : certains oprateurs (laffectation par exemple) sont valus de droite
gauche !
Que faire si lordre des priorits ne vous convient pas ? Prenons un exemple :
totalSecondes = nbMinutesA+nbMinutesB * 60;
Dans cette expression, nbMinutesB sera multipli par 60, puis ajout nbMinutesA. Or,
on souhaite en fait ajouter les deux variables pour obtenir le nombre de minutes, puis
multiplier ce rsultat par 60 pour obtenir le nombre de secondes.
Les parenthses permettent de dnir lordre des priorits de faon adquate. Lexemple
prcdent devrait donc scrire de la faon suivante :
totalSecondes = (nbMinutesA+nbMinutesB) * 60;
C++Livre Page 75 J eudi, 7. mai 2009 9:45 09
76 Le langage C++
Parenthses imbriques
Dans les expressions complexes, vous pouvez imbriquer des parenthses. Par exemple,
pour dterminer le nombre total de secondes puis calculer le nombre total de personnes
concernes avant de multiplier le nombre de secondes par le nombre de personnes, on
crira lexpression suivante :
totalPersonSecondes = ( ( (nbMinutesA +
nbMinutesB) * 60) *
(personTravail+personVacances) );
Cette expression complexe se lit de lintrieur vers lextrieur. Elle ajoute dabord
nbMinutesA nbMinutesB, puis multiplie le rsultat par 60. Ensuite, elle additionne
personTravail et personVacances et multiplie cette somme par le nombre de secondes.
Cet exemple met en vidence un problme important : cette expression est facile
comprendre pour un ordinateur mais trs difcile lire, modier et comprendre pour un
humain. On pourrait la dcomposer en utilisant des variables entires temporaires : ,
totalMinutes = nbMinutesA+nbMinutesB;
totalSecondes = totalMinutes * 60;
totalPersonnel = personTravail+personVacances;
totalPersonSecondes = totalPersonnel * totalSecondes;
Cet exemple est plus facile comprendre, mme sil est plus long crire et quil utilise
plus de variables temporaires que le prcdent. Si vous prfrez ce type de programma-
tion, noubliez pas dinsrer des commentaires et de remplacer 60 par une constante
symbolique : vous aurez alors un code qui sera plus facile comprendre et maintenir.
Faire Ne pas faire
Se souvenir que les expressions ont une
valeur.
Pour incrmenter ou dcrmenter une varia-
ble avant son utilisation dans lexpression,
utiliser loprateur prxe (++variable).
Pour incrmenter ou dcrmenter une varia-
ble aprs son utilisation dans lexpression,
utiliser loprateur sufxe (variable++).
Modier les priorits de calcul laide de
parenthses.
Avoir recours des imbrications trop profon-
des qui rendent lexpression difcile
comprendre et mettre jour.
Confondre le sufxe et le prxe.
C++Livre Page 76 J eudi, 7. mai 2009 9:45 09
Chapitre 4 Expressions et instructions 77
Vrai ou faux
Toute expression peut tre value en terme de vrai ou faux. Une expression qui donne un
rsultat mathmatique de zro renverra false, et true dans tous les autres cas. ,
Dans les versions prcdentes de C++, les notions de vrai ou faux taient reprsentes par
des entiers, mais la norme ANSI a introduit le type bool. Une variable de ce type peut
avoir deux valeurs : true (vrai) ou false (faux).
De nombreux compilateurs proposaient dj un type bool, qui tait reprsent
en interne par un entier long et occupait donc 4 octets. Aujourdhui, les compi-
lateurs compatibles ANSI proposent souvent un type bool qui noccupe quun
seul octet.
Oprateurs relationnels
Les oprateurs relationnels permettent dvaluer lgalit ou la diffrence entre deux
nombres. Une instruction relationnelle produit toujours un rsultat true ou false. Les diff-
rents oprateurs disponibles sont prsents dans le Tableau 4.1.
Tous les oprateurs relationnels renvoient une valeur de type bool : soit true,
soit false. Dans les prcdentes versions du C++, ces oprateurs renvoyaient
0 (pour false) ou une valeur non nulle (gnralement 1) pour true.
Si la variable entire monAge est gale 45 et que la variable entire tonAge est gale 50,
vous pouvez tester si elles sont gales laide de loprateur dgalit (==) :
// La valeur de monAge est-elle gale celle de tonAge?
monAge == tonAge;
La valeur renvoye est false puisque les variables sont diffrentes. Vous pouvez vrier
que monAge est infrieure tonAge grce lexpression :
// La valeur de moAge est-elle infrieure celle de tonAge?
monAge < tonAge;
La rponse est true, 45 tant infrieur 50.
Certains programmeurs dbutants confondent loprateur daffectation (=)
avec loprateur dgalit (==). Il en rsulte des bogues dans les programmes.
In
f
o
In
f
o
A
tte
n
tio
n
C++Livre Page 77 J eudi, 7. mai 2009 9:45 09
78 Le langage C++
Il existe six oprateurs relationnels : gal (==), infrieur (<), suprieur (>), infrieur
ou gal (<=), suprieur ou gal (>=) et diffrent de (!=). Le Tableau 4.1 dresse la liste
des oprateurs relationnels.
Linstruction if
Cette instruction permet de tester une condition (si deux variables sont gales, par exem-
ple) et de se brancher sur un bloc dinstructions ou sur un autre en fonction du rsultat
obtenu.
La forme la plus simple de linstruction if est :
if (expression)
instruction;
Tableau 4.1 : Oprateurs relationnels
Nom Oprateur Exemple Rsultat
Egal == 100 == 50;
50 == 50;
faux
vrai
Diffrent de != 100!= 50;
50!= 50;
vrai
faux
Suprieur > 100 > 50;
50 > 50;
vrai
faux
Suprieur ou gal >= 100 >= 50;
50 >= 50;
vrai
vrai
Infrieur < 100 < 50;
50 < 50;
faux
faux
Infrieur ou gal <= 100 <= 50;
50 <= 50;
faux
vrai
Faire Ne pas faire
Se rappeler que les oprateurs relationnels
renvoient une valeur true (vrai) ou false
(faux).
Confondre loprateur daffectation (=) avec
loprateur gal (==). Cest lune des erreurs
les plus frquentes en C++.
C++Livre Page 78 J eudi, 7. mai 2009 9:45 09
Chapitre 4 Expressions et instructions 79
Lexpression entre parenthses peut contenir nimporte quelle expression, mais le plus
souvent il sagit dune expression relationnelle. Si le rsultat est faux, linstruction est
ignore ; sil est vrai, linstruction est excute. Voici un exemple :
if (grandNombre > petitNombre)
grandNombre = petitNombre;
Ce code compare grandNombre et petitNombre. Si grandNombre est suprieur petit-
Nombre, la ligne suivante affecte la valeur de petitNombre grandNombre. Dans le cas
contraire, la seconde ligne est ignore.
Un bloc dinstruction entour par des accolades tant quivalent une instruction simple,
le branchement peut effectuer plusieurs instructions :
if (expression)
{
instruction1;
instruction2;
instruction3;
}
Exemple :
if (grandNombre > petitNombre)
{
grandNombre = petitNombre;
std::cout << "grandNombre : " << grandNombre << "\n";
std::cout << "petitNombre : " << petitNombre << "\n";
}
Cette fois, si grandNombre est suprieur petitNombre, la valeur de petitNombre sera
affecte grandNombre et les valeurs de ces deux variables seront afches. Le
Listing 4.4 est un exemple plus complet dutilisation des oprateurs relationnels pour
effectuer des branchements.
Listing 4.4 : Branchements conditionnels
1: // Branchements conditionnels laide
2: // doprateurs relationnels
3: #include <iostream>
4: int main()
5: {
6: using std::cout;
7: using std::cin;
8:
9: int OMScore, PSGScore;
C++Livre Page 79 J eudi, 7. mai 2009 9:45 09
80 Le langage C++
10: cout << "Score de lOM: ";
11: cin >> OMScore;
12:
13: cout << "\nScore du PSG: ";
14: cin >> PSGScore;
15:
16: cout << "\n";
17:
18: if (OMScore > PSGScore)
19: cout << "Allez lOM!\n";
20:
21: if (OMScore < PSGScore)
22: {
23: cout << "Vive le PSG!\n";
24: }
25:
26: if (OMScore == PSGScore)
27: {
28: cout << "Egalite? Oh non!\n";
29: cout << "Donne-moi le score du PSG: ";
30: cin >> PSGScore;
31:
32: if (OMScore > PSGScore)
33: cout << "Je le savais, allez lOM!";
34:
35: if (PSGScore > OMScore)
36: cout << "Je le savais, vive le PSG!";
37:
38: if (PSGScore == OMScore)
39: cout << "Match nul!";
40: }
41:
42: cout << "\nOK.\n";
43: return 0;
44: }
Ce programme produit par exemple le rsultat suivant :
Score de lOM: 2
Score du PSG: 2
Egalite? Oh non!
Donne-moi le score du PSG: 2
Match nul!
OK.
Ce programme demande les scores de deux quipes de football. Les variables sont compa-
res aux lignes 18, 21 et 26.
C++Livre Page 80 J eudi, 7. mai 2009 9:45 09
Chapitre 4 Expressions et instructions 81
Si lun des scores est suprieur lautre, un message apparat. Si les scores sont gaux, le
bloc de code de la ligne 26 la ligne 40 sexcute. Le second score est demand
nouveau, puis les scores sont compars.
Notez que si le score initial du PSG est suprieur celui de lOM, la condition de linstruc-
tion if (ligne 18) donne false et la ligne 19 nest pas excute. Si la ligne 21 renvoie
une valeur true, linstruction de la ligne 23 sexcute. Le test de la ligne 26 sexcute et si
le rsultat est faux, le programme passe directement la ligne 41, sautant tout le bloc
dinstructions.
Cet exemple montre quun rsultat vrai un test if nempche pas lvaluation des autres
instructions if.
Notez que laction des deux premires instructions if tient sur une ligne (afchage de
"Allez lOM" ou "Vive le PSG"). Dans le premier exemple (ligne 19) nous navons pas
utilis daccolades car elles ne sont pas obligatoires pour un bloc dune seule instruction,
mais elles peuvent tre ajoutes comme aux lignes 22 24.
viter les erreurs classiques avec les instructions if
De nombreux programmeurs C++ dbutants ajoutent par inadvertance un point-virgule
aprs les instructions if: ,
if(uneValeur < 10); // Attention! Remarquez le point-virgule.
uneValeur = 10;
Le but ici tait de tester si uneValeur tait infrieure 10 et, dans lafrmative, de lui
affecter la valeur 10. Si vous excutez le code tel quil est crit ci-dessus, vous constaterez
que uneValeur est toujours mis 10 ! Pourquoi ? Linstruction if se termine par un point-
virgule ce qui correspond un oprateur vide (qui ne fait rien).
Lindentation nayant aucun sens pour le compilateur, le code prcdent revient crire :
if (uneValeur < 10) // test
; // ne rien faire
uneValeur = 10; // affectation
La suppression du point-virgule la n de linstruction if rsoudra le problme.
Pour rduire les risques, vous pouvez toujours crire vos instructions if entre accolades,
mme lorsque le corps ne stale que sur une ligne :
if (uneValeur < 10)
{
uneValeur = 10;
}
C++Livre Page 81 J eudi, 7. mai 2009 9:45 09
82 Le langage C++
Lindentation
Le Listing 4.4 prsente un des styles dindentation pour les instructions if. Le choix du
meilleur style pour lalignement des accolades est un sujet qui dclenche toujours les
passions parmi les programmeurs. Il en existe des douzaines, mais vous rencontrerez le
plus souvent les trois suivants :
Laccolade douverture est place la suite de la condition et laccolade de fermeture
est aligne sur le if.
if (expression) {
instructions
}
Les deux accolades sont alignes sous le if et les instructions sont dcales.
if (expression)
{
instructions
}
Les accolades et les instructions sont dcales.
if (expression)
{
instructions
}
Cet ouvrage utilise le deuxime style, car les blocs apparaissent plus clairement lorsque
leurs accolades sont alignes avec la condition. Cela dit, vous tes tout fait libre dadopter la
mthode de votre choix du moment que vous restez cohrent.
Linstruction else
Un programme doit souvent effectuer un branchement lorsquune condition est vraie et un
autre si elle est fausse. Cela vite les redondances dans les chiers sources (voir le
Listing 4.5).
Le mot-cl else permet deffectuer ces branchements de faon claire :
if (expression)
instruction;
else
instruction;
C++Livre Page 82 J eudi, 7. mai 2009 9:45 09
Chapitre 4 Expressions et instructions 83
Listing 4.5 : Utilisation du mot-cl else
1: // Utilisation de la clause else
2: //
3: #include <iostream>
4: int main()
5: {
6: using std::cout;
7: using std::cin;
8:
9: int premierNombre, secondNombre;
10: cout << "Entrez un nombre: ";
11: cin >> premierNombre;
12: cout << "\nEntrez un nombre plus petit: ";
13: cin >> secondNombre;
14: if (premierNombre > secondNombre)
15: cout << "\nMerci!\n";
16: else
17: cout << "\nErreur! Le premier nest pas superieur!";
18:
19: return 0;
20: }
Ce programme produit le rsultat suivant :
Entrez un nombre: 10
Entrez un nombre plus petit: 12
Erreur! Le premier nest pas superieur!
La ligne 14 value la condition de linstruction if. Si elle est vraie, la ligne 15 sexcute et
le programme avance jusqu la ligne 18 (aprs linstruction else). Si la condition de la
ligne 14 renvoie false, le ux de contrle passe la clause else et cest la ligne 17 qui
sexcute. Si la clause else tait supprime, la ligne 17 sexcuterait chaque fois, quel
que soit le rsultat de la condition de linstruction if.
Les deux instructions du if et du else peuvent tre remplaces par des blocs entre accolades.
Instruction if
La syntaxe de linstruction if est la suivante :
Forme 1
if (expression)
instruction;
instruction_suivante;
C++Livre Page 83 J eudi, 7. mai 2009 9:45 09
84 Le langage C++
Si expression est vraie, linstruction sexcute et le programme continue jusqu
linstruction_suivante. Si expression est fausse, lexcution passe directement lins-
truction_suivante.
Linstruction peut tre une expression simple ou un bloc dinstructions entre accolades.
Forme 2
if (expression)
instruction1;
else
instruction2;
instruction_suivante;
Si expression est vraie, linstruction1 sexcute. Dans le cas contraire, cest linstruction2.
Le programme passe ensuite linstruction_suivante.
Exemple :
if (val < 10)
cout << "val est inferieur 10");
else
cout << "val nest pas inferieur 10!");
cout << "Termine." << endl;
Instructions if imbriques
Une instruction if...else peut contenir une autre instruction if...else. Cette pratique
sappelle limbrication de conditions :
if (expression1)
{
if (expression2)
instruction1;
else
{
if (expression3)
instruction2;
else
instruction3;
}
}
else
instruction4;
Les choses se compliquent ! Si expression1 est vraie et expression2 est vraie,
linstruction1 sexcute. Si expression1 est vraie alors que expression2 est fausse, et
que expression3 est vraie, linstruction2 sexcute. Si expression1 est vraie, et que
C++Livre Page 84 J eudi, 7. mai 2009 9:45 09
Chapitre 4 Expressions et instructions 85
les expressions2 et 3 sont fausses, linstruction3 sexcute. Enn, si expression1 est
fausse, linstruction4 sexcute. Comme vous pouvez le constater, les instructions if
imbriques peuvent devenir assez confuses !
Le Listing 4.6 montre un exemple dutilisation de cette structure.
Listing 4.6 : Conditions imbriques
1: // Listing4.6 - instructions if imbriques
2: //
3: #include <iostream>
4: int main()
5: {
6: // Demander deux nombres
7: // Les affecter nb1 et nb2
8: // Si nb1 est suprieur ou gal nb2,
9: // vrifier sils sont divisibles
10: // Si oui, vrifier sils sont identiques
11:
12: using namespace std;
13:
14: int nb1, nb2;
15: cout << "Entrez deux nombres.\nPremier: ";
16: cin >> nb1;
17: cout << "\nSecond: ";
18: cin >> nb2;
19: cout << "\n\n";
20:
21: if (nb1 >= nb2)
22: {
23: if ( (nb1% nb2) == 0) // sont-ils divisibles?
24: {
25: if (nb1 == nb2)
26: cout << "Ils sont identiques!\n";
27: else
28: cout << "Ils sont divisibles!\n";
29: }
30: else
31: cout << "Ils ne sont pas divisibles!\n";
32: }
33: else
34: cout << "Le second nombre est suprieur!\n";
35: return 0;
36: }
C++Livre Page 85 J eudi, 7. mai 2009 9:45 09
86 Le langage C++
Ce programme produit par exemple le rsultat suivant :
Entrez deux nombres.
Premier: 15
Second: 4
Ils ne sont pas divisibles!
Les deux nombres sont saisis et compars. Linstruction if de la ligne 21 vrie que le
premier est suprieur ou gal au second. Si ce nest pas le cas, la clause else de la ligne 33
sexcute.
Si le premier if est vrai, la ligne 22 sexcute et le second if est test. Il vrie que la
division ne produit pas de reste, ce qui signie que les nombres sont divisibles ou gaux.
Linstruction if de la ligne 25 vrie sils sont gaux et afche un message appropri .
Si linstruction if de la ligne 23 choue, cest la clause else de la ligne 30 qui sexcute.
Utilisation daccolades dans les instructions if
imbriques
Bien quil soit permis de ne pas mettre daccolades pour des instructions if composes
dune seule instruction et que lon puisse ainsi imbriquer des instructions if, cela peut
entraner une grosse confusion. Le code suivant, par exemple, est parfaitement autoris en
C++ mais semble bien confus :
if (x > y) // si x > y
if (x < z) // et x < z
x = y; // x reoit la valeur de y
else // sinon, si x nest pas inferieur z
x = z; // definir x sur la valeur de z
else // sinon, si x nest pas superieur y
y = x; // definir y sur la valeur de x
Les indentations et les espaces sont pratiques pour le programmeur, mais ne changent rien
pour le compilateur. Il est trs facile de se tromper et daffecter une clause else au
mauvais if, comme le montre le Listing 4.7
Listing 4.7 : Intrt des accolades pour clarier les instructions conditionnelles imbriques
1: // Importance des accolades
2: // dans des instructions if imbriques
3: #include <iostream>
C++Livre Page 86 J eudi, 7. mai 2009 9:45 09
Chapitre 4 Expressions et instructions 87
4: int main()
5: {
6: int x;
7: std::cout << "Entrez un nombre infrieur 10 ou suprieur 100: ";
8: std::cin >> x;
9: std::cout << "\n";
10:
11: if (x >= 10)
12: if (x > 100)
13: std::cout << "Suprieur 100, Merci!\n";
14: else // pas la directive else prvue!
15: std::cout << "Infrieur 10, Merci!\n";
16:
17: return 0;
18: }
Ce programme produit par exemple le rsultat suivant :
Entrez un nombre infrieur 10 ou suprieur 100: 30
Infrieur 10, Merci!
Lintention du programmeur, ici, tait de vrier que le nombre saisi tait infrieur 10 ou
suprieur 100 et denvoyer un message de remerciement.
Lorsque la condition de linstruction if de la ligne 11 est vraie, la ligne 12 sexcute. Ici,
la ligne 12 sexcute si le nombre entr est suprieur ou gal 10. La ligne 12 contient
aussi une instruction if dont la condition est vraie si le nombre entr est suprieur 100.
Dans ce cas, linstruction de la ligne 13 sexcute et afche le message qui convient.
Si le nombre entr est infrieur 10, la condition de la ligne 11 renvoie false. Le programme
se poursuit alors la ligne suivant cette instruction if (ici, la ligne 16). Si vous entrez un
nombre infrieur 10, vous obtiendrez donc :
Entrez un nombre inferieur a 10 ou superieur a 100: 9
Comme vous le constatez, aucun message ne safche. La clause else de la ligne 14 tait
cense correspondre linstruction if de la ligne 11, comme lindiquait son indentation.
Malheureusement, elle est en fait associe la clause if de la ligne 12, ce qui entrane le
problme que nous venons de constater.
Ce bogue est subtil car le compilateur ne le signale pas ! Cest un programme syntaxique-
ment correct, mais qui ne fait tout simplement pas ce que lon attend de lui. En outre, il
fonctionne correctement la plupart du temps, tant que lon entre un nombre suprieur
100. Cependant, avec un nombre compris entre 11 et 99, vous constaterez quil y a un
problme vident !
Le Listing 4.8 corrige ce problme en utilisant des accolades.
C++Livre Page 87 J eudi, 7. mai 2009 9:45 09
88 Le langage C++
Listing 4.8 : Utilisation correcte daccolades dans une instruction if
1: // Utilisation correcte daccolades
2: // dans les instructions if imbriques
3: #include <iostream>
4: int main()
5: {
6: int x;
7: std::cout << "Entrez un nombre infrieur 10 ou suprieur 100: ";
8: std::cin >> x;
9: std::cout << "\n";
10:
11: if (x >= 10)
12: {
13: if (x > 100)
14: std::cout << "Suprieur 100, Merci!\n";
15: }
16: else // Bogue corrig !
17: std::cout << "Infrieur 10, Merci!\n";
18: return 0;
19: }
Ce programme ragit maintenant correctement, par exemple :
Entrez un nombre infrieur 10 ou suprieur 100: 34
Entrez un nombre infrieur 10 ou suprieur 100: 8
Infrieur 10, Merci!
Entrez un nombre infrieur 10 ou suprieur 100: 101
Suprieur 100, Merci!
Les accolades des lignes 12 et 15 regroupent ce quelles contiennent en une seule instruction.
La ligne 16 correspond maintenant linstruction if de la ligne 11.
Vous pouvez viter bon nombre de problmes lis aux instructions ifelse en
plaant toujours des accolades pour les instructions des clauses if et else,
mme lorsque la condition nest suivie que dune seule instruction :
if (valeur < 10)
{
valeur = 10;
}
else
{
valeur = 25;
};
A
s
t
u
c
e
C++Livre Page 88 J eudi, 7. mai 2009 9:45 09
Chapitre 4 Expressions et instructions 89
Oprateurs logiques
On veut parfois tester plusieurs conditions la fois, par exemple pour savoir si "x est sup-
rieur y et si y est suprieur z". Le programme doit dterminer si ces deux conditions
sont vries ou non an dagir en consquence.
Imaginons un systme dalarme volu. Si lalarme de la porte sonne ET sil est plus de
18 h ET si nous ne sommes PAS en vacances OU si cest un week-end, appeler la police.
Pour ce type dvaluation, il faut utiliser les trois oprateurs logiques de C++, qui sont
prsents dans le Tableau 4.2.
ET logique
Il permet dvaluer deux expressions. Pour renvoyer un rsultat vrai, les deux expressions
doivent tre toutes les deux vraies. Sil est vrai que vous avez faim ET que vous ayez de
largent, ALORS vous pouvez acheter une pizza. Par consquent,
if ( (x == 5) && (y == 5) )
sera vrie si x et y sont tous les deux gaux 5.
Notez que loprateur logique ET est symbolis par deux symboles &&, ce qui est diffrent
de loprateur binaire & prsent au Chapitre 21.
OU logique
Il permet dvaluer deux expressions. Si lune ou lautre est vraie, le rsultat est vrai. Si
vous avez de largent liquide OU une carte de crdit (ou les deux), ALORS vous pouvez
payer laddition. Par consquent,
if ( (x == 5) || (y == 5) )
sera vrie si x ou y est gal 5, ou si tous les deux sont gaux 5.
Loprateur logique OU est symbolis par deux barres verticales (||). La barre verticale
simple symbolise un oprateur diffrent qui sera tudi au Chapitre 21.
Tableau 4.2 : Oprateurs logiques
Oprateur Symbole Exemple
AND && (et) expression1 && expression2
OR || (ou) expression1 || expression2
NOT ! (non) !expression
C++Livre Page 89 J eudi, 7. mai 2009 9:45 09
90 Le langage C++
NON logique
Il donne un rsultat vrai si lexpression teste est fausse. Par exemple, lexpression :
if (!(x == 5) )
ne sera vraie que si x nest pas gal 5, ce qui revient crire :
if (x!= 5)
valuation en court-circuit
Lors de lvaluation dune instruction ET comme :
if ( (x == 5) && (y == 5) )
le compilateur teste dabord la vracit de la premire instruction (x==5) ; en cas dchec
(si x nest pas gal 5), la seconde instruction (y == 5) ne sera pas teste puisque la
clause ET requiert que les deux conditions soient vraies.
De mme, dans le cas dune clause OR comme :
if ( (x == 5) || (y == 5) )
si la premire instruction donne vrai (x == 5), la seconde ne sera pas excute (y == 5)
puisque lune ou lautre des conditions est sufsante.
Bien que cela puisse ne pas sembler important, tudiez lexemple suivant :
if ( (x == 5 )|| (++y == 3) )
Si x nest pas gal 5, (++y == 3) ne sera pas valu. Si vous comptiez sur lincrmentation
de y, celle-ci risque de ne pas toujours tre excute.
Priorit des oprateurs relationnels
Comme pour toutes les expressions C++, lutilisation des oprateurs relationnels et des
oprateurs logiques, renvoie une valeur qui est, ici, true ou false. Pour connatre lordre
dans lequel ils sont excuts, reportez-vous lAnnexe C. Voici un exemple :
if ( x > 5 && y > 5 || z > 5)
On peut supposer que le programmeur souhaite que lexpression soit vraie si x et y sont
suprieurs 5 ou si z est suprieur 5. Cela peut aussi signier que lexpression nest
vraie que si x est suprieur 5, et si soit y soit z est suprieur 5.
C++Livre Page 90 J eudi, 7. mai 2009 9:45 09
Chapitre 4 Expressions et instructions 91
Si x est gal 3 et que y et z valent tous les deux 10, la premire interprtation sera vraie
(z est suprieur 5, on ignore donc x et y), mais la seconde sera fausse (il nest pas vrai
que x soit suprieur 5 et peu importe ce qui se trouve du ct droit du symbole && puisque
les deux cts doivent tre vrais).
Les parenthses permettent donc daffecter explicitement des priorits et rendent ainsi
lexpression plus lisible :
if ( (x > 5) && (y > 5 || z > 5) )
Cette instruction donne un rsultat faux, car x nest pas suprieur 5. La partie gauche de
lexpression AND tant fausse, toute lexpression est fausse.
Pour clarier vos intentions, nhsitez pas insrer des parenthses. Elles
prcisent ce que vous voulez faire et vitent les erreurs nes dune mauvaise
comprhension de lordre des oprateurs.
Vrai ou faux
En C++, la valeur zro quivaut false alors que les autres valeurs sont considres
comme true. Une expression ayant toujours une valeur, de nombreux programmeurs C++
exploitent cette fonctionnalits dans leurs programmes. Une instruction comme :
if (x) // si x est vrai (non zro)
x = 0;
peut ainsi se lire "si x est diffrent de zro, x reoit la valeur 0". Ce qui revient crire :
if (x!= 0) // si x est diffrent de zro
x = 0;
Ces deux instructions sont correctes, mais la seconde est plus lisible. Il est donc conseill
de rserver la premire forme aux vritables tests logiques plutt que de sen servir pour
tester si une valeur est nulle.
Ces deux instructions sont galement quivalentes :
if (!x) // si x est faux (zro)
if (x == 0) // si x gale zro
La seconde instruction est plus explicite si vous testez la valeur mathmatique de x plutt
que son tat logique.
In
f
o
C++Livre Page 91 J eudi, 7. mai 2009 9:45 09
92 Le langage C++
Oprateur conditionnel (ternaire)
Loprateur conditionnel (?:) est le seul oprateur C++ ternaire (il comprend trois termes).
Il utilise trois expressions et renvoie une valeur :
(expression1)? (expression2): (expression3)
Cette ligne signie : "Si expression1 est vraie, renvoyer la valeur de lexpression2 ; sinon,
renvoyer la valeur de lexpression3". Le rsultat de cet oprateur est gnralement affect
une variable. Le Listing 4.9 montre comment lutiliser la place dune instruction if.
Listing 4.9 : Exemple doprateur conditionnel
1: // Listing4.9 -
2: //
3: #include <iostream>
4: int main()
5: {
6: using namespace std;
7:
8: int x, y, z;
9: cout << "Entrez deux nombres.\n";
10: cout << "Premier: ";
11: cin >> x;
12: cout << "\nSecond: ";
13: cin >> y;
14: cout << "\n";
15:
16: if (x > y)
17: z = x;
18: else
19: z = y;
20:
Faire Ne pas faire
Mettre des parenthses dans vos tests logi-
ques pour les rendre plus clairs et dnir
explicitement les priorits.
Utiliser des accolades pour clarier les
instructions if imbriques.
Utiliser if (x) comme synonyme de if
(x!= 0);.
Utiliser if (!x) comme synonyme de if
(x == 0);.
C++Livre Page 92 J eudi, 7. mai 2009 9:45 09
Chapitre 4 Expressions et instructions 93
21: cout << "Aprs le test if, z: " << z;
22: cout << "\n";
23:
24: z = (x > y)? x: y;
25:
26: cout << "Aprs le test conditionnel, z: " << z;
27: cout << "\n";
28: return 0;
29: }
Ce programme produit le rsultat suivant :
Entrez deux nombres.
Premier: 5
Second: 8
Aprs le test if, z: 8
Aprs le test conditionnel, z: 8
Trois variables entires sont cres : x, y et z. Les deux premires sont saisies par lutilisa-
teur. Linstruction if de la ligne 16 dtermine la plus grande et laffecte z. Le rsultat
safche la ligne 21.
Loprateur conditionnel de la ligne 24 ralise le mme test et affecte la valeur la plus
leve z. Si x est suprieur y, il renvoie la valeur de x ; sinon, il renvoie la valeur de y.
Cette valeur est affecte z et le rsultat safche la ligne 26. Comme vous pouvez le
constater, linstruction conditionnelle est en fait un quivalent (raccourci) de linstruction
if...else.
Questions-rponses
Q Pourquoi utiliser des parenthses superues lorsque la priorit des oprateurs est
vidente ?
R Parce que le programme gagne en lisibilit et peut tre mis jour plus aisment.
Q Si les oprateurs relationnels renvoient true ou false, pourquoi une valeur diff-
rente de zro est-elle considre comme vraie ?
R Cette convention provient du langage C, qui tait souvent utilis pour crire les logiciels
de bas niveau, comme les systmes dexploitation et les logiciels de contrle en temps
rel. Il est probable que cette utilisation ait volu pour devenir un raccourci permet-
tant de tester rapidement si tous les bits dun masque ou dune variable sont gaux 0.
C++Livre Page 93 J eudi, 7. mai 2009 9:45 09
94 Le langage C++
Les oprateurs relationnels renvoient true ou false, mais chaque expression renvoie une
valeur et ces valeurs peuvent tre values dans une instruction if. Voici un exemple :
if ( (x = a+b) == 35 )
Cette instruction est tout fait correcte en C++. Elle produit une valeur, mme si
a+b ne donne pas 35. Notez aussi que la somme obtenue est affecte x dans tous
les cas.
Q quoi servent les tabulations, les espaces et les sauts de ligne dans un
programme ?
R Ils nont aucun effet sur le droulement du programme, mais rendent le code plus lisible.
Q Les nombres ngatifs peuvent-ils possder les valeurs true ou false ?
R Tous les nombres diffrents de zro, quils soient positifs ou ngatifs, sont considrs
comme vrais.
Testez vos connaissances
1. Quest-ce quune expression ?
2. Est-ce que x = 5+7 est une expression ? Quelle est sa valeur ?
3. Quelle est la valeur de 201 / 4 ?
4. Quelle est la valeur de 201% 4 ?
5. Si monAge, a et b sont des variables entires, quel est le rsultat de ces expressions ?
monAge = 39;
a = monAge++;
b = ++monAge;
6. Quelle est la valeur de 8+2*3 ?
7. Quelle est la diffrence entre if (x = 3) et if (x == 3) ?
8. Les valeurs suivantes sont-elles gales true ou false ?
a. 0
b. 1
c. -1
d. x = 0
e. x == 0 // en admettant que x ait la valeur 0
C++Livre Page 94 J eudi, 7. mai 2009 9:45 09
Chapitre 4 Expressions et instructions 95
Exercices
1. crivez une instruction if qui teste deux variables entires et qui affecte la valeur la
plus faible la variable la plus leve. Nutilisez quune seule clause else.
2. Lisez ce programme. Imaginez la saisie de trois nombres et crivez le rsultat.
1: #include <iostream>
2: using namespace std;
3: int main()
4: {
5: int a, b, c;
6: cout << "Entrez trois nombres.\n";
7: cout << "a: ";
8: cin >> a;
9: cout << "\nb: ";
10: cin >> b;
11: cout << "\nc: ";
12: cin >> c;
13:
14: if (c = (a-b))
15: cout << "a :" << a << " moins b :" << b <<
16: " est gal c :" << c << "\n";
17: else
18: cout << "a-b nest pas gal c :";
19: return 0;
20: }
3. Saisissez le programme de lExercice 2 ; compilez-le, liez-le et lancez-le. Entrez les
nombres 10, 20 et 50. Obtenez-vous le rsultat attendu ? Pourquoi ?
4. Examinez le programme suivant et imaginez le rsultat :
1: #include <iostream>
2: using namespace std;
3: int main()
4: {
5: int a = 2, b = 2, c;
6: if (c = (a-b))
7: cout << "c vaut: " << c;
8: return 0;
9: }
5. Saisissez, compilez et lancez le programme prcdent. Obtenez-vous le rsultat
attendu ? Pourquoi ?
C++Livre Page 95 J eudi, 7. mai 2009 9:45 09
C++Livre Page 96 J eudi, 7. mai 2009 9:45 09
5
Fonctions
Au sommaire de ce chapitre
Le rle dune fonction et ses diffrents lments
Comment dclarer et dnir les fonctions
Comment passer des paramtres une fonction
Comment renvoyer une valeur partir dune fonction
On pourrait penser que la programmation oriente objet a privilgi les objets au dtriment
des fonctions. Il nen est rien : ces dernires demeurent un lment essentiel des applica-
tions. Les fonctions globales existent en dehors des objets et des classes, tandis que les
fonctions membres (ou mthodes membres) existent au sein dune classe et accomplissent
ses diffrentes tches.
Nous ne prsenterons ici que les fonctions globales ; nous tudierons le principe des fonctions
membres au cours du prochain chapitre.
C++Livre Page 97 J eudi, 7. mai 2009 9:45 09
98 Le langage C++
Quest-ce quune fonction ?
Par nature, une fonction est un sous-programme qui agit sur des donnes et renvoie une
valeur. Tout programme C++ comprend au moins une fonction : la fonction main(), qui
est la fonction principale qui sexcute automatiquement et qui peut appeler dautres fonc-
tions, qui peuvent elles-mmes en appeler dautres.
Ces fonctions ne faisant pas partie dun objet, elles sont appeles "globales", cest--dire
quelles sont accessibles de nimporte quel endroit du programme. Les fonctions abordes
dans ce chapitre sont les fonctions globales, sauf mention contraire.
Chaque fonction porte un nom particulier qui lidentie dans le programme. Lorsque ce
nom est rencontr, le programme se place directement au dbut de la fonction cest
que lon dsigne par le terme dappel de fonction. Lorsque la fonction se termine (en
rencontrant une instruction return ou laccolade fermante de la fonction), le
programme poursuit son excution la ligne qui suit lappel de la fonction (voir
Figure 5.1).
Lorsquelle est bien conue, une fonction ralise une seule action spcique, identie par
le nom de la fonction. Les tches complexes doivent tre divises en fonctions simples qui
seront appeles tour tour.
Il existe deux catgories de fonctions : les fonctions dnies par lutilisateur et les fonc-
tions prdnies qui sont intgres au compilateur. Les fonctions dnies par lutilisateur
sont celles que vous crivez vous-mme.
Figure 5.1
Aprs lexcution dune
fonction, le programme
revient linstruction
qui suit lappel.
Programme
main()
{ instruction;
fonction1();
instruction;
fonction2();
instruction;
fonction4();
instruction;
}
Fonction1
return;
Fonction4
return;
Fonction3
return;
Fonction2
instruction;
fonction3();
return;
C++Livre Page 98 J eudi, 7. mai 2009 9:45 09
Chapitre 5 Fonctions 99
Valeurs renvoyes, paramtres formels et effectifs
Vous lavez vu au Chapitre 2, les fonctions peuvent recevoir des valeurs et renvoyer une
valeur. Lorsquelle est appele, une fonction effectue une certaine tche puis renvoie un
rsultat lappelant. Cette valeur sappelle la valeur renvoye et son type doit tre dclar.
Lorsque vous crivez :
int maFonction();
vous dclarez donc que maFonction renverra une valeur entire. tudiez maintenant la
dclaration suivante :
int maFonction(int unEntier, float unFlottant);
Cette dclaration indique que maFonction renverra une valeur entire et prendra deux
valeurs.
Lorsque vous envoyez des valeurs une fonction, celles-ci agissent comme des variables
que vous pouvez manipuler lintrieur de la fonction. La description des valeurs
envoyes est appele liste des paramtres formels. Dans lexemple prcdent, cette liste
contient unEntier, qui est une variable entire et unFlottant, une variable de type
float.
Un paramtre formel dcrit le type de la valeur passe la fonction. La valeur qui sera
rellement passe lors de lappel est appele paramtre effectif (ou rel). Dans lexemple
suivant :
int valeurRenvoyee = maFonction(5, 6.7);
la variable entire valeurRenvoyee reoit la valeur renvoye par maFonction et les
valeurs 5 et 6.7 sont passes en paramtres. Le type des paramtres effectifs doit corres-
pondre au type des paramtres formels correspondants. Dans ce cas, 5 va dans un entier et
6.7 dans une variable float ; les types correspondent donc bien.
Dclaration et dnition de fonctions
Pour tre exploitable dans le programme, une fonction doit tre dclare puis dnie. La
dclaration indique au compilateur le nom de la fonction, le type de la valeur quelle
renvoie et les paramtres attendus par la fonction. La dnition dcrit au compilateur le
fonctionnement de la fonction.
Il est impossible dappeler une fonction qui na pas au pralable t dclare. La dclaration
dune fonction est dsigne sous le nom de prototype.
C++Livre Page 99 J eudi, 7. mai 2009 9:45 09
100 Le langage C++
Il existe trois manires de dclarer une fonction. Vous pouvez :
crire le prototype dans un chier, que vous incluerez dans votre chier source laide
de la directive #include ;
crire le prototype de la fonction dans le chier source du programme qui lutilise ;
dnir la fonction avant quelle ne soit appele par une autre fonction. Dans ce cas, la
dnition tient lieu de prototype.
Bien que lon puisse dnir une fonction avant de lutiliser, ce qui pargne de crer un
prototype, cette pratique nest pas conseille pour trois raisons.
Tout dabord, cest une mauvaise ide dobliger les fonctions apparatre dans le chier
selon un ordre prdtermin, car cela complique la maintenance du programme lorsque
ces exigences voluent.
Deuximement, il est possible quune fonction A() doive pouvoir appeler une fonction
B() qui, elle-mme, a besoin dappeler la fonction A(). Il est donc impossible de dnir
A() avant de dnir B() et de dnir B() avant A(). En ce cas, il faut au moins dclarer
lune dentre elles.
Enn, les prototypes de fonctions facilitent la mise au point du programme. Si le prototype
dclare les paramtres de la fonction et le type du rsultat dune fonction et que la fonction
ne correspond pas ce prototype, le compilateur peut dtecter cette incompatibilit et
produire un message, ce qui vitera que lerreur ne soit visible quau moment de lexcu-
tion. Le prototype et la dnition se contrlent mutuellement, ce qui rduit la probabilit
quune faute de frappe provoque un bogue dans la programme.
Malgr tout, la grande majorit des programmeurs optent pour le troisime choix parce
quil rduit le nombre de lignes de code, quil simplie la maintenance (les changements
den-tte de la fonction exigent aussi le changement du prototype) et parce que lordre des
fonctions dans un chier est gnralement assez stable. Cela dit, les prototypes sont nces-
saires dans certains cas.
Prototypes de fonctions
Les prototypes de la plupart des fonctions prdnies sont dj crits et sont stocks dans
des chiers que vous pouvez inclure dans vos programmes laide de la directive
#include. Vous devrez en revanche fournir un prototype pour les fonctions que vous crivez
vous-mme.
Le prototype dune fonction est une instruction, ce qui signie quil doit se terminer par un
point-virgule. Il est form du type de la valeur renvoye et de la signature. La signature
dune fonction est compose de son nom et de la liste de ses paramtres formels.
C++Livre Page 100 J eudi, 7. mai 2009 9:45 09
Chapitre 5 Fonctions 101
La liste des paramtres formels numre tous les paramtres attendus et leurs types respectifs,
en les sparant par des virgules, comme dans la Figure 5.2.
Le prototype de la fonction et sa dnition doivent se correspondre, cest--dire contenir
les mmes informations (type de la valeur renvoye et signature). Si ce nest pas le cas,
cela provoque une erreur de compilation. Notez cependant que le prototype de la fonction
ne comprend pas ncessairement le nom des paramtres formels, mais quil peut se
contenter de leur type, comme dans cet exemple :
long Surface(int, int);
Ce prototype dclare la fonction Surface() qui renvoie un entier long et attend deux
paramtres entiers. Bien que cette syntaxe soit correcte, il est prfrable de mentionner le
nom des deux paramtres car cela produit une dclaration plus comprhensible :
long Surface(int longueur, int largeur);
Le but de la fonction et de ses deux paramtres apparat plus clairement.
Toutes les fonctions nont pas de type explicite pour leur rsultat. En labsence de ce type,
C++ supposera que la fonction renvoit une valeur int (entier), mais il est conseill de
lindiquer explicitement, y compris pour la fonction main().
Si votre fonction ne renvoie pas de valeur, son type de rsultat doit tre void (vide),
comme dans cet exemple :
void afficheNombre(int unNombre);
Cette ligne dclare une fonction appele afficheNombre possdant un paramtre entier.
void tant utilis comme type de rsultat, aucun lment nest renvoy.
Dnir la fonction
La dnition dune fonction est constitue de son en-tte et de son corps. Len-tte
ressemble au prototype de la fonction, sauf que les paramtres doivent tre nomms et
quil ny a pas de point-virgule nal.
Figure 5.2
lments dun prototype
de fonction.
unsigned short int
type du rsultat nom paramtres point-virgule
calculSurface (int longueur, int largeur) ;
type de paramtre
nom du paramtre
C++Livre Page 101 J eudi, 7. mai 2009 9:45 09
102 Le langage C++
Le corps de la fonction est un ensemble dinstructions inclus entre accolades, comme dans
la Figure 5.3.
Le chier source du Listing 5.1 contient un prototype dclarant une fonction dont le rle
est de calculer une surface.
Listing 5.1 : Dclaration, dnition et utilisation de la fonction Surface()
1: // Listing5.1 - Utilisation des prototypes de fonctions
2:
3: #include <iostream>
4: int Surface(int longueur, int largeur); // prototype
5:
6: int main()
7: {
8: using std::cout;
9: using std::cin;
10:
11: int longueurCour;
12: int largeurCour;
13: int surfaceCour;
14:
15: cout << "\nLargeur de votre cour? ";
16: cin >> largeurCour;
17: cout << "\nLongueur de votre cour? ";
18: cin >> longueurCour;
19:
20: surfaceCour= Surface(longueurCour,largeurCour);
21:
22: cout << "\nLa surface de votre cour est de ";
23: cout << surfaceCour;
Figure 5.3
Len-tte et le corps
dune fonction.
type du rsultat
mot cl
valeur renvoye
accolade ouvrante
nom paramtres
accolade fermante
C++Livre Page 102 J eudi, 7. mai 2009 9:45 09
Chapitre 5 Fonctions 103
24: cout << " mtres carrs\n\n";
25: return 0;
26: }
27:
28: int Surface(int longue, int large)
29: {
30: return longue * large;
31: }
Ce programme produit le rsultat suivant :
Largeur de votre cour? 10
Longueur de votre cour? 200
La surface de votre cour est de 2000 mtres carrs
Le prototype de la fonction Surface() se trouve la ligne 4. Comparez-le la dnition
de la fonction la ligne 28. Vous pouvez constater que le nom, le type de la valeur
renvoye et les types de paramtres sont identiques. Sil y avait une diffrence, le compila-
teur produirait un message derreur et ne crerait pas de chier objet. En fait, la seule
diffrence est que le prototype se termine par un point-virgule et na pas de corps.
Au niveau du prototype, les paramtres sappellent longueur et largeur et correspondent
aux paramtres longue et large de la dnition. Les premiers gurent dans le chier
titre dinformation alors que les seconds sont traits par la fonction. Il est souhaitable
(mais non obligatoire) que les noms des paramtres dans le prototype correspondent
ceux de limplmentation.
Les paramtres effectifs sont passs la fonction dans lordre exact o ils ont t dclars
et dnis, mais les noms ne sont pas pris en compte : vous auriez pass largeurCour suivi
de longueurCour, la fonction aurait utilis le premier comme longueur et la second
comme largeur.
Le corps de la fonction doit toujours tre plac entre accolades, mme sil ny a
quune seule instruction.
Excution des fonctions
Lorsque vous appelez une fonction, son excution commence avec linstruction situe
immdiatement aprs laccolade ouvrante ({). Cette excution peut bien sr contenir des
parties conditionnelles (les instructions if et associes seront prsentes plus en dtail au
In
f
o
C++Livre Page 103 J eudi, 7. mai 2009 9:45 09
104 Le langage C++
Chapitre 7). Les fonctions peuvent galement appeler dautres fonctions et sappeler elles-
mmes (on parle alors de rcursivit).
la n de lexcution dune fonction, la fonction appelante reprend la main. Pour la fonction
main(), cest le systme dexploitation qui reprend la main.
Porte des variables
Une variable a une porte, qui dtermine sa dure de vie dans le programme et les endroits
qui y ont accs. La porte des variables dclares dans un bloc se limite celui-ci ; elles ne
peuvent tre lues que dans ce bloc et disparaissent une fois quil a t excut. En revan-
che, les variables globales ont une porte globale, cest--dire quelles sont disponibles
depuis nimporte quel point du programme.
Variables locales
Non seulement on peut passer des variables une fonction, mais on peut galement dcla-
rer des variables dans le corps de celle-ci. Les variables dclares dans la fonction sont
dites "locales", car elles nexistent que dans le cadre de cette fonction. Lorsque la fonction
rend la main au programme, elles disparaissent.
Les variables locales sont des variables classiques et sont donc dnies comme toutes les
autres variables. Les paramtres passs la fonction sont galement considrs comme
des variables locales et peuvent tre utiliss comme sils avaient t dnis dans le corps
de la fonction. Le Listing 5.2 montre lutilisation des paramtres et des variables locales
dans une fonction.
Listing 5.2 : Utilisation des paramtres et des variables locales
1: #include <iostream>
2:
3: float Convert(float);
4: int main()
5: {
6: using namespace std;
7:
8: float tempFar;
9: float tempCel;
10:
11: cout << "Entrez la temprature en degrs Fahrenheit: ";
12: cin >> tempFar;
13: tempCel = Convert(tempFar);
14: cout << "\nTemprature en degrs Celsius: ";
C++Livre Page 104 J eudi, 7. mai 2009 9:45 09
Chapitre 5 Fonctions 105
15: cout << tempCel << endl;
16: return 0;
17: }
18:
19: float Convert(float tempFar)
20: {
21: float tempCel;
22: tempCel = ((tempFar - 32) * 5) / 9;
23: return tempCel;
24: }
Quelques exemples de rsultats :
Entrez la temprature en degrs Fahrenheit: 212
Temprature en degrs Celsius: 100
Entrez la temprature en degrs Fahrenheit: 32
Temprature en degrs Celsius: 0
Entrez la temprature en degrs Fahrenheit: 85
Temprature en degrs Celsius: 29.4444
Aux lignes 8 et 9, les deux variables de type float dclares doivent recevoir respective-
ment la temprature en degrs Fahrenheit et en degrs Celsius. la ligne 11, lutilisateur
entre la valeur en degrs Fahrenheit convertir et celle-ci est passe la fonction
Convert() la ligne 13.
Avec lappel Convert() ligne 13, le programme va directement la ligne 21, cest--
dire la premire ligne de la fonction de conversion. La variable locale tempCel est alors
dclare. Attention : bien quelle porte le mme nom, cette variable est diffrente de la
variable tempCel dclare la ligne 9. Le programme ne risque pas de confondre les deux
variables puisque la variable tempCel locale disparat aprs lexcution de la fonction de
conversion. Le paramtre tempFar est une copie locale de la variable passe par la fonction
main().
Le paramtre formel et la variable locale pourraient sappeler farTemp et celTemp (ou
autrement), le programme fonctionnerait de la mme faon. Vous pouvez essayer dentrer
dautres noms et recompiler le programme !
La variable locale tempCel reoit la valeur correspondant la temprature en degrs
Celsius. Il sagit dter 32 la temprature en degrs Fahrenheit, de multiplier le rsultat
par 5 puis de le diviser par 9. Cette valeur renvoye est affecte la variable tempCel dans
la fonction main() la ligne 13 et afche la ligne 15.
Le rsultat prcdent montre que ce programme a t excut trois fois. La premire fois, il
montre que 212 degrs Fahrenheit correspondent au point dbullition de leau, cest--dire
C++Livre Page 105 J eudi, 7. mai 2009 9:45 09
106 Le langage C++
100 degrs Celsius. La deuxime fois, vous pouvez constater que 32 degrs Fahrenheit
correspondent au point de gel de leau. Enn, le troisime test passe une valeur quelconque,
qui produit un rsultat fractionnaire.
Variables locales dans des blocs
Vous pouvez dnir des variables tout endroit dune fonction, pas seulement au dbut.
Lorsquelle est dnie dans un bloc dinstructions, la porte dune variable locale est limite
ce bloc. Pour illustrer ces propos, nous vous proposons le listing suivant :
Listing 5.3 : Variables visibles au niveau du bloc
1: // Listing5.3 - dmonstration de la visibilit de
2: // variables au niveau du bloc
3:
4: #include <iostream>
5:
6: void maFct();
7:
8: int main()
9: {
10: int x = 5;
11: std::cout << "\nDans main(), x vaut: " << x;
12:
13: maFct();
14:
15: std::cout << "\nDe retour dans main, x vaut: " << x;
16: return 0;
17: }
18:
19: void maFct()
20: {
21: int x = 8;
22: std::cout << "\nDans maFct, variable locale x: "
22a: << x << std::endl;
23:
24: {
25: std::cout << "\nDans le bloc de maFct, x vaut: " << x;
26:
27: int x = 9;
28:
29: std::cout << "\nVariable x trs locale: " << x;
30: }
31:
32: std::cout << "\nHors du bloc, dans maFct, x: "
32a: << x << std::endl;
33: }
C++Livre Page 106 J eudi, 7. mai 2009 9:45 09
Chapitre 5 Fonctions 107
Ce programme produit le rsultat suivant :
Dans main(), x vaut: 5
Dans maFct, variable locale x : 8
Dans le bloc de maFct, x vaut: 8
Variable x trs locale: 9
Hors du bloc, dans maFct, x: 8
De retour dans main, x vaut: 5
Ce programme commence la ligne 10 avec linitialisation dune variable locale x dans
main(). la ligne 11, sa valeur (5) safche lcran.
La ligne 13 appelle la fonction maFct(). la ligne 21, dans maFct(), une variable locale
x est initialise 8. Ce chiffre safche lcran.
Laccolade ouvrante de la ligne 24 commence un bloc dinstructions. Le contenu de la
variable x est afch. Une nouvelle variable galement appele x, mais dont la porte est
limite au bloc, est initialise 9 dans la ligne 27 ; cest cette variable qui est afche par
la ligne 30.
Le bloc local se termine la ligne 30 ; la variable locale dclare la ligne 27 devient hors
de porte est nest donc plus visible.
La valeur de la variable x afche la ligne 32 est celle qui a t dclare la ligne 21
dans maFct() ; elle est toujours gale 8. En effet, elle na pas t affecte par la dni-
tion de la ligne 27 dans le bloc, puisque les portes des deux variables x taient diffrentes.
la ligne 33, maFct() devient hors de port et sa variable locale x disparat en mme
temps. Le programme reprend la main la ligne 14. La ligne 15 reprend la valeur de la
variable x cre la ligne 10. Vous constaterez quelle na pas t modie par les autres
affectations des variables dnies dans maFct().
Bien entendu, ce programme na quune valeur dexemple et serait plus lisible si toutes les
variables locales portaient des noms distincts !
Les paramtres sont des variables locales
Les paramtres passs une fonction sont locaux cette fonction. Les modications qui
leur sont apportes naffectent pas les valeurs dans la fonction appelante. Cest ce que lon
appelle un passage par valeur, qui revient crer une copie locale de chaque paramtre
dans la fonction. Ces copies locales sont considres comme nimporte quelle autre variable
locale. Un exemple est propos dans le Listing 5.4.
C++Livre Page 107 J eudi, 7. mai 2009 9:45 09
108 Le langage C++
Listing 5.4 : Passage par valeur
1: // Listing5.4 - Dmonstration de passage PAR VALEUR
2: #include <iostream>
3:
4: using namespace std;
5: void swap(int x, int y);
6:
7: int main()
8: {
9: int x = 5, y = 10;
10:
11: cout << "Dans main(). Avant change, x: "
11a: << x << ", y: " << y << endl;
12: swap(x,y);
13: cout << "Dans main(). Aprs change, x: "
13a: << x << ", y: " << y << endl;
14: return 0;
15: }
16:
17: void swap (int x, int y)
18: {
19: int temp;
20:
21: cout << "Dans swap. Avant change, x: "
21a: << x << ", y: " << y << endl;
22:
23: temp = x;
24: x = y;
25: y = temp;
26:
27: cout << "Dans swap. Aprs change, x: "
27a: << x << ", y: " << y << endl;
28: }
Ce programme produit le rsultat suivant :
Dans main(). Avant change. x: 5, y:10
Dans swap. Avant change, x: 5, y: 10
Dans swap. Aprs change, x: 10, y: 5
Dans main(). Aprs change. x: 5, y:10
Ce programme initialise deux variables dans la fonction principale, puis les passe la
fonction swap() qui les intervertit. De retour dans la fonction main(), leur contenu na
pas t modi !
C++Livre Page 108 J eudi, 7. mai 2009 9:45 09
Chapitre 5 Fonctions 109
Linitialisation et lafchage des variables seffectuent aux lignes 9 et 11. La fonction
swap() est appele la ligne 12 et les variables lui sont passes en paramtre.
La fonction afche nouveau les variables la ligne 21. Comme lon pouvait sy attendre,
elles apparaissent dans le mme ordre que dans la fonction principale. Puis elles sont inter-
verties aux lignes 23 25, ce qui est conrm par lafchage de la ligne 27. Dans la fonction
swap(), les deux valeurs sont donc bien changes.
Le programme revient ensuite la ligne 13. Dans la fonction main(), les valeurs nont pas
t changes.
Comme vous lavez devin, les paramtres sont passs par valeur la fonction appele, ce
qui signie quil sagit de copies locales la fonction. Ce sont donc ces copies qui ont t
changes aux lignes 23 25, pas les originales.
Aux Chapitres 8 et 10, vous verrez quil existe dautres solutions que celle du passage par
valeur, qui permettent de modier les valeurs prsentes dans main().
Variables globales
Les variables dnies en dehors de toute fonction ont une porte globale et sont donc
disponibles partir de nimporte quelle fonction du programme, y compris main().
Une variable locale peut porter le mme nom quune variable globale : la modication de
son contenu ne modiera pas la variable globale, mais la variable locale masquera la
variable globale (voir le Listing 5.5). Lorsquune fonction possde une variable locale de
mme nom quune variable globale, ce nom fait rfrence la variable locale lorsquelle
est utilise dans la fonction.
Listing 5.5 : Utilisation de variables locales et de variables globales
1: #include <iostream>
2: void maFonction(); // prototype
3:
4: int x = 5, y = 7; // variables globales
5: int main()
6: {
7: using namespace std;
8:
9: cout << "x dans main: " << x << endl;
10: cout << "y dans main: " << y << endl << endl;
11: maFunction();
12: cout << "De retour de maFonction! << endl << endl;
13: cout << "x dans main: " << x << endl;
14: cout << "y dans main: " << y << endl;
15: return 0;
C++Livre Page 109 J eudi, 7. mai 2009 9:45 09
110 Le langage C++
16: }
17:
18: void maFonction()
19: {
20: using std::cout;
21:
22: int y = 10;
23:
24: cout << "x dans maFonction: " << x << endl;
25: cout << "y dans maFonction: " << y << endl << endl;
26: }
Ce programme produit le rsultat suivant :
x dans main: 5
y dans main: 7
x dans maFonction: 5
y dans maFonction: 10
De retour de maFonction!
x dans main: 5
y dans main: 7
Ce programme simple met en vidence la confusion que peuvent causer variables locales
et variables globales. La ligne 4 dclare et initialise les deux variables globales x et y avec
les valeur 5 et 7.
Ces valeurs safchent alors que le programme est encore dans la fonction main(). Vous
pouvez constater que la fonction principale ne dnit pas les variables puisque x et y existent
au niveau global.
La ligne 11 appelle la fonction maFonction(). Le programme saute directement la
ligne 18 et lit la premire instruction de cette fonction. La variable locale y est initialise
10. Elle se substitue la variable globale de mme nom. la ligne 25, lorsque la variable
y est afche, cest donc le contenu de la variable locale qui apparat et qui masque la
variable globale de mme nom.
Lorsque la fonction prend n, le programme redonne la main la fonction principale et
tient de nouveau compte des variables globales x et y. Vous pouvez remarquer que la
variable globale y na pas t affecte par la modication de la variable y locale
maFonction().
C++Livre Page 110 J eudi, 7. mai 2009 9:45 09
Chapitre 5 Fonctions 111
Variables globales : quelques conseils
Bien que les variables globales soient autorises par le langage, on ne les utilise quasiment
jamais en C++. En C, elles sont dangereuses mais ncessaires lorsque lon souhaite parta-
ger des donnes entre plusieurs fonctions sans les passer constamment en paramtre,
notamment lorsquil sagit simplement de les transmettre dune fonction lautre.
Les variables globales sont dangereuses, car ce sont des donnes partages et parce quune
fonction peut modier une variable globale sans que les autres fonctions ne le voient, ce
qui peut produire des bogues trs difciles dtecter.
En C++, il est possible de remplacer les variables globales par des variables membres
statiques. Pour en savoir plus, reportez-vous au Chapitre 15.
Instructions des fonctions
En thorie, les instructions dans le corps dune fonction ne sont limites ni en nombre ni
en type. Bien que vous nayez pas le droit de dnir une fonction lintrieur dune fonc-
tion, vous pouvez appeler une fonction, ce qui est courant dans la fonction principale
main(). Les fonctions peuvent sappeler elles-mmes, ce que nous verrons bientt dans la
section consacre la rcursivit.
Bien que sa taille ne soit pas limite, une fonction C++ bien conue est de taille raisonna-
ble. Certains programmeurs limitent la taille de chaque fonction un cran, ce qui leur
permet de visualiser toutes les instructions simultanment. Cette rgle nest pas stricte,
mais il est vrai quune fonction courte est plus facile comprendre et mettre jour.
Une fonction ne devrait tre consacre qu une seule tche simple et facile comprendre.
Une fonction longue et complexe peut toujours se dcomposer en fonctions courtes et
simples.
Retour sur les paramtres des fonctions
Toute expression valide de C++ peut tre un paramtre de fonction, y compris les constan-
tes, les expressions mathmatiques et logiques et les autres fonctions qui renvoient une
valeur. Le point important est que le type de la valeur renvoye par lexpression doit
correspondre au type du paramtre attendu par la fonction.
Il est mme possible dutiliser la valeur renvoye par une fonction comme paramtre
dune autre fonction. Toutefois, cela peut rendre le code correspondant difcile lire et
dboguer.
C++Livre Page 111 J eudi, 7. mai 2009 9:45 09
112 Le langage C++
Prenons les fonctions monDouble(), triple(), carre() et cube(), qui renvoient
chacune une valeur. Vous pourriez crire lexpression suivante :
resultat = (monDouble(triple(carre(cube(maValeur)))));
Vous pouvez aborder cette instruction de deux manires. Dune part, la fonction monDou-
ble() prend la fonction triple() comme paramtre. son tour, triple() prend la fonc-
tion carre(), qui prend la fonction cube() comme paramtre. La fonction cube() prend
la variable maValeur comme paramtre.
Si lon part dans lautre sens, cette instruction prend la variable maValeur et la passe en
paramtre la fonction cube(), la valeur renvoye est passe en paramtre la fonction
carre(), et ainsi de suite. La valeur obtenue la n est copie dans resultat.
Vous remarquerez quil est assez difcile de dterminer les priorits dexcution de cette
expression.
Pour mieux comprendre ce traitement et rendre le code plus lisible, il est donc prfrable
de crer une variable intermdiaire recevant le rsultat de chaque appel de fonction :
unsigned long maValeur = 2;
unsigned long monCube = cube(maValeur); // monCube = 8
unsigned long monCarre = carre(monCube); // monCarre = 64
unsigned long monTriple = triple(monCarre); // monTriple = 192
unsigned long resultat = monDouble(monTriple); // Resultat = 384
Le programme est plus explicite !
Bien que C++ facilite lcriture dun code compact (voir lexemple prcdent)
pour combiner les fonctions cube(), carre(), triple() et monDouble(),
cette possibilit nest pas une obligation. Il vaut mieux que le code soit simple
lire, donc entretenir, que compact.
Retour sur les valeurs renvoyes
Les fonctions renvoient une valeur ou ne renvoient rien, auquel cas le type du rsultat est
void.
Pour renvoyer une valeur, il suft dentrer le mot-cl return suivi de la valeur rcuprer.
Cette valeur peut tre, elle-mme, une expression qui produit une valeur :
return 5;
return (x > 5);
return (Fonction());
A
tte
n
tio
n
C++Livre Page 112 J eudi, 7. mai 2009 9:45 09
Chapitre 5 Fonctions 113
Ces trois instructions return sont valides, condition que Fonction() renvoie une
valeur. La valeur de la deuxime instruction return (x > 5) est gale true ou false
selon que x est suprieur 5 ou infrieur ou gal 5. La valeur renvoye nest pas gale
x, mais au rsultat du test.
Lorsque le mot-cl return est dtect, lexpression situe immdiatement aprs est
renvoye comme valeur de la fonction. Le code qui a appel la fonction reprend alors la
main et les instructions qui sont ventuellement place aprs le mot-cl return ne sont
pas prises en compte.
Une fonction peut contenir une ou plusieurs instructions return, comme le montre le
Listing 5.6.
Listing 5.6 : Utilisation de plusieurs instructions return
1: // Listing5.6 - Plusieurs instructions return
2: // Dmonstration
3: #include <iostream>
4:
5: int Doubleur(int montantADoubler);
6:
7: int main()
8: {
9: using std::cout;
10:
11: int resultat = 0;
12: int entree;
13:
14: cout <<
14a: "Entrez un nombre entre 0 et 10000 multiplier par 2: ";
15: std::cin >> entree;
16:
17: cout << "\nAvant dappeler la fonction Doubleur()... ";
18: cout << "\nValeur entre: " << entree <<
18a: ", Valeur double: " << resultat << "\n";
19:
20: resultat = Doubleur(entree);
21:
22: cout << "\nDe retour de la fonction Doubleur()...\n";
23: cout << "\nValeur entre: " << entree <<
23a: ", Valeur double: " << resultat << "\n";
24:
25: return 0;
26: }
C++Livre Page 113 J eudi, 7. mai 2009 9:45 09
114 Le langage C++
27:
28: int Doubleur(int original)
29: {
30: if (original <= 10000)
31: return original * 2;
32: else
33: return -1;
34: std::cout << "Vous natteindrez jamais cette ligne!\n";
35: }
Ce programme produit le rsultat suivant :
Entrez un nombre entre 0 et 10000 multiplier par 2: 9000
Avant dappeler la fonction Doubleur()...
Valeur entre: 9000, Valeur double: 0
De retour de la fonction Doubleur() ...
Valeur entre: 9000, Valeur double: 18000
Entrez un nombre entre 0 et 10000 multiplier par 2: 11000
Avant dappeler la fonction Doubleur()...
Valeur entre: 11000, Valeur double: 0
De retour de la fonction Doubleur()...
Valeur entre: 11000, Valeur double: -1
Vous tes invit entrer un nombre (lignes 14 et 15) dans une variable locale que lon af-
che ensuite, accompagne de la valeur courante de resultat (lignes 17 et 18). La ligne 20
appelle la fonction Doubleur() en lui passant en paramtre la valeur saisie. Le rsultat
renvoy par la fonction est copi dans la variable locale resultat et les valeurs sont de
nouveau afches au niveau de la ligne 23.
Dans la fonction Doubleur() (ligne 30), le programme dtermine si le paramtre est inf-
rieur ou gal 10 000. Si cest le cas, le nombre dorigine est renvoy aprs avoir t
doubl. Dans le cas contraire, la valeur renvoye -1 indique une erreur.
Lexcution de la fonction natteint jamais la ligne 34 car elle sarrte la ligne 31 ou la
ligne 33, quelle que soit la valeur saisie. Un bon compilateur dtectera lerreur et mettra
un message davertissement et un bon dveloppeur supprimera cette ligne inutile !
C++Livre Page 114 J eudi, 7. mai 2009 9:45 09
Chapitre 5 Fonctions 115
Quelle est la diffrence entre int main() et void main(); ? Quelle forme faut-il utiliser ?
Jai utilis les deux avec succs, pourquoi faut-il crire : int main(){ return 0;} ?
Les deux fonctionnent sur la plupart des compilateurs, mais seul int main() est compatible
ANSI, il est donc assur de continuer fonctionner.
La diffrence est la suivante : int main() renvoie une valeur au systme dexploitation.
Lorsque votre programme se termine, cette valeur peut tre teste dans un script, par
exemple.
Dans ce livre, nous nutilisons pas la valeur renvoye, mais le standard ANSI exige que
main() en renvoie une.
Paramtres par dfaut
chaque paramtre dclar dans un prototype ou dans une dnition correspond une
valeur que le programme doit passer la fonction. Bien entendu, le type de valeur doit tre
identique celui qui a t dclar. Exemple :
long maFonction(int);
Ici, la fonction attend une variable entire. Si la dnition ne correspond pas ou si le type
est diffrent, le compilateur produit une erreur et ne cre pas de chier objet.
Les prototypes qui dclarent une valeur par dfaut pour le paramtre font exception. Une
valeur par dfaut est une valeur utilise lorsque aucune autre nest fournie. La dclaration
gurant plus haut pourrait tre rcrite ainsi :
long maFonction (int x = 50);
Ce prototype signie que la fonction renvoie un entier long et quel attend un paramtre
entier. Si aucun paramtre ne lui est transmis lors de lappel, elle utilisera 50 comme
valeur par dfaut. Rappelons que les noms de paramtres ntant pas obligatoires dans les
prototypes de fonctions, on pourrait galement crire :
long maFonction (int = 50);
La dnition de la fonction nest pas modie par la prsence dun paramtre par dfaut et
son en-tte se prsenterait donc de la faon suivante :
long maFonction (int x)
Si la fonction appelante ne fournit pas de valeur lappel de maFonction(), le nombre 50
sera copi dans le paramtre x. Il nest pas ncessaire de donner le mme nom au paramtre
C++Livre Page 115 J eudi, 7. mai 2009 9:45 09
116 Le langage C++
par dfaut dans le prototype et dans len-tte de fonction, car la valeur par dfaut est affec-
te en fonction de sa position, pas de son nom.
Vous pouvez fournir des valeurs par dfaut selon vos besoins. La seule rgle que vous
devez respecter est que si un paramtre na pas de valeur par dfaut, aucun des paramtres
qui le prcdent ne peut en avoir (en dautres termes, les paramtres avec des valeurs par
dfaut doivent apparatre la n de la liste).
Si le prototype de la fonction est de la forme :
long maFonction (int Param1, int Param2, int Param3);
Param2 ne peut avoir une valeur par dfaut que si Param3 en a galement une. De mme,
Param1 ne peut avoir une valeur par dfaut que si vous en avez affect une aux deux
autres. Le Listing 5.7 illustre cette rgle.
Listing 5.7 : Valeurs par dfaut des paramtres
1: // Listing5.7 - Utilisation des
2: // valeurs de paramtres par dfaut
3: #include <iostream>
4:
5: int VolumeCube
5a: (int longueur, int largeur = 25, int hauteur = 1);
6:
7: int main()
8: {
9: int longueur = 100;
10: int largeur = 50;
11: int hauteur = 2;
12: int volume;
13:
14: volume = VolumeCube(longueur, largeur, hauteur);
15: std::cout << "Le premier volume est gal : "
15a: << volume << "\n";
16:
17: volume = VolumeCube(longueur, largeur);
18: std::cout << "Le deuxime volume est gal a: "
18a: << volume << "\n";
19:
20: volume = VolumeCube(longueur);
21: std::cout << "Le troisime volume est gal : "
21a: << volume << "\n";
22: return 0;
23: }
24:
25: int VolumeCube(int longueur, int largeur, int hauteur)
C++Livre Page 116 J eudi, 7. mai 2009 9:45 09
Chapitre 5 Fonctions 117
26: {
27:
28: return (longueur * largeur * hauteur);
29: }
Ce programme produit le rsultat suivant :
Le premire volume est gal : 10000
Le deuxime volume est gal : 5000
Le troisime volume est gal: 2500
la ligne 5, le prototype VolumeCube() indique que la fonction de mme nom attend trois
paramtres entiers, dont les deux derniers ont une valeur par dfaut.
La fonction calcule le volume du cube dont on lui a pass les dimensions. Si on ne lui
fournit pas de largeur explicite, elle utilise une largeur de 25 et une hauteur de 1. Il est
impossible de passer la hauteur sans passer aussi une largeur.
Aux lignes 9, 10 et 11, on initialise les variables longueur, largeur et hauteur, qui sont
ensuite passes la fonction VolumeCube() en ligne 14. Le rsultat de ce calcul est afch
la ligne 15.
la ligne 17, le programme reprend la main et rappelle la fonction. Cette fois-ci, aucune
valeur nest fournie pour la hauteur et cest donc sa valeur par dfaut qui est prise en
compte. Le calcul seffectue et le rsultat est de nouveau afch.
Lexcution se poursuit la ligne 20, cette fois-ci en ne fournissant ni la largeur ni la
hauteur. Les valeurs par dfaut correspondantes sont donc utilises et lon afche le rsultat
obtenu.
Surcharge de fonctions
C++ permet de crer plusieurs fonctions portant le mme nom. Cette fonctionnalit est
appele surcharge de fonction. Une fonction surcharge doit tre distincte au niveau de la
Faire Ne pas faire
Considrer les paramtres comme des variables
locales dont la porte se limite la fonction
appele.
Se souvenir que la modication dune varia-
ble globale dans une fonction est rpercute
dans toutes les fonctions.
Crer une valeur par dfaut pour un paramtre
sil nen existe pas pour le paramtre suivant.
Oublier quun paramtre pass par valeur ne
modie pas la variable correspondante dans la
fonction appelante.
C++Livre Page 117 J eudi, 7. mai 2009 9:45 09
118 Le langage C++
liste de ses paramtres, qui doit diffrer par le type et/ou de le nombre des paramtres.
Exemple :
int Fonction (int, int);
int Fonction (long, long);
int Fonction (long);
La fonction Fonction() est surcharge avec trois listes de paramtres. Dans les deux
premires versions, les paramtres changent de type. Dans la dernire, la liste des paramtres
ne contient pas le mme nombre de paramtres.
Le type des valeurs renvoyes par les fonctions surcharges peut tre identique ou non.
Lorsque deux fonctions renvoient des valeurs de types diffrents alors que leur
nom et leur liste de paramtres sont identiques, on obtient une erreur de compi-
lation. Pour modier le type du rsultat, vous devez galement modier la
signature (nom et/ou liste de paramtres).
La surcharge de fonctions est aussi appele polymorphisme de fonction. Poly signie
plusieurs et morphe signie forme : une fonction polymorphe pourra donc prendre plusieurs
formes.
Le polymorphisme de fonction reprsente la capacit de "surcharger" une fonction avec
plusieurs signications. Comme nous lavons vu, il est possible de modier le nombre et
le type des paramtres en surchargeant une fonction. Entre deux fonctions surcharges, le
compilateur dterminera celle qui doit tre appele en fonction du type et/ou du nombre
des paramtres utiliss. Ainsi, grce la surcharge, vous pouvez crer la fonction
Moyenne() capable de calculer tour tour la moyenne de valeurs entires, la moyenne de
valeurs fractionnaires, etc., ce qui vous vite de devoir dnir les fonctions MoyenInt(),
MoyenFloat(), etc.
Supposons que vous dnissiez une fonction qui multiplie par 2 la valeur saisie. Il sera
souhaitable quelle puisse calculer des valeurs de tout type (int, float ou double). Si
vous nutilisez pas la surcharge de fonction, vous devrez crer quatre fonctions distinctes :
int DoubleInt(int);
long DoubleLong(long);
float DoubleFloat(float);
double DoubleDouble(double);
Grce la surcharge, il sufra de dclarer la mme fonction, de la faon suivante :
int Double(int);
long Double(long);
In
f
o
C++Livre Page 118 J eudi, 7. mai 2009 9:45 09
Chapitre 5 Fonctions 119
float Double(float);
double Double(double);
ce qui est bien plus facile lire et utiliser. Vous navez plus besoin de savoir quelle fonc-
tion appeler : il sufra de passer une valeur en paramtre et le programme appellera auto-
matiquement la bonne fonction, comme le montre le Listing 5.8.
Listing 5.8 : Exemple de fonction polymorphe
1: // Listing5.8 - Exemple de
2: // fonction polymorphe
3: #include <iostream>
4:
5: int Double(int);
6: long Double(long);
7: float Double(float);
8: double Double(double);
9:
10: using namespace std;
11:
12: int main()
13: {
14: int monInt = 6500;
15: long monLong = 65000;
16: float monFloat = 6.5F;
17: double monDouble = 6.5e20;
18:
19: int doubleInt;
20: long doubleLong;
21: float doubleFloat;
22: double doubleDouble;
23:
24: cout << "monInt: " << monInt << "\n";
25: cout << "monLong: " << monLong << "\n";
26: cout << "monFloat: " << monFloat << "\n";
27: cout << "monDouble: " << monDouble << "\n";
28:
29: doubleInt = Double(monInt);
30: doubleLong = Double(monLong);
31: doubleFloat = Double(monFloat);
32: doubleDouble = Double(monDouble);
33:
34: cout << "doubleInt: " << doubleInt << "\n";
35: cout << "doubleLong: " << doubleLong << "\n";
36: cout << "doubleFloat: " << doubleFloat << "\n";
37: cout << "doubleDouble: " << doubleDouble << "\n";
C++Livre Page 119 J eudi, 7. mai 2009 9:45 09
120 Le langage C++
38:
39: return 0;
40: }
41:
42: int Double(int original)
43: {
44: cout << "Dans Double(int)\n";
45: return 2 * original;
46: }
47:
48: long Double(long original)
49: {
50: cout << "Dans Double(long)\n";
51: return 2 * original;
52: }
53:
54: float Double(float original)
55: {
56: cout << "Dans Double(float)\n";
57: return 2 * original;
58: }
59:
60: double Double(double original)
61: {
62: cout << "Dans Double(double)\n";
63: return 2 * original;
64: }
Ce programme produit le rsultat suivant :
monInt: 6500
monLong: 65000
monFloat: 6.5
monDouble: 6.5e+20
Dans Double(int)
Dans Double(long)
Dans Double(float)
Dans Double(double)
DoubleInt: 13000
DoubleLong: 130000
DoubleFloat: 13
DoubleDouble: 1.3e+21
La fonction Double() est surcharge avec int, long, float et double. Les prototypes
gurent de la ligne 5 la ligne 8, les dnitions de la ligne 42 la ligne 64.
C++Livre Page 120 J eudi, 7. mai 2009 9:45 09
Chapitre 5 Fonctions 121
Notez que, dans cet exemple, linstruction using namespace std; a t ajoute la
ligne 10, hors de toute fonction spcique. Linstruction est donc globale au chier et
lespace de nom est utilis dans toutes les fonctions dclares.
Le corps du programme contient huit variables locales. De la ligne 14 la ligne 17, quatre
valeurs sont initialises alors que les quatre autres reoivent le rsultat de la fonction
Double() (lignes 29 32). Lorsque la fonction est appele, cest le paramtre qui est
pass qui permet au programme de dterminer la bonne version.
Pour cela, le compilateur examine les paramtre, puis appelle lune des quatre fonctions
Double() surcharges. Le rsultat afch montre quil ne sest pas tromp !
Pour en savoir plus sur les fonctions
Les fonctions tant un aspect crucial de la programmation, certains points particuliers
mritent dtre connus pour rsoudre des problmes inhabituels. Utilises correctement,
les fonctions en ligne permettent dextraire la dernire goutte de performances dun
programme. La rcursivit, quant elle, fait partie de ces merveilles sotriques de
la programmation et permet de rsoudre aisment des problmes qui pourraient pas tre
traits aussi simplement sans elle.
Fonctions en ligne
Lorsque lon dnit une fonction, le compilateur produit gnralement un seul ensemble
dinstructions en mmoire. Lorsque cette fonction est appele, le programme saute direc-
tement au dbut de ces instructions et, quand la fonction se termine, il revient la ligne qui
suit immdiatement lappel. Si vous invoquez dix fois la fonction, le programme excutera
autant de fois les mmes instructions, mais il nexistera en fait quune seule copie de la
fonction dans le code (et non 10).
Ces sauts et ces retours ont un petit impact sur les performances. Certaines fonctions sont
trs courtes puisquelles nont quune ou deux lignes de code : il serait alors plus efcace
pour le programme dviter de se brancher un autre emplacement mmoire uniquement
pour excuter une ou deux instructions. En programmation, efcacit rime avec rapidit :
un programme sexcute plus vite sil y a moins dappels de fonctions.
C++ propose une solution. Lorsquune fonction est dclare avec le mot-cl inline, le
compilateur ne cre pas une vritable fonction et se contente de remplacer lappel de cette
fonction par son code dans la fonction appelante. Il ny a donc plus de saut en mmoire
lors de lappel : cest exactement comme si vous aviez directement crit les instructions de
la fonction dans la fonction appelante.
C++Livre Page 121 J eudi, 7. mai 2009 9:45 09
122 Le langage C++
Cela dit, les fonctions en ligne peuvent coter cher. Si la fonction est appele dix fois, le
code inline sera copi autant de fois dans les fonctions appelantes. Cet accroissement de
la taille du chier excutable peut compltement occulter le gain minime de rapidit
obtenu et, en fait, le programme peut parfois sen trouver ralenti !
De nos jours, les compilateurs peuvent quasiment toujours faire un meilleur travail que
vous pour prendre cette dcision lorsquelle simpose et il est gnralement prfrable de
ne pas dclarer une fonction inline, sauf si elle ne comporte quune ou deux instructions.
Dans le doute, ne le faites pas.
Loptimisation des performances est une opration difcile et la plupart des
programmeurs ont du mal identier les problmes de leurs programmes sans
aide, ce qui implique des programmes spcialiss, comme les dbogueurs et les
proleurs.
De mme, il vaut toujours mieux crire du code clair et comprhensible plutt
que supposer quil sera lent ou rapide, quitte crire un code difcile
comprendre. En effet, il est toujours plus facile dacclrer un code clair.
Listing 5.9 : Exemple de fonction en ligne
1: // Listing5.9 - Exemple de fonction en ligne
2: #include <iostream>
3:
4: inline int Double(int);
5:
6: int main()
7: {
8: int cible;
9: using std::cout;
10: using std::cin;
11: using std::endl;
12:
13: cout << "Entrez un nombre: ";
14: cin >> cible;
15: cout << "\n";
16:
17: cible = Double(cible);
18: cout << "Valeur cible: " << cible << endl;
19:
20: cible = Double(target);
21: cout << "Valeur cible: " << cible << endl;
22:
23: cible = Double(target);
24: cout << "Valeur cible: " << cible << endl;
In
f
o
C++Livre Page 122 J eudi, 7. mai 2009 9:45 09
Chapitre 5 Fonctions 123
25: return 0;
26: }
27:
28: int Double(int cible)
29: {
30: return 2*cible;
31: }
Ce programme produit le rsultat suivant :
Entrez un nombre: 20
Valeur cible: 40
Valeur cible: 80
Valeur cible: 160
La ligne 4 dclare la fonction Double() inline avec un paramtre et type de rsultat qui
sont tous deux des int. Cette dclaration ressemble nimporte quel autre prototype, sauf
que le mot-cl inline prcde le type du rsultat.
La compilation revient produire le code suivant :
cible = 2 * cible;
chaque fois que le programme va rencontrer :
cible = Double(cible);
Au moment de lexcution, les instructions de la fonction ont t intgres dans le code,
compiles dans le chier .obj. Cela vite un branchement et un retour au cours de
lexcution du programme, au prix dun chier excutable plus volumineux.
inline est un mot-cl indiquant simplement au compilateur que vous voulez
que la fonction soit en ligne. Le compilateur peut ne pas en tenir compte et
raliser un appel normal.
Rcursivit
Une fonction peut sappeler elle-mme. Cette fonctionnalit sappelle la rcursivit. Elle
peut tre directe ou indirecte. Elle est directe lorsque la fonction sappelle elle-mme et
indirecte lorsquune fonction appelle une autre fonction qui appelle son tour la premire.
Certains problmes se rsolvent plus simplement laide de la rcursivit : il sagit gn-
ralement de ceux o lon traite des donnes pour obtenir un rsultat et que lon applique le
mme traitement ce rsultat. Quelle soit directe ou indirecte, la rcursivit peut se terminer
et produire un rsultat ou ne jamais nir et provoquer une erreur dexcution.
In
f
o
C++Livre Page 123 J eudi, 7. mai 2009 9:45 09
124 Le langage C++
Lorsquune fonction sappelle elle-mme, il faut bien comprendre que cest une nouvelle
copie de la fonction qui sexcute. Les variables locales de cette copie sont totalement
indpendantes de celles de la premire et elles ne peuvent pas les modier directement,
exactement comme les variables locales de main() ne peuvent pas affecter les variables
locales des fonctions quelle appelle, comme on la montr dans le Listing 5.3.
Nous allons utiliser la rcursivit pour traiter la suite de Fibonacci :
1,1,2,3,5,8,13,21,34...
Aprs le deuxime, chaque nombre est la somme des deux prcdents. Un problme de
Fibonacci consiste, par exemple, trouver le 12
e
nombre de la suite.
Pour rsoudre ce problme, il faut soigneusement tudier la suite. Les deux premires
valeurs sont gales 1. Chacun des nombres qui suit est la somme des deux prcdents.
Le septime nombre, par exemple, correspond la somme du sixime et du cinquime.
Plus gnralement, le nime nombre est la somme de n-2 et de n-1 pour n > 2.
Toute fonction rcursive doit inclure une condition darrt. En son absence, le programme
ne se terminerait jamais. Dans notre exemple, la condition darrt correspond n < 3
(cest--dire que lorsque n est infrieur 3, nous pouvons arrter le traitement).
Lalgorithme du programme se prsente ainsi :
1. Entrer la position trouver dans la suite.
2. Appeler la fonction fib(), en transmettant la valeur saisie.
3. La fonction fib() teste son paramtre (n). Si n < 3, elle renvoie 1 ; dans le cas
contraire, elle sappelle elle-mme deux fois, en passant la valeur n-2 puis la valeur n-1.
Enn, elle renvoie la somme des rsultats des deux appels la fonction principale.
Si vous appelez fib(1) ou fib(2), la fonction renvoie 1 et sarrte. Si vous appelez
fib(3), elle retournera la somme des valeurs calcules lors de lappel fib(2) et de
lappel fib(1), cest--dire 2 (1+1).
De mme, lappel fib(4) renvoie la somme de lappel de fib(3) et de fib(2). Nous
venons de voir que fib(3) renvoyait 2 (en appellant fib(2) et fib(1)) et que fib(2)
renvoyait 1. Par consquent, fib(4) additionne ces deux nombres et renvoie 3, qui est le
quatrime nombre de la suite.
Avec une tape de plus, fib(5) renvoie la somme de fib(4) et fib(3), soit 3 + 2, cest--
dire 5.
Cette mthode nest pas la plus efcace pour rsoudre le problme (lappel fib(20)
appellera 13 529 fois la fonction fib() avant de renvoyer le rsultat !), mais elle fonc-
tionne. Faites attention : si vous utilisez un paramtre trop grand, vous tomberez cours de
mmoire car, chaque appel, la mmoire est sollicite et nest libre quaprs la n du
C++Livre Page 124 J eudi, 7. mai 2009 9:45 09
Chapitre 5 Fonctions 125
premier appel. Elle risque donc dtre rapidement sature. Cette fonction est implmente
dans le Listing 5.10.
Dans le programme ci-aprs, vitez de choisir une valeur suprieure 15, car
la mmoire de votre PC risque ne pas tre sufsante !
Listing 5.10 : La rcursivit applique la suite de Fibonacci
1: // Exemple de rcursivit
2: #include <iostream>
3: int fib (int n);
4:
5: int main()
6: {
7:
8: int n, reponse;
9: std::cout << "Entrez le nombre a trouver: ";
10: std::cin >> n;
11:
12: std::cout << "\n\n";
13:
14: reponse = fib(n);
15:
16: std::cout << reponse << " est le " << n;
17: std::cout << "e nombre dans la suite\n";
18: return 0;
19: }
20:
21: int fib (int n)
22: {
23: std::cout << "Traitement fib(" << n << ")... ";
24:
25: if (n < 3 )
26: {
27: std::cout << "Renvoie 1!\n";
28: return (1);
29: }
30: else
31: {
32: std::cout << "Appel de fib(" << n-2 << ") ";
33: std::cout << "et fib(" << n-1 << ").\n";
34: return( fib(n-2)+ fib(n-1));
35: }
36: }
A
tte
n
tio
n
C++Livre Page 125 J eudi, 7. mai 2009 9:45 09
126 Le langage C++
Ce programme produit le rsultat suivant :
Entrez le rang a trouver: 6
Traitement fib(6)... Appel de fib(4) et fib(5).
Traitement fib(4)... Appel de fib(2) et fib(3).
Traitement fib(2)... Renvoie 1!
Traitement fib(3)... Appel de fib(1) et fib(2).
Traitement fib(1)... Renvoie 1!
Traitement fib(2)... Renvoie 1!
Traitement fib(5)... Appel de fib(3) et fib(4).
Traitement fib(3)... Appel de fib(1) et fib(2).
Traitement fib(1)... Renvoie 1!
Traitement fib(2)... Renvoie 1!
Traitement fib(4)... Appel de fib(2) et fib(3).
Traitement fib(2)... Renvoie 1!
Traitement fib(3)... Appel de fib(1) et fib(2).
Traitement fib(1)... Renvoie 1!
Traitement fib(2)... Renvoie 1!
8 est le 6e nombre dans la suite
Certains compilateurs ont des difcults avec lusage des oprateurs dans une
instruction cout. Si vous recevez un avertissement la ligne 32, mettez des
parenthses autour de la soustraction pour que les lignes 32 et 33 deviennent :
std::cout << "Appel de fib(" << (n-2) << ") ";
std::cout << "et fib(" << (n-1) << ").\n";
la ligne 9, lutilisateur entre un nombre qui est copi dans n. Cette valeur est transmise
fib(). Le programme se branche sur la fonction fib(), comme vous pouvez le vrier
lcran (ligne 23).
la ligne 25, le programme vrie si largument n est infrieur 3. Si tel est le cas, la
fonction fib() renvoie la valeur 1. Dans le cas contraire, elle renvoie la somme des
valeurs aprs avoir trait n-2 et n-1.
Ces valeurs ne peuvent pas tre renvoyes avant la n de lappel fib(). Vous pouvez
donc imaginez que le programme appelle fib de faon rptitive jusqu ce quun de ces
appels renvoie une valeur. Les seuls qui renvoient une valeur sont les appels fib(2) et
fib(1). Ces valeurs sont ensuite passes aux appelants en attente qui, leur tour, ajoutent
la valeur renvoye la leur propre, puis la retournent. Les Figures 5.4 et 5.5 illustrent ces
appels rcursifs fib().
In
f
o
C++Livre Page 126 J eudi, 7. mai 2009 9:45 09
Chapitre 5 Fonctions 127
Dans cet exemple, n est gal 6 et la fonction fib(6) est appele depuis la fonction prin-
cipale. la ligne 25, le programme vrie si n < 3. Le test choue et la fonction fib(6)
renvoie la ligne 34 la somme des valeurs renvoyes par fib (4) et fib(5). Voici la
ligne 34 :
return( fib(n-2)+ fib(n-1));
Un appel est fait fib(4) depuis cette instruction de retour (comme n == 6, fib(n-2) est
la mme chose que fib(4)) et un autre appel est fait fib(5) (fib(n-1)), puis la fonc-
tion dans laquelle vous vous trouvez (fib(6)) attend que ces appels renvoient une valeur.
ce moment, la fonction peut renvoyer le rsultat de laddition de ces deux valeurs.
Figure 5.4
Utilisation
de la rcursivit.
Figure 5.5
Retour aprs
les appels rcursifs.
int main()
{
int x = fib(6);
}
return fib(4) + fib(5) return fib(3) + fib(4)
fib(6) fib(5)
return fib(2) + fib(3)
fib(4)
return fib(1) + fib(2)
fib(3)
return fib(2) + fib(3)
fib(4)
return 1
fib(2)
return 1
fib(1)
return 1
fib(1)
return 1
fib(2)
return 1
fib(2)
return fib(1) + fib(2)
fib(3)
return 1
fib(2)
return fib(1) + fib(2)
fib(3)
return 1
fib(1)
return 1
fib(2)
int main()
{
int x = fib(6);
}
return fib(4) + fib(5) return fib(3) + fib(4)
fib(6) fib(5)
return fib(2) + fib(3)
fib(4)
return fib(1) + fib(2)
fib(3)
return fib(2) + fib(3)
fib(4)
return 1
fib(2)
return 1
fib(1)
return 1
fib(1)
return 1
fib(2)
return 1
fib(2)
return fib(1) + fib(2)
fib(3)
return 1
fib(2)
return fib(1) + fib(2)
fib(3)
return 1
fib(1)
return 1
fib(2)
8 5
3
2 2
2 3
1
1 1
1
1
1 1
1
C++Livre Page 127 J eudi, 7. mai 2009 9:45 09
128 Le langage C++
Puisque fib(5) passe un argument qui nest pas infrieur 3, fib() sera de nouveau
appele, cette fois avec 4 et 3. fib(4) son tour, appellera fib(3) et fib(2).
Grce lafchage des valeurs, vous pouvez suivre le droulement du programme. votre
tour de tester le code. Lorsque le chier excutable est prt, entrez 1, puis 2, puis 3 et allez
jusqu 6 en tudiant soigneusement ce qui safche.
Le moment est tout fait propice au test de votre dbogueur. Placez un point darrt la
ligne 21, puis faites un pas pas dtaill (ou step into) dans chaque appel fib, en
gardant la trace de la valeur de n chaque appel rcursif fib.
La rcursivit nest pas trs utilise en programmation C++. Toutefois, elle savre trs
efcace pour rsoudre certains problmes.
La rcursivit est un domaine difcile matriser. Nous lavons prsente
parce quil fallait que vous connaissiez ce concept. Toutefois, ne vous inquitez
pas si certains dtails vous ont chapp.
Principe des fonctions
Lors de lappel dune fonction, le programme se branche directement dans le code corres-
pondant, les paramtres sont passs et le corps de la fonction sexcute. Une fois termine,
la fonction renvoie une valeur (sauf si vous avez indiqu le mot-cl void) et rend la main
la fonction appelante.
Comment cela est-il possible ? Comment le code sait-il o se brancher ? O sont stockes
les variables qui sont transmises la fonction ? Quarrive-t-il aux variables qui sont d-
nies dans la fonction ? Comment la valeur renvoye est-elle retransmise au programme ?
Comment le programme sait-il o il doit reprendre ?
La plupart des manuels dinitiation la programmation nabordent pas ces questions et le
dveloppement garde ses secrets. Pour comprendre comment cela se passe, immergeons-
nous dans la mmoire de lordinateur.
Les niveaux dabstraction de la programmation
Au dbut de sa carrire, un programmeur manque de mthodologie et gre difcilement
les niveaux dabstraction intellectuelle du dveloppement. Bien entendu, les ordinateurs sont
des machines lectroniques totalement dnues dintelligence. Ils ne "connaissent" rien des
fentres et des menus, ils ne savent rien des programmes ou des instructions et ne connais-
sent mme pas les zros et les uns, puisquils fonctionnent par impulsions lectriques au
niveau des circuits imprims... ce qui est aussi intrinsquement une abstraction.
In
f
o
C++Livre Page 128 J eudi, 7. mai 2009 9:45 09
Chapitre 5 Fonctions 129
Certains programmeurs refusent de connatre en dtail la structure de la mmoire vive,
prtextant quil nest pas utile dtre expert en mcanique pour conduire une voiture, par
exemple.
Vous devez comprendre lorganisation de la mmoire vive. Il est indispensable de savoir
comment les variables sont cres et gres et dans quelle partie les paramtres sont
transfrs.
Le partitionnement de la RAM
Lorsque vous crez un programme, le systme dexploitation (DOS, Linux/UNIX ou
Windows, par exemple) congure les diffrentes zones de la mmoire en fonction des
informations fournies par le compilateur. Comme vous tes un dveloppeur C++, vous
allez entendre parler despace de noms global, de mmoire libre, de registres, despace de
code et de pile.
Les variables globales sont dans lespace de noms global. Vous dcouvrirez les notions
despace de noms global et despace libre dans les chapitres suivants. Pour le moment,
nous allons nous intresser aux registres, lespace de code et la pile.
Les registres sont une zone de mmoire spciale, intgre lunit centrale (galement
appele UC ou CPU). Ils grent le fonctionnement interne de lordinateur. Leur mode de
traitement est complexe et dpasse de loin le cadre de cet ouvrage mais les registres qui
nous intressent sont ceux qui pointent en permanence sur la ligne suivante de code et
qui forment ce que lon appelle le pointeur dinstruction. Un pointeur dinstruction garde
la trace de la prochaine ligne de code excuter.
Les instructions se trouvent dans lespace de code, qui est la partie de la mmoire qui
stocke le code binaire produit par le compilateur. Chaque ligne de code source est traduite
en une suite dinstructions, chacune delles occupant une adresse particulire en mmoire.
Le pointeur dinstruction contient ladresse de la prochaine instruction traiter. Tout cela
est reprsent dans la Figure 5.6.
Figure 5.6
Le pointeur dinstruction.
Espace de code
Pointeur
d'instruction
100 Int x=5;
101 Int y=7;
102 cout << x;
103 Fonction(x,y);
104 y=9;
105 return;
102
C++Livre Page 129 J eudi, 7. mai 2009 9:45 09
130 Le langage C++
La pile est une zone spciale de mmoire alloue au programme, dans laquelle celui-ci
organise les donnes utilises par les fonctions. On lappelle pile car cest une structure de
donne LIFO (Last In, First Out, soit "dernier entr, premier sorti"), que lon pourrait
comparer une pile dassiettes (voir Figure 5.7).
Comme pour une pile dassiettes ou un tas de pices, le dernier lment pos est le premier
retir. Cela diffre de la plupart des les dattente dans lesquelles le premier arriv est le
premier sorti.
La taille de la pile varie avec le nombre dlments qui entrent, puis sortent. Lorsque lon
empile des assiettes, la pile grossit ; lorsquon dpile des assiettes, elle se rduit. Il est
impossible de retirer une assiette situe la base dune pile sans retirer dabord toutes les
assiettes poses dessus.
Une pile dassiettes est une analogie classique, mais il serait plus juste de comparer une
pile un ensemble de cases superposes de haut en bas. Le sommet de la pile est la case
sur laquelle pointe le pointeur de pile (qui est galement un registre).
Chaque case est associe une adresse squentielle, lune delles tant stocke dans le
registre du pointeur de pile. Toutes les adresses situes en dessous de cette adresse "magi-
que" appartiennent la pile. Tout ce qui est au-dessus du sommet se trouve en dehors de la
pile et est considr comme non valide (voir Figure 5.8).
Lorsque des donnes sont poses sur la pile, elles sont places dans une case situe au-
dessus du pointeur, puis ce dernier se dplace vers le haut pour pointer sur ces nouvelles
donnes. Lorsque des donnes sont tes de la pile, le pointeur se dplace simplement sur
ladresse prcdente (voir Figure 5.9).
Les donnes au-dessus du pointeur de pile (hors de la pile) peuvent ou non tre changes
tout moment. Ces valeurs sont dsignes sous le nom de garbage (informations incohrentes)
car leurs valeurs ne sont plus ables.
Figure 5.7
Une pile.
C++Livre Page 130 J eudi, 7. mai 2009 9:45 09
Chapitre 5 Fonctions 131
La pile et les fonctions
Ce qui suit est une approximation de ce qui se passe lorsque votre programme se branche une
fonction. Les dtails peuvent diffrer en fonction du systme dexploitation et du compilateur.
1. Ladresse dans le pointeur dinstruction devient celle qui suit lappel de la fonction.
Elle est place sur la pile, ce sera ladresse de retour aprs la n de la fonction.
2. La pile est prpare pour recevoir le type de la valeur renvoye : si ce type est un int
et quun int occupe deux octets sur lordinateur, deux octets vides sont ajouts au
sommet de la pile, mais ne sont pas inititaliss (cela signie que ce quils contiennent
y restera jusqu ce que la variable locale soit initialise).
3. Ladresse de la fonction appele, conserve dans une zone spciale de la mmoire, est
charge dans le pointeur dinstruction. La prochaine instruction excute sera donc la
premire instruction de la fonction appele.
Figure 5.8
Le pointeur de pile.
Figure 5.9
Dplacement du pointeur
de pile.
Pile
80
100
101
102
103
104
105
106
107
108
109
110
37 votreAge
50 monAge
Dans la pile
Hors de la pile
Pointeur de pile
102
laVariable
Pile
80
100
101
102
103
104
105
106
107
108
109
110
37 votreAge
50 monAge
Dans la pile
Hors de la pile
Pointeur de pile
108
laVariable
C++Livre Page 131 J eudi, 7. mai 2009 9:45 09
132 Le langage C++
4. Le sommet courant de la pile est marqu laide dun pointeur spcial appel stack
frame. Tout ce qui sera ajout la pile jusquau retour de la fonction aura dsormais
une porte locale, cest--dire limite la fonction.
5. Tous les paramtres passs la fonction sont placs sur la pile.
6. Linstruction prsente dans le pointeur dinstruction sexcute. Il sagit de la premire
instruction de la fonction appele.
7. Les variables locales sont empiles sur la pile au fur et mesure quelles sont dnies.
Quand la fonction est prte se terminer, la valeur de son rsultat est place dans la zone
de la pile rserve ltape 2. Puis la pile est dpile jusquau stack frame, ce qui revient
faire disparatre toutes les variables locales et les paramtres passs la fonction.
La valeur renvoye est dpile de la pile et devient la valeur de lappel de la fonction.
Ladresse de ltape 1 est rcupre et place dans le pointeur dinstruction. Le programme
reprend alors son droulement aprs linstruction dappel de la fonction.
Bien entendu, les dtails du processus varient dun compilateur un autre et en fonction
des systmes dexploitation ou des processeurs. Nanmoins, lorganisation de la pile est
sensiblement la mme sur tous les systmes dexploitation.
Au cours des prochains chapitres, nous aborderons dautres emplacements mmoire qui
servent stocker des donnes qui doivent persister en dehors de la vie des fonctions.
Questions-rponses
Q Pourquoi ne pas utiliser que des variables globales ?
R Il y a quelques annes, ctait la rgle. Cependant, mesure que les programmes sont
devenus plus complexes, il est devenu difcile de trouver les bogues dus des varia-
bles pouvant tre modies par nimporte quelle fonction. En effet, les variables
globales peuvent tre modies de nimporte quel point du programme. Des annes
dexprience ont convaincu les programmeurs que les donnes devaient rester les plus
locales possibles et que leur accs devait tre restreint.
Q Dans un prototype de fonction, quand dois-je utiliser le mot-cl inline ?
R Lorsque la fonction nexcde pas deux instructions et quelle est peu appele.
Q Pourquoi les modications apportes aux valeurs des paramtres dune fonction
ne se retent-elles pas dans la fonction appelante ?
R Les paramtres sont passs par valeur la fonction, ce qui signie quils sont dupli-
qus et que la valeur dorigine est conserve dans la fonction appelante. Pour en savoir
plus, reportez-vous la section "Principe des fonctions".
C++Livre Page 132 J eudi, 7. mai 2009 9:45 09
Chapitre 5 Fonctions 133
Q Si les paramtres sont passs par valeur, que faire si lon souhaite que les modi-
cations soient retes dans la fonction appelante ?
R La solution consiste utiliser les pointeurs ou des rfrences (voir les Chapitres 8 et
9). Vous y trouverez aussi une solution pour quune fonction puisse renvoyer plusieurs
valeurs.
Q Je rencontre ces deux fonctions :
int Surface(int largeur, int longueur = 1);
int Surface(int taille);
Q Y a-t-il surcharge ? Nous avons un nombre diffrent de paramtres, mais le premier
prototype a une valeur par dfaut.
R Les dclarations seront compiles sans erreur, mais si vous invoquez la fonction Sur-
face avec un seul paramtre, vous obtiendrez une erreur signalant quil y a ambigut
entre Surface(int, int) et Surface(int).
Testez vos connaissances
1. Quelle diffrence y a-t-il entre un prototype de fonction et une dnition de fonction ?
2. Les noms de paramtres doivent-ils tre identiques dans le prototype, la dnition et
lappel la fonction ?
3. Comment dclarer une fonction qui ne renvoie pas de valeur ?
4. En labsence de valeur renvoye, quel sera le type du rsultat ?
5. Quest-ce quune variable locale ?
6. Quest-ce que la porte ?
7. Que signie le mot "rcursivit" ?
8. Quand doit-on utiliser des variables globales ?
9. Quest-ce que la surcharge de fonction ?
Exercices
1. crivez le prototype de la fonction Perimetre(), qui renvoie un entier long non sign
et a deux paramtres entiers courts non signs.
2. crivez la dnition de la fonction Perimetre(), dcrite ci-dessus. Les deux paramtres
correspondent la longueur et la largeur dun rectangle. Rappel : le primtre est
gal deux fois la largeur + deux fois la longueur.
C++Livre Page 133 J eudi, 7. mai 2009 9:45 09
134 Le langage C++
3. CHERCHEZ LERREUR : cette fonction est bogue.
#include <iostream>
void maFct(unsigned short int x);
int main()
{
unsigned short int x, y;
y = maFct(int);
std::cout << "x : " << x << " y : " << y << "\n";
return 0;
}
void maFct(unsigned short int x)
{
return (4*x);
}
4. CHERCHEZ LERREUR : cette fonction est galement bogue.
#include <iostream>
int maFct(unsigned short int x);
int main()
{
unsigned short int x, y;
x = 7;
y = maFct(x);
std::cout << "x : " << x << " y : " << y << "\n";
return 0;
}
int maFct(unsigned short int x);
{
return (4*x);
}
5. crivez une fonction qui reoit deux paramtres entiers courts non signs, divise le
premier par le second et renvoie le rsultat. Si le second nombre est gal zro, la
division est impossible et la fonction doit renvoyer la valeur -1.
6. crivez un programme qui invite entrer deux valeurs puis qui appelle la fonction
dnie la question 5. Afchez le rsultat ou un message derreur si la valeur
renvoye est gale -1.
7. crivez un programme qui demande un nombre et un exposant. Crez une fonction
rcursive qui met le nombre la puissance indique. Par exemple, si le nombre entr
est 2 et lexposant 4, la fonction doit renvoyer 16.
C++Livre Page 134 J eudi, 7. mai 2009 9:45 09
6
Programmation oriente
objet
Au sommaire de ce chapitre
Les classes et les objets
La dnition dune classe et la cration dobjets associs
La notion de fonction membre et de donne membre
Le rle des constructeurs
C++ est-il orient objet ?
une certaine poque, C le prdcesseur de C++ tait le langage de programmation le
plus populaire pour le dveloppement de logiciels professionnels. Cest lui qui a t utilis
pour crer les systmes dexploitation (comme Unix), pour la programmation en temps
rel (contrle des machines, des priphriques et de llectronique). Ce nest que plus tard
quil a t employ comme langage de programmation conventionnel. Il avait pour but de
simplier la programmation en ce qui concerne laspect matriel.
C++Livre Page 135 J eudi, 7. mai 2009 9:45 09
136 Le langage C++
C a t conu pour tre un compromis entre les langages de programmation de haut
niveau, comme COBOL, et lassembleur, qui est extrmement performant, mais difcile
utiliser. C mettait en uvre la programmation "structure", o les problmes sont dcom-
poss en units plus petites qui peuvent tre rptes (les procdures) et les donnes sont
regroupes en paquets (appels structures).
Or, les langages de recherche, comme Smalltalk et CLU, avaient dj commenc choisir
une nouvelle orientation (lorientation objet), qui associait les donnes dans des assembla-
ges comme les structures, mais avec les fonctionnalits des procdures, le tout en une
seule unit : lobjet.
Le monde qui nous entoure est peupl dobjets : voitures, chiens, arbres, nuages,
eurs, etc. Chacun possde ses caractristiques propres (rapide, amical, noir...).
La plupart ont un comportement (ils roulent, aboient, poussent...). Gnralement, nous
ne faisons pas rfrence aux donnes dun chien, ni la faon de les manipuler : nous
voyons un chien comme un lment faisant partie de notre univers (son apparence, ce
quil fait). Cela vaut galement pour tout objet du monde rel intgr dans le domaine de
linformatique.
Les programmes crits en ce dbut de XXI
e
sicle sont beaucoup plus complexes que ceux
crits la n du prcdent. Les programmes crs laide de langages procduraux se
rvlent difcile maintenir et coteux modier. Les interfaces utilisateur graphiques,
Internet, la tlphonie numrique et sans l, et toutes les nouvelles technologies ont consi-
drablement accru la complexit des projets alors mme que les attentes des utilisateurs
augmentaient.
Les langages de programmation orients objet constituent un outil prcieux pour les ds
du dveloppement logiciel. Bien quil nexiste pas de solution idale pour le dvelop-
pement de logiciels complexes, ces langages crent un lien puissant entre les structures
de donnes et les mthodes qui les manipulent, se rapprochant ainsi de notre manire de
penser (nous, les programmeurs et les clients). La communication et la qualit des logi-
ciels samliorent en consquence. On ne pense dsormais plus en termes de structures de
donnes et de fonctions permettant de les manipuler, mais en termes dobjets, comme sil
sagissait de leurs quivalents dans le monde rel, qui apparaissent et agissent dune
certaine faon.
C++ a t conu comme un pont entre la programmation oriente objet et le langage C.
Lobjectif tait de fournir une conception oriente objet une plate-forme de dveloppe-
ment de logiciels professionnels rapide, avec un intrt tout particulier pour les performances.
Vous verrez plus loin comment C++ a atteint ces objectifs.
C++Livre Page 136 J eudi, 7. mai 2009 9:45 09
Chapitre 6 Programmation oriente objet 137
Cration de nouveaux types
Les programmes ont gnralement pour but de rsoudre des problmes de la vie courante,
comme grer la liste des employs ou simuler le fonctionnement dun systme de chauf-
fage. Bien quil soit possible de rsoudre des problmes complexes uniquement avec des
nombres et des caractres, il est bien plus pratique de pouvoir crer des reprsentations des
objets que vous voulez traiter texte. En dautres termes, il est plus facile de simuler le
fonctionnement dun systme de chauffage si vous pouvez crer des variables qui repr-
sentent les pices, les capteurs, les thermostats et les radiateurs. Plus les variables seront
proches de la ralit, plus le programme sera simple crire.
Au cours des chapitres prcdents, vous avez tudi un certain nombre de types de varia-
bles, dont les entiers non signs et les caractres. Le type dune variable fournit des infor-
mations prcieuses sur celle-ci. Si, par exemple, vous dclarez Hauteur et Largeur
comme des entiers courts non signs (unsigned short), vous savez quelles pourront
accueillir une valeur comprise entre 0 et 65 535, en supposant quun entier non sign soit
cod sur deux octets sur votre plate-forme. Si vous essayez dy copier autre chose, le
programme dtectera une erreur. Vous ne pouvez donc pas stocker votre nom dans ces
variables et vous ne devriez mme pas essayer de le faire.
Le type dune variable nous renseigne sur :
sa taille en mmoire ;
les informations quelle peut contenir ;
les actions qui peuvent sy appliquer.
Dans des langages traditionnels comme le C, les types taient intgrs au langage.
En C++, le programmeur a la possibilit dtendre le langage de base en crant les types
dont il a besoin chacun de ces nouveaux types peut avoir toutes les fonctionnalits et la
puissance des types prdnis.
Inconvnients de la cration de types avec struct
Le langage C permet de crer de nouveaux types en combinant des variables au sein de
structures (struct). Ces structures peuvent tre assimiles un nouveau type de donnes
via linstruction typedef.
Cette fonctionnalit a toutefois quelques lacunes :
Les struct et les fonctions qui les manipulent ne sont pas lies ; on ne retrouve ces
fonctions quen lisant les chiers den-tte des bibliothques disponibles et en
recherchant celles qui ont un paramtre du nouveau type.
C++Livre Page 137 J eudi, 7. mai 2009 9:45 09
138 Le langage C++
La coordination des activits des fonctions lies un struct particulier est plus difcile
car tout ce qui se trouve dans le struct peut tre modi tout moment un autre
endroit du programme. Il ny a aucun moyen de protger la structure des interfrences
extrieures.
Les oprateurs prdnis ne fonctionnent pas sur les struct ; il nest pas possible
dadditionner deux struct avec le signe plus (+), mme si cela semblerait naturel (par
exemple lorsque deux struct reprsentent des lments textuels complexes que lon
souhaite concatner ensemble).
Prsentation des classes et des membres
La cration dun type passe, en C++, par la dclaration dune classe. Une classe est
simplement un ensemble de variables, souvent de types diffrents, associes un ensemble
de fonctions.
Une voiture, par exemple, est un ensemble constitu de cinq roues, dun moteur, dune
carrosserie avec des portes, de siges, etc. Avec votre auto, vous pouvez avancer, reculer,
tourner, vous arrter, vous garer, etc. Une classe permet dencapsuler les diffrents
lments et les fonctions dans un unique ensemble appel objet.
Lencapsulation de tous les lments en une classe prsente de nombreux avantages pour
le programmeur. Tout est regroup au mme endroit, ce qui facilite les actions sur les
donnes (accs, copie, etc.). Les clients de la classe (cest--dire les parties du programme
qui utilisent la classe) peuvent utiliser les objets sans se soucier ni de leur structure, ni de
leur mode de fonctionnement.
Une classe peut contenir nimporte quelle combinaison de types de variables, mais
galement dautres types de classes. Les variables dune classe sont gnralement
appeles variables membres ou donnes membres. Une classe Voiture, par exemple,
pourrait avoir des variables membres reprsentant les siges, le type dautoradio, les
pneus, etc.
Une classe peut aussi contenir des fonctions appeles fonctions membres, ou mthodes.
Les fonctions membres font partie de la classe au mme titre que les variables membres.
Elles dterminent les actions pouvant tre ralises par la classe.
Gnralement, les fonctions membres de la classe manipulent ses variables membres. Par
exemple, les mthodes de la classe Voiture, par exemple, pourraient inclure les fonctions
Demarrer() et Freiner(). Une classe Chat pourrait contenir des donnes membres
correspondant lge et au poids de lanimal et ses mthodes pourraient inclure les fonctions
Dormir(), Miauler() et ChasserSouris().
C++Livre Page 138 J eudi, 7. mai 2009 9:45 09
Chapitre 6 Programmation oriente objet 139
Dclaration dune classe
La dclaration dune classe donne au compilateur des informations sur la classe. Pour
dclarer une classe, vous devez utiliser le mot-cl class suivi du nom de la classe,
dune accolade ouvrante et de la liste des donnes membres et des mthodes associes.
Pour clore la dclaration, utilisez une accolade fermante suivie dun point-virgule. Voici
par exemple comment dclarer la classe Chat :
class Chat
{
public:
unsigned int sonAge;
unsigned int sonPoids;
void Miauler();
};
Cette dclaration nalloue pas de mmoire pour un chat. Elle dcrit uniquement ce quest
un Chat, quelles sont ses donnes membres (sonAge et sonPoids) et ce quil sait faire
(Miauler()). Mme si la mmoire nest pas alloue, elle indique au compilateur la taille
de la classe et lespace mmoire ncessaire chaque chat. Si un entier occupe 4 octets sur
lordinateur, un objet Chat aura donc une taille de 8 octets : 4 pour sonAge et 4 pour
sonPoids. Miauler() noccupera que lespace ncessaire aux stockage des informations
permettant de connatre son emplacement en mmoire ; il sagit en fait dun pointeur vers
une fonction qui peut occuper 4 octets sur une plate-forme 32 bits.
Conventions dattribution de noms
Lattribution dun nom chaque variable, fonction membre et classe fait partie de la tche
dun programmeur. Vous avez appris au Chapitre 3 que les noms de variables et de
constantes devaient tre signicatifs. Chat, Rectangle et Employe sont de bons noms
de classes, par exemple. Miauler() et StopperMoteur() sont galement de bons noms de
mthodes puisquils indiquent clairement le rle de chaque fonction. Nombre de program-
meurs choisissent de prxer le nom de leurs variables membres avec son (ou its)
comme dans sonAge, sonPoids ou saVitesse. Cela permet de distinguez plus facilement
les variables membres de celles qui ne le sont pas.
Il est galement possible dutiliser dautres prxes, comme monAge, monPoids ou maVi-
tesse, ou tout simplement la lettre "m" (comme membre), avec ventuellement un carac-
tre de soulignement (_), par exemple mAge ou m_age.
Certains choisissent de prxer leur nom de classe avec une lettre particulire, comme
cChat ou cPersonne et dautres prfrent nutiliser que des majuscules ou que des minus-
cules. Dans ce livre, les noms de classes commenceront tous par une majuscule comme
dans Chat ou Personne.
C++Livre Page 139 J eudi, 7. mai 2009 9:45 09
140 Le langage C++
De mme, de nombreux programmeurs dbutent tous les noms de mthodes par une
majuscule et nutilisent que des minuscules pour les noms des variables membres. Les
mots les composant sont spars par le caractre de soulignement comme dans
Chasser_Souris, ou simplement indiqus avec la premire lettre en majuscule (Chasser-
Souris ou DessinerCercle).
Le plus important est de choisir un style et de lappliquer dans tous les programmes.
Avec le temps, votre style voluera, non seulement pour les conventions de noms,
mais galement pour lindentation, lalignement des accolades et les styles de
commentaires.
La plupart des socits de dveloppement ont bien sr adopt un "standard
maison" pour rgler les problmes de style. Cela garantit que tous les dve-
loppeurs pourront relire aisment le code de leurs collgues. Malheureuse-
ment, cela sapplique galement aux socits qui dveloppent des systmes
dexploitation et des bibliothques de classes rutilisables, ce qui signie gn-
ralement que les programmes C++ doivent travailler simultanment avec
plusieurs conventions de nommage.
Comme on la dj indiqu, C++ diffrencie les majuscules des minuscules.
Les noms de classes, fonctions et variables devront donc tous tre choisis sur le
mme modle. Cela vous pargnera davoir rechercher comment le nom de la
classe a t orthographi : Rectangle, RECTANGLE ou rectangle ?
Dnition dun objet
Aprs avoir dclar une classe, vous pouvez lutiliser comme nouveau type pour dclarer
des variables de ce type. Il sagit dune opration aussi simple que la dnition dune
variable entire :
unsigned int PoidsBrut; // dfinition dun entier non sign
Chat Frisky; // dfinition dun chat
Dans ce code, on dnit la variable PoidsBrut de type entier non sign, ainsi que lobjet
Frisky de la classe (ou du type) Chat.
Classes et objets
Vous ne dorlotez pas la dnition dun chat, vous dorlotez un chat particulier. Vous faites
une diffrence entre un chat en gnral et celui qui gt langoureusement sur le divan de
votre salon. De la mme faon, C++ fait la diffrence entre la classe Chat en gnral et
In
f
o
A
tte
n
tio
n
C++Livre Page 140 J eudi, 7. mai 2009 9:45 09
Chapitre 6 Programmation oriente objet 141
chaque objet Chat individuel. Frisky est un objet de la classe Chat, comme PoidsBrut
est une variable de type entier non sign.
Un objet est une instance particulire dune classe.
Accs aux membres dune classe
Aprs avoir dni un objet rel Chat (Frisky, par exemple), vous pouvez accder ses
membres laide de loprateur point (.). Pour affecter 5 la variable membre sonPoids
de Frisky, on utilise donc lexpression suivante :
Frisky.sonPoids = 5;
Pour appeler la fonction Miauler(), il suft de taper :
Frisky.Miauler();
Pour utiliser une mthode de la classe, il suft de lappeler. Dans lexemple ci-dessus,
vous appelez la fonction Miauler() de Frisky.
Affectez des valeurs aux objets, pas aux classes
En C++, vous ne pouvez pas affecter des valeurs des types. Lattribution de valeurs
concerne les variables. Par exemple, vous navez pas le droit dcrire :
int = 5; // erreur
Le compilateur dtectera lerreur, car il est impossible daffecter 5 un entier. En revanche,
vous pouvez dnir une variable de type entier et lui affecter la valeur 5. Exemple :
int x; // dfinir x comme un entier
x = 5; // affecter la valeur 5 x
Lexpression suivante produit galement une erreur :
Chat.sonAge = 5; // erreur
Le compilateur produira une erreur, car il est impossible daffecter la valeur 5 au membre
sonAge de la classe Chat. Vous devez crer un objet Chat spcique, puis lui attribuer
cette valeur. Exemple :
Chat Frisky; // mme chose que int x;
Frisky.sonAge = 5; // mme chose que x = 5;
C++Livre Page 141 J eudi, 7. mai 2009 9:45 09
142 Le langage C++
Les membres non dclars nexistent pas
Essayez lexprience suivante : montrez votre chat un enfant de trois ans et dites-lui "il
sappelle Frisky et il connat un tour de magie : Frisky, aboie !". Lenfant gloussera et vous
rpondra : "mais non, idiot, les chats naboient pas".
Si vous crivez :
Chat Frisky; // Cration dun chat nomm Frisky
Frisky.Aboyer(); // Demande Frisky daboyer
Le compilateur vous rpondra aussi " mais non, idiot, les chats naboient pas" (bien quil
utilisera srement dautres termes moins potiques, comme [531] Error: Member
function Aboyer not found in class Chat). Le compilateur sait que Frisky ne peut
pas aboyer car la classe Chat ne possde pas de mthode Aboyer() et il ne saura pas non
plus que Frisky peut miauler si vous navez pas dni de mthode Miauler().
Accs priv et accs public
Une dclaration de classe fait galement appel des mots-cls supplmentaires. private
et public font partie des plus importants.
Ces deux mots-cls sont utiliss avec les membres dune classe, que ce soient des donnes
ou des mthodes. Les membres privs ne peuvent tre accdes que depuis les mthodes
de la classe elle-mme alors que les membres publics peuvent tre accds via nimporte
quel objet de la classe. Cette distinction est trs importante, mais souvent source de confusion
chez le dveloppeur dbutant. Par dfaut, tous les membres dune classe sont privs.
Pour clarier les choses, reprenons lexemple dcrit plus haut :
class Chat
{
Faire Ne pas faire
Dclarer une classe laide du mot-cl
class.
Utiliser loprateur point (.) pour accder aux
membres et aux mthodes de la classe.
Confondre une dclaration avec une dni-
tion. Une dclaration dcrit une classe, une
dnition rserve de la mmoire pour un
objet.
Confondre classe et objet.
Affecter des valeurs une classe. En revan-
che, vous pouvez attribuer des valeurs aux
donnes membres dun objet.
C++Livre Page 142 J eudi, 7. mai 2009 9:45 09
Chapitre 6 Programmation oriente objet 143
unsigned int sonAge;
unsigned int sonPoids;
void Miauler();
};
Dans cette dclaration, sonAge, sonPoids et Miauler() sont privs, car tous les membres
dune classe sont privs par dfaut. moins de prciser le contraire, ils sont privs. Si
vous crez un programme et que vous excutez les deux instructions suivantes dans
main() :
int main()
{
Chat Mistigri;
Mistigri.sonAge = 5; // erreur! impossible daccder des
// donnes prives!
...
le compilateur produira une erreur. En effet, en laissant ces membres privs, vous indiquez
au compilateur que lon ne pourra accder sonAge, sonPoids et Miauler() qu partir
des fonctions membres de la classe Chat. Pourtant, ici, vous avez tent daccder au
membre sonAge de Mistigri depuis lextrieur dune mthode de Chat. Le fait que
Mistigri soit un objet Chat ne signie pas que vous pouvez accder ses composants
privs (mme sils sont visibles dans la dclaration).
Les dbutants en C++ sont souvent troubls par ce fonctionnement. Je vous entends pres-
que hurler "Je viens de dire que Mistigri est un chat ! Pourquoi ne peut-il pas accder son
propre ge ?". En fait, il le peut, mais pas vous ! Dans ses propres mthodes, Mistigri
peut accder tous ses membres, publics ou privs. Le fait que vous ayez cr un Chat ne
signie pas que vous pouvez voir ou modier ses parties prives.
Vous pouvez avoir accs aux donnes membres de la classe Chat en rendant publics
certains des membres :
class Chat
{
public:
unsigned int sonAge;
unsigned int sonPoids;
void Miauler();
};
Dans cette dclaration, sonAge, sonPoids et Miauler() sont dsormais publics. Misti-
gri.sonAge = 5, de lexemple prcdent, peut donc tre compil sans problme.
C++Livre Page 143 J eudi, 7. mai 2009 9:45 09
144 Le langage C++
Le mot-cl public sapplique tous les membres de la dclaration jusqu ce
que lon rencontre le mot-cl private et vice versa. Cela vous permet de
dclarer facilement des parties de la classe comme prives ou publiques.
Dans le Listing 6.1, les membres de la classe Chat sont publics.
Listing 6.1 : Accs aux membres public dune classe simple
1: // Dclaration dune classe Chat et
2: // dfinition dun objet de la classe
3:
4: #include <iostream>
5:
6: class Chat // dclare la classe Chat
7: {
8: public: // les membres qui suivent sont publics
9: int sonAge; // variable membre
10: int sonPoids; // variable membre
11: }; // remarquez le point-virgule
12:
13: int main()
14: {
15: Chat Frisky;
16: Frisky.sonAge = 5; // affecte 5 la variable membre
17: std::cout << "Frisky est un chat qui a ";
18: std::cout << Frisky.sonAge << " ans.\n";
19: return 0;
20: }
Ce programme produit le rsultat suivant :
Frisky est un chat qui a 5 ans.
La ligne 6 contient le mot-cl class, indiquant que ce qui suit est une dclaration de
classe. Ici, cette classe sappelera Chat.
Le corps de la dclaration commence par une accolade ouvrante la ligne 7 et se termine
par une accolade fermante et un point-virgule la ligne 11. la ligne 8, le mot-cl public
suivi de deux-points signie que tout ce qui suit est public jusqu la rencontre du mot-cl
private ou de la n de la dclaration.
Les lignes 9 et 10 contiennent les dclarations des membres sonAge et sonPoids.
La fonction principale du programme commence la ligne 13 et la ligne 15 dnit Frisky
comme une instance de Chat : cest donc un objet de la classe Chat. Lge est dni la
ligne 16, puis afch. Notez comment on accde aux membres de lobjet Frisky aux
In
f
o
C++Livre Page 144 J eudi, 7. mai 2009 9:45 09
Chapitre 6 Programmation oriente objet 145
lignes 16 et 18 : sonAge est accd laide du nom dobjet (Frisky ici), suivi dun point
puis du nom du membre (sonAge ici).
Essayez de mettre en commentaire (cest--dire de dsactiver) la ligne 8, puis
recompilez le programme. La ligne 16 produira une erreur car sonAge nest
plus public mais est revenu laccs par dfaut, comme les autres membres, et
cet accs est priv.
Rendre prives les donnes membres
Une rgle gnrale de la conception est que les donnes membres dune classe doivent
rester prives. Bien entendu, si toutes les donnes membres deviennent prives, vous
risquez de vous demander comment accder aux informations sur la classe. Si sonAge est
priv, par exemple, comment dnir ou rcuprer lge dun objet Chat ?
Pour accder aux donnes prives dune classe, vous devez crer des fonctions publiques,
appeles mthodes daccs. Ces mthodes sont des fonctions membres qui peuvent tre
appeles dautres endroits de votre programme pour accder aux donnes prives.
Une mthode daccs publique est une fonction membre de la classe capable de lire (rcu-
prer) la valeur dune variable membre prive de la classe et/ou de lui affecter une valeur.
Pourquoi faire intervenir un niveau supplmentaire daccs ? Pourquoi ajouter dautres
fonctions alors quil est plus ais et plus simple dutiliser directement des donnes ? Pourquoi
passer par des mthodes daccs ?
La rponse ces questions est que les mthodes daccs permettent de dissocier la faon
dont les donnes sont stockes et la manire dont elles sont utilises. En obligeant le code
client passer par des mthodes daccs, le concepteur de la classe peut ensuite modier
le mode de stockage des donnes membres (et donc galement les mthodes daccs) sans
que cela ne perturbe le code client.
Une fonction qui accde directement llment sonAge pour connatre lge dun chat
devra tre rcrite si vous dcidez, plus tard, de modier le mode de stockage de cette
information. En obligeant la fonction cliente appeler LireAge() pour obtenir lge de
notre flin, celle-ci na pas besoin de connatre le type de la variable qui contient lge, ni
de savoir comment il est calcul.
Il va sans dire que cette technique simplie normment la maintenance des programmes.
En outre, le code a une dure de vie suprieure car dventuelles modications de conception
naffecteront plus les programmes qui lutilisent.
Les mthodes daccs peuvent galement contenir des traitements supplmentaires. Sil y
a peu de chances, par exemple, que lge du chat soit suprieur 100, ou que son poids
In
f
o
C++Livre Page 145 J eudi, 7. mai 2009 9:45 09
146 Le langage C++
soit gal 1 000, ces valeurs ne devraient donc pas tre autorises. Les mthodes daccs
peuvent mettre en place ces types de contraintes et raliser bien dautres oprations.
Le Listing 6.2 dclare la classe Chat qui comprend des donnes membres prives et des
mthodes daccs publiques. Attention : ce code nest pas excutable sil est compil.
Listing 6.2 : Exemples de mthodes daccs
1: // Dclaration de la classe Chat
2: // Les donnes membres sont prives, les mthodes daccs
3: // publiques permettent laffectation et la rcupration
3a: // des donnes prives
4: class Chat
5: {
6: public:
7: // mthodes daccs publiques
8: unsigned int LireAge();
9: void DefAge(unsigned int Age);
10:
11: unsigned int LirePoids();
12: void DefPoids(unsigned int Poids);
13:
14: // fonctions membres publiques
15: void Miauler();
16:
17: // donnes membres prives
18: private:
19: unsigned int sonAge;
20: unsigned int sonPoids;
21: };
Cette classe contient cinq mthodes publiques. Les lignes 8 et 9, dune part, et 11 et 12,
dautre part, correspondent respectivement aux mthodes daccs de sonAge et de
sonPoids. Vous pouvez constater que la ligne 8 dclare une mthode pour obtenir lge et
que la ligne 9 en dclare une permettant de le modier. Les lignes 11 et 12 font de mme
pour le poids. Ces mthodes daccs permettent donc dinitialiser les valeurs des variables
membres et de les lire.
La ligne 15 dclare la fonction membre publique Miauler(). Il ne sagit pas dune fonc-
tion daccs ; elle ne lit pas ou ne modie pas une variable membre mais effectue une
action pour la classe qui consiste, ici, afcher "Miaou", comme vous le verrez dans le
programme du Listing 6.3.
Les variables membres sont dclares aux lignes 19 et 20.
C++Livre Page 146 J eudi, 7. mai 2009 9:45 09
Chapitre 6 Programmation oriente objet 147
Pour dnir lge de Frisky, vous devez passer la valeur la mthode DefAge() :
Chat Frisky;
Frisky.DefAge(5); // fixe lge laide de la mthode daccs
Plus loin, vous trouverez le code permettant de faire fonctionner SetAge et les autres mthodes.
Dclarer des mthodes ou des donnes prives permet au compilateur de dtecter des
erreurs de programmation avant quelles ne deviennent des bogues. Tout programmeur
peut contourner les restrictions daccs sil le souhaite. Bjarne Stroustrup, linventeur
de C++, a dailleurs lui-mme dclar que les mcanismes des contrles des accs prot-
geaient des accidents, mais pas de la fraude (ARM, 1990).
Le mot-cl class
Sa syntaxe se prsente ainsi :
class nom_de_la_classe
{
// mots cls des contrles daccs
// variables et mthodes membres
};
Le mot-cl class permet de dclarer de nouveaux types. Une classe est un ensemble de
donnes membres, qui sont des variables de diffrents types, y compris dautres classes. La
classe contient galement des fonctions (ou mthodes) qui permettent de manipuler les
donnes de la classe ou deffectuer un certain nombre de services pour celle-ci.
On peut comparer le processus de dnition des objets du nouveau type celui dune
variable. On indique le type (le nom de la classe) suivi du nom de la variable (lobjet). Pour
avoir accs aux fonctions et aux membres de cet objet, utilisez loprateur point (.).
Les sections dune classe peuvent tre dclares prives ou publiques en utilisant les mots-
cls private ou public. Par dfaut, le contrle daccs est de type priv. Laccs ne change
pas tant quun nouveau mot-cl nest pas prcis dans le code source. Les dclarations de
classe se terminent par une accolade fermante et un point-virgule.
Exemple 1
class Chat
{
public:
unsigned int sonAge;
unsigned int sonPoids;
void Miauler();
};
Chat Frisky;
Frisky.sonAge = 8;
Frisky.sonPoids = 9;
Frisky.Miauler();
C++Livre Page 147 J eudi, 7. mai 2009 9:45 09
148 Le langage C++
Exemple 2
class Voiture
{
public: // les cinq membres suivants sont publics
void Demarrer();
void Accelerer();
void Freiner();
void DefAnnee(int annee);
int LireAnnee();
private: // le reste est priv
int sonAnnee;
char sonModele [255];
}; // fin de la dclaration
Voiture Star5; // cre une instance de Voiture
int achat; // variable locale entire
Star5.DefAnnee(84); // voiture achete en 84
achat = Star5.LireAnnee(); // extraction du millsime
Star5.Demarrer(); // appel de la mthode Demarrer
Implmentations des mthodes de la classe
Comme vous venez de le voir, une fonction daccs fournit une interface publique aux
donnes membres prives dune classe. Comme nimporte quelle mthode de la classe,
elle doit tre associ du code. Son implmentation est appele dnition de la fonction.
Cette dnition commence de la mme manire que celle dune fonction ordinaire. On
indique dabord le type du rsultat de la fonction, ou void si elle ne renvoie rien. Il est
suivi du nom de la classe, puis de deux caractres deux-points, du nom de la fonction et de
ses paramtres. Le Listing 6.3 montre comment dclarer la classe Chat et implmenter ses
mthodes daccs et une mthode membre gnrale.
Listing 6.3 : Implmentation des mthodes dune classe simple
1: // Dclaration dune classe et
2: // dfinition des mthodes de la classe,
3: #include <iostream> // pour cout
Faire Ne pas faire
Utiliser des mthodes daccs publiques.
Accder des variables membres prives
depuis des fonctions membres dans une
classe.
Dclarer les variables membres comme publi-
ques si ce nest pas ncessaire.
Essayer daccder des variables membres
prives de lextrieur dune classe.
C++Livre Page 148 J eudi, 7. mai 2009 9:45 09
Chapitre 6 Programmation oriente objet 149
4:
5: class Chat // dbut de la dclaration de la classe
6: {
7: public: // dbut de section publique
8: int LireAge(); // mthode daccs
9: void DefAge (int age); // mthode daccs
10: void Miauler(); // mthode gnrale
11: private: // dbut de section prive
12: int sonAge; // variable membre
13: };
14:
15: // LireAge, mthode daccs publique
16: // renvoie la valeur du membre sonAge
17: int Chat::LireAge()
18: {
19: return sonAge;
20: }
21:
22: // dfinition de DefAge,
23: // fonction daccs publique
24: // dfinit le membre sonAge
25: void Chat::DefAge(int age)
26: {
27: // initialise la variable membre sonAge avec
28: // la valeur passe par le paramtre age
29: sonAge = age;
30: }
31:
32: // dfinition de la mthode Miauler
33: // renvoie: void
34: // paramtres: Aucun
35: // rle: afficher "Miaou" lcran
36: void Chat::Miauler()
37: {
38: std::cout << "Miaou.\n";
39: }
40:
41: // crer un objet Chat, dfinir son ge, le faire miauler,
42: // dire son ge, le faire miauler de nouveau.
43: int main()
44: {
45: Chat Frisky;
46: Frisky.DefAge(5);
47: Frisky.Miauler();
C++Livre Page 149 J eudi, 7. mai 2009 9:45 09
150 Le langage C++
48: std::cout << "Frisky est un chat qui a ";
49: std::cout << Frisky.LireAge() << " ans.\n";
50: Frisky.Miauler();
51: return 0;
52: }
Ce programme produit le rsultat suivant :
Miaou.
Frisky est un chat qui a 5 ans.
Miaou.
La classe Chat est dnie de la ligne 5 la ligne 13. la ligne 7, le mot-cl public
demande au compilateur de crer un ensemble de membres publics. La ligne 8 dclare la
mthode daccs publique LireAge(), qui a pour but dextraire la valeur de la variable
membre sonAge dclare la ligne 12. la ligne 9, la fonction daccs publique
DefAge() est suivie dun paramtre entier qui sera affect la variable membre sonAge.
La ligne 10 dclare la mthode Miauler(). Il ne sagit pas dune mthode daccs. Il sagit
plutt dune mthode gnrale qui afche "Miaou" lcran.
La section private commence la ligne 11 : elle comprend la dclaration de la variable
membre sonAge la ligne suivante. Enn, la dclaration de classe se termine par une
accolade fermante et un point-virgule la ligne 13.
La fonction membre LireAge() est dnie entre les lignes 17 et 20. Cette mthode ne
prend pas de paramtre mais renvoie une valeur entire. Vous remarquerez, la ligne 17,
quelle contient le nom de la classe suivi de deux caractres deux-points et du nom de la
fonction. Le compilateur peut ainsi comprendre que la fonction LireAge() que vous d-
nissez ici est celle que vous avez dclare dans la classe Chat. Hormis sa ligne den-tte, la
fonction LireAge() est cre comme nimporte quelle fonction.
Cette fonction noccupe quune seule ligne et renvoie la valeur de sonAge. Notez que la
fonction main() na pas accs la variable sonAge puisquelle est dnie comme membre
priv de la classe Chat. En fait, la fonction principale ne peut avoir accs la variable
sonAge qu travers la fonction publique LireAge() qui est une fonction membre de la
classe Chat. Cela permet de connatre lge de Frisky.
La fonction membre DefAge() est dnie la ligne 25. On voit que cette fonction prend
une valeur entire, appele age, et quelle ne renvoie pas de rsultat car son type est void.
SetAge() prend la valeur du paramtre age et lattribue sonAge la ligne 29. Cette fonc-
tion est membre de la classe Chat et, ce titre, peut avoir accs la variable prive
sonAge.
La mthode Miauler() de la classe Chat est dnie la ligne 36. Elle afche le mot
"Miaou" lcran. Vous remarquerez la prsence du caractre \n qui permet de passer la
C++Livre Page 150 J eudi, 7. mai 2009 9:45 09
Chapitre 6 Programmation oriente objet 151
ligne suivante. Vous pouvez constater que Miauler() est dnie comme les mthodes
daccs : elle commence par le type de son rsultat, suivi du nom de la classe, du nom de
la fonction et de ses paramtres (aucun dans ce cas).
Le programme commence vritablement la ligne 43 avec la fameuse fonction main().
la ligne 45, main() dclare un objet nomm Frisky, du type Chat. Dit autrement,
main() dclare un Chat nomm Frisky. La ligne 46, affecte la valeur 5 la variable
membre sonAge par lintermdiaire de la fonction daccs DefAge(). Pour appeler cette
mthode, le programme utilise le nom de lobjet (Frisky) suivi de loprateur de membre
(.) et du nom de la mthode (DefAge()). Les autres mthodes de lobjet peuvent tre
appeles de la mme faon.
Les termes fonction membre et mthode sont interchangeables.
La fonction membre Miauler() est appele la ligne 47. la ligne suivante, lge du chat
safche par le biais de la fonction daccs publique LireAge(). Enn, la fonction Miau-
ler() est appele de nouveau la ligne 50. Mme si ces mthodes font partie dune classe
(Chat) et sont utilises via un objet (Frisky), elles agissent comme les fonctions vues
prcdemment.
Ajout de constructeurs et de destructeurs
Pour dnir une variable entire, vous pouvez procder de deux faons. Vous pouvez dnir
la variable, puis lui affecter ultrieurement une valeur dans le programme. Exemple :
int Poids; // dfinition dune variable
... // autres instructions
Poids = 7; // affectation dune valeur
Il est galement possible de dnir une variable et de linitialiser aussitt. Exemple :
int Poids = 7; // dfinition et initialisation 7
Linitialisation consiste dnir une variable et lui affecter une valeur. Bien entendu,
vous pouvez modier cette valeur ultrieurement, car linitialisation nest une opration ni
irrversible ni dnitive. Elle garantit que la variable aura toujours une valeur signifi-
cative.
Comment initialiser les donnes membres dune classe ? Vous pouvez le faire en utilisant
une fonction membre spciale appele constructeur, qui peut prendre autant de paramtres
In
f
o
C++Livre Page 151 J eudi, 7. mai 2009 9:45 09
152 Le langage C++
que ncessaire, mais qui ne renvoie pas de valeur (pas mme void). En fait, le constructeur
est une mthode de la classe qui porte le mme nom que celle-ci.
Lorsque vous dclarez un constructeur, il est souhaitable de dclarer galement un
destructeur. Alors que les constructeurs crent et initialisent des objets de la classe, les
destructeurs librent la mmoire ou les ressources qui ont t alloues (soit dans le
constructeur, soit au cours de la vie de lobjet). Un destructeur porte toujours le nom de
la classe prcd dun tilde (~). Il ne prend pas de paramtre et ne renvoie pas de valeur.
Si vous deviez dclarer un destructeur pour la classe Chat, sa dclaration serait donc la
suivante :
~Chat();
Constructeurs et destructeurs par dfaut
Il existe diffrents types de constructeurs ; certains acceptent des paramtres, dautres non.
Un constructeur sans paramtre est appel constructeur par dfaut. Il nexiste par contre
quune sorte de destructeur qui, comme le constructeur par dfaut, ne prend aucun para-
mtre.
Si vous ne crez ni constructeur ni destructeur, le compilateur vous en fournit automati-
quement.
Ce constructeur et ce destructeur par dfaut crs par le compilateur ne prennent pas de
paramtres et, en fait, ne font rien. Si vous souhaitez quils fassent quelque chose, vous
devez crer vos propres constructeur ou destructeur par dfaut.
Appeler le constructeur par dfaut
quoi peut bien servir un constructeur qui ne fait rien ? Cest en partie une question de
forme. Tous les objets doivent tre "construits" puis "dtruits" et ces fonctions "inoprantes"
sont appeles dans le cadre du processus de construction et de destruction.
Pour dclarer un objet sans passer de paramtres, comme ici :
Chat Felix; // Felix ne prend pas de paramtres
vous devez avoir un constructeur de la forme :
Chat();
Lorsque vous dnissez un objet dune classe, le constructeur est appel. Si le constructeur de
Chat attend deux paramtres, la dnition dun objet Chat sera de la forme :
Chat Frisky(5, 7);
C++Livre Page 152 J eudi, 7. mai 2009 9:45 09
Chapitre 6 Programmation oriente objet 153
Dans cet exemple, le premier paramtre pourrait tre son ge et le second son poids. Si le
constructeur attend un seul paramtre, vous crirez :
Chat Frisky(3);
Si le constructeur ne prend pas de paramtres du tout (il sagit alors du constructeur par
dfaut), supprimez les parenthses et crivez :
Chat Frisky;
Cest donc une exception la rgle qui veut que toutes les fonctions doivent tre suivies de
parenthses, mme celles qui ne prennent pas de paramtres. Vous pouvez donc crire :
Chat Frisky;
car cela sera interprt comme un appel au constructeur par dfaut. Il ne comprend ni
paramtres ni parenthses.
Vous ntes pas oblig de vous contenter du constructeur par dfaut produit par le compi-
lateur : vous pouvez crire le vtre, cest--dire un constructeur sans paramtres. Le corps
de ce constructeur par dfaut peut ainsi contenir du code vous permettant dinitialiser
lobjet comme vous le souhaitez. En ralit, il est toujours conseill de dnir un construc-
teur et dinitialiser les variables membres avec des valeurs par dfaut appropries an que
lobjet se comporte toujours correctement.
Lorsque vous dclarez un constructeur, noubliez non plus pas de dnir un destructeur
mme sil ne fait rien. Bien que celui fourni par dfaut par le compilateur fonctionnera
correctement, il est prfrable de le dnir soi-mme an de rendre le code plus clair.
Le Listing 6.4 rcrit la classe Chat pour quelle utilise un constructeur qui nest pas celui
par dfaut et qui se charge dinitialiser lge de lobjet avec la valeur quon lui a transmis.
Lappel du destructeur est galement clairement indiqu.
Listing 6.4 : Utilisation de constructeurs et de destructeurs
1: // Dclaration de constructeurs et de
2: // destructeurs pour la classe Chat
3: // Le programmeur cre un constructeur par dfaut
4: #include <iostream> // pour cout
5:
6: class Chat // dbut de la dclaration de la classe
7: {
8: public: // dbut de section publique
9: Chat(int initialAge); // constructeur
10: ~Chat(); // destructeur
11: int LireAge(); // fonction daccs
12: void DefAge(int age); // fonction daccs
C++Livre Page 153 J eudi, 7. mai 2009 9:45 09
154 Le langage C++
13: void Miauler();
14: private: // dbut de section prive
15: int sonAge; // variable membre
16: };
17:
18: // constructeur de Chat,
19: Chat::Chat(int initialAge)
20: {
21: sonAge = initialAge;
22: }
23:
24: Chat::~Chat() // destructeur, ne fait rien
25: {
26: }
27:
28: // LireAge, fonction daccs publique
29: // renvoie la valeur du membre sonAge
30: int Chat::LireAge()
31: {
32: return sonAge;
33: }
34:
35: // Dfinition de DefAge,
36: // fonction daccs publique
37: void Chat::DefAge(int age)
38: {
39: // affecter la variable membre sonAge
40: // la valeur passe par le paramtre age
41: sonAge = age;
42: }
43:
44: // dfinition de la mthode Miauler
45: // renvoie: void
46: // paramtres: Aucun
47: // rle: imprime "Miaou" lcran
48: void Chat::Miauler()
49: {
50: std::cout << "Miaou.\n";
51: }
52:
53: // Crer un objet chat, dfinir son ge, le faire miauler,
54: // afficher son ge, le faire miauler de nouveau.
55: int main()
56: {
57: Chat Frisky(5);
58: Frisky.Miauler();
59: std::cout << "Frisky est un chat qui a ";
C++Livre Page 154 J eudi, 7. mai 2009 9:45 09
Chapitre 6 Programmation oriente objet 155
60: std::cout << Frisky.LireAge() << " ans.\n";
61: Frisky.Miauler();
62: Frisky.DefAge(7);
63: std::cout << "Maintenant Frisky a ";
64: std::cout << Frisky.LireAge() << " ans.\n";
65: return 0;
66: }
Ce programme produit le rsultat suivant :
Miaou.
Frisky est un chat qui a 5 ans.
Miaou.
Maintenant Frisky a 7 ans.
Le Listing 6.4 ressemble trangement au listing prcdent. la ligne 9, pourtant, le
constructeur prend en paramtre un entier, tandis que la ligne 10 dclare le destructeur, qui
ne prend pas de paramtre. Les destructeurs ne prennent jamais de paramtres et, comme
les constructeurs, ils ne renvoient aucune valeur (pas mme void).
Les lignes 19 22 montrent limplmentation du constructeur, qui ressemble celle de la
mthode daccs DefAge(). Comme vous pouvez le constater, le nom de classe prcde
le nom du constructeur, ce qui identie la mthode, Chat() ici, comme une partie de la
classe Chat. Cest un constructeur, qui ne renvoie donc pas de valeur, mme pas void.
la ligne 21, il prend toutefois une valeur en paramtre qui est affecte la donne membre
sonAge.
Les lignes 24 26 dcrivent limplmentation du destructeur ~Chat(). Pour linstant,
cette fonction ne fait rien mais vous devez inclure sa dnition si vous lavez dclare dans
la dclaration de la classe. Comme le constructeur et les autres mthodes, elle est galement
prcde du nom de la classe.
La ligne 57 dnit lobjet Chat Frisky en passant la valeur 5 au constructeur de cet objet.
Il est inutile dappeler DefAge() car Frisky a t cr avec la valeur 5 qui a t affecte
la variable membre sonAge la ligne 60. la ligne 62, cette variable est modie pour
valoir 7. Cest cette valeur qui safche lcran deux lignes plus loin.
Faire Ne pas faire
Initialiser des objets laide de constructeurs.
Ajouter un destructeur si vous ajoutez un
constructeur.
Attribuer un type de rsultat aux construc-
teurs et aux destructeurs.
Attribuer des paramtres aux destructeurs.
C++Livre Page 155 J eudi, 7. mai 2009 9:45 09
156 Le langage C++
Inclure des fonctions membres const
Vous avez dj employ le mot-cl const pour dclarer des variables qui ne changeront
pas. Vous pouvez galement lutiliser avec les fonctions membres dune classe. Lorsque
lon dclare une mthode const, on sengage ce que cette mthode ne modie jamais
aucun membre de la classe.
Pour dclarer une mthode const, il suft dintercaler le mot-cl entre la liste des paramtres
et le point-virgule qui termine la dclaration de la mthode. Par exemple :
void Fonction() const;
Cette ligne dclare une mthode membre constante appele Fonction() qui ne prend pas
de paramtre et qui ne renvoie rien. Vous savez quelle ne changera aucune des donnes
membres de la mme classe car elle a t dclare const.
Les mthodes daccs qui ne font que renvoyer des valeurs sont souvent dclares constantes.
Nous avons vu plus haut que la classe Chat contenait deux fonctions daccs :
void DefAge(int Age);
int LireAge();
La fonction DefAge() ne peut pas tre constante puisquelle modie la variable membre
sonAge. En revanche, LireAge() peut et devrait tre constante car elle ne modie pas du
tout les objets de la classe : elle ne fait que renvoyer la valeur de sonAge. Il serait donc
prfrable de dclarer ces fonctions sous la forme suivante :
void DefAge(int Age);
int LireAge() const;
Si une fonction modie la valeur de lun des membres dun objet alors quelle est dclare
comme constante, le compilateur produira une erreur. Si, par exemple, la fonction
LireAge() devait mmoriser le nombre de fois o un chat a d dire son ge, une erreur de
compilation serait produite, puisque vous modieriez alors lobjet Chat lorsque cette
mthode serait appele.
Les mthodes constantes sont trs intressantes puisquelles permettent au compilateur de
dtecter les erreurs avant lexcution du programme. Cest pourquoi il est souhaitable den
dclarer aussi souvent que possible dans les applications.
Interface et implmentation
On appelle client toute partie dun programme qui cre et utilise les objets dune classe.
Linterface publique (la dclaration de la classe) peut tre considre comme le contrat qui
la lie aux clients. Ce contrat prcise le comportement de la classe.
C++Livre Page 156 J eudi, 7. mai 2009 9:45 09
Chapitre 6 Programmation oriente objet 157
La dclaration de la classe Chat, par exemple, indique que lge de chaque chat peut tre
initialis dans son constructeur, modi par sa fonction daccs DefAge(), puis lu par sa
fonction daccs LireAge(). Vous promettez galement que chaque chat saura miauler
grce la fonction Miauler(). Notez que vous ne dites rien de la variable membre
sonAge dans linterface publique ; il sagit dun dtail dimplmentation qui ne fait pas
partie de votre contrat. Vous fournirez un ge (LireAge()) et le dnirez (DefAge()),
mais le mcanisme (sonAge) reste invisible.
Si vous dnissez LireAge() comme une fonction constante, ce qui est souhaitable, le
contrat stipule galement que son appel ne modiera pas lobjet Chat sur lequel elle est
appele.
C++ tant fortement typ, le compilateur fera respecter ces contrats en produisant une
erreur chaque fois que le programme tentera de les violer. Le Listing 6.5 montre un
programme qui ne se compilera pas cause de ses violations de contrat.
La compilation du Listing 6.5 chouera !
Listing 6.5 : Programme contenant plusieurs erreurs de compilation
1: // Dmonstration derreurs de compilation
2: // La compilation chouera!
3: #include <iostream> // pour cout
4:
5: class Chat
6: {
7: public:
8: Chat(int initialAge);
9: ~Chat();
10: int LireAge() const; // fonction daccs constante
11: void DefAge (int age);
12: void Miauler();
13: private:
14: int sonAge;
15: };
16:
17: // constructeur de Chat,
18: Chat::Chat(int initialAge)
19: {
20: sonAge = initialAge;
21: std::cout << "Constructeur de Chat \n";
22: }
23:
A
tte
n
tio
n
C++Livre Page 157 J eudi, 7. mai 2009 9:45 09
158 Le langage C++
23a: // destructeur, ne ralise aucune action
24: Chat::~Chat()
25: {
26: std::cout << "Destructeur de Chat \n";
27: }
28: // LireAge, fonction constante
29: //, mais il y a violation!
30: int Chat::LireAge() const
31: {
32: return (sonAge++); // violation de const!
33: }
34:
35: // dfinition de DefAge,
36: // fonction daccs publique
37:
38: void Chat::DefAge(int age)
39: {
40: // dfinir la variable membre sonAge la
41: // valeur passe par le paramtre age
42: sonAge = age;
43: }
44:
45: // dfinition de la mthode Miauler
46: // renvoie: void
47: // paramtres: Aucun
48: // rle: Affiche "Miaou" lcran
49: void Chat::Miauler()
50: {
51: std::cout << "Miaou.\n";
52: }
53:
54: // dmonstration des diffrentes violations de
55: // linterface, et erreurs de compilation correspondantes
56: int main()
57: {
58: Chat Frisky; // ne correspond pas la dclaration
59: Frisky.Miauler();
60: Frisky.Aboyer(); // la mthode nest pas dclare
61: Frisky.sonAge = 7; // sonAge est prive
62: return 0;
63: }
Le programme ne produit pas de rsultat puisquil nest pas compil.
La ligne 10 dclare LireAge() comme une fonction daccs constante alors quelle incr-
mente la variable membre sonAge la ligne 32. Le compilateur a dtect une erreur et la
signale.
C++Livre Page 158 J eudi, 7. mai 2009 9:45 09
Chapitre 6 Programmation oriente objet 159
Miauler() nest pas dclare comme une mthode constante en ligne 12, ce qui en soi ne
constitue pas une erreur mais est une mauvaise habitude de la part du programmeur.
En effet, cette fonction ne doit en rien affecter les variables membres de la classe
Chat. Cest pourquoi il est prfrable de la dclarer comme mthode constante.
Lobjet Frisky de la classe Chat est cr la ligne 58. Le constructeur associ comprend
un paramtre entier, or on nen passe aucun : le compilateur dtecte une erreur.
Si vous fournissez ne serait-ce quun constructeur, le compilateur nen crera
pas. Cela signie que si vous crez quelque part un constructeur avec un para-
mtre, vous naurez pas de constructeur par dfaut automatique et vous devrez
lcrire vous-mme.
La ligne 60 appelle la mthode Aboyer(). Or, celle-ci na pas t dclare.
La variable sonAge reoit la valeur 7 la ligne 61, cest--dire dans le corps du
programme, ce qui constitue une erreur puisquil sagit dune donne membre prive.
Pourquoi dtecter des erreurs laide du compilateur ?
Il est illusoire desprer crire du code sans erreurs. Toutefois, il est possible den viter un
certain nombre, en les dtectant et en les rsolvant ds le dbut du processus.
Les erreurs de compilation sont la bte noire des programmeurs. Pourtant, leur dtection
rvle la qualit du compilateur. En effet, un langage faiblement typ permet deffectuer
des oprations non autorises, ce qui produira des erreurs au moment de lexcution. Pire
encore, le test napportera que peu daide pour dcouvrir les erreurs, car il existe trop de
possibilits dans les vrais programmes pour avoir lespoir de les tester tous.
Les erreurs de compilation sont prfrables aux erreurs dexcution, car elles peuvent tre
plus aisment repres et corriges. On reconnat la qualit dun programmeur sa capa-
cit dvelopper des applications exemptes derreurs dexcution. Vous ntes pas infailli-
ble. Cest pourquoi le compilateur est charg de vrier la cohrence du code avant de
crer le chier excutable.
O placer les dclarations de classes
et les dnitions de mthodes ?
Toute fonction dclare dans une classe doit possder une dnition, que lon appelle
galement implmentation de la fonction. Comme pour les autres fonctions, la dnition
dune mthode de classe comprend un en-tte de fonction et un corps de fonction.
In
f
o
C++Livre Page 159 J eudi, 7. mai 2009 9:45 09
160 Le langage C++
La dnition doit se trouver dans un chier dont le chemin daccs doit tre connu du
compilateur. La plupart des compilateurs exigent que ce chier se termine par lextension
.c ou .cpp cest cette dernire qui a t adopte dans ce livre. Pour connatre les exten-
sions prises en charge par votre compilateur, reportez-vous sa documentation technique.
De nombreux compilateurs considrent que les chiers dont les noms se termi-
nent par .c sont des chiers sources C et que ceux qui se terminent par .cpp
sont des chiers sources C++. Vous pouvez utiliser lune ou lautre de ces
extension, mais lutilisation de .cpp rduira les confusions possibles.
Il est possible de placer galement la dclaration dans ce chier, bien que cette pratique
soit peu recommande en POO. Par convention, la plupart des programmeurs crivent la
dclaration dans un chier en-tte portant le mme nom que le chier source, mais suivi
de lextension .h, .hp ou .hpp. Dans cet ouvrage, nous avons opt pour la dernire.
Au besoin, consultez la documentation de votre compilateur.
Par exemple, la classe Chat est dclare dans le chier en-tte Chat.hpp et les dnition
des mthodes se trouvent dans le chier Chat.cpp. Pour associer le chier en-tte au
chier .cpp, ajoutez linstruction suivante au dbut de Chat.cpp :
#include "Chat.hpp"
Le compilateur insrera alors le code du chier Chat.hpp comme sil avait t saisi dans
le chier source. Notez que certains compilateurs exigent que la casse des caractres soit
la mme dans linstruction #include et dans le systme de chiers.
Pourquoi sparer le contenu de votre chier .hpp et de votre chier .cpp pour nalement
les runir ? Le plus souvent, les clients de votre classe ne se soucient pas des dtails
dimplmentation. La lecture du chier den-tte leur fournit toutes les informations dont
ils ont besoin. En outre, vous pouvez galement inclure le mme chier .hpp dans
plusieurs chiers .cpp.
La dclaration dune classe est appele interface, parce quelle contient un certain
nombre dinformations prcieuses pour lutilisateur. Il sagit, entre autres, de la
structure de la classe, du type des donnes utilises et des noms des fonctions appe-
les. Linterface gure gnralement dans un chier .hpp, plus connu sous le nom
de chier en-tte.
La dnition dune fonction renseigne le compilateur sur le comportement de cette
dernire. Elle est appele implmentation de la mthode et gure dans un chier
.cpp. Par ailleurs, les dtails de limplmentation ne concernent que le crateur
de la classe ; les clients de la classe (les parties du programme qui lutilisent) nont
pas besoin de connatre la faon dont les mthodes sont implmentes.
In
f
o
In
f
o
C++Livre Page 160 J eudi, 7. mai 2009 9:45 09
Chapitre 6 Programmation oriente objet 161
Implmentation en ligne
Comme les fonctions classiques, les mthodes des classes peuvent tre dclares en ligne.
Dans ce cas, le mot-cl inline doit gurer avant le type du rsultat. Voici un exemple,
avec la fonction LirePoids() :
inline int Chat::LirePoids()
{
return sonPoids; // renvoie la donne membre Poids
}
Il est galement possible de dnir une fonction dans la dclaration de la classe, ce qui la
dnit automatiquement en ligne. Exemple :
class Chat
{
public:
int LirePoids() const { return sonPoids; } // en ligne
void DefPoids(int unPoids);
};
Notez la syntaxe de la dnition de LirePoids(). Le corps de la fonction en ligne
commence immdiatement aprs la dclaration de la mthode ; il ny a pas de point-
virgule aprs les parenthses. Comme pour nimporte quelle fonction, la dnition est
comprise entre une accolade ouvrante et une accolade fermante. Les tabulations et les
espaces ninuant pas sur son droulement, la classe pourrait tre dnie ainsi :
class Chat
{
public:
int LirePoids() const
{
return sonPoids;
} // en ligne
void DefPoids(int unPoids);
};
Les Listings 6.6 et 6.7 recrent la classe Chat, mais la dclaration se trouve dsormais
dans le chier en-tte Chat.hpp et limplmentation des fonctions dans le chier
Chat.cpp. Notez que les fonctions daccs et la fonction Miauler() sont dclares en
ligne dans le Listing 6.7.
C++Livre Page 161 J eudi, 7. mai 2009 9:45 09
162 Le langage C++
Listing 6.6 : Dclaration de la classe Chat dans le chier Chat.hpp
1: #include <iostream>
2: class Chat
3: {
4: public:
5: Chat (int ageInitial);
6: ~Chat();
6a: // les trois fonctions suivantes sont en ligne
7: int LireAge() const { return sonAge;}
8: void DefAge (int age) { sonAge = age;}
9: void Miauler() const { std::cout << "Miaou.\n";}
10: private:
11: int sonAge;
12: };
Listing 6.7 : Implmentation de la classe Chat dans le chier Chat.CPP
1: // Dmonstration de fonctions en ligne
2: // et inclusion de fichiers en-tte
3: // Noubliez pas dinclure le fichier en-tte!
4: #include "Chat.hpp"
5:
6:
7: Chat::Chat(int ageInitial) // constructeur
8: {
9: sonAge = ageInitial;
10: }
11:
12: Chat::~Chat() // destructeur, ne fait rien
13: {
14: }
15:
16: // Crer un chat, dfinir son ge, le faire miauler,
17: // afficher son ge, le faire miauler de nouveau.
18: int main()
19: {
20: Chat Frisky(5);
21: Frisky.Miauler();
22: std::cout << "Frisky est un chat qui a ";
23: std::cout << Frisky.LireAge() << " ans.\n";
24: Frisky.Miauler();
25: Frisky.DefAge(7);
26: std::cout << "Maintenant Frisky a ";
27: std::cout << Frisky.LireAge() << " ans.\n";
28: return 0;
29: }
C++Livre Page 162 J eudi, 7. mai 2009 9:45 09
Chapitre 6 Programmation oriente objet 163
Ce programme produit le rsultat suivant :
Miaou.
Frisky est un chat qui a 5 ans.
Miaou.
Maintenant, Frisky a 7 ans.
Le code prsent dans les deux listings ci-dessus ressemble trangement celui du
Listing 6.4 mais on a dclar ici trois mthodes en ligne dans le chier en-tte Chat.hpp
(Listing 6.6).
La fonction LireAge() est dclare et implmente en ligne la ligne 6 de Chat.hpp,.
Aux lignes 7 et 8, les autres fonctions sont galement dclares en ligne. Leur comportement
na cependant pas t modi.
La ligne 4 de Chat.cpp (Listing 6.7) contient la directive #include "Chat.hpp" qui
inclut le contenu du chier Chat.hpp dans le chier source. Le prcompilateur va donc
inclure le chier Chat.hpp comme si son code avait t tap partir de la ligne 5 de ce
programme.
Cette technique permet de rpartir les dclarations et limplmentation dans des chiers
spars tout en les rendant disponibles pour le compilateur, ce qui est une pratique trs
courante en C++. Les dclarations de classe sont gnralement enregistres dans un chier
.hpp qui est ensuite inclus dans le chier .cpp associ.
Les lignes 18 29 reproduisent la fonction main() du Listing 6.4, ce qui montre quavoir
mis ces fonctions en ligne ne modie pas leurs fonctionnement.
Classes imbriques
Il nest pas rare de crer une classe complexe en dclarant plusieurs classes simples et en
les incluant dans la dclaration de la classe complexe. Vous pouvez, par exemple, dclarer
une classe Roue, une classe Moteur, une classe Transmission, etc., puis les combiner
dans la classe Voiture. Ce processus tablit une relation "a-un" ou relation dappartenance.
Une voiture a un moteur, elle a des roues et elle a une transmission.
Prenons un deuxime exemple. Un rectangle est form de lignes, chaque ligne tant d-
nie par deux points. Dans lespace, un point est identi laide dune coordonne x et
dune coordonne y. Le Listing 6.8 contient la dclaration complte dune classe Rectan-
gle, enregistre dans le chier Rectangle.hpp. Un rectangle tant dni par quatre lignes
traces entre quatre points dintersection et chaque point correspondant une position
dans un graphique, il faut dclarer en premier la classe Point destine recevoir les coor-
donnes x et y de chaque point. Les deux classes sont implmentes dans le Listing 6.9.
C++Livre Page 163 J eudi, 7. mai 2009 9:45 09
164 Le langage C++
Listing 6.8 : Dclaration complte dune classe
1: // dbut de Rectangle.hpp
2: #include <iostream>
3: class Point // contient les coordonnes x,y
4: {
5: // pas de constructeur, on utilise celui par dfaut
6: public:
7: void SetX(int x) { sonX = x; }
8: void SetY(int y) { sonY = y; }
9: int GetX()const { return sonX;}
10: int GetY()const { return sonY;}
11: private:
12: int sonX;
13: int sonY;
14: }; // fin de la dclaration de la classe Point
15:
16:
17: class Rectangle
18: {
19: public:
20: Rectangle (int haut, int gauche, int bas, int droite);
21: ~Rectangle () {}
22:
23: int GetHaut() const { return sonHaut; }
24: int GetGauche() const { return saGauche; }
25: int GetBas() const { return sonBas; }
26: int GetDroite() const { return saDroite; }
27:
28: Point GetHautGauche() const { return sonHautGauche ; }
29: Point GetBasGauche() const { return sonBasGauche; }
30: Point GetHautDroite() const { return sonHautDroite; }
31: Point GetBasDroite() const { return sonBasDroite; }
32:
33: void SetHautGauche (Point pt) { sonHautGauche = pt; }
34: void SetBasGauche (Point pt) { sonBasGauche= pt; }
35: void SetHautDroite (Point pt) { sonHautDroite= pt; }
36: void SetBasDroite (Point pt) { sonBasDroite= pt; }
37:
38: void SetHaut(int haut) { sonHaut = haut; }
39: void SetGauche (int gauche) { saGauche = gauche; }
40: void SetBas (int bas) { sonBas = bas; }
41: void SetDroite(int droite) { saDroite = droite; }
42:
43: int GetSurface() const;
44:
45: private:
C++Livre Page 164 J eudi, 7. mai 2009 9:45 09
Chapitre 6 Programmation oriente objet 165
46: Point sonHautGauche;
47: Point sonHautDroite;
48: Point sonBasGauche;
49: Point sonBasDroite;
50: int sonHaut;
51: int saGauche;
52: int sonBas;
53: int saDroite;
54: };
55: // fin de Rectangle.hpp
Listing 6.9 : Rect.cpp
1: // dbut de Rect.cpp
2: #include "Rectangle.hpp"
3: Rectangle::Rectangle(int haut, int gauche, int bas,
3a: int droite)
4: {
5: sonHaut = haut;
6: saGauche = gauche;
7: sonBas = bas;
8: sonBas = droite;
9:
10: sonHautGauche.SetX(gauche);
11: sonHautGauche.SetY(haut);
12:
13: sonHautDroite.SetX(droite);
14: sonHautDroite.SetY(haut);
15:
16: sonBasGauche.SetX(gauche);
17: sonBasGauche.SetY(bas);
18:
19: sonBasDroite.SetX(droite);
20: sonBasDroite.SetY(bas);
21: }
22:
23:
24: // calculer la surface du rectangle en trouvant les angles,
25: // dterminer la longueur et la largeur et multiplier
26: int Rectangle::GetSurface() const
27: {
28: int Largeur = saDroite - saGauche;
29: int Hauteur = sonHaut - sonBas;
30: return (Largeur * Hauteur);
31: }
32:
33: int main()
C++Livre Page 165 J eudi, 7. mai 2009 9:45 09
166 Le langage C++
34: {
35: // initialiser une variable locale Rectangle
36: Rectangle MonRectangle (100, 20, 50, 80 );
37:
38: int Surface = MonRectangle.GetSurface();
39:
40: std::cout << "Surface: " << Surface << "\n";
41: std::cout << "Coordonne X coin suprieur gauche: ";
42: std::cout << MonRectangle.GetHautGauche().GetX();
43: return 0;
44: }
Ce programme produit le rsultat suivant :
Surface: 3000
Coordonne X coin suprieur gauche: 20
Les lignes 3 14 de Rectangle.hpp (Listing 6.8) dclarent la classe Point ; cest elle qui
va recevoir les coordonnes x et y. Comme vous pouvez le constater, ce programme ne
fait gure appel des objets Point ; cependant, ceux-ci peuvent servir dautres mthodes
de trac.
Certains compilateurs signalent une erreur si vous dclarez une classe appele
Rectangle. Ceci est gnralement d lexistence dune classe interne
nomme Rectangle. Dans ce cas, renommez simplement la classe monRectangle,
par exemple
Les variables membres sonX et sonY sont dnies la ligne 12 et la ligne 13. mesure
que la valeur de la coordonne x augmente, on se dplace vers la droite et on se dplace
vers le haut lorsque la valeur de la coordonne y augmente. Bien entendu, il est possible
dutiliser un autre systme : dans certains programmes de fentrage, la fentre se dplace
vers le bas mesure que la coordonne y augmente, par exemple.
Pour lire et dnir les points x et y, la classe Point fait appel aux fonctions daccs en
ligne dclares aux lignes 7 10. En outre, elle utilise le constructeur et le destructeur par
dfaut, ce qui signie que vous devez dnir les coordonnes explicitement.
La ligne 17 commence la dclaration de la classe Rectangle. Les quatre angles du rectangle
sont alors dnis.
Le constructeur de Rectangle prend en charge quatre entiers appels respectivement
haut, gauche, bas et droite. Ces paramtres sont affects aux quatre variables membres
(voir le Listing 6.9) an de dnir le rectangle.
In
f
o
C++Livre Page 166 J eudi, 7. mai 2009 9:45 09
Chapitre 6 Programmation oriente objet 167
Outre les fonctions daccs dcrites plus haut, la classe Rectangle possde une fonction
GetSurface() (voir ligne 43). La surface nest pas stocke dans une variable, mais calcule
aux lignes 28 et 29 du Listing 6.9. Pour cela, la fonction GetSurface() dtermine la
hauteur et la largeur du rectangle, puis multiplie ces deux valeurs.
Pour extraire la coordonne x de langle suprieur gauche du rectangle, vous devez lire la
valeur x du point HautGauche. La fonction GetHautGauche() tant une mthode de
Rectangle, elle a accs directement aux donnes prives, parmi lesquelles sonHautGau-
che. Cette valeur tant un Point et la valeur de sonX tant prive, elle ne peut pas y acc-
der directement et doit faire appel la fonction daccs publique GetX() pour obtenir cette
coordonne.
Le programme commence rellement la ligne 33 du Listing 6.9,. Vous pouvez constater
que la mmoire nest pas alloue avant la ligne 36. Pour le moment, le compilateur sait
crer un point et un rectangle, au cas o il en aurait besoin.
la ligne 36, on dnit un rectangle en passant les valeurs pour les quatre paramtres
haut, gauche, bas et droite de son constructeur.
la ligne 38, le programme cre la variable locale Surface, de type int. Elle reoit la
valeur de la surface, renvoye par la fonction GetSurface() de la classe Rectangle. Un
client de la classe Rectangle pourrait crer un objet Rectangle et dterminer sa surface
sans connatre limplmentation de la mthode LireSurface().
Le chier Rectangle.hpp est prsent dans le Listing 6.8. En examinant simplement la
dclaration de la classe Rectangle, le programmeur sait que GetSurface() renvoie un
entier. Le calcul de la surface ne concerne pas lutilisateur de cette classe. En fait, lauteur
de Rectangle pourrait modier le corps de la mthode GetSurface() sans inuer sur les
programmes qui utilise cette classe, du moment quelle renvoie un entier.
La ligne 42 du Listing 6.9 peut sembler trange, mais rchissez ce qui se passe. Cette
ligne de code fournit les coordonnes x du point suprieur gauche de votre rectangle. Dans
cette ligne, vous appelez la mthode GetHautGauche() de votre rectangle, qui vous
renvoie un Point. Vous voulez obtenir les coordonnes x partir de ce Point. Vous avez
vu que la mthode daccs pour une coordonne x dans la classe Pointsappelle GetX().
La ligne 42 rassemble donc simplement les mthodes daccs GetHautGauche() et
GetX() :
MonRectangle.GethautGauche().GetX();
Cet appel permet dobtenir la coordonne x du point suprieur gauche de lobjet MonRec-
tangle.
C++Livre Page 167 J eudi, 7. mai 2009 9:45 09
168 Le langage C++
Quelle est la diffrence entre une dclaration et une dnition ?
Une dclaration introduit un nom, mais ne rserve pas demplacement en mmoire. Une
dnition alloue la mmoire correspondante.
quelques rares exceptions prs, toutes les dclarations sont aussi des dnitions. Les
exceptions les plus notables sont la dclaration dune fonction globale (un prototype) et
la dclaration dune classe (gnralement dans un chier en-tte).
Les structures
Un proche cousin du mot-cl class est le mot-cl struct qui permet de dclarer une
structure. En C++, une structure est une classe dont tous les membres sont publics par
dfaut. Une dclaration de structure comprend des donnes membres et des fonctions,
comme une dclaration de classe. En fait, si vous respectez les bonnes habitudes de
programmation en dclarant toujours explicitement chaque section de classe laide des
mots-cls public et private, il ny aura aucune diffrence entre les deux.
Modiez le Listing 6.8 :
la ligne 3, remplacez class Point par struct Point.
la ligne 17, remplacez class Rectangle par struct Rectangle.
Lancez nouveau le programme et comparez les rsultats obtenus. Il ne devrait pas y avoir
de diffrence.
Vous vous demandez alors peut-tre pourquoi il existe deux mots cls pour la mme chose.
Il sagit dun accident de lhistoire. C++ a t construit comme une extension du langage
C, qui dispose de structures, bien quelles ne puissent pas contenir de mthodes. Bjarne
Stroustrup, le crateur de C++, a utilis le mot-cl class pour dsigner ces structures
tendues et la nouvelle visibilit par dfaut de leurs membres. Cela a galement permis de
continuer utiliser la vaste bibliothque des fonctions C dans les programmes C++.
Faire Ne pas faire
Dclarer la classe dans un chier en-tte
(.hpp) et les fonctions membres dans le
chier source du programme (.cpp).
Utiliser le mot-cl const le plus souvent
possible.
Poursuivre sans avoir compris le fonctionne-
ment des classes.
C++Livre Page 168 J eudi, 7. mai 2009 9:45 09
Chapitre 6 Programmation oriente objet 169
Questions-rponses
Q Quelle est la taille dun objet de la classe ?
R Elle est dtermine par la somme des tailles des diffrentes variables membres.Les
mthodes noccupent quun petit espace mmoire, utilis pour conserver des informations
sur lemplacement de la mthode (un pointeur).
Certains compilateurs alignent les variables en mmoire de sorte que des variables de
deux octets consomment en ralit un espace suprieur. Pour en savoir plus sur la gestion
de la mmoire, reportez-vous au manuel utilisateur de votre compilateur.
Q Si je dclare une classe Chat comprenant un membre priv sonAge, puis que je
dnis deux objets de cette classe, Frisky et Mistigri. Mistigri a-t-il accs la
variable membre sonAge de Frisky ?
R Les diffrentes instances de la classe peuvent accder aux donnes non publiques de
lautre, pas leurs membres privs. Frisky et Mistrigri tant toutes les deux des ins-
tances de Chat, les fonctions membres de Frisky peuvent accder aux donnes de
Frisky, mais pas celles de Mistigri.
Q Pourquoi ne pas rendre toutes les donnes membres publiques ?
R Les donnes membres prives sont propres une classe, ce qui rend leur traitement et
leur stockage totalement transparents pour les clients. Par exemple, les clients de la
classe Chat nont pas accs directement limplmentation de la mthode LireAge().
Peu importe le traitement effectu, lge du chat sera rcupr. Le programmeur de la
classe Chat peut changer ensuite son implmentation sans obliger tous les utilisateurs
modier aussi leurs programmes.
Q Si lutilisation dune fonction const pour modier une classe produit une erreur
de compilation. Pourquoi ne pas supprimer ce mot-cl pour viter des erreurs ?
R Si votre fonction membre ne doit logiquement pas modier la classe laquelle elle
appartient, la prsence du mot-cl const permet de dtecter des erreurs. Par exemple,
LireAge() nest pas suppose modier la classe Chat. Or si limplmentation contient
la ligne suivante :
if (sonAge = 100) cout << "Tiens, vous tes centenaire!\n";
et que vous dnissez la fonction LireAge() comme constante, le compilateur dtec-
tera lerreur. En effet, linstruction if tait cense vrier si sonAge tait gal 100
alors que vous affectez en ralit 100 cette variable. Le compilateur a trouv lerreur,
car vous tentez de modier une mthode constante.
C++Livre Page 169 J eudi, 7. mai 2009 9:45 09
170 Le langage C++
Une erreur de ce type est difcile trouver en parcourant simplement les nombreuses
lignes dun chier source. Le programme peut mme sembler tourner correctement
alors que sonAge a reu une valeur incorrecte. Cela posera un problme tt ou tard.
Q Lutilisation des structures est-elle encore ncessaire en C++ ?
R Certains programmeurs C++ rservent le mot-cl struct aux classes dpourvues de
fonctions. Cette habitude, hrite du langage C, est proscrire car une structures d-
nie aujourdhui peut trs bien ncessiter lajout ultrieur de mthodes ; vous devriez
alors modier cette struct en class ou violer la rgle que vous vous tes x. La
seule bonne raison dutiliser une structure est lorsque vous devez appeler une fonction
C existante qui exige un struct particulier.
Q Certaines personnes travaillant avec la programmation oriente objet utilisent le
terme "instanciation". Que signie-t-il ?
R Linstanciation nest quun mot la mode dcrivant la cration dun objet partir
dune classe. Un objet spcique dni comme tant du type dune classe constitue
une instance de cette classe.
Testez vos connaissances
1. Quest-ce que loprateur point (.) ? quoi sert-il ?
2. Qui rserve de lespace en mmoire vive, la dclaration ou la dnition ?
3. La dclaration dune classe correspond-elle son interface ou son implmentation ?
4. Quelle est la diffrence entre des donnes membres prives et des donnes membres
publiques ?
5. Les fonctions membres peuvent-elles tre prives ?
6. Les donnes membres peuvent-elles tre publiques ?
7. Si vous dclarez deux objets Chat, leurs donnes membres sonAge peuvent-elles avoir
des contenus diffrents ?
8. Les dclarations de classe se terminent-elles par un point-virgule ? Et les dnitions
de mthodes de la classe ?
9. Quel serait len-tte de la fonction Miaou appartenant la classe Chat, ne prenant en
charge aucun paramtre et renvoyant void ?
10. Quelle est la fonction qui permet dinitialiser une classe ?
C++Livre Page 170 J eudi, 7. mai 2009 9:45 09
Chapitre 6 Programmation oriente objet 171
Exercices
1. crivez la dclaration de la classe Personnel contenant les donnes membres sonAge,
sonAnciennete et sonSalaire.
2. Modiez la dclaration de la classe Personnel de sorte que ses donnes membres
soient prives et que des mthodes daccs puissent lire et mettre jour chacune
delles.
3. crivez un programme qui cre deux objets de la classe Personnel. Pour chacun,
dnissez lge, lanciennet et le salaire, puis afchez ces valeurs. Vous devrez aussi
ajouter le code des mthodes daccs.
4. En vous inspirant de lexercice prcdent, crivez le code dune mthode qui afche le
salaire de chaque employ, arrondi la centaine deuros la plus proche.
5. Modiez de nouveau la classe Personnel de faon initialiser les donnes membres
sonAge, sonAnciennete et sonSalaire lors de la cration dun objet.
6. CHERCHEZ LERREUR : pourquoi cette dclaration est-elle errone ?
class Carre
{
public:
int Cote;
}
7. CHERCHEZ LERREUR : pourquoi cette dclaration de classe nest-elle pas trs
utile ?
class Chat
{
int GetAge() const;
private:
int sonAge;
};
8. CHERCHEZ LERREUR : il y a trois erreurs dans ce code. O se cachent-elles ?
class TV
{
public:
void SetStation(int Station);
int GetStation() const;
private:
int saStation;
};
C++Livre Page 171 J eudi, 7. mai 2009 9:45 09
172 Le langage C++
int main()
{
TV maTele;
maTele.saStation = 9;
TV.SetStation(10);
TV monAutreTele(2);
}
C++Livre Page 172 J eudi, 7. mai 2009 9:45 09
7
Droulement
dun programme
Au sommaire de ce chapitre
La nature et le rle des boucles
La cration des diffrentes boucles
Une alternative aux instructions imbriques ifelse
Un programme repose principalement sur des branchements et des boucles. Au Chapitre 4,
vous avez appris comment le programme effectue un branchement laide de linstruction if.
Les boucles
En programmation, nombreux sont les traitements rptitifs, cest--dire qui traitent
plusieurs fois les mmes groupes de donnes. Ils peuvent sexcuter par rcursivit
(prsente au Chapitre 5) ou par itration. Litration consiste rpter plusieurs fois des
instructions pour traiter des informations. La principale mthode ditration est la boucle.
C++Livre Page 173 J eudi, 7. mai 2009 9:45 09
174 Le langage C++
Les origines des boucles : goto
Aux dbuts de linformatique, les programmes manquaient cruellement de convivialit.
Ils taient courts et directs. Les boucles taient formes dune tiquette, de quelques
instructions puis dun saut vers cette tiquette.
En C++, une tiquette est simplement un nom suivi du signe deux-points (:) plac
gauche dune instruction. Pour raliser un saut, il suft de taper le mot-cl goto suivi du
nom de ltiquette, comme le montre le Listing 7.1.
Listing 7.1 : Boucle laide du mot-cl goto
1: // Listing7.1
2: // Utilisation de goto
3: #include <iostream>
4:
5: int main()
6: {
7: using namespace std;
8: int compteur = 0; // initialiser le compteur
9: boucle:
10: compteur++; // dbut de la boucle
11: cout << "compteur: " << compteur << endl;
12: if (compteur < 5) // tester le compteur
13: goto boucle; // revenir au dbut
14:
15: cout << "Fin du traitement. Valeur du compteur: "
15a: << compteur << endl;
16: return 0;
17: }
Ce programme produit le rsultat suivant :
compteur: 1
compteur: 2
compteur: 3
compteur: 4
compteur: 5
Fin du traitement. Valeur du compteur: 5
La ligne 8 initialise le compteur zro. Une tiquette appele boucle marque le dbut de la
boucle (ligne 9). Le compteur est incrment et sa nouvelle valeur apparat lcran en
ligne 11. La ligne 12 teste la valeur du compteur : si elle est infrieure 5, la condition de
linstruction if est vrie et linstruction goto sexcute. Le programme revient sur
ltiquette boucle de la ligne 9 et litration continue jusqu ce que le compteur soit
C++Livre Page 174 J eudi, 7. mai 2009 9:45 09
Chapitre 7 Droulement dun programme 175
gal 5. En ce cas, le programme quitte la boucle et excute la ligne 13 qui indique que le
traitement est termin.
Pourquoi jeter linstruction goto aux oubliettes ?
Cette instruction est dcrie, juste titre, par les dveloppeurs. Elle permet deffectuer un
saut vers nimporte quelle instruction situe en amont ou en aval de la squence en cours.
A priori, cette fonctionnalit est trs pratique mais, en ralit, lorsquelle est utilise abusi-
vement, elle rend les programmes illisibles et impossibles maintenir car les sauts seffectuent
dans toutes les directions : on parle alors de code spaghetti.
Linstruction goto
Elle doit tre suivie du nom de ltiquette et elle provoque un saut inconditionnel vers
cette dernire.
Exemple
if (valeur > 10)
goto fin;
if (valeur < 10)
goto fin;
cout << "valeur = 10!";
fin:
cout << "Fin";
Dautres instructions de boucle plus sophistiques permettent dviter lusage de goto :
for, while et do...while.
Les boucles while
Une boucle while rpte une squence dinstructions tant que la condition de dpart est
vraie. Dans lexemple du Listing 7.1, le compteur tait incrment jusqu ce quil soit
gal 5. Pour cela, nous utilisions linstruction goto. Au Listing 7.2, le mme programme
a t optimis, laide de linstruction while.
Listing 7.2 : Boucles while
1: // Listing7.2
2: // Une boucle while
3: #include <iostream>
4:
C++Livre Page 175 J eudi, 7. mai 2009 9:45 09
176 Le langage C++
5: int main()
6: {
7: using namespace std;
8: int compteur = 0; // initialiser la condition
9:
10: while(compteur < 5) // condition toujours vraie?
11: {
12: compteur++; // corps de la boucle
13: cout << "Compteur: " << compteur << endl;
14: }
15:
16: cout << "Termin. Le compteur vaut: "
16a: << compteur << endl;
17: return 0;
18: }
Ce programme produit le rsultat suivant :
Compteur: 0
Compteur: 1
Compteur: 2
Compteur: 3
Compteur: 4
Compteur: 5
Termin. Le compteur vaut: 5
Ce programme illustre le fonctionnement dune boucle while. La ligne 8 initialise zro
la variable entire compteur qui est ensuite employe dans la condition de la boucle. La
condition est teste : si elle est vraie, linstruction gurant dans le corps de litration
sexcute. Dans cet exemple, le test est effectu la ligne 10. Si la valeur du compteur est
infrieure 5, lincrmentation a lieu la ligne 12 ; le rsultat est afch la ligne
suivante. Dans le cas contraire, le corps de la boucle (lignes 11 14) nest pas excut.
Le programme se branche directement sur la ligne 15.
Notez quil vaut toujours mieux utiliser des accolades autour du bloc excut par une
boucle, mme lorsquil ne contient quune ligne de code. Cela permet dviter lerreur
classique consistant placer par inadvertance un point-virgule juste aprs la condition, ce
qui cre une boucle sans n, comme ici :
int compteur = 0;
while ( compteur < 5 );
compteur++;
Dans cet exemple, compteur++ nest jamais excut.
C++Livre Page 176 J eudi, 7. mai 2009 9:45 09
Chapitre 7 Droulement dun programme 177
Linstruction while
Syntaxe de linstruction while :
while ( condition )
instruction;
condition est une expression C++ tandis que instruction correspond une instruction
ou un bloc dinstructions C++ valide. Lorsque la condition est vrie, linstruction
sexcute, puis la condition est de nouveau teste. Ce traitement se rpte jusqu ce que
la condition soit fausse. La boucle while prend alors n et le programme lit linstruction
situe immdiatement aprs linstruction ou le bloc dinstructions. Exemple :
// incrmentation du compteur jusqu 10
int x = 0;
while (x < 10)
cout << "X: " << x++;
Instructions while complexes
La condition teste dans une boucle while peut tre aussi complexe que nimporte quelle
expression C++ : elle peut donc contenir des oprateurs logiques comme && (ET), || (OU)
et ! (NON), comme le montre le Listing 7.3.
Listing 7.3 : Boucles while complexes
1: // Listing7.3
2: // Instructions while complexes
3: #include <iostream>
4:
5: int main()
6: {
7: using namespace std;
8: unsigned short petit;
9: unsigned long grand;
10: const unsigned short MAXPETIT = 65535;
11:
12: cout << "Entrez un petit nombre: ";
13: cin >> petit;
14: cout << "Entrez un grand nombre: ";
15: cin >> grand;
16:
17: cout << "Petit nombre: " << petit << "...";
18:
19: // pour chaque itration, tester deux conditions
C++Livre Page 177 J eudi, 7. mai 2009 9:45 09
178 Le langage C++
20: while (petit < grand && petit < MAXPETIT)
21: {
22: // crire un point chaque incrment de 5000
22a: if (petit % 5000 == 0)
23: cout << ".";
24:
25: petit++;
26: grand -= 2;
27: }
28:
29: cout << "\nPetit nombre: " << petit <<
29a: ", Grand nombre: " << grand << endl;
30: return 0;
31: }
Ce programme produit le rsultat suivant :
Entrez un petit nombre: 2
Entrez un grand nombre: 100000
Petit nombre: 2.........
Petit nombre: 33335, Grand nombre: 33334
Il sagit dun jeu. Entrez deux nombres, le premier tant infrieur au second. Le programme
incrmente de un point le petit nombre et dcrmente de deux points le grand nombre.
Lobjectif est de dterminer le moment auquel les deux valeurs se rejoignent.
La saisie a lieu de la ligne 12 la ligne 15. La ligne 20 dnit une boucle while qui
sexcute tant que les deux conditions suivantes sont vraies :
petit < grand (le petit nombre est infrieur au grand).
petit ne dpasse pas la taille maximale autorise pour un entier court (MAXPETIT).
La ligne 22 calcule la valeur de petit modulo 5 000. Ce calcul ne modie pas la variable,
mais extrait simplement le reste de la division. Sil est gal 0, petit est un multiple de
5 000 et un point (.) apparat lcran. La ligne 25 incrmente petit et la ligne suivante
dcrmente deux fois grand.
Si lune des deux conditions est fausse, la boucle while prend n et le programme se
poursuit aprs laccolade fermant la boucle la ligne 27.
Pour en savoir plus sur loprateur modulo (%) et les oprateurs logiques,
reportez-vous au Chapitre 3.
In
f
o
C++Livre Page 178 J eudi, 7. mai 2009 9:45 09
Chapitre 7 Droulement dun programme 179
Les instructions continue et break
Dans certains cas, il est souhaitable de retourner au dbut de la boucle avant que le bloc
dinstructions du corps de la boucle ne soit totalement excut. Linstruction continue
permet deffectuer cette opration.
Inversement, linstruction break permet de sortir de la boucle et dexcuter linstruction
situe immdiatement aprs laccolade fermante.
Nous utilisons ces instructions dans le Listing 7.4 car le jeu sest compliqu. Lutilisateur
saisit maintenant un petit nombre, un grand nombre, une valeur de saut et une valeur cible.
Comme dans le listing prcdent, le petit nombre est incrment de un point, alors que le
grand nombre est dcrment de deux points. La dcrmentation ne seffectuera pas lors-
que le petit nombre est un multiple de la valeur du saut. Le jeu sarrte lorsque petit est
suprieur grand. Si le grand nombre est gal la valeur cible, un message apparat
lcran et le programme se termine.
Lobjectif est de trouver une valeur cible pour le grand nombre, de sorte que le jeu sarrte.
Listing 7.4 : break et continue
1: // Listing7.4 - Instructions break et continue
2: #include <iostream>
3:
4: int main()
5: {
6: using namespace std;
7:
8: unsigned short petit;
9: unsigned long grand;
10: unsigned long saut;
11: unsigned long cible;
12: const unsigned short MAXPETIT = 65535;
13:
14: cout << "Entrez un petit nombre: ";
15: cin >> petit;
16: cout << "Entrez un grand nombre: ";
17: cin >> grand;
18: cout << "Entrez un nombre pour les sauts: ";
19: cin >> saut;
20: cout << "Entrez un nombre cible: ";
21: cin >> cible;
22:
23: cout << "\n";
24:
25: // configurer deux conditions darrt pour la boucle
C++Livre Page 179 J eudi, 7. mai 2009 9:45 09
180 Le langage C++
26: while (petit < grand && petit < MAXPETIT)
27: {
28: petit++;
29:
30: if (petit % saut == 0) // sauter la dcrmentation?
31: {
32: cout << "Saut " << petit << endl;
33: continue;
34: }
35:
36: if (grand == cible) // galit avec la cible?
37: {
38: cout << "Cible atteinte!";
39: break;
40: }
41:
42: grand -= 2;
43: } // fin de la boucle while
44:
45: cout << "\nPetit nombre: " << petit <<
45a: ", grand nombre: " << grand << endl;
46: return 0;
47: }
Ce programme produit le rsultat suivant :
Entrez un petit nombre: 2
Entrez un grand nombre: 20
Entrez un nombre pour les sauts: 4
Entrez un nombre cible: 6
Saut 4
Saut 8
Petit nombre: 10, grand nombre: 8
Dans cet exemple, lutilisateur a perdu, car petit a dpass la valeur de grand avant que
la valeur cible (6) ne soit atteinte.
La ligne 26 teste les conditions de litration. Si petit est infrieur grand mais quil
nest pas suprieur la valeur maximale autorise, le corps de la boucle sexcute.
La ligne 30 calcule le reste de la division de la valeur de petit par saut. Si petit est un
multiple de saut, linstruction continue sexcute et le traitement revient donc au dbut
de la boucle, en sautant le test de la cible et la dcrmentation de grand.
C++Livre Page 180 J eudi, 7. mai 2009 9:45 09
Chapitre 7 Droulement dun programme 181
La ligne 36 compare les valeurs de cible et de grand ; si elles sont gales, lutilisateur a
gagn. Le programme afche alors un message et linstruction break est atteinte et excute.
Elle provoque une sortie immdiate de la boucle et lexcution se poursuit la ligne 44.
Les instructions continue et break doivent tre utilises avec prcaution pour
les mmes raisons que linstruction goto. Les programmes qui changent bruta-
lement de direction sont plus difciles relire ; lutilisation exagre de
continue et break peut rendre illisible une boucle while, mme simple.
Le besoin de placer une instruction break dans une boucle indique souvent que
la condition de n de la boucle na pas t dnie avec lexpression boolenne
qui convient. Il vaut souvent mieux utiliser une instruction if pour sauter
certaines lignes plutt quune instruction break.
Linstruction continue
Linstruction continue; oblige le programme revenir au dbut dune boucle while,
dowhile ou for.
Le Listing 7.4 donne un exemple de son utilisation.
Linstruction break
break; provoque la sortie immdiate dune boucle while, dowhile ou for.
Exemple
while (condition)
{
if (condition2)
break;
// instructions;
}
Les boucles while (true)
La condition teste au dbut dune boucle while peut correspondre nimporte quelle
expression C++ valide. Tant quelle est vraie, litration se poursuit. Vous pouvez donc
crer une boucle sans n en utilisant la valeur boolenne true comme condition. Dans le
Listing 7.5, nous utilisons cette fonctionnalit pour incrmenter un compteur jusqu 10.
In
f
o
C++Livre Page 181 J eudi, 7. mai 2009 9:45 09
182 Le langage C++
Listing 7.5 : Exemple de boucle while (true)
1: // Listing7.5
2: // Dmonstration dune boucle while (true)
3: #include <iostream>
4:
5: int main()
6: {
7: int compteur = 0;
8:
9: while (true)
10: {
11: compteur++;
12: if (compteur > 10)
13: break;
14: }
15: std::cout << "Compteur: " << compteur << std::endl;
16: return 0;
17: }
Ce programme produit le rsultat suivant :
Compteur: 11
La ligne 9 congure la boucle while avec une condition qui ne pourra jamais tre fausse.
La variable compteur est incrmente la ligne 11, puis le programme vrie si sa valeur
est suprieure 10. Dans le cas contraire, la boucle sexcute nouveau. En revanche, si le
compteur est suprieur 10, linstruction break interrompt la boucle et va directement la
ligne 15. Le rsultat est alors afch.
Bien quil fonctionne correctement, ce programme est un bon exemple du mauvais choix
dun outil pour effectuer une tche. En effet, il est prfrable, dans ce cas, de tester la
valeur du compteur dans la condition while.
Les boucles sans n de type while(true) risquent de ger votre ordinateur si
la condition de sortie nest jamais atteinte. Il est donc conseill de les utiliser
avec prcaution et de les tester soigneusement.
Avec C++, vous pouvez raliser une mme tche de plusieurs faons. La vritable dif-
cult consiste choisir loutil adapt.
A
tte
n
tio
n
C++Livre Page 182 J eudi, 7. mai 2009 9:45 09
Chapitre 7 Droulement dun programme 183
Les boucles do...while
Le corps dune boucle while peut ne jamais sexcuter car la condition est vrie avant
tout traitement : si son rsultat est gal false, la squence dinstructions ne sera donc
pas traite, comme le montre le Listing 7.6.
Listing 7.6 : Cas o le traitement de la boucle while ne sexcute jamais
1: // Listing7.6
2: // Exemple de saut du corps de la boucle while
3: // si la condition est false.
4:
5: #include <iostream>
6:
7: int main()
8: {
9: int compteur;
10: std::cout << "Combien de bips voulez-vous afficher? ";
11: std::cin >> compteur;
12: while (compteur > 0)
13: {
14: std::cout << "Bip!\n";
15: compteur--;
16: }
17: std::cout << "Compteur la sortie de la boucle: "
17a: << compteur;
18: return 0;
19: }
Ce programme produit le rsultat suivant :
Combien de bips voulez-vous afficher? 2
Bip!
Bip!
Compteur la sortie de la boucle: 0
Combien de bips voulez-vous afficher? 0
Compteur la sortie de la boucle: 0
Faire Ne pas faire
Utiliser une boucle while pour rpter une
tche tant que la condition est vraie.
Utiliser les instructions break et continue
avec prcaution.
Vrier que litration peut prendre n.
Utiliser linstruction goto.
Oublier la diffrence entre continue et
break. continue revient au dbut, break va
la n.
C++Livre Page 183 J eudi, 7. mai 2009 9:45 09
184 Le langage C++
Lutilisateur entre une valeur de dpart (ligne 10) qui est copie dans la variable entire
compteur dont la valeur est teste la ligne 12, puis dcrmente dans le corps de la
boucle while. Dans le rsultat, vous pouvez constater qu la premire excution du
programme, lutilisateur a entr 2 et que le corps de la boucle sest excut deux fois. Dans
le second exemple, lutilisateur a tap 0. La valeur du compteur a t teste la ligne 12.
La condition tait fausse puisque compteur ntait pas suprieur 0. La squence
dinstructions ne sest donc pas excute et aucun bip ne sest afch lcran.
Comment faire pour afcher au moins un bip ? Vous pouvez placer une instruction if avant la
boucle while an daffecter une valeur minimale compteur an dentrer dans la boucle :
if (compteur < 1) // affecte une valeur minimale
compteur = 1;
Mais cette solution est un peu boiteuse et inlgante.
Linstruction do...while
Une boucle do...while garantit que les instructions de la boucle sexcuteront au moins une
fois, car le corps de la boucle est excut avant le test de la condition. Le code du Listing 7.7
correspond au programme du listing prcdent, mais utilise une boucle do...while.
Listing 7.7 : Exemple de boucle do...while
1: // Listing7.7
2: // Boucle do while
3:
4: #include <iostream>
5:
6: int main()
7: {
8: using namespace std;
9: int compteur;
10: cout << "Combien de bips voulez-vous afficher? ";
11: cin >> compteur;
12: do
13: {
14: cout << "Bip!\n";
15: compteur--;
16: } while (compteur >0 );
17: cout << "Compteur la sortie de la boucle: "
17a: << counter << endl;
18: return 0;
19: }
C++Livre Page 184 J eudi, 7. mai 2009 9:45 09
Chapitre 7 Droulement dun programme 185
Ce programme produit le rsultat suivant :
Combien de bips voulez-vous afficher? 0
Bip!
Compteur la sortie de la boucle: -1
Comme le programme prcdent, le Listing 7.7 afche le mot "Bip" sur la console le
nombre de fois indiqu. Par contre, il lafchera toujours au moins une fois.
La ligne 10 demande lutilisateur dentrer une valeur de dpart qui est stocke dans la
variable entire compteur. Le corps de la boucle tant excut avant le test de la condition,
un bip est afch la ligne 14. La ligne suivante dcrmente le compteur et la ligne 16
teste la condition. Si elle est vrie, le programme retourne au dbut de la boucle, la
ligne 14. Dans le cas contraire, cest la ligne 17 qui sexcute.
Les instructions continue et break remplissent le mme rle que dans une boucle while.
La seule diffrence entre une boucle while et une boucle dowhile concerne le moment
o la condition est teste.
Linstruction do...while
La syntaxe de linstruction do...while est la suivante :
do
instruction
while (condition);
Linstruction est excute, puis la condition est value. Si cette dernire renvoie true, le
programme effectue un autre passage dans la boucle, sinon le traitement prend n. Une
instruction do...while peut inclure les mmes instructions et conditions quune boucle while.
Exemple 1
// incrmentation jusqu 10
int x = 0;
do
cout << "X: " << x++;
while (x < 10);
Exemple 2
// affichage de lalphabet en minuscules.
char lettre = a;
do
{
cout << lettre << ;
lettre++;
} while ( lettre <= z );
C++Livre Page 185 J eudi, 7. mai 2009 9:45 09
186 Le langage C++
Les boucles for
Dans une boucle while, il est souvent ncessaire de raliser trois tapes : dnir une
condition de dpart, dterminer si elle est vraie et incrmenter une variable ou y affecter
une valeur diffrente chaque passage. Cest ce que lon fait dans le Listing 7.8.
Listing 7.8 : Boucle while revisite
1: // Listing7.8
2: // Traitement itratif avec while
3:
4: #include <iostream>
5:
6: int main()
7: {
8: int compteur = 0;
9:
10: while (compteur < 5)
11: {
12: compteur++;
13: std::cout << "Bip! ";
14: }
15:
16: std::cout << "\nCompteur la sortie de la boucle: "
16a: << compteur << std::endl;
17: return 0;
18: }
Ce programme produit le rsultat suivant :
Bip! Bip! Bip! Bip! Bip!
Compteur la sortie de la boucle: 5.
Faire Ne pas faire
Utiliser do...while lorsque la boucle doit
sexcuter au moins une fois.
Utiliser une boucle while pour sauter le
corps de la boucle si la condition est fausse.
Vrier toutes les boucles pour vous assurer
quelles effectuent le traitement attendu.
Utiliser break et continue avec des
boucles, moins que laction du code ne soit
vidente. Il y a souvent des mthodes plus
claires pour accomplir ces oprations.
Utiliser linstruction goto.
C++Livre Page 186 J eudi, 7. mai 2009 9:45 09
Chapitre 7 Droulement dun programme 187
Dans ce listing, on voit bien les trois tapes mentionnes plus haut. La condition de dpart
est dnie la ligne 8 : la variable compteur est initialise 0. Deux lignes plus loin, le
programme dtermine si elle est infrieure 5. Enn, la variable compteur est incrmen-
te la ligne 12. Cette boucle afche un simple message la ligne 13, mais ce traitement
pourrait tre plus compliqu.
Une boucle for combine ces trois oprations (initialisation, test et incrmentation) en une
seule instruction. La syntaxe est simple : il suft dutiliser le mot-cl for suivi de paren-
thses () entre lesquelles gurent les trois oprations spares les unes des autres par un
point-virgule (;).
for ( initialisation ; test; action )
{
...
}
La premire expression, initialisation, est la condition de dpart, ou initialisation ; elle
peut contenir nimporte quelle instruction C++ valide, mais lusage veut que lon y cre et
initialise une variable compteur. La deuxime expression, test, correspond au test, mais
rien ne vous empche dy placer une instruction C++ diffrente. Elle remplit le mme rle
que lexpression conditionnelle dune boucle while. Enn, la troisime expression,
action, correspond laction raliser. Cette action concerne gnralement laugmenta-
tion ou la baisse dune valeur. L aussi, vous pouvez indiquer une instruction permettant
deffectuer une autre tche. Le Listing 7.9 utilise une boucle for pour effectuer le mme
traitement que le Listing 7.8.
Listing 7.9 : Exemple de boucle for
1: // Listing7.9
2: // Traitement itratif avec for
3:
4: #include <iostream>
5:
6: int main()
7: {
8: int compteur;
9: for (compteur = 0; compteur < 5; compteur++)
10: std::cout << "Bip! ";
11:
12: std::cout << "\nCompteur la sortie de la boucle: "
12a: << compteur << std::endl;
13: return 0;
14: }
C++Livre Page 187 J eudi, 7. mai 2009 9:45 09
188 Le langage C++
Ce programme produit le rsultat suivant :
Bip! Bip! Bip! Bip! Bip!
Compteur la sortie de la boucle: 5.
Linstruction for rassemble trois oprations la ligne 9 : le compteur est initialis zro,
le test porte sur la valeur de cette variable qui doit tre infrieure 5 et enn, le compteur
est incrment de un point chaque passage. Le corps de la boucle consiste en une seule
instruction la ligne 10, mais, il est possible de placer un bloc dinstructions.
Linstruction for
La syntaxe de linstruction for est la suivante :
for (initialisation; test; action)
instruction;
Linstruction initialisation permet daffecter une valeur au compteur ou de prparer le
traitement itratif. Le test (nimporte quelle expression C++) est valu chaque passage
dans la boucle. Si le rsultat est vrai, le corps de la boucle sexcute puis linstruction
spcie dans action (en gnral, le compteur est incrment).
Exemple 1
// afficher Bonjour 10 fois
for (int i = 0; i<10; i++)
cout << "Bonjour! ";
Exemple 2
for (int i = 0; i < 10; i++)
{
cout << "Bonjour!" << endl;
cout << "i est gal a " << i << endl;
}
Boucles for volues
Linstruction for est trs puissante et dune utilisation trs souple. Ses trois lments
(initialisation, test et action) autorisent un grand nombre de variations.
Initialisations et incrmentations multiples
Il nest pas rare dinitialiser plusieurs variables, de tester une expression logique complexe
et dexcuter plusieurs instructions. Vous avez la possibilit de remplacer linitialisation et
C++Livre Page 188 J eudi, 7. mai 2009 9:45 09
Chapitre 7 Droulement dun programme 189
laction par plusieurs instructions C++, spares les unes des autres par une virgule, comme
dans le Listing 7.10
Listing 7.10 : Boucle for contenant plusieurs instructions
1: //Listing7.10
2: // Boucle for comportant plusieurs
3: // instructions
4: #include <iostream>
5:
6: int main()
7: {
8:
9: for (int i=0, j=0; i<3; i++, j++)
10: std::cout << "i: " << i << ", j: " << j << std::endl;
11: return 0;
12: }
Ce programme produit le rsultat suivant :
i: 0 , j: 0
i: 1 , j: 1
i: 2 , j: 2
La ligne 9 initialise zro les deux variables i et j en sparant par une virgule les deux
expressions. Vous pouvez galement constater que ces initialisations sont spares de la
condition de test par le point-virgule attendu.
Lorsque ce programme sexcute, le test (i<3) est valu. Le rsultat tant vrai, le corps de
linstruction for sexcute et les valeurs apparaissent lcran. Puis, la troisime clause
de linstruction for est excute. Cette instruction comporte galement deux expressions
pour incrmenter i et j.
Aprs excution de linstruction de la ligne 10, la condition est de nouveau value. Si elle
est toujours vraie, les actions se rptent (i et j sont nouveau incrmentes), puis la
boucle sexcute une nouvelle fois. Lorsque la condition est fausse, laction nest pas
excute et le programme quitte la boucle.
Utilisation dinstructions nulles dans les boucles for
Toute instruction dune boucle for peut tre omise. Pour cela, utilisez une instruction
nulle en insrant simplement un point-virgule lendroit o aurait gur la clause. Dans le
Listing 7.11, la premire et la troisime clause sont nulles : la boucle for agit alors comme
une boucle while.
C++Livre Page 189 J eudi, 7. mai 2009 9:45 09
190 Le langage C++
Listing 7.11 : Instructions nulles dans une boucle for
1: // Listing7.11
2: // Boucle For avec des instructions nulles
3:
4: #include <iostream>
5:
6: int main()
7: {
8: int compteur = 0;
9:
10: for(; compteur < 5; )
11: {
12: compteur++;
13: std::cout << "Bip! ";
14: }
15:
16: std::cout << "\nCompteur la sortie de la boucle: "
16a: << compteur << std::endl;
17: return 0;
18: }
Ce programme produit le rsultat suivant :
Bip! Bip! Bip! Bip! Bip!
Compteur la sortie de la boucle: 5.
Vous constatez que cette boucle fonctionne exactement comme celle du Listing 7.8. La
ligne 8 initialise la variable compteur. la ligne 10, linstruction for ninitialise pas de
valeur, mais vrie simplement si le compteur est infrieur 5. Lincrmentation se fait
dans le corps de la boucle, ce qui revient crire :
while (compteur < 5)
Cette fois encore, vous pouvez constater que C++ permet de raliser une mme tche de
plusieurs faons. Un programmeur expriment nutiliserait pas une instruction for
comme celle du Listing 7.11 : cet exemple na quune valeur de dmonstration de la
souplesse de linstruction for. En fait, comme le montre le Listing 7.12, il est possible
dutiliser des instructions break et continue pour crer une boucle for vide.
Listing 7.12 : Instruction for vide
1: //Listing7.12: illustration
2: //dune boucle for vide
3:
C++Livre Page 190 J eudi, 7. mai 2009 9:45 09
Chapitre 7 Droulement dun programme 191
4: #include <iostream>
5:
6: int main()
7: {
8: int compteur = 0; // initialisation
9: int max;
10: std::cout << "Combien de bips voulez-vous afficher? ";
11: std::cin >> max;
12: for (;;) // boucle for sans fin
13: {
14: if (compteur < max) // test
15: {
16: std::cout << "Bip! " << std::endl;
17: compteur++; // incrmentation
18: }
19: else
20: break;
21: }
22: return 0;
23: }
Ce programme produit le rsultat suivant :
Combien de bips voulez-vous afficher? 3
Bip!
Bip!
Bip!
Cette boucle for est dpouille lextrme car linstruction ne contient aucune des trois
clauses habituelles initialisation, test et action. Tout dabord, linitialisation
seffectue la ligne 8 avant que la boucle for ne commence. Le test est ralis dans
une instruction distincte, la ligne 14. Si le rsultat est vrai, lincrmentation seffectue
la ligne 17. Dans le cas contraire, le programme quitte la boucle pour aller directement
la ligne 20.
Bien entendu, ce programme est quelque peu absurde mais, dans certains cas, une boucle
for(;;) ou une boucle while (true) rpondront parfaitement vos besoins. Avec
linstruction switch, vous allez apprendre utiliser correctement ces types de boucles.
Boucles for vides
Parfois, linstruction for se suft elle-mme pour raliser un traitement. Le corps de la
boucle devient alors inutile et, dans ce cas, vous devez insrer une instruction nulle (;).
Le point-virgule peut gurer sur la mme ligne que linstruction for, mais il est alors
facile de loublier. Le Listing 7.13 prsente un exemple de boucle for vide.
C++Livre Page 191 J eudi, 7. mai 2009 9:45 09
192 Le langage C++
Listing 7.13 : Corps dune boucle for vide
1: // Listing7.13
2: // Exemple de corps de la boucle for
3: // compos dune instruction nulle
4:
5: #include <iostream>
6: int main()
7: {
8: for (int i=0; i<5; std::cout << "I: " << i++ << std::endl)
9: ; // instruction vide (corps de la boucle)
10: return 0;
11: }
Ce programme produit le rsultat suivant :
i: 0
i: 1
i: 2
i: 3
i: 4
Linstruction for de la ligne 8 comprend trois lments : linstruction dinitialisation
dnit le compteur i, puis linitialise zro. Linstruction de test vrie si i est
infrieure 5. Enn, linstruction daction afche la valeur de i et incrmente le
compteur.
Comme il ny a plus rien faire dans le corps de la boucle, on utilise linstruction nulle
(;). Notez que cette boucle nest pas trs bien construite, car linstruction daction peut
poser des problmes de comprhension au moment de la compilation. Il serait prfrable
de taper les instructions comme suit :
8: for (int i = 0; i<5; i++)
9: cout << "i: " << i << endl;
Le rsultat est le mme, mais avouez que cest plus clair.
Imbrication de boucles
Nimporte quelle boucle peut tre imbrique dans le corps dune autre. Une boucle interne
sexcute en totalit chaque itration de la boucle de niveau immdiatement suprieur.
Le Listing 7.14 utilise deux boucles for imbriques pour afcher une matrice.
C++Livre Page 192 J eudi, 7. mai 2009 9:45 09
Chapitre 7 Droulement dun programme 193
Listing 7.14 : Boucles for imbriques
1: //Listing7.14
2: //Boucles imbriques
3: #include <iostream>
4:
5: int main()
6: {
7: using namespace std;
8: int lignes, colonnes;
9: char car;
10: cout << "Nombre de lignes? ";
11: cin >> lignes;
12: cout << "Nombre de colonnes? ";
13: cin >> colonnes;
14: cout << "Caractre utiliser? ";
15: cin >> car;
16: for (int i = 0; i < lignes; i++)
17: {
18: for (int j = 0; j < colonnes; j++)
19: cout << car ;
20: cout << endl;
21: }
22: return 0;
23: }
Ce programme produit le rsultat suivant :
Nombre de lignes: 4
Nombre de colonnes: 12
Caractre utiliser? X
XXXXXXXXXXXX
XXXXXXXXXXXX
XXXXXXXXXXXX
XXXXXXXXXXXX
Lutilisateur est invit entrer le nombre de lignes et le nombre de colonnes, puis le carac-
tre qui va se rpter dans toutes les cases du tableau. La premire boucle for, la
ligne 16, initialise un compteur (i) 0, puis excute le corps de la boucle for externe.
La ligne 18 est la premire ligne du corps de la boucle for externe, elle met en place une
autre boucle for. Cette boucle imbrique initialise zro un deuxime compteur (j) et
excute son corps. La ligne 19 afche le caractre choisi et lexcution revient len-tte
de la boucle imbrique. Vous remarquerez que la boucle imbrique na quune seule
instruction (lafchage du caractre). Sa condition (j < colonnes) est value ; si elle est
C++Livre Page 193 J eudi, 7. mai 2009 9:45 09
194 Le langage C++
vraie, j est incrmente et le caractre suivant est afch. Ce traitement sarrte lorsque j
est gale au nombre de colonnes souhait.
Lorsque le test de la boucle imbrique renvoie un rsultat faux (ici, aprs lafchage de 12 X),
le programme saute la ligne 20 et afche un retour la ligne. La boucle externe revient
alors son en-tte pour tester la condition i < lignes. Si le rsultat est vrai (i est inf-
rieur au nombre de lignes), i est incrmente et le corps de la boucle sexcute.
la deuxime itration de la boucle externe, la boucle imbrique sexcute nouveau ;
j est rinitialise 0 et le traitement complet de la boucle recommence.
Le point important noter, ici, est quune boucle imbrique sexcute compltement
chaque itration de la boucle extrieure. Dans notre exemple, le caractre X safche donc
colonnes fois pour chaque ligne.
Nombreux sont les programmeurs qui utilisent les lettres i et j comme variables de
compteur. Cette habitude provient du FORTRAN, qui nautorisait que les lettres i,
j, k, l, m et n comme noms de variables de comptage.
Mme si cela peut sembler inoffensif, vos lecteurs pourraient sinterroger sur
lobjectif du compteur et lutiliser mauvais escient. Un programme complexe
contenant des boucles imbriques devient encore plus confus. En ce cas, il vaut
mieux prciser le but de la variable compteur dans son nom, en utilisant par exemple
IndiceClient ou CompteurEntrees.
Porte des variables dans une boucle for
Par le pass, la porte des variables dclares dans une boucle for stendait au bloc ext-
rieur. La norme ANSI a modi ce comportement en rduisant la porte de ces variables
au bloc de la boucle for ; cependant, certains compilateurs ne respectent pas encore cette
spcication. Le mieux est de tester le vtre avec le code suivant :
#include <iostream>
int main()
{
// porte de i limite la boucle for?
for (int i = 0; i < 5; i++)
{
std::cout << "i : " << i << std::endl;
}
i = 7; // ne devrait pas tre accessible!
return 0;
}
In
f
o
C++Livre Page 194 J eudi, 7. mai 2009 9:45 09
Chapitre 7 Droulement dun programme 195
Si la compilation se droule sans problme, votre compilateur ne prend pas en compte ce
nouvel aspect de la norme ANSI.
Si votre compilateur signale que i nest pas encore dni ( la ligne i = 7), il tient compte
de ce nouveau standard. Le code suivant sera compil dans les deux cas si vous dclarez i
en dehors de la boucle, comme ici :
#include <iostream>
int main()
{
int i; // i dclar hors de la boucle for
for (i = 0; i < 5; i++)
{
std::cout << "i: " << i << std::endl;
}
i = 7; // i est maintenant accessible dans tous les cas
return 0;
}
Rsum des boucles
Au Chapitre 5, nous avons rsolu la suite de Fibonacci en faisant appel la rcursivit.
Rappelons que cette suite commence par les chiffres 1, 1, 2, 3, les autres valeurs tant la
somme des deux nombres prcdents. Exemple :
1, 1, 2, 3, 5, 8, 13, 21, 34...
Le nombre de Fibonacci n est la somme du nombre n-1 et du nombre n-2. Le Listing 7.15
prsente une solution ce problme en utilisant une itration.
Listing 7.15 : Dterminer la valeur dun nombre de Fibonacci laide dune itration
1: // Listing7.15 - Dterminer la valeur du nime nombre
2: // laide dune itration
3:
4: #include <iostream>
5:
6: unsigned int fib(unsigned int position);
7: int main()
8: {
9: using namespace std;
10: unsigned int reponse, position;
11: cout << "Quelle position ? ";
12: cin >> position;
C++Livre Page 195 J eudi, 7. mai 2009 9:45 09
196 Le langage C++
13: cout << endl;
14:
15: reponse = fib(position);
16: cout << reponse << " est le ";
17: cout << position << "e nombre dans la suite. " << endl;
18: return 0;
19: }
20:
21: unsigned int fib(unsigned int n)
22: {
23: unsigned int moinsDeux=1, moinsUn=1, reponse=2;
24:
25: if (n < 3)
26: return 1;
27:
28: for (n -= 3; n!= 0; n--)
29: {
30: moinsDeux = moinsUn;
31: moinsUn = reponse;
32: reponse = moinsUn+moinsDeux;
33: }
34:
35: return reponse;
36: }
Ce programme produit le rsultat suivant :
Quelle position ? 4
3 est le 4e nombre dans la suite.
Quelle position ? 5
5 est le 5e nombre dans la suite.
Quelle position ? 20
6765 est le 20e nombre dans la suite.
Dans le Listing 7.15, nous avons prfr litration la rcursivit, car cette approche est
plus rapide et utilise moins de mmoire que la solution rcursive.
La ligne 11 demande lutilisateur de saisir la position du nombre trouver. Puis, on
appelle la fonction fib() pour calculer la valeur correspondante. Si la position est inf-
rieure 3, la fonction renvoie 1. partir de la position 3, la fonction utilise lalgorithme
suivant :
1. Dterminer la position de dpart : affecter la valeur 2 la variable reponse, la valeur 1
moinsDeux et la valeur 1 moinsUn. On te 3 la position, car les deux premiers
nombres sont pris en charge par la position de dpart.
C++Livre Page 196 J eudi, 7. mai 2009 9:45 09
Chapitre 7 Droulement dun programme 197
2. Pour chaque nombre, dterminer la valeur dans la suite. Pour cela :
a. Transfrer la valeur de moinsUn dans moinsDeux.
b. Copier la valeur de reponse dans moinsUn.
c. Faire la somme de moinsUn et de moinsDeux, puis copier le rsultat dans la variable
reponse.
d. Dcrmenter n.
3. Lorsque n est gal 0, renvoyer le rsultat.
Cest comme si vous rsolviez le problme avec du papier et un crayon. Pour le cinquime
nombre de Fibonacci, vous cririez dabord :
1, 1, 2,
puis vous vous diriez quil y a encore deux nombres de plus trouver. Vous additionneriez
alors 2 et 1 et cririez 3 : il ne resterait alors plus quun nombre trouver. Pour cela, vous
additionneriez 3 et 2 et vous cririez 5. En pratique, vous reportez chaque fois votre
attention dun nombre vers la droite et vous dcrmentez le nombre de valeurs restant
trouver.
Remarquez la condition qui est teste la ligne 28 (n!= 0). De nombreux programmeurs
utiliseraient plutt cette notation :
for ( n-=3; n; n-- )
Au lieu dutiliser une condition relationnelle, seule la valeur de n sert la condition dans
linstruction for. Il sagit dune expression caractristique de C++ ; n est considr
comme tant quivalent lexpression n!= 0. Se contenter dutiliser n sappuie sur le fait
que lorsque n est gal 0, cette valeur est considre comme fausse par C++. Pour se
conformer la norme actuelle de C++, il vaut mieux utiliser une vritable condition pour
valuer la valeur false plutt quutiliser une valeur numrique.
Compilez, excutez le programme et comparez le avec la solution rcursive du Chapitre 5.
Tapez 25 comme position dans la suite de Fibonacci et comparez les temps dexcution
des deux versions : vous pouvez constater que le programme itratif tourne bien plus rapi-
dement que le programme rcursif car ce dernier ralise de nombreux appels de fonctions,
qui ont un impact sur les performances. En outre, la solution rcursive, bien qulgante,
effectue plusieurs fois les mmes calculs. Les ordinateurs modernes optimisant les traitements
arithmtiques, la solution itrative devrait tre trs rapide.
Attention la taille du nombre saisi car le nombre de Fibonacci augmente rapidement :
mme la capacit des entiers longs non signs sera vite dpasse.
C++Livre Page 197 J eudi, 7. mai 2009 9:45 09
198 Le langage C++
Contrler le ux avec des instructions switch
Au Chapitre 4, nous avons tudi les instructions if et if...else. Lorsque leur niveau
dimbrication devient trop lev, il est prfrable dutiliser linstruction switch. la
diffrence de if, qui ne permet de tester quune seule condition, une instruction switch
permet deffectuer un "aiguillage" dans le programme en fonction des diffrentes valeurs
dune expression. Cette instruction se prsente sous la forme suivante :
switch (expression)
{
case valeur1: instruction;
break;
case valeur2: instruction;
break;
....
case valeurN: instruction;
break;
default: instruction;
}
expression correspond nimporte quelle expression C++ renvoyant une valeur entire.
Les instructions sont des instructions ou des blocs dinstructions C++. Les diffrentes
valeurs des clauses case sont des valeurs entires (ou des expressions pouvant tre sans
ambigut convertie en une valeur entire). Linstruction switch value la valeur dexpres-
sion puis compare le rsultat avec les valeurs des clauses case. Cette comparaison seffectue
toujours selon lgalit, les oprateurs de comparaison et boolens sont interdits.
Si lune des valeurs de case correspond lexpression, le programme saute vers les
instructions correspondantes, puis continue lexcution jusqu la n de linstruction
switch, moins quil ne rencontre une instruction break. Si aucune correspondance nest
trouve, il va directement linstruction default, qui est facultative ; sil ny a ni valeur
case correspondante, ni de clause default, le programme quitte le bloc dinstructions
switch.
Il est toujours judicieux dutiliser une instruction default dans un bloc
dinstructions switch. Mme si vous navez pas de valeur par dfaut traiter,
cette instruction pourra servir tester un cas qui survient rarement ou af-
cher un message derreur. Elle peut galement tre trs pratique lors de la mise
au point de lapplication.
Il est important de noter que si une clause case ne se termine pas par break, lexcution du
programme se poursuit avec la clause case suivante. Cest parfois ncessaire, mais il sagit
gnralement dun oubli du programmeur, qui provoquera un comportement inattendu.
In
f
o
C++Livre Page 198 J eudi, 7. mai 2009 9:45 09
Chapitre 7 Droulement dun programme 199
Si vous dcidez sciemment dautoriser ce passage la clause case suivante, noubliez pas
de placer un commentaire pour indiquer quil ne sagit pas dun oubli.
Le Listing 7.16 illustre lutilisation de linstruction switch.
Listing 7.16 : Utilisation de linstruction switch
1: //Listing7.16
2: // Linstruction switch
3: #include <iostream>
4:
5: int main()
6: {
7: using namespace std;
8: unsigned short int nombre;
9: cout << "Entrez un nombre entre 1 et 5: ";
10: cin >> nombre;
11: switch (nombre)
12: {
13: case 0: cout << "Trop petit, refaire!";
14: break;
15: case 5: cout << "Bravo! " << endl; // case suivant
16: case 4: cout << "Bon choix! " << endl; // case suivant
17: case 3: cout << "Excellent! " << endl; // case suivant
18: case 2: cout << "Formidable! " << endl; // case suivant
19: case 1: cout << "Incroyable! " << endl;
20: break;
21: default: cout << "Trop grand!" << endl;
22: break;
23: }
24: cout << endl << endl;
25: return 0;
26: }
Ce programme produit le rsultat suivant :
Entrez un nombre entre 1 et 5: 3
Excellent!
Formidable!
Incroyable!
Entrez un chiffre entre 1 et 5: 8
Trop grand!
C++Livre Page 199 J eudi, 7. mai 2009 9:45 09
200 Le langage C++
Aux lignes 9 et 10, lutilisateur est invit entrer un nombre qui sera ensuite transmis
linstruction switch en ligne 11. Si la valeur saisie est zro, le programme saute linstruc-
tion case de la ligne 13. Le message "Trop petit, refaire !" apparat alors et linstruction
break de la ligne 14 met n au switch. Si la valeur est gale 5, lexcution passe la
ligne 15 et les instructions case qui suivent sexcutent les unes aprs les autres jusqu
linstruction break de la ligne 20, qui met n au switch.
Le nombre de messages afchs est donc gal la valeur saisie. Si cette dernire est sup-
rieure 5, la clause default sexcute et le message "Trop grand !" apparat (ligne 21).
Linstruction switch
La syntaxe de linstruction switch est la suivante :
switch (expression)
{
case valeur1: instruction;
case valeur2: instruction;
....
case valeurN: instruction
default: instruction;
}
Linstruction switch permet daiguiller le programme en diffrent points en fonction de
la valeur de expression. Aprs lvaluation de lexpression, le pointeur dinstruction va
directement clause case dont la valeur correspond. Lexcution du programme se pour-
suit jusqu la n de linstruction switch ou jusqu ce quune instruction break soit
rencontre.
Si lexpression ne correspond aucune clause case, et quil y a une clause default, le
programme excute cette clause ; sinon, linstruction switch se termine.
Exemple 1
switch (choix)
{
case 0:
cout << "Zro!" << endl;
break
case 1:
cout << "Un!" << endl;
break;
case 2:
cout << "Deux!" << endl;
default:
cout << "Clause par dfaut!" << endl;
}
C++Livre Page 200 J eudi, 7. mai 2009 9:45 09
Chapitre 7 Droulement dun programme 201
Exemple 2
switch (choix)
{
case 0:
case 1:
case 2:
cout << "Infrieur 3!";
break;
case 3:
cout << "gal 3!";
break;
default:
cout << "Suprieur 3 !";
}
Utilisation de switch dans un menu
Le Listing 7.17 reprend le principe de la boucle for(;;) dcrite plus haut. Une boucle qui
ne se termine pas est appele boucle sans n, car elle se rptera continuellement si elle ne
rencontre pas une instruction break. Dans le Listing 7.17, le programme afche un menu,
invite lutilisateur faire un choix, agit en fonction de ce choix, puis afche de nouveau le
menu. Pour mettre n ce traitement, lutilisateur doit choisir loption Quitter.
Certains programmeurs aiment crire :
#define EVER;;
for (EVER)
{
// instructions
}
Une boucle sans n ne contient pas de condition de sortie. Seule une instruction break
permet den sortir. Les boucles sans n sont galement appeles boucles innies.
Listing 7.17 : Exemple de boucle sans n
1: //Listing7.17
2: //Boucle sans fin permettant une interaction
2a: // avec lutilisateur laide dun menu
3: #include <iostream>
4:
In
f
o
C++Livre Page 201 J eudi, 7. mai 2009 9:45 09
202 Le langage C++
5: // prototypes
6: int menu();
7: void Tache();
8: void AutreTache(int);
9:
10: using namespace std;
11:
12: int main()
13: {
14: bool fin = false;
15: for (;;) // dbut de la boucle sans fin
16: {
17: int choix = menu();
18: switch(choix)
19: {
20: case (1):
21: Tache();
22: break;
23: case (2):
24: AutreTache(2);
25: break;
26: case (3):
27: AutreTache(3);
28: break;
29: case (4):
30: continue; // redondant!
31: break;
32: case (5):
33: fin = true;
34: break;
35: default:
36: cout << "Choix non valide, recommencez! " << endl;
37: break;
38: } // fin de switch
39:
40: if (fin == true)
41: break;
42: } // fin de la boucle
43: return 0;
44: } // fin de main()
45:
46: int menu()
47: {
48: int choix;
49:
50: cout << " **** Menu **** " << endl << endl;
51: cout << "(1) Choix un. " << endl;
C++Livre Page 202 J eudi, 7. mai 2009 9:45 09
Chapitre 7 Droulement dun programme 203
52: cout << "(2) Choix deux. " << endl;
53: cout << "(3) Choix trois. " << endl;
54: cout << "(4) Rafficher le menu. " << endl;
55: cout << "(5) Quitter. " << endl << endl;
56: cout << "Votre choix: ";
57: cin >> choix;
58: return choix;
59: }
60:
61: void Tache()
62: {
63: cout << "Tche une! " << endl;
64: }
65:
66: void AutreTache(int combien)
67: {
68: if (combien == 2)
69: cout << "Tche deux! " << endl;
70: else
71: cout << "Tche trois! " << endl;
72: }
Ce programme produit le rsultat suivant :
**** Menu ****
(1) Choix un.
(2) Choix deux.
(3) Choix trois.
(4) Rafficher le menu
(5) Quitter.
Votre choix: 1
Tche une!
**** Menu ****
(1) Choix un.
(2) Choix deux.
(3) Choix trois.
(4) Rafficher le menu
(5) Quitter.
Votre choix: 3
Tche trois!
**** Menu ****
(1) Choix un.
(2) Choix deux.
C++Livre Page 203 J eudi, 7. mai 2009 9:45 09
204 Le langage C++
(3) Choix trois.
(4) Rafficher le menu
(5) Quitter.
Votre choix: 5
Ce programme rassemble des concepts abords dans ce chapitre (comme linstruction
switch) et dans les chapitres prcdents.
La boucle sans n commence la ligne 15. La fonction menu() afche le menu lcran
et rcupre le choix de lutilisateur. Cette valeur est traite dans linstruction switch qui
stend de la ligne 18 la ligne 38.
Si lutilisateur choisit 1, le programme va directement la clause case (1): de la
ligne 20, puis il excute la fonction Tache(), qui afche un message. Linstruction break
de la ligne 22 interrompt lexcution de linstruction switch et le programme va alors
directement la ligne 39. la ligne suivante, la variable fin est value. Si elle vaut true,
linstruction break de la ligne 41 sexcute et la boucle for(;;) prend n. Dans le cas
contraire, lexcution reprend au dbut de la boucle (ligne 15).
Linstruction continue de la ligne 30 est redondante. Si cette instruction ntait pas
mentionne, linstruction break suivante mettrait n linstruction switch, le rsultat
du test sur fin serait faux et un nouveau passage dans la boucle aurait lieu, ce qui raf-
cherait le menu. Linstruction continue permet toutefois dviter le test de fin.
Questions-rponses
Q Comment choisir entre linstruction ifelse et linstruction switch ?
R Si la condition comprend plus dune ou deux clauses else testant la mme valeur, pr-
frez linstruction switch.
Faire Ne pas faire
Documenter toutes les clause case ne conte-
nant pas (sciemment) dinstruction break.
Prvoir une clause default dans les instruc-
tions switch, an de traiter les situations
exceptionnelles.
Utiliser des instructions ifelse complexes
alors quune instruction switch plus claire
conviendrait.
Oublier linstruction break dans une suite de
clauses case, sauf si vous voulez que le trai-
tement se poursuive dans la clause case
suivante.
C++Livre Page 204 J eudi, 7. mai 2009 9:45 09
Chapitre 7 Droulement dun programme 205
Q Comment choisir entre linstruction while et linstruction do...while ?
R Si le corps de litration doit sexcuter au moins une fois, choisissez linstruction
do...while. Dans le cas contraire, prfrez linstruction while.
Q Comment choisir entre linstruction while et linstruction for ?
R Linstruction for permet dinitialiser une variable compteur, de tester cette variable,
puis de lincrmenter chaque passage. En revanche, si la variable est dj initialise
et na pas besoin dtre incrmente chaque itration, prfrez linstruction while.
Les programmeurs expriments sattendent cette utilisation et auront plus de mal
comprendre votre code si vous nagissez pas ainsi.
Q Doit-on prfrer linstruction while (true) linstruction for(;;) ?
R Non. Ces instructions permettent toutes les deux de dnir une boucle sans n, mais il
vaut mieux les viter.
Q Pourquoi ne faut-il pas utiliser une variable comme condition, par exemple
while(n) ?
R Dans la norme C++ actuelle, une expression renvoie une valeur boolenne true ou
false. Vous pouvez associer false 0 et true une autre valeur, mais il vaut mieux
utiliser une expression qui renvoie lune des deux valeurs boolennes.
Testez vos connaissances
1. Comment peut-on initialiser plusieurs variables dans une boucle for ?
2. Pourquoi faut-il viter dutiliser linstruction goto ?
3. Est-il possible de crer une boucle for dont la squence dinstructions ne sexcute
jamais ?
4. Lorsque cette boucle for se termine, quelle est la valeur de x ?
for (int x = 0; x < 100; x++)
5. Est-il possible dimbriquer des boucles while dans des boucles for ?
6. Est-il possible de crer une boucle qui ne se termine jamais ? Donnez un exemple.
7. Que se passe-t-il si vous crez une boucle sans n ?
C++Livre Page 205 J eudi, 7. mai 2009 9:45 09
206 Le langage C++
Exercices
1. crivez une boucle for imbrique qui produit une matrice de 10 fois 10 zros.
2. Comptez de 100 200, de deux en deux en utilisant une boucle for.
3. Mme exercice que ci-dessus mais avec une boucle while.
4. Mme exercice que ci-dessus mais avec une boucle do...while.
5. CHERCHEZ LERREUR : pourquoi ce code est-il bogu ?
int compteur = 0
while (compteur < 10)
{
cout << "Compteur: " << compteur;
}
6. CHERCHEZ LERREUR : pourquoi ce code est-il bogu ?
for (int compteur = 0; compteur < 10; compteur++);
cout << compteur << "\n ";
7. CHERCHEZ LERREUR : pourquoi ce code est-il bogu ?
int compteur = 100;
while (compteur < 10)
{
cout << "Le compteur indique: " << compteur;
compteur--;
}
8. CHERCHEZ LERREUR : pourquoi ce code est-il bogu ?
cout << "Entrez une valeur comprise entre 0 et 5: ";
cin >> Valeur;
switch (Valeur)
{
case 0:
Traitement0();
case 1: // case suivant
case 2: // case suivant
case 3: // case suivant
case 4: // case suivant
case 5:
Traitement1A5();
break;
default:
ParDefaut();
break;
}
C++Livre Page 206 J eudi, 7. mai 2009 9:45 09
Partie II
La premire partie de cet ouvrage vous a initi aux bases de C++. Vous tes mainte-
nant en mesure de saisir du code, de suivre le droulement dune application, de dnir
des objets et des classes et dutiliser le compilateur.
Le Chapitre 8 traite des pointeurs. Les pointeurs sont traditionnellement rputs
comme lun des sujets les plus ardus pour les programmeurs dbutants. Aussi lappro-
che est-elle progressive pour vous permettre den assimiler parfaitement les concepts
et dcrire vos premiers programmes. Dans le Chapitre 9, vous ferez connaissance
avec les rfrences, qui sont des proches cousins des pointeurs. Le Chapitre 10 est
consacr la surcharge de fonctions.
Le Chapitre 11 traite de la conception et de lanalyse oriente objet. Le Chapitre 12
aborde le concept dhritage, trs important en POO, dont ltude se poursuit au
Chapitre 13 qui tudie les tableaux et les collections. Enn, le Chapitre 14 traite
du polymorphisme.
C++Livre Page 207 J eudi, 7. mai 2009 9:45 09
C++Livre Page 208 J eudi, 7. mai 2009 9:45 09
8
Pointeurs
Au sommaire de ce chapitre
Nature et fonctionnalit des pointeurs
Dclaration et utilisation de pointeurs
Espace adressable et gestion de la mmoire
La gestion directe de la mmoire laide de pointeurs constitue une fonctionnalit puis-
sante mais de bas niveau du langage C++. Cest un avantage de C++ par rapport dautres
langages, comme C# et Visual Basic, mme si C# permet dutiliser les pointeurs sous
certaines conditions.
Les pointeurs posent deux problmes aux programmeurs dbutants : dune part, leur
approche est ardue et, dautre part, il est difcile de dterminer quand et comment il est
souhaitable de les utiliser. Dans ce chapitre, les pointeurs sont prsents de faon progressive,
de sorte que vous saurez exactement quand y avoir recours.
C++Livre Page 209 J eudi, 7. mai 2009 9:45 09
210 Le langage C++
La possibilit dutiliser des pointeurs et de manipuler la mmoire bas niveau
est lun des facteurs qui font de C++ un langage de choix pour les applications
embarques et en temps rel.
Quest-ce quun pointeur ?
Un pointeur est une variable qui contient une adresse mmoire. Cest tout. Si vous comprenez
cette simple phrase, vous savez lessentiel au sujet des pointeurs.
Mmoire
Pour comprendre les pointeurs, vous devez avoir quelques notions sur lorganisation de la
mmoire dun ordinateur. Cette mmoire est divise en emplacements numrots squen-
tiellement. Chaque variable rside dans un emplacement unique en mmoire, connu par
son adresse. La Figure 8.1 reprsente schmatiquement la faon dont la variable unsigned
long monAge est stocke en mmoire.
Rcuprer ladresse mmoire dune variable
Lorganisation de la mmoire et la numrotation des adresses varient dun ordinateur un
autre. En gnral, les dveloppeurs nont pas besoin de connatre les adresses des variables
utilises, car ces dtails sont grs par le compilateur. Si vous avez besoin de connatre
ladresse dun objet en mmoire, vous pouvez toutefois utiliser loprateur adresse de (&),
comme le montre le Listing 8.1.
Figure 8.1
Reprsentation
schmatique
de la variable
monAge.
In
f
o
monAge
100 101 102 103 104 105 106 107 108 109 110 111 112 113
chaque emplacement = 1 octet
entier long non sign monAge = 4 octets = 32 bits
la variable monAge pointe sur le premier octet
l'adresse de monAge est 102
10110101
01110110
11110110
11101110
Mmoire
C++Livre Page 210 J eudi, 7. mai 2009 9:45 09
Chapitre 8 Pointeurs 211
Listing 8.1 : Exemple dutilisation de loprateur adresse de
1: // Listing8.1 - Utilisation de loprateur "adresse de" (&)
2: // et adresses des variables locales
3: #include <iostream>
4:
5: int main()
6: {
7: using namespace std;
8: unsigned short varCourte = 5;
9: unsigned long varLongue = 65535;
10: long varSignee = -65535;
11:
12: cout << "varCourte :\t" << varCourte;
13: cout << "\tAdresse de varCourte :\t";
14: cout << &varCourte << endl;
15:
16: cout << "varLongue :\t" << varLongue;
17: cout << "\tAdresse de varLongue :\t";
18: cout << &varLongue << endl;
19:
20: cout << "varSignee :\t" << varSignee;
21: cout << "\tAdresse de varSignee :\t";
22: cout << &varSignee << endl;
23:
24: return 0;
25: }
Ce programme produit le rsultat suivant :
varCourte :5 Adresse de varCourte : 0xbffff97e
varLongue :65535 Adresse de varLongue : 0xbffff978
varSignee :-65535 Adresse de varSignee : 0xbffff974
( les valeurs afches sur votre ordinateur seront probablement diffrentes, en particulier
dans la dernire colonne).
Le programme commence par la dclaration et linitialisation de trois variables : la
premire de type unsigned short (ligne 8), la deuxime de type unsigned long
(ligne 9) et la dernire de type long (ligne 10). Leurs valeurs et adresses respectives sont
extraites laide de loprateur adresse de (&), puis afches lcran (lignes 12 22). Cet
oprateur est simplement plac devant le nom de la variable pour que ladresse soit
renvoye.
La variable varCourte est initialise la ligne 12. Dans la premire ligne du rsultat, son
adresse hexadcimale est gale 00bffff97e lorsque le programme tourne sur un ordinateur
C++Livre Page 211 J eudi, 7. mai 2009 9:45 09
212 Le langage C++
dot dun processeur Intel Core 2 Duo (il a t ici compil sur 32 bits). Cette adresse est
propre chaque ordinateur et varie dune session une autre. Les rsultats que vous
obtiendrez seront diffrents.
Lorsque lon dclare une variable, le compilateur dtermine lespace mmoire ncessaire
en fonction du type de la variable. Il soccupe ensuite dallouer automatiquement la mmoire
et daffecter une adresse pour cette variable. Un entier long occupant gnralement
4 octets, le compilateur affectera une adresse qui pointera sur 4 octets en mmoire.
Stockage dune adresse de variable dans un pointeur
Comme nous lavons vu, toute variable est associe une adresse. Il est possible de stocker
cette adresse dans un pointeur sans en connatre la valeur.
Supposons que la variable votreAge soit de type entier. Le pointeur pAge associ, qui va
recevoir son adresse, sera dclar ainsi :
int *pAge = 0;
Il sagit dun pointeur sur un entier, ce qui signie que pAge est destin recevoir
ladresse dun entier.
Notez que pAge est une variable. Lorsque vous dclarez une variable entire (de type
int), le compilateur rserve la mmoire ncessaire pour le stockage dun entier. Si vous
dclarez une variable pointeur telle que pAge, le compilateur rserve sufsamment de
mmoire pour contenir une adresse (4 octets sur la plupart des ordinateurs). Un pointeur,
et donc pAge, est simplement un type de variable diffrent.
Noms des pointeurs
Les pointeurs ntant que des variables, vous pouvez utiliser nimporte quel nom autoris
pour les variables : les mmes rgles et suggestions sappliquent. De nombreux
programmeurs respectent la convention de nommage avec un p initial, comme dans
pAge ou pNombre.
Dans lexemple,
int *pAge = 0;
pAge est initialis 0, ce qui caractrise les pointeurs null. Les pointeurs devant toujours
tre initialiss, la solution consiste affecter une valeur nulle lorsque ladresse nest pas
encore dtermine. Un pointeur qui nest pas initialis est trs dangereux, on lappelle un
pointeur fou, car il peut pointer vers nimporte quoi.
C++Livre Page 212 J eudi, 7. mai 2009 9:45 09
Chapitre 8 Pointeurs 213
Pour viter les erreurs de compilation et dexcution, noubliez pas dinitialiser
tous vos pointeurs.
Pour quun pointeur stocke une adresse, cette adresse doit lui tre affecte. Dans lexemple
prcdent, vous devez affecter ladresse de MonAge page :
unsigned short int MonAge = 50; // cre une variable
unsigned short int * pAge = 0; // cre un pointeur
pAge = &MonAge; // affecte ladresse de MonAge pAge
La premire ligne initialise 50 la variable MonAge, de type unsigned short int. La
seconde ligne dclare pAge comme un pointeur vers un unsigned short int et linitia-
lise zro. On sait que pAge est un pointeur grce lastrisque (*) place entre le type et
le nom de la variable.
La dernire ligne affecte ladresse de MonAge au pointeur pAge, grce loprateur adresse
de (&). Sans lui, la valeur de MonAge aurait t affecte au pointeur pAge. Cette valeur
aurait pu, ou non, tre une adresse valide.
La valeur de ladresse de MonAge est prsent stocke dans pAge. MonAge contient la
valeur 50. Laffectation de ces valeurs aurait pu tre simplie :
unsigned short int monAge = 50; // variable
unsigned short int * pAge = &monAge; // pointeur sur monAge
Le pointeur pAge contient ladresse de la variable MonAge.
Obtenir la valeur dune variable
laide de pAge, vous pouvez dterminer la valeur numrique contenue dans MonAge. La
technique consistant accder la valeur stocke dans une variable par lintermdiaire
dun pointeur sappelle adressage indirect ou indirection, car vous accdez indirectement
la variable, via un pointeur. Vous pouvez, par exemple, utiliser lindirection avec le pointeur
pAge pour accder la valeur MonAge.
Lindirection consiste accder la valeur stocke ladresse que contient un pointeur.
Le pointeur fournit un moyen indirect daccder la valeur stocke cette adresse.
Avec une variable normale, le type indique au compilateur la taille mmoire nces-
saire pour contenir la valeur. Dans le cas dun pointeur, cest diffrent : tous les
pointeurs ont la mme taille, gnralement 4 octets sur un processeur 32 bits et
8 octets sur un processeur 64 bits. Le type indique alors au compilateur la mmoire
ncessaire pour contenir lobjet ladresse que contient le pointeur !
A
tte
n
tio
n
In
f
o
C++Livre Page 213 J eudi, 7. mai 2009 9:45 09
214 Le langage C++
Dans la dclaration :
unsigned short int * pAge = 0; // cration dun pointeur
pAge est dclar comme un pointeur sur un entier court non sign. Le compila-
teur sait que le pointeur (qui a besoin de 4 octets pour contenir une adresse)
contiendra ladresse dun objet du type entier court non sign, qui lui-mme
ncessite 2 octets.
Drfrencement avec loprateur dindirection
Loprateur dindirection (*) est galement appel oprateur de drfrencement. Lorsquun
pointeur est drfrenc, on obtient la valeur situe ladresse stocke dans le pointeur.
Laccs la valeur dune variable classique est direct. Pour crer la variable VotreAge, de
type entier court non sign et lui affecter la valeur de MonAge, il suft dcrire les instructions
suivantes :
unsigned short int VotreAge;
VotreAge = MonAge;
Pour affecter la valeur de MonAge la variable VotreAge, en utilisant le pointeur pAge,
vous devez crire :
unsigned short int VotreAge;
VotreAge = *pAge;
Loprateur dindirection (*) devant une variable pointeur signie : "valeur stocke
ladresse". Dans le cas prsent, cette affectation signie donc : "prend la valeur stocke
ladresse de pAge, puis affecte-la la variable VotreAge". Si vous naviez pas inclus
loprateur dindirection :
VotreAge = pAge; // Mauvais!!
vous tenteriez daffecter la valeur de pAge, une adresse mmoire, votreAge. Votre
compilateur afcherait probablement un avertissement.
Les diffrentes utilisations de lastrisque
Lastrisque (*) est utilis de deux faons diffrentes avec les pointeurs : comme compo-
sante du pointeur et comme oprateur dindirection.
Lorsque vous dclarez un pointeur, lastrisque fait partie de la dclaration et suit le type
de lobjet vers lequel on pointe. Exemple :
// cration dun pointeur vers un unsigned short
unsigned short * pAge = 0;
C++Livre Page 214 J eudi, 7. mai 2009 9:45 09
Chapitre 8 Pointeurs 215
Par ailleurs, loprateur dindirection (ou de drfrencement) appliqu un pointeur
permet daccder non pas ladresse elle-mme, mais lemplacement mmoire situ
cette adresse. Exemple :
*pAge = 5; // affecte 5 la valeur situe pAge
Ce caractre est aussi utilis comme oprateur de multiplication. Rassurez-vous : le compi-
lateur sait, daprs le contexte, de quel oprateur il sagit.
Pointeurs, adresses et variables
Avant de continuer, il convient de distinguer le pointeur, ladresse stocke dans le pointeur
et la valeur ladresse contenue par le pointeur. Ces trois concepts sont souvent source de
confusion chez les programmeurs.
Prenons un exemple :
int maVariable = 5;
int * pPointeur = &maVariable;
La variable maVariable de type entier reoit la valeur numrique 5. Le pointeur
pPointeur est dclar comme pointeur sur un entier et est initialis avec ladresse de
maVariable. La valeur de ladresse stocke dans pPointeur est ladresse de maVariable.
La Figure 8.2 est une reprsentation graphique de cet exemple.
Dans cette gure, la valeur 5 est stocke ladresse mmoire 101, indique par le nombre
binaire :
0000000000000101
Il sagit de deux octets (16 bits) dont la valeur dcimale est 5.
Figure 8.2
Reprsentation
schmatique
de la mmoire.
5
0000
100 101 102 103 104 105 106 107 108 109
0000
0000
0101
0000
0000
0000
0000
0000
0000
0110
0101
101
Nom
de la variable
maVariable
pPointeur
C++Livre Page 215 J eudi, 7. mai 2009 9:45 09
216 Le langage C++
La variable pointeur se trouve lemplacement 106. Sa valeur est :
0000000000000000000000001100101
Il sagit de la reprsentation binaire de la valeur 101, qui est ladresse de maVariable dont
la valeur est 5.
Bien que la mmoire soit reprsente ici de faon schmatique, elle illustre bien la faon
dont les pointeurs stockent une adresse.
Gestion de donnes laide de pointeurs
Outre loprateur dindirection qui permet de lire les donnes stockes une position
pointe par une variable, vous pouvez aussi manipuler ces donnes car, aprs avoir affect
une adresse un pointeur, vous pouvez avoir accs aux donnes quil pointe.
Le Listing 8.2 reprend ce que vous venez dapprendre sur les pointeurs. Vous verrez
comment ladresse dune variable locale est affecte un pointeur, qui peut ensuite tre
utilis avec loprateur dindirection pour manipuler les donnes contenues dans cette
variable.
Listing 8.2 : Manipulation de donnes par pointeurs
1: // Listing8.2 Utilisation des pointeurs
2: #include <iostream>
4: typedef unsigned short int USHORT;
5:
6: int main()
7: {
8:
9: using namespace std;
10:
11: USHORT monAge; // une variable
12: USHORT * pAge = 0; // un pointeur
13:
14: moAge = 5;
15:
16: cout << "moAge: " << moAge << endl;
17: pAge = &moAge; // affecte ladresse de monAge pAge
18: cout << "*pAge: " << *pAge << endl << endl;
19:
20: cout << "Affectation *pAge de la valeur 7... " << endl;
21: *pAge = 7; // affecte la valeur 7 monAge
22:
23: cout << "*pAge: " << *pAge << endl;
C++Livre Page 216 J eudi, 7. mai 2009 9:45 09
Chapitre 8 Pointeurs 217
24: cout << "monAge: " << monAge << endl << endl;
25:
26: cout << "Affectation moAge de la valeur 9... " << endl;
27: monAge = 9;
28:
29: cout << "monAge: " << monAge << endl;
30: cout << "*pAge: " << *pAge << endl;
31:
32: return 0;
33: }
Ce programme produit le rsultat suivant :
monAge : 5
*pAge : 5
Affectation *pAge de la valeur 7...
*pAge : 7
monAge : 7
Affectation myAge de la valeur 9...
monAge : 9
*pAge : 9
Ce programme commence par dclarer deux variables : monAge (de type entier court) et
son pointeur pAge. La ligne 14 affecte 5 la variable monAge, ce que conrme lafchage
produit par la ligne 16.
la ligne 17, pAge reoit ladresse de monAge. La ligne 18 "drfrence" le pointeur avec
loprateur dindirection (*) et obtient ainsi la valeur stocke dans monAge, cest--dire 5,
qui est afche. La valeur 7 est ensuite affecte la variable par lintermdiaire du poin-
teur (ligne 21). Les messages produits aux lignes 23 et 24 conrment cette opration.
Remarquez une fois de plus que laccs indirect la variable a t obtenu avec lastrisque
(loprateur dindirection, dans ce contexte).
Enn, le chiffre 9 est affect la variable monAge (ligne 27). La ligne 29 lit directement la
variable alors que la ligne suivante la lit indirectement, en dfrenant pAge.
Lecture dune adresse
Un pointeur permet de manipuler une adresse sans mme connatre sa valeur. Laffectation
dune adresse de variable un pointeur fait que celui-ci contient ladresse de cette valeur.
Nous allons juste le vrier laide du Listing 8.3.
C++Livre Page 217 J eudi, 7. mai 2009 9:45 09
218 Le langage C++
Listing 8.3 : Lire le contenu dun pointeur
1: // Listing8.3
2: Ce qui est stock dans un pointeur.
3: #include <iostream>
4:
5: int main()
6: {
7: using namespace std;
8:
9: unsigned short int monAge = 5, tonAge = 10;
10:
11: // un pointeur
12: unsigned short int * pAge = &monAge;
13:
14: cout << "monAge:\t" << monAge
15: << "\t\ttonAge:\t" << tonAge << endl;
16:
17: cout << "&monAge:\t" << &monAge
18: << "\t&tonAge:\t" << &tonAge << endl;
19:
20: cout << "pAge:\t" << pAge << endl;
21: cout << "*pAge:\t" << *pAge << endl;
22:
23:
24: cout << "\nRaffectation de pAge = &tonAge... "
24a! << endl << endl;
25: pAge = &tonAge; // raffectation du pointeur
26:
27: cout << "monAge:\t" << monAge <<
28: "\t\ttonAge:\t" << tonAge << endl;
29:
30: cout << "&monAge:\t" << &monAge
31: << "\t&tonAge:\t" << &tonAge << endl;
32:
33: cout << "pAge:\t" << pAge << endl;
34: cout << "*pAge:\t" << *pAge << endl;
35:
36: cout << "\n&pAge:\t" << &pAge << endl;
37:
38: return 0;
39: }
Ce programme produit le rsultat suivant :
monAge : 5 tonAge : 10
&monAge : 0xbffff96e &tonAge : 0xbffff96c
C++Livre Page 218 J eudi, 7. mai 2009 9:45 09
Chapitre 8 Pointeurs 219
pAge : 0xbffff96e
*pAge : 5
Raffectation de pAge = &tonAge...
monAge : 5 tonAge : 10
&monAge : 0xbffff96e &tonAge : 0xbffff96c
pAge : 0xbffff96c
*pAge : 10
&pAge : 0xbffff968
(Le rsultat que vous obtiendrez sera diffrent.)
La ligne 9 dclare les variables entires monAge et tonAge, de type unsigned short, puis
dclare le pointeur pAge et linitialise avec ladresse de monAge (ligne 12).
Les valeurs numriques et les adresses des deux variables apparaissent alors lcran
(lignes 14 18). la ligne 20, vous pouvez remarquer que le contenu de pAge correspond
ladresse de monAge. La ligne 21 afche le rsultat du drfrencement du pointeur, ce
qui afche la valeur pointe par pAge, qui est celle de la variable monAge, cest--dire 5.
Cet exemple illustre parfaitement lextraction de donnes laide dun pointeur. Elle est
directe la ligne 20 et indirecte (par drfrencement du pointeur) la ligne suivante.
Avant de poursuivre, vous devez comprendre ces deux approches. Au besoin, modiez les
valeurs et examinez le rsultat obtenu.
La ligne 25 affecte ladresse de tonAge au pointeur : pAge "pointe" donc dsormais vers
cette variable. Pour vous aider comprendre lopration, les valeurs et les adresses appa-
raissent de nouveau lcran. Le pointeur pAge contient dsormais ladresse de tonAge.
Par drfrencement, on obtient donc la valeur de tonAge.
Ladresse du pointeur pAge apparat lcran (ligne 36). Comme toute variable, le pointeur
possde une adresse (qui peut elle-mme tre copie dans un pointeur). Nous prsenterons
bientt laffectation dune adresse de pointeur un autre pointeur.
Faire Ne pas faire
Accder aux donnes stockes ladresse
dun pointeur, laide de loprateur dindi-
rection (*).
Initialiser lensemble des pointeurs dun
programme avec une adresse existante ou la
valeur nulle (0).
Confondre ladresse stocke dans un pointeur
avec la valeur stocke cette adresse.
C++Livre Page 219 J eudi, 7. mai 2009 9:45 09
220 Le langage C++
Utilisation des pointeurs
Pour dclarer un pointeur, vous devez dabord indiquer le type de la variable ou de lobjet
point, suivi de loprateur * et du nom du pointeur. Exemple :
unsigned short int * pPointeur = 0;
Pour affecter une valeur un pointeur ou linitialiser, vous devez faire prcder le nom de
la variable sur laquelle il porte de loprateur adresse de (&). Exemple :
unsigned short int maVariable = 5;
unsigned short int * pPointeur = &maVariable;
Pour drfrencer un pointeur, faites prcder son nom de loprateur (*). Exemple :
unsigned short int maValeur = *pPointeur
Utilit des pointeurs
Vous venez dapprendre les dtails de laffectation dune adresse de variable un pointeur.
Dans la pratique, cependant, vous ne le ferez jamais. Pourquoi passer par un pointeur pour
lire une variable, alors que la valeur de cette dernire est directement accessible ? Lobjec-
tif de ce type de manipulation tait simplement de vous montrer le fonctionnement des
pointeurs. Maintenant que leur syntaxe na plus de secrets pour vous, vous pouvez les
utiliser correctement. Les pointeurs sont gnralement utiliss pour trois raisons :
grer des donnes dans lespace mmoire adressable ;
accder aux donnes membres et aux fonctions des classes ;
passer des variables par rfrence des fonctions.
Le reste de ce chapitre est consacr la gestion de donnes dans lespace mmoire adres-
sable et laccs aux donnes membres et aux fonctions dune classe. Le passage de variables
laide des pointeurs, appel passage par rfrence, sera tudi au chapitre suivant.
La pile et lespace mmoire adressable (tas)
la n du Chapitre 5, nous avons voqu les cinq parties suivantes de la mmoire :
lespace global de noms ;
lespace mmoire adressable (appel aussi mmoire "heap" ou tas) ;
les registres ;
C++Livre Page 220 J eudi, 7. mai 2009 9:45 09
Chapitre 8 Pointeurs 221
lespace de code ;
la pile.
Les variables locales et les paramtres passs aux fonctions sont placs sur la pile. Le code
du programme rside dans lespace de code. Quant aux registres, ils permettent au
systme de raliser des oprations internes, comme le suivi du contenu de la pile et du
pointeur dinstruction. Le reste de la mmoire est essentiellement compos de lespace
adressable, aussi appel le tas.
Les variables locales disparaissent lorsque la fonction qui les a dnies se termine : le
programmeur na donc pas besoin de grer cette partie de la mmoire. Par contre, ces
objets locaux ne peuvent pas tre facilement transmis dautres objets ou dautres fonc-
tions : pour ce faire, il faut copier ces objets de la pile vers la valeur de retour de la fonc-
tion, puis vers lobjet destinataire dans lappelant et cela a un cot. Les variables globales
permettent de pallier cet inconvnient au prix dun accs illimit aux donnes, ce qui peut
rendre le code illisible et impossible mettre jour. La solution consiste alors utiliser le
tas, mais le programmeur doit alors grer correctement ces donnes.
Le tas peut tre considr comme un ensemble de milliers de cases mmoires destines
recevoir des donnes. la diffrence de la pile, ces cases ne sont tre nommes : pour
accder une donne stocke dans le tas, il faut obtenir ladresse de lemplacement que
lon a rserv pour elle, puis copier cette adresse dans un pointeur, an de la mmoriser.
Prenons un exemple. Un ami vous communique par crit le numro de tlphone du
garage SuperAuto. De retour votre domicile, vous composez ce numro, puis vous jetez
le papier la corbeille. Vous ne pouvez donc plus connatre le numro de tlphone de
cette entreprise, mais la mmoire de lappareil tlphonique continue de vous permettre
de les appeler. SuperAuto correspond aux donnes stockes dans le tas. Vous ne savez pas
o le garage se trouve, mais vous savez comment y accder, en utilisant son adresse son
numro de tlphone stock dans la mmoire de votre combin, ici. Vous ne connaissez
mme pas ce numro, mais vous lavez mis dans un pointeur (la mmoire du combin),
qui vous permet de continuer joindre le garage. De la mme faon, dans un programme,
un pointeur permet davoir accs une donne sans connatre son emplacement en
mmoire.
La pile est vide automatiquement quand la fonction prend n : toutes les variables locales
deviennent hors de porte et sont supprimes de la pile. En revanche, le tas nest vid
quaprs la n du programme. Pendant la session du programme, la gestion du tas et la
libration de la mmoire vous incombent. Cest dans ce cas que les destructeurs sont abso-
lument ncessaires : ils fournissent un endroit permettant de librer les emplacements du
tas qui ont t allous lors de la construction de lobjet.
C++Livre Page 221 J eudi, 7. mai 2009 9:45 09
222 Le langage C++
Lavantage du tas est que les emplacements que vous avez rservs restent disponibles
jusqu ce que vous les libriez explicitement. Si vous allouez de la mmoire sur le tas
dans une fonction, cette mmoire reste alloue aprs la n de la fonction.
Cette persistance des emplacements rservs est aussi un inconvnient car, si vous oubliez
de les librer, vous pouvez vous retrouver dans une situation o il ny aura plus assez de
place disponible, auquel cas votre systme peut se planter.
Lavantage daccder la mmoire de cette faon au lieu de passer par des variables globa-
les est que seules les fonctions qui ont accs au pointeur (qui contient ladresse appro-
prie) peuvent accder aux donnes sur le tas. Cela impose donc que lobjet contenant le
pointeur, ou le pointeur lui-mme, soit explicitement pass toute fonction apportant des
changements, ce qui rduit les risques quune fonction puisse subrepticement modier
des donnes.
Pour que tout cela fonctionne, vous devez donc crer un pointeur vers une zone du tas,
puis le passer comme paramtre aux fonctions, comme on lexplique dans la section qui
suit.
Allouer de lespace avec le mot-cl new
Le mot-cl new permet de rserver de lespace dans le tas. Il doit tre suivi du type de
lobjet an que le compilateur dtermine la quantit de mmoire requise. Ainsi, lexpres-
sion new unsigned short int et lexpression new long rservent, respectivement, 2 et
4 octets sur le tas, en supposant que votre systme utilise un entier court non sign sur
2 octets et une entier long sur 4 octets.
La valeur renvoye par new est ladresse de lemplacement mmoire rserv sur le tas.
Sachant que les adresses mmoire sont stockes dans des pointeurs, il nest pas surprenant
que la valeur de retour de new soit affecte un pointeur. Pour crer un entier court non
sign dans le tas, on crira donc :
unsigned short int * pPointeur;
pPointeur = new unsigned short int;
Bien entendu, on peut faire tout cela en une seule ligne, en initialisant le pointeur lors de sa
dclaration :
unsigned short int * pPointeur = new unsigned short int;
Dans les deux cas, pPointeur pointera sur une variable de type entier court non sign rsi-
dant dans le tas. Vous pouvez galement placer une valeur dans cette zone mmoire :
*pPointeur = 72;
C++Livre Page 222 J eudi, 7. mai 2009 9:45 09
Chapitre 8 Pointeurs 223
Cette instruction signie "Affecter 72 la valeur pointe par pPointeur," ou "Affecter la
valeur 72 lemplacement du tas sur lequel pPointeur pointe".
Sil ne parvient pas rserver de lespace supplmentaire dans le tas (la
mmoire est une ressource limite), new lance une exception (voir Chapitre 20).
Librer de la mmoire : le mot-cl delete
Aprs utilisation, la zone de mmoire dnie laide du mot-cl new doit tre libre pour
tre rendue au systme. Pour cela, appelez le mot-cl delete sur le pointeur.
Il est essentiel de se souvenir que la mmoire alloue grce new nest pas automatique-
ment libre. Si une variable de pointeur pointe vers de la mmoire dans lespace libre et
que le pointeur devient hors de porte, la mmoire nest pas automatiquement rendue
lespace libre. Elle est considre alloue ; le pointeur ntant plus disponible, vous ne
pouvez plus y accder. Cela survient, par exemple, lorsquun pointeur est une variable
locale. Au retour de la fonction dans laquelle ce pointeur est dclar, la porte du pointeur
cesse et celui-ci est perdu. La mmoire alloue avec new nest pas libre (et ne peut plus
ltre puisquon a perdu le pointeur) elle devient indisponible.
Cette situation est appele fuite mmoire, car lespace mmoire ne pourra tre rcupr
quaprs la n du programme : cest comme si la mmoire fuyait de votre ordinateur.
Pour viter ces fuites de mmoire, vous devrez restituer au tas toute la mmoire que vous
avez alloue en utilisant le mot-cl delete :
delete pPointeur;
Cette opration permet de librer la mmoire dont ladresse est stocke dans le pointeur.
Cela revient donc dire "libre lemplacement du tas point par ce pointeur". Le pointeur
reste un pointeur et peut tre raffect. Dans le Listing 8.4, nous effectuerons trois oprations :
dnir une variable et lallouer dans le tas, lutiliser puis la supprimer.
Le plus souvent, on alloue des lments sur le tas partir dun constructeur et on les libre
dans le destructeur. Dans dautres cas, on initialise les pointeurs dans le constructeur, on
alloue les emplacements mesure que lobjet est utilis et, dans le destructeur, on vrie si
ces pointeurs sont nuls et on libre les emplacements quils pointent si ce nest pas le cas.
Lorsque vous appelez delete sur un pointeur, vous librez la mmoire sur
laquelle il pointe. Vous ne devez le faire quune seule fois, sous peine dentra-
ner une erreur dexcution. Pour viter ce dsagrment, affectez la valeur
nulle (0) au pointeur aussitt aprs lavoir supprim. Appeler delete sur un
In
f
o
A
tte
n
tio
n
C++Livre Page 223 J eudi, 7. mai 2009 9:45 09
224 Le langage C++
pointeur nul naura aucun incidence sur le droulement du programme. Voici
un exemple :
Animal *pChien = new Animal; // allouer de la mmoire
delete pChien; // libre la mmoire
pChien = 0; // affecte la valeur nulle au pointeur
//...
delete pChien; // sans dommage pour le programme
Listing 8.4 : Allocation, utilisation et suppression dun pointeur
1: // Listing8.4
2: // Allocation et suppression dun pointeur
3: #include <iostream>
4: int main()
5: {
6: using namespace std;
7: int variableLocale = 5;
8: int * pLocal= &variableLocale;
9: int * pTas = new int;
10: *pTas = 7;
11: cout << "variableLocale : " << variableLocale << endl;
12: cout << "*pLocal: " << *pLocal << endl;
13: cout << "*pTas: " << *pTas << endl;
14: delete pTas;
15: pTas = new int;
16: *pTas = 9;
17: cout << "*pTas: " << *pTas << endl;
18: delete pTas;
19: return 0;
20: }
Ce programme produit le rsultat suivant :
variableLocale : 5
*pLocal: 5
*pTas: 7
*pTas: 9
La ligne 7 dclare et initialise la variable locale (ironiquement nomme variable-
Locale). La ligne 8 dclare un pointeur appel pLocal et linitialise avec ladresse de la
variable locale. La ligne 9 dclare un second pointeur, appel pTas ; comme elle linitia-
lise avec la valeur renvoye par linstruction new int, il dsigne donc un nouvel emplace-
ment rserv pour un entier sur le tas. La valeur 7 est affecte lespace mmoire qui vient
dtre rserv (ligne 10).
C++Livre Page 224 J eudi, 7. mai 2009 9:45 09
Chapitre 8 Pointeurs 225
La ligne 11 afche la valeur de la variable locale (variableLocale), la ligne 12 celle de
lemplacement point par pLocal et la ligne 13, la valeur de lemplacement point par
pTas, On remarque que les valeurs afches par les lignes 11 et 12 se correspondent.
La ligne 14 libre avec delete lespace mmoire qui a t allou la ligne 9. Le pointeur
est donc dissoci de ladresse sur laquelle il pointait et peut maintenant pointer vers un
autre emplacement mmoire. On le raffecte aux lignes 15 et 16 et on afche la valeur du
nouvel emplacement quil pointe la ligne 17. Enn, la ligne 18 libre nouveau lempla-
cement qui a t allou.
Bien que la ligne 18 soit redondante car la mmoire sera de toute faon libre la n du
programme, il est prfrable de librer explicitement cette mmoire. Si le programme est
ensuite modi ou tendu, il sera toujours bnque davoir dj pris en compte cette
tape.
En savoir plus sur les fuites mmoire
Les fuites mmoire sont lun des problmes les plus srieux des pointeurs. Nous venons de
voir une des situations qui provoquent ces fuites, mais raffecter une nouvelle adresse un
pointeur avant de librer lemplacement quil pointait en est une autre. Voici un exemple :
1: unsigned short int * pPointeur = new unsigned short int;
2: *pPointeur = 72;
3: pPointeur = new unsigned short int;
4: *pPointeur = 84;
La ligne 1 cre pPointeur et lui affecte ladresse dun espace mmoire libre sur le tas. La
ligne 2 y stocke la valeur 72. La ligne 3 raffecte pPointeur un autre emplacement
mmoire et la ligne 4 y place la valeur 84. Lemplacement dorigine qui contient 72 est
donc dsormais indisponible puisque le pointeur vers cet emplacement a t raffect : il
nest plus possible daccder cet emplacement et on ne peut plus non plus le librer avant
la n du programme.
Il aurait fallu utiliser le code suivant :
1: unsigned short int * pPointeur = new unsigned short int;
2: *pPointeur = 72;
3: delete pPointeur;
4: pPointeur = new unsigned short int;
5: *pPointeur = 84;
La mmoire sur laquelle pointait lorigine pPointeur est supprime et par consquent
libre la ligne 3.
C++Livre Page 225 J eudi, 7. mai 2009 9:45 09
226 Le langage C++
chaque instruction new devrait correspondre une instruction delete. En
effet, il est important de librer lespace mmoire ds quil nest plus nces-
saire.
Cration dobjets sur le tas
Un pointeur peut pointer sur nimporte quel type de donnes, y compris des instances de
classes. Si vous disposez dune classe Chat, par exemple, vous pouvez dclarer un poin-
teur permettant de pointer des objets de cette classe et crer une instance de Chat sur le
tas, exactement comme vous pouvez crer un objet sur la pile. La syntaxe est la mme que
pour les entiers :
Chat *pChat = new Chat;
Cette instruction appelle le constructeur par dfaut (cest--dire le constructeur qui ne
prend pas de paramtre). Le constructeur sexcute ds quun objet est cr (que ce soit sur
la pile ou sur le tas). Vous ntes pas limit au constructeur par dfaut lors de la cration dun
objet avec new : vous pouvez employer nimporte quel constructeur.
Suppression dobjets du tas
Linstruction delete appelle le destructeur de lobjet avant de librer lemplacement
point, ce qui permet deffectuer des oprations de nettoyage (consistant, gnralement,
librer dautres emplacements du tas, allous par lobjet), exactement comme pour un
objet allou sur la pile. Le Listing 8.5 cre et supprime des objets sur le tas.
Listing 8.5 : Cration et suppression dobjets sur le tas
1: // Listing8.5 - Cration dobjets dans le tas
2: // laide de new et delete
3:
4: #include <iostream>
5:
6: using namespace std;
7:
8: class ChatSimple
9: {
10: public:
11: ChatSimple();
12: ~ChatSimple();
13: private:
14: int sonAge;
In
f
o
C++Livre Page 226 J eudi, 7. mai 2009 9:45 09
Chapitre 8 Pointeurs 227
15: };
16:
17: ChatSimple::ChatSimple ()
18: {
19: cout << "Appel du constructeur." << endl;
20: sonAge = 1;
21: }
22:
23: ChatSimple::~ChatSimple ()
24: {
25: cout << "Appel du destructeur." << endl;
26: }
27:
28: int main()
29: {
30: cout << "ChatSimple Frisky... " << endl;
31: ChatSimple Frisky;
32: cout << "ChatSimple *pRags = new ChatSimple..." << endl;
33: ChatSimple * pRags = new ChatSimple;
34: cout << "Suppression de pRags... " << endl;
35: delete pRags;
36: cout << "Fin, Frisky disparait... " << endl;
37: return 0;
38: }
Ce programme produit le rsultat suivant :
ChatSimple Frisky...
Appel du constructeur.
ChatSimple *pRags = new ChatSimple...
Appel du constructeur.
Suppression de pRags...
Appel du destructeur.
Fin, Frisky disparait...
Appel du destructeur.
Les lignes 8 15 dclarent une classe ChatSimple minimale. La ligne 11 dclare son
constructeur, qui est dni par les lignes 17 21. La ligne 12 dclare son destructeur, qui
est dni par les lignes 23 26. Le constructeur et le destructeur afchent un message
simple indiquant quils ont t appels.
La ligne 31 cre Frisky comme une variable locale ordinaire, donc sur la pile. Cette cra-
tion fait appel au constructeur. Deux lignes plus loin, on cre galement un objet Chat-
Simple, qui sera point par pRags ; cette fois-ci, puisque nous utilisons un pointeur, cet
objet sera cre sur le tas. L aussi, cette cration fait appel au constructeur.
C++Livre Page 227 J eudi, 7. mai 2009 9:45 09
228 Le langage C++
La ligne 35 appelle delete sur le pointeur pRags ; le destructeur est alors appel et la
mmoire qui a t alloue pour contenir cet objet est libre. Lorsque la fonction prend n
la ligne 38, lobjet Frisky devient hors de porte et il est supprim automatiquement
laide du destructeur.
Accs aux donnes membres
Le Chapitre 6 vous a montr comment accder aux donnes et aux fonctions membres
laide de loprateur point (.). Les objets de la classe Chat avaient t crs sur la pile.
Accder aux membres dun objet lors de lutilisation dun pointeur est un peu plus
complexe. Pour lire lun de ses membres, vous devez dabord drfrencer le pointeur,
puis utiliser loprateur point sur lobjet dsign par le pointeur. Il nest pas inutile de le
rpter : vous devez dabord drfrencer le pointeur et vous utilisez ensuite la valeur
drfrence (celle qui est pointe) avec loprateur point pour accder aux membres de
lobjet. Pour accder la fonction membre GetAge() dun objet point par pRags, par
exemple, vous devez donc utiliser linstruction suivante :
(*pRags).GetAge();
Les parenthses servent garantir que pRags sera drfrenc avant laccs la fonction
GetAge() car les parenthses ont la priorit sur tous les autres oprateurs, y compris le
point.
Cette syntaxe pouvant devenir fastidieuse, C++ fournit un raccourci ce genre daccs indi-
rect : loprateur daccs au membre dune classe (->). Cet oprateur se compose de deux
caractres : un tiret court (-) et le signe suprieur (>). Dans le Listing 8.6, le programme
utilise cet oprateur pour accder aux donnes et aux fonctions membres des objets crs
sur le tas.
Loprateur daccs au membre dune classe (->) pouvant aussi tre employ
pour laccs indirect aux membres dun objet (via un pointeur), il peut gale-
ment tre considr comme un oprateur dindirection. Certains programmeurs
lappellent dailleurs oprateur pointer sur car cest exactement ce quil fait.
Listing 8.6 : Lecture de donnes membres des objets dans le tas
1: // Listing8.6 - Accs aux donnes membres des objets du tas
2: // laide de loprateur ->
3:
4: #include <iostream>
5:
In
f
o
C++Livre Page 228 J eudi, 7. mai 2009 9:45 09
Chapitre 8 Pointeurs 229
6: class SimpleChat
7: {
8: public:
9: ChatSimple() {sonAge = 2; }
10: ~ChatSimple() {}
11: int GetAge() const { return sonAge; }
12: void SetAge(int age) { sonAge = age; }
13: private:
14: int sonAge;
15: };
16:
17: int main()
18: {
19: using namespace std;
20: ChatSimple * Frisky = new ChatSimple;
21: cout << "Frisky a " << Frisky->GetAge() << " ans\n";
22: Frisky->SetAge(5);
23: cout << "Frisky a " << Frisky->GetAge() << " ans\n";
24: delete Frisky;
25: return 0;
26: }
Ce programme produit le rsultat suivant :
Frisky a 2 ans
Frisky a 5 ans
La ligne 20 cre une instance de ChatSimple sur le tas, dsigne par le pointeur Frisky.
Le constructeur par dfaut de lobjet dnit lge 2. La mthode GetAge() extrait la
valeur du pointeur, laide de loprateur -> (ligne 21). Frisky tant un pointeur, on
utilise loprateur dindirection -> pour accder ses donnes et ses fonctions membres.
La ligne 22 appelle la mthode SetAge() et la ligne suivante appelle nouveau GetAge().
Cration de donnes membres dans le tas
Outre crer des objets sur le tas, vous pouvez aussi crer des donnes membres sur le tas
car des membres dune classe peuvent eux-mmes tre des pointeurs. Grce ce que vous
avez appris, vous pouvez allouer de la mmoire sur le tas pour ces pointeurs. Cette alloca-
tion peut avoir lieu dans le constructeur de la classe ou dans lune de ses mthodes. Lors-
que vous avez termin dutiliser le membre, vous pouvez et devez le supprimer dans
lune des mthodes ou dans le destructeur, comme le montre le Listing 8.7.
C++Livre Page 229 J eudi, 7. mai 2009 9:45 09
230 Le langage C++
Listing 8.7 : Les pointeurs comme donnes membres dune classe
1: // Listing8.7 - Donnes membres pointeurs
2: // Accs laide de loprateur ->
3:
4: #include <iostream>
5:
6: class ChatSimple
7: {
8: public:
9: ChatSimple();
10: ~ChatSimple();
11: int GetAge() const { return *sonAge; }
12: void SetAge(int age) { *sonAge = age; }
13:
14: int GetPoids() const { return *sonPoids; }
15: void SetPoids (int poids) { *sonPoids = poids; }
16:
17: private:
18: int * sonAge;
19: int * sonPoids;
20: };
21:
22: ChatSimple::ChatSimple()
23: {
24: sonAge = new int(2);
25: sonPoids = new int(5);
26: }
27:
28: ChatSimple::~ChatSimple()
29: {
30: delete sonAge;
31: delete sonPoids;
32: }
33:
34: int main()
35: {
36: using namespace std;
37: ChatSimple *Frisky = new ChatSimple;
38: cout << "Frisky a " << Frisky->GetAge()
39: << " ans " << endl;
40: Frisky->SetAge(5);
41: cout << "Frisky a " << Frisky->GetAge()
42: << " ans\ " << endl;
43: delete Frisky;
44: return 0;
45: }
C++Livre Page 230 J eudi, 7. mai 2009 9:45 09
Chapitre 8 Pointeurs 231
Ce programme produit le rsultat suivant :
Frisky a 2 ans
Frisky a 5 ans
La classe ChatSimple contient deux variables membres, qui sont toutes les deux des poin-
teurs vers des entiers (lignes 18 et 19). Le constructeur les initialise an quils pointent sur des
emplacements du tas et affecte des valeurs par dfaut ces emplacements (lignes 22 26).
Les lignes 24 et 25 utilisent un pseudo-constructeur sur le nouvel entier, en lui passant une
valeur initiale ; cela a pour effet de crer un entier sur le tas et dinitialiser sa valeur ( 2
la ligne 24, et 5 la ligne 25).
Le destructeur libre ensuite la mmoire (lignes 28 32). Il ny a pas besoin daffecter 0
ces pointeurs aprs la libration des zones quils pointaient puisquon est dans le destructeur.
Cest lun des cas o il est possible de transgresser la rgle que nous avions mentionnes.
La fonction appelante (main(), ici) ignore que les variables sonAge et sonPoids sont des
pointeurs ; elle se contente dappeler les mthodes GetAge() et SetAge() sans connatre
les dtails de la gestion mmoire car celle-ci est cache dans limplmentation de la classe,
ce qui est une bonne chose.
Lorsque Frisky est supprim la ligne 41, son destructeur est appel. Il libre alors les
emplacements points par les deux membres. Si ces derniers pointaient sur des objets de
classes dnies par lutilisateur, leurs destructeurs sexcuteraient galement.
Comprendre ce que lon fait
Utiliser des pointeurs comme dans le Listing 8.7 serait assez ridicule dans un vrai programme,
sauf sil y a de bonnes raisons pour que les objets Chat stockent leurs membres de cette
faon. Ici, nous navions aucune raison dutiliser des pointeurs pour accder sonAge et
sonPoids, mais cela pourrait tre trs utile dans dautres cas.
Ceci implique une question vidente : que voulons-nous faire exactement lorsque lon
utilise des pointeurs pour dsigner des variables au lieu de variables normales ? Ceci
dmontre galement toute limportance de la conception. Si lon a conu un objet qui fait
rfrence un autre objet et que le second objet peut natre avant le premier et continuer
dexister aprs lui, le premier objet doit contenir une rfrence au second.
Le premier objet pourrait, par exemple, tre une fentre et le second un document. La
fentre doit pouvoir accder au document, mais elle ne contrle pas la dure de vie du
document. Elle doit, par consquent, contenir une rfrence au document.
Cette implmentation est ralise en C++ laide de pointeurs ou de rfrences. Ces
dernires seront tudies au Chapitre 9.
C++Livre Page 231 J eudi, 7. mai 2009 9:45 09
232 Le langage C++
Le pointeur this
Toute mthode membre dune classe a un paramtre cach : le pointeur this, qui pointe
sur lobjet sur laquelle elle est appele. Lorsque les fonctions GetAge() ou SetAge() sont
appeles sur un objet particulier, elles reoivent en paramtre et en coulisses le pointeur
this de cet objet
Il est possible dutiliser le pointeur this de faon explicite, comme dans le Listing 8.8.
Listing 8.8 : Utilisation du pointeur this
1: // Listing8.8
2: // Utilisation du pointeur this
3:
4: #include <iostream>
5:
6: class Rectangle
7: {
8: public:
9: Rectangle();
10: ~Rectangle();
11: void SetLongueur(int longueur)
12: { this->saLongueur = longueur; }
13: int GetLongueur() const
14: { return this->saLongueur; }
15:
16: void SetLargeur(int largeur)
17: { saLargeur = largeur; }
18: int GetLargeur() const
19: { return saLargeur; }
20:
21: private:
22: int saLongueur;
23: int saLargeur;
24: };
25:
26: Rectangle::Rectangle()
27: {
28: saLargeur = 5;
29: saLongueur = 10;
30: }
31: Rectangle::~Rectangle()
32: {}
33:
34: int main()
35: {
C++Livre Page 232 J eudi, 7. mai 2009 9:45 09
Chapitre 8 Pointeurs 233
36: using namespace std;
37: Rectangle unRect;
38: cout << "unRect mesure " << unRect.LireLongueur()
39: << " cm de long. " endl;
40: cout << "unRect mesure " << unRect.LireLargeur()
41: << " cm de large. " << endl;
42: unRect.SetLongueur(20);
43: unRect.SetLargeur(10);
44: cout << "unRect mesure " << unRect.LireLongueur()
45: << " cm de long. " << endl;
46: cout << "unRect mesure " << unRect.LireLargeur()
47: << " cm de large. " << endl;
48: return 0;
49: }
Ce programme produit le rsultat suivant :
unRect mesure 10cm de long.
unRect mesure 5cm de large.
unRect mesure 20cm de long.
unRect mesure 10cm de large.
Les fonctions daccs SetLongueur() et GetLongueur() [lignes 11-12 et 13-14] utilisent
toutes les deux explicitement le pointeur this pour accder aux variables membres de
lobjet Rectangle, ce qui nest pas le cas de SetLargeur() et GetLargeur() [lignes 16
19]. Leur comportement est pourtant identique aux deux premires et la syntaxe des
instructions est plus simple comprendre.
Sil ne servait qu a, le pointeur this aurait peu dintrt, mais cest en ralit un outil
puissant car, en tant que pointeur, il mmorise ladresse dun objet.
Le Chapitre 10 prsentera un cas dutilisation de this, lorsque nous aborderons la surcharge
des oprateurs. Pour le moment, vous pouvez vous contenter de savoir quil pointe sur
lobjet sur lequel a t appel la mthode.
Vous navez pas vous soucier de sa cration ou de sa suppression : le compilateur sen
charge pour vous.
Pointeurs perdus, incontrlables ou pointeurs fous
Une fois de plus, les problmes de pointeurs refont surface. Ceci est d au fait que les
erreurs imputables aux pointeurs sont parmi les plus difciles dceler et les plus probl-
matiques. Les pointeurs sont la cause de nombreux bogues particulirement difciles dtec-
ter. Un pointeur perdu apparat lorsque vous le supprimez laide du mot-cl delete vous
librez donc la mmoire vers laquelle il pointait sans lui affecter ensuite la valeur null.
C++Livre Page 233 J eudi, 7. mai 2009 9:45 09
234 Le langage C++
Si vous tentez de lutiliser nouveau sans lavoir raffect, le rsultat est imprvisible et,
dans le meilleur des cas, le programme plantera.
Cest comme si le garage SuperAuto avait dmnag mais que vous continuiez utiliser le
numro de tlphone que vous aviez mmoris dans votre combin. Cela pourrait ne pas
avoir de consquences fcheuses un tlphone sonnerait dans une pice vide. En revan-
che, si ce numro a t raffect une usine de munitions, votre appel pourrait dclencher
une explosion et soufer toute la ville !
En rsum, faites attention ne pas utiliser un pointeur qui a t supprim laide de
delete. Il pointe toujours sur lancienne zone de mmoire, mais le compilateur a pu y
mettre dautres donnes ; rutiliser ce pointeur sans lui rallouer un nouvel emplacement
mmoire peut provoquer le plantage de votre programme. Pire encore, le programme peut
continuer tranquillement son excution et se planter quelques minutes plus tard. Ce type
derreur est une vritable bombe retardement. Pour plus de scurit, affectez la valeur
nulle (0) tous les pointeurs que vous supprimez an de dsamorcer ces bombes.
En jargon informatique, les pointeurs perdus sont parfois appels pointeurs
fous, pointeurs incontrlables ou pointeurs pendants.
Le Listing 8.9 illustre la cration dun pointeur perdu.
Nexcutez pas ce programme ! Il cre dlibrment un pointeur perdu. Dans le
meilleur des cas, il provoquera un plantage !
Listing 8.9 : Cration dun pointeur perdu
1: // Listing8.9 - Un pointeur perdu
2:
3: typedef unsigned short int USHORT;
4: #include <iostream>
5:
6: int main()
7: {
8: USHORT * pInt = new USHORT;
9: *pInt = 10;
10: std::cout << "*pInt: " << *pInt << std::endl;
11: delete pInt;
12:
13: long * pLong = new long;
14: *pLong = 90000;
In
f
o
In
f
o
C++Livre Page 234 J eudi, 7. mai 2009 9:45 09
Chapitre 8 Pointeurs 235
15: std::cout << "*pLong: " << *pLong << std::endl;
16:
17: *pInt = 20; // Attention, a t supprim!
18:
19: std::cout << "*pInt: " << *pInt << std::endl;
20: std::cout << "*pLong: " << *pLong << std::endl;
21: delete pLong;
22: return 0;
23: }
Ce programme produit le rsultat suivant :
*pInt: 10
*pLong: 90000
*pInt: 20
*pLong: 65556
(ne tentez pas dexcuter ce programme. Le rsultat que vous obtiendriez serait au mieux
diffrent ; au pire, votre ordinateur planterait).
La ligne 8 dnit pInt comme un pointeur sur un USHORT. la ligne suivante, la valeur 10
est place dans la mmoire alloue pInt. La valeur dsigne safche alors (ligne 10).
Linstruction delete supprime ensuite lemplacement point par pInt, qui devient alors
un pointeur perdu (aprs la ligne 11).
La ligne 13 dclare un nouveau pointeur, pLong, et le fait pointer sur un nouvel emplace-
ment laide de new. Cet emplacement la valeur 90 000 en ligne 14 et la ligne suivant
afche cette valeur.
Cest la ligne 17 que les problmes commencent. La ligne 17 affecte la valeur 20
lemplacement point par pInt alors que ce dernier ne pointe plus sur une adresse valide
puisque la zone mmoire vers laquelle il pointait a t libre en ligne 11. Cette opration
est la porte ouverte provoquera sans aucun doute un dsastre.
La ligne 19 afche le contenu point par pInt,, cest--dire 20. La ligne suivante afche
le contenu point par pLong et lon saperoit quil a soudainement t chang en 65 556.
Ceci nous amne poser deux questions :
1. Pourquoi le contenu de pLong a-t-il chang spontanment alors quon ne la pas
touch ?
2. Ou a t le nombre 20 utilis avec pInt la ligne 17 ?
Comme vous pouvez le deviner, ces deux questions sont troitement lies. La ligne 17 a
affect 20 la zone de mmoire sur laquelle pointait pInt au dbut du programme.
Comme elle a t libre la ligne 11, le compilateur a pu la rutiliser. Lorsque le pointeur
pLong a t cr la ligne 13, il a t associ lancienne zone mmoire pointe par pInt
C++Livre Page 235 J eudi, 7. mai 2009 9:45 09
236 Le langage C++
(sur certaines machines, il est possible que vous ne remarquiez rien selon les emplace-
ments o ont t stockes ces valeurs). Lorsque la valeur 20 a t affecte lemplacement
anciennement point par pInt, elle a donc cras la valeur pointe par pLong. On dit que
le pointeur a t "cras" et cest lune des consquences possibles de lutilisation dun
pointeur perdu.
Il sagit dun bogue particulirement vicieux car la valeur qui a t modie ntait pas
associe au pointeur perdu. Cette modication de la valeur pointe par pLong est un effet
de bord de la mauvaise utilisation de pInt. Dans un programme trs long, une telle erreur
serait trs difcile dtecter et corriger.
Pour la petite histoire, voici pourquoi ladresse mmoire de pLong contient le nombre
65 556 (Listing 8.9).
1. Alors que pInt pointait sur une adresse mmoire particulire, le nombre 10 lui a t
affect.
2. Linstruction delete appele sur pInt a indiqu au compilateur quil pouvait rutiliser
cet emplacement. Il la affect au pointeur pLong.
3. La valeur 90 000 a t affecte *pLong. Sur lordinateur utilis, cette valeur de type
long de 4 octets (00015F 90) est stocke dans lordre inverse, en permutant les
octets. Elle a donc t stocke comme 5F 900001.
4. pInt a t initialis 20 (soit 0014 en notation hexadcimale). Comme il pointait
sur la mme adresse, les deux premiers octets de pLong ont t crass. Le contenu
de ce dernier est donc devenu 00140001.
5. Au moment de lafchage de cette valeur, le systme a effectu une permutation
doctets pour retrouver lordre correct 00010014, qui a t traduit dans sa valeur
dcimale, 65556.
Quelle est la diffrence entre un pointeur nul et un pointeur perdu ?
Lorsque vous supprimez un pointeur, vous demandez au compilateur de librer la
mmoire, mais le pointeur lui-mme existe toujours. Il est devenu un pointeur perdu.
Si vous crivez alors :
monPtr = 0;
de pointeur perdu, il devient pointeur nul.
Normalement, si vous supprimez un pointeur et que vous le supprimez nouveau, votre
programme est dans un tat indni, cest--dire que tout peut arriver. Si vous avez de la
chance, le programme plantera. Si vous supprimez un pointeur nul, aucune consquence
fcheuse nen rsultera.
C++Livre Page 236 J eudi, 7. mai 2009 9:45 09
Chapitre 8 Pointeurs 237
Lutilisation dun pointeur nul ou perdu (en crivant, par exemple monPtr = 5;) est ill-
gal et peut provoquer un plantage. Avec un pointeur nul, le plantage est certain, ce qui
est un autre avantage des pointeurs nuls par rapport aux pointeurs perdus car les plantages
prvisibles sont plus faciles dboguer.
Pointeurs const
Vous pouvez utiliser le mot-cl const avec les pointeurs en le plaant avant et/ou aprs le
type point. Les dclarations suivantes, par exemple, sont toutes correctes :
const int * pUn;
int * const pDeux;
const int * const pTrois;
Chacune effectue toutefois des actions diffrentes :
pUn est un pointeur sur un entier constant. La valeur sur laquelle il pointe ne peut pas
tre modie.
pDeux est un pointeur constant sur un entier. La valeur sur laquelle il pointe peut
tre modie, mais pDeux est constant et ne peut donc pas pointer sur un autre
emplacement.
pTrois est un pointeur constant sur un entier constant. La valeur sur laquelle il pointe
ne peut pas tre modie et il ne peut pas pointer sur un autre emplacement.
Le mot-cl const sapplique llment qui gure immdiatement aprs lui. Si le type
suit le mot-cl const, la valeur est constante. Si la variable se trouve droite du mot-cl
const, cest la variable pointeur elle-mme qui est constante.
const int * p1; // lentier point est constant
int * const p2; // le pointeur p2 est constant :
// il ne peut pas pointer sur autre chose
Pointeurs const et fonctions membres const
Au Chapitre 6, vous avez appris dnir des fonctions constantes laide du mot-cl
const. Si une fonction const tente de modier les donnes de lobjet, le compilateur indique
une erreur.
Si vous dclarez un pointeur vers un objet constant, seules des mthodes constantes
peuvent tre appeles sur ce pointeur, comme le montre le Listing 8.10.
C++Livre Page 237 J eudi, 7. mai 2009 9:45 09
238 Le langage C++
Listing 8.10 : Utilisation de pointeurs sur des objets constants
1: // Listing8.10 - Pointeurs et mthodes const
2:
3: #include <iostream>
4: using namespace std;
5:
6: class Rectangle
7: {
8: public:
9: Rectangle();
10: ~Rectangle();
11: void SetLongueur(int longueur) { saLongueur = longueur; }
12: int GetLongueur() const { return saLongueur; }
13: void SetLargeur(int largeur) { saLargeur = largeur; }
14: int GetLargeur() const { return saLargeur; }
15:
16: private:
17: int saLongueur;
18: int saLargeur;
19: };
20:
21: Rectangle::Rectangle()
22: {
23: saLargeur = 5;
24: saLongueur = 10;
25: }
26:
27: Rectangle::~Rectangle()
28: {}
29:
30: int main()
31: {
32: Rectangle* pRect = new Rectangle;
33: const Rectangle * pRectConst = new Rectangle;
34: Rectangle * const pPtrConst = new Rectangle;
35:
36: cout << "Largeur de pRect: " << pRect->GetLargeur()
37: << " m" << endl;
38: cout << "Largeur de pRectConst: "
38a: << pRectConst->GetLargeur()
39: << " m" << endl;
40: cout << "Largeur de pPtrConst: " << pPtrConst->GetLargeur()
41: << " m" << endl;
C++Livre Page 238 J eudi, 7. mai 2009 9:45 09
Chapitre 8 Pointeurs 239
42:
43: pRect->SetLargeur(10);
44: // pRectConst->SetLargeur(10);
45: pPtrConst->SetLargeur(10);
46:
47: cout << "Largeur de pRect: " << pRect->GetLargeur()
48: << " m\n";
49: cout << "Largeur de pRectConst: "
49a: << pRectConst->GetLargeur()
50: << " m\n";
51: cout << "Largeur de pPtrConst: "
51a: << pPtrConst->GetLargeur()
52: << " m\n";
53: return 0;
54: }
Ce programme produit le rsultat suivant :
Largeur de pRect: 5 m
Largeur de pRectConst: 5 m
Largeur de pPtrConst: 5 m
Largeur de pRect: 10 m
Largeur de pRectConst: 5 m
Largeur de pPtrConst: 10 m
La classe Rectangle est dnie de la ligne 6 la ligne 19. La dclaration de la
mthode constante GetLargeur() gure la ligne 14. La ligne 32 dclare un pointeur
sur Rectangle, nomm pRect. La ligne 33 dclare un pointeur pRectConst vers un
Rectangle constant alors que la ligne 34 dclare un pointeur constant pPtrConst vers
un Rectangle.
Les valeurs de ces trois pointeurs safchent aux lignes 36 41.
la ligne 43, le pointeur pRect est utilis pour modier la largeur de son rectangle
(10 mtres). la ligne 44, le pointeur pRectConst devrait faire de mme, mais il na pas
le droit dappeler une fonction membre non constante car cest un pointeur vers un
Rectangle constant. Ntant pas une instruction valide, nous lavons place en commen-
taire.
la ligne 45, pPtrConst appelle son tour la fonction SetLargeur(). Comme cest un
pointeur constant sur un rectangle, il ne peut pas pointer sur autre chose mais le rectangle
lui-mme ntant pas constant, les mthodes comme GetLargeur() et SetLargeur()
peuvent tre appeles.
C++Livre Page 239 J eudi, 7. mai 2009 9:45 09
240 Le langage C++
Pointeurs const this
Lorsque vous dclarez un objet const, vous dclarez en fait que le pointeur this de cet
objet est un pointeur sur un objet constant. Un pointeur const this ne peut donc tre
utilis quavec des fonctions membres constantes.
Nous reviendrons sur les objets constants et les pointeurs constants dans le prochain chapitre,
lorsque nous prsenterons les rfrences vers des objets constants.
Questions-rponses
Q Pourquoi les pointeurs jouent-ils un rle si important ?
R Les pointeurs sont importants pour de nombreuses raisons. Ils permettent de stocker
les adresses des objets et de les passer en paramtre par rfrence. Au Chapitre 14,
vous verrez comment on les utilise pour mettre en uvre le polymorphisme de classe.
En outre, de nombreux systmes dexploitation et bibliothques de classes crent des
objets en votre nom et renvoient des pointeurs vers ces objets.
Q quoi sert le tas ?
R Les objets stocks sur le tas ne disparaissent pas aprs le retour dune fonction. La pos-
sibilit de stocker des objets sur le tas vous permet de dcider lors de lexcution du
nombre dobjets dont vous avez besoin au lieu dtre oblig de les dclarer lavance.
Ceci sera prsent plus en dtail dans le chapitre suivant.
Q Pourquoi dclarer un objet constant si cela limite ce que je peux en faire ?
R Comme tous les programmeurs, vous souhaitez que le compilateur dtecte les erreurs
avant de crer le chier excutable. Une des erreurs les plus sournoises est le cas dune
fonction qui modie un objet dune manire peu dtectable dans la fonction appelante.
Pour protger les objets qui ne doivent pas tre mis jour, il suft de les dclarer
laide du mot-cl const.
Faire Ne pas faire
Protger les objets passs par rfrence
laide du mot-cl const sils ne doivent pas
tre modis.
Affecter 0 aux pointeurs plutt que de les
laisser non-initialiss ou pendants.
Utiliser un pointeur dont on a libr lempla-
cement quil pointait.
Appeler plusieurs fois delete sur le mme
pointeur.
C++Livre Page 240 J eudi, 7. mai 2009 9:45 09
Chapitre 8 Pointeurs 241
Testez vos connaissances
1. Quel oprateur permet dextraire ladresse dune variable ?
2. Quel oprateur permet dextraire la valeur stocke lemplacement point par un
pointeur ?
3. Quest-ce quun pointeur ?
4. Quelle est la diffrence entre ladresse stocke dans un pointeur et la valeur cette
adresse ?
5. Quelle est la diffrence entre loprateur dindirection et loprateur adresse de ?
6. Quelle est la diffrence entre ces deux dclarations ?
const int * ptrUn;
int * const ptrDeux;
Exercices
1. Que signient ces dclarations ?
a. int * pUn;
b. int vDeux;
c. int * pTrois = &vDeux;
2. La variable votreAge est de type unsigned short. Dclarez un pointeur permettant
de la manipuler.
3. Affectez la valeur 50 la variable votreAge en utilisant le pointeur dclar prcdemment.
4. crivez un petit programme dans lequel vous allez dclarer un entier et un pointeur
vers un entier. Affectez tout dabord ladresse de lentier au pointeur, puis utilisez le
pointeur pour affecter une valeur la variable entire.
5. CHERCHEZ LERREUR : o lerreur se cache-t-elle dans ce programme ?
#include <iostream>
using namespace std;
int main()
{
int *pInt;
*pInt = 9;
cout << "Valeur pInt: " << *pInt;
return 0;
}
C++Livre Page 241 J eudi, 7. mai 2009 9:45 09
242 Le langage C++
6. CHERCHEZ LERREUR : o lerreur se cache-t-elle dans ce programme ?
#include <iostream>
using namespace std;
int main()
{
int Variable = 5;
cout << "Variable: " << Variable << endl;
int *pVar = &Variable;
pVar = 9;
cout << "Variable: " << *pVar << endl;
return 0;
}
C++Livre Page 242 J eudi, 7. mai 2009 9:45 09
9
Rfrences
Au sommaire de ce chapitre
Nature et rle des rfrences
Diffrences entre les rfrences et les pointeurs
Cration et utilisation des rfrences
Limites dutilisation des rfrences
Passage par rfrence de valeurs et dobjets des fonctions et rcupration des valeurs
renvoyes
Dans le chapitre prcdent, vous avez appris grer des objets sur le tas et vous y rfrer
de faon indirecte. Les rfrences prsentent quasiment toutes les fonctionnalits des
pointeurs, mais avec une syntaxe beaucoup plus simple.
C++Livre Page 243 J eudi, 7. mai 2009 9:45 09
244 Le langage C++
Quest-ce quune rfrence ?
Une rfrence est un alias. En dautres termes, une rfrence est initialise avec le nom
dun autre objet appel objet cible. Ds lors, elle se comporte comme lobjet cible et toute
opration effectue sur la rfrence est rpercute sur celui-ci.
Pour crer une rfrence, il suft dindiquer le type de lobjet cible, suivi de loprateur
rfrence (&) et du nom de la rfrence, puis le signe gal et, enn, le nom de lobjet cible.
Une rfrence peut porter nimporte quel nom de variable autoris en C++. Certains
programmeurs utilisent une convention consistant prxer le nom des rfrences par la
lettre "r", comme ici :
int &rReference = VariableEntiere;
Dans cette instruction, rReference est une rfrence une variable de type int, Varia-
bleEntiere. Les rfrences sont diffrentes des autres variables que vous pouvez dclarer
car elles doivent tre initialises lors de leur dclaration. Si vous tentez de crer une varia-
ble rfrence sans lui fournir une valeur initiale, le compilateur produit une erreur.
Le Listing 9.1 montre comment crer et utiliser des rfrences.
Loprateur rfrence et loprateur adresse de utilisent le mme symbole (&). Bien
qutroitement lis, ces oprateurs sont diffrents.
Lespace avant loprateur rfrence est obligatoire, mais celui entre loprateur
et le nom de la variable est facultatif. Par consquent ces deux instructions sont
correctes :
int &rReference = VariableEntiere; // syntaxe OK
int & rReference = VariableEntiere; // syntaxe OK
Listing 9.1 : Cration et utilisation de rfrences
1: //Listing9.1 - Utilisation des rfrences
2:
3: #include <iostream>
4:
5: int main()
6: {
7: using namespace std;
8: int intUn;
9: int &rUneRef = intUn;
10:
11: intUn = 5;
12: cout << "intUn: " << intUn << endl;
13: cout << "rUneRef: " << rUneRef << endl;
In
f
o
C++Livre Page 244 J eudi, 7. mai 2009 9:45 09
Chapitre 9 Rfrences 245
14:
15: rUneRef = 7;
16: cout << "intUn: " << intUn << endl;
17: cout << "rUneRef: " << rUneRef << endl;
18:
19: return 0;
20: }
Ce programme produit le rsultat suivant :
intUn: 5
rUneRef: 5
intUn: 7
rUneRef: 7
La ligne 8 dclare la variable locale intUn de type entier. La ligne suivante dclare une
rfrence un entier, rUneRef, et linitialise pour quelle dsigne intUn. Comme on la
dj mentionn, si vous oubliez dinitialiser une rfrence, le compilateur renvoie une
erreur et ne cre pas le chier objet.
La ligne 11 affecte 5 la variable intUn. Les valeurs contenues dans intUe et rUneRef
sont ensuite afches aux lignes 12 et 13. Bien entendu, elles sont gales.
La ligne 15 affecte 7 rUneRef. Comme il sagit dun alias de la variable intUn, cette
dernire reoit galement la valeur 7, comme le montre les afchages des lignes 16 et 17.
Utilisation de loprateur adresse de (&)
sur des rfrences
Vous savez maintenant que le symbole & permet la fois dobtenir ladresse dune variable
et de dclarer une rfrence. Mais que se passe-t-il si vous demandez ladresse dune
variable rfrence ? Le programme renverra ladresse de lobjet cible, ce qui est logique
puisque la rfrence est un alias de la cible (voir Listing 9.2).
Listing 9.2 : Extraction de ladresse dune rfrence
1: //Listing9.2 - Utilisation des rfrences
2:
3: #include <iostream>
4:
5: int main()
6: {
7: using namespace std;
8: int intUn;
C++Livre Page 245 J eudi, 7. mai 2009 9:45 09
246 Le langage C++
9: int &rUneRef = intUn;
10:
11: intOne = 5;
12: cout << "intUn : " << intUn << endl;
13: cout << "rUneRef : " << rUneRef << endl;
14:
15: cout << "&intUn : " << &intUn << endl;
16: cout << "&rUneRef : " << &rUneRef << endl;
17:
18: return 0;
19: }
Ce programme produit le rsultat suivant :
intUn : 5
rUneRef : 5
&intUn : 0x3500
&rUneRef : 0x3500
Les deux dernires lignes afchent des adresses mmoire qui risquent dtre
particulires votre ordinateur ou une excution du programme ; le rsultat
que vous obtiendrez sera srement diffrent.
rUneRef est nouveau initialise pour faire rfrence la variable intUn. Les adresses des
deux variables safchent : elles sont identiques (lignes 15 et 16).
C++ ne permet pas daccder directement ladresse dune rfrence car cela naurait
aucun intrt, contrairement ladresse dun pointeur ou dune variable. Les rfrences
tant toujours initialises lors de leur cration, elles se comportent toujours comme un
synonyme de leur cible, mme si on leur applique loprateur adresse de.
Supposons que lon dispose dune classe President ; vous pourriez crer une instance de
celle-ci avec linstruction suivante :
President UnPrez;
Vous pouvez ensuite crer une rfrence de President et linitialiser avec cet objet :
President &ChefNation = unPrez;
Un seul President existe. Les deux identicateurs dsignent le mme objet de la mme
classe. Toute action effectue sur ChefNation se rpercutera donc sur UnPrez.
Il faut bien faite la distinction entre le symbole & gurant la ligne 9 du Listing 9.2 qui
dclare une rfrence rUneRef une variable entire et les mmes symboles aux
lignes 15 et 16 qui renvoient les adresses de la variable entire intUn et de la rfrence
A
tte
n
tio
n
C++Livre Page 246 J eudi, 7. mai 2009 9:45 09
Chapitre 9 Rfrences 247
rUneRef. Le compilateur fait la distinction entre ces deux utilisations grce leur contexte
dutilisation.
Lutilisation dune rfrence ne ncessite pas loprateur adresse de. Il suft
dutiliser la rfrence comme vous utiliseriez la variable cible.
Tentative de raffecter des rfrences (ne le faites pas !)
Les variables rfrence ne peuvent pas tre raffectes. Mme des programmeurs chevron-
ns peuvent se laisser piger en croyant raffecter une rfrence alors quils raffectent
simplement lobjet cible, comme le montre le Listing 9.3. Les variables de rfrence sont
toujours des alias de leur cible.
Listing 9.3 : Affectation une rfrence
1: //Listing9.3 - Raffectation dune rfrence
2:
3: #include <iostream>
4:
5: int main()
6: {
7: using namespace std;
8: int intUn;
9: int &rUneRef = intUn;
10:
11: intUn = 5;
12: cout << "intUn : " << intUn << endl;
13: cout << "rUneRef : " << rUneRef << endl;
14: cout << "&intUn : " << &intUn << endl;
15: cout << "&rUneRef : " << &rUneRef << endl;
16:
17: int intDeux = 8;
18: rUneRef = intDeux; // diffrent de ce que vous croyez!
19: cout << "\nintUn : " << intUn << endl;
20: cout << "intDeux : " << intDeux << endl;
21: cout << "rUneRef : " << rUneRef << endl;
22: cout << "&intUn: " << &intUn << endl;
23: cout << "&intDeux : " << &intDeux << endl;
24: cout << "&rUneRef : " << &rUneRef << endl;
25: return 0;
26: }
In
f
o
C++Livre Page 247 J eudi, 7. mai 2009 9:45 09
248 Le langage C++
Ce programme produit le rsultat suivant :
intUn : 5
rUneRef : 5
&intUn : 0012FEDC
&rUneRef : 0012FEDC
intUn : 8
intDeux : 8
rUneRef : 8
&intUn : 0012FEDC
&intDeux : 0012FEE0
&UneRef : 0012FEDC
Cette fois encore, une variable entire et une rfrence un entier sont dclares aux
lignes 8 et 9. La ligne 11 affecte 5 lentier et les valeurs et leurs adresses sont afches
aux lignes 12 15.
La ligne 17 dnit et initialise la variable intDeux. la ligne suivante, le programmeur
tente de raffecter rUneRef pour quelle devienne un alias de cette nouvelle variable, mais
ce nest pas ce qui se passe en ralit : rUneRef continue dtre un alias de intUn et cette
affectation est donc quivalente linstruction suivante :
intUne = intDeux;
Comme lafchage des lignes 19 21 le prouve, intUn, rUneRef et intDeux sont dsor-
mais gales. Lafchage des adresses prouve galement que rUneRef fait toujours rf-
rence intUn, pas intDeux.
Rfrencement des objets
Tous les objets, y compris ceux dnis par lutilisateur, peuvent tre rfrencs. Une rf-
rence ne portant jamais sur une classe, mais sur lun de ses objets, cette expression est
interdite :
int & rRefInt = int; // erreur
Faire Ne pas faire
Utiliser les rfrences pour crer des alias
dobjets.
Initialiser toutes les rfrences.
Tenter de raffecter une rfrence.
Confondre loprateur adresse de et lop-
rateur rfrence.
C++Livre Page 248 J eudi, 7. mai 2009 9:45 09
Chapitre 9 Rfrences 249
La rfrence rRefInt doit tre initialise avec un entier spcique, comme ici :
int Valeur = 200;
int & rRefInt = Valeur;
De la mme faon, il est interdit dinitialiser une rfrence avec la classe Chat :
Chat & rRefChat = Chat; // erreur
Vous devez initialiser une rfrence avec un objet de la classe Chat, comme ici :
Chat Mistigri;
Chat & rRefChat = Mistigri;
La rfrence un objet est utilise comme lobjet lui-mme. Ainsi, les donnes et les
mthodes membres sont accessibles comme avec nimporte quel objet de la classe, laide
de loprateur point (.), comme le montre le Listing 9.4.
Listing 9.4 : Rfrences aux objets
1: // Listing9.4 - Rfrences des objets dune classe
2:
3: #include <iostream>
4:
5: class SimpleChat
6: {
7: public:
8: SimpleChat (int age, int poids);
9: ~SimpleChat() {}
10: int GetAge() { return sonAge; }
11: int GetPoids() { return sonPoids; }
12: private:
13: int sonAge;
14: int sonPoids;
15: };
16:
17: SimpleChat::SimpleChat(int age, int poids)
18: {
19: sonAge = age;
20: sonPoids = weight;
21: }
22:
23: int main()
24: {
25: SimpleChat Frisky(5,4);
26: SimpleChat & rChat = Frisky;
27:
C++Livre Page 249 J eudi, 7. mai 2009 9:45 09
250 Le langage C++
28: std::cout << "Frisky a ";
29: std::cout << Frisky.GetAge() << " ans. " << std::endl;
30: std::cout << "Frisky pse ";
31: std::cout << rChat.GetPoids() << " kilos." std::endl;
32: return 0;
33: }
Ce programme produit le rsultat suivant :
Frisky a 5 ans
Frisky pse 4kilos.
La ligne 25 dclare Frisky comme un objet de la classe SimpleChat. La ligne suivante
cre une rfrence rChat vers un SimpleChat et linitialise avec lobjet Frisky. Les
lignes 29 31 font appel aux mthodes accesseurs en utilisant dabord lobjet lui-mme,
puis sa rfrence. On remarque ce ces deux accs utilisent la mme syntaxe puisquici
aussi, une rfrence est un alias de lobjet rel.
Rfrences
Les rfrences agissent comme un alias de variable. Dclarer une rfrence consiste en
indiquer le type, suivi de loprateur rfrence (&) et du nom de la rfrence. Les rfrences
doivent tre initialises lors de leur cration.
Exemple 1
int sonAge;
int &rAge = sonAge;
Exemple 2
Chat Frisky;
Chat &rRefChat = Frisky;
Pointeurs nuls et rfrences nulles
Lorsquun pointeur na pas t initialis ou lorsque lemplacement quil pointe a t
supprim, il doit tre initialis zro. Ceci ne sapplique pas aux rfrences, car elles
doivent tre initialises sur ce quelles rfrencent lors de la dclaration.
Toutefois, C++ tant utilisable par des pilotes de priphriques, des systmes embarqus
et des systmes en temps rel qui peuvent accder directement au matriel, il faut
pouvoir rfrencer des adresses spciques. La plupart des compilateurs permettent
C++Livre Page 250 J eudi, 7. mai 2009 9:45 09
Chapitre 9 Rfrences 251
donc dinitialiser une rfrence zro ou une autre valeur numrique ; le programme ne
se plantera que si vous tentez dutiliser lobjet et que cette rfrence est invalide.
Il nest cependant pas conseill dutiliser cette possibilit pour une programmation classi-
que. En effet, si un tel programme est port dune machine une autre, de mystrieuses
erreurs pourraient apparatre si vous utilisez des rfrences nulles.
Passage de paramtres par rfrence
Au Chapitre 5, nous avons vu que les fonctions prsentent deux restrictions : les paramtres
sont passs par valeur et linstruction return ne peut renvoyer quune seule valeur.
Passer des valeurs par rfrence permet de contourner ces deux problmes. En C++, vous
pouvez passer des paramtres par rfrence de deux faons : en utilisant des pointeurs ou
des rfrences. Vous passez par rfrence en utilisant un pointeur ou vous passez une rf-
rence en utilisant une rfrence.
La syntaxe qui utilise un pointeur est diffrente de celle qui utilise une rfrence, mais
leffet est le mme : cest lobjet dorigine qui est mis la disposition de la fonction, non
une copie de celui-ci
Passer un objet par rfrence permet la fonction de modier lobjet rfrenc. Au Chapi-
tre 5, vous avez appris que les paramtres des fonctions sont passs via la pile. Lorsquune
valeur est passe par rfrence avec un pointeur ou une rfrence , seule ladresse de
lobjet original est place sur la pile, pas lobjet lui-mme. En ralit, sur certains ordina-
teurs, ladresse est stocke dans un registre, ce qui vite dutiliser la pile. Dans les deux
cas, comme cest une adresse qui est transmise, le compilateur peut identier lobjet
dorigine et les modications auront lieu sur celui-ci, pas sur une copie.
Dans le Listing 5.5 du Chapitre 5, nous avions utilis une fonction de permutation appele
swap() qui naffectait pas les valeurs dans la fonction appelante. titre de rappel, le
Listing 9.5 reproduit cet exemple.
Listing 9.5 : Passage de paramtres par valeur
1: //Listing9.5 - Passage par valeur
2: #include <iostream>
3:
4: using namespace std;
5: void swap(int x, int y);
6:
7: int main()
8: {
9: int x = 5, y = 10;
C++Livre Page 251 J eudi, 7. mai 2009 9:45 09
252 Le langage C++
10:
11: cout << "Dans Main. Avant change, x : " << x << " y : "
11a: << y << endl;
12: swap(x, y);
13: cout << "Dans Main. Aprs change, x : " << x << " y : "
13a: << y << endl;
14: return 0;
15: }
16:
17: void swap (int x, int y)
18: {
19: int temp;
20:
21: cout << "Dans Swap. Avant change, x : " << x << " y: "
21a: << y << endl;
22:
23: temp = x;
24: x = y;
25: y = temp;
26:
27: cout << "Dans Swap. Aprs change, x : " << x << " y : "
27a: << y << endl;
28: }
Ce programme produit le rsultat suivant :
Dans Main. Avant change, x : 5 y : 10
Dans Swap. Avant change, x : 5 y : 10
Dans Swap. Aprs change, x : 10 y : 5
Dans Main. Aprs change, x : 5 y : 10
Ce programme initialise deux variables dans la fonction main(), puis les passe la fonc-
tion swap() qui semble les intervertir. De retour dans la fonction principale, ces valeurs
nont pas t modies !
Le problme, ici, est que x et y ont t passes par valeur et que la fonction en a donc cr
des copies locales. Ces copies ont donc t modies et ont t ensuite supprimes la n
de la fonction, en mme temps que ses variables locales. Il est prfrable de les passer par
rfrence, an de modier les valeurs sources des variables, et non des copies locales.
Pour ce faire, vous avez deux solutions en C++ : soit vous faites en sorte que les param-
tres de la fonction swap() soient des pointeurs sur les valeurs dorigine, soit vous passez
en paramtre des rfrences aux valeurs originales.
C++Livre Page 252 J eudi, 7. mai 2009 9:45 09
Chapitre 9 Rfrences 253
Utilisation de pointeurs avec la fonction swap()
Si vous passez des pointeurs en paramtre, vous transmettez en fait les adresses des objets,
ce qui fait que la fonction peut modier les valeurs situes ces adresses. swap() doit
donc tre dclare pour attendre deux pointeurs dentiers. En drfrenant ces pointeurs,
la fonction pourra alors accder aux vritables valeurs de x et y et les changer. Cest ce
que fait le Listing 9.6
Listing 9.6 : Passage par rfrence laide de pointeurs
1: //Listing9.6 Passage par rfrence
2: #include <iostream>
3:
4: using namespace std;
5: void swap(int *x, int *y);
6:
7: int main()
8: {
9: int x = 5, y = 10;
10:
11: cout << "Dans Main. Avant change, x : " << x << " y : "
11a: << y << endl;
12: swap(&x,&y);
13: cout << "Dans Main. Aprs change, x : " << x << " y : "
13a: << y << endl;
14: return 0;
15: }
16:
17: void swap (int *px, int *py)
18: {
19: int temp;
20:
21: cout << "Dans Swap. Avant change, *px : " << *px <<
22: " *py : " << *py << endl;
23:
24: temp = *px;
25: *px = *py;
26: *py = temp;
27:
28: cout << "Dans Swap. Aprs change, *px : " << *px <<
29: " *py : " << *py << endl;
30:
31: }
C++Livre Page 253 J eudi, 7. mai 2009 9:45 09
254 Le langage C++
Ce programme produit le rsultat suivant :
Dans Main. Avant change, x : 5 y : 10
Dans Swap. Avant change, *px : 5 *py : 10
Dans Swap. Apres change, *px : 10 *py : 5
Dans Main. Apres change, x : 10 y : 5
a marche ! la ligne 5, le prototype de la fonction swap() indique que les paramtres
sont dsormais des pointeurs vers des entiers au lieu de variables entires. Lors de
lappel la fonction, en ligne 12, on passe en paramtre les adresses de x et de y : on
peut voir que ce sont des adresses qui sont passes car lon utilise loprateur adresse
de (&).
La ligne 19 dclare la variable locale temp dans la fonction swap(). Il ne sagit pas dun
pointeur, mais dune variable qui contiendra la valeur de *px (cest--dire la valeur de
lemplacement point par px, qui correspond celle de x dans la fonction appelante).
Lorsque la fonction se termine, cette variable locale disparait de la pile.
La valeur ladresse px est affecte temp la ligne 24, lemplacement point par px
recevant ensuite la valeur situe ladresse py (ligne 25). La valeur stocke dans la variable
temp est ensuite copie dans lemplacement point par py.
Les valeurs de la fonction appelante dont on a transmis les adresses la fonction swap()
sont ainsi interverties.
Utililisation de rfrences avec la fonction swap()
Bien que programme prcdent fasse ce que lon attend, la syntaxe de la fonction swap()
est un peu lourde. Les oprations rptes de drfrencement des pointeurs sont difciles
relire et sujettes aux erreurs si vous oubliez de drfrencer le pointeur, le compilateur
vous permettra quand mme de lui affecter un entier et cest lutilisateur de votre fonction
qui subira lerreur. En outre, devoir explicitement passer en paramtre les adresses des
variables de la fonction appelante lors de lappel expose aux utilisateurs le fonctionnement
interne de la fonction swap().
Un langage orient objet comme C++ se doit de masquer les dtails de fonctionnement
interne dune fonction. Lutilisation des fonction doit tre totalement transparente pour les
utilisateurs. Lutilisation de paramtres pointeurs reporte donc la charge du passage par
rfrence sur la fonction appelante alors que cela devrait tre gr par la fonction appele.
Le Listing 9.7 propose une autre criture de swap(), qui utilise cette fois-ci des paramtres
rfrences.
C++Livre Page 254 J eudi, 7. mai 2009 9:45 09
Chapitre 9 Rfrences 255
Listing 9.7 : Fonction swap() rcrite avec des rfrences
1: //Listing9.7 Passage par rfrence
2: // laide de rfrences!
3: #include <iostream>
4:
5: using namespace std;
6: void swap(int &x, int &y);
7:
8: int main()
9: {
10: int x = 5, y = 10;
11:
12: cout << "Dans Main. Avant change, x : " << x << " y : "
13: << y << endl;
14:
15: swap(x,y);
16:
17: cout << "Dans Main. Aprs change, x : " << x << " y "
18: << y << endl;
19:
20: return 0;
21: }
22:
23: void swap (int &rx, int &ry)
24: {
25: int temp;
26:
27: cout << "Dans Swap. Avant change, rx : " << rx
28: << " ry : " << ry << endl;
29:
30: temp = rx;
31: rx = ry;
32: ry = temp;
33:
34:
35: cout << "Dans Swap. Aprs change, rx : " << rx
36: << " ry : " << ry << endl;
37:
38: }
Ce programme produit le rsultat suivant :
Dans Main. Avant change, x : 5 y : 10
Dans Swap. Avant change, rx : 5 ry : 10
Dans Swap. Aprs change, rx : 10 ry : 5
Dans Main. Aprs change, x : 10, y : 5
C++Livre Page 255 J eudi, 7. mai 2009 9:45 09
256 Le langage C++
Comme dans lexemple prcdent, le programme commence par dclarer deux variables
(ligne 10) et afche leurs valeurs (ligne 12). La ligne 15 appelle la fonction swap() en lui
passant en paramtre les variables x et y (et non leurs adresses).
Le programme va alors directement la ligne 23, o ces paramtres sont identis comme
des rfrences. Les valeurs de ces variables sont afches la ligne 27 et vous pouvez
constater quil nest pas ncessaire dutiliser des oprateurs spciaux : ces variables sont
des alias des variables dorigine et peuvent donc tre utilises comme telles.
Une fois interverties (lignes 30 32), on afche nouveau les valeurs puis le programme
revient en la ligne 17 pour afcher les variables de la fonction main(). Les paramtres de
swap() tant des rfrences, les oprations ralises dans la fonction appele se sont
rpercutes au variables initiales de la fonction appelante.
Comme on le voit dans ce listing, les rfrences permettent donc dutiliser des variables
normales et de disposer de toute la puissance du passage par rfrence avec les pointeurs !
En-ttes et prototypes de fonctions
Dans les deux listings ci-dessus, les programmes ont utilis successivement des pointeurs
et des rfrences. Lutilisation des rfrences est plus aise et rend le code plus facile
comprendre mais comment la fonction appelante peut-elle savoir si les paramtres ont t
passs par valeur ou par rfrence ? En tant que client (ou utilisateur) de swap(), le
programmeur doit sassurer que cette fonction modiera bien ses paramtres.
Pour le savoir, il peut examiner les paramtres dclars dans le prototype de la fonction.
Ce dernier se trouve gnralement dans un chier en-tte, accompagn dautres prototy-
pes. Grce ces informations, le programmeur peut donc savoir que ses valeurs seront
passes par rfrence la fonction swap() et seront donc correctement changes. Le
prototype swap() apparat la ligne 6 du Listing 9.7 : on peut constater que les deux
paramtres sont passs comme des rfrences.
Si swap() avait t une fonction membre dune classe, la dclaration de classe galement
disponible dans le chier en-tte aurait fourni ces informations.
En C++, les clients des classes et des fonctions peuvent se er aux chiers en-ttes pour
connatre tout ce quils ont besoin de savoir. Ce chier agit alors comme une interface vers
la classe ou la fonction, dont limplmentation est cache lutilisateur. Celui-ci peut alors
se consacrer son problme et utiliser les classes ou les fonctions sans se soucier des
dtails de leur fonctionnement.
Lors de la construction du pont de Brooklyn, le colonel John Roebling supervisait totale-
ment les travaux, de la fabrication des matriaux (cbles, briques et ciment) au respect des
plans. De nos jours, les ingnieurs utilisent mieux leur temps en ne soccupant que de la
C++Livre Page 256 J eudi, 7. mai 2009 9:45 09
Chapitre 9 Rfrences 257
mise en uvre des matriaux, sans soccuper de savoir comment ils ont t fabriqus du
moment quils correspondent au cahier des charges.
La dmarche est la mme en C++. Les programmeurs utilisent des classes et des fonctions
prouves sans se proccuper de leur fonctionnement internes. Ces "composants logiciels"
peuvent tre assembls pour produire un programme, exactement comme les cbles, les
briques et le ciment sont assembls pour fabriquer des immeubles et des ponts.
Comme un ingnieur des ponts et chausses se reporte la documentation technique pour
connatre la capacit, le volume et le dbit dun tuyau dvacuation, par exemple, le dve-
loppeur examine la dclaration dune fonction ou dune classe pour connatre les services
quelle fournit, les paramtres quelle attend et les rsultats quelle produit.
Renvoi de plusieurs valeurs
Comme nous lavons vu plus haut, une fonction ne peut renvoyer quune seule valeur la
fois. Comment faire pour en renvoyer deux ? La premire solution consiste passer deux
objets par rfrence la fonction. Le passage par rfrence autorisant une fonction modi-
er les objets dorigine, elle peut donc renvoyer deux lments dinformation. Cette prati-
que permet de se passer de la valeur renvoye par la fonction, qui peut par exemple servir
signaler une erreur.
L encore, on peut utiliser des rfrences ou des pointeurs. Dans le Listing 9.8, la fonction
renvoie trois valeurs, la valeur de renvoi habituelle et deux paramtres pointeurs.
Listing 9.8 : Renvoi de valeurs laide de pointeurs
1: //Listing9.8 - Renvoi de plusieurs valeurs par une fonction
2:
3: #include <iostream>
4:
5: using namespace std;
6: short Facteur(int n, int* pCarre, int* pCube);
7:
8: int main()
9: {
10: int nombre, carre, cube;
11: short erreur;
12:
13: cout << "Entrez un nombre entre 0 et 20: ";
14: cin >> nombre;
15:
16: erreur = Facteur(nombre, &carre, &cube);
17:
C++Livre Page 257 J eudi, 7. mai 2009 9:45 09
258 Le langage C++
18: if (!erreur)
19: {
20: cout << "Nombre: " << nombre << endl;
21: cout << "Carr: " << carre << endl;
22: cout << "Cube: " << cube << endl;
23: }
24: else
25: cout << "Erreur!" << endl;
26: return 0;
27: }
28:
29: short Facteur(int n, int *pCarre, int *pCube)
30: {
31: short Valeur = 0;
32: if (n > 20)
33: Valeur = 1;
34: else
35: {
36: *pCarre = n*n;
37: *pCube = n*n*n;
38: Valeur = 0;
39: }
40: return Valeur;
41: }
Ce programme produit le rsultat suivant :
Entrez un nombre entre 0 et 20: 3
Valeur: 3
Carr: 9
Cube: 27
La ligne 10 dnit les variables de type entier court, nombre, carre, et cube. Puis nombre
reoit la valeur saisie par lutilisateur la ligne 14. La ligne 16 transmet cette valeur et les
adresses de carre et cube la fonction Facteur().
La ligne 32 examine le premier paramtre, qui a t pass par valeur. Sil est suprieur
20 (valeur maximale gre par la fonction), la valeur renvoye (Valeur) correspondra
une erreur, 0 indiquant que tout sest bien pass. Valeur est renvoye la fonction appe-
lante la ligne 40.
Les valeurs correspondant au carr et au cube du nombre saisi ne sont pas renvoyes de
manire traditionnelle mais en modiant les emplacements points par les deux paramtres
pointeurs passs la fonction.
Les valeurs de renvoi sont donc affectes aux pointeurs (lignes 36 et 37). Ces valeurs sont
affectes aux variables initiales au moyen du mcanisme dindirection, que lon peut
C++Livre Page 258 J eudi, 7. mai 2009 9:45 09
Chapitre 9 Rfrences 259
reprer par lutilisation de loprateur de drfrencement (*) sur les noms de pointeurs.
la ligne 38, Valeur reoit la valeur 0 signiant une n normale du traitement et elle est
renvoye lappelant la ligne 40.
Le passage par rfrence ou par pointeur permettant un accs non contrl aux
attributs dobjets et aux mthodes, il est conseill de ne passer que le minimum
requis pour que la fonction ralise sa tche. Cest un gage de scurit et de lisi-
bilit.
Renvoi de valeurs par rfrence
Bien que le Listing 9.8 fonctionne correctement, il est conseill de privilgier les rfren-
ces aux pointeurs car la maintenance et la relecture du code seront plus faciles. Le
Listing 9.9 correspond au Listing 9.8 mais utilise des rfrences la place des pointeurs.
Il inclut galement une autre amlioration puisquil utilise un enum pour que la valeur de
retour soit plus simple comprendre. Au lieu de renvoyer 0 ou 1, cet enum permet la
fonction de renvoyer SUCCES ou ECHEC.
Listing 9.9 : Utilisation de rfrences
1: //Listing9.9
2: // Renvoi de plusieurs valeurs par une fonction
3: // laide de rfrences
4: #include <iostream>
5:
6: using namespace std;
7:
8: enum CODE_ERREUR { SUCCES, ECHEC };
9:
10: CODE_ERREUR Facteur(int, int&, int&);
11:
12: int main()
13: {
14: int nombre, carre, cube;
15: CODE_ERREUR resultat;
16:
17: cout << "Entrez un nombre entre 0 et 20: ";
18: cin >> nombre;
19:
20: resultat = Facteur(nombre, carre, cube);
21:
22: if (resultat == SUCCES)
23: {
24: cout << "Nombre: " << nombre << endl;
A
s
t
u
c
e
C++Livre Page 259 J eudi, 7. mai 2009 9:45 09
260 Le langage C++
25: cout << "Carr: " << carre << endl;
26: cout << "Cube: " << cube << endl;
27: }
28: else
29: cout << "Erreur!" << endl;
30: return 0;
31: }
32:
33: CODE_ERREUR Facteur(int n, int &rCarre, &rCube)
34: {
35: if (n > 20)
36: return ECHEC; // simple code derreur
37: else
38: {
39: rCarre = n*n;
40: rCube = n*n*n;
41: return SUCCES;
42: }
43: }
Ce programme produit le rsultat suivant :
Entrez un nombre entre 0 et 20: 3
Valeur: 3
Carr: 9
Cube: 27
Le Listing 9.9 est identique au prcdent, deux exceptions prs : lnumration
CODE_ERREUR permet de renvoyer un code derreur bien plus explicite (lignes 36 et 41) et
la gestion des erreurs est plus complte (ligne 22).
Notez que la fonction Facteur() est dsormais dclare pour attendre des rfrences aux
variables carre et cube, et non des pointeurs. Ceci facilite la fois la manipulation de ces
paramtres et la relecture du code.
Efcacit du passage de paramtres par rfrence
Le passage par valeur dun objet une fonction implique une copie de cet objet. Lorsque
la fonction appele renvoie un objet par valeur, une autre copie a lieu.
Au Chapitre 5, nous avons vu que ces objets sont stocks sur la pile. Ces oprations pren-
nent du temps et consomme de la mmoire. Ces cots soient ngligeables pour les petits
objets comme les entiers.
C++Livre Page 260 J eudi, 7. mai 2009 9:45 09
Chapitre 9 Rfrences 261
En revanche, ils ne le sont plus du tout pour les objets plus volumineux, crs par lutilisa-
teur. Dans la pile, la taille dun tel objet correspond en effet la somme de toutes les varia-
bles membres qui le composent. Ces variables sont parfois elles-mmes des objets dnis
par lutilisateur. Passer en paramtre une structure aussi massive en la copiant sur la pile
peut avoir un impact important sur les performances et la consommation mmoire du
programme.
Un autre cot sajoute encore. Avec les classes que vous crez, chacune de ces copies
temporaires est cre par un appel un constructeur spcial, appel constructeur de copie,
qui est automatiquement appel par le compilateur . Au Chapitre 10, vous apprendrez les
utiliser et crer vos propres constructeurs de copies. Pour linstant, il vous suft de savoir
que le constructeur de copie est appel chaque fois quune copie temporaire de lobjet est
plac sur la pile.
Lorsque cet objet temporaire est dtruit la n de lexcution de la fonction, son destruc-
teur est appel. Si la fonction renvoie un objet par valeur, il faut galement faire une copie
de cet objet puis le dtruire.
Avec des objets volumineux, ces appels de constructeurs et de destructeurs peuvent avoir
un impact sur la vitesse dexcution du programme et sur son utilisation de la mmoire.
Pour illustrer ce phnomne, le Listing 9.10 prsente une version pure de la classe
SimpleChat les vritables objets manipuls par les programmes sont plus gros et plus
coteux, mais cet exemple suft montrer la frquence des appels du constructeur de
copie et du destructeur.
Listing 9.10 : Passage dobjets par rfrence
1: //Listing9.10 - Passage de pointeurs sur des objets
2:
3: #include <iostream>
4:
5: using namespace std;
6: class SimpleChat
7: {
8: public:
9: SimpleChat (); // constructeur
10: SimpleChat(SimpleChat&); // constructeur de copie
11: ~SimpleChat(); // destructeur
12: };
13:
14: SimpleChat::SimpleChat()
15: {
16: cout << "Constructeur de SimpleChat..." << endl;
17: }
18:
C++Livre Page 261 J eudi, 7. mai 2009 9:45 09
262 Le langage C++
19: SimpleChat::SimpleChat(SimpleChat&)
20: {
21: cout << "Constructeur de copie de SimpleChat..." << endl;
22: }
23:
24: SimpleChat::~SimpleChat()
25: {
26: cout << "Destructeur de SimpleChat..." << endl;
27: }
28:
29: SimpleChat FonctionUn (SimpleChat leChat);
30: SimpleChat* FonctionDeux (SimpleChat *leChat);
31:
32: int main()
33: {
34: cout << "Cration dun objet Chat..." << endl;
35: SimpleChat Frisky;
36: cout << "Appel de FonctionUn..." << endl;
37: FonctionUn(Frisky);
38: cout << "Appel de FonctionDeux..." << endl;
39: FonctionDeux(&Frisky);
40: return 0;
41: }
42:
43: // FonctionUn, passage par valeur
44: SimpleChat FonctionUn(SimpleChat leChat)
45: {
46: cout << "Retour de FonctionUn..." << endl;
47: return leChat;
48: }
49:
50: // FonctionDeux, passage par rfrence
51: SimpleChat* FonctionDeux(SimpleChat *leChat)
52: {
53: cout << "Retour de FonctionDeux..." << endl;
54: return leChat;
55: }
Ce programme produit le rsultat suivant :
Cration de lobjet Chat...
Constructeur de SimpleChat...
Appel de FonctionUn...
Constructeur de copie de SimpleChat...
Retour de FonctionUn...
C++Livre Page 262 J eudi, 7. mai 2009 9:45 09
Chapitre 9 Rfrences 263
Constructeur de copie de SimpleChat...
Destructeur de SimpleChat...
Destructeur de SimpleChat...
Appel de FonctionDeux...
Retour de FonctionDeux...
Destructeur de SimpleChat...
Le Listing 9.10 cre lobjet SimpleChat, puis appelle deux fonctions. La premire reoit
leChat par valeur, puis le renvoie par valeur. La seconde reoit un pointeur vers lobjet, et
non lobjet lui-mme, puis renvoie un pointeur vers lobjet.
Les lignes 6 12 dclarer une classe SimpleChat minimale. Le constructeur, le construc-
teur de copie et le destructeur se contentent de produire un message pour vous informer du
droulement du programme.
la ligne 34, la fonction main() afche le premier message qui apparat dans le rsultat.
La ligne suivante instancie un objet SimpleChat, ce qui provoque lappel du constructeur
et donc lafchage de la deuxime ligne du rsultat
La ligne 36 signale lappel de FonctionUn(). Cette dernire reoit lobjet SimpleChat
par valeur, ce qui provoque la cration dune copie locale sur la pile et donc lappel du
constructeur de copie, qui afche la quatrime ligne du rsultat.
Le programme va ensuite directement la ligne 46, dans la fonction appele qui afche la
cinquime ligne du rsultat. La fonction principale reprend la main et rcupre lobjet
SimpleChat renvoy par valeur, ce qui a pour effet de crer une autre copie de lobjet et
donc dappeler le constructeur de copie qui afche la sixime ligne du rsultat.
La valeur renvoye par FonctionUn() ntant affecte aucun objet, le destructeur
supprime lobjet temporaire et afche la septime ligne. FonctionUn() stant termine,
sa copie locale devient hors de porte et est dtruite par le destructeur qui afche la
huitime ligne.
La fonction principale reprend le contrle et appelle la fonction FonctionDeux() en
passant le paramtre par rfrence, ce qui ne produit pas de copie de lobjet sur la pile.
FonctionDeux() afche le message qui apparat la dixime ligne du rsultat et renvoie
lobjet SimpleChat par rfrence, ce qui vite l aussi dappeler le constructeur et le
destructeur.
Enn, le programme se termine et Frisky sort de sa porte, ce qui cause un dernier appel
au destructeur, qui afche la dernire ligne du rsultat.
La fonction FonctionUn() ayant reu le paramtre SimpleChat par valeur a donc provoqu
deux appels du constructeur de copie et deux appels du destructeur, alors que Fonction-
Deux() nen a produit aucun.
C++Livre Page 263 J eudi, 7. mai 2009 9:45 09
264 Le langage C++
Passer un pointeur const
Le passage dun pointeur FonctionDeux() a beau tre plus efcace, il nen demeure pas
moins dangereux. Cette fonction nest, en effet, pas cense modier lobjet qui lui est
pass, or elle reoit son adresse ce qui expose franchement lobjet original des modications
et brise la protection offerte par le passage par valeur.
Le passage par valeur revient donner un muse une photographie de la pice matresse
de votre collection : si un vandale labime, loriginal ne sera pas touch. Le passage par
rfrence, au contraire, consiste donner votre adresse au muse et inviter les visiteurs
venir chez vous voir cette uvre.
La solution consiste passer un pointeur sur une constante SimpleChat, ce qui aura pour
effet dinterdire lappel de mthodes modicatrices sur cet objet et donc de le protger
contre les modications.
Le passage dune rfrence constante permet aux visiteurs de contempler votre peinture,
mais leur interdit de laltrer de quelque faon que ce soit. Le Listing 9.11 donne un exemple
dutilisation dun paramtre pointeur vers une constante.
Listing 9.11 : Passage de pointeur sur un objet constant
1: //Listing9.11 - Passage de pointeurs dobjets
2:
3: #include <iostream>
4:
5: using namespace std;
6: class SimpleChat
7: {
8: public:
9: SimpleChat();
10: SimpleChat(SimpleChat&);
11: ~SimpleChat();
12:
13: int GetAge() const { return sonAge; }
14: void SetAge(int age) { sonAge = age; }
15:
16: private:
17: int sonAge;
18: };
19:
20: SimpleChat::SimpleChat()
21: {
22: cout << "Constructeur de SimpleChat..." << endl;
23: wAge = 2;
24: }
C++Livre Page 264 J eudi, 7. mai 2009 9:45 09
Chapitre 9 Rfrences 265
25:
26: SimpleChat::SimpleChat(SimpleChat&)
27: {
28: cout << "Constructeur de copie de SimpleChat..." << endl;
29: }
30:
31: SimpleChat::~SimpleChat()
32: {
33: cout << "Destructeur de SimpleChat..." << endl;
34: }
35:
36: const SimpleChat * const FonctionDeux
37: (const SimpleChat * const leChat);
38:
39: int main()
40: {
41: cout << "Cration dun objet Chat..." << endl;
42: SimpleChat Frisky;
43: cout << "Frisky a ";
44: cout << Frisky.GetAge();
45: cout << " ans" << endl;
46: int age = 5;
47: Frisky.SetAge(age);
48: cout << "Frisky a ";
49: cout << Frisky.GetAge();
50: cout << " ans" << endl;
51: cout << "Appel de FonctionDeux..." << endl;
52: FonctionDeux(&Frisky);
53: cout << "Frisky a ";
54: cout << Frisky.GetAge();
55: cout << " ans" << endl;
56: return 0;
57: }
58:
59: // FonctionDeux attend un pointeur const
60: const SimpleChat * const FunctionTwo
61: (const SimpleChat * const leChat)
62: {
63: cout << "Fin de FonctionDeux..." << endl;
64: cout << "Frisky a maintenant " << leChat->GetAge();
65: cout << " ans " << endl;
66: // leChat->SetAge(8); const!
67: return leChat;
68: }
C++Livre Page 265 J eudi, 7. mai 2009 9:45 09
266 Le langage C++
Ce programme produit le rsultat suivant :
Cration dun objet Chat...
Constructeur de SimpleChat...
Frisky a 2 ans
Frisky a 5 ans
Appel de FonctionDeux...
Fin de FonctionDeux...
Frisky a maintenant 5 ans
Frisky a 5 ans
Destructeur de SimpleChat...
SimpleChat contient deux fonctions daccs : lune est constante (GetAge(), la
ligne 13), lautre, non constante (SetAge(), la ligne 14). La variable membre sonAge est
dnie la ligne 17.
Les appels respectifs au constructeur, au constructeur de copie et au destructeur sont
signals par des messages. On constate que le constructeur de copie nest jamais appel
puisque lobjet est pass par rfrence et quaucune copie nest donc ralise. La ligne 42
cre un objet dont lge par dfaut est afch par la ligne suivante.
La ligne 47 modie la variable sonAge en appelant la mthode daccs SetAge() et la
ligne suivante afche cette nouvelle valeur. FonctionDeux() a t lgrement modie :
son paramtre, comme sa valeur de retour, est dsormais un pointeur constant sur un objet
constant (ligne 36).
Le paramtre et la valeur de renvoi tant toujours passs par rfrence, aucune copie nest
ralise et le constructeur de copie nest donc pas sollicit. Cependant, lobjet point dans
la fonction FonctionDeux() est dsormais constant et ne peut donc pas appeler la fonc-
tion SetAge() qui, elle, ne lest pas. Si la ligne 66 navait pas t mise en commentaire, le
programme naurait pas pu pourrait pas tre compil.
Lobjet cr dans la fonction main() ntant pas constant, Frisky peut appeler SetAge().
Ladresse de cet objet non constant est passe la fonction FonctionDeux() mais, comme
cette fonction dclare son paramtre comme tant un pointeur constant vers un objet
constant, lobjet est trait comme sil tait constant !
Une autre solution : le passage de rfrences
Dans le Listing 9.11, le programme passe les paramtres par rfrence, ce qui vite de
crer des copies locales et donc dappeler le constructeur de copie et le destructeur. Il
utilise pour cela des pointeurs constants sur des objets constants an dempcher la fonc-
tion de modier lobjet. Toutefois, le passage de pointeurs exige une notation assez lourde.
C++Livre Page 266 J eudi, 7. mai 2009 9:45 09
Chapitre 9 Rfrences 267
Comme on sait que ces objets ne seront jamais nuls, il serait plus simple de travailler sur
des rfrences, comme le montre le Listing 9.12.
Listing 9.12 : Passage de rfrences des objets
1: //Listing9.12 - Passage de rfrences des objets
2:
3: #include <iostream>
4:
5: using namespace std;
6: class SimpleChat
7: {
8: public:
9: SimpleChat();
10: SimpleChat(SimpleChat&);
11: ~SimpleChat();
12:
13: int GetAge() const { return sonAge; }
14: void SetAge(int age) { sonAge = age; }
15:
16: private:
17: int sonAge;
18: };
19:
20: SimpleChat::SimpleChat()
21: {
22: cout << "Constructeur de SimpleChat..." << endl;
23: wAge = 2;
24: }
25:
26: SimpleChat::SimpleChat(SimpleChat&)
27: {
28: cout << "Constructeur de copie de SimpleChat..." << endl;
29: }
30:
31: SimpleChat::~SimpleChat()
32: {
33: cout << "Destructeur de SimpleChat..." << endl;
34: }
35:
36: const SimpleChat & FonctionDeux(const SimpleChat & leChat);
37:
38: int main()
39: {
40: cout << "Cration dun objet Chat..." << endl;
41: SimpleChat Frisky;
42: cout << "Frisky a " << Frisky.GetAge() << " ans" << endl;
C++Livre Page 267 J eudi, 7. mai 2009 9:45 09
268 Le langage C++
43: int age = 5;
44: Frisky.DefAge(age);
45: cout << "Frisky a " << Frisky.GetAge() << " ans" << endl;
46: cout << "Appel de FonctionDeux...\n";
47: FonctionDeux(Frisky);
48: cout << "Frisky a " << Frisky.GetAge() << " ans" << endl;
49: return 0;
50: }
51:
52: // FonctionDeux, attend une rfrence un objet const
53: const SimpleChat & FonctionDeux(const SimpleChat & leChat)
54: {
55: cout << "Fin de FonctionDeux..." << endl;
56: cout << "Frisky a maintenant " << leChat.GetAge();
57: cout << " ans " << endl;
58: // leChat.SetAge(8); const !
59: return leChat;
60: }
Ce programme produit le rsultat suivant :
Cration dun objet Chat...
Constructeur de SimpleChat...
Frisky a 2 ans
Frisky a 5 ans
Appel de FunctionDeux
Fin de FunctionDeux...
Frisky a maintenant 5 ans
Frisky a 5 ans
Destructeur de SimpleChat...
Le rsultat est identique celui du Listing 9.11. La fonction FonctionDeux() attend et
renvoie dsormais une rfrence un objet constant. Vous pouvez constater une nouvelle
fois quil est plus simple de travailler avec des rfrences quavec des pointeurs et que lon
en tire les mmes avantages en termes de performances, tout en conservant la scurit
offerte par const.
Rfrences const
En gnral, les programmeurs C++ ne font pas la diffrence entre une "rfrence constante
un objet de la classe" et une "rfrence un objet constant de la classe". Il est vrai
quune rfrence est toujours constante, puisquelle ne peut jamais tre raffecte un
autre objet. Lorsque le mot-cl const sapplique une rfrence, il concerne donc lobjet
rfrenc.
C++Livre Page 268 J eudi, 7. mai 2009 9:45 09
Chapitre 9 Rfrences 269
Choisir entre rfrences et pointeurs
Les dveloppeurs expriments prfrent souvent les rfrences aux pointeurs parce
quelles sont plus faciles utiliser, plus lisibles et quelles permettent de mieux masquer
les informations, comme on la vu dans le Listing 9.12.
Nanmoins, une rfrence ne peut pas tre raffecte. Si vous devez dsigner dabord un
objet, puis un autre, vous devez donc utiliser un pointeur. En outre, une rfrence ne
pouvant jamais tre nulle, vous ne pouvez pas utiliser une rfrence si lobjet dsign
risque dtre nul ; vous devez utiliser un pointeur.
Supposons que vous vouliez rserver un emplacement mmoire pour un objet. Si le mot-
cl new est incapable dallouer cet emplacement sur le tas, il renvoie un pointeur nul. Une
rfrence ne pouvant jamais tre nulle, vous ne devez pas initialiser une rfrence avec cet
emplacement tant que vous navez pas vri quil nest pas nul. Lexemple suivant
montre comment procder :
int *pInt = new int;
if (pInt!= NULL)
int &rInt = *pInt;
Dans cet exemple, le pointeur pInt est dclar et initialis avec ladresse mmoire
renvoye par loprateur new. Ladresse contenue dans ce pointeur est ensuite teste. Si
elle nest pas nulle, pInt est drfrenc. Le rsultat de lopration produit un objet entier
et rInt est initialis pour y faire rfrence. rInt est donc devenu un alias de lentier
renvoy par new.
Mlanger les rfrences et les pointeurs
dans une liste de paramtres
Il est tout fait possible de mlanger des objets passs par valeur, des pointeurs et des
rfrences dans la mme liste de paramtres. Voici un exemple :
Chat * Fonction(Utilisateur &Proprio, Objet *unObjet, int age);
Faire Ne pas faire
Passer des paramtres par rfrence le plus
souvent possible.
Protger les rfrences et les pointeurs
laide du mot-cl const.
Utiliser des pointeurs lorsquil est possible de
se servir de rfrences.
Tenter de raffecter une rfrence une autre
variable. Cest impossible.
C++Livre Page 269 J eudi, 7. mai 2009 9:45 09
270 Le langage C++
Cette dclaration indique que Fonction attend trois paramtres. Le premier est une rf-
rence un objet Utilisateur, le deuxime est un pointeur sur un objet Objet et le troi-
sime est un entier pass par valeur. Elle renvoie un pointeur vers un objet Chat.
Lemplacement de loprateur dindirection (*) et de loprateur de rfrence (&) dans ces
dclarations de paramtres est sujet controverse parmi les programmeurs. Les trois cri-
tures suivantes sont tout fait correctes lorsque vous voulez dclarer une rfrence :
1: Chat& rMistigri;
2: Chat & rMistigri;
3: Chat &rMistigri;
Les espaces ntant pas pris en compte au niveau syntaxique, vous pouvez insrer autant
de tabulations, despaces et de lignes que vous le souhaitez.
La question est de savoir quelle est lexpression la plus adapte ? Voici des arguments pour
ces trois syntaxes :
Dans le premier cas, le paramtre est une variable appele rMistigri, dont le type peut
tre considr comme une "rfrence un objet Chat". Loprateur & doit donc tre associ au
type.
Le contre-argument est que le type est Chat. Loprateur & fait partie du "dclarateur", qui
comprend le nom de la variable et lesperluette. Lorsquil est coll au nom du type,
loprateur est source derreur dans lexpression suivante :
Chat& rMistigri, rFrisky;
A priori, rMistigri et rFrisky laissent penser quil sagit de rfrences des objets de
la classe Chat. En fait, le premier lment est ici une rfrence un objet Chat, alors que
rFrisky (malgr son nom) est une variable classique de la classe Chat. Cette ligne devrait
donc tre crite de la faon suivante :
Chat &rMistigri, rFrisky;
La rponse cette objection est quil ne faudrait jamais mlanger de cette faon les dcla-
rations de rfrences et de variables. Voici les dclarations quivalentes correctes :
Chat& rMistigri;
Chat Frisky;
La plupart des programmeurs optent gnralement pour une solution intermdiaire, en
plaant loprateur entre les lments de la liste (voir la deuxime proposition).
Bien entendu, ce qui sapplique loprateur rfrence (&) concerne galement loprateur
dindirection (*). Chacun utilise les oprateurs sa faon, lessentiel tant de rester homo-
gne et cohrent dans les programmes.
C++Livre Page 270 J eudi, 7. mai 2009 9:45 09
Chapitre 9 Rfrences 271
Les deux conventions suivantes sont frquemment adoptes pour la dclaration
de pointeurs et de rfrences :
Lesperluette et lastrisque occupent une position mdiane, en tant prcds et
suivis dun espace.
Ne jamais dclarer des rfrences, des pointeurs et des variables sur la mme
ligne.
Renvoi de rfrences dobjet hors de porte
Les programmeurs C++ qui ont pris lhabitude de passer des paramtres par rfrence ont
tendance abuser de cette pratique. Une rfrence correspond lalias dun objet existant.
Lorsque vous passez ou rcuprez une rfrence dans une fonction, posez-vous la question
suivante : "Quel est lobjet que je rfrence et existera-t-il toujours chaque fois quil est
utilis ?".
Le Listing 9.13 illustre le danger du renvoi dune rfrence un objet qui nexiste plus.
Listing 9.13 : Renvoi dune rfrence un objet inexistant
1: // Listing9.13
2: // Renvoi dune rfrence un objet
3: // qui nexiste plus
4:
5: #include <iostream>
6:
7: class SimpleChat
8: {
9: public:
10: SimpleChat (int age, int poids);
11: ~SimpleChat() {}
12: int GetAge() { return sonAge; }
13: int GetPoids() { return sonPoids; }
14: private:
15: int sonAge;
16: int sonPoids;
17: };
18:
19: SimpleChat::SimpleChat(int age, int poids)
20: {
21: sonAge = age;
22: sonPoids = poids;
23: }
24:
25: SimpleChat &UneFonction();
In
f
o
C++Livre Page 271 J eudi, 7. mai 2009 9:45 09
272 Le langage C++
26:
27: int main()
28: {
29: SimpleChat &rChat = UneFonction();
30: int age = rChat.GetAge();
31: std::cout << "rChat a " << age << " ans!" << std::endl;
32: return 0;
33: }
34:
35: SimpleChat &UneFonction()
36: {
37: SimpleChat Frisky(5, 9);
38: return Frisky;
39: }
Ce programme ne pourra pas tre compil avec un compilateur Borland, alors
quil le sera avec un compilateur Microsoft ou avec le compilateur GNU.
Cette faon de procder nest toutefois pas recommande.
La classe SimpleChat est dclare de la ligne 7 la ligne 17. Une rfrence un objet
SimpleChat est ensuite initialise avec le rsultat de lappel la fonction UneFonction()
(ligne 29), qui est dclare comme devant renvoyer une rfrence cette classe, comme
lindique la ligne 25.
Le corps de la fonction UneFonction() [lignes 35-39] dclare un objet local de type
SimpleChat, et initialise son ge et son poids. Cet objet est ensuite renvoy par rfrence
la fonction appelante. Certains compilateurs sont capables de dtecter cette erreur et donc
dempcher la cration du chier excutable, alors que dautres compilent le chier source
(en produisant ventuellement un message davertissement) et crent ainsi un programme
qui produira des rsultats imprvisibles.
Lorsque la fonction UneFonction() se termine, lobjet local Frisky est dtruit. La rf-
rence renvoye par cette fonction est donc un alias dun objet qui nexiste plus, ce qui est
incorrect.
Renvoi dune rfrence un objet dans le tas
On pourrait tre tent de rsoudre le problme prcdent en faisant en sorte que la fonction
UneFonction() cre Frisky sur le tas. De cette faon, Frisky continuerait dexister aprs
lexcution de la fonction.
Le problme de cette approche est alors ce quil advient de la mmoire alloue lobjet
Frisky lorsque lon nen a plus besoin. Le Listing 9.14 illustre ce problme.
A
tte
n
tio
n
C++Livre Page 272 J eudi, 7. mai 2009 9:45 09
Chapitre 9 Rfrences 273
Listing 9.14 : Fuites mmoire
1: // Listing9.14 - Rsoudre les problmes de fuites mmoire
2:
3: #include <iostream>
4:
5: class SimpleChat
6: {
7: public:
8: SimpleChat (int age, int poids);
9: ~SimpleChat() {}
10: int GetAge() { return sonAge; }
11: int GetPoids() { return sonPoids; }
12:
13: private:
14: int sonAge;
15: int sonPoids;
16: };
17:
18: SimpleChat::SimpleChat(int age, int weight)
19: {
20: sonAge = age;
21: sonPoids = poids;
22: }
23:
24: SimpleChat & UneFonction();
25:
26: int main()
27: {
28: SimpleChat & rChat = UneFonction();
29: int age = rChat.GetAge();
30: std::cout << "rChat a " << age << " ans!" << std::endl;
31: std::cout << "&rChat : " << &rChat << std::endl;
32: // Comment se dbarrasser de cette mmoire?
33: SimpleChat * pChat = &rChat;
34: delete pChat;
35: // rChat fait maintenant rfrence ??
36: return 0;
37: }
38:
39: SimpleChat &UneFonction()
40: {
41: SimpleChat * pFrisky = new SimpleChat(5, 9);
42: std::cout << "pFrisky : " << pFrisky << std::endl;
43: return *pFrisky;
44: }
C++Livre Page 273 J eudi, 7. mai 2009 9:45 09
274 Le langage C++
Ce programme produit le rsultat suivant :
pFrisky : 0x00431C60
rChat a 5 ans!
&rChat : 0x00431C60
Une fois compil, ce programme semble fonctionner correctement. Il sagit en
fait dune vritable bombe retardement.
La fonction UneFonction() [lignes 39 44] ne renvoie plus de rfrence une variable
locale. La mmoire est alloue sur le tas et affecte un pointeur la ligne 41. Ladresse
correspondante est afche, puis le pointeur est drfrenc, ce qui permet de renvoyer
lobjet SimpleChat par rfrence.
La ligne 28 affecte le rsultat de cette fonction une rfrence de SimpleChat et cet objet
est utilis pour obtenir lge du chat, qui est afch la ligne 30.
La rfrence dclare dans la fonction main() dsigne lobjet plac sur le tas par
UneFonction(). Pour le prouver, on applique loprateur adresse de rChat. Les deux
adresses qui safchent renvoient la mme valeur : lobjet cit par rfrence et son adresse
dans le tas sont identiques.
Pour linstant, tout va bien, mais comment librer cet espace mmoire ? Souvenez-vous
quil est interdit dappeler delete sur une rfrence. Une solution astucieuse consiste
crer un autre pointeur, pour linitialiser ensuite avec ladresse obtenue par rChat. La
mmoire est libre et on vite ainsi une fuite mmoire. Mais un autre problme subsiste :
sur quel objet la rfrence rChat porte-t-elle aprs la ligne 34 ? Comme vous lavez vu
plus haut, une rfrence doit toujours tre lalias dun objet existant non nul. Si lobjet est
nul ou nexiste pas (comme cest le cas ici), le programme nest pas valide.
Nutilisez jamais de rfrences des objets nuls. En effet, les programmes
peuvent parfois tre compils, mais ils produiront des erreurs inattendues lors
de leur excution.
Il existe trois solutions ce problme. La premire consiste dclarer un objet Simple-
Chat la ligne 28, puis le renvoyer par valeur la fonction appelante. La deuxime
consiste dclarer un objet SimpleChat sur le tas dans UneFonction() mais faire en sorte
que celle-ci renvoie un pointeur vers cet emplacement : la fonction appelante pourra alors
librer la zone pointe par ce pointeur lorsquelle nen aura plus besoin. Enn, la dernire
solution (de loin la meilleure) consiste dclarer lobjet dans la fonction appelante, puis
le passer par rfrence UneFonction().
A
tte
n
tio
n
In
f
o
C++Livre Page 274 J eudi, 7. mai 2009 9:45 09
Chapitre 9 Rfrences 275
O le pointeur est-il pass ?
Lorsquun programme alloue de lespace mmoire sur le tas, il faut imprativement
conserver un pointeur vers cet emplacement. Dans le cas contraire, la mmoire ne peut
plus tre libre ce que entrane une fuite mmoire.
Durant le droulement du programme, ce pointeur passe dune fonction une autre. En
rgle gnrale, la valeur place dans le bloc de mmoire est passe laide de rfrences et
cest la fonction qui a cr cet emplacement qui le supprime galement. Mais il ne sagit
que dune rgle gnrale qui nest pas grave dans le marbre.
Toutefois, il est dangereux de supprimer une zone mmoire cr dans une fonction partir
dune autre fonction. En effet, le pointeur risque de ne pas tre supprim ou de ltre deux
fois, ce qui produit des erreurs dexcution dans le programme. Il est plus sr de construire
vos fonctions de sorte quelles librent les espaces quelles ont elles-mmes allous.
Si vous crivez une fonction qui doit allouer de la mmoire puis la repasser la fonction
appelante, vous devriez sans doute modier votre interface pour faire en sorte que ce soit
la fonction appelante qui alloue la mmoire, puis la passe par rfrence votre fonction.
Cette approche dplace toute la gestion de la mmoire de votre programme vers la fonction
qui est prpare pour la supprimer.
Questions-rponses
Q quoi bon utiliser des rfrences alors que les pointeurs sont aussi performants ?
R Les rfrences sont plus faciles employer et comprendre. Lindirection est cache,
et il est inutile de drfrencer la variable chaque accs.
Q Si les rfrences sont plus faciles mettre en uvre, pourquoi utilise-t-on encore
des pointeurs ?
R Les rfrences ne peuvent jamais tre nulles et ne peuvent pas tre raffectes. Les
pointeurs offrent plus de souplesse, mais sont un peu plus difciles utiliser.
Faire Ne pas faire
Passer les paramtres par valeur si ncessaire.
Renvoyer le rsultat dune fonction par valeur
si ncessaire.
Renvoyer un lment par rfrence si sa
porte est locale.
Perdre la trace du moment et de lendroit o
la mmoire est alloue car vous risquez ainsi
de ne pas savoir si elle est libre.
C++Livre Page 275 J eudi, 7. mai 2009 9:45 09
276 Le langage C++
Q Pourquoi le rsultat dune fonction doit-il tre renvoy par valeur ?
R Si lobjet renvoy est local la fonction, vous devez le renvoyer par valeur sous peine
de renvoyer une rfrence vers un objet inexistant.
Q Puisque le renvoi par rfrence est dangereux, pourquoi ne pas toujours renvoyer
un rsultat par valeur ?
R Le renvoi par rfrence est plus efcace. Il conomise la mmoire et le programme
sexcute plus vite.
Testez vos connaissances
1. Quelle est la diffrence entre une rfrence et un pointeur ?
2. Quand faut-il prfrer les pointeurs aux rfrences ?
3. Que renvoie new lorsquil ny a plus assez de mmoire pour crer un nouvel objet ?
4. Quest-ce quune rfrence constante ?
5. Quelle est la diffrence entre le passage dobjets par rfrence et le passage dune
rfrence ?
6. Lors de la dclaration dune rfrence, que faut-il crire ?
a. int& maRef = monInt;
b. int & maRef = monInt;
c. int &maRef = monInt;
Exercices
1. Dans un programme, dclarez une variable entire, une rfrence cet objet et un
pointeur vers un entier. Utilisez le pointeur et la rfrence pour manipuler la valeur de
lentier.
2. crivez un programme dclarant un pointeur constant sur un entier constant. Initiali-
sez le pointeur avec ladresse dune variable entire Var1. Affectez 6 Var1 et utilisez
le pointeur pour affecter 7 Var1. Crez ensuite une autre variable entire Var2 et
raffectez le pour quil pointe sur Var2. Ne compilez pas encore cet exercice.
3. Compilez maintenant le programme de lExercice 2. Produit-il des erreurs ? Quels
sont les messages davertissement ?
4. crivez un programme qui cre un pointeur perdu.
C++Livre Page 276 J eudi, 7. mai 2009 9:45 09
Chapitre 9 Rfrences 277
5. Corrigez ce programme.
6. crivez un programme provoquant une fuite mmoire.
7. Corrigez ce programme.
8. CHERCHEZ LERREUR : o se cache lerreur dans ce programme ?
1: #include <iostream>
2: using namespace std;
3: class CHAT
4: {
5: public:
6: CHAT(int age) { sonAge = age; }
7: ~CHAT(){}
8: int GetAge() const { return sonAge;}
9: private:
10: int sonAge;
11: };
12:
13: CHAT & CreerChat(int age);
14: int main()
15: {
16: int age = 7;
17: CHAT Felix = CreerChat(age);
18: cout << "Felix a " << Felix.GetAge()
19: << " ans" << endl;
20: return 0;
21: }
22:
23: CHAT & CreerChat(int age)
24: {
25: CHAT * pChat = new CHAT(age);
26: return *pChat;
27: }
9. Corrigez le programme de lexercice prcdent.
C++Livre Page 277 J eudi, 7. mai 2009 9:45 09
C++Livre Page 278 J eudi, 7. mai 2009 9:45 09
10
Fonctions avances
Au sommaire de ce chapitre
La surcharge de fonctions membres
La surcharge des oprateurs
Les fonctions grant des classes avec des variables membres alloues dynamiquement
Au Chapitre 5, vous avez appris utiliser des fonctions. Vous connaissez prsent le fonc-
tionnement des pointeurs et des rfrences. Dans ce chapitre, nous allons approfondir
notre connaissance des fonctions.
Fonctions membres surcharges
Au Chapitre 5, nous avons vu comment implmenter le polymorphisme de fonctions, ou
surcharge de fonctions, qui consiste crer au moins deux fonctions de mme nom mais
avec des paramtres diffrents. Cette technique concerne galement les fonctions
membres des classes (ou mthodes).
C++Livre Page 279 J eudi, 7. mai 2009 9:45 09
280 Le langage C++
Dans le Listing 10.1, la classe Rectangle contient deux fonctions DessinerForme().
Lune ne prend aucun paramtre et trace le rectangle en fonction de ses valeurs courantes
alors que lautre prend deux valeurs (largeur et hauteur) et les utilise pour tracer le rectangle
sans tenir compte de ses valeurs actuelles.
Listing 10.1 : Surcharge de fonctions membres
1: //Listing10.1 Surcharge de fonctions membres de la classe
2: #include <iostream>
3:
4: // Dclaration de la classe Rectangle
5: class Rectangle
6: {
7: public:
8: // constructeurs
9: Rectangle(int largeur, int hauteur);
10: ~Rectangle(){}
11:
12: // fonction DessinerForme surcharge
13: void DessinerForme() const;
14: void DessinerForme(int uneLargeur, int uneHauteur) const;
15:
16: private:
17: int saLargeur;
18: int saHauteur;
19: };
20:
21: // implmentation du constructeur
22: Rectangle::Rectangle(int largeur, int hauteur)
23: {
24: saLargeur = largeur;
25: saHauteur = hauteur;
26: }
27:
28:
29: // fonction DessinerForme surcharge sans paramtres
30: // Dessine en fonction des valeurs membres courantes
30a: // de lobjet
31: void Rectangle::DessinerForme() const
32: {
33: DessinerForme(saLargeur, saHauteur);
34: }
35:
36:
C++Livre Page 280 J eudi, 7. mai 2009 9:45 09
Chapitre 10 Fonctions avances 281
37: // fonction DessinerForme surcharge - prend deux valeurs
38: // Dessine en fonction des paramtres passs
39: void Rectangle::DessinerForme(int largeur, int hauteur) const
40: {
41: for (int i = 0; i < hauteur; i++)
42: {
43: for (int j = 0; j < largeur; j++)
44: {
45: std::cout << "*";
46: }
47: std::cout << std::endl;
48: }
49: }
50:
51: // Programme principal de dmonstration
51a: // des fonctions surcharges
52: int main()
53: {
54: // initialise un rectangle de 30x5
55: Rectangle unRect(30,5 );
56: std::cout << "DessinerForme():" << std::endl;
57: unRect.DessinerForme();
58: std::cout << "\nDessinerForme(40, 2):" << std::endl;
59: unRect.DessinerForme(40,2);
60: return 0;
61: }
Ce programme produit le rsultat suivant :
DessinerForme() :
******************************
******************************
******************************
******************************
******************************
DessinerForme(40, 2) :
************************************************************
************************************************************
La partie la plus importante du programme rside dans la surcharge de la fonction Dessi-
nerForme() (lignes 13 et 14). La mise en uvre des mthodes surcharges seffectue de la
ligne 31 la ligne 49. Vous remarquerez que la version de DessinerForme() qui ne prend
aucun paramtre se contente dappeler lautre version en lui passant les variables membres
courantes. Essayez toujours dviter de dupliquer le code dans deux fonctions car cela
complique les modications ultrieures et multiplie les risques derreur.
C++Livre Page 281 J eudi, 7. mai 2009 9:45 09
282 Le langage C++
Le programme commence rellement la ligne 52 et se poursuit jusqu la ligne 61. Il
cre un objet Rectangle, appelle la premire version de la mthode DessinerForme()
(sans paramtre) puis la seconde (en lui passant deux paramtres entiers courts non
signs).
Le compilateur dtermine la mthode qui doit tre appele en utilisant le nombre et le type
de paramtres passs lappel. On pourrait imaginer une troisime mthode Dessiner-
Forme() qui prendrait en paramtre une dimension et une numration qui indiquerait sil
sagit de la hauteur ou de la largeur.
Utilisation de valeurs par dfaut
Tout comme les fonctions globales, les mthodes dune classe peuvent inclure une ou
plusieurs valeurs par dfaut, comme le montre le Listing 10.2.
Listing 10.2 : Utilisation de valeurs par dfaut
1: //Listing10.2 Valeurs par dfaut dans les fonctions membres
2: #include <iostream>
3:
4: using namespace std;
5:
6: // Dclaration de la classe Rectangle
7: class Rectangle
8: {
9: public:
10: // constructeurs
11: Rectangle(int largeur, int hauteur);
12: ~Rectangle(){}
13: void DessinerForme(int uneLargeur, int uneHauteur,
14: bool ValsActuelles = false) const;
15:
16: private:
17: int saLargeur;
18: int saHauteur;
19: };
20:
21: // Implmentation du constructeur
22: Rectangle::Rectangle(int largeur, int hauteur):
23: saLargeur(largeur), // initialisations
24: saHauteur(hauteur)
25: {} // le corps est vide
26:
27:
C++Livre Page 282 J eudi, 7. mai 2009 9:45 09
Chapitre 10 Fonctions avances 283
28: // valeurs par dfaut utilises pour le 3me paramtre
29: void Rectangle::DessinerForme(
30: int largeur,
31: int hauteur,
32: bool ValeursActuelles
33: ) const
34: {
35: int printLargeur;
36: int printHauteur;
37:
38: if (ValeursActuelles == true)
39: {
39a: // utiliser les valeurs actuelles
40: printLargeur = saLargeur;
41: printHauteur = saHauteur;
42: }
43: else
44: {
45: printLargeur = largeur; // utiliser les paramtres
46: printHauteur = hauteur;
47: }
48:
49:
50: for (int i = 0; i < printHauteur; i++)
51: {
52: for (int j = 0; j < printLargeur; j++)
53: {
54: cout << "*";
55: }
56: cout << endl;
57: }
58: }
59:
60: // Programme principal pour la dmonstration
60a: // des fonctions surcharges
61: int main()
62: {
63: // Initialise un rectangle de 30x5
64: Rectangle unRect(30, 5);
65: cout << "DessinerForme(0, 0, true)..." << endl;
66: unRect.DessinerForme(0, 0, true);
67: cout <<"DessinerForme(40, 2)..." << endl;
68: unRect.DessinerForme(40, 2);
69: return 0;
70: }
C++Livre Page 283 J eudi, 7. mai 2009 9:45 09
284 Le langage C++
Ce programme produit le rsultat suivant :
DessinerForme(0, 0, true)...
******************************
******************************
******************************
******************************
******************************
DessinerForme(40, 2)...
************************************************************
************************************************************
Dans ce listing, la fonction surcharge DessinerForme() est remplace par une unique
fonction avec trois paramtres par dfaut (ligne 14). Les deux premiers, uneLargeur et
uneHauteur, sont de type entier, alors que le troisime, de type bool (ValsActuelles),
est gal false par dfaut.
Cette fonction quelque peu complexe est implmente partir de la ligne 29. Vous savez
que les espaces nont pas dimportance en C++ ; len-tte de la fonction se trouve donc
aux lignes 29 33.
Dans la mthode, le troisime paramtre (ValeursActuelles) est valu la ligne 38.
Sil vaut true, les variables locales printLargeur et printHauteur reoivent respecti-
vement les valeurs courantes des membres saLargeur et saHauteur.
Dans le cas contraire, soit que le paramtre ait t initialis par dfaut false ou dni
par lutilisateur, les deux premiers paramtres servent initialiser les variables printLargeur
et printHauteur.
Si ValeursActuelles vaut true, les valeurs des deux autres paramtres ne sont pas
prises en compte.
Choisir entre des valeurs par dfaut et des fonctions
surcharges
Les deux premiers programmes de ce chapitre ralisent les mmes oprations. Pourtant,
les fonctions surcharges du Listing 10.1 sont plus faciles utiliser et comprendre. En
outre, il est plus simple dtendre la surcharge en crant, par exemple, une troisime fonc-
tion si lutilisateur souhaite entrer uniquement la hauteur ou la largeur du rectangle mais
pas les deux. En revanche, une valeur par dfaut deviendrait rapidement trs complexe
mesure que lon ajouterait des variantes.
C++Livre Page 284 J eudi, 7. mai 2009 9:45 09
Chapitre 10 Fonctions avances 285
Comment choisir entre la surcharge de fonction et lutilisation de valeurs par dfaut ?
Voici une rgle trs simple :
Prfrez la surcharge de fonction dans les cas suivants :
Il nexiste pas de valeurs par dfaut signicatives.
Vous devez utiliser des algorithmes diffrents.
Vous devez pouvoir grer des types diffrents dans la liste des paramtres.
Le constructeur par dfaut
Le rle dun constructeur est de dnir un objet ; celui dun constructeur Rectangle, par
exemple, est de construire un objet rectangle valide. Avant le lancement du constructeur, il
nexiste pas dobjet rectangle mais uniquement un espace en mmoire. Aprs son ex-
cution, il existe un objet prt lemploi. Cest un avantage essentiel de la programmation
oriente objet : le programme appelant na rien faire pour sassurer que lobjet dmarre
dans un tat cohrent.
Comme nous lavons vu au Chapitre 6, si vous ne dnissez pas explicitement de construc-
teur pour une classe, le compilateur cre pour vous un constructeur par dfaut, qui nattend
aucun paramtre et qui ne fait rien. Bien entendu, vous avez la possibilit de dclarer votre
propre constructeur par dfaut, dpourvu de paramtres, mais permettant de "mettre en
place" lobjet comme vous le souhaitez
Ce constructeur est appel constructeur "par dfaut" bien quil soit dni par lutilisateur
ce terme dsigne en fait tout constructeur qui nattend pas de paramtre.
Si vous dnissez un ou plusieurs constructeurs dans une classe, le compilateur
ne produira pas de constructeur par dfaut pour celle-ci. Si vous en avez
besoin dun, vous devez donc le crer vous-mme.
Surcharge des constructeurs
Les constructeurs, comme nimporte quelle fonction membre, peuvent tre surchargs, ce
qui leur ajoute de la puissance et de la souplesse.
Par exemple, les objets rectangles pourraient avoir deux constructeurs. Lun tracerait un
rectangle partir de la longueur et de la hauteur entres par lutilisateur alors que lautre
ne prendrait aucune valeur et dnirait un rectangle par dfaut. Le Listing 10.3 impl-
mente ces deux constructeurs.
In
f
o
C++Livre Page 285 J eudi, 7. mai 2009 9:45 09
286 Le langage C++
Listing 10.3 : Surcharge dun constructeur
1: // Listing10.3 - surcharge de constructeurs
2:
3: #include <iostream>
4: using namespace std;
5:
6: class Rectangle
7: {
8: public:
9: Rectangle();
10: Rectangle(int largeur, int longueur);
11: ~Rectangle() {}
12: int GetLargeur() const { return saLargeur; }
13: int GetLongueur() const { return saLongueur; }
14: private:
15: int saLargeur;
16: int saLongueur;
17: };
18:
19: Rectangle::Rectangle()
20: {
21: saLargeur = 5;
22: saLongueur = 10;
23: }
24:
25: Rectangle::Rectangle (int largeur, int longueur)
26: {
27: saLargeur = largeur;
28: saLongueur = longueur;
29: }
30:
31: int main()
32: {
33: Rectangle Rect1;
34: cout << "Largeur de Rect1: "
34a: << Rect1.GetLargeur() << endl;
35: cout << "Longueur de Rect1: "
35a: << Rect1.GetLongueur() << endl;
36:
37: int uneLargeur, uneLongueur;
38: cout << "Entrez la largeur: ";
39: cin >> uneLargeur;
40: cout << "\nEntrez la longueur: ";
41: cin >> uneLongueur;
42:
43: Rectangle Rect2(uneLargeur, uneLongueur);
C++Livre Page 286 J eudi, 7. mai 2009 9:45 09
Chapitre 10 Fonctions avances 287
44: cout << "\nLargeur de Rect2: "
44a: << Rect2.GetLargeur() << endl;
45: cout << "Longueur de Rect2: "
45a: << Rect2.GetLongueur() << endl;
46: return 0;
47: }
Ce programme produit le rsultat suivant :
Largeur de Rect1: 5
Longueur de Rect1: 10
Entrez la largeur : 20
Entrez la longueur: 50
Largeur de Rect2: 20
Longueur de Rect2: 50
La classe Rectangle est dclare de la ligne 6 la ligne 17. Le programme comprend
deux constructeurs : le constructeur "par dfaut" (dni la ligne 9) et un constructeur
(ligne 10) prenant en paramtre deux valeurs entires.
La ligne 33 cre un rectangle laide du constructeur par dfaut et les lignes 34 et 35 af-
chent ses dimensions. Lutilisateur est ensuite invit entrer une largeur et une longueur
(lignes 38 41) qui sont transmises au constructeur qui attend deux paramtres (ligne 43).
Enn, la largeur et la hauteur de ce rectangle sont afches.
Comme avec toute fonction surcharge, le compilateur appelle le constructeur qui
convient en fonction du nombre et du type des paramtres.
Initialisation des objets
Jusqu prsent, vous avez dni les variables membres des objets dans le corps du construc-
teur. Les constructeurs, cependant, sont invoqus en deux tapes : la phase dinitialisation et
lexcution de leurs corps.
La plupart des variables peuvent tre dnies durant lune ou lautre de ces tapes, soit
lors de la phase initialisation, soit par affectation de valeurs dans le corps du constructeur.
Il est plus propre et gnralement plus lisible dinitialiser les variables membres lors de la
phase dinitialisation. Voici un exemple dinitialisation des variables membre dun Chat :
Chat(): // nom et paramtres du constructeur
sonAge(5), // liste dinitialisation
sonPoids(8)
{ } // corps du constructeur
C++Livre Page 287 J eudi, 7. mai 2009 9:45 09
288 Le langage C++
La parenthse fermante de la liste des paramtres du constructeur doit tre suivie du carac-
tre deux-points. Placez ensuite le nom de la variable membre, suivi de deux parenthses
entre lesquelles vous pouvez indiquer une ou plusieurs expressions qui initialisent la variable
membre. Sil y plusieurs initialisations, sparez-les par une virgule.
Dans le Listing 10.4, nous avons choisi dinitialiser les variables membres dans la partie
dinitialisation du constructeur au lieu de leur affecter des valeurs dans le corps, comme
nous lavions fait dans le Listing 10.3.
Listing 10.4 : Extrait de code initialisant des variables membres
1: Listing10.4 Initialisation de variables membres
2: Rectangle::Rectangle():
3: saLargeur(5),
4: saLongueur(10)
5: {
6: }
7:
8: Rectangle::Rectangle (int largeur, int longueur):
9: saLargeur(largeur),
10: saLongueur(longueur)
11: {
12: }
Le Listing 10.4 ntant quun extrait de code, il nafche donc rien. La ligne 2 dbute le
constructeur par dfaut. Aprs len-tte standard, on a ajout un caractre deux-points,
suivi des dnitions des valeurs par dfaut 5 et 10 pour saLargeur et saLongueur aux
lignes 3 et 4.
La ligne 8 dbute la dnition du deuxime constructeur. Dans cette version surcharge,
on attend deux paramtres qui servent initialiser les membres de la classe aux lignes 9 et
10.
Certaines variables, comme les rfrences ou les constantes, doivent tre initialises, mais
pas affectes. Le corps dun constructeur contient souvent dautres affectations ou des
instructions diverses, mais il est toujours prfrable dutiliser le plus possible la phase
dinitialisation.
Le constructeur de copie
Outre un constructeur par dfaut et un destructeur, le compilateur produit galement un
constructeur de copie par dfaut. Le constructeur de copie est appel chaque fois quune
copie dobjet est cre sur la pile ou sur le tas.
C++Livre Page 288 J eudi, 7. mai 2009 9:45 09
Chapitre 10 Fonctions avances 289
Comme nous lavons vu au Chapitre 9, le passage dun objet par valeur en paramtre une
fonction ou comme valeur de retour dune fonction provoque la cration dune copie
temporaire de cet objet. Sil sagit dune instance dune classe dnie par lutilisateur,
cest le constructeur de copie de cette classe qui est appel (voir le Listing 9.6 dans le
chapitre prcdent).
Un constructeur de copie ne prend toujours quun seul paramtre : une rfrence un objet
de la mme classe. De prfrence, ce sera une rfrence constante an dviter toute modi-
cation malencontreuse de lobjet qui a t pass. Exemple :
Chat(const Chat & leChat);
Le constructeur de Chat attend ici une rfrence constante un objet existant de la classe
Chat. Le rle de ce constructeur est de crer une copie de lobjet leChat.
Le constructeur de copie par dfaut se contente de copier chaque variable membre de
lobjet pass en paramtre dans les variables membres du nouvel objet. Cest ce que lon
appelle une copie de surface : bien quelle convienne la plupart des variables membres,
elle pose de srieux problmes lorsque ces membres sont des pointeurs sur des objets
allous sur le tas.
Une copie de surface duplique les valeurs exactes des variables membres dun objet dans
un autre objet. Les pointeurs de ces deux objets nissent donc par pointer sur le mme
espace mmoire. Une copie en profondeur, au contraire, copie les valeurs alloues sur le
tas dans une nouvel emplacement, diffrent du premier.
Si la classe Chat contient une variable membre appele sonAge, qui pointe sur un entier
allou sur le tas, le constructeur de copie par dfaut copiera le contenu de la variable
sonAge de lobjet pass en paramtre dans la variable membre sonAge de lobjet nouvel-
lement cr. Ces deux objets pointeront alors sur la mme adresse mmoire (voir
Figure 10.1).
Cette situation mnera au dsastre si lun des deux objets Chat sort de sa porte. Si le
destructeur du Chat initial libre son emplacement alors que lobjet dupliqu pointe
Figure 10.1
Utilisation du construc-
teur de copie par dfaut.
5
sonAge
ancien objet
Chat
sonAge
nouvel objet
Chat
Tas
C++Livre Page 289 J eudi, 7. mai 2009 9:45 09
290 Le langage C++
toujours sur cette zone, on a cr un pointeur perdu et le programme court un danger
mortel. La Figure 10.2 illustre ce problme.
La solution consiste donc crer votre propre constructeur de copie et allouer lespace
mmoire en fonction de vos besoins. Une fois que vous avez allou une nouvelle zone
mmoire, vous pouvez copier les anciennes valeurs dans ce nouvel emplacement : vous
avez ralis une copie en profondeur, comme on le montre dans le Listing 10.5.
Listing 10.5 : Constructeurs de copie
1: // Listing10.5 - constructeurs de copie
2:
3: #include <iostream>
4: using namespace std;
5:
6: class Chat
7: {
8: public:
9: Chat(); // constructeur par dfaut
10: Chat (const Chat &); // constructeur de copie
11: ~Chat(); // destructeur
12: int GetAge() const { return *sonAge; }
13: int GetPoids() const { return *sonPoids; }
14: void SetAge(int age) { *sonAge = age; }
15:
16: private:
17: int *sonAge;
18: int *sonPoids;
19: };
20:
21: Chat::Chat()
22: {
23: sonAge = new int;
24: sonPoids = new int;
Figure 10.2
Cration
dun pointeur perdu.
5
sonAge
ancien objet
Chat
sonAge
nouvel objet
Chat
Tas
C++Livre Page 290 J eudi, 7. mai 2009 9:45 09
Chapitre 10 Fonctions avances 291
25: *sonAge = 5;
26: *sonPoids = 9;
27: }
28:
29: Chat::Chat(const Chat & rhs)
30: {
31: sonAge = new int;
32: sonPoids = new int;
33: *sonAge = rhs.GetAge(); // accs public
34: *sonPoids = *(rhs.sonPoids); // accs priv
35: }
36:
37: Chat::~Chat()
38: {
39: delete sonAge;
40: sonAge = 0;
41: delete sonPoids;
42: sonPoids = 0;
43: }
44:
45: int main()
46: {
47: Chat Frisky;
48: cout << "ge de Frisky: " << Frisky.GetAge() << endl;
49: cout << "Lge de Frisky est fix 6 ans...\n";
50: Frisky.SetAge(6);
51: cout << "Cration de lobjet Boots partir de Frisky\n";
52: Chat Boots(Frisky);
53: cout << "ge de Frisky: " << Frisky.GetAge() << endl;
54: cout << "ge de Boots: " << Boots.GetAge() << endl;
55: cout << "Lge de Frisky est fix 7 ans...\n";
56: Frisky.SetAge(7);
57: cout << "ge de Frisky: " << Frisky.GetAge() << endl;
58: cout << "ge de Boots: " << Boots.GetAge() << endl;
59: return 0;
60: }
Ce programme produit le rsultat suivant :
ge de Frisky : 5
Lge de Frisky est fix 6 ans...
Cration de lobjet Boots partir de Frisky
ge de Frisky : 6
ge de Boots : 6
Lge de Frisky est fix 7 ans...
ge de Frisky : 7
ge de Boots : 6
C++Livre Page 291 J eudi, 7. mai 2009 9:45 09
292 Le langage C++
La classe Chat est dclare de la ligne 6 la ligne 19. Vous pouvez constater quun
constructeur par dfaut et un constructeur de copie sont dclars respectivement la
ligne 9 et la ligne 10. On reconnat que le constructeur de la ligne 10 est un constructeur
de copie, car il prend en paramtre une rfrence (une rfrence de constante dans ce cas)
un objet du mme type.
Les deux variables membres dclares aux lignes 17 et 18 sont chacune associes un
pointeur sur un entier. En rgle gnrale, les variables membres sont rarement stockes
comme des pointeurs, mais on la fait ici pour illustrer la gestion de variables membres sur
le tas.
Le constructeur par dfaut rserve de lespace mmoire pour les deux variables entires
sur le tas, puis leur affecte une valeur (lignes 21 27).
Le constructeur de copie commence la ligne 29. Vous remarquerez que nous avons
appel son paramtre rhs, ce qui est assez courant en C++ (rhs est labrviation de right-
hand side). Si vous examinez les lignes 33 et 34, vous remarquerez en effet que lobjet
pass en paramtre se trouve droite du signe gal.
Les lignes 31 32 allouent deux emplacements sur le tas, puis les deux lignes suivantes y
affectent les valeurs des membres de lobjet Chat pass en paramtre.
Le paramtre rhs est un objet Chat pass au constructeur de copie en tant que rfrence
constante. tant un objet de la classe Chat, cette rfrence a les mmes membres que
nimporte quel autre objet Chat.
Tout objet Chat peut accder toutes les variables membres prives dun autre objet
Chat ; il est toutefois prfrable dutiliser les mthodes daccs publiques dans la mesure
du possible. La fonction membre rhs.GetAge() renvoie la valeur stocke dans lemplace-
ment mmoire point par la variable membre sonAge de rhs. Dans une vritable applica-
tion, vous obtiendrez la valeur de sonPoids de la mme manire, grce une mthode
daccs. La ligne 34, toutefois, montre que les diffrents objets dune mme classe ont
accs aux membres privs des autres. Une copie est alors ralise directement depuis le
membre sonPoids priv de lobjet rhs.
La Figure 10.3 montre ce qui sest donc pass : les valeurs pointes par les variables
membres de lobjet Chat pass en paramtre ont t copies dans la mmoire alloue au
nouvel objet Chat.
La ligne 47 cre lobjet Frisky de la classe Chat. Son ge apparat lcran puis il
reoit la valeur 6 (ligne 50). la ligne 52, le constructeur de copie duplique Frisky et
baptise le nouvel objet Boots. Si Frisky avait t pass par valeur (et non par rf-
rence) une fonction quelconque, le compilateur aurait appel ce constructeur de copie
de la mme faon.
C++Livre Page 292 J eudi, 7. mai 2009 9:45 09
Chapitre 10 Fonctions avances 293
Les ges respectifs des deux objets Chat apparaissent alors lcran (lignes 53 et 54).
Boots a 6 ans comme Frisky, puisquil a hrit des proprits de ce dernier. En revanche,
lorsque lge de Frisky est redni avec la valeur 7 ( la ligne 56), lge de Boots nest
pas modi, ce qui prouve que les deux objets rsident dans des zones distinctes en
mmoire.
Lorsque les objets Chat deviennent hors de porte, ils sont effacs automatiquement par
leurs destructeurs respectifs (lignes 37 43). Le destructeur dun Chat utilise delete pour
librer les emplacements rservs sur le tas pour sonAge et sonPoids et affecte zro ces
deux pointeurs pour plus de scurit.
Surcharge des oprateurs
C++ dispose dun certain nombre de types prdnis (int, char, float, etc.) et chacun
deux possde un certain nombre doprateurs (+, *, etc.) permettant de raliser des opra-
tions mathmatiques ou logiques. Vous pouvez galement ajouter ces oprateurs vos
propres classes.
La surcharge doprateur est une technique que nous allons tudier dans le Listing 10.6.
Nous allons crer une classe Compteur qui servira au comptage (quelle surprise !) dans les
boucles et les autres situations o lon a besoin dincrmenter, dcrmenter ou tester un
nombre.
Listing 10.6 : Implmentation de la classe Counter
1: // Listing10.6 - La classe Compteur
2:
3: #include <iostream>
4: using namespace std;
Figure 10.3
Illustration dune copie
en profondeur.
5
5
sonAge
ancien objet
Chat
sonAge
nouvel objet
Chat
Tas
C++Livre Page 293 J eudi, 7. mai 2009 9:45 09
294 Le langage C++
5:
6: class Compteur
7: {
8: public:
9: Compteur();
10: ~Compteur(){}
11: int GetVal()const { return saVal; }
12: void SetVal(int x) {saVal = x; }
13:
14: private:
15: int saVal;
16: };
17:
18: Compteur::Compteur():
19: saVal(0)
20: {}
21:
22: int main()
23: {
24: Compteur i;
25: cout << "La valeur de i est " << i.GetVal() << endl;
26: return 0;
27: }
Ce programme produit le rsultat suivant :
La valeur de i est 0
Telle quelle est crite, cette classe est assez inutile. Dclare de la ligne 6 la ligne 16,
elle ne contient quune variable membre de type int. Le constructeur par dfaut, dclar
la ligne 9, initialise lunique variable membre saVal zro (ligne 18).
la diffrence dun objet standard de type int, lobjet Compteur ne peut pas tre incr-
ment, dcrment, additionn, etc. En outre, lafchage de sa valeur est bien plus compliqu
que celle dun entier !
Implmentation dune fonction dincrmentation
La surcharge doprateurs va permettre de restituer un certain nombre des fonctionnalits
qui ont t supprimes de cette classe. Il y a deux moyens, par exemple, dajouter la possi-
bilit dincrmenter un objet Compteur. Le premier consiste crire une mthode dincr-
mentation, comme dans le Listing 10.7.
C++Livre Page 294 J eudi, 7. mai 2009 9:45 09
Chapitre 10 Fonctions avances 295
Listing 10.7 : Ajout dun oprateur dincrmentation
1: // Listing10.7 - La classe Compteur
2:
3: #include <iostream>
4: using namespace std;
5:
6: class Compteur
7: {
8: public:
9: Compteur();
10: ~Compteur(){}
11: int GetVal() const { return saVal; }
12: void SetVal(int x) {saVal = x; }
13: void Increment() { ++saVal; }
14:
15: private:
16: int saVal;
17: };
18:
19: Compteur::Compteur():
20: saVal(0)
21: {}
22:
23: int main()
24: {
25: Compteur i;
26: cout << "La valeur de i est " << i.GetVal() << endl;
27: i.Increment();
28: cout << "La valeur de i est " << i.GetVal() << endl;
29: return 0;
30: }
Ce programme produit le rsultat suivant :
La valeur de iest 0
La valeur de iest 1
La fonction Increment est dnie la ligne 13. Bien quelle fonctionne, elle est assez
lourde utiliser. Il serait ici plus judicieux dajouter un oprateur ++.
Surcharger loprateur prxe
Les oprateurs prxes peuvent tre surchargs laide dune dclaration de la forme :
Type_de_valeur_renvoyee operatorop()
C++Livre Page 295 J eudi, 7. mai 2009 9:45 09
296 Le langage C++
Ici, op correspond loprateur qui doit tre surcharg. Loprateur ++ prxe peut donc
tre surcharg en utilisant la syntaxe suivante :
void operator++()
Cette instruction indique que vous surchargez loprateur ++ et quil ne renverra pas de
rsultat cest la raison pour laquelle le type du rsultat est void. Le Listing 10.8 modie
la classe prcdente pour intgrer cette nouvelle fonctionnalit.
Listing 10.8 : Surcharge de loprateur ++
1: // Listing10.8 - La classe Compteur
2: // oprateur prfixe dincrmentation
3:
4: #include <iostream>
5: using namespace std;
6:
7: class Compteur
8: {
9: public:
10: Compteur();
11: ~Compteur(){}
12: int GetVal()const { return saVal; }
13: void SetVal(int x) {saVal = x; }
14: void Increment() { ++saVal; }
15: void operator++ () { ++saVal; }
16:
17: private:
18: int saVal;
19: };
20:
21: Compteur::Compteur ():
22: saVal(0)
23: {}
24:
25: int main()
26: {
27: Compteur i;
28: cout << "La valeur de i est " << i.GetVal() << endl;
29: i.Increment();
30: cout << "La valeur de i est " << i.GetVal() << endl;
31: ++i;
32: cout << "La valeur de i est " << i.GetVal() << endl;
33: return 0;
34: }
C++Livre Page 296 J eudi, 7. mai 2009 9:45 09
Chapitre 10 Fonctions avances 297
Ce programme produit le rsultat suivant :
La valeur de i est 0
La valeur de i est 1
La valeur de i est 2
La ligne 15 surcharge operator++. Vous pouvez constater que cette surcharge incrmente
simplement la valeur du membre priv saVal. Il est ensuite utilis la ligne 31 et vous
remarquerez que cette syntaxe est bien plus proche de celle dun type prdni comme
int.
Vous pourriez galement en proter pour ajouter les fonctionnalits supplmentaires pour
lesquelles on a t amen crer une classe Compteur, comme la dtection du dpasse-
ment de capacit. Cela dit, cette criture de loprateur dincrmentation recle une
erreur car, si vous lutilisez droite dune expression daffectation, vous obtiendrez une erreur.
Voici un exemple :
Compteur a = ++i;
Cette instruction est suppose crer un nouvel objet a de la classe Compteur, puis lui affec-
ter la valeur de i aprs son incrmentation. Le constructeur de copie par dfaut va grer
cette affectation, mais votre oprateur dincrmentation renverra void, pas un objet Comp-
teur. Or, il est interdit daffecter la valeur void quoi que ce soit (on ne peut rien crer
partir de rien).
Renvoi de valeurs partir des oprateurs surchargs
Votre objectif est de renvoyer un objet Compteur qui pourra tre affect un autre objet de
cette classe. Quel objet devez-vous rcuprer aprs traitement ? Une solution consiste
crer un objet temporaire dans la fonction, puis le renvoyer, comme dans le Listing 10.9.
Listing 10.9 : Renvoi dun objet temporaire
1: // Listing10.9 - operator++ renvoie un objet temporaire
2:
3: #include <iostream>
4:
5: using namespace std;
6:
7: class Compteur
8: {
9: public:
10: Compteur ();
11: ~Compteur (){}
12: int GetVal()const { return itsVal; }
C++Livre Page 297 J eudi, 7. mai 2009 9:45 09
298 Le langage C++
13: void SetVal(int x) {saVal = x; }
14: void Increment() { ++saVal; }
15: Compteur operator++();
16:
17: private:
18: int saVal;
19:
20: };
21:
22: Compteur::Compteur():
23: saVal(0)
24: {}
25:
26: Compteur Compteur::operator++()
27: {
28: ++saVal;
29: Compteur temp;
30: temp.SetVal(saVal);
31: return temp;
32: }
33:
34: int main()
35: {
36: Compteur i;
37: cout << "La valeur de i est " << i.GetVal() << endl;
38: i.Increment();
39: cout << "La valeur de i est " << i.GetVal() << endl;
40: ++i;
41: cout << "La valeur de i est " << i.GetVal() << endl;
42: Compteur a = ++i;
43: cout << "Valeur de a: " << a.GetVal();
44: cout << " et de i: " << i.GetVal() << endl;
45: return 0;
46: }
Ce programme produit le rsultat suivant :
La valeur de i est 0
La valeur de i est 1
La valeur de i est 2
Valeur de a: 3 et de i: 3
Ici, la fonction operator ++ dclare la ligne 15 et dnie aux lignes 26 32 renvoie un
objet Compteur. Pour cela, on cre la variable temporaire temp la ligne 29 an
daccueillir la valeur de lobjet incrmenter. Aprs lincrmentation, cette valeur tempo-
raire est renvoye. la ligne 42, vous pouvez constater que la variable temporaire est
immdiatement affecte a.
C++Livre Page 298 J eudi, 7. mai 2009 9:45 09
Chapitre 10 Fonctions avances 299
Renvoi de variables temporaires anonymes
Il nest pas toujours utile dattribuer un nom une variable temporaire comme celle de la
ligne 29. Si Compteur avait un constructeur qui prenait une valeur en paramtre, vous
auriez pu simplement renvoyer le rsultat de ce constructeur comme valeur de retour de
loprateur dincrmentation. Cest ce que nous faisons dans le Listing 10.10.
Listing 10.10 : Renvoi dun objet temporaire anonyme
1: // Listing10.10 - operator++ renvoie un objet temporaire anonyme
2:
3: #include <iostream>
4:
5: using namespace std;
6:
7: class Compteur
8: {
9: public:
10: Compteur ();
11: Compteur (int val);
12: ~Compteur (){}
13: int GetVal()const { return saVal; }
14: void SetVal(int x) {saVal = x; }
15: void Increment() { ++saVal; }
16: Compteur operator++ ();
17:
18: private:
19: int saVal;
20: };
21:
22: Compteur::Compteur ():
23: saVal(0)
24: {}
25:
26: Compteur::Compteur (int val):
27: saVal(val)
28: {}
29:
30: Compteur Compteur::operator++()
31: {
32: ++saVal;
33: return Compteur(saVal);
34: }
35:
36: int main()
37: {
38: Compteur i;
C++Livre Page 299 J eudi, 7. mai 2009 9:45 09
300 Le langage C++
39: cout << "La valeur de i est " << i.GetVal() << endl;
40: i.Increment();
41: cout << "La valeur de i est " << i.GetVal() << endl;
42: ++i;
43: cout << "La valeur de i est " << i.GetVal() << endl;
44: Compteur a = ++i;
45: cout << "Valeur de a: " << a.GetVal();
46: cout << " et de i: " << i.GetVal() << endl;
47: return 0;
48: }
Ce programme produit le rsultat suivant :
La valeur de i est 0
La valeur de i est 1
La valeur de i est 2
Valeurs de a: 3 et de i: 3
Le nouveau constructeur de la ligne 11 attend un paramtre de type int. Il est implment
aux lignes 26 28 et initialise la variable saVal avec la valeur quon lui a transmise.
Vous pouvez constater que limplmentation doperator++ est simplie. En effet, saVal
est incrmente la ligne 32 puis la ligne suivante cre un objet Compteur temporaire
partir de la nouvelle valeur de saVal. Cet objet est renvoy comme rsultat de
operator++.
Cette procdure est plus lgante, mais elle soulve la question suivante : "ne pourrait-on
pas se passer des objets temporaires ?". Noublions pas que chaque objet temporaire doit
tre cr puis supprim et que ces deux oprations peuvent tre coteuses. En outre,
lobjet i existe dj et a dj la bonne valeur : pourquoi ne pas le renvoyer ? Ce problme
peut tre rsolu grce au pointeur this.
Utilisation du pointeur this
Le pointeur this est automatiquement pass toutes les fonctions membres, y compris les
oprateurs surchargs comme operator++(). Dans les listings que nous avons montrs,
this pointe vers i. Une fois drfrenc, il renvoie lobjet i qui a dj la bonne valeur
dans sa variable membre itsVal. Le Listing 10.11 illustre le renvoi du pointeur this
drfrenc an dconomiser la cration dun objet temporaire superu.
Listing 10.11 : Renvoi du pointeur this
1: // Listing10.11 - Renvoi du pointeur this drfrenc
2:
3: #include <iostream>
C++Livre Page 300 J eudi, 7. mai 2009 9:45 09
Chapitre 10 Fonctions avances 301
4:
5: using namespace std;
6:
7: class Compteur
8: {
9: public:
10: Compteur();
11: ~Compteur(){}
12: int GetVal()const { return saVal; }
13: void SetVal(int x) {saVal = x; }
14: void Increment() { ++saVal; }
15: const Compteur& operator++();
16:
17: private:
18: int saVal;
19: };
20:
21: Compteur::Compteur():
22: saVal(0)
23: {};
24:
25: const Compteur& Compteur::operator++()
26: {
27: ++saVal;
28: return *this;
29: }
30:
31: int main()
32: {
33: Compteuri;
34: cout << "La valeur de i est " << i.GetVal() << endl;
35: i.Increment();
36: cout << "La valeur de i est " << i.GetVal() << endl;
37: ++i;
38: cout << "La valeur de i est " << i.GetVal() << endl;
39: Compteur a = ++i;
40: cout << "Valeur de a: " << a.GetVal();
41: cout << " et de i: " << i.GetVal() << endl;
42: return 0;
43: }
Ce programme produit le rsultat suivant :
La valeur de i est 0
La valeur de i est 1
La valeur de i est 2
Valeur de a: 3 et de i: 3
C++Livre Page 301 J eudi, 7. mai 2009 9:45 09
302 Le langage C++
Dans ce programme, operator++ drfrence le pointeur this an de renvoyer lobjet
courant (lignes 25 29). Le rsultat de cette opration est donc un objet Compteur qui peut
tre affect la variable a. Comme nous lavons dj voqu, si lobjet Compteur allouait
de la mmoire, il faudrait surcharger le constructeur de copie par dfaut mais, ici, il suft
amplement.
La valeur renvoye est une rfrence un objet Compteur, ce qui vous pargne davoir
crer un objet temporaire supplmentaire. Cette rfrence est constante car la valeur ne
doit pas pouvoir tre modie par la fonction qui utilisera le Compteur renvoy.
Lobjet Compteur renvoy doit tre constant. Dans le cas contraire, il serait possible den
changer la valeur. Sil ntait pas constant, vous pourriez par exemple crire la ligne 39 de
la faon suivante :
39: Compteur a = ++++i;
On sattend alors ce que loprateur dincrmentation (++) soit appel sur le rsultat de
lappel de++i. Cela reviendrait en fait appeler deux fois loprateur dincrmentation
sur lobjet i, ce quil est gnralement conseill dviter.
Faites un essai : remplacez la valeur renvoye pour quelle ne soit plus constante la fois
dans la dclaration et limplmentation (lignes 15 et 25), puis remplacez la ligne 39 par
(++++i). Placez un point darrt dans votre dbogueur la ligne 39 et effectuez un pas
pas dtaill (step into). Vous constaterez que loprateur dincrmentation est appel deux
fois. Il est appliqu la valeur renvoye (devenue non constante).
Cest pour viter ce type de comportement que vous devez dclarer la valeur renvoye
comme constante. Si vous remettez les lignes 15 et 25 en ltat (const), sans modier la
ligne 39 (++++i) le compilateur indiquera que vous ne pouvez pas appeler loprateur
dincrmentation sur un objet constant.
Surcharger loprateur sufxe
Sil est possible de surcharger un oprateur prxe (voir plus haut), pourquoi ne pour-
rions-nous pas galement surcharger loprateur sufxe dincrmentation ? Comment le
compilateur va-t-il distinguer le prxe du sufxe ? Par convention, il suft dindiquer un
paramtre entier dans la dclaration de loprateur. La valeur quil contient ne sera pas
prise en compte, mais il servira signaler quil sagit dun oprateur sufxe.
Diffrence entre oprateurs prxe et sufxe
Les deux oprateurs prxe et sufxe ont t tudis au Chapitre 4 (voir Listing 4.3).
Vous avez vu que prxe signie "on incrmente puis on utilise", alors que sufxe signie
"on utilise puis on incrmente".
C++Livre Page 302 J eudi, 7. mai 2009 9:45 09
Chapitre 10 Fonctions avances 303
Loprateur prxe peut donc se contenter dincrmenter la valeur puis de renvoyer lobjet
modi alors que loprateur sufxe doit renvoyer la valeur telle quelle tait avant lincr-
mentation. Pour ce faire, il faut donc crer un objet temporaire qui contiendra la valeur
initiale, incrmenter ensuite la valeur de lobjet original puis renvoyer lobjet temporaire.
Examinez la ligne suivante :
a = x++;
Si la x valait 5, a aura la valeur 5 et x la valeur 6 aprs lexcution de cette ligne. La valeur
stocke dans x a donc bien t renvoye et attribue a avant dtre incrmente. Si x est
un objet, son oprateur dincrmentation sufxe doit enregistrer la valeur initiale (5) dans
un objet temporaire, affecter x la valeur 6, puis renvoyer lobjet temporaire pour attribuer
sa valeur dorigine a.
Comme on renvoie lobjet temporaire, celui-ci doit tre renvoy par valeur et non par rf-
rence puisque sa porte se limite la fonction.
Les oprateurs prxe et sufxe sont implments dans le Listing 10.12.
Listing 10.12 : Oprateurs prxe et sufxe
1: // Listing10.12 - Oprateurs prfixe et suffixe
2:
3: #include <iostream>
4:
5: using namespace std;
6:
7: class Compteur
8: {
9: public:
10: Compteur();
11: ~Compteur(){}
12: int GetVal()const { return saVal; }
13: void SetVal(int x) {saVal = x; }
14: const Compteur& operator++ (); // prfixe
15: const Compteur operator++ (int); // suffixe
16:
17: private:
18: int saVal;
19: };
20:
21: Compteur::Compteur():
22: itsVal(0)
23: {}
24:
25: const Compteur& Compteur::operator++()
C++Livre Page 303 J eudi, 7. mai 2009 9:45 09
304 Le langage C++
26: {
27: ++saVal;
28: return *this;
29: }
30:
31: const Compteur Compteur::operator++(int flag)
32: {
33: Compteur temp(*this);
34: ++saVal;
35: return temp;
36: }
37:
38: int main()
39: {
40: Compteur i;
41: cout << "La valeur de i est " << i.GetVal() << endl;
42: i++;
43: cout << "La valeur de i est " << i.GetVal() << endl;
44: ++i;
45: cout << "La valeur de i est " << i.GetVal() << endl;
46: Compteur a = ++i;
47: cout << "Valeur de a: " << a.GetVal();
48: cout << " et de i: " << i.GetVal() << endl;
49: a = i++;
50: cout << "Valeur de a: " << a.GetVal();
51: cout << " et de i: " << i.GetVal() << endl;
52: return 0;
53: }
Ce programme produit le rsultat suivant :
La valeur de i est 0
La valeur de i est 1
La valeur de i est 2
Valeur de a: 3 et de i: 3
Valeur de a: 3 et de i: 4
Dclar la ligne 15, loprateur sufxe est implment de la ligne 31 la ligne 36.
Loprateur prxe est dclar la ligne 14.
Le paramtre pass loprateur sufxe la ligne 32 (flag) sert simplement indiquer
quil sagit de loprateur sufxe, mais il nest jamais utilis.
Surcharge des oprateurs mathmatiques binaires
Loprateur dincrmentation est un oprateur unaire, car il nagit que sur un objet. La
plupart des oprateurs mathmatiques sont binaires ; ils portent sur deux objets (un de
C++Livre Page 304 J eudi, 7. mai 2009 9:45 09
Chapitre 10 Fonctions avances 305
la classe courante et lautre de nimporte quelle classe). La surcharge doprateurs comme
les oprateurs daddition (+), de soustraction (), de multiplication (*), de division (/) et
de modulo (%) sera donc videmment diffrente de la surcharge des oprateurs de
prxe et de sufxe. Voyons, par exemple, comment surcharger loprateur + pour les
objets Compteur.
Lobjectif est de pouvoir dclarer deux variables Compteur, puis de les additionner,
comme ici :
Compteur VarUne, VarDeux, VarTrois;
VarTrois = VarUne+VarDeux;
L encore, on peut commencer par crire une mthode Add() qui prendra un Compteur en
paramtre, ajoutera les valeurs et renverra un Compteur correspondant au rsultat de
laddition. Cest cette approche que nous utilisons dans le Listing 10.13.
Listing 10.13 : La mthode Add()
1: // Listing10.13 - Mthode Add
2:
3: #include <iostream>
4:
5: using namespace std;
6:
7: class Compteur
8: {
9: public:
10: Compteur();
11: Compteur(int ValInit);
12: ~Compteur(){}
13: int GetVal()const { return saVal; }
14: void SetVal(int x) {saVal = x; }
15: Compteur Add(const Compteur&);
16:
17: private:
18: int saVal;
19: };
20:
21: Compteur::Compteur(int ValInit):
22: saVal(ValInit)
23: {}
24:
25: Compteur::Compteur():
26: saVal(0)
27: {}
28:
C++Livre Page 305 J eudi, 7. mai 2009 9:45 09
306 Le langage C++
29: Compteur::Add(const Compteur & rhs)
30: {
31: return Compteur(saVal+ rhs.GetVal());
32: }
33:
34: int main()
35: {
36: Compteur varUne(2), varDeux(4), varTrois;
37: varTrois = varUne.Add(varDeux);
38: cout << "varUne: " << varUne.GetVal()<< endl;
39: cout << "varDeux: " << varDeux.GetVal() << endl;
40: cout << "varTrois: " << varTrois.GetVal() << endl;
41:
42: return 0;
43: }
Ce programme produit le rsultat suivant :
varUne: 2
varDeux: 4
varTrois: 6
Dclare la ligne 15, la fonction Add() prend en paramtre une rfrence constante
au Compteur correspondant au nombre additionner lobjet en cours. Elle renvoie un
objet Compteur qui sera affect la partie gauche de laffectation ralise la ligne 37.
varUne et varDeux sont respectivement lobjet et le paramtre passs la fonction Add(),
le rsultat de lopration tant affect varTrois
Pour crer varTrois sans devoir linitialiser, vous avez besoin dun constructeur par
dfaut. Celui-ci initialise saVal zro (lignes 25 et 27). En revanche, varOne et varTwo
doivent tre initialises avec une valeur non nulle, ce qui oblige crer un autre construc-
teur (lignes 21 23). Une autre solution au problme serait de passer une valeur de zro
par dfaut au constructeur dclar la ligne 11.
Les lignes 29 32 ralisent laddition des deux objets. La mthode fonctionne, mais son
utilisation nest pas trs naturelle.
Surcharge de loprateur daddition (oprateur +)
La surcharge de loprateur + permet dutiliser la classe Compteur de faon plus naturelle.
On rappelle que, pour surcharger un oprateur, il faut utiliser la syntaxe suivante :
TypeRetour operatorop()
Le Listing 10.4 surcharge loprateur daddition en utilisant cette fonctionnalit.
C++Livre Page 306 J eudi, 7. mai 2009 9:45 09
Chapitre 10 Fonctions avances 307
Listing 10.14 : Surcharge de loprateur +
1: // Listing10.14 - Surcharge de loprateur +
2:
3: #include <iostream>
4:
5: using namespace std;
6:
7: class Compteur
8: {
9: public:
10: Compteur();
11: Compteur(int valInit);
12: ~Compteur (){}
13: int GetVal()const {return saVal; }
14: void SetVal(int x) {saVal = x; }
15: Compteur operator+ (const Compteur&);
16: private:
17: int saVal;
18: };
19:
20: Compteur::Compteur(int valInit):
21: saVal(valInit)
22: {}
23:
24: Compteur::Compteur ():
25: saVal(0)
26: {}
27:
28: Compteur Compteur::operator+(const Compteur & rhs)
29: {
30: return Compteur(saVal+rhs.GetVal());
31: }
32:
33: int main()
34: {
35: Compteur varUne(2), varDeux(4), varTrois;
36: varTrois = varUne+varDeux;
37: cout << "varUne: " << varUne.GetVal()<< endl;
38: cout << "varDeux: " << varDeux.GetVal() << endl;
39: cout << "varTrois: " << varTrois.GetVal() << endl;
40:
41: return 0;
42: }
C++Livre Page 307 J eudi, 7. mai 2009 9:45 09
308 Le langage C++
Ce programme produit le rsultat suivant :
varUne: 2
varDeux: 4
varTrois: 6
operator+ est dclar la ligne 15 et dni de la ligne 28 la ligne 31.
On remarque que cela ressemble beaucoup la dclaration et la dnition de la fonction
Add() du programme prcdent. En revanche, leur syntaxe dutilisation est radicalement
diffrente. Il est bien plus naturel dcrire :
varTrois = varUne+varDeux;
que :
varTrois = varUne.Add(varDeux);
Les deux expressions donnent le mme rsultat, mais la premire est plus facile
comprendre et utiliser.
Ce nouvel oprateur est utilis la ligne 36 :
36: varTrois = varUne+varDeux;
Ce qui est traduit par le compilateur en :
varTrois = varUne.operator+(varDeux);
(vous auriez dailleurs pu lcrire galement sous cette forme).
La mthode operator+() est appele sur loprande gauche et reoit loprande droit en
paramtre.
Pour en savoir plus sur la surcharge doprateurs
Les oprateurs surchargs peuvent tre des mthodes membres, comme on vient de le
montrer, ou des fonctions standard. Cette deuxime possibilit sera dcrite au Chapitre 15
lorsque nous aborderons les fonctions amies.
Les seuls oprateurs qui doivent tre membres dune classe sont loprateur daffectation
(=), loprateur dindexation ([]), loprateur dappel de fonction (()) et loprateur
dindirection (->).
Loprateur [] sera prsent au Chapitre 13, alors que la surcharge de loprateur -> sera
traite au Chapitre 15 lorsque nous prsenterons les pointeurs intelligents.
C++Livre Page 308 J eudi, 7. mai 2009 9:45 09
Chapitre 10 Fonctions avances 309
Restrictions sur la surcharge des oprateurs
Les oprateurs des types prdnis (comme int) ne peuvent pas tre surchargs. Par
ailleurs, leur priorit ne peut pas tre modie, pas plus que leur arit (unaire ou binaire).
Vous ne pouvez pas non plus crer de nouveaux oprateurs : il est par exemple impossible
de crer un oprateur ** pour implmenter lopration "puissance de".
Larit dun oprateur indique le nombre de termes utiliss avec celui-ci.
Certains oprateurs C++ sont unaires et agissent sur un seul terme (maVa-
leur++). Dautres sont binaires et portent sur deux termes (a+b). Il nexiste
quun seul oprateur ternaire (trois termes), loprateur ? et cest la raison
pour laquelle on le nomme souvent tout simplement loprateur ternaire (a >
b? x: y).
Savoir utiliser la surcharge
La surcharge doprateur est lune des fonctionnalits dont abusent le plus les program-
meurs C++ dbutants. Il est en effet tentant de dnir de nouvelles fonctionnalits pour
certains des oprateurs les plus obscurs, mais cela a pour effet irrmdiable de rendre le
code confus et difcile relire.
Il est peut tre amusant de transformer les oprateurs, en dcidant par exemple que
loprateur + et loprateur * permettront deffectuer, respectivement, des divisions et des
additions, mais aucun dveloppeur srieux ne sy risquerait. Le plus grand danger vient
dune utilisation bien intentionne mais originale des oprateurs utiliser + pour effectuer
une concatnation de chane ou / pour dcouper une chane, par exemple. Il y a de bonnes
raisons de penser ces utilisations, mais il y en a encore de meilleures pour sen mer.
Noubliez pas que le but de la surcharge des oprateurs est damliorer la lisibilit du code
et de faciliter lutilisation des objets.
Faire Ne pas faire
Surcharger les oprateurs pour simplier la
mise en uvre et la maintenance dun
programme.
Renvoyer un objet de la classe partir des
oprateurs surchargs.
Crer des oprateurs arithmtiques "non
intuitifs".
Confondre les oprateurs de prxe et de
sufxe, en particulier lors dune surcharge.
In
f
o
C++Livre Page 309 J eudi, 7. mai 2009 9:45 09
310 Le langage C++
Loprateur daffectation
La quatrime et dernire mthode fournie par le compilateur, si vous ne la dnissez pas
vous-mme, est loprateur daffectation operator=(). Cet oprateur est appel chaque
fois que vous affectez une valeur un objet. Voici un exemple :
Chat chatUn(5, 7);
Chat chatDeux(3, 4);
// ... autres instructions
chatDeux = chatUn;
Lobjet chatUn est cr et initialis avec sonAge gal 5 et sonPoids gal 7. Un autre
objet, chatDeux, est cr et initialis avec les valeurs 3 et 4.
Aprs un certain nombre doprations, les valeurs de chatUn sont affectes chatDeux.
Deux problmes se posent ici : que se passe-t-il si sonAge est un pointeur et que deviennent
les valeurs initiales de chatDeux ?
La gestion des variables membres qui stockent leurs valeurs sur le tas a t voque plus
haut, lors de la prsentation du constructeur de copie. Les mmes problmes risquent de
survenir lissue de cette opration, comme on la vu avec les Figures 10.1 et 10.2.
En C++, il faut faire la diffrence entre une copie de surface et une copie en profondeur.
La premire copie simplement les membres et les deux objets pointent nalement sur le
mme emplacement du tas, alors que la seconde alloue la mmoire ncessaire pour y
dupliquer les contenus des objets, comme on la vu avec la Figure 10.3.
Cependant, un autre problme se pose avec loprateur daffectation. Lobjet chatDeux
existe dj et sa mmoire est dj alloue lorsque laffectation est ralise. Pour viter
toute fuite mmoire, il faut donc supprimer lemplacement quil occupait, mais que se
passe-t-il alors si lon affecte chatDeux lui-mme, comme ici ?
chatDeux = chatDeux;
Cette opration est le plus souvent le rsultat dune erreur. Elle peut galement tre acci-
dentelle et dcouler de rfrencements et de drfrencements successifs de pointeurs
cachant cette auto-affectation.
Si vous ne traitez pas correctement ce problme, chatDeux librera lespace quil occupe
en mmoire. Il se posera alors un gros problme lorsquil sera prt recevoir les valeurs de
la partie droite de laffectation puisquelles auront disparu !
Pour viter cela, loprateur daffectation doit tester si la partie droite de loprateur
daffectation est lobjet lui-mme. Pour cela, il examine la valeur du pointeur this,
comme le montre le Listing 10.15. Ceci vite galement le problme que nous venons
dvoquer.
C++Livre Page 310 J eudi, 7. mai 2009 9:45 09
Chapitre 10 Fonctions avances 311
Listing 10.15 : Oprateur daffectation
1: // Listing10.15 - Loprateur daffectation
2:
3: #include <iostream>
4:
5: using namespace std;
6:
7: class Chat
8: {
9: public:
10: Chat(); // constructeur par dfaut
11: // constructeur de copie et destructeur absents!
12: int GetAge() const { return *sonAge; }
13: int GetPoids() const { return *sonPoids; }
14: void SetAge(int age) { *sonAge = age; }
15: Chat & operator=(const Chat &);
16:
17: private:
18: int *sonAge;
19: int *sonPoids;
20: };
21:
22: Chat::Chat()
23: {
24: sonAge = new int;
25: sonPoids = new int;
26: *sonAge = 5;
27: *sonPoids = 9;
28: }
29:
30:
31: Chat & Chat::operator=(const Chat & rhs)
32: {
33: if (this == &rhs)
34: return *this;
35: *sonAge = rhs.GetAge();
36: *sonPoids = rhs.GetPoids();
37: return *this;
38: }
39:
40:
41: int main()
42: {
43: Chat Frisky;
44: cout << "ge de Frisky: " << Frisky.GetAge() << endl;
45: cout << "Lge de Frisky est fix 6...\n";
C++Livre Page 311 J eudi, 7. mai 2009 9:45 09
312 Le langage C++
46: Frisky.SetAge(6);
47: Chat Minou;
48: cout << "ge de Minou : " << Minou.GetAge() << endl;
49: cout << "Copie de Frisky dans Minou...\n";
50: Minou = Frisky;
51: cout << "ge de Minou : " << Minou.GetAge() << endl;
52: return 0;
53: }
Ce programme produit le rsultat suivant :
ge de Frisky: 5
Lge de Frisky est fix a 6...
ge de Minou : 5
Copie de Frisky dans Minou...
ge de Minou : 6
Ce programme reprend nouveau la classe Chat, mais ne dnit ni le constructeur de
copie ni le destructeur an gagner de la place. Loprateur daffectation surcharg est
dclar la ligne 15 et dni de la ligne 31 la ligne 38.
La ligne 33 compare les deux parties de laffectation. Pour cela, on teste si ladresse de
lobjet Chat de droite (rhs) est gale ladresse stocke dans le pointeur this. Si elles
sont identiques, il ny a rien faire, car lobjet de gauche est identique lobjet de droite.
Du coup, la ligne 34 renvoie lobjet courant.
Si lobjet de droite est diffrent, les membres sont copis dans lobjet aux lignes 35 et 36
avant quil soit renvoy.
Lutilisation de loprateur daffectation est illustre la ligne 50 du programme principal,
lorsquun objet Chat appel Frisky est affect lobjet Chat Minou. Le reste devrait vous
tre familier.
Ce listing suppose que deux objets pointant vers la mme adresse sont identiques. Bien
entendu, il est galement possible de surcharger loprateur dgalit (==) an de dnir ce
que vous entendez par "galit" de deux objets de cette classe.
Conversion de type (transtypage)
Maintenant que vous savez affecter un objet un autre objet du mme type, consid-
rons une autre situation. Que se passera-t-il si vous tentez daffecter une variable dun
type prdni (int ou unsigned short, par exemple) un objet dune classe dnie
par lutilisateur ? Prenons lexemple de la classe Compteur cre prcdemment : que
C++Livre Page 312 J eudi, 7. mai 2009 9:45 09
Chapitre 10 Fonctions avances 313
se passera-t-il si vous lui affectez un entier ? Le Listing 10.16 tente deffectuer cette
opration.
Ce programme ne se compilera pas !
Listing 10.16 : Tentative daffectation dun int un Compteur
1: // Listing10.16 - Ce code provoquera une erreur de compilation!
2:
3: #include <iostream>
4:
5: using namespace std;
6:
7: class Compteur
8: {
9: public:
10: Compteur();
11: ~Compteur(){}
12: int GetVal()const {return saVal; }
13: void SetVal(int x) {saVal = x; }
14: private:
15: int saVal;
16: };
17:
18: Compteur::Compteur ():
19: saVal(0)
20: {}
21:
22: int main()
23: {
24: int unInt = 5;
25: Compteur unCpt = unInt;
26: cout << "unCpt: " << unCpt.GetVal() << endl;
27: return 0;
28: }
La compilation de ce programme produira le message suivant :
Compiler error! Unable to convert int to Compteur
En dautres termes, le compilateur est incapable de convertir un type prdni en un objet
dune classe dnie par lutilisateur.
A
tte
n
tio
n
C++Livre Page 313 J eudi, 7. mai 2009 9:45 09
314 Le langage C++
Dclare de la ligne 7 la ligne 16, la classe Compteur na quun constructeur par dfaut.
Elle ne dclare aucune mthode permettant de transformer un type prdni en Compteur.
La ligne 24 de la fonction main() dclare un entier qui est ensuite affect un objet
Compteur, mais cette ligne produit une erreur de compilation. En effet, le compilateur est
incapable de savoir comment affecter un int la variable membre saVal, sauf si vous lui
expliquez.
Le Listing 10.17 corrige le problme en ajoutant un oprateur de conversion, cest--dire
un constructeur prenant un int en paramtre et renvoyant un objet Compteur.
Listing 10.17 : Conversion dun objet de type int en objet de la classe Compteur
1: // Listing10.17 Constructeur/oprateur de conversion
2:
3: #include <iostream>
4:
5: using namespace std;
6:
7: class Compteur
8: {
9: public:
10: Compteur();
11: Compteur(int val);
12: ~Compteur(){}
13: int GetVal()const { return saVal; }
14: void SetVal(int x) {saVal = x; }
15: private:
16: int saVal;
17: };
18:
19: Compteur::Compteur():
20: saVal(0)
21: {}
22:
23: Compteur:: Compteur(int val):
24: saVal(val)
25: {}
26:
27: int main()
28: {
29: int unInt = 5;
30: Compteur unCpt = unInt;
31: cout << "unCpt: " << unCpt.GetVal() << endl;
32: return 0;
33: }
C++Livre Page 314 J eudi, 7. mai 2009 9:45 09
Chapitre 10 Fonctions avances 315
Ce programme produit le rsultat suivant :
unCpt: 5
Deux modications majeures ont t apportes ce programme. Le constructeur est
surcharg pour pouvoir prendre en paramtre un objet de type int (ligne 11). Il est ensuite
implment de la ligne 23 la ligne 25. Ce constructeur permet de crer un objet Compteur
partir dun int.
Le compilateur peut donc dsormais appeler le constructeur en lui passant un int en para-
mtre :
tape 1 : Cration dun compteur appel unCpt.
Cest comme crire int x = 5; pour crer une variable entire x et linitialiser la valeur
5. Ici, nous crons un objet Compteur appel unCpt et nous linitialisons laide de la
variable entire unInt.
tape 2 : Affectation de la valeur de unInt unCpt
Cependant, unInt est un entier, pas un compteur ! Nous devons donc dabord le convertir
en Compteur. Le compilateur tentera automatiquement plusieurs conversions pour vous,
mais vous devez lui indiquer comment faire en crant un constructeur. Ici, il faut donc un
constructeur prenant un entier pour seul paramtre :
class Compteur
{
Compteur(int x);
// ..
};
Ce constructeur fabrique des objets Compteur partir dentiers en crant un compteur
temporaire anonyme. Pour les besoins de notre explication, nous supposerons que lobjet
Compteur temporaire cr partir de lentier court sappelle tempInt.
tape 3 : Affectation de tempInt unCpt, ce qui est quivalent :
unCpt = tempInt;
Ici, tempInt (le compteur temporaire cr par le constructeur) remplace ce qui se trouvait
droite de laffectation. Le compilateur utilise maintenant le Compteur temporaire pour
initialiser unCpt.
Pour mieux comprendre ce mcanisme, il faut savoir que toutes les surcharges dopra-
teurs fonctionnent de la mme faon : vous dclarez un oprateur surcharg laide du
C++Livre Page 315 J eudi, 7. mai 2009 9:45 09
316 Le langage C++
mot cl operator. Avec des oprateurs binaires (comme = ou +) la variable place droite
devient le paramtre de ces surcharges. Cest ce que fait le constructeur. Par consquent :
a = b;
devient :
a.operator=(b);
Pourtant, que se passerait-il si vous tentiez de remplacer laffectation par les instructions
suivantes ?
1: Compteur unCpt(5);
2: int unInt = unCpt;
3: cout << "unInt: " << unInt << endl;
Le compilateur produirait une erreur car, bien quil sache maintenant crer un Compteur
partir dun int, il ne sait pas raliser lopration inverse.
Oprateurs de conversion
Pour grer la conversion des objets de votre classe vers un autre type, C++ propose un
certain nombre doprateurs de conversion que vous pouvez ajouter vos classes an
quelles sachent se convertir implicitement vers des types prdnis. Le Listing 10.18
donne un exemple dun tel oprateur. Vous pourrez remarquer que les oprateurs de
conversion ne prcisent pas le type de leur rsultat, bien quils renvoient une valeur
convertie (en fait, le type du rsultat est le nom mme de la mthode de conversion).
Listing 10.18 : Conversion dun objet Compteur en unsigned short()
1: // Listing10.18 - Oprateurs de conversion
2: #include <iostream>
3:
4: class Compteur
5: {
6: public:
7: Compteur();
8: Compteur(int val);
9: ~Compteur(){}
10: int GetVal()const { return saVal; }
11: void SetVal(int x) {saVal = x; }
12: operator unsigned int();
13: private:
14: int saVal;
15: };
16:
C++Livre Page 316 J eudi, 7. mai 2009 9:45 09
Chapitre 10 Fonctions avances 317
17: Compteur::Compteur():
18: saVal(0)
19: {}
20:
21: Compteur::Compteur(int val):
22: saVal(val)
23: {}
24:
25: Compteur::operator unsigned int()
26: {
27: return ( int(saVal) );
28: }
29:
30: int main()
31: {
32: Compteur cpt(5);
33: int unInt = cpt;
34: std::cout << "unInt: " << unInt << std::endl;
35: return 0;
36: }
Ce programme produit le rsultat suivant :
unInt: 5
La ligne 12 dclare loprateur de conversion. Cette dclaration commence par le mot-cl
operator et ne nindique pas le type du rsultat. La fonction de conversion est implmente
de la ligne 25 la ligne 28. La ligne 27 renvoie la valeur de saVal convertie en int.
Le compilateur tant en mesure de convertir un int en Compteur et vice-versa, les varia-
bles de ces deux types peuvent tre affectes librement lune lautre. Vous pouvez
videmment utiliser la mme approche pour grer galement dautres types.
Questions-rponses
Q Pourquoi utiliser des valeurs par dfaut lorsquon peut surcharger une fonction ?
R Il est souvent plus ais de grer une seule fonction comprenant des valeurs par dfaut
que dtudier le droulement de deux fonctions. En outre, si vous modiez une fonction
surcharge, vous devez galement mettre jour toutes les autres fonctions surcharges
correspondantes an dviter toute erreur dexcution ou de compilation.
Q Au vu de toutes ces contraintes, doit-on prfrer les valeurs par dfaut ?
R Non. Les fonctions surcharges offrent des fonctionnalits qui ne sont pas disponibles
avec les valeurs par dfaut. Par exemple, il est possible de modier non seulement le
C++Livre Page 317 J eudi, 7. mai 2009 9:45 09
318 Le langage C++
nombre de paramtres dans une liste, mais galement leur type, ou encore de fournir
une implmentation diffrente pour diverses combinaisons de types de paramtres.
Q Lors de la cration dun constructeur, comment les oprations doivent-elles se
rpartir entre la phase dinitialisation et la phase dexcution ?
R En rgle gnrale, il faut utiliser le plus possible la phase dinitialisation cest--dire
initialiser toutes les variables membres cet endroit. Certaines oprations, comme les
calculs (y compris ceux qui servent linitialisation) et les afchages doivent avoir lieu
dans le corps du constructeur.
Q Une fonction surcharge peut-elle inclure un paramtre par dfaut ?
R Oui. Il est possible dassocier une liste de valeurs par dfaut une fonction surcharge.
La syntaxe employe et les rgles respecter sont identiques celles utilises pour les
variables par dfaut de nimporte quelle fonction.
Q Pourquoi certaines fonctions membres sont-elles dnies dans les dclarations de
classe alors que dautres ne le sont pas ?
R Les fonctions dont limplmentation est dnie lors de la dclaration dune classe sont
traduites par des fonctions en ligne. Gnralement, cela ne concerne que les fonctions
dont le corps est trs simple. Vous pouvez galement demander ce quune mthode
soit en ligne laide du mot-cl inline, mme si elle est dnie en dehors de la dcla-
ration de la classe.
Testez vos connaissances
1. En quoi les fonctions surcharges doivent-elles tre diffrentes ?
2. Quelle est la diffrence entre une dclaration et une dnition ?
3. Quand un constructeur de copie est-il appel ?
4. Quand le destructeur est-il appel ?
5. En quoi le constructeur de copie diffre-t-il de loprateur daffectation (=) ?
6. Quest-ce que le pointeur this ?
7. Quelle est la diffrence entre la surcharge dun oprateur prxe dincrmentation et
la surcharge dun oprateur sufxe ?
8. Est-il possible de surcharger loprateur + avec des entiers courts ?
9. Peut-on surcharger loprateur ++ pour quil dcrmente un objet dune classe ?
10. Quelle type de rsultat doit gurer dans la dclaration des oprateurs de conversion ?
C++Livre Page 318 J eudi, 7. mai 2009 9:45 09
Chapitre 10 Fonctions avances 319
Exercices
1. crivez la dclaration dune classe Cercle comprenant une variable membre
SonRayon. Associez un constructeur par dfaut, un destructeur et plusieurs mthodes
daccs cette variable.
2. Dans la classe cre lors de lexercice prcdent, implmentez le constructeur par
dfaut, qui affecte la valeur 5 la variable sonRayon. Faites-le dans la phase dinitiali-
sation du constructeur et non dans le corps.
3. Ajoutez ensuite un deuxime constructeur prenant un paramtre permettant daffecter
une valeur la variable sonRayon.
4. Dans la classe Cercle, crez un oprateur prxe et un oprateur sufxe permettant
dincrmenter la variable sonRayon.
5. Modiez la classe Cercle pour quelle stocke sonRayon sur le tas. Pour cela, vous
devrez modier les mthodes existantes.
6. Ajoutez un constructeur de copie la classe Cercle.
7. Ajoutez un oprateur daffectation la classe Cercle.
8. Crez deux objets Cercle. Utilisez le constructeur par dfaut pour le premier et affec-
tez la valeur 9 au second. Appelez loprateur dincrmentation pour chacun deux,
puis afchez leurs valeurs. Enn, aprs avoir affect la valeur du second au premier,
afchez leurs valeurs.
9. CHERCHEZ LERREUR : limplmentation de loprateur daffectation est errone.
CARRE CARRE::operator=(const CARRE & rhs)
{
sonCote = new int;
*sonCote = rhs.GetCote();
return *this;
}
10. CHERCHEZ LERREUR : limplmentation de loprateur daddition est errone.
EntierCourt EntierCourt::operator+(const EntierCourt& rhs)
{
saVal += rhs.GetVal();
return *this;
}
C++Livre Page 319 J eudi, 7. mai 2009 9:45 09
C++Livre Page 320 J eudi, 7. mai 2009 9:45 09
11
Analyse et conception
oriente objet
Au sommaire de ce chapitre
Comment utiliser lanalyse oriente objet pour comprendre le problme rsoudre
Comment obtenir, grce la conception oriente objet, une solution robuste, extensible
et able
Comment documenter lanalyse et la conception de vos applications laide de UML
(Unied Modeling Language)
force de se concentrer sur la syntaxe du langage C++, on risque de perdre de vue
limportance de ces techniques de construction des programmes.
C++Livre Page 321 J eudi, 7. mai 2009 9:45 09
322 Le langage C++
Les modles
Lobjectif dun modle est de crer une abstraction signicative du monde rel. Cette
abstraction doit tre plus simple que la ralit, mais aussi la reter dlement an que le
modle puisse servir prvoir le comportement des choses dans la ralit.
Un globe terrestre pos sur un bureau en est un exemple classique. Le modle nest pas la
Terre elle-mme et il ne risque pas dtre confondu avec la chose relle. Cependant lun
est une reprsentation sufsamment dle de lautre pour nous permettre lapprentissage
de la Terre en tudiant le globe.
Il y a, bien sr, des simplications importantes : il ne pleut jamais sur le globe de mon ls et
il nest pas sujet aux mares, aux tremblements de terre, etc. Cependant, je peux lutiliser
pour prvoir le temps quil me faudra pour me rendre de chez moi New York.
Un modle qui ne serait pas plus simple que ce quil reprsente ne serait pas dune grande
utilit.
La conception oriente objet impose la construction de modles efcaces, elle met en jeu
deux lments importants : un langage et un processus de modlisation.
Conception : le langage de modlisation
Le langage de modlisation est laspect le moins important de lanalyse et la conception
oriente objet, mais cest malheureusement lui que sintressent principalement la
plupart des gens. Un langage de modlisation nest rien de plus quune convention permet-
tant de dnir un modle sur un autre support papier ou systme informatique et dans
un autre format comme les graphiques, le texte ou les symboles. Rien ne nous empche,
par exemple, de reprsenter nos classes sous la forme de triangles et la relation dhritage
comme une ligne pointille. Vous pourriez alors modliser un granium comme sur la
Figure 11.1.
Cette gure montre quun granium est une sorte particulire de eur. Tant que nous
sommes daccord sur cette reprsentation du concept dhritage (gnralisation/spciali-
sation), nous nous comprendrons parfaitement. Au cours du temps, nous voudrons sre-
ment modliser de nombreuses relations complexes et nous dvelopperons donc nos
propres conventions et rgles pour nos diagrammes.
Il reste bien entendu faire connatre ces conventions toutes les personnes qui travaillent
avec nous et les apprendre aux nouveaux venus. Si nous collaborons avec dautres socits
qui utilisent leurs propres conventions, nous devrons alors mettre en place une convention
commune de manire viter les malentendus invitables.
C++Livre Page 322 J eudi, 7. mai 2009 9:45 09
Chapitre 11 Analyse et conception oriente objet 323
Il serait encore plus pratique davoir recours un langage commun dni une fois pour
toutes. Le langage universel en matire danalyse et de conception logicielle sappelle
UML (Unied Modeling Language). Son rle est de rpondre des questions comme
"Comment reprsenter une relation dhritage ?". En UML, le granium de notre exemple
aurait ainsi t reprsent par la Figure 11.2.
En UML, les classes sont reprsentes par des rectangles et lhritage sous la forme
dune che partant de la classe la plus spcialise vers la plus gnrale. Le sens de cette
che peut sembler peu intuitif, mais il na pas vraiment dimportance ; lessentiel,
aprs avoir appris la reprsentation, consiste utiliser une convention commune pour
communiquer.
Les dtails de UML sont assez simples. Les reprsentations schmatiques sont gnrale-
ment assez faciles comprendre et appliquer et vous les apprendrez mesure que nous
les prsenterons. Bien quil soit possible dcrire un livre entirement consacr UML, la
vrit est que lon nen utilise la plupart du temps quun petit sous-ensemble, qui sapprend
rapidement.
Figure 11.1
Gnralisation/
spcialisation.
Figure 11.2
Reprsentation UML de
la spcialisation.
Fleur
Granium
Fleur
Granium
C++Livre Page 323 J eudi, 7. mai 2009 9:45 09
324 Le langage C++
Conception : le processus
Le processus de lanalyse et de la conception oriente objet est bien plus complexe et plus
important que le langage de modlisation. Il est donc assez amusant que lon en entende
beaucoup moins parler. En effet, le dbat sur les langages de modlisation est plus ou
moins termin lindustrie a choisi UML comme standard, alors que le dbat sur le
processus fait toujours rage.
Une mthode est un langage et un processus de modlisation. On la dsigne souvent par le
terme de "mthodologie", bien que ce soit une erreur puisque ce mot dsigne ltude des
mthodes.
Il existe plusieurs mthodes, parmi lesquelles la mthode de Grady Booch, celle de Ivar
Jacobson et la mthode OMT de James Rumbaugh (Object Modeling Technologt). Ces trois
hommes ont concoct Objectory, dsormais appel Rational Unied Process, qui est la fois
une mthode et un produit commercial distribu par Rational Software, Inc. Lorsquils
travaillaient pour cette division dIBM, ils taient affectueusement appels les Three Amigos.
Ce chapitre respecte en gros leur processus, sans le suivre aveuglment. Dautres mthodes
sont intressantes diffrents points de vue, lassociation des unes et des autres permettant
dobtenir une schmatique assez efcace. Tout le monde nadopte pas cette approche ; il
est donc conseill de vous documenter sur la pratique de lingnierie logicielle pour vous
faire votre propre ide.
Le processus de la conception peut tre itratif, ce qui signie quau cours du dveloppe-
ment du logiciel, la totalit du processus peut tre rpte mesure que vous ajoutez des
amliorations dcoulant dune meilleure comprhension des besoins. La conception guide
limplmentation, mais les dtails absents de limplmentation ralimentent la conception.
Il est important de ne pas tenter, ds le dpart, de dvelopper un projet important de faon
rectiligne et squentielle, mais plutt de boucler dans diffrentes parties du projet en
amliorant constamment sa conception et en afnant son implmentation.
Dveloppement itratif ou dveloppement en cascade ?
Le dveloppement itratif se distingue du dveloppement en cascade, dans lequel la sortie
dune tape devient lentre de la suivante et o il est impossible de revenir en arrire (voir
Figure 11.3).
Dans un processus en cascade, les besoins sont dtaills et conrms par le client, ils sont
alors transmis au concepteur et gravs sans le marbre. Le concepteur fait son travail puis
passe sa conception au programmeur qui limplmente. Le programmeur crit le code qui
est test par le Service Qualit, puis livr au client. Belle thorie qui peut se rvler catas-
trophique en pratique.
C++Livre Page 324 J eudi, 7. mai 2009 9:45 09
Chapitre 11 Analyse et conception oriente objet 325
Processus du dveloppement itratif
Dans un dveloppement itratif, on part dun concept (une ide de ce que lon veut construire).
Cette vision stend et volue mesure que lon examine les dtails.
Ds que nous avons une ide des besoins, la conception dmarre tout en sachant que les ques-
tions qui vont se poser entraineront des modications qui auront probablement un effet sur les
besoins. Tout en travaillant sur la conception, on commence galement le prototypage puis
limplmentation du produit. Les problmes survenant pendant le dveloppement ont une
rpercussion sur la conception et peuvent mme inuencer notre comprhension des
besoins. Nous ne concevons et implmentons que des parties du produit complet, en
rebouclant continuellement dans les phases de conception et dimplmentation.
Bien que les tapes du processus se rptent, il est pratiquement impossible de les dcrire
de manire cyclique ; cest la raison pour laquelle la liste qui suit les dcrit de manire
squentielle.
tapes du processus de dveloppement itratif :
1. Conceptualisation.
La conceptualisation est la "vision". Cest une simple phrase qui dcrit la grande ide.
2. Analyse.
Lanalyse est le processus de comprhension des besoins.
3. Conception.
La conception est la phase de cration du modle de vos classes, partir duquel vous
produirez votre code.
4. Implmentation.
Limplmentation est la transcription dans le langage choisi (C++, par exemple).
5. Tests.
Les tests consistent vrier que le travail a t bien fait.
Figure 11.3
La mthode en cascade.
Analyse
Conception
Implmentation
Tests
C++Livre Page 325 J eudi, 7. mai 2009 9:45 09
326 Le langage C++
6. Livraison.
La livraison est la remise au client.
Ce ne sont pas les mmes tapes que les phases de Rational Unied Process,
qui sont les suivantes :
Mise en route ;
laboration ;
Construction ;
Transition.
Ni de son workow, qui est constitu des tapes suivantes :
Modlisation dactivit ;
Besoins ;
Analyse et conception ;
Implmentation ;
Tests ;
Dploiement ;
Conguration et gestion des modications ;
Gestion de projet ;
Environnement.
Il doit tre bien clair que chacune de ces tapes sera rpte de nombreuses fois pendant le
dveloppement dun produit donn. Pour bien comprendre le processus de conception
itratif, il est toutefois plus simple de les prsenter lune aprs lautre.
Ce processus est simple. Le reste du chapitre ne fait quen prsenter les dtails.
Controverses
Ce qui se passe au cours de chacune des tapes dun processus de conception itratif,
mme le nom attribu ces tapes, fait lobjet de controverses sans n. En fait, cela na
aucune importance. Les tapes essentielles sont les mmes dans tous les processus : savoir
ce que vous voulez construire, concevoir une solution et limplmenter.
Le but est de produire un code qui respecte les besoins noncs, qui soit able, extensible
et facilement mis jour, tout en respectant les dlais et le budget impartis.
In
f
o
C++Livre Page 326 J eudi, 7. mai 2009 9:45 09
Chapitre 11 Analyse et conception oriente objet 327
tape 1 La conceptualisation : commencer
par la vision
Tout bon logiciel commence par une vision, un produit ou un service que quelquun pense
intressant de dvelopper. La vision dun projet est rarement le fait dun comit.
La toute premire phase dune analyse et dune conception orientes objet consiste
capturer cette vision sous la forme dune simple phrase (ou, au plus, dun court paragra-
phe), qui deviendra le principe directeur du dveloppement. Lquipe qui sera charge
dimplmenter cette vision pourra sy rfrer et la modier si ncessaire mesure de
lavancement du projet.
Mme si lnonc de la vision a t produit par un comit du dpartement nancier, une
seule personne devrait tre considre comme le "visionnaire". Son travail consiste tre
le gardien de la lumire sacre. mesure que le projet avance, les besoins voluent ; les
contraintes de planication et de livraison peuvent (et devraient) modier ce que vous
tentez daccomplir la premire itration du programme, mais le visionnaire doit garder
un il sur lide essentielle pour sassurer que ce qui est produit rete trs dlement la
vision initiale. Cest ce dvouement sans faille, passionn, qui permettra damener le
projet ses ns. Si cette vision est perdue, le produit lest aussi.
La phase de conceptualisation, dans laquelle sarticule la vision, est trs courte. Elle ne
doit pas dpasser le temps du ash rvlateur et de sa retranscription. Dans dautres
projets, la vision exige une phase de dtermination de lenvergure plus complexe, o il faut
parvenir un accord sur les composants, entre les personnes impliques. Dans ce cas, le
succs du projet sera fonction des lments retenus ou refuss.
tape 2 Lanalyse : collecter les besoins
Certaines socits confondent la vision et les besoins. Une bonne vision est ncessaire,
mais pas sufsante. Pour passer la conception, il faut comprendre lutilisation qui sera
faite du produit et le comportement quil devra avoir. Lobjectif de lanalyse est darticuler
et de collecter ces besoins. Son rsultat est un document des besoins, dont la premire
partie est lanalyse des case dutilisation (use cases).
Les cas dutilisation
Les use cases , ou cas dutilisation, sont tout simplement une description de haut niveau
de la faon dont le produit sera utilis. Ils orientent non seulement lanalyse, mais gale-
ment la conception en vous aidant dterminer les classes et sont particulirement
importants pour les tests du produit.
C++Livre Page 327 J eudi, 7. mai 2009 9:45 09
328 Le langage C++
Llaboration dun jeu complet de cas dutilisation peut reprsenter le travail le plus
important de lanalyse. Les experts du domaine sont les plus qualis pour vous aider dans
cette tche car ce sont eux qui ont le plus dinformations sur les besoins que vous tentez de
cerner.
Les cas dutilisation ne tiennent pas compte des dtails de linterface utilisateur, pas plus
que des rouages internes du systme que vous tes en train de construire. Ils devraient
plutt se consacrer aux interactions qui doivent survenir et aux personnes et aux systmes
(les acteurs) qui devront collaborer pour produire le rsultat attendu. .
Voici quelques dnitions pour rsumer cette partie :
Cas dutilisation. Une description de la faon dont le logiciel sera utilis.
Experts du domaine. Comme le nom lindique, ce sont les experts du domaine auquel
le produit est destin.
Acteur. Tout utilisateur ou systme interagissant avec le systme que vous dveloppez.
Un cas dutilisation est une description de linteraction entre un acteur et le systme lui-
mme. Pour les besoins de lanalyse des cas dutilisation, le systme est considr comme
une "bote noire". Un acteur "envoie un message au systme", et une raction se produit :
des informations sont renvoyes, lengin spatial change de trajectoire, etc.
Les cas dutilisation ne sufsent pas connatre tous les besoins, mais ils sont essentiels et
reoivent souvent la plus grande attention. On peut galement inclure les rgles dentre-
prise, les donnes et les besoins techniques en matire de performances, de scurit, etc.
Identier les acteurs
Les acteurs ne sont pas ncessairement des personnes, les systmes qui interagissent avec
le systme que vous construisez le sont aussi. Si, par exemple, vous construisez un guichet
automatique de banque, le client et lemploy de banque sont tous les deux acteurs de
mme que tout systme impliqu, comme un systme de prt ou de suivi des dcouverts.
Les caractristiques essentielles dun acteur sont :
dtre externes au systme ;
dinteragir avec le systme.
Le dmarrage est souvent la partie la plus difcile de lanalyse des cas dutili-
sation. Le meilleur point de dpart peut tre une sance de brainstorming.
Notez la liste des personnes et des systmes qui interagiront avec votre nouveau
systme. Noubliez pas que Personnes signie en ralit rles le guichetier, le
directeur, le client, etc. Une mme personne peut avoir plusieurs rles.
A
s
t
u
c
e
C++Livre Page 328 J eudi, 7. mai 2009 9:45 09
Chapitre 11 Analyse et conception oriente objet 329
Dans notre exemple de guichet automatique, nous pourrions avoir les rles suivants :
le client ;
le personnel de la banque ;
un systme back-ofce ;
la personne qui alimente le guichet automatique.
Cette liste suft pour commencer la gnration de nos cas dutilisation.
Dterminer les premiers cas dutilisation
Dans lexemple du guichet automatique, commenons par le client. Il peut :
vrier son solde ;
dposer de largent sur son compte ;
retirer de largent ;
faire des virements entre ses comptes ;
ouvrir un compte ;
fermer un compte.
Doit-on faire la distinction entre "dposer de largent sur son compte courant" et "dposer
de largent sur son compte pargne", ou ces actions doivent-elles tre combines en une
seule (comme nous lavons fait) : "dposer de largent sur son compte" ? Cela dpend si la
diffrence est signicative dans le domaine en question.
Pour le savoir, vous devez demander si les mcanismes (actions entreprises par le client) et
les sorties (rponse du systme) sont diffrents. La rponse aux deux questions est "non" :
le client dpose de largent de la mme faon sur ces deux comptes et la sortie est quasi-
ment la mme ; le guichet rpond en incrmentant le solde du compte concern.
Lacteur et le systme se comportant et rpondant de faon quasiment identique dans le
cas dun versement sur lun ou lautre compte, ces deux cas dutilisation nen forment
rellement quun seul. Vous pourrez plus tard tenter deux variantes pour voir sil existe
une quelconque diffrence.
En examinant chaque acteur, vous pouvez dcouvrir des cas dutilisation supplmentaires
en rpondant aux questions suivantes :
Pourquoi lacteur utilise-t-il ce systme ?
Le client lutilise pour obtenir du liquide, faire un dpt ou vrier son solde.
C++Livre Page 329 J eudi, 7. mai 2009 9:45 09
330 Le langage C++
Quelle est la sortie que lacteur veut ou attend ?
Ajout dargent sur un compte ou obtention de liquide pour faire un achat.
Quest-ce qui incite lacteur utiliser maintenant le systme ?
Il peut avoir reu de largent sur son compte ou avoir lintention de faire un achat.
Que doit faire lacteur pour utiliser le systme ?
Sidentier en insrant une carte dans la machine.
Ah ! Nous avons donc besoin dun cas dutilisation supplmentaire pour la connexion
du client au systme.
Quelles informations lacteur doit-il fournir au systme ?
Entrer un code personnel.
Ah ! Nous avons donc encore besoin dune cas dutilisation supplmentaire pour
obtenir et modier son code personnel.
Quelles sont les informations que lacteur attend du systme ?
Solde du compte, etc.
Vous pouvez aussi vous concentrer sur les attributs des objets du domaine. Le client a un
nom, un code et un numro de compte. Avez-vous des cas dutilisation pour grer ces
objets ? Un compte a un numro, un solde et un historique des oprations. Ces lments
gurent-ils dans les cas dutilisation ?
Aprs avoir explor en dtail les cas dutilisation du client, ltape suivante consiste
dvelopper ceux des autres acteurs. Dans notre exemple :
Le client vrie son ou ses soldes.
Le client dpose de largent sur son ou ses comptes.
Le client retire de largent de son ou ses comptes.
Le client fait des virements.
Le client ouvre un compte.
Le client ferme un compte.
Le client se connecte son ou ses comptes.
Le client vrie ses dernires transactions.
Lemploy de banque se connecte un compte spcial de gestion.
Lemploy de banque modie un compte client.
Un systme back-ofce met un compte client jour en fonction dune activit externe.
C++Livre Page 330 J eudi, 7. mai 2009 9:45 09
Chapitre 11 Analyse et conception oriente objet 331
Les modications dun compte client sont retes dans un systme back ofce.
Le guichet automatique signale quil est court dargent.
Le technicien alimente le guichet automatique en argent et en imprims.
Cration du modle du domaine
Aprs avoir tabli un premier dcoupage de vos cas dutilisation, le document des besoins
peut tre toff avec un modle du domaine dtaill. Ce document capture tout ce que vous
savez du domaine. Les objets du domaine que vous crez, dcrivant tous les objets
mentionns dans vos cas dutilisation, en font partie. Notre exemple comprend pour
linstant les objets suivants : client, personnel de la banque, systmes back-ofce, compte
courant, compte pargne, etc.
Pour chacun de ces objets du domaine, nous devons capturer des donnes essentielles
comme le nom de lobjet (client, compte, etc.), si lobjet est un acteur, ses attributs et ses
comportements principaux, etc. De nombreux outils de modlisation permettent de captu-
rer ce type dinformations dans des descriptions de "classe". La Figure 11.4 montre
comment ces informations sont reprsentes avec loutil de modlisation Rational Rose.
Il est important de raliser que les objets dcrits ne sont pas la classe qui sera utilise dans
la conception (mme sil y aura srement des classes similaires), mais des classes dobjets
du domaine. Il sagit dune documentation des besoins de fonctionnement du systme, pas
dune documentation de la faon dont celui-ci va rpondre ces besoins.
Les relations entre les objets du domaine de notre exemple peuvent tre reprsentes en
UML avec les mmes conventions que celles que nous utiliserons plus tard pour dcrire
Figure 11.4
Rational Rose.
C++Livre Page 331 J eudi, 7. mai 2009 9:45 09
332 Le langage C++
les relations entre les classes de la conception. Cest lun des principaux avantages de
UML : vous pouvez utiliser la mme reprsentation chaque tape du projet.
Par exemple, la Figure 11.5 prcise que les vrications des comptes courant ou pargne
sont des spcialisations du concept plus gnral de compte bancaire.
Dans la Figure 11.5, les rectangles reprsentent les diffrents objets du domaine et les
ches indiquent une gnralisation. Le sens des ches va de la classe spcialise vers la
classe de base plus gnrale. Les comptes courant et pargne sont donc tous deux une
forme spcialise de compte bancaire.
Nous montrons ici la relation entre les classes du domaine des besoins. Vous
pourrez ultrieurement, au moment de la conception, dcider davoir un objet
CompteCourant et un objet CompteBancaire, et implmenter cette relation
laide de lhritage, mais ce seront des dcisions de conception. Lors de
lanalyse, nous nous contentons de documenter notre comprhension de ces
objets dans le domaine.
UML est un langage de modlisation riche permettant de reprsenter toutes les relations.
Les principales, captures lors de lanalyse, sont les suivantes :
la gnralisation (ou spcialisation) ;
la composition ;
lassociation.
Figure 11.5
Spcialisation.
Compte bancaire
Compte courant Compte pargne
Gnralisation
Objet du domaine
In
f
o
C++Livre Page 332 J eudi, 7. mai 2009 9:45 09
Chapitre 11 Analyse et conception oriente objet 333
Gnralisation
Elle est souvent assimile lhritage, mais il existe une distinction signicative entre les
deux. La gnralisation dcrit la relation ; lhritage est limplmentation de la gnralisa-
tion dans le code. Le corollaire la gnralisation est la spcialisation. Un chat est une
forme spcialise danimal ; lanimal est un concept gnralis qui runit un chien ou dun
chat.
La spcialisation implique que lobjet driv est-un sous-type de lobjet de base. Un
compte courant est-un compte bancaire. Cette relation est symtrique : un compte bancaire
gnralise le comportement et les attributs communs dun compte courant et dun compte
pargne.
Dans la phase danalyse du domaine, vous devez chercher capturer ces relations telles
quelles existent dans la ralit.
Composition
Un objet est souvent compos de plusieurs sous-objets. Un compte courant comprend un
solde, un historique des transactions, une identication du client, etc. On dit que le compte
courant a ces lments ; la composition modlise la relation a-un. En UML, elle est repr-
sente par une che avec un losange partant de lobjet contenant vers lobjet contenu,
comme le montre la Figure 11.6.
Le compte courant a-un solde. Vous pouvez combiner ces diagrammes pour reprsenter un
ensemble de relations assez complexe. La Figure 11.7 montre quun compte courant et un
compte pargne sont tous les deux des comptes bancaires et que tous les comptes bancaires
ont la fois un solde et un historique des transactions.
Figure 11.6
Composition.
Compte courant
Solde
Agrgation
C++Livre Page 333 J eudi, 7. mai 2009 9:45 09
334 Le langage C++
Association
La troisime relation frquemment reprsente lors de lanalyse du domaine est une
simple association. Une association suggre linteraction quelconque de deux objets, sans
prciser vraiment son objectif. Cette dnition sera afne lors de la phase de conception.
Pour lanalyse, elle indique simplement quun objet A et un objet B sont lis, mais
quaucun deux ne contient, ni est une spcialisation de lautre. La notation UML la repr-
sente comme une simple ligne entre les objets (voir Figure 11.8).
tablir des scnarios
Avec nos cas dutilisation prliminaires et les outils permettant de reprsenter les relations
entre les objets du domaine, nous pouvons formaliser les cas et les approfondir.
Figure 11.7
Relations entre objets.
Figure 11.8
Association.
Compte bancaire
Compte courant Compte pargne
Solde Historique
des transactions
Objet A Objet B
Association
C++Livre Page 334 J eudi, 7. mai 2009 9:45 09
Chapitre 11 Analyse et conception oriente objet 335
Chaque cas dutilisation peut tre dcompos en une srie de scnarios. Un scnario est
une description dun ensemble prcis de circonstances qui se distinguent parmi les
lments du cas dutilisation. Par exemple, le cas "Le client retire de largent de son ou ses
comptes" peut avoir les scnarios suivants :
Le client demande un retrait de 300 de son compte courant, il prend les billets, et le
systme imprime un reu.
Le client demande un retrait de 300 de son compte courant, mais son solde est de
200 . Il est avis que son compte nest pas sufsamment approvisionn.
Le client demande un retrait de 300 de son compte courant, mais il a dj retir 100
aujourdhui et la limite est de 300 par jour. Il est inform du problme et doit dcider
sil ne veut prlever que 200 .
Le client demande un retrait de 300 de son compte courant, mais le reu ne peut tre
imprim par manque de papier. Il est inform du problme et doit dcider sil veut ou
non poursuivre lopration sans recevoir de reu.
Ainsi de suite... Chaque scnario explore une variante du cas original et reprsente parfois
des cas exceptionnels (pas assez dargent sur le compte, plus de papier, etc.). Ils peuvent
aussi impliquer des dcisions nuances dans le cas lui-mme (le client veut-il transfrer de
largent avant de procder au retrait ?).
Il est impossible dexplorer tous les scnarios. En ralit, on recherche plutt ceux qui
expriment les besoins du systme ou les dtails de linteraction avec lutilisateur.
tablir des directives
Vous devez crer des directives pour documenter chaque scnario. Celles-ci sont captures
dans vos spcications des besoins. Gnralement, vous devrez vous assurer que chaque
scnario comprend :
Des prconditions. Ce qui doit tre vrai pour que le scnario commence.
Des dclencheurs. Les vnements qui provoquent le dmarrage du scnario.
Des actions ralises par lacteur.
Des rsultats ou modications provoqus par le systme.
Un retour reu par lacteur.
Des actions rptitives se produisent-elles ? Ce qui leur fait prendre n.
Une description du ux logique du scnario.
Ce qui provoque la n du scnario.
Des postconditions. Ce qui doit tre vrai lorsque le scnario est termin.
C++Livre Page 335 J eudi, 7. mai 2009 9:45 09
336 Le langage C++
En outre, vous devrez nommer chaque cas dutilisation et chaque scnario. Par exemple :
Ce cas peut tre reprsent laide du schma simple de la Figure 11.9.
La seule information capture ici est une interaction abstraite entre un acteur (le client) et
le systme. Le diagramme devient un peu plus utile lorsque lon montre linteraction entre
les cas dutilisation. Nous avons crit un peu plus utile parce quil nexiste que deux inte-
ractions possibles : <<utilise>> et <<tend>>. La premire indique quun cas dutilisa-
tion est un surensemble dun autre. Il nest, par exemple, pas possible de retirer de largent
sans stre identi. Cette relation est reprsente dans la Figure 11.10.
Cette gure indique que le cas dutilisation "Retire de largent" utilise le cas "Sidentie"
et que ce dernier fait donc partie du premier.
En fait, le strotype <<tend>> tait cens indiquer une relation conditionnelle ressem-
blant un peu lhritage, mais il y a tant de confusion entre <<utilise>> et <<tend>>
dans la modlisation objet que de nombreux dveloppeurs ont simplement mis de ct
<<tend>> en estimant que sa signication ntait pas sufsamment bien comprise.
Cas dutilisation : Le client demande retirer du liquide.
Scnario : Retrait russi du compte courant.
Prconditions : Le client est dj connect au systme.
Dclencheur : Le client demande un "retrait".
Description : Le client choisit de retirer du liquide dun compte courant. Le guichet automatique est appro-
visionn en argent liquide et en papier pour le reu, le systme fonctionne. Le guichet auto-
matique demande au client le montant retirer, le client demande 300 , ce quil est autoris
faire. La machine lui donne 300 et imprime un reu, le client prend largent et le reu.
Postconditions : Le compte du client est dbit de 300 , et le client possde 300 en liquide.
Figure 11.9
Reprsentation
dun cas dutilisation.
Retire du liquide
Association
Client
Acteur
Cas d'utilisation
C++Livre Page 336 J eudi, 7. mai 2009 9:45 09
Chapitre 11 Analyse et conception oriente objet 337
Personnellement, nous utilisons <<utilise>> lorsque nous pourrions simplement copier
et coller sur place le cas dutilisation existant et <<tend>> lorsque lon utilise le cas
dutilisation sous certaines conditions.
Diagrammes dinteraction
Bien que le diagramme du cas dutilisation ait, en lui-mme, un intrt limit, vous pouvez
associer des diagrammes aux cas dutilisation pour amliorer la documentation et la compr-
hension des interactions. Nous savons, par exemple, que le scnario Retirer de largent repr-
sente les interactions des objets suivants du domaine : client, compte courant et interface
utilisateur. Vous pouvez donc documenter cette interaction avec un diagramme dinteraction
(galement appel diagramme de collaboration) comme celui de la Figure 11.11.
Figure 11.10
Le strotype
<<utilise>>.
Figure 11.11
Diagramme
dinteraction UML.
Retire de l'argent
Client
Se connecte
<<utilise>>
Client
Interface utilisateur
(guichet automatique)
Compte
courant
1: Demande de retrait
2: Affichage options
3: Indication montant et compte
4: Vrification solde et tat
5: Renvoi autorisation
7: Distribution du liquide
8: Demande de reu
9: Impression du reu
300
C++Livre Page 337 J eudi, 7. mai 2009 9:45 09
338 Le langage C++
Le diagramme dinteraction de la Figure 11.11 capture les dtails du scnario qui pour-
raient ne pas apparatre clairement la lecture du texte. Les objets qui interagissent sont
des objets du domaine, et lensemble "guichet automatique/Interface utilisateur" est trait
comme un unique objet, avec seulement le compte bancaire demand en dtail.
Cet exemple de guichet assez simple ne montre quun ensemble dinteractions, mais
dtailler leurs spcicits est un outil puissant, permettant de comprendre la fois le
problme du domaine et les besoins du nouveau systme.
Crer des paquetages
Les cas dutilisation peuvent tre regroups en packages avec UML.
Un paquetage ressemble un rpertoire ou un dossier : cest une collection dobjets de
modlisation (classes, acteurs, etc.). Pour grer la complexit des cas dutilisation, vous
pouvez les regrouper en paquetages en fonction de critres correspondant votre
problme. Les cas dutilisation peuvent ainsi tre agrgs par type de compte (compte
courant ou compte pargne), par crdit ou dbit, par type de client ou toute autre caract-
ristique qui vous semble judicieuse. En outre, un mme cas dutilisation peut apparatre
dans des paquetages diffrents, ce qui offre une grande souplesse.
Analyse de lapplication
Outre les cas dutilisation, la spcication de besoins doit capturer les suppositions du
client et toutes les contraintes ou besoins concernant le matriel, les systmes dexploita-
tion, la scurit, les performances, etc. Ces besoins sont les prrequis particuliers du client
vous pourriez les dcouvrir au moment de la conception et de limplmentation, mais
votre client a dcid pour vous.
Les besoins de lapplication (quelquefois appels "besoins techniques") sont souvent
dicts par la ncessit de communiquer avec des systmes existants. La comprhension de
ce que font les systmes existants et de la faon dont ils le font est donc une composante
essentielle de lanalyse.
Dans lidal, vous analysez le problme, concevez la solution, puis choisissez les plates-
formes et systmes dexploitation qui sont les mieux adapts au besoin. Cependant, en
pratique, cest le client qui dcide en fonction de son systme existant et cest vous de
vous adapter.
Analyse des systmes
Certains logiciels sont autonomes et ninteragissent quavec le client nal. Le plus
souvent, vous devrez raliser une interface avec un systme existant. Lanalyse du systme
est le processus consistant recueillir tous les dtails concernant le systme avec lequel
C++Livre Page 338 J eudi, 7. mai 2009 9:45 09
Chapitre 11 Analyse et conception oriente objet 339
vous allez interagir. Votre systme sera-t-il un serveur fournissant des services au systme
existant ou sera-t-il un client ? Devrez-vous prvoir une interface nouvelle entre les syst-
mes ou en adapter une existante ? Les autres systmes sont-ils stables ou devrez-vous
continuellement tenter datteindre une cible mouvante ?
Vous devez avoir la rponse toutes ces questions et bien dautres lors de la phase
danalyse, avant de dmarrer votre conception. En outre, vous devez essayer de capturer
les contraintes et les limites implicites des interactions avec les autres systmes. Ralenti-
ront-ils la ractivit du vtre ? Imposeront-ils une charge importante au niveau ressources
et temps de traitement ?
Documents de planning
Une fois que vous savez ce que doit faire votre systme et comment il doit se comporter, il
est temps dtablir une prvision en ce qui concerne le budget et les dlais de ralisation.
Le plus souvent, cest le client qui dnit la date butoir : "Vous avez 18 mois." Idalement,
vous valuez le temps et le budget ncessaires limplmentation de votre solution, mais,
en pratique, vous devez le plus souvent vous arranger pour raliser le travail dans les dlais
et avec le budget impartis.
Partez du principe que :
Si on vous octroie une fourchette, la limite suprieure est probablement optimiste.
Toute ralisation prendra plus de temps que prvu.
Vous devez donc imprativement tablir des priorits an de commencer par ce qui est le
plus important. Nesprez pas avoir le temps de nir, cest aussi simple que a. Lorsque
vous aurez atteint la limite du temps imparti, il faut que ce que vous avez fait fonctionne et
puisse reprsenter une premire version satisfaisante. Si vous construisez un pont et que le
temps vous manque, vous devez pouvoir y faire circuler au moins une bicyclette. Cest
toujours mieux que davoir un beau pont bien gnol, qui sarrte la moiti du chemin.
Un autre point essentiel concernant les prvisions est quelles sont gnralement fausses.
ce stade du processus, il est pratiquement impossible de faire une estimation able de la
dure du projet. Lorsque vous disposerez des spcications des besoins, vous pourrez
avoir une ide plus prcise et tablir une estimation en ajoutant au moins 20 25 % de
marge de scurit que vous pourrez rduire mesure que vous progresserez et que vous
en saurez plus.
Visualisation
La dernire pice de la spcication des besoins est la visualisation, qui est un joli nom
pour dsigner les diagrammes, les images, les captures dcrans, les prototypes et toutes
C++Livre Page 339 J eudi, 7. mai 2009 9:45 09
340 Le langage C++
les autres reprsentations visuelles qui vous permettent dimaginer et de concevoir linter-
face graphique de votre produit.
Pour les trs gros projets, vous pouvez dvelopper un prototype complet pour vous aider
(ainsi que vos clients) comprendre comment se comportera le systme. Dans certaines
quipes, ce prototype devient mme la spcication des besoins ; le "vrai" systme est
alors conu pour implmenter les fonctionnalits prsentes dans le prototype.
Artefacts
la n de chaque phase danalyse et de conception, vous allez crer une srie de docu-
ments ou (souvent appels "artefacts"). Le Tableau 11.1 dcrit certains artefacts de la
phase danalyse. Ces documents sont utiliss par plusieurs groupes. Le client sen servira
pour vrier que vous comprenez ses besoins, lutilisateur nal pour vous fournir un
retour dinformations et des conseils, lquipe projet pour concevoir et implmenter le
code. La plupart de ces documents fournit galement des informations cruciales pour la
documentation et indique au service qualit comment devrait se comporter le systme.
Tableau 11.1 : Documents crs pendant la phase danalyse du projet
Document Description
Rapport de cas dutilisation Rapport dtaillant les cas dutilisation, scnarios, strotypes,
prconditions, postconditions et documents visuels
Analyse du domaine Document et diagrammes dcrivant les relations entre les objets
du domaine
Diagrammes danalyse de collaboration Diagrammes de collaboration dcrivant les interactions entre les
objets du domaine
Diagrammes danalyse de lactivit Diagrammes dactivit dcrivant les interactions entre les objets
du domaine
Analyse des systmes Rapport et diagrammes dcrivant les systmes matriels et de bas
niveau sur lesquels sera construit le projet
Analyse de lapplication Rapport et diagrammes dcrivant les besoins spciques du client
pour ce projet particulier
Rapport des contraintes oprationnelles Rapport dcrivant les caractristiques et contraintes de perfor-
mance imposes par ce client
Budget et planning Rapport avec diagrammes et graphiques donnant les prvisions en
matire de planning, de repres et de cots
C++Livre Page 340 J eudi, 7. mai 2009 9:45 09
Chapitre 11 Analyse et conception oriente objet 341
tape 3 La conception
Lanalyse met laccent sur le domaine du problme, alors que ltape suivante, celle de la
conception, se concentre sur la cration de la solution, cest--dire la transformation de
notre comprhension des besoins en un modle pouvant tre implment par un
programme. Le rsultat de ce processus est la production dun document de conception.
Ce document peut tre divis en deux sections : les mcanismes darchitecture et la
conception des classes. Cette dernire est son tour divise en conception statique (dtail
des classes, de leurs relations et de leurs caractristiques) et conception dynamique (dtail
des interactions entre classes).
La section mcanismes darchitecture fournit des dtails sur la faon dont vous implmen-
terez la persistance des objets, les conits daccs, un systme objet distribu, etc. Le reste
de ce chapitre se concentre sur laspect conception de classe du document de conception.
Limplmentation des diffrents mcanismes darchitecture sera prsente dans dautres
chapitres.
Quest-ce que les classes ?
Vous savez maintenant crer des classes. Une mthode de conception formelle exige de
sparer le concept de classe C++ de celui de classe de conception, bien quils soient
intimement lis. La classe C++ que vous crivez est la mise en uvre de la classe que
vous avez conue. Chaque classe de votre conception correspondra une classe dans
votre code, mais elles ne doivent pas tre confondues : il est possible dimplmenter
vos classes de conception dans un autre langage, auquel cas il faudra modier la
syntaxe des dnitions de classes.
Cela dit, la plupart du temps nous parlons de ces classes sans faire la distinction car les
diffrences sont purement abstraites. Si vous dites que, dans votre modle, votre classe
Chat aura une mthode Miauler(), cela signie que vous crirer aussi une mthode
Miauler() dans votre classe C++.
Vous capturez les classes du modle dans des diagrammes UML et vous capturez les clas-
ses C++ de limplmentation dans le code qui est compil. La distinction est subtile, mais
signicative.
La plus grande difcult, pour bien des dbutants, consiste dterminer lensemble initial
des classes et de comprendre ce qui fait une classe bien conue. Une technique trs simple
suggre dcrire les scnarios des cas dutilisation puis de crer une classe pour chaque
nom. Considrez, par exemple, le scnario suivant :
Le client choisit de retirer du liquide de son compte courant. Le compte est suf-
samment approvisionn, le guichet automatique est aliment en liquide et en reus et
C++Livre Page 341 J eudi, 7. mai 2009 9:45 09
342 Le langage C++
le rseau est oprationnel. Le guichet automatique demande au client dindiquer un
montant pour le retrait et le client demande 300 ce qui est autoris. La machine
distribue 300 et imprime un reu, le client prend largent et le reu.
Vous pouvez dduire de ce scnario les classes suivantes :
Client
Liquide
CompteCourant
Compte
Recus
GuichetAuto
Reseau
Montant
Retrait
Machine
Argent
On peut ensuite regrouper les synonymes pour ne conserver que la liste suivante, puis
crer des classes pour chacun de ces noms :
Client
Liquide (argent, montant, retrait)
CompteCourant
Compte
Recus
GuichetAuto (machine)
Reseau
Ce nest pas un mauvais dbut. Vous pouvez ensuite schmatiser les relations videntes
entre certaines de ces classes comme sur la Figure 11.12.
Transformations
Cette extraction des noms du scnario dans la prcdente section est surtout le dbut de la
transformation des objets provenant de lanalyse du domaine. Ce nest quune premire
tape. Bien souvent, nombre des objets du domaine auront des substituts dans la concep-
tion. Un objet est appel substitut pour distinguer le reu physique distribu par le guichet
de lobjet de la conception, qui nest quune abstraction implmente dans le programme.
C++Livre Page 342 J eudi, 7. mai 2009 9:45 09
Chapitre 11 Analyse et conception oriente objet 343
Vous dcouvrirez probablement que la plupart des objets du domaine ont une reprsenta-
tion dans la conception cest--dire quil existe une relation unun entre lobjet du
domaine et lobjet de conception. Dautres fois, un mme objet du domaine est reprsent
dans la conception par une srie dobjets de conception. Il peut mme arriver quun
ensemble dobjets du domaine soit reprsent par un seul objet de conception.
Vous remarquez, dans la Figure 11.12, que lon a dj captur le fait que CompteCourant
soit une spcialisation de Compte. De mme, partir de lanalyse du domaine, nous savons
que le GuichetAuto distribue la fois du liquide et des recus et nous lavons aussitt
captur dans la conception.
La relation entre le Client et CompteCourant est moins vidente. Nous savons quune
telle relation existe, mais les dtails ne sont pas clairs et il est donc prfrable de la laisser
de ct.
Autres transformations
Souvent, chaque acteur possde une classe. Aprs avoir transform les objets du domaine,
vous pouvez commencer rechercher dautres objets utiles au moment de la conception,
commencer par linterface entre votre nouveau systme et tous les systmes existants
cela devrait tre encapsul dans une classe de linterface. Prenez garde toutefois lorsque
vous traitez des bases de donnes et autres supports de stockage externes. Il est gnrale-
ment prfrable de donner la responsabilit chaque classe de grer sa propre "persis-
tance" (son stockage et sa rcupration entre les sessions utilisateurs). Ces classes de
conception peuvent bien entendu utiliser des classes communes pour accder aux chiers
Figure 11.12
Classes prliminaires.
CompteCheque
Compte
GuichetAuto
Liquide Recu
Client
Reseau
distribue
distribue
C++Livre Page 343 J eudi, 7. mai 2009 9:45 09
344 Le langage C++
ou aux bases de donnes mais, le plus souvent, ce sera le fournisseur du systme dexploi-
tation ou de la base de donnes qui vous les procurera.
Ces classes dinterface permettent dencapsuler les interactions de votre systme avec
lautre systme et protgent donc votre code des modications dans ce dernier. Elles vous
permettent galement de modier votre propre conception ou de satisfaire les change-
ments dans la conception des autres systmes, sans altrer le reste du code. Tant que les
deux systmes continuent de respecter linterface convenue, ils peuvent voluer indpen-
damment lun de lautre.
Manipulation des donnes
De la mme faon, vous devrez peut-tre crer des classes pour la manipulation des
donnes. Si vous devez transmettre des donnes dun format vers un autre (de degrs
Fahrenheit en Celsius par exemple), vous encapsulerez ces manipulations dans une classe
spciale. Cette technique peut servir chaque fois que vous devez transformer des
donnes dans les formats exigs par les autres systmes ou pour les transmettre sur Inter-
net en rsum, chaque fois que vous devez manipuler des donnes dans un format
prcis, il faut encapsuler le protocole dans une classe de manipulation.
Vues et rapports
Chaque "vue" ou "tat" produit par votre systme (ou, si vous produisez de nombreux
tats, chaque ensemble dtats) est un candidat pour une classe. Les rgles conditionnant
la production de ltat comment les informations sont rassembles et afches peuvent
tre encapsules dans une classe Vue.
Priphriques
Si votre systme utilise des priphriques (imprimantes, appareils photo, modems, scan-
ners, etc.), les caractristiques de leurs protocoles doivent tre encapsules dans une classe.
Ici encore, en crant des classes pour linterface vers le priphrique, vous pouvez en bran-
cher de nouveaux avec de nouveaux protocoles sans toucher au reste du code ; il suft de
crer une nouvelle classe dinterface avec la mme interface (ou drivant de celle-ci).
Construction du modle statique
Votre ensemble prliminaire de classes tant tabli, vous pouvez commencer modliser
leurs relations et interactions. Nous dcrivons ici dabord le modle statique, puis le
modle dynamique, mais, dans le processus rel de conception, vous passerez de lun
lautre pour en complter les dtails en ajoutant de nouvelles classes et en les bauchant
au fur et mesure de votre progression.
C++Livre Page 344 J eudi, 7. mai 2009 9:45 09
Chapitre 11 Analyse et conception oriente objet 345
Les trois points essentiels du modle statique sont : les responsabilits, les attributs et les
relations. Le principal celui, quil faut considrer en premier lieu est lensemble des
responsabilits de chaque classe. Le principe essentiel est : chaque classe doit tre respon-
sable dune seule chose.
Ceci ne signie pas quune classe ne possde quune mthode, loin de l. De nombreuses
classes en ont des douzaines, mais toutes ces mthodes doivent tre cohrentes et cohsi-
ves ; elles doivent tre mutuellement apparentes et contribuer la capacit de la classe
exercer un unique domaine de responsabilit.
Chaque objet doit tre une instance dune classe bien dnie charge dune responsabilit
particulire. Les classes dlguent gnralement les responsabilits annexes dautres
classes connexes, ce qui facilite la maintenance dun programme.
Pour baucher les responsabilits de vos classes, vous pouvez runir un groupe de trois
six personnes, dont un informaticien ayant une exprience de lanalyse et de la conception
oriente objet, un ou deux experts du domaine, et un "meneur" qui gardera un il sur
lobjectif et recentrera la discussion.
Le but est de dnir les classes laide de ches, didentier leurs responsabilits et de
comprendre leurs interactions.
Prvoyez une srie de ches denviron 10 15 cm, sur lesquelles vous placerez le nom
dune classe en titre, puis sparez la che en deux colonnes : Responsabilits et Collabora-
tion. Commencez par complter les ches pour les classes les plus importantes que vous
avez identies, crivez au dos une simple phrase pour les dnir. Si vous pouvez gale-
ment savoir quelle est la classe que cette classe spcialise, crivez simplement "Super-
classe:" sous le nom de la classe, suivi du nom de la classe dont elle drive.
Ne vous attardez pas sur les attributs des classes, ne capturez que ceux qui sont vidents et
essentiels. Concentrez-vous sur leurs responsabilits, en mentionnant simplement dans la
colonne Collaboration si la classe doit dlguer une autre.
Si la place vient manquer sur une che, demandez-vous si vous ne dlguez pas trop
cette classe. Les mthodes dune classe doivent tre cohrentes et cohsives et contribuer
lachvement de la responsabilit globale de la classe.
ce stade, ne vous proccupez pas des relations ni de linterface, pas plus que de savoir si
telle ou telle mthode sera publique ou prive.
Une fois cette srie de ches tablie, distribuez-les arbitrairement dans le groupe et reprenez
votre scnario prcdent :
Le client choisit de retirer du liquide de son compte courant. Le compte est suf-
samment approvisionn, le guichet automatique est aliment en liquide et en reus,
et le rseau est oprationnel. Le guichet automatique demande au client dindiquer un
C++Livre Page 345 J eudi, 7. mai 2009 9:45 09
346 Le langage C++
montant pour le retrait, et le client demande 300 ce qui est autoris. La machine
distribue 300 et imprime un reu, le client prend largent et le reu.
La suite ressemble un jeu de rles o chaque participant se met dans la peau du personnage
qui donnerait vie chaque classe. Par exemple, le participant possdant la che CompteCou-
rant va dire : "Je dis au client la somme qui est disponible sur le compte. Il me demande
300 , jenvoie un message la machine pour lui demander de le faire." Le titulaire de la che
GuichetAuto prend alors la parole : "Je suis le distributeur, je donne 300 et jenvoie
CompteCourant un message pour lui dire de dbiter le compte de 300 . La machine dispose
de 300 de moins. qui dois-je le dire ? Est-ce que je dois en garder la trace ?, etc.
Cette premire approche permet de clarier les responsabilits de chaque classe et leurs
interactions.
Cette mthode a toutefois ses limites. Elle nest tout dabord par utilisable grande
chelle ; pour un projet trs complexe, vous serez vite dpass. Par ailleurs, ce systme de
ches ne permet pas dapprhender les relations interclasses. Bien que les collaborations
soient captures, leur nature nest pas bien modlise. Il est difcile de passer des ches au
code et, plus important encore, les ches sont statiques.
Pour rsumer, les ches sont un bon dbut, mais vous devrez voluer vers la notation UML
pour pouvoir modliser un modle complet et able de votre conception. Cette transition vers
UML nest pas trs difcile, mais elle est irrversible. Vous devez dnitivement renoncer aux
ches, car il est tout simplement impossible de conserver les deux modles synchroniss.
Chaque che peut tre traduite directement en une classe UML, les responsabilits en
mthodes de la classe, et les attributs capturs ajouts. La dnition de classe, inscrite au
dos de la che, devient la documentation de la classe. La Figure 11.13 montre la relation
entre la che CompteCourant et la classe UML correspondante.
Classe : CompteCourant
SuperClasse : Compte
Responsabilits : Garder la trace du solde
Accepter les dpts et virements crditer
crire les chques
Transfrer le liquide demand
Mettre jour le solde du jour des retraits de la machine
Collaborations : Autres comptes
Systmes back-ofce
Distributeur de liquide
C++Livre Page 346 J eudi, 7. mai 2009 9:45 09
Chapitre 11 Analyse et conception oriente objet 347
Relations entre les classes
Lorsque les classes sont transcrites en notation UML, vous pouvez commencer vous
proccuper des relations entre les diffrentes classes. Les relations principales modliser
sont les suivantes :
gnralisation ;
association ;
agrgation ;
composition.
La relation de gnralisation est implmente en C++ par le biais de lhritage public. Du
point de vue conception, cest plus la smantique qui nous importe (ce quimplique cette
relation) que son mcanisme.
Nous avons vu la relation de gnralisation dans la phase danalyse, mais nous nous
concentrons prsent sur les objets dans notre conception plutt que simplement sur les
objets dans le domaine. Nous devons "factoriser" les fonctionnalits communes des classes
connexes dans les classes de base pour encapsuler les responsabilits partages.
Cette "factorisation" consiste dplacer la fonctionnalit des classes spcialises vers la
classe plus gnrale. Si vous remarquez que vos comptes courant et pargne ont tous les
deux besoin de mthodes pour transfrer de largent dans et dehors du compte, vous allez
dplacer la mthode TransfrerFonds() dans la classe de base Compte. Cette pratique
permet daccentuer le caractre polymorphe de votre conception.
Une fonctionnalit de C++, non disponible dans Java, est le concept dhritage multiple
(Java dispose dune fonctionnalit similaire, bien que limite, les interfaces multiples).
Figure 11.13
Fiche CompteCourant.
<<Abstrait>>
Compte
CompteCourant
Solde : int
RetraitsGuichetAutoJour
LireSolde() : int
Deposer(int montant)() : void
VirementCredit(int montant)() : bool
VirementDebit() : int
EcrireCheques(int montant)() : bool
C++Livre Page 347 J eudi, 7. mai 2009 9:45 09
348 Le langage C++
Lhritage multiple permet une classe dhriter de membres et mthodes de plusieurs
classes de base.
Lhritage multiple doit tre utilis judicieusement, car il peut compliquer la fois la
conception et limplmentation. Pour cette raison, de nombreux problmes autrefois rso-
lus par lhritage multiple le sont aujourdhui par lagrgation. Ceci dit, lhritage multiple
est un outil puissant si votre conception ncessite quune unique classe spcialise le
comportement de deux ou plusieurs autres classes.
Hritage multiple ou composition
Un objet est-il la somme de ses parties ? Est-il logique de modliser un objet Auto en tant
que spcialisation de Volant, Porte, et Pneu, comme sur la Figure 11.14 ?
Petit rappel : lhritage public doit toujours modliser la gnralisation. On dit que lhri-
tage doit modliser une relation est-un. Pour modliser une relation a-un par exemple,
une auto a-un volant il faut utiliser lagrgation (voir Figure 11.15).
Figure 11.14
Faux hritage.
Figure 11.15
Agrgation.
Auto
Volant Pneu Porte
Auto
Volant Pneu Porte
1
2..5
4
C++Livre Page 348 J eudi, 7. mai 2009 9:45 09
Chapitre 11 Analyse et conception oriente objet 349
Le diagramme de la Figure 11.15 indique quune auto a-un volant, 4 roues et 2 5 portes,
ce qui est un meilleur modle de la relation entre une auto et ses lments. Remarquez que
le losange est vid puisquil sagit dune agrgation et non dune composition. Cette
dernire implique le contrle de la dure de vie de lobjet. Bien quune auto ait des pneus
et une porte, ceux-ci existent avant de faire partie de lauto et existent encore mme sils
nen font plus partie.
La Figure 11.16 modlise la composition. Ce modle indique que le corps nest pas seule-
ment une agrgation dune tte, de deux bras et deux jambes, mais que ces objets sont
crs en mme temps que le corps et disparaissent en mme temps que lui. Leur existence
dpend de celle du corps et leur dure de vie est inextricablement lie.
Discriminateurs
Comment procder pour dnir les classes retant les diffrents modles dun fabricant
dautomobiles ? Supposons que vous ayez dans la Socit UniversAuto les modles
suivants : la Pluton (petite avec un petit moteur), la Vnus (berline 4 portes avec un moteur
moyen), la Mars (coup sport avec le moteur le plus puissant), la Jupiter (mini-fourgon
avec le mme moteur que le coup, mais conu pour avoir plus de couple an de grer le
poids plus lev de ce modle), et la Globe (un 44).
Vous pouvez commencer par crer les sous-types retant les diffrents modles, puis
crer des instances de chaque la sortie des chanes de montage (voir Figure 11.17).
Quest-ce qui diffrencie ces modles ? La taille du moteur, le type de carrosserie et les
performances. Ces caractristiques peuvent tre combines pour crer les diffrents modles.
En UML cela se traduit par le strotype de discrimination (voir Figure 11.18).
Le diagramme de la Figure 11.18 indique que les classes peuvent tre drives de Auto en
combinant trois attributs. Vous pouvez avoir un 44 puissant et sport, une berline familiale
peu puissante, etc.
Figure 11.16
Composition.
Corps
Tte Jambes Bras
1
2
2
C++Livre Page 349 J eudi, 7. mai 2009 9:45 09
350 Le langage C++
Chaque attribut peut tre implment laide dune simple numration dans le code. Par
exemple :
enum TypeCarrosserie = { berline, coupe, miniFourgon, quatre4 };
Une seule valeur peut savrer insufsante pour modliser un discriminateur particulier :
les performances, par exemple, forment une caractristique assez complexe. Le discrimi-
nateur peut alors tre modlis comme une classe et la discrimination encapsule dans une
instance de ce type.
Les caractristiques de performance seront donc modlises dans un type performance,
qui contiendra toutes les informations techniques du moteur. Le strotype UML pour une
classe qui encapsule un discriminateur et qui peut tre utilis pour crer des instances
dune classe (Auto) de diffrents types est <<typepuissance>>. Ici la classe Perfor-
mance est un typepuissance pour Auto. Vous instanciez un objet Performance en mme
temps quun Auto, et vous associez un objet Performance donn un objet Auto donn,
comme sur la Figure 11.19.
Les typepuissance permettent de crer des types logiques sans utiliser lhritage et de
grer un ensemble important et complexe de types.
Figure 11.17
Modlisation
des sous-types.
Figure 11.18
Modlisation
du discriminateur.
Auto
Globe Jupiter Mars Venus Pluton
Auto
puissant berline coup Familiale
peu puissant Sport
moteur performance
carrosserie
C++Livre Page 350 J eudi, 7. mai 2009 9:45 09
Chapitre 11 Analyse et conception oriente objet 351
En C++, ils sont gnralement implments laide de pointeurs. Ici, la classe Auto
contient un pointeur sur une instance de la classe CaracteristiquesPerformance (voir
Figure 11.20).
La cration de nouveaux types au moment de lexcution avec cette mthode
peut rduire les avantages du typage fort en C++, dans lequel le compilateur
peut faire appliquer la justesse des relations interclasses. utiliser avec
prcaution.
Class Car: public Vehicule
{
public:
Auto();
~Auto();
// autres mthodes publiques
private:
CaracteristiquesPerformance * pPerformance;
};
Figure 11.19
Un discriminateur en
tant que typepuissance.
Figure 11.20
La relation entre
un objet Auto et son
typepuissance.
Auto
puissant berline coup Familiale
peu puissant Sport
moteur performance:CaracteristiquesPerformance
carrosserie
<<typepuissance>>
Caractristiques de performances
palier vitesses
tours/mn
Acclrer
Familiale:CaracteristiquesPerformance
Sport:CaracteristiquesPerformance
Auto
puissant berline coup
peu puissant
moteur
carrosserie
Caractristiques de performances
palier vitesses
tours/mn
Acclrer
A
tte
n
tio
n
C++Livre Page 351 J eudi, 7. mai 2009 9:45 09
352 Le langage C++
Enn, les typepuissance permettent de crer de nouveaux types (pas seulement des
instances) au moment de lexcution. Chaque type logique tant diffrenci seulement par
les attributs du typepuissance associ, ils peuvent tre les paramtres du constructeur de
typepuissance et vous pouvez crer de nouveaux types dauto la vole au moment de
lexcution. En passant des tailles de moteur ou des paliers de vitesse diffrents au type-
puissance, vous pouvez crer de nouvelles caractristiques de performance. En affectant
ces caractristiques aux diffrentes autos, vous pouvez agrandir le jeu des types de voitures au
moment de lexcution.
Modle dynamique
Outre les relations entre classes, il est important de modliser leurs interactions. Par exem-
ple, les classes CompteCourant, GuichetAuto et Recu peuvent interagir avec le Client
par lintermdiaire du cas dutilisation "Retirer liquide" (voir Figure 11.21).
Ce diagramme montre linteraction entre plusieurs classes. La classe GuichetAuto va
dlguer la classe CompteCourant toute la responsabilit de gestion du solde, alors que
CompteCourant va dlguer GuichetAuto la gestion de lafchage lutilisateur.
Le diagramme de la Figure 11.21 est un diagramme de squence. Il existe aussi des
diagrammes de collaboration qui insistent sur les interactions intemporelles entre les classes
Figure 11.21
Diagramme de squence.
Client GuichetAuto CompteCourant
1: Vrifier soldes
2: Lire solde
3: Afficher solde
6: Imprimer
4 : Retirer liquide
5 : Distribuer
Recu
C++Livre Page 352 J eudi, 7. mai 2009 9:45 09
Chapitre 11 Analyse et conception oriente objet 353
et que vous pouvez directement produire partir du prcdent, notamment avec Rational
Rose (voir Figure 11.22).
Diagrammes de transition dtat
Chaque objet a plusieurs tats possibles, dont les transitions peuvent tre modlises dans
un diagramme dtat ou diagramme de transition dtat. La Figure 11.23 montre les diff-
rents tats de la classe CompteClient lors de la connexion du client au systme.
Chaque diagramme dtat commence par un simple tat de dpart et se termine avec zro
ou plusieurs tats de fin. Les tats individuels sont nomms et les transitions peuvent tre
tiquetes. Le garde indique une condition qui doit tre satisfaite pour quun objet passe
dun tat un autre.
Figure 11.22
Diagramme
de collaboration.
Figure 11.23
tats du compte client.
Client
GuichetAuto
CompteCourant
1: Vrifier soldes
2: Lire solde
3: Afficher solde
6: Imprimer
4 : Retirer liquide
5 : Distribuer
Recu
Lecture infos
du compte
Saisie mot de passe
Non connect
Connect
tat transitoire
tat
Garde
Fin
[ID compte valide]
Dbut
C++Livre Page 353 J eudi, 7. mai 2009 9:45 09
354 Le langage C++
Super tats
Le client peut changer davis tout moment et dcider de ne pas sauthentier. Le systme
doit toujours accepter cette annulation et revenir ltat "non authenti" (voir Figure 11.24).
Le diagramme devient un peu plus compliqu, mais il peut tre simpli laide dun
super tat (voir Figure 11.25).
Figure 11.24
Lutilisateur peut annuler.
Figure 11.25
Super tat.
Lecture infos du compte
Saisie mot de passe
Non connect
Connect
Dbut
Annul
Annul
Fin
Non connect
Connect
Dbut
Saisie mot de passe
Lecture infos
du compte
Annul
Annulable
Fin
C++Livre Page 354 J eudi, 7. mai 2009 9:45 09
Chapitre 11 Analyse et conception oriente objet 355
Le diagramme de la Figure 11.25 fournit les mmes informations que celui de la
Figure 11.24, mais il est plus clair et plus facile lire. Entre le moment de la demande
dauthentication jusqu la n de cette opration, le processus peut tre annul, vous
revenez alors ltat "non authenti".
tapes 4 6 Implmentation, tests et dploiement
Les trois dernires tapes sont importantes, mais ne seront pas traites ici. Si vous utilisez
C++, limplmentation sera dtaille dans le reste de cet ouvrage. Les tests et le dploie-
ment sont deux disciplines complexes, qui nentrent pas dans le cadre de ce livre.
Noubliez pas tout de mme de tester soigneusement vos classes la fois sparment et
ensemble pour vrier votre implmentation et votre conception.
Itrations
Dans le cadre de Rational Unied Process, les activits que nous venons dnumrer sont
appeles workows ("ux de travail") ; elles varient dans les phases de mise en route,
dlaboration, de construction et de transition. La modlisation dactivit est son apoge
lors de la mise en route, mais elle peut toujours survenir pendant la construction. Limpl-
mentation, quant elle, est forte pendant la construction, mais elle peut survenir au
moment de la cration des prototypes pour la phase dlaboration.
Dans chaque phase, la construction par exemple, il peut y avoir plusieurs itrations. Dans
la premire itration de construction, il est ainsi possible de dvelopper les fonctions cls
du systme. Dans la deuxime, on les approfondit et on en ajoute dautres. Lapprofondis-
sement et lajout sont accentus dans la troisime, jusqu parvenir une itration produisant
le systme complet.
Questions-rponses
Q Je nai appris aucun lment de programmation C++ dans ce chapitre. Quel est
son objectif ?
R Pour rdiger des programmes C++ efcaces, vous devez savoir les structurer. En planiant
votre projet avant de commencer le codage, vous construirez de meilleurs programmes.
Q En quoi lanalyse et la conception orientes objet diffrent-elles fondamentalement
des autres approches ?
R Avec les autres techniques, les analystes et programmeurs tendaient penser aux
programmes comme des groupes de fonctions agissant sur des donnes. Dans la
C++Livre Page 355 J eudi, 7. mai 2009 9:45 09
356 Le langage C++
programmation oriente objet, les donnes et fonctionnalits sont des units ayant la
fois la connaissance (les donnes) et les capacits (les fonctions). Les programmes
procduraux mettent laccent sur les fonctions et sur la faon dont elles agissent sur les
donnes. On dit souvent que les programmes crits en Pascal ou en C sont des collections
de procdures alors que ceux crits en C++ sont des collections de classes.
Q La programmation oriente objet est-elle la solution miracle tous les problmes ?
R Non, mais en ce qui concerne les problmes importants et complexes, lanalyse et la
conception orientes objet fournissent des outils apprciables et ingals.
Testez vos connaissances
1. Quelle est la diffrence entre la programmation oriente objet et la programmation
procdurale ?
2. Quelles sont les phases de lanalyse et de la conception orientes objet ?
3. Quest-ce que lencapsulation ?
4. Au regard de lanalyse, quest-ce quun domaine ?
5. Au regard de lanalyse, quest-ce quun acteur ?
6. Quest-ce quun cas dutilisation ?
7. Laquelle des afrmations suivantes est vraie ?
a.Un chat est une forme danimal spcialise.
b.Un animal est une forme spcialise de chat et de chien.
Exercices
1. Un systme informatique est constitu de plusieurs lments : clavier, souris, cran,
unit centrale. Dessinez un diagramme de composition pour illustrer la relation entre
lordinateur et ses lments. Astuce : il sagit dune agrgation.
2. Supposons que vous deviez simuler lintersection de deux rues avec des feux tricolo-
res et des passages pour pitons. Lobjectif est de dterminer si le chronomtrage des
feux permettra un ux normal de la circulation.
Quelles sortes dobjets faut-il modliser ? Quelles seraient les classes ?
C++Livre Page 356 J eudi, 7. mai 2009 9:45 09
12
Hritage
Au sommaire de ce chapitre
Nature de lhritage
Comment utiliser lhritage pour driver dune classe partir dune autre
Rle et utilisation des accs protgs
Nature et rle des fonctions virtuelles
Vous avez vu, au chapitre prcdent, un certain nombre de concepts lis la programma-
tion oriente objet qui incluent les principes de spcialisation/gnralisation implments
en C++ par le biais de lhritage.
Quest-ce que lhritage ?
Lorsque vous regardez un chien, que voyez-vous ? Une tte sur quatre pattes. Un biolo-
giste verra un ensemble dorganes, un physicien considrera quil sagit datomes exerant
des forces, et un zoologiste le classera dans lespce canine domesticus.
C++Livre Page 357 J eudi, 7. mai 2009 9:45 09
358 Le langage C++
Nous nous intresserons ici cette dernire faon de voir. Un chien fait partie de lespce
canine, un canin est un mammifre, etc. Les zoologistes classent les tres vivants en
Rgne, Ligne, Classe, Ordre, Famille, Genre et Espce.
Cette hirarchie de spcialisation/gnralisation tablit une relation est-un. Un Homo
Sapiens (un homme) est une sorte de primate et cette relation apparat partout : un mono-
space est une sorte de voiture, qui est elle-mme une sorte de vhicule. Une glace est une
sorte de dessert, qui est lui-mme une sorte daliment.
Lorsque lon peut dire que quelque chose est une sorte dautre chose, cela signie quil
est une spcialisation de cette autre chose. Une voiture, par exemple, est un type spcial
de vhicule.
Hritage et drivation
Un chien hrite de cest--dire reoit automatiquement toutes les caractristiques
dun mammifre. tant un mammifre, vous savez donc quil se dplace et quil
respire laide de poumons car tous les mammifres, par dnition, le font. Le
concept de chien ajoute lide quil aboie, quil remue la queue, quil mange mes
chaussons, etc.
Vous pouvez diviser les chiens en chien de travail, en chien de garde et en chien de chasse,
et vous pouvez encore subdiviser les chiens de chasse en pagneuls, en retrievers, etc.
Finalement, chacun dentre eux peut encore tre spcialis ; les retrievers, par exemple,
peuvent tre diviss en labradors et en goldens.
Un labrador est une sorte de retriever, qui est un chien de chasse, qui est un chien et donc
une sorte de mammifre, qui est une sorte danimal et, par consquent, un tre vivant.
Cette hirarchie est reprsente par la Figure 12.1.
C++ essaie de reprsenter ce type de relations en dnissant des classes qui drivent les
unes des autres. La drivation est un moyen dexprimer une relation est-un. Par exemple,
la classe Chien drive de la classe Mammifere. Ainsi, vous navez pas besoin de dnir
explicitement les mouvements des chiens, puisquils hritent cette caractristique des
mammifres.
Une classe qui ajoute une fonctionnalit une classe existante drive de cette classe
dorigine qui est donc la classe de base de la nouvelle classe.
Si la classe Chien drive de la classe Mammifere, Mammifere est la classe de base de
Chien. Les classes drives sont des surensembles de leurs classes de base. De la mme
faon quun chien est un mammifre spcialis, la classe Chien inclut des mthodes et des
donnes supplmentaires par rapport la classe Mammifere.
C++Livre Page 358 J eudi, 7. mai 2009 9:45 09
Chapitre 12 Hritage 359
En gnral, une classe de base est associe plusieurs classes drives. Les classes Chien,
Chat et Cheval drivent toutes de la classe Mammifere.
Le rgne animal
Pour clarier les concepts de drivation et dhritage, les diffrentes classes prsentes
dans ce chapitre portent des noms danimaux. Supposez que vous deviez concevoir un jeu
de simulation dune exploitation agricole.
Vous allez dvelopper un ensemble danimaux domestiques, parmi lesquelles gurent des
chevaux, des vaches, des cochons, des moutons, etc. Pour que les animaux ralisent des
actions, vous implmentez des mthodes pour ces classes mais, pour le moment, vous
allez vous crer de simples squelettes de fonctions qui se contenteront dafcher un simple
message.
crire des squelettes de fonctions signie que vous crirez le strict minimum pour montrer
quelles sont appeles et que vous laisserez les dtails pour plus tard, lorsque vous aurez
plus de temps. Vous tes videmment libre dtendre le code minimal de ce chapitre pour
permettre aux animaux de se comporter plus naturellement.
Figure 12.1
Hirarchie danimaux.
Animal
Mammifere Reptile
Cheval Chien
Retriever Epagneul
Labrador Golden
de garde d'agrment de chasse
C++Livre Page 359 J eudi, 7. mai 2009 9:45 09
360 Le langage C++
Les exemples qui utilisent des animaux sont simples, mais ces concepts sappliquent
dautres domaines. Si vous construisez un distributeur automatique, par exemple, vous
aurez un compte chques, qui est un type de compte bancaire, qui est un type de compte.
Syntaxe de la drivation
Pour dclarer une classe drive, vous devez indiquer le nom de la classe, suivi dun
caractre deux-points, du type de la drivation (public, ou autre) et de la classe de base.
Exemple :
class classeDerivee: typeAcces classeBase
Par exemple, pour crer une nouvelle classe appele Chien qui hrite dune classe Mammi-
fere existante :
class Chien: public Mammifere
Les diffrents types de drivations (typeAcces) sont dcrits dans la suite du chapitre. Pour
linstant, nous utiliserons le plus courant : public. La classe de base doit avoir t
dclare avant la classe drive ; sinon, le compilateur sera incapable de crer le chier
excutable. Dans le Listing 12.1, la classe Chien est drive de la classe Mammifere.
Listing 12.1 : Hritage simple
1: //Listing12.1 Hritage simple
2: #include <iostream>
3: using namespace std;
4:
5: enum RACE { GOLDEN, CAIRN, DANDIE, SHETLAND, DOBERMAN, LAB };
6:
7: class Mammifere
8: {
9: public:
10: // constructeurs
11: Mammifere();
12: ~Mammifere();
13:
14: // mthodes daccs
15: int GetAge() const;
16: void SetAge(int);
17: int GetPoids() const;
18: void SetPoids();
19:
20: // autres mthodes
21: void Crier() const;
22: void Dormir() const;
C++Livre Page 360 J eudi, 7. mai 2009 9:45 09
Chapitre 12 Hritage 361
23:
24:
25: protected:
26: int sonAge;
27: int sonPoids;
28: };
29:
30: class Chien: public Mammifere
31: {
32: public:
33:
34: // constructeurs
35: Chien();
36: ~Chien();
37:
38: // mthodes daccs
39: RACE GetRace() const;
40: void SetRace(RACE);
41:
42: // autres mthodes
43: RemuerQueue();
44: Quemander();
45:
46: protected:
47: RACE saRace;
48: };
Ce programme ne produit pas de rsultat visible car il ne contient quun ensemble de
dclarations de classes sans implmentation, mais cela nenlve rien son intrt.
La classe Mammifere est dclare de la ligne 6 la ligne 27. Dans cet exemple, elle nest
pas drive dune autre classe. Dans le monde rel, les mammifres driveraient dune
autre classe puisquen ralit un mammifre est une sorte danimal. En C++, vous ne
pouvez reprsenter quune partie des informations relatives un objet donn : la ralit est
trop complexe pour tre cerne dans tous ses dtails et sa globalit. Une hirarchie C++ est
donc toujours une reprsentation partielle des informations disponibles. Pour crer efca-
cement des classes drives en C++, le meilleur moyen est de regrouper le plus de caract-
ristiques communes aux objets de la vie courante, sans ajouter de complexit inutile.
Il fallait une origine la hirarchie : nous lavons trouve dans la classe Mammifere. Pour
cette raison, certaines variables membres qui devraient appartenir des classes de base
situes en amont sont reprsentes dans cette classe. Les animaux ont tous un poids et un
ge, par exemple : si Mammifere drivait de Animal, elle hriterait de ces attributs au lieu
de les dnir.
C++Livre Page 361 J eudi, 7. mai 2009 9:45 09
362 Le langage C++
Si lon devait plus tard ajouter un autre animal partageant certaines de ces fonctionnalits
(par exemple Insecte), les attributs concerns pourraient tre dirigs vers une nouvelle
classe Animal qui deviendrait la classe de base de Mammifere et Insecte. Cest ainsi
quvoluent les hirarchies de classes.
Pour faciliter la comprhension et la gestion des applications, seules six mthodes ont t
intgres la classe Mammifere : quatre mthodes publiques daccs ainsi que Crier(),
et Dormir().
Comme lindique la ligne 30, la classe Chien hrite de la classe Mammifere. Vous le savez
grce aux deux-points qui suivent le nom de la classe (Chien), suivis du nom de la classe
de base (Mammifere).
Chaque objet Chien a trois variables membres : sonAge, sonPoids et saRace. Vous remar-
querez que les variables sonAge et sonPoids ne gurent pas dans la dclaration de la
classe Chien ; elles sont hrites de la classe Mammifere, tout comme loprateur de copie,
les constructeurs et le destructeur.
Priv ou protg ?
Vous avez certainement dcouvert un nouveau mot-cl aux lignes 24 et 45 du
Listing 12.1 : il sagit de protected. Jusqu prsent, les donnes des classes taient
prives, mais les membres privs ne sont pas accessibles en dehors de la classe existante.
Cette condentialit empche galement laccs ces membres partir des classes dri-
ves. Les variables sonAge et sonPoids pourraient tre publiques, mais ce nest pas
souhaitable car vous ne voulez pas que dautres classes accdent directement ces
donnes.
Stroustrup (le crateur de C++), dans The Design and Evolution of C++,
nonce que toutes les donnes membres devraient tre dclares comme
prives, jamais protges. Les mthodes protges, cependant, ne posent gn-
ralement pas de problmes et peuvent tre trs utiles.
En ralit, vous souhaitez les rendre visibles uniquement cette classe et ses classes
drives et cest exactement ce fait le mot-cl protected.
Il existe trois identicateurs daccs : public, protected et private. Si un objet de votre
classe est utilis par une fonction, celui-ci permet daccder toutes les donnes et fonc-
tions membres publiques. leur tour, les fonctions membres peuvent manipuler toutes les
donnes et fonctions membres prives de leur classe et toutes les donnes et fonctions
membres protges de toutes les classes dont elles hritent. En consquence, la fonction
In
f
o
C++Livre Page 362 J eudi, 7. mai 2009 9:45 09
Chapitre 12 Hritage 363
Chien::RemuerQueue() a accs la donne prive saRace et aux donnes protges
sonAge et sonPoids de la classe Mammifere.
Mme si dautres classes sintercalaient entre Mammifere et Chien (AnimalDomestique,
par exemple), la classe Chien aurait quand mme accs aux membres protgs de la classe
Mammifere condition que ces autres classes utilisent toutes un hritage public. Pour en
savoir plus sur lhritage priv, reportez-vous au Chapitre 16.
Le Listing 12.2 cre des objets de type Chien, puis accde aux donnes et aux mthodes
membres de ces objets.
Listing 12.2 : Utilisation dun objet driv
1: //Listing12.2 Utilisation dun objet driv
2: #include <iostream>
3: using std::cout;
4: using std::endl;
5:
6: enum RACE { GOLDEN, CAIRN, DANDIE, SHETLAND, DOBERMAN, LAB };
7:
8: class Mammifere
9: {
10: public:
11: // constructeurs
12: Mammifere():sonAge(2), sonPoids(5){}
13: ~Mammifere(){}
14:
15: // mthodes daccs
16: int GetAge() const { return sonAge; }
17: void SetAge(int age) { sonAge = age; }
18: int GetPoids() const { return sonPoids; }
19: void GetPoids(int Poids) { sonPoids = Poids; }
20:
21: // autres mthodes
22: void Crier()const
22a: { cout <<"Le cri du mammifre!\n"; }
23: void Dormir()const { cout << "Chut. Je dors.\n"; }
24:
25: protected:
26: int sonAge;
27: int sonPoids;
28: };
29:
30: class Chien: public Mammifere
31: {
32: public:
33:
C++Livre Page 363 J eudi, 7. mai 2009 9:45 09
364 Le langage C++
34: // constructeurs
35: Chien():saRace(GOLDEN){}
36: ~Chien(){}
37:
38: // mthodes daccs
39: RACE GetRace() const { return saRace; }
40: void SetRace(RACE race) { saRace = race; }
41:
42: // autres mthodes
43: void RemuerQueue() const
43a: { cout << "Je remue la queue...\n"; }
44: void Quemander() const
44a: { cout << "Je mendie de la nourriture...\n"; }
45:
46: private:
47: RACE saRace;
48: };
49:
50: int main()
51: {
52: Chien Fido;
53: Fido.Crier();
54: Fido.RemuerQueue();
55: cout << "Fido a " << Fido.sonAge() << " ans" << endl;
56: return 0;
57: }
Ce programme produit le rsultat suivant :
Le cri du mammifre!
Je remue la queue...
Fido a 2 ans
La classe Mammifere est dclare de la ligne 8 la ligne 28 (pour raccourcir le listing, les
fonctions membres ont t dnies inline). La classe Chien est une classe drive de la
classe Mammifere (lignes 30 48), ce qui confre les mmes attributs tous les chiens : un
ge, un poids et une race. Comme on la indiqu plus haut, les deux premiers proviennent
de la classe de base, Mammifere.
La ligne 52 dclare un objet Chien nomm Fido. Il hrite de tous les attributs de la classe
Mammifere et de la classe Chien. Ainsi, Fido peut remuer la queue, crier et dormir,
puisquil dispose des fonctions RemuerQueue(), Crier() et Dormir(). Aux lignes 53 et
54, Fido appelle deux de ces mthodes de la classe de base Mammifere. La ligne 55
appelle la mthode daccs LireAge() de la classe de base.
C++Livre Page 364 J eudi, 7. mai 2009 9:45 09
Chapitre 12 Hritage 365
Lhritage avec les constructeurs et les destructeurs
Par hritage, les objets Chien sont galement des objets Mammifere. Lorsque Fido est
cr, son constructeur de base est donc appel pour produire un objet Mammifere. Puis, le
constructeur de Chien est appel son tour pour complter lobjet Fido. Comme on na
pas fourni de paramtre au constructeur de Chien, cest le constructeur par dfaut qui est
appel dans les deux cas. Fido ne peut exister tant quil na pas t totalement construit, ce
qui signie que sa partie et sa partie Chien doivent avoir t construites. Cest pour cette
raison que les deux constructeurs doivent tre appels.
Lorsque lobjet Fido est supprim, le destructeur de Chien est dabord appel, puis cest
le tour du destructeur de la partie Mammifere. Chacun deux permet de nettoyer les attri-
buts de sa propre partie de Fido. Le Listing 12.3 met en vidence les appels aux constructeurs
et aux destructeurs.
Listing 12.3 : Appel des constructeurs et des destructeurs
1: //Listing12.3 Appel des constructeurs et des destructeurs.
2: #include <iostream>
3: using namespace std;
4: enum RACE { GOLDEN, CAIRN, DANDIE, SHETLAND, DOBERMAN, LAB };
5:
6: class Mammifere
7: {
8: public:
9: // constructeurs
10: Mammifere();
11: ~Mammifere();
12:
13: // mthodes daccs
14: int GetAge() const { return sonAge; }
15: void SetAge(int age) { sonAge = age; }
16: int GetPoids() const { return sonPoids; }
17: void SetPoids(int Poids) { sonPoids = Poids; }
18:
19: // autres mthodes
20: void Crier() const { cout << "Le cri du mammifre!\n"; }
21: void Dormir() const { cout << "Chut. Je dors.\n"; }
22:
23: protected:
24: int sonAge;
25: int sonPoids;
26: };
27:
28: class Chien: public Mammifere
C++Livre Page 365 J eudi, 7. mai 2009 9:45 09
366 Le langage C++
29: {
30: public:
31:
32: // constructeurs
33: Chien();
34: ~Chien();
35:
36: // mthodes daccs
37: RACE GetRace() const { return saRace; }
38: void SetRace(RACE race) { saRace = race; }
39:
40: // autres mthodes
41: void RemuerQueue() const
41a { cout << "Je remue la queue...\n"; }
42: void Quemander() const
42a: { cout << "Je mendie de la nourriture...\n"; }
43:
44: private:
45: RACE saRace;
46: };
47:
48: Mammifere::Mammifere():
49: sonAge(3),
50: sonPoids(5)
51: {
52: std::cout << "Constructeur de Mammifere... " << endl;
53: }
54:
55: Mammifere::~Mammifere()
56: {
57: std::cout << "Destructeur de Mammifere... " << endl;
58: }
59:
60: Chien::Chien():
61: saRace(GOLDEN)
62: {
63: std::cout << "Constructeur de Chien... " << endl;
64: }
65:
66: Chien::~Chien()
67: {
68: std::cout << "Destructeur de Chien... " <<endl;
69: }
70: int main()
71: {
72: Chien Fido;
73: Fido.Crier();
C++Livre Page 366 J eudi, 7. mai 2009 9:45 09
Chapitre 12 Hritage 367
74: Fido.RemuerQueue();
75: std::cout << "Fido a " << Fido.GetAge() << " ans" << endl;
76: return 0;
77: }
Ce programme produit le rsultat suivant :
Constructeur de Mammifere...
Constructeur de Chien...
Le cri du mammifre!
Je remue la queue...
Fido a 3 ans
Destructeur de Chien...
Destructeur de Mammifere...
Ce listing ressemble beaucoup au Listing 12.2, mais les lignes 48 69 signalent dsormais
les appels aux constructeurs et destructeurs lcran. Le constructeur de Mammifere est
appel avant le constructeur de Chien. La classe Chien est alors compltement dnie et
les mthodes sont implmentes. Lorsque Fido devient hors de porte, le destructeur de
Chien, puis celui de Mammifere sont appels. Cela est conrm par le rsultat du listing.
Passer des paramtres aux constructeurs de base
Vous souhaiterez peut-tre initialiser certaines valeurs dans un constructeur de base. Vous
pourriez, par exemple, surcharger le constructeur de Mammifere et le constructeur de
Chien pour utiliser respectivement un ge et une race spciques. Comment faire pour
passer lge et le poids au bon constructeur de Mammifere ? Comment faire pour initialiser
le poids dun objet Chien si cette opration nest pas prvue dans la classe Mammifere ?
Linitialisation de la classe de base peut avoir lieu pendant celle de la classe drive, en
prcisant le nom de la classe de base, suivi des paramtres voulus. Le Listing 12.4 montre
comment faire.
Listing 12.4 : Surcharge de constructeurs dans des classes drives
1: // Listing12.4 Surcharge des constructeurs
1a: // dans des classes drives
2: #include <iostream>
3: using namespace std;
4:
5: enum RACE { GOLDEN, CAIRN, DANDIE, SHETLAND, DOBERMAN, LAB };
6:
7: class Mammifere
8: {
9: public:
C++Livre Page 367 J eudi, 7. mai 2009 9:45 09
368 Le langage C++
10: // constructeurs
11: Mammifere();
12: Mammifere(int age);
13: ~Mammifere();
14:
15: // mthodes daccs
16: int GetAge() const { return sonAge; }
17: void SetAge(int age) { sonAge = age; }
18: int GetPoids() const { return sonPoids; }
19: void SetPoids(int Poids) { sonPoids = Poids; }
20:
21: //autres mthodes
22: void Crier() const
22a: { cout << "Le cri du mammifre!\n"; }
23: void Dormir() const { cout << "Chut. Je dors.\n"; }
24:
25:
26: protected:
27: int sonAge;
28: int sonPoids;
29: };
30:
31: class Chien: public Mammifere
32: {
33: public:
34:
35: // constructeurs
36: Chien();
37: Chien(int age);
38: Chien(int age, int poids);
39: Chien(int age, RACE race);
40: Chien(int age, int poids, RACE race);
41: ~Chien();
42:
43: // mthodes daccs
44: RACE GetRace() const { return saRace; }
45: void SetRace(RACE race) { saRace = race; }
46:
47: // autres mthodes
48: void RemuerQueue() const
48a: { cout << "Je remue la queue...\n"; }
49: void Quemander() const
49a: { cout << "Je mendie de la nourriture...\n"; }
50:
51: private:
52: RACE saRace;
53: };
C++Livre Page 368 J eudi, 7. mai 2009 9:45 09
Chapitre 12 Hritage 369
54:
55: Mammifere::Mammifere():
56: sonAge(1),
57: sonPoids(5)
58: {
59: cout << "Constructeur de Mammifere..." << endl;
60: }
61:
62: Mammifere::Mammifere(int age):
63: sonAge(age),
64: sonPoids(5)
65: {
66: cout << "Constructeur de Mammifere(int)..." << endl;
67: }
68:
69: Mammifere::~Mammifere()
70: {
71: cout << "Destructeur de Mammifere..." << endl;
72: }
73:
74: Chien::Chien():
75: Mammifere(),
76: saRace(GOLDEN)
77: {
78: cout << "Constructeur de Chien..." << endl;
79: }
80:
81: Chien::Chien(int age):
82: Mammifere(age),
83: saRace(GOLDEN)
84: {
85: cout << "Constructeur de Chien(int)..." << endl;
86: }
87:
88: Chien::Chien(int age, int poids):
89: Mammifere(age),
90: saRace(GOLDEN)
91: {
92: sonPoids = poids;
93: cout << "Constructeur de Chien(int, int)..." << endl;
94: }
95:
96: Chien::Chien(int age, int poids, RACE race):
97: Mammifere(age),
98: saRace(race)
99: {
C++Livre Page 369 J eudi, 7. mai 2009 9:45 09
370 Le langage C++
100: sonPoids = Poids;
101: cout << "Constructeur de Chien(int, int, RACE)..." << endl;
102: }
103:
104: Chien::Chien(int age, RACE race):
105: Mammifere(age),
106: saRace(race)
107: {
108: cout << "Constructeur de Chien(int, RACE)..." << endl;
109: }
110:
111: Chien::~Chien()
112: {
113: cout << "Destructeur de Chien..." << endl;
114: }
115: int main()
116: {
117: Chien Fido;
118: Chien Rover(5);
119: Chien Buster(6, 8);
120: Chien Yorkie (3, GOLDEN);
121: Chien Dobbie (4, 20, DOBERMAN);
122: Fido.Crier();
123: Rover.RemuerQueue();
124: cout << "Yorkie a " << Yorkie.GetAge()
125: << " ans" << endl;
126: cout << "Dobbie pse ";
127: cout << Dobbie.GetPoids() << " kilos" << endl;
128: return 0;
129: }
Le rsultat obtenu a t numrot pour faciliter lanalyse du code.
Ce programme produit le rsultat suivant :
1: Constructeur de Mammifere...
2: Constructeur de Chien...
3: Constructeur de Mammifere(int)...
4: Constructeur de Chien(int)...
5: Constructeur de Mammifere(int)...
6: Constructeur de Chien(int, int)...
7: Constructeur de Mammifere(int)...
8: Constructeur de Chien(int, RACE)....
9: Constructeur de Mammifere(int)...
In
f
o
C++Livre Page 370 J eudi, 7. mai 2009 9:45 09
Chapitre 12 Hritage 371
10: Constructeur de Chien(int, int, RACE)...
11: Le cri du mammifre!
12: Je remue la queue...
13: Yorkie a 3 ans.
14: Dobbie pse 20kilos.
15: Destructeur de Chien...
16: Destructeur de Mammifere...
17: Destructeur de Chien...
18: Destructeur de Mammifere...
19: Destructeur de Chien...
20: Destructeur de Mammifere...
21: Destructeur de Chien...
22: Destructeur de Mammifere...
23: Destructeur de Chien...
24: Destructeur de Mammifere...
Le constructeur de Mammifere a t surcharg la ligne 12 pour prendre en charge un
entier (lge de lanimal). La variable sonAge est initialise avec la valeur passe au
constructeur et sonPoids est dnie 5 (lignes 62 67).
Chien surcharge cinq constructeurs (lignes 36 40). Le premier est le constructeur par
dfaut. la ligne 37, le deuxime prend un ge, qui est le mme paramtre que celui du
constructeur de Mammifere. Le troisime constructeur prend la fois un ge et un poids, le
quatrime un ge et une race ; enn, le dernier prend un ge, un poids et une race.
La ligne 74 contient le code du constructeur par dfaut de Chien et vous pouvez constater
que quelque chose a chang. Lorsque ce constructeur est appel, il invoque son tour le
constructeur par dfaut de Mammifere, comme on le voit la ligne 75. Bien que ce ne soit
pas strictement obligatoire de procder ainsi, cela permet dindiquer explicitement que
lon appelle le constructeur sans paramtre de la classe de base. Ce constructeur serait
automatiquement appel de toutes faons, mais cette faon de procder explicite clairement
vos intentions.
Limplmentation du constructeur de Chien, qui prend une valeur entire, gure de la
ligne 81 la ligne 86. Lors de ltape dinitialisation (lignes 82 et 83), Chien initialise sa
classe de base en lui passant le paramtre, puis initialise sa race (saRace).
Le deuxime constructeur de Chien est implment de la ligne 88 la ligne 94. Il prend en
charge deux paramtres. Cette fois encore, la classe de base est initialise laide du
constructeur appropri (ligne 89) mais on en prote galement pour affecter un poids
la variable sonPoids de la classe de base. Vous ne pouvez pas effectuer cette opration
lors de la phase dinitialisation car Mammifere ne possde pas de constructeur prenant un
poids en paramtre : cest la raison pour laquelle on initialise cette variable dans le corps
du constructeur de Chien.
C++Livre Page 371 J eudi, 7. mai 2009 9:45 09
372 Le langage C++
Examinez le fonctionnement des autres constructeurs pour tre certain de bien le compren-
dre. Distinguez notamment ce qui est initialis lors de la phase dinitialisation de ce qui
doit tre trait dans le corps du constructeur.
Le rsultat obtenu a t numrot an de pouvoir faire rfrence chacune des lignes. Les
deux premires correspondent la cration de linstance de Fido, laide du constructeur
par dfaut.
Les lignes 3 et 4 de la sortie correspondent la cration de Rover, alors que Buster est
reprsent aux lignes 5 et 6. Le constructeur de Mammifere appel prend en paramtre un
seul entier, alors que le constructeur de Chien prend deux valeurs entires.
Une fois que les objets ont t crs, ils sont traits puis dtruits. Pour cela, le destructeur
de Chien est appel avant celui de Mammifere, cinq fois chacun.
Rednition des mthodes de la classe de base
Un objet Chien a accs toutes les donnes et fonctions membres de la classe Mammifere,
ainsi qu ses propres fonctions et donnes membres, comme RemuerQueue(). Une classe
drive peut galement rednir une mthode de la classe de base. La rdnition dune
mthode modie limplmentation dune mthode de la classe de base dans une classe
drive.
Quand on rednit une mthode, sa signature doit correspondre celle de la mthode de la
classe de base. Une signature de mthode est son prototype, sans le type du rsultat elle
est donc forme du nom de la mthode, de la liste de ses paramtres et du mot const sil
est prsent. Le rsultat dune mthode rednie peut donc tre diffrent de celui de la
mthode dans la classe de base.
Dans le Listing 12.5, la classe Chien rednit la mthode Crier() de la classe Mammi-
fere. Pour des raisons despace, les fonctions daccs ont t volontairement omises.
Listing 12.5 : Rednition dune mthode de classe de base dans une classe drive
1: // Listing12.5 Redfinition dune mthode de
1a: // classe de base dans une classe drive
2: #include <iostream>
3: using std::cout;
4:
5: enum RACE { GOLDEN, CAIRN, DANDIE, SHETLAND, DOBERMAN, LAB };
6:
7: class Mammifere
8: {
9: public:
C++Livre Page 372 J eudi, 7. mai 2009 9:45 09
Chapitre 12 Hritage 373
10: // constructeur et destructeur
11: Mammifere() { cout << "Constructeur de Mammifere...\n"; }
12: ~Mammifere() { cout << "Destructeur de Mammifere...\n"; }
13:
14: // autres mthodes
15: void Crier()const
15a: { cout << "Le cri du mammifre!\n"; }
16: void Dormir()const { cout << "Chut. Je dors.\n"; }
17:
18: protected:
19: int sonAge;
20: int sonPoids;
21: };
22:
23: class Chien: public Mammifere
24: {
25: public:
26: // constructeur et destructeur
27: Chien(){ cout << "Constructeur de Chien...\n"; }
28: ~Chien(){ cout << "Destructeur de Chien...\n"; }
29:
30: // autres mthodes
31: void RemuerQueue() const
31a: { cout << "Je remue la queue...\n"; }
32: void Quemander() const
32a: { cout << "Je mendie de la nourriture...\n"; }
33: void Crier() const { cout << "Ouah Ouah!\n"; }
34:
35: private:
36: RACE saRace;
37: };
38:
39: int main()
40: {
41: Mammifere grosAnimal;
42: Chien Fido;
43: grosAnimal.Crier();
44: Fido.Crier();
45: return 0;
46: }
Ce programme produit le rsultat suivant :
Constructeur de Mammifere...
Constructeur de Mammifere...
Constructeur de Chien...
Le cri du mammifre!
C++Livre Page 373 J eudi, 7. mai 2009 9:45 09
374 Le langage C++
Ouah Ouah!
Destructeur de Chien...
Destructeur de Mammifere...
Destructeur de Mammifere...
On voit une mthode Crier() dnie la ligne 15 dans la classe Mammifere. La classe
Chien dclare aux lignes 23 37 hrite de Mammifere (ligne 23) et a donc accs cette
mthode Crier(). Elle la rednit toutefois ligne 33 an que les objets Chien aboient lors
de lappel cette mthode. La ligne 41 cre lobjet Mammifere grosAnimal, comme le
montre la premire ligne du rsultat. La ligne suivante cre Fido, ce qui dclenche lappel
du constructeur de Mammifere et du constructeur de Chien.
la ligne 43, lobjet grosAnimal appelle sa mthode Crier(), puis cest au tour de
lobjet Chien dappeler sa mthode Crier(). Lafchage du programme rete bien
lappel aux deux mthodes. Enn, les deux objets sont supprims laide de leurs destructeurs
respectifs.
Vous ne devez pas confondre les termes surcharge et rednition. Lorsque vous
surchargez une mthode, vous crez plusieurs mthodes de mme nom avec des
signatures diffrentes dans la mme classe. La rednition dune mthode
consiste crer dans une classe drive une mthode de mme nom et de mme
signature que celle de la classe de base.
Masquage de la mthode de la classe de base
Dans le listing prcdent, la mthode Crier() de la classe Chien cachait la mthode de la
classe de base. Cest ce que nous voulions, mais cette opration risque de produire des
rsultats inattendus. Sil existe une mthode Bouger() surcharge dans la classe Mammi-
fere et que la classe Chien la rednit, la mthode de la classe Chien masquera toutes les
mthodes portant le mme nom dans la classe Mammifere.
Supposons que la classe Mammifere surcharge la mthode Bouger() laide de trois fonc-
tions la premire nattend aucun paramtre, la deuxime attend un entier et la dernire
attend un entier et une direction et que la classe Chien rednit simplement la mthode
Bouger() qui ne prend pas de paramtre. Il sera alors difcile davoir accs aux deux
autres mthodes depuis un objet Chien. Le Listing 12.6 illustre ce problme.
Listing 12.6 : Masquage de mthodes
1: //Listing12.6 Masquage de mthodes
2: #include <iostream>
3: using std::cout;
4:
In
f
o
C++Livre Page 374 J eudi, 7. mai 2009 9:45 09
Chapitre 12 Hritage 375
5: class Mammifere
6: {
7: public:
8: void Bouger() const
8a: { cout << "Les mammifres se dplacent dun pas\n"; }
9: void Bouger(int distance) const
10: {
11: cout << "Les mammifres se deplacent de ";
12: cout << distance <<" pas.\n";
13: }
14: protected:
15: int sonAge;
16: int sonPoids;
17: };
18:
19: class Chien: public Mammifere
20: {
21: public:
22: // Avertissement possible signalant
22a: // que vous masquez une fonction!
23: void Bouger() const
23a: { cout << "Le chien se dplace de 5 pas.\n"; }
24: };
25:
26: int main()
27: {
28: Mammifere grosAnimal;
29: Chien Fido;
30: grosAnimal.Bouger();
31: grosAnimal.Bouger(2);
32: Fido.Bouger();
33: // Fido.Bouger(10);
34: return 0;
35: }
Ce programme produit le rsultat suivant :
Les mammifres se dplacent dun pas
Les mammifres se dplacent de 2 pas
Le chien se dplace de 5 pas
Toutes les mthodes et les donnes non indispensables ont t supprimes de ces classes.
Le programme commence par la dclaration des mthodes Bouger() surcharges dans la
classe Mammifere (lignes 8 et 9). la ligne 23, la classe Chien rednit la version de la
fonction Bouger() sans paramtre. Les appels ces mthodes (lignes 30 32) sont signals
dans le rsultat.
C++Livre Page 375 J eudi, 7. mai 2009 9:45 09
376 Le langage C++
Vous pouvez constater que la ligne 33 a t mise en commentaire (dsactive) car elle
produit une erreur de compilation. Aprs avoir redni lune des mthodes, vous ne
pouvez plus utiliser aucune des mthodes de base du mme nom. Bien quun objet Chien
aurait pu appeler la mthode Bouger(int) sil navait pas redni la mthode Bouger()
sans paramtre, il doit dsormais rednir les deux sil compte utiliser les deux. Sinon, il
cache la mthode qui nest pas rednie. Ce comportement est analogue la rgle qui veut
que si vous fournissez un constructeur quelconque, le compilateur ne vous fournira plus de
constructeur par dfaut.
La rgle est donc la suivante : si vous rednissez une mthode surcharge, tous les autres
surcharges de cette mthode sont masques. Si tel nest pas votre souhait, vous devez
toutes les rednir.
Les programmeurs dbutants oublient souvent le mot-cl const lorsquils rednissent
une mthode de la classe de base et la masque donc involontairement. En effet, le mot-cl
const fait partie de la signature : loublier revient modier la signature et donc cacher
la mthode, au lieu de la rednir.
Rednir ou cacher
La section qui suit dcrit les mthodes virtuelles. La rednition dune mthode virtuelle
autorise le polymorphisme, alors que le masquage dune mthode le dtriore. Vous allez
trs vite en apprendre davantage.
Appel de la mthode de base
Si vous avez redni une mthode de la classe de base, il est toujours possible de lappeler
en indiquant son nom complet, aprs le nom de la classe de base :
classeBase::Methode()
Vous pouvez donc appeler la mthode Bouger() de la classe Mammifere comme suit :
Mammifere::Bouger().
Vous pouvez utiliser ces noms qualis comme avec tout autre nom de mthode. Dans le
Listing 12.6, il aurait donc t possible de modier la ligne 33, de sorte que le programme
puisse tre compil :
Fido.Mammifere::Bouger(10);
Cette ligne appelle explicitement la mthode de la classe Mammifere. Le Listing 12.7
exploite cette syntaxe.
C++Livre Page 376 J eudi, 7. mai 2009 9:45 09
Chapitre 12 Hritage 377
Listing 12.7 : Appel dune mthode de base depuis une mthode rednie
1: // Listing12.7 Appel dune mthode de base
1a: // depuis une mthode redfinie.
2: #include <iostream>
3: using namespace std;
4:
5: class Mammifere
6: {
7: public:
8: void Bouger() const
8a: { cout << "Les mammifres se dplacent dun pas\n"; }
9: void Bouger(int distance) const
10: {
11: cout << "Les mammifres se dplacent de " << distance;
12: cout << " pas." << endl;
13: }
14:
15: protected:
16: int sonAge;
17: int sonPoids;
18: };
19:
20: class Chien: public Mammifere
21: {
22: public:
23: void Bouger() const;
24: };
25:
26: void Chien::Bouger() const
27: {
28: cout << "Mthode Bouger de Chien...\n";
29: Mammifere::Bouger(3);
30: }
31:
32: int main()
33: {
34: Mammifere grosAnimal;
35: Chien Fido;
36: grosAnimal.Bouger(2);
37: Fido.Mammifere::Bouger(6);
38: return 0;
39: }
Ce programme produit le rsultat suivant :
Les mammifres se dplacent de 2 pas
Les mammifres se dplacent de 6 pas
C++Livre Page 377 J eudi, 7. mai 2009 9:45 09
378 Le langage C++
Les lignes 34 et 35 crent les objets grosAnimal et Fido, respectivement de la classe
Mammifere et de la classe Chien. La mthode appele la ligne 36 est la mthode
Bouger() de la classe Mammifere qui attend un paramtre entier.
Dans le listing prcdent, le programmeur souhaitait galement faire bouger lobjet Chien
en appelant la fonction Bouger(int) de la classe Chien, or cette classe a redni la
mthode (sans paramtre) sans rednir galement celle qui en attend un elle ne
dispose donc pas dune version de cette mthode prenant un entier en paramtre. Pour
rsoudre ce problme, la ligne 37 appelle explicitement la mthode Bouger(int) de la
classe de base.
Lors de lappel des mthodes rednies des classes anctres laide de "::", si
une nouvelle classe est insre dans la hirarchie dhritage entre le descen-
dant et son anctre, le descendant fera un donc appel qui ignorera la classe
intermdiaire et risque de ne pas appeler certaines capacits essentielles,
implmentes par celle-ci.
Mthodes virtuelles
Nous avons vu quun objet Chien est-un objet Mammifere. Jusqu maintenant, cela signi-
ait simplement quil hritait de tous les attributs (donnes) et de toutes les fonctionnalits
(mthodes) de sa classe de base. Cependant, en C++ la relation est-un va beaucoup plus
loin que cela.
En C++, il est en effet possible dtendre le polymorphisme pour permettre aux pointeurs
vers les classes de base de recevoir des objets des classes drives. On peut donc crire :
Mammifere* pMammifere = new Chien;
Cette instruction cre un nouvel objet Chien sur le tas et renvoie un pointeur vers cet objet,
que lon peut affecter un pointeur sur un objet Mammifere. Ceci est possible car un chien
est un mammifre.
Faire Ne pas faire
Driver des classes existantes et bien testes
pour en accrotre les fonctionnalits.
Rednir les mthodes de la classe de base
pour modier le comportement de certaines
mthodes dans la classe drive.
Masquer une fonction de la classe de base en
modiant sa signature.
Oublier que const fait partie de la signature.
Oublier que le type du rsultat ne fait pas
partie de la signature.
A
s
t
u
c
e
C++Livre Page 378 J eudi, 7. mai 2009 9:45 09
Chapitre 12 Hritage 379
Il sagit de lessence mme du polymorphisme. Par exemple, vous pouvez crer
plusieurs types de fentres (botes de dialogue, fentres de dlement, zones de
listes, etc.) et associer chacune delles une fonction virtuelle dessiner(). En
affectant les types drivs un pointeur de fentre, le programme est capable
dappeler la fonction dessiner() approprie, quel que soit le type de fentre
trait.
Vous pouvez ensuite utiliser ce pointeur pour appeler nimporte quelle mthode de la
classe Mammifere. Lobjectif est que les mthodes rednies dans la classe Chien() appel-
lent la fonction adquate et cest justement ce que permettent les fonctions virtuelles. Pour
crer une fonction virtuelle, ajoutez le mot-cl virtual devant sa dclaration.
Le Listing 12.8 illustre parfaitement ce processus.
Listing 12.8 : Utilisation de fonctions virtuelles
1: //Listing12.8 Utilisation de fonctions virtuelles
2: #include <iostream>
3: using std::cout;
4:
5: class Mammifere
6: {
7: public:
8: Mammifere():wAge(1)
8a: { cout << "Constructeur de Mammifere...\n"; }
9: virtual ~Mammifere()
9a: { cout << "Destructeur de Mammifere...\n"; }
10: void Bouger() const
10a: { cout << "Les mammifres se dplacent dun pas\n"; }
11: virtual void Crier() const
11a: { cout << "Le cri du mammifre!\n"; }
12:
13: protected:
14: int sonAge;
15: };
16:
17: class Chien: public Mammifere
18: {
19: public:
20: Chien() { cout << "Constructeur de Chien...\n"; }
21: virtual ~Chien() { cout << "Destructeur de Chien...\n"; }
22: void RemuerQueue() { cout << "Je remue la queue...\n"; }
23: void Crier()const { cout << "Ouah Ouah!\n"; }
24: void Bouger()const
24a: { cout << "Le chien se dplace de 5 pas...\n"; }
25: };
In
f
o
C++Livre Page 379 J eudi, 7. mai 2009 9:45 09
380 Le langage C++
26:
27: int main()
28: {
29: Mammifere *pChien = new Chien;
30: pChien->Bouger();
31: pChien->Crier();
32:
33: return 0;
34: }
Ce programme produit le rsultat suivant :
Constructeur de Mammifere...
Constructeur de Chien...
Les mammifres se deplacent dun pas
Ouah Ouah!
La ligne 11 dnit la mthode virtuelle Crier() dans la classe Mammifere. Le concepteur
de cette classe indique donc quil sattend ce quelle devienne la classe de base dune
autre et que la classe drive voudra srement rednir cette mthode
La ligne 29 cre un pointeur vers un Mammifere (pChien), mais lui affecte ladresse dun
nouvel objet Chien. Cette affectation est autorise puisquun chien est-un mammifre. La
ligne 30 se sert ensuite de ce pointeur pour appeler la mthode Bouger(). Comme il sait
que pChien est un Mammifere, le compilateur examine lobjet Mammifere pour trouver sa
mthode Bouger(). La ligne 10 montre quil sagit dune mthode classique, non
virtuelle, et cest la raison pour laquelle il appelle la version de Mammifere.
La ligne 31 appelle la mthode Crier() sur ce mme pointeur. Celle-ci tant virtuelle
(ligne 11), cest la mthode rednie dans la classe Chien qui est cette fois-ci invoque.
Tout ceci a un aspect presque magique. La fonction appelante sait quelle dispose dun
pointeur vers un Mammifere, pourtant cest une mthode de Chien qui est appele. En fait,
si lon avait un tableau de pointeurs de Mammifere, pointant chacun sur une sous-classe
diffrente de celle-ci, on pourrait les appeler tour de rle et, chaque fois, ce serait la
mthode approprie qui serait invoque. Cest ce que lon fait dans le Listing 12.9.
Listing 12.9 : Appels conscutifs de plusieurs fonctions virtuelles
1: //Listing12.9 Appels conscutifs de
1a: // plusieurs fonctions virtuelles
2: #include <iostream>
3: using namespace std;
4:
5: class Mammifere
6: {
C++Livre Page 380 J eudi, 7. mai 2009 9:45 09
Chapitre 12 Hritage 381
7: public:
8: Mammifere():sonAge(1) { }
9: virtual ~Mammifere() { }
10: virtual void Crier() const
10a: { cout << "Le cri du mammifre!\n"; }
11:
12: protected:
13: int sonAge;
14: };
15:
16: class Chien: public Mammifere
17: {
18: public:
19: void Crier() const { cout << "Ouah Ouah!\n"; }
20: };
21:
22: class Chat: public Mammifere
23: {
24: public:
25: void Crier() const { cout << "Miaou!\n"; }
26: };
27:
28:
29: class Cheval: public Mammifere
30: {
31: public:
32: void Crier() const { cout << "Hihiiiii!\n"; }
33: };
34:
35: class Cochon: public Mammifere
36: {
37: public:
38: void Crier() const { cout << "Grouiiiik !\n"; }
39: };
40:
41: int main()
42: {
43: Mammifere* Tableau[5];
44: Mammifere* ptr;
45: int choix, i;
46: for ( i = 0; i < 5; i++)
47: {
48: cout <<
48a: "(1)Chien (2)Chat (3)Cheval (4)Cochon - Votre choix: ";
49: cin >> choix;
50: switch (choix)
51: {
C++Livre Page 381 J eudi, 7. mai 2009 9:45 09
382 Le langage C++
52: case 1: ptr = new Chien;
53: break;
54: case 2: ptr = new Chat;
55: break;
56: case 3: ptr = new Cheval;
57: break;
58: case 4: ptr = new Cochon;
59: break;
60: default: ptr = new Mammifere;
61: break;
62: }
63: Tableau[i] = ptr;
64: }
65: for (i = 0; i < 5; i++)
66: Tableau[i]->Crier();
67: return 0;
68: }
Ce programme produit le rsultat suivant :
(1)Chien (2)Chat (3)Cheval (4)Cochon- Votre choix: 1
(1)Chien (2)Chat (3)Cheval (4)Cochon- Votre choix: 2
(1)Chien (2)Chat (3)Cheval (4)Cochon- Votre choix: 3
(1)Chien (2)Chat (3)Cheval (4)Cochon- Votre choix: 4
(1)Chien (2)Chat (3)Cheval (4)Cochon- Votre choix: 5
Ouah Ouah !
Miaou!
Hihiiiii!
Grouiiiik !
Le cri du mammifre!
Ce programme allg illustre les mthodes virtuelles sous leur forme la plus pure. Les
quatre classes qui sont dclares (Chien, Chat, Cheval et Cochon) drivent toutes de
Mammifere.
La ligne 10 dclare la fonction membre Crier() de la classe Mammifere comme virtuelle.
Les quatre classes drives rednissent toutes cette mthode (lignes 19, 25, 32 et 38).
Le programme boucle cinq fois entre les lignes 46 et 64. chaque itration, lutilisateur
est invit choisir un objet crer et lon ajoute automatiquement au tableau un nouveau
pointeur vers ce type dobjet grce linstruction switch des lignes 50 62.
Au moment de la compilation, il est donc impossible de connatre les objets qui seront
crs et par consquent les mthodes Crier() qui seront appeles. Le pointeur ptr est
associ son objet au moment de lexcution : cest ce que lon appelle une liaison dyna-
mique (ou liaison retarde), par opposition la liaison statique (qui lieu au moment de la
compilation).
C++Livre Page 382 J eudi, 7. mai 2009 9:45 09
Chapitre 12 Hritage 383
Le programme boucle nouveau sur le tableau entre les lignes 65 et 66. Cette fois-ci,
chaque objet appelle sa mthode Crier(). Celle-ci tant virtuelle dans la classe de base,
ce seront les mthodes Crier() appropries chaque type qui seront appeles. Vous
voyez dans le rsultat que si vous choisissez des types diffrents, la mthode correspon-
dante est appele chaque fois.
Le fait de marquer une mthode membre comme virtuelle dans la classe de base impli-
que-t-il de devoir faire de mme dans les classes drives ?
Non, lorsquune mthode est virtuelle, elle le reste si vous la rednissez dans les classes
drives. Il est cependant conseill (bien que non obligatoire) de la signaler comme
virtuelle, an de rendre le code plus comprhensible.
Fonctionnement des mthodes virtuelles
Lorsquon cre un objet driv comme un objet Chien, on appelle dabord le constructeur
de la classe de base puis le constructeur de la classe drive. La Figure 12.2 prsente
lobjet Chien aprs sa cration : vous remarquerez que la partie Mammifere de lobjet se
situe en mmoire ct de la partie Chien.
Lorsque lon cre une mthode virtuelle dans un objet, celui-ci doit garder la trace de
cette fonction. Beaucoup de compilateurs crent une table de fonction virtuelle,
nomme v-table. chaque type correspond une table et chaque objet dun type dispose
dun pointeur de table virtuel (appel vptr ou v-pointeur), qui pointe sur cette table.
Bien que les implmentations puissent varier, tous les compilateurs doivent accomplir la
mme tche.
Le v-pointeur de chaque objet pointe sur la v-table qui contient elle-mme des pointeurs
vers chaque mthode virtuelle (les pointeurs de fonctions sont traits au Chapitre 15).
Lorsque la partie Mammifere de lobjet Chien est cre, le vptr est donc initialis pour
pointer sur la zone approprie de la v-table, comme le montre la Figure 12.3.
Figure 12.2
Lobjet Chien
aprs sa cration.
Mammifere
Chien
Objet Chien
Partie Mammifere
C++Livre Page 383 J eudi, 7. mai 2009 9:45 09
384 Le langage C++
Lorsque le constructeur de Chien est appel et que la partie Chien de cet objet est ajoute,
le v-pointeur est modi pour pointer sur la fonction virtuelle rednie (le cas chant)
dans lobjet Chien (voir Figure 12.4).
Lorsque lon utilise un pointeur vers un objet Mammifere, le v-pointeur continue pointer
sur la fonction approprie au type "rel" de lobjet. Ainsi, lorsquon va appeler Crier(),
on obtiendra bien la fonction adquate.
Accs aux mthodes partir dune classe de base
Nous avons vu des mthodes dune classe drive, auxquelles on a accd partir dune
classe de base grce au mcanisme des fonctions virtuelles. Que se passerait-il sil y avait
une mthode dans la classe drive qui ne soit pas dans la classe de base ? Pourriez-vous y
accder de la mme manire quen utilisant la classe de base pour accder aux mthodes
virtuelles ? Il ne devrait pas y avoir de conit de noms puisque seule la classe drive
dispose de cette mthode.
Si lobjet Chien possde une mthode RemuerQueue() par exemple, qui ne gure pas dans
la classe Mammifere, vous ne pouvez pas vous servir du pointeur de la classe de base pour
avoir accs la fonction de la classe drive. La mthode RemuerQueue() ntant pas
virtuelle et nappartenant pas la classe Mammifere, elle ne peut tre appele si vous
ntes ni un Chien ni un pointeur sur celui-ci.
Figure 12.3
La v-table dun objet
Mammifere.
Figure 12.4
La v-table dun objet
Chien.
Mammifere
VPTR
& Bouger
& Crier
Mammifere
Chien
Mammifere : Bouger()
VPTR
&
Chien : Crier()
&
C++Livre Page 384 J eudi, 7. mai 2009 9:45 09
Chapitre 12 Hritage 385
On pourrait transtyper le pointeur de Mammifere en pointeur sur un Chien. Toutefois, cette
pratique est dconseiller, car elle nest pas sre si le Mammifere point nest pas un
Chien : il existe un mthode bien plus able dappeler RemuerQueue(). Les transtypages
doivent tre utiliss avec la plus grande prudence, car ils engendrent des erreurs dexcution.
Pour en savoir plus, reportez-vous aux Chapitres 15 et 20.
Dcoupage des donnes
La magie des mthodes virtuelles nagit que sur les pointeurs et les rfrences. Passer un
objet par valeur ne permet pas dappeler une fonction virtuelle, comme le montre le
Listing 12.10.
Listing 12.10 : Passage de paramtres par valeur et dcoupage de donnes
1: // Listing12.10 Passage de paramtres par valeur
1a: // et dcoupage de donnes
2: #include <iostream>
3: using namespace std;
4:
5: class Mammifere
6: {
7: public:
8: Mammifere():sonAge(1) { }
9: virtual ~Mammifere() { }
10: virtual void Crier() const
10a: { cout << "Le cri du mammifre!\n"; }
11:
12: protected:
13: int sonAge;
14: };
15:
16: class Chien: public Mammifere
17: {
18: public:
19: void Crier()const { cout << "Ouah Ouah !\n"; }
20: };
21:
22: class Chat: public Mammifere
23: {
24: public:
25: void Crier()const { cout << "Miaou!\n"; }
26: };
27:
28: void FoncVal(Mammifere);
29: void FoncPtr(Mammifere*);
C++Livre Page 385 J eudi, 7. mai 2009 9:45 09
386 Le langage C++
30: void FoncRef(Mammifere&);
31: int main()
32: {
33: Mammifere* ptr = 0;
34: int choix;
35: while (1)
36: {
37: bool fQuit = false;
38: cout << "(1)Chien (2)Chat (0)Quitter - Votre choix: ";
39: cin >> choix;
40: switch (choix)
41: {
42: case 0: fQuit = true;
43: break;
44: case 1: ptr = new Chien;
45: break;
46: case 2: ptr = new Chat;
47: break;
48: default: ptr = new Mammifere;
49: break;
50: }
51: if (fQuit)
52: break;
53: FoncPtr(ptr);
54: FoncRef(*ptr);
55: FoncVal(*ptr);
56: }
57: return 0;
58: }
59:
60: void FoncVal(Mammifere ValeurMammifere)
61: {
62: ValeurMammifere.Crier();
63: }
64:
65: void FoncPtr(Mammifere * pMammifere)
66: {
67: pMammifere->Crier();
68: }
69:
70: void FoncRef(Mammifere & rMammifere)
71: {
72: rMammifere.Crier();
73: }
C++Livre Page 386 J eudi, 7. mai 2009 9:45 09
Chapitre 12 Hritage 387
Ce programme produit le rsultat suivant :
(1)Chien (2)Chat (0)Quitter - Votre choix: 1
Ouah Ouah !
Ouah Ouah !
Le cri du mammifre!
(1)Chien (2)Chat (0)Quitter - Votre choix: 2
Miaou!
Miaou!
Le cri du mammifre!
(1)Chien (2)Chat (0)Quitter - Votre choix: 0
Le programme commence par les dclarations des versions rduites de Mammifere, Chien
et Chat. Les fonctions FoncPtr(), FoncRef() et FoncVal() sont ensuite dclares et
prennent respectivement un pointeur sur un objet Mammifere, une rfrence un objet
Mammifere et un objet Mammifere. Comme vous pouvez le constater aux lignes 60 73,
elles appellent toutes les trois la mthode Crier().
Lutilisateur a le choix entre un chien et un chat. En fonction de la valeur saisie, les
lignes 44 46 crent un pointeur vers le type correspondant.
Sur la premire ligne du rsultat, lutilisateur a choisi loption 1. Un objet Chien est alors
cr sur le tas (ligne 44), puis pass successivement aux trois fonctions comme pointeur en
ligne 53, comme rfrence en ligne 54 et par valeur en ligne 55.
Le pointeur et les appels de rfrence permettent dappeler la fonction virtuelle Chien-
>Crier() (voir les deux premires lignes du rsultat aprs le choix de lutilisateur).
Par contre, on a pass par valeur le pointeur drfrenc la fonction dnie aux lignes 60
63. Celle-ci attendant un objet Mammifere, le compilateur dcoupe la partie de base
contenue dans lobjet Chien et ne conserve que la partie Mammifre. Lorsque lon appelle
la mthode Crier() de Mammifere la ligne 72, seules les informations sur les mammif-
res sont donc disponibles, tout ce qui concerne les chiens a disparu. On peut le constater
avec la troisime ligne du rsultat, aprs le choix de lutilisateur. Cet effet est appel
dcoupage, car les parties Chien (celles de votre classe drive) de votre objet ont t
supprimes lors de la conversion en Mammifere (la classe de base).
Cette opration est ensuite rpte pour lobjet Chat, avec les mmes consquences.
Destructeurs virtuels
Il est courant et classique de passer un pointeur sur un objet dune classe drive lorsque le
programme attend un pointeur sur un objet de la classe de base. Que se passe-t-il si le
pointeur de lobjet driv est supprim ? Si le destructeur est virtuel, comme cela devrait
C++Livre Page 387 J eudi, 7. mai 2009 9:45 09
388 Le langage C++
tre le cas, tout se passera bien cest le destructeur de la classe drive qui sera appel.
Ce destructeur appelant automatiquement celui de la classe de base, tout lobjet sera
correctement dtruit.
La rgle est la suivante : si lune des mthodes dune classe est virtuelle, le destructeur de
cette classe doit ltre lui aussi.
Les listings de ce chapitre comprenaient des destructeurs virtuels. Vous savez
maintenant pourquoi ! En gnral, il vaut toujours mieux que les destructeurs
soient virtuels.
Constructeurs de copie virtuels
Les constructeurs ne pouvant pas tre virtuels, on ne peut donc techniquement pas crire
un constructeur de copie virtuel. Cependant, un programme a parfois dsesprment
besoin de pouvoir passer un pointeur vers un objet de la classe de base et dobtenir une
copie de lobjet driv correspondant qui a t cr.
La solution la plus courante consiste crer une mthode Clone() dans la classe de base
en la dclarant virtuelle. Cette mthode cre une copie de lobjet de la classe courante et la
renvoie.
Toutes les classes drives rednissant la mthode Clone(), la copie cre sera une copie
de la classe drive. Cette procdure est mise en uvre dans le Listing 12.11.
Listing 12.11 : Constructeur de copie virtuel
1: //Listing12.11 Constructeur de copie virtuel
2: #include <iostream>
3: using namespace std;
4:
5: class Mammifere
6: {
7: public:
8: Mammifere():sonAge(1)
8a: { cout << "Constructeur de Mammifere...\n"; }
9: virtual ~Mammifere()
9a: { cout << "Destructeur de Mammifere...\n"; }
10: Mammifere const Mammifere & rhs);
11: virtual void Crier() const
11a: { cout << "Le cri du mammifre!\n"; }
12: virtual Mammifere* Clone()
12a: { return new Mammifere(*this); }
In
f
o
C++Livre Page 388 J eudi, 7. mai 2009 9:45 09
Chapitre 12 Hritage 389
13: int GetAge()const { return sonAge; }
14: protected:
15: int wAge;
16: };
17:
18: Mammifere::Mammifere (const Mammifere & rhs):
18a sonAge(rhs.GetAge())
19: {
20: cout << "Constructeur de copie de Mammifere...\n";
21: }
22:
23: class Chien: public Mammifere
24: {
25: public:
26: Chien() { cout << "Constructeur de Chien...\n"; }
27: virtual ~Chien() { cout << "Destructeur de Chien...\n"; }
28: Chien(const Chien & rhs);
29: void Crier()const { cout << "Ouah Ouah !\n"; }
30: virtual Mammifere* Clone() { return new Chien(*this); }
31: };
32:
33: Chien::Chien(const Chien & rhs):
34: Mammifere(rhs)
35: {
36: cout << "Constructeur de copie de Chien...\n";
37: }
38:
39: class Chat: public Mammifere
40: {
41: public:
42: Chat() { cout << "Constructeur de Chat...\n"; }
43: ~Chat() { cout << "Destructeur de Chat...\n"; }
44: Chat(const Chat &);
45: void Crier()const { cout << "Miaou!\n"; }
46: virtual Mammifere* Clone() { return new Chat(*this); }
47: };
48:
49: Chat::Chat(const Chat & rhs):
50: Mammifere(rhs)
51: {
52: cout << "Constructeur de copie de Chat...\n";
53: }
54:
55: enum ANIMALS { MAMMIFERE, CHIEN, CHAT};
56: const int NbreTypesAnimaux = 3;
57: int main()
58: {
C++Livre Page 389 J eudi, 7. mai 2009 9:45 09
390 Le langage C++
59: Mammifere *Tableau[NbreTypesAnimaux ];
60: Mammifere* ptr;
61: int choix, i;
62: for ( i = 0; i < NbreTypesAnimaux; i++)
63: {
64: cout <<
64a: "(1)Chien (2)Chat (3)Mammifre - Votre choix: ";
65: cin >> choix;
66: switch (choix)
67: {
68: case CHIEN: ptr = new Chien;
69: break;
70: case CHAT: ptr = new Chat;
71: break;
72: default: ptr = new Mammifere;
73: break;
74: }
75: Tableau[i] = ptr;
76: }
77: Mammifere *AutreTableau[NbreTypesAnimaux];
78: for (i = 0; i < NbreTypesAnimaux; i++)
79: {
80: Tableau[i]->Crier();
81: AutreTableau[i] = Tableau[i]->Clone();
82: }
83: for (i = 0; i < NbreTypesAnimaux; i++)
84: AutreTableau[i]->Crier();
85: return 0;
86: }
Ce programme produit le rsultat suivant :
1: (1)Chien (2)Chat (3)Mammifre - Votre choix : 1
2: Constructeur de Mammifere...
3: Constructeur de Chien...
4: (1)Chien (2)Chat (3)Mammifre - Votre choix : 2
5: Constructeur de Mammifere...
6: Constructeur de Chat...
7: (1)Chien (2)Chat (3)Mammifre - Votre choix: 3
8: Constructeur de Mammifere...
9: Ouah Ouah !
10: Constructeur de copie de Mammifere...
11: Constructeur de copie de Chien...
12: Miaou!
13: Constructeur de copie de Mammifere...
14: Constructeur de copie de Chat...
15: Le cri du mammifre!
C++Livre Page 390 J eudi, 7. mai 2009 9:45 09
Chapitre 12 Hritage 391
16: Constructeur de copie de Mammifere...
17: Ouah Ouah !
18: Miaou!
19: Le cri du mammifre!
Le Listing 12.11 ressemble beaucoup aux deux prcdents, sauf que la ligne 12 ajoute la
classe Mammifere une nouvelle mthode virtuelle appele Clone(). Cette mthode
renvoie un pointeur sur un nouvel objet Mammifere. Pour cela, elle appelle le constructeur
de copie en passant en paramtre lobjet lui-mme (*this) comme rfrence constante.
Chien et Chat rednissent toutes les deux la mthode Clone() en initialisant leurs
donnes et en passant des copies de leurs objets leurs propres constructeurs de copie.
Cette mthode tant virtuelle, cela revient donc crer un constructeur de copie "virtuel".
Vous pouvez le constater lorsque la ligne 81 sexcute.
Lutilisateur a le choix entre chiens, chats et mammifres. Les objets correspondants sont
crs de la ligne 68 la ligne 73. Pour chaque choix, un pointeur est stock dans un
tableau (ligne 75).
Lorsque le programme boucle sur le tableau (lignes 78 82), chaque objet appelle ses
mthodes Crier() et Clone(). Le rsultat de lappel Clone() la ligne 81 est un poin-
teur vers une copie de lobjet, qui est ensuite stock dans un second tableau.
la premire ligne du rsultat, lutilisateur a choisi loption 1 pour crer un objet Chien.
Les constructeurs de Mammifere et de Chien sont alors appels. La procdure est semblable
lorsquil choisit un Chat (ligne 4) et un Mammifere (ligne 8).
La ligne 9 du rsultat est le rsultat de lappel Crier() sur le premier objet, le Chien. La
mthode virtuelle Crier() est appele et invoque donc la version correcte de cette
mthode. Puis, on appelle la mthode Clone() et, puisquelle est galement virtuelle,
cest la mthode Clone() de Chien qui est invoque. Le constructeur de Mammifere et le
constructeur de copie de Chien sont donc appels.
Le mme traitement sexcute pour le chat et le mammifre (lignes 12 14 et lignes 15 et
16) Pour nir, le nouveau tableau est parcouru (lignes 83 et 84) et chaque lment appelle
sa mthode Crier() (lignes 17 19 du rsultat).
Avantages et inconvnients des mthodes virtuelles
Les objets qui possdent des mthodes virtuelles consomment plus de mmoire cause de
la v-table quils doivent grer. Si votre est trs petite et que vous ne prvoyez pas que
dautres classes en hriteront, il est inutile davoir recours aux mthodes virtuelles.
La v-table occupe sa place en mmoire ds la cration de la premire fonction virtuelle et
sa taille volue peu par la suite (mme si chaque nouvelle entre augmente un peu sa
C++Livre Page 391 J eudi, 7. mai 2009 9:45 09
392 Le langage C++
taille). Vous avez donc pay lessentiel du billet dentre et vous pouvez alors en proter :
le destructeur devra tre virtuel et toutes les autres mthodes devront srement ltre aussi.
Prenez le temps dexaminer chacune des mthodes non virtuelles et assurez-vous davoir
bien compris pourquoi elles ne le sont pas.
Questions-rponses
Q Les membres et les mthodes hrits sont-ils transmis aux gnrations suivan-
tes ? Si Chien drive de Mammifere et que cette dernire classe drive elle-mme
de Animal, Chien hrite-t-il des mthodes et des donnes de Animal ?
R Oui. Les classes drives hritent de lensemble des mthodes et des donnes de toutes
leurs classes de base, mais elles ne peuvent accder qu celles qui sont protges ou
publiques.
Q Si, dans lexemple prcdent, Mammifere rednit une mthode de la classe
Animal, la classe Chien accdera-t-elle la fonction dorigine ou la fonction
rednie ?
R Si Chien hrite de Mammifere, cest la fonction rednie qui sera appele.
Q Est-ce quune classe drive peut rendre prive une mthode publique de la
classe de base ?
R Oui, la classe drive peut rednir la mthode et la rendre prive. Elle le demeure
ensuite pour toutes les classes drives suivantes. Cela doit toutefois tre vit autant
que possible, car les utilisateurs de votre classe sattendront ce quelle contienne la
somme des mthodes fournies par ses anctres.
Q Pourquoi toutes les fonctions de classe ne sont-elles pas virtuelles ?
R La premire fonction cre dans la table virtuelle consomme davantage de ressources
que les autres, mais ce surcot augmente peu ensuite. De nombreux programmeurs
C++ pensent que si une mthode est virtuelle, toutes les autres doivent ltre gale-
ment. Dautres ne sont pas daccord et pensent quil doit toujours exister une bonne
raison de le faire.
Faire Ne pas faire
Utiliser des mthodes virtuelles si vous envi-
sagez de crer des classes drives.
Utiliser un destructeur virtuel si la classe
comporte au moins une mthode virtuelle.
Dclarer un constructeur virtuel.
Tenter daccder des donnes prives dans
une classe de base partir dune classe dri-
ve.
C++Livre Page 392 J eudi, 7. mai 2009 9:45 09
Chapitre 12 Hritage 393
Q Supposons que la fonction Fonc() soit virtuelle dans une classe de base et
surcharge pour prendre en paramtre un ou deux entiers. La classe drive
rednit la fonction qui na quun paramtre. Laquelle des deux fonctions sera
appele lorsquun pointeur vers un objet driv invoquera la fonction avec deux
paramtres ?
R La rednition de la fonction avec un seul paramtre a masqu les deux fonctions de la
classe de base, ce qui les rend inutilisables. Le compilateur va produire une erreur indi-
quant que la fonction nattend quun seul paramtre de type entier.
Testez vos connaissances
1. Quest-ce quune v-table ?
2. Quest-ce quun destructeur virtuel ?
3. Comment dclare-t-on un constructeur virtuel ?
4. Comment cre-t-on un constructeur de copie virtuel ?
5. Comment peut-on appeler une fonction membre dune classe de base partir dune
classe drive dans laquelle elle a t rednie ?
6. Comment peut-on appeler une fonction membre dune classe de base partir dune
classe drive dans laquelle elle na pas t rednie ?
7. Si une classe de base dclare une fonction virtuelle et que le mot-cl virtual est omis
dans la classe drive lors de la rednition de cette fonction, sera-t-elle toujours
virtuelle lorsquelle sera hrite par une classe de troisime gnration ?
8. quoi sert le mot-cl protected ?
Exercices
1. Dclarez une fonction virtuelle qui prend un entier en paramtre et renvoie void.
2. Dclarez une classe Carre, drive de Rectangle, elle-mme drive de Forme.
3. Dans lExercice 2, le constructeur de Forme ne prend pas de paramtres, alors que
celui de Rectangle attend une longueur et une largeur. Celui de Carre attend seule-
ment une longueur. Montrez linitialisation dun Carre ralise par son constructeur.
4. crivez un constructeur de copie virtuel pour la classe Carre de lExercice 3.
5. CHERCHEZ LERREUR dans cet extrait de programme :
void Fonction(Forme);
C++Livre Page 393 J eudi, 7. mai 2009 9:45 09
394 Le langage C++
Forme * pRect = new Rectangle;
Fonction(*pRect);
6. CHERCHEZ LERREUR dans cet extrait de programme :
class Forme()
{
public:
Forme();
virtual ~Forme();
virtual Forme(const Forme&);
};
C++Livre Page 394 J eudi, 7. mai 2009 9:45 09
13
Tableaux et chanes
Au sommaire de ce chapitre
Nature, rle et dclaration des tableaux
Nature des chanes et cration partir de tableaux de caractres
Relations entre tableaux et pointeurs
Utilisation de larithmtique des pointeurs
Les listes chanes
Quest-ce quun tableau ?
Un tableau est une collection squentielle demplacements en mmoire pouvant contenir,
chacun, des donnes de mme type. Chaque emplacement est appel lment du tableau.
Pour dclarer un tableau, vous devez indiquer un type, suivi du nom du tableau et dun
nombre entre crochets, correspondant au nombre dlments du tableau. Par exemple :
long TableauLong[25];
Cette ligne dclare un tableau de 25 entiers longs, nomm TableauLong. Le compilateur
rservera un espace sufsant en mmoire pour contenir ces 25 lments. Si un entier long
C++Livre Page 395 J eudi, 7. mai 2009 9:45 09
396 Le langage C++
occupe 4 octets, cette dclaration rserve donc 100 octets contigus en mmoire vive (voir
Figure 13.1).
Accs aux lments de tableau
On accde un lment particulier dun tableau en indiquant son dcalage par rapport au
dbut du tableau. Les dcalages dlments (appels plus communment indices) tant
numrots partir de 0, le premier lment du tableau TableauLong est reprsent par
TableauLong[0], le second par TableauLong[1], etc.
Cette syntaxe peut prter confusion. La dclaration Tableau[3], par exemple, indique
que Tableau contiendra trois lments : Tableau[0], Tableau[1] et Tableau[2]. Plus
gnralement, la dclaration Tableau[n] cre un tableau de n lments numrots de
Tableau[0] Tableau[n-1]. Ceci est d au fait que lindice est un dcalage et que le
premier lment est donc 0 emplacement par rapport au dbut du tableau, le deuxime
un emplacement, et ainsi de suite.
En consquence, TableauLong[25] est numrot de TableauLong[0] Tableau-
Long[24]. Le Listing 13.1 dclare un tableau de cinq entiers, puis affecte une valeur
chacun de ses lments.
partir de maintenant, les numros de ligne commenceront par zro pour vous
aider vous souvenir que les tableaux C++ commencent aussi par zro !
Listing 13.1 : Utilisation dun tableau dentiers
0: //Listing13.1 - Tableaux
1: #include <iostream>
2:
3: int main()
4: {
Figure 13.1
Dclaration dun tableau.
4 octets
100 octets
In
f
o
C++Livre Page 396 J eudi, 7. mai 2009 9:45 09
Chapitre 13 Tableaux et chanes 397
5: int monTableau[5]; // Tableau de cinq entiers
6: int i;
7: for ( i = 0; i < 5; i++) // 0 4
8: {
9: std::cout << "Valeur de monTableau[" << i << "]: ";
10: std::cin >> monTableau[i];
11: }
12: for (i = 0; i < 5; i++)
13: std::cout << i << ": " << monTableau[i] << std::endl;
14: return 0;
15: }
Ce programme produit le rsultat suivant :
Valeur de monTableau[0]: 3
Valeur de monTableau[1]: 6
Valeur de monTableau[2]: 9
Valeur de monTableau[3]: 12
Valeur de monTableau[4]: 15
0: 3
1: 6
2: 9
3: 12
4: 15
Le Listing 13.1 cre un tableau, vous fait saisir les valeurs de chaque lment, puis les af-
che lcran. La dclaration de la ligne 5 indique que monTableau est un tableau
dentiers ; elle contient le chiffre 5 entre crochets, ce qui signie que le tableau contient
cinq lments entiers, chacun pouvant tre considr comme une variable entire. la
ligne 7, la variable de boucle sincrmente de 0 4 an dinitialiser les lments du
tableau avec la valeur saisie par lutilisateur.
Si lon se penche sur la ligne 10, on voit que laccs chaque lment se fait laide du
nom du tableau suivi de crochets, contenant le dcalage. Chaque lment peut ensuite tre
trait comme une variable du type des lments du tableau.
La premire valeur est stocke dans monTableau[0], la deuxime dans monTableau[1], etc.
La deuxime boucle permet dafcher toutes les valeurs lcran.
La numrotation des lments dun tableau commence 0 (jamais 1), ce qui
est une cause frquente derreurs pour les dbutants en C++. Lindice doit tre
considr comme un dcalage. Par exemple, un tableau contenant 10 lments
sera numrot de Tableau[0] Tableau[9], llment Tableau[10] corres-
pondant un onzime lment situ hors limite.
In
f
o
C++Livre Page 397 J eudi, 7. mai 2009 9:45 09
398 Le langage C++
criture dlments hors limite
Lorsque lon crit une valeur dans un tableau, le compilateur calcule lemplacement de
stockage en fonction de la taille de chaque lment et son indice. Supposons, par exemple,
que vous souhaitiez stocker une valeur dans TableauLong[5] alors que le tableau ne
comprend que 5 lments. Le compilateur va multiplier la valeur du dcalage (5) par la
taille de chaque lment (4 octets, ici) et donc dplacer le pointeur de 20 octets par rapport
au dbut du tableau. Enn, il va crire la nouvelle valeur cet emplacement.
En vrit, le compilateur ralise la copie de la valeur lendroit indiqu, mme si ce
dernier se situe en dehors de la limite suprieure du tableau. Par exemple, si vous tentez de
stocker une valeur dans TableauLong[50], le compilateur se placera 200 octets partir
du dbut du tableau, puis crasera les donnes qui se trouvent cet emplacement en les
remplaant par la nouvelle valeur. Cette opration peut, dans le meilleur des cas, produire
une erreur immdiate (dans le meilleur des cas). Dans le pire des cas, cette erreur ne
surviendra pas tout de suite et vous nirez par obtenir des rsultats bizarres que vous aurez
beaucoup de mal expliquer.
Le compilateur se comporte comme un aveugle estimant une distance partir dune
maison, Maison[0]. Si vous lui demandez daller la sixime maison de la rue, il se dira
"je dois aller 5 maisons plus loin. Chaque maison faisant quatre grands pas de large, je
dois avancer de 20 pas.". Si vous lui demandez daller Maison[100] alors que la rue na
que 25 maisons, il se dplacera de 400 pas et se retrouvera srement sous les roues dun
camion avant de les avoir tous parcourus. Vous devez donc tre sr de lendroit o vous
lenvoyez.
Le Listing 13.2 crit une valeur aprs la limite suprieure dun tableau. Compilez ce
listing pour voir les messages derreur et davertissement qui safchent. Sil ny en a pas,
faites dautant plus attention lorsque vous travaillerez avec les tableaux !
Ce programme contient des erreurs classiques ne pas commettre. Ne lexcutez
pas, car il risquerait de faire planter votre systme.
Listing 13.2 : Stockage dune valeur au-del de la limite suprieure dun tableau
0: // Listing13.2 - criture dune valeur au-del de la limite
1: // suprieure dun tableau
2: #include <iostream>
3: using namespace std;
4:
5: int main()
6: {
A
tte
n
tio
n
C++Livre Page 398 J eudi, 7. mai 2009 9:45 09
Chapitre 13 Tableaux et chanes 399
7: // sentinelles
8: long SentinelUne[3];
9: long TableauCible[25]; // Tableau remplir
10: long SentinelDeux[3];
11: int i;
12: for (i = 0; i < 3; i++)
13: {
14: SentinelUne[i] = 0;
15: SentinelDeux[i] = 0;
16: }
17: for (i = 0; i < 25; i++)
18: TableauCible[i] = 10;
19:
19a: // test de la valeur actuelle (devrait tre 0)
20: cout << "Test 1: \n";
21: cout << "TableauCible[0]: " << TableauCible[0] << endl;
22: cout << "TableauCible[24]: " << TableauCible[24]
22a: << endl << endl;
23:
24: for (i = 0; i < 3; i++)
25: {
26: cout << "SentinelUne[" << i << "]: ";
27: cout << SentinelUne[i] << endl;
28: cout << "SentinelDeux[" << i << "]: ";
29: cout << SentinelDeux[i]<< endl;
30: }
31:
32: cout << "\Affectation en cours...";
33: for (i = 0; i <= 25; i++) // Va un peu trop loin!
34: TableauCible[i] = 20;
35:
36: cout << "\nTest 2: \n";
37: cout << "TableauCible[0]: " << TableauCible[0] << endl;
38: cout << "TableauCible[24]: " << TableauCible[24] << endl;
39: cout << "TableauCible[25]: " << TableauCible[25]
39a: << endl << endl;
40: for (i = 0; i < 3; i++)
41: {
42: cout << "SentinelUne[" << i << "]: ";
43: cout << SentinelUne[i]<< endl;
44: cout << "SentinelDeux[" << i << "]: ";
45: cout << SentinelDeux[i]<< endl;
46: }
47:
48: return 0;
49: }
C++Livre Page 399 J eudi, 7. mai 2009 9:45 09
400 Le langage C++
Ce programme produirait le rsultat suivant :
Test 1:
TableauCible[0]: 10
TableauCible[24]: 10
SentinelUne[0]: 0
SentinelDeux[0]: 0
SentinelUne[1]: 0
SentinelDeux[1]: 0
SentinelUne[2]: 0
SentinelDeux[2]: 0
Affectation en cours...
Test 2:
TableauCible[0]: 20
TableauCible[24]: 20
TableauCible[25]: 20
SentinelUne[0]: 20
SentinelDeux[0]: 0
SentinelUne[1]: 0
SentinelDeux[1]: 0
SentinelUne[2]: 0
SentinelDeux[2]: 0
Les deux tableaux de trois entiers longs dclars aux lignes 8 et 10 vont jouer le rle de
sentinelles autour du tableau TableauCible. Ces sentinelles sont initialises 0 par les
lignes 12 16. Comme elles sont dclares avant et aprs le tableau cible, il y a de fortes
chances quelles soient places en mmoire de part et dautre de ce dernier. Si le
programme crit des donnes aprs la n de TableauCible, les sentinelles seront donc
probablement modies. Certains compilateurs allouent vers le bas de la mmoire alors
que dautres allouent vers le haut, cest pour cette raison que lon a encadr le tableau
cible par deux sentinelles.
Lors du premier test, ralis de la ligne 20 la ligne 30, les valeurs des sentinelles sont
correctes, comme leur afchage, tout comme le premier et le dernier lment de Tableau-
Cible. la ligne 34, les membres de TableauCible sont tous raffects avec la valeur
20, mais le compteur atteint (ligne 34) la valeur de dcalage 25, qui nexiste pas le tableau
cible.
Les valeurs de TableauCible apparaissent lcran (lignes 37 39) sous forme de
deuxime test. Vous pouvez constater (ligne 39) que TableauCible[25] contient bien
le nombre 20. Cependant, lorsque les sentinelles sont afches, on constate que la valeur
de SentinelUne[0] a t modie. En effet, la zone mmoire situe 25 lments de
C++Livre Page 400 J eudi, 7. mai 2009 9:45 09
Chapitre 13 Tableaux et chanes 401
TableauCible[0] est la mme que SentinelUne[0]. Lorsquon a tent daccder la
valeur inexistante TableauCible[25], on a donc accd en ralit au contenu de
SentinelUne[0].
Les compilateurs utilisant diffremment la mmoire, vos rsultats peuvent
varier. Il se peut que les sentinelles ne soient pas crases. Dans ce cas, essayez
de modier la ligne 33 pour utiliser un autre indice (passez de 25 26). Ceci
augmentera la probabilit dcraser une sentinelle, mais vous risquez bien sr
dcraser autre chose ou de faire planter votre systme.
Cette erreur est difcile dtecter, car la valeur de SentinelUne[0] est modie dans une
partie du programme qui naccde pas directement SentinelUne.
Erreurs dintervalle
Il est si courant dcrire une valeur aprs la limite suprieure dun tableau que cette erreur
a son propre nom : on lappelle erreur dintervalle ou erreur de borne. Lorsque lon
demande combien il y a de piquets sur une barrire de 10 mtres, sachant que lespace-
ment entre les piquets est de un mtre, la plupart des gens rpondent sans rchir : "10 !",
or la bonne rponse est 11 (voir Figure 13.2).
Cette faon de compter "en partant par dfaut de un" est la bte noire des programmeurs
C++ dbutants. Avec le temps, vous vous habituerez lide quun tableau de 25 lments
est numrot de 0 24.
Certains programmeurs dsignent Tableau[0] comme llment 0. Cette habi-
tude est proscrire, car si Tableau[0] est llment 0, quoi correspond
Tableau[1] ? au premier lment ? Dans ce cas, lorsque vous verrez
Tableau[24], raliserez-vous quil ne sagit pas du vingt-quatrime lment
du tableau, mais du vingt-cinquime ? Pour viter toute confusion, il est prf-
rable de dire que Tableau[0] est au dcalage 0 et correspond donc au premier
lment.
Figure 13.2
Erreurs dintervalle.
In
f
o
1m
1 2 3 4 5 6 7 8 9 10 11
2m 3m 4m 5m 6m 7m 8m 9m 10m
In
f
o
C++Livre Page 401 J eudi, 7. mai 2009 9:45 09
402 Le langage C++
Initialisation des tableaux
Lors de la dclaration dun tableau dlments de type prdni (entiers, caractres, par
exemple), vous pouvez en proter pour initialiser ses valeurs. Pour ce faire, vous devez
placer le signe gal (=) aprs la dclaration du tableau, suivi dune liste de valeurs entre
accolades, spares les unes des autres par des virgules. Exemple :
int TableauInt[5] = { 10, 20, 30, 40, 50 };
Cette instruction dclare TableauInt comme un tableau de cinq entiers et affecte la
valeur 10 TableauInt[0], la valeur 20 TableauInt[1], etc.
Si vous omettez de prciser la taille du tableau, le compilateur produira un tableau conte-
nant autant dlments que la liste et leur affectera les valeurs respectives. Par exemple,
lexpression :
int TableauInt[] = { 10, 20, 30, 40, 50 };
produit le mme tableau que prcdemment, un tableau de cinq lments.
Il est interdit dinitialiser plus dlments que nen contient le tableau. Exemple :
int TableauInt[5] = { 10, 20, 30, 40, 50, 60 };
Cette expression produira une erreur de compilation car vous avez tent daffecter six
valeurs aux cinq lments du tableau. En revanche, vous avez le droit dcrire :
int TableauInt[5] = { 10, 20 };
Ici, vous avez dclar un tableau de cinq lments et initialis uniquement les deux
premiers, TableauInt[0] et TableauInt[1].
Dclaration de tableaux
Pour linstant, nous avons utilis des "nombres magiques" : 3 pour la taille des tableaux de
sentinelles et 25 pour la taille de TableauCible. Il vaut mieux utiliser des constantes pour
pouvoir modier toutes ces valeurs un seul endroit.
Faire Ne pas faire
Laisser le compilateur calculer la taille des
tableaux initialiss.
Se rappeler que le premier lment dun
tableau porte le numro de dcalage zro.
crire au-del de la limite suprieure du
tableau.
Donner des noms confus aux tableaux. Les
noms doivent tre vocateurs, exactement
comme pour toute autre variable.
C++Livre Page 402 J eudi, 7. mai 2009 9:45 09
Chapitre 13 Tableaux et chanes 403
Un tableau peut porter un nom de variable, condition quil ne sagisse pas du nom dune
variable ou dun tableau existant dans la mme porte. Vous ne pouvez donc pas avoir la
fois un tableau Chats[5] et une variable Chats.
En outre, lors de la dclaration du nombre dlments, vous pouvez utiliser une constante
ou une numration. Cest mme prfrable des nombres littraux car cela permet de
regrouper au mme endroit le contrle du nombre des lments. Le Listing 13.2 utilisait
des nombres littraux : si vous souhaitez changer le TableauCible pour quil ne
contienne plus que vingt lments, vous deviez modier plusieurs lignes de code. Si vous
aviez utilis une constante, il sufsait de ne modier que la valeur de cette constante.
Indiquer le nombre dlments, ou la dimension, avec une numration est un peu diff-
rent. Le Listing 13.3 montre la cration dun tableau contenant une valeur par jour de la
semaine.
Listing 13.3 : Utilisation de constantes et dnumrations dans les tableaux
0: // Listing13.3
1: // Dimensionnement de tableaux
2: // laide de constantes et dnumrations
3: #include <iostream>
4: int main()
5: {
6: enum Semaine { Dim, Lun, Mar,
7: Mer, Jeu, Ven, Sam, NbJoursSemaine };
8: int TabSemaine[NbJoursSemaine] =
8a: { 10, 20, 30, 40, 50, 60, 70 };
9:
10: std::cout << "La valeur de Mardi est: "
10a << TabSemaine[Mar];
11: return 0;
12: }
Ce programme produit le rsultat suivant :
La valeur de Mardi est: 30
La ligne 6 cre lnumration Semaine, qui contient huit membres. Le premier lment
(Dim) et le dernier lment (NbJoursSemaine) correspondent respectivement 0 et 7.
La ligne 8 dclare le tableau TabSemaine de NbJoursSemaines lments, cest--dire 7.
La ligne 10 utilise la constante numre Mar comme indice du tableau. Cette constante
correspondant 2, llment dsign est donc le troisime tableau, TabSemaine[2].
La valeur obtenue est afche la ligne 10.
C++Livre Page 403 J eudi, 7. mai 2009 9:45 09
404 Le langage C++
Tableaux
Pour dclarer un tableau, indiquez le type des objets stocker, suivi du nom du tableau et
de la valeur dindice correspondant au nombre dlments.
Exemple 1
int TableauInt[90];
Exemple 2
long * TabPointeursSurLongs[100];
Pour accder aux lments du tableau, indiquez lindice souhait.
Exemple 1
// affecter le 9e lment de TableauInt neuviemeElt
int neuviemeElt = TableauInt[8];
Exemple 2
// affecter le 9e lment de TabPtrSurLongs pLong
long * pLong = TabPtrSurLongs[8];
Un tableau de n lments est toujours numrot de 0 n-1.
Tableaux dobjets
Nimporte quel objet, dun type prdni ou non, peut tre stock dans un tableau. Il suft
dindiquer au compilateur le type des lments stocker et leur nombre : le compilateur
pourra alors calculer lespace mmoire ncessaire pour chaque lment en fonction de la
dclaration de la classe. La classe des lments dun tableau doit comprendre un construc-
teur par dfaut sans paramtre, an que les objets puissent tre crs au moment de la
dnition du tableau.
Laccs aux donnes membres dun tableau dobjets se fait en deux tapes. On identie
dabord llment du tableau laide de loprateur dindexation ([ ]), puis on lui ajoute
loprateur daccs aux membres (.). Le Listing 13.4 montre comment crer et manipuler
un tableau de cinq objets Chat.
Listing 13.4 : Cration dun tableau dobjets
0: // Listing13.4 - Un tableau dobjets
1:
2: #include <iostream>
3: using namespace std;
C++Livre Page 404 J eudi, 7. mai 2009 9:45 09
Chapitre 13 Tableaux et chanes 405
4:
5: class Chat
6: {
7: public:
8: Chat() { sonAge = 1; sonPoids = 5; }
9: ~Chat() {}
10: int GetAge() const { return sonAge; }
11: int GetPoids() const { return sonPoids; }
12: void SetAge(int age) { sonAge = age; }
13:
14: private:
15: int sonAge;
16: int sonPoids;
17: };
18:
19: int main()
20: {
21: Chat Portee[5];
22: int i;
23: for (i = 0; i < 5; i++)
24: Portee[i].SetAge(2*i +1);
25:
26: for (i = 0; i < 5; i++)
27: {
28: cout << "Chat numro " << i+1 << ": ";
29: cout << Portee[i].GetAge() << endl;
30: }
31: return 0;
32: }
Ce programme produit le rsultat suivant :
Chat numro 1: 1
Chat numro 2: 3
Chat numro 3: 5
Chat numro 4: 7
Chat numro 5: 9
La classe Chat est dclare de la ligne 5 la ligne 17. Elle doit possder un constructeur
par dfaut pour que ses objets puissent tre crs dans un tableau. Ici, ce constructeur par
dfaut est dclar et dni la ligne 8. Pour chaque Chat, lge par dfaut est x 1 et
le poids par dfaut 5. Noubliez pas que si vous dnissez un autre constructeur, le
constructeur par dfaut ne sera pas produit par le compilateur (ce qui vous oblige le crer
vous-mme).
C++Livre Page 405 J eudi, 7. mai 2009 9:45 09
406 Le langage C++
La premire boucle for xe lge des cinq objets du tableau (lignes 23 et 24), la seconde
(lignes 26 30) accde chaque lment et appelle sa mthode GetAge(), ce qui permet
dafcher lge de tous les chats.
La mthode GetAge() de chaque chat est appele en accdant chacun des lments du
tableau Portee, suivi de loprateur point (.) et du nom de la fonction membre. Vous
pouvez accder dautres membres et mthodes de la mme manire.
Tableaux multidimensionnels
Un tableau peut avoir plus dune dimension. Chacune delles est reprsente par un indice
du tableau : un tableau bidimensionnel aura donc deux indices, un tableau tridimensionnel
trois indices, et ainsi de suite. Vous pouvez crer autant de dimensions que vous le souhaitez,
bien quen pratique la plupart de vos tableaux nauront quune ou deux dimensions.
Un chiquier est un bon exemple de tableau deux dimensions. Une dimension reprsente
les 8 lignes, lautre les 8 colonnes (voir Figure 13.3).
Supposons que vous ayez une classe CARRE. La dclaration dun tableau Echiquier serait
alors :
CARRE Echiquier[8][8];
Vous pourriez galement reprsenter cet chiquier comme un tableau une dimension de
64 carrs :
CARRE Echiquier[64];
Cependant, ce tableau ne correspondrait pas autant un vritable chiquier quun tableau
deux dimensions. Lorsque la partie commence, le roi se situe la quatrime position de
Figure 13.3
Lchiquier est un tableau
bidimensionnel.
1
2
3
4
5
6
7
8
2 3 4 5 6 7 8
8 8 8 8 8 8 8 8
7
6
5
4
3
2
1
0
7
6
5
4
3
2
1
1
7
6
5
4
3
2
1
2
7
6
5
4
3
2
1
3
7
6
5
4
3
2
1
4
7
6
5
4
3
2
1
5
7
6
5
4
3
2
1
6
7
6
5
4
3
2
1
7
8
7
6
5
4
3
2
1
8
C++Livre Page 406 J eudi, 7. mai 2009 9:45 09
Chapitre 13 Tableaux et chanes 407
la premire ligne. La numrotation commenant zro, cette position sexprime donc
ainsi :
Echiquier[0][3];
en supposant que le premier indice correspond aux lignes et le second aux colonnes.
Initialisation de tableaux plusieurs dimensions
Comme les tableaux une dimension, les tableaux plusieurs dimensions peuvent tre
initialiss. Les valeurs sont affectes dans lordre des lments, le dernier indice (celui qui
est le plus droite) voluant pendant que le prcdent reste stable (comme un compteur
kilomtrique). Si vous avez la dclaration suivante :
int Tableau[5][3];
les trois premiers lments iront donc dans Tableau[0], les trois suivants dans
Tableau[1], etc. :
int Tableau[5][3] = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 };
Pour plus de clart, vous pouvez regrouper les diffrentes initialisations entre accolades.
Exemple :
int Tableau[5][3] = { {1,2,3},
{4,5,6},
{7,8,9},
{10,11,12},
{13,14,15} };
Le compilateur ne tient pas compte des accolades internes, mais elles clarient la rpartition
des nombres.
Lorsque vous initialisez les lments dun tableau, chaque valeur doit tre spare de la
suivante par une virgule, indpendamment des accolades. Linitialisation complte doit
gurer entre accolades et se terminer par un point-virgule.
Le Listing 13.5 cre un tableau deux dimensions, dont la premire est un ensemble de
nombres de 0 4. La seconde regroupe les doubles des valeurs de la premire.
Listing 13.5 : Cration dun tableau plusieurs dimensions
0: // Listing13.5 - Cration dun tableau plusieurs dimensions
1: #include <iostream>
2: using namespace std;
C++Livre Page 407 J eudi, 7. mai 2009 9:45 09
408 Le langage C++
3:
4: int main()
5: {
6: int UnTableau[2][5] =
6a: { {0,1,2,3,4}, {0,2,4,6,8}};
7: for (int i = 0; i < 2; i++)
8: {
9: for (int j = 0; j < 5; j++)
10: {
11: cout << "UnTableau[" << i << "][" << j << "]: ";
12: cout << UnTableau[i][j]<< endl;
13: }
14: }
15: return 0;
16: }
Ce programme produit le rsultat suivant :
UnTableau[0][0]: 0
UnTableau[0][1]: 1
UnTableau[0][2]: 2
UnTableau[0][3]: 3
UnTableau[0][4]: 4
UnTableau[1][0]: 0
UnTableau[1][1]: 2
UnTableau[1][2]: 4
UnTableau[1][3]: 6
UnTableau[1][4]: 8
La ligne 6 dclare UnTableau comme un tableau deux dimensions. La premire dimen-
sion indique quil y aura deux ensembles de donnes et la deuxime est forme de cinq
entiers. Cela cre donc une matrice de 2 lignes par 5 colonnes, comme le montre la
Figure 13.4
Les valeurs sont initialises sur la base des deux ensembles de nombres. Le premier
comprend les nombres initiaux, le deuxime les nombres doubls. Dans ce listing, les
valeurs sont simplement dnies, mais elles pourraient tre calcules. Les lignes 7 et 9
Figure 13.4
Tableau de 2 par 5.
0
1
2
3
4
0
UnTableau[5] [2]
1
2
3
4
C++Livre Page 408 J eudi, 7. mai 2009 9:45 09
Chapitre 13 Tableaux et chanes 409
crent deux boucles for imbriques. La boucle externe ( partir de la ligne 7) traite chaque
membre de la premire dimension (chacun des deux ensembles), puis appelle la boucle
interne ( partir de la ligne 9) qui traite chaque lment de la seconde dimension, comme
le montrent les valeurs afches lcran. UnTableau[0][0] est donc suivi de UnTa-
bleau[0][1]. Lindice de la premire dimension est incrment uniquement aprs lincr-
mentation de lindice de la seconde dimension. Le traitement recommence alors.
Tableaux et mmoire
La dclaration dun tableau indique explicitement le nombre dlments qui le compo-
sent. Le compilateur se charge alors de rserver un espace sufsant en mmoire vive,
mme si vous nen faites pas usage. Cette opration ne pose aucun problme lorsque vous
savez exactement combien le tableau contiendra dobjets. Par exemple, un chiquier se
compose de 64 cases, alors quune porte comprend entre un et dix matous. Si vous
navez aucune ide du nombre dlments que devra contenir un tableau, vous devez
faire appel des structures de donnes plus complexes.
Cet ouvrage traite des tableaux de pointeurs, des tableaux allous sur le tas et dun
certain nombre dautres collections. Vous dcouvrirez quelques structures de donnes
complexes, mais vous en apprendrez plus en lisant C++ Unleashed, publi par Sams
Publishing. Vous pouvez galement consulter lannexe E de cet ouvrage.
Deux aspects importants de la programmation sont quil y a toujours plus de choses
apprendre et quil existe toujours des livres permettant de les apprendre.
Construire des tableaux de pointeurs
Jusqu prsent, les tableaux utiliss stockaient leurs lments dans la pile et, comme vous
le savez, lespace disponible dans la pile est limit. Pour plus de libert, vous pouvez
dclarer les objets sur le tas et ne stocker dans le tableau que des pointeurs vers ces objets.
La quantit requise despace mmoire sen trouvera ainsi considrablement rduite. Le
Listing 13.6 effectue les mmes oprations que le programme du Listing 13.4, mais stocke
tous les objets sur le tas. Le tableau peut alors passer de 5 500 lments. Il sappelle
dsormais Famille et non plus Portee.
Listing 13.6 : Stockage dun tableau dans le tas
0: // Listing13.6 - Un tableau de pointeurs sur les objets
1:
2: #include <iostream>
3: using namespace std;
4:
C++Livre Page 409 J eudi, 7. mai 2009 9:45 09
410 Le langage C++
5: class Chat
6: {
7: public:
8: Chat() { sonAge = 1; sonPoids = 5; }
9: ~Chat() {} // destructeur
10: int GetAge() const { return sonAge; }
11: int GetPoids() const { return sonPoids; }
12: void SetAge(int age) { sonAge = age; }
13:
14: private:
15: int sonAge;
16: int sonPoids;
17: };
18:
19: int main()
20: {
21: Chat * Famille[500];
22: int i;
23: Chat * pChat;
24: for (i = 0; i < 500; i++)
25: {
26: pChat = new Chat;
27: pChat->SetAge(2*i +1);
28: Famille[i] = pChat;
29: }
30:
31: for (i = 0; i < 500; i++)
32: {
33: cout << "Chat numro " << i+1 << ": ";
34: cout << Famille[i]->GetAge() << endl;
35: }
36: return 0;
37: }
Ce programme produit le rsultat suivant :
Chat numro 1: 1
Chat numro 2: 3
Chat numro 3: 5
...
Chat numro 499: 997
Chat numro 500: 999
Lobjet Chat dclar de la ligne 5 la ligne 17 est identique celui du Listing 13.4. Cette
fois-ci, cependant, le tableau sappelle Famille et comprend 500 lments, qui sont des
pointeurs vers des objets de la classe Chat.
C++Livre Page 410 J eudi, 7. mai 2009 9:45 09
Chapitre 13 Tableaux et chanes 411
Dans la boucle initiale (de la ligne 24 la ligne 29), les 500 objets Chat sont crs sur le
tas. Lge de chacun est gal deux fois la valeur dindice, plus un : lge du premier objet
Chat est gal 1, le deuxime 3, le troisime 5, etc. Aprs la cration du pointeur, la
ligne 28 affecte le pointeur au tableau. Le tableau ayant t dclar comme un tableau de
pointeurs, cest le pointeur et non la valeur drfrence de ce dernier qui est ajout au
tableau.
La seconde boucle permet dafcher les valeurs du tableau (lignes 31 35). La ligne 33
afche un nombre pour indiquer lobjet concern. Les dcalages dindice partant de zro,
la ligne 33 ajoute 1 pour un comptage commenant 1. la ligne 34, on accde au poin-
teur laide de lindice i du tableau Famille. Cette adresse permet alors dappeler la
mthode GetAge() de lobjet concern.
Dans cet exemple, le tableau Famille et ses pointeurs sont stocks dans la pile, alors que
les 500 objets Chat sont crs sur le tas.
Larithmtique des pointeurs, un sujet complexe
Les pointeurs ont t introduits au Chapitre 8. Avant de poursuivre avec les tableaux, revenons
un instant sur ce sujet pour comprendre larithmtique des pointeurs.
Les pointeurs ne permettent que peu de choses du point de vue mathmatique ; ils peuvent
tre soustraits les uns des autres. Une technique puissante consiste faire pointer deux
pointeurs vers des lments diffrents dun tableau et calculer la diffrence entre ces
deux pointeurs pour savoir combien dlments se trouvent entre les deux. Cette technique
est particulirement utile pour analyser des tableaux de caractres, comme le montre le
Listing 13.7.
Listing 13.7 : Analyse des mots partir dune chane de caractres
0: #include <iostream>
1: #include <ctype.h>
2: #include <string.h>
3:
4: bool LireMot(char* chaine,
5: char* mot, int& motDecale);
6:
7: // Programme principal
8: int main()
9: {
10: const int tailleTampon = 255;
11: char tampon[tailleTampon+1]; // contient la chane
12: char mot[tailleTampon+1]; // contient le mot
C++Livre Page 411 J eudi, 7. mai 2009 9:45 09
412 Le langage C++
13: int motDecale = 0; // commencer au dbut
14:
15: std::cout << "Entrez une chane: ";
16: std::cin.getline(tampon, tailleTampon);
17:
18: while (LireMot(tampon, mot, motDecale))
19: {
20: std::cout << "Mot obtenu: " << mot << std::endl;
21: }
22: return 0;
23: }
24:
25: // Fonction pour analyser les mots dune chane.
26: bool LireMot(char* chaine, char* mot, int& motDecale)
27: {
28: if (chaine[motDecale] == 0) // fin de la chane?
29: return false;
30:
31: char *p1, *p2;
32: p1 = p2 = chaine + motDecale; // pointe sur le mot suivant
33:
34: // effacer les premiers espaces
35: for (int i = 0; i < (int)strlen(p1) &&!isalnum(p1[0]);
35a: i++)
36: p1++;
37:
38: // voir sil y a un mot
39: if (!isalnum(p1[0]))
40: return false;
41:
42: // p1 pointe maintenant pour commencer le mot suivant
43: // point p2 ici aussi
44: p2 = p1;
45:
46: // amener p2 la fin du mot
47: while (isalnum(p2[0]))
48: p2++;
49:
50: // p2 est maintenant la fin du mot
51: // p1 est au dbut du mot
52: // la longueur du mot = la diffrence
53: int lg = int (p2 - p1);
54:
55: // copier le mot dans le tampon
56: strncpy (mot, p1, lg);
57:
58: // null termine la chane
C++Livre Page 412 J eudi, 7. mai 2009 9:45 09
Chapitre 13 Tableaux et chanes 413
59: mot[lg] = \0;
60:
61: // trouver le dbut du mot suivant
62: for (int j = int(p2 - chaine); j < (int)strlen(chaine)
63: &&!isalnum(p2[0]); j++)
64: {
65: p2++;
66: }
67:
68: motDecale = int(p2 - chaine);
69:
70: return true;
71: }
Ce programme produit le rsultat suivant :
Entrez une chane : ce code est apparu pour la premiere fois dans C++ Report
Mot obtenu : ce
Mot obtenu : code
Mot obtenu : est
Mot obtenu : apparu
Mot obtenu : pour
Mot obtenu : la
Mot obtenu : premiere
Mot obtenu : fois
Mot obtenu : dans
Mot obtenu : C
Mot obtenu : Report
Ce programme permet de saisir une phrase, qui est ensuite dcompose en mots (ensem-
bles de caractres alphanumriques). La ligne 15 demande lutilisateur dentrer une
chane, qui est ensuite passe la mthode LireMot() ligne 18 avec un tampon permettant
de conserver le premier mot et une variable entire appele motDecale, initialise zro
la ligne 13.
LireMot() renvoie chaque mot de la chane. Ces mots sont afchs la ligne 20 jusqu ce
que LireMot() renvoie false.
Chaque appel LireMot() entrane un saut la ligne 26. La ligne 28 teste si la valeur de
chaine[motDecale] est gale zro. Ce sera vrai si vous tes la n de la chane, auquel
cas LireMot() renvoie false. cin.getline() vrie que la chane entre se termine par
un caractre nul, cest--dire le caractre \0.
La ligne 31 dclare deux pointeurs de caractres, p1 et p2. Ils sont initialis la ligne 32
an de pointer lindice motDecale de la chane. Initialement, motDecale valant zro, ces
pointeurs pointent donc au dbut de la chane.
C++Livre Page 413 J eudi, 7. mai 2009 9:45 09
414 Le langage C++
Les lignes 35 et 36 parcourent la chane, en poussant p1 jusquau premier caractre alpha-
numrique. Les lignes 39 et 40 sassurent que lon en trouve un, sinon false est renvoy.
p1 pointe maintenant sur le dbut du mot suivant et la ligne 44 initialise p2 de sorte quil
pointe vers cette mme position.
Les lignes 47 et 48 font parcourir le mot par p2 an quil sarrte au premier caractre qui
nest pas alphanumrique. p2 pointe donc maintenant la n du mot dont le dbut est
point par p1. En soustrayant p1 de p2 (ligne 53) et en transtypant le rsultat en un entier,
on peut donc connatre la longueur du mot, qui est ensuite copie dans le tampon mot
laide dune fonction de copie de chane fournie par la bibliothque standard.
La ligne 59 ajoute un caractre nul pour marquer la n du mot. Puis, p2est incrment
pour pointer au dbut du mot suivant et le dcalage de ce mot est pouss dans la rfrence
entire motDecale. Enn, true est renvoy pour indiquer quon a trouv un mot.
Il sagit dun exemple classique de code qui est plus comprhensible si lon utilise un
dbogueur an de suivre son excution pas pas.
Larithmtique des pointeurs est employe divers endroits de ce listing. En soustrayant
un pointeur dun autre (comme la ligne 53), on dtermine le nombre dlments qui les
spare. En outre, la ligne 55 montre que lincrmentation dun pointeur le fait passer
llment suivant du tableau au lieu de simplement lui ajouter un. Larithmtique des poin-
teurs est trs utilise avec les pointeurs et les tableaux, mais elle reprsente aussi une acti-
vit dangereuse et doit donc tre considre avec circonspection.
Dclaration de tableaux sur le tas
Il est possible de stocker un tableau entier sur le tas. Pour cela, vous devez crer un pointeur
vers le tableau en utilisant le mot-cl new et loprateur dindexation. Vous obtenez ainsi un
pointeur vers une zone du tas contenant le tableau. La ligne suivante, par exemple :
Chat *Famille = new Chat[500];
dclare Famille comme un pointeur sur le premier lment dun tableau de 500 objets
Chat. En dautres termes, Famille pointe sur ou contient ladresse de Famille[0].
Lavantage de dclarer Famille de cette faon est que cela vous permet dutiliser larith-
mtique des pointeurs pour accder chaque lment de Famille. Vous pouvez par
exemple crire :
Chat *Famille = new Chat[500];
Chat *pChat = Famille; // pChat pointe sur Famille[0]
pChat->SetAge(10); // dfinit Famille[0] 10
pChat++; // pointe sur Famille[1]
pChat->SetAge(20); // dfinit Famille[1] 20
C++Livre Page 414 J eudi, 7. mai 2009 9:45 09
Chapitre 13 Tableaux et chanes 415
Ces lignes dclarent un tableau de 500 objets Chat et un pointeur vers le premier lment
de ce tableau. En utilisant ce pointeur, on peut appeler la mthode SetAge() en lui passant
la valeur 10. Puis, ce pointeur est incrment pour dsigner lobjet Chat suivant du
tableau. On appelle ensuite nouveau la mthode SetAge() en lui passant cette fois-ci la
valeur 20.
Pointeurs sur tableau et tableaux de pointeurs
Examinez les trois dclarations suivantes :
1: Chat Famille1[500];
2: Chat * Famille2[500];
3: Chat * Famille3 = new Chat[500];
Famille1 est un tableau de 500 objets Chat, alors que Famille2 est un tableau de
500 pointeurs sur des objets de la classe Chat. Enn, Famille3 est un pointeur sur un
tableau contenant 500 objets Chat.
Les diffrences entre ces trois lignes de code affectent normment le comportement de
ces tableaux. Ce qui peut sembler le plus surprenant est que Famille3 est une variante
de Famille1, alors quil est trs diffrent de Famille2.
Ceci soulve le problme pineux de la relation entre les pointeurs et les tableaux. Dans le
troisime exemple, Famille3 est un pointeur sur un tableau. Ladresse contenue dans
ce pointeur correspond ladresse du premier lment du tableau et cest exactement la
mme chose pour Famille1.
Pointeurs et noms de tableaux
En C++, un nom de tableau est un pointeur constant sur le premier lment du tableau.
Par consquent, dans la dclaration suivante :
Chat Famille[50];
Famille est un pointeur sur &Famille[0], qui est ladresse du premier lment du tableau
Famille.
Vous avez le droit dutiliser des noms de tableaux comme des pointeurs constants et rci-
proquement. Famille+4, par exemple, est une expression permettant daccder llment
Famille[4].
Le compilateur se charge deffectuer les calculs lorsque vous ajoutez, incrmentez et
dcrmentez des pointeurs. Lorsque vous crivez Famille+4, ladresse obtenue ne se
C++Livre Page 415 J eudi, 7. mai 2009 9:45 09
416 Le langage C++
situe pas 4 octets plus loin, mais 4 objets plus loin. Si chaque objet occupe 4 octets,
Famille+4 indiquera donc un dplacement de 16 octets par rapport au dbut du tableau.
Si chaque objet Chat contient 4 variables membres de type long et 2 variables membres
de type short, la taille dun chat sera donc de 20 octets ((44)+(22)). En ce cas,
Famille+4 correspondra un dplacement de 80 octets aprs le dbut du tableau.
Le Listing 13.8 montre comment dclarer et utiliser un tableau stock sur le tas.
Listing 13.8 : Cration dun tableau sur le tas laide de new
0: // Listing13.8 - Cration dun tableau sur le tas
1:
2: #include <iostream>
3:
4: class Chat
5: {
6: public:
7: Chat() { sonAge = 1; sonPoids = 5; }
8: ~Chat();
9: int GetAge() const { return sonAge; }
10: int GetPoids() const { return sonPoids; }
11: void SetAge(int age) { sonAge = age; }
12:
13: private:
14: int sonAge;
15: int sonPoids;
16: };
17:
18: Chat:: ~Chat()
19: {
20: // std::cout << "Appel du destructeur!\n";
21: }
22:
23: int main()
24: {
25: Chat * Famille = new Chat[500];
26: int i;
27:
28: for (i = 0; i < 500; i++)
29: {
30: Famille[i].SetAge(2*i +1);
31: }
32:
33: for (i = 0; i < 500; i++)
34: {
35: std::cout << "Chat numro " << i+1 << ": ";
C++Livre Page 416 J eudi, 7. mai 2009 9:45 09
Chapitre 13 Tableaux et chanes 417
36: std::cout << Famille[i].GetAge() << std::endl;
37: }
38:
39: delete [] Famille;
40:
41: return 0;
42: }
Ce programme produit le rsultat suivant :
Chat numro 1: 1
Chat numro 2: 3
Chat numro 3: 5
...
Chat numro 499: 997
Chat numro 500: 999
La ligne 25 dclare Famille, qui est un pointeur vers un tableau de 500 objets Chat. Le tableau
complet est cr sur le tas par un appel new Chat[500].
En ligne 30, vous pouvez constater que le pointeur que vous avez dclar peut tre utilis
avec loprateur dindexation [] et quil peut donc tre manipul comme un tableau ordi-
naire. La ligne 36 lutilise nouveau pour appeler la mthode GetAge(). Dun point de
vue pratique, vous pouvez considrer ce pointeur vers le tableau Famille comme un nom
de tableau, mais il ne faut pas oublier de librer la mmoire que vous aviez alloue lors de
la conguration du tableau. Cette opration est ralise la ligne 39 par un appel
delete.
Suppression des tableaux stocks sur le tas
Que devient la mmoire alloue aux objets Chat lorsque le tableau est dtruit ? Il y a-t-il
un risque de fuite mmoire ?
La suppression de Famille libre automatiquement toute la mmoire alloue au tableau si
vous utilisez delete avec loprateur []. Lorsquil voit les crochets, le compilateur est
sufsamment intelligent pour savoir quil doit dtruire chaque objet du tableau et restituer
sa mmoire au tas.
Pour le constater, modiez la taille du tableau pour la faire passer de 500 10 lments
(lignes 27, 28 et 33). Puis supprimez les barres obliques de commentaire devant linstruc-
tion cout, la ligne 20. Lorsque le programme atteint la ligne 39 et dtruit le tableau, vous
verrez apparaitre les diffrents appels aux destructeurs des objets Chat.
C++Livre Page 417 J eudi, 7. mai 2009 9:45 09
418 Le langage C++
Lorsquun lment est cr sur le tas laide du mot-cl new, il doit toujours tre supprim
et sa mmoire libre avec mot-cl delete. De mme, si vous crez un tableau laide de
new <classe>[taille], vous devez supprimer le tableau et librer sa mmoire laide
de delete[]. Les crochets indiquent au compilateur que lobjet supprimer est un
tableau.
Si vous omettez les crochets, seul le premier objet du tableau sera supprim. Amusez-vous
supprimer les crochets de linstruction delete gurant la ligne 39. Si vous avez modi-
la ligne 20 pour que lappel du destructeur soit afch, vous pourrez constater quun
seul objet Chat est supprim. Flicitations ! Vous venez de produire une fuite mmoire.
Redimensionner les tableaux en cours dexcution
Le plus gros avantage dallouer des tableaux sur le tas est quil est possible de dterminer
la taille du tableau au moment de lexcution, avant de lallouer. Si, par exemple, vous
avez demand lutilisateur dentrer la taille dune famille dans une variable appele
TailleFamille, vous pourrez ensuite dclarer un tableau Chat de la faon suivante :
Chat *pFamille = new Chat[TailleFamille];
Vous avez maintenant un pointeur vers un tableau dobjets Chat. Vous pouvez ensuite
crer un pointeur vers le premier lment et parcourir ce tableau laide dun pointeur et
de larithmtique des pointeurs :
Chat *pChatActuel = Famille[0];
for ( int i = 0; i < TailleFamille; i++, pChatActuel++ )
{
pChatActuel->SetAge(i);
};
C++ considrant les tableaux comme des pointeurs spciaux, vous pouvez ignorer le
deuxime pointeur et utiliser simplement lindexation classique des tableaux :
for (int i = 0; i < TailleFamille; i++)
{
pFamille[i].SetAge(i);
};
Lutilisation des crochets drfrence automatiquement le pointeur et le compilateur
ralise le calcul appropri en utilisant larithmtique des pointeurs. Vous pouvez ga-
lement utiliser une technique semblable pour redimensionnement le tableau en cours
dexcution si vous manquez de place, comme dans le Listing 13.9.
C++Livre Page 418 J eudi, 7. mai 2009 9:45 09
Chapitre 13 Tableaux et chanes 419
Listing 13.9 : Rallocation dun tableau en cours dexcution
0: //Listing13.9
1:
2: #include <iostream>
3: using namespace std;
4: int main()
5: {
6: int TailleAllocation = 5;
7: int *pTableauDeNombres = new int[TailleAllocation];
8: int ElementsUtilises = 0;
9: int ElementsMaximumAutorises = TailleAllocation;
10: int NombreSaisi = -1;
11:
12: cout << endl << "Nombre suivant = ";
13: cin >> NombreSaisi;
14:
15: while ( NombreSaisi > 0 )
16: {
17: pTableauDeNombres[ElementsUtilises++] = NombreSaisi;
18:
19: if ( ElementsUtilises == ElementsMaximumAutorises )
20: {
21: int *pGrandTableau =
22: new int[ElementsMaximumAutorises+TailleAllocation];
23:
24: for ( int IndiceCopie = 0;
25: IndiceCopie < ElementsMaximumAutorises;
26: IndiceCopie ++ )
27: {
28: pGrandTableau[IndiceCopie] =
28a pTableauDeNombres[IndiceCopie];
29: }
30:
31: delete [] pTableauDeNombres;
32: pTableauDeNombres = pGrandTableau;
33: ElementsMaximumAutorises+= TailleAllocation;
34: }
35: cout << endl << "Nombre suivant = ";
36: cin >> NombreSaisi;
37: }
38:
39: for (int Index = 0; Index < ElementsUtilises; Index++)
40: {
41: cout << pTableauDeNombres[Index] << endl;
42: }
43: return 0;
44: }
C++Livre Page 419 J eudi, 7. mai 2009 9:45 09
420 Le langage C++
Ce programme produit le rsultat suivant :
Nombre suivant = 10
Nombre suivant = 20
Nombre suivant = 30
Nombre suivant = 40
Nombre suivant = 50
Nombre suivant = 60
Nombre suivant = 70
Nombre suivant = 0
10
20
30
40
50
60
70
Dans cet exemple, les nombres sont saisis les uns aprs les autres et stocks dans un tableau.
Lorsquun nombre saisi est infrieur ou gal 0, le tableau qui a t constitu est afch.
Les lignes 6 9 dclarent plusieurs variables. Plus prcisment, la taille initiale du tableau
est xe 5 la ligne 6, puis le tableau est allou la ligne 7 et son adresse est affecte
pTableauDeNombres.
Les lignes 12 et 13 rcuprent le premier nombre entr et le placent dans la variable
NombreSaisi. la ligne 15, si le nombre entr est suprieur zro, le traitement a lieu.
Dans le cas contraire, le programme passe la ligne 38.
La ligne 17 place NombreSaisi dans le tableau. Ceci ne posera pas de problme la
premire fois car vous savez quil y a de la place. La ligne 19 teste si cest le dernier
lment pour lequel le tableau a de la place. Sil reste de la place, le contrle passe la
ligne 35 ; sinon, le corps de linstruction if est excut pour augmenter la taille du tableau
(lignes 20-34).
Un nouveau tableau est cr la ligne 21 pour contenir cinq lments (TailleAlloca-
tion) de plus que le tableau courant. Les lignes 24 29 copient ensuite lancien tableau
dans le nouveau, laide de la notation des tableaux (vous pourriez utiliser larithmtique
des pointeurs).
C++Livre Page 420 J eudi, 7. mai 2009 9:45 09
Chapitre 13 Tableaux et chanes 421
La ligne 31 supprime lancien tableau et la ligne 32 remplace lancien pointeur par celui
du nouveau tableau. La ligne 33 augmente ElementsMaximumAutorises pour ladapter
la nouvelle taille.
Les lignes 39 42 afchent le rsultat.
Tableaux de caractres et chanes
Une chane "de type C" est un tableau de caractres qui se termine par un caractre nul.
Jusqu prsent, les seules chanes de ce type que vous avez rencontres taient les
constantes chanes anonymes utilises dans les instructions dafchage comme celle-ci :
cout << "bonjour";
Vous pouvez dclarer et initialiser une chane de type C comme vous le feriez pour
nimporte quel tableau. Voici un exemple :
char Salut[] = { b, o, n, j, o, u ,r,\0 };
Ici, Salut est dclar comme un tableau de caractres et initialis avec plusieurs caractres.
Le dernier caractre, \0, est le caractre nul, qui est le caractre de terminaison des
chanes de type C. Bien que cette approche caractre par caractre fonctionne, elle est
difcile saisir et est bien trop sujette aux erreurs de frappe. C permet dutiliser la forme
suivante, qui est un quivalent raccourci de la prcdente :
char Salut[] = "Bonjour";
Il faut noter deux choses dans cette syntaxe :
Les caractres entre apostrophes simples, spars par des virgules, ont t remplacs
par une chane de caractres entre guillemets, sans accolades.
Il nest pas ncessaire dajouter le caractre nul la n car le compilateur sen charge
pour vous.
Faire Ne pas faire
Se rappeler quun tableau de n lments est
indic de 0 n-1.
Utiliser lindexation des tableaux avec les
pointeurs qui pointent sur des tableaux.
Utiliser delete[] pour supprimer un tableau
cr sur le tas. delete (sans les crochets) ne
supprime que le premier lment.
crire ou lire aprs la n dun tableau.
Confondre un tableau de pointeurs et un poin-
teur sur un tableau.
Oublier de supprimer la mmoire alloue
avec new.
C++Livre Page 421 J eudi, 7. mai 2009 9:45 09
422 Le langage C++
Lorsque vous dclarez une chane, vous devez vrier quelle pourra contenir ce que vous
souhaitez y mettre. La longueur dune chane de type C est le nombre de ses caractres, y
compris le caractre nul. La chane Bonjour occupe donc 8 octets : le mot occupe 7 octets
et le caractre nul 1 octet.
Vous pouvez aussi crer des tableaux de caractres non initialiss. Comme pour tout autre
tableau, vous devez vrier que vous nen mettez pas plus quil y a dlments rservs.
Le Listing 13.10 met en uvre un tampon non initialis.
Listing 13.10 : Remplissage dun tableau
0: //Listing13.10 Tampons de caractres
1:
2: #include <iostream>
3:
4: int main()
5: {
6: char tampon[80];
7: std::cout << "Entrez la chane: ";
8: std::cin >> tampon;
9: std::cout << "Contenu : " << tampon << std::endl;
10: return 0;
11: }
Ce programme produit le rsultat suivant :
Entrez la chane: Bonjour les amis
Contenu : Bonjour
La ligne 6 indique que le tampon peut recevoir 80 caractres, soit 79 caractres et 1 carac-
tre nul.
La ligne suivante invite lutilisateur entrer une chane qui est stocke dans le tampon (ligne 8).
cin place un caractre nul immdiatement aprs la n du mot transfr dans le tampon.
Ce programme contient deux erreurs potentielles. La premire est que si lutilisateur tape
plus de 79 caractres, cin crira aprs la n du tampon. La seconde est quun caractre
despacement est considr par cin comme une n de chane et quil arrte alors dcrire
dans le tampon.
Pour rsoudre ces problmes, vous devez appeler la mthode spciale get() sur cin, en
lui passant les trois paramtres suivants :
le tampon remplir ;
le nombre maximal de caractres lire ;
le dlimiteur de n de saisie.
C++Livre Page 422 J eudi, 7. mai 2009 9:45 09
Chapitre 13 Tableaux et chanes 423
Par dfaut, le dlimiteur est le caractre nouvelle ligne, comme le montre le Listing 13.11.
Listing 13.11 : Remplir un tableau avec un nombre maximal de caractres
0: //Listing13.11 - Utiliser cin.get()
1:
2: #include <iostream>
3: using namespace std;
4:
5: int main()
6: {
7: char tampon[80];
8: cout << "Entrez une chane: ";
9: cin.get(tampon, 79); // Lit 79 car max ou nouvelle ligne
10: cout << "Contenu du tampon: " << tampon << endl;
11: return 0;
12: }
Ce programme produit le rsultat suivant :
Entrez une chane: Bonjour les amis
Contenu du tampon: Bonjour les amis
Le tampon dclar la ligne 7 est pass comme premier paramre la mthode get() de
cin (ligne 9). Le second paramtre correspond au nombre maximum de caractres lire.
Ici, il ne doit pas dpasser 79 caractres pour laisser une place au caractre nul. Il est
inutile de fournir un caractre de terminaison car, ici, la valeur par dfaut (nouvelle ligne)
suft.
Si vous entrez des espaces, des tabulations ou dautres sortes despaces, ils seront affects
la chane. La saisie se terminera si vous entrez un caractre de nouvelle ligne ou que
vous avez saisi 79 caractres. Vous pouvez le vrier en relanant ce programme et en
essayant dentrer une chane suprieure 79 caractres.
Les fonctions strcpy() et strncpy()
La bibliothque C++ fournit plusieurs fonctions permettant de traiter des chanes. C++
hrite de son anctre C les fonctions de manipulations des chanes de type C. Parmi elles,
strcpy() et strncpy() permettent de copier une chane vers une autre. strcpy() copie
le contenu entier dune chane dans un tampon, tandis que strncpy() ne copie quun
nombre donn de caractres. Le Listing 13.12 montre comment utiliser strcpy().
C++Livre Page 423 J eudi, 7. mai 2009 9:45 09
424 Le langage C++
Listing 13.12 : Utilisation de la fonction strcpy()
0: //Listing13.12 Utiliser strcpy()
1:
2: #include <iostream>
3: #include <string.h>
4: using namespace std;
5:
6: int main()
7: {
8: char Chaine1[] = "Nul nest prophete en son pays";
9: char Chaine2[80];
10:
11: strcpy(Chaine2, Chaine1);
12:
13: cout << "Chaine1: " << Chaine1 << endl;
14: cout << "Chaine2: " << Chaine2 << endl;
15: return 0;
16: }
Ce programme produit le rsultat suivant :
Chaine1: Nul nest prophete en son pays
Chaine2: Nul nest prophete en son pays
Ce programme est relativement simple. Il copie les donnes dune chane dans une autre.
Le chier en-tte string.h inclus la ligne 3 contient le prototype de la fonction
strcpy(). Cette dernire prend en paramtre deux tableaux de caractres, le tableau cible
suivi du tableau source de la copie. La ligne 11 appelle cette fonction pour Chaine1 en
Chaine2.
Le fonction strcpy() prsente un inconvnient majeur puisquelle peut copier les caract-
res au-del du dernier lment du tableau cible si le tableau source est plus grand. strn-
cpy() permet de remdier ce problme puisquelle permet de limiter le nombre des
caractres copis. Elle effectue donc la copie dans le tampon de destination jusqu
rencontrer un caractre nul ou atteindre le nombre maximal de caractres dans le tampon
source. Le Listing 13.13 montre comment utiliser cette fonction.
Listing 13.13 : Utilisation de la fonction strncpy()
0: //Listing13.13 Utiliser strncpy()
1:
2: #include <iostream>
3: #include <string.h>
4:
C++Livre Page 424 J eudi, 7. mai 2009 9:45 09
Chapitre 13 Tableaux et chanes 425
5: int main()
6: {
7: const int LongMax = 80;
8: char Chaine1[] = "Nul nest prophete en son pays";
9: char Chaine2[LongMax+1];
10:
11: strncpy(Chaine2, Chaine1, LongMax);
12:
13: std::cout << "Chaine1: " << Chaine1 << std::endl;
14: std::cout << "Chaine2: " << Chaine2 << std::endl;
15: return 0;
16: }
Ce programme produit le rsultat suivant :
Chaine1: Nul nest prophete en son pays
Chaine2: Nul nest prophete en son pays
Comme le prcdent, ce programme copie le contenu dune chane dans une autre. Cepen-
dant, la ligne 12 a t modie puisquelle appelle dsormais la fonction strncpy().
Celle-ci utilise un troisime paramtre, qui est le nombre maximal de caractres copier.
La taille du tampon Chaine2 est gale LongMax+1 caractres. Le caractre supplmen-
taire correspond au caractre nul qui est ajout automatiquement la n de la chane.
Comme avec le tableau dentiers du Listing 13.9, les tableaux de caractres
peuvent tre redimensionns laide des techniques dallocation sur le tas et
de la copie, lment par lment. Les classes de chanes de C++, plus souples,
utilisent une variante de cette technique pour permettre aux chanes de grandir
et de se rduire en fonction des besoins, ou pour insrer ou supprimer des
lments au milieu de la chane.
La classe String
Hrites du langage C, les chanes termines par un caractre nul et la bibliothque de
fonctions (parmi lesquelles gure strcpy()) sintgrent mal une structure de
programme oriente objet. La bibliothque standard de C++ inclut une classe String qui
fournit un ensemble encapsul de donnes et de mthodes permettant de masquer les
dtails internes aux clients de la classe.
Avant dutiliser cette classe, nous allons crer une classe String personnalise qui nous
permettra de comprendre les problmes rsoudre. Notre classe String doit au mininum
surmonter les limites des tableau de caractres.
In
f
o
C++Livre Page 425 J eudi, 7. mai 2009 9:45 09
426 Le langage C++
Comme tous les tableaux, un tableau de caractres est statique. Le programmeur doit donc
dnir sa taille en mmoire, mme sil ne lutilise pas en totalit. Comme nous lavons vu,
toute tentative dcriture au-del du dernier lment est dsastreuse.
Une classe String bien conue ne doit utiliser que lespace mmoire ncessaire et
sufsant. Si elle narrive pas allouer de la mmoire, lapplication doit se terminer
proprement.
Le Listing 13.14 prsente une premire approche de notre classe String.
Cette classe String personnalise a ses limites et ne doit pas tre utilise en
ltat dans une application commerciale. La bibliothque standard en propose
une qui offre toutes les garanties.
Listing 13.14 : Cration dune classe String
0: //Listing13.14 Utilisation dune classe String
1:
2: #include <iostream>
3: #include <string.h>
4: using namespace std;
5:
6: // Classe String rudimentaire
7: class String
8: {
9: public:
10: // constructeurs
11: String();
12: String(const char *const);
13: String(const String &);
14: ~String();
15:
16: // oprateurs surchargs
17: char & operator[](unsigned short indice);
18: char operator[](unsigned short indice) const;
19: String operator+(const String&);
20: void operator+=(const String&);
21: String & operator= (const String &);
22:
23: // mthodes daccs
24: unsigned short GetLen() const { return saLongueur; }
25: const char * GetString() const { return saChaine; }
26:
27: private:
28: String (unsigned short); // constructeur priv
29: char * saChaine;
In
f
o
C++Livre Page 426 J eudi, 7. mai 2009 9:45 09
Chapitre 13 Tableaux et chanes 427
30: unsigned short saLongueur;
31: };
32:
33: // le constructeur par dfaut cre une chane de 0 octet
34: String::String()
35: {
36: saChaine = new char[1];
37: saChaine[0] = \0;
38: saLongueur = 0;
39: }
40:
41: // constructeur (utilitaire) priv, utilis seulement par
42: // les mthodes de la classe pour crer une nouvelle chane
43: // de la taille requise. Complte par des zros.
44: String::String(unsigned short lg)
45: {
46: saChaine = new char[lg+1];
47: for (unsigned short i = 0; i <= lg; i++)
48: saChaine[i] = \0;
49: saLongueur = lg;
50: }
51:
52: // Convertit un tableau de caractres en une chane
53: String::String(const char * const chaineC)
54: {
55: saLongueur = strlen(chaineC);
56: saChaine = new char[saLongueur+1];
57: for (unsigned short i = 0; i < saLongueur; i++)
58: saChaine[i] = chaineC[i];
59: saChaine[saLongueur]=\0;
60: }
61:
62: // constructeur de copie
63: String::String (const String & rhs)
64: {
65: saLongueur = rhs.GetLen();
66: saChaine = new char[saLongueur+1];
67: for (unsigned short i = 0; i < saLongueur; i++)
68: saChaine[i] = rhs[i];
69: saChaine[saLongueur] = \0;
70: }
71:
72: // destructeur, libre toute la mmoire alloue
73: String::~String ()
74: {
75: delete [] saChaine;
76: saLongueur = 0;
C++Livre Page 427 J eudi, 7. mai 2009 9:45 09
428 Le langage C++
77: }
78:
79: // oprateur daffectation, libre la mmoire existante
80: // puis copie la chane et la taille
81: String& String::operator=(const String & rhs)
82: {
83: if (this == &rhs)
84: return *this;
85: delete [] saChaine;
86: saLongueur = rhs.GetLen();
87: saChaine = new char[saLongueur+1];
88: for (unsigned short i = 0; i < saLongueur; i++)
89: saChaine[i] = rhs[i];
90: saChaine[saLongueur] = \0;
91: return *this;
92: }
93:
94: // oprateur dindexation non constant, renvoie une
95: // rfrence au caractre pour quil puisse
96: // tre modifi!
97: char & String::operator[](unsigned short indice)
98: {
99: if (indice > saLongueur)
100: return saChaine[saLongueur-1];
101: else
102: return saChaine[indice];
103: }
104:
105: // oprateur dindexation constant pour utilisation sur
106: // les objets constants (voir le constructeur de copie!)
107: char String::operator[](unsigned short indice) const
108: {
109: if (indice > saLongueur)
110: return saChaine[saLongueur-1];
111: else
112: return saChaine[indice];
113: }
114:
115: // cre une nouvelle chane en ajoutant
116: // la chane actuelle rhs
117: String String::operator+(const String& rhs)
118: {
119: unsigned short lgTotale = saLongueur +rhs.GetLen();
120: String temp(lgTotale);
121: unsigned short i;
122: for ( i= 0; i < saLongueur; i++)
123: temp[i] = saChaine[i];
C++Livre Page 428 J eudi, 7. mai 2009 9:45 09
Chapitre 13 Tableaux et chanes 429
124: for (unsigned short j = 0; j < rhs.GetLen(); j++, i++)
125: temp[i] = rhs[j];
126: temp[lgTotale]=\0;
127: return temp;
128: }
129:
130: // modifie la chane actuelle, ne renvoie rien
131: void String::operator+=(const String& rhs)
132: {
133: unsigned short lgRhs = rhs.GetLen();
134: unsigned short lgTotale = saLongueur+lgRhs;
135: String temp(lgTotale);
136: unsigned short i;
137: for (i = 0; i < saLongueur; i++)
138: temp[i] = saChaine[i];
139: for (unsigned short j = 0; j < lgRhs; j++, i++)
140: temp[i] = rhs[i-saLongueur];
141: temp[lgTotale]=\0;
142: *this = temp;
143: }
144:
145: int main()
146: {
147: String s1("Test initial");
148: cout << "S1:\t" << s1.GetString() << endl;
149:
150: char * temp = "Bonjour tout le monde ";
151: s1 = temp;
152: cout << "S1:\t" << s1.GetString() << endl;
153:
154: char temp2[20];
155: strcpy(temp2,"; comment ca va?");
156: s1 += temp2;
157: cout << "temp2:\t" << temp2 << endl;
158: cout << "S1:\t" << s1.GetString() << endl;
159:
160: cout << "S1[4]:\t" << s1[4] << endl;
161: s1[4]= x;
162: cout << "S1:\t" << s1.GetString() << endl;
163:
164: cout << "S1[999]:\t" << s1[999] << endl;
165:
166: String s2(" Autre chane");
167: String s3;
168: s3 = s1 + s2;
169: cout << "S3:\t" << s3.GetString() << endl;
170:
C++Livre Page 429 J eudi, 7. mai 2009 9:45 09
430 Le langage C++
171: String s4;
172: s4 = "Pourquoi ca marche?";
173: cout << "S4:\t" << s4.GetString() << endl;
174: return 0;
175: }
Ce programme produit le rsultat suivant :
S1: Test initial
S1: Bonjour tout le monde
temp2: ; comment ca va?
S1: Bonjour tout le monde; comment ca va?
S1[4]: o
S1: Bonjxur tout le monde; comment ca va?
S1[999]: ?
S3: Bonjxur tout le monde; comment ca va? Autre chane
S4: Pourquoi ca marche?
La dclaration de notre classe String est comprise de la ligne 7 la ligne 31. Pour ajouter
de la souplesse la classe, il y a trois constructeurs aux lignes 11 13 : le constructeur par
dfaut, le constructeur de copie et un constructeur prenant en paramtre une chane exis-
tante termine par un caractre nul (style C).
Pour permettre aux utilisateurs de manipuler facilement les chanes, cette classe surcharge
plusieurs oprateurs, dont loprateur dindexation ([ ]), loprateur plus (+) et lopra-
teur plus-gal (+=). Loprateur dindexation est surcharg deux fois : la premire comme
mthode constante renvoyant un caractre, la deuxime comme mthode variable
renvoyant une rfrence sur un caractre.
La version non constante est utilise dans des instructions comme celle-ci (ligne 161) :
S1[4]=x;
Elle permet davoir un accs direct aux caractres de la chane. Cette version renvoie une
rfrence au caractre an que la fonction appelante puisse le traiter.
La version constante, quant elle, permet davoir accs aux objets constants de la classe
String, pour limplmentation du constructeur de copie, par exemple ( partir de la
ligne 63). Vous pouvez noter que le programme lit la variable rhs[i], bien quelle soit
dclare comme une const String &. Comme il est interdit de lire cet objet laide dune
fonction membre variable, loprateur dindexation doit tre surcharg avec une fonction
daccs constante. Si lobjet renvoy tait volumineux, il serait prfrable de renvoyer une
rfrence constante, mais cest inutile ici car un caractre noccupe quun octet en
mmoire.
C++Livre Page 430 J eudi, 7. mai 2009 9:45 09
Chapitre 13 Tableaux et chanes 431
Le constructeur par dfaut est implment des lignes 34 39. Il cre une chane de taille
nulle ne contenant que le caractre nul nal. Ce dernier nest pas pris en compte par la
classe String lorsquelle renvoie la longueur de la chane.
Le constructeur de copie dnit la longueur de la nouvelle chane, en ajoutant le caractre
nul la longueur existante. Pour cela, il copie un un les caractres de la chane source
vers la chane cible, puis ajoute le caractre nul (lignes 63 70). la diffrence des opra-
teurs daffectation, les constructeurs de copie nont pas besoin de tester si la chane copie
dans ce nouvel objet est cet objet lui-mme ; cela ne peut jamais arriver.
De la ligne 53 la ligne 60, un constructeur, similaire au constructeur de copie, prend en
paramtre une chane de type C. Pour dterminer la taille de la chane existante, il appelle
la fonction standard strlen().
La ligne 28 dclare le constructeur String(unsigned short) comme une mthode
prive, an dempcher toute tentative de cration dune chane de longueur arbitraire par
les clients de la classe. Il a pour rle daider les fonctions spciales crer des chanes
comme dans le cas, par exemple, de operator+= la ligne 131. Cette fonctionnalit est
dcrite dans la section consacre loprateur +=, plus loin dans ce chapitre.
Aux lignes 44 50, le constructeur String(unsigned short) complte tous les lments
du tableau par des caractres nuls (\0). Cest pourquoi la boucle for vrie que le nombre
dlments lus est bien infrieur ou gal la longueur de la chane (i< = lg).
La chane de caractres gre par la classe est supprime par le destructeur (lignes 73 77).
Assurez-vous de ne pas oublier les crochets aprs le mot-cl delete car il faut supprimer
tous les lments du tableau, pas seulement le premier.
Loprateur daffectation (=) est surcharg aux lignes 81 92. Cette mthode dtermine si
les deux cts de lexpression sont identiques ou non. Sils sont diffrents, la chane exis-
tante est supprime, puis remplace par la nouvelle. Cette mthode renvoie une rfrence,
ce qui permet dcrire :
Chaine1 = Chaine2 = Chaine3;
Loprateur dindexation est galement surcharg. Il lest dabord aux lignes 97 103,
puis nouveau aux lignes 107 113. Dans les deux cas, il effectue un test trs simple pour
vrier que lindice fourni est infrieur la borne suprieure de la chane. Si lutilisateur
tente daccder un caractre en dehors de la limite suprieure du tableau, loprateur
renvoie le dernier caractre.
Loprateur + permet de concatner deux chanes (lignes 117 127) car il est pratique de
pouvoir crire :
Chaine3 = Chaine1+Chaine2;
C++Livre Page 431 J eudi, 7. mai 2009 9:45 09
432 Le langage C++
an que Chaine3 contienne le rsultat de la concatnation des deux autres chanes. Pour
cela, loprateur + additionne les tailles respectives des deux chanes, puis appelle le
constructeur priv en lui passant cette longueur totale an quil cre une chane temporaire
temp de cette longueur en linitialisant avec des caractres nuls. Ceux-ci sont ensuite
remplacs par les contenus des deux chanes. La chane de gauche (*this) est copie en
premier, puis cest au tour de la chane de droite (rhs). La premire boucle for parcourt la
chane de gauche et ajoute chacun de ses caractres la nouvelle chane. La seconde
boucle for fait de mme pour la chane de droite. Vous pouvez constater que les compteurs
i et j sincrmentent simultanment, car i dsigne lindice de la chane cible, tandis que j
dsigne lindice de la chane de droite.
la ligne 127, la chane temp renvoye par valeur par loprateur plus est affecte la
chane situe gauche dans laffectation (chaine1). Aux lignes 131 143, loprateur +=
agit directement sur la chane existante (cest--dire sur la partie gauche de linstruction
chaine1 += chaine2). Il fonctionne comme loprateur plus, sauf que la valeur tempo-
raire temp est affecte la chaine courante (*this = temp) la ligne 142.
La fonction main() (lignes 143 173) sert de programme de test pour la classe. La
ligne 147 cre un objet String en utilisant le constructeur qui prend en paramtre une
chane de type C. La ligne suivante afche son contenu, quelle obtient via un appel sa
mthode daccs GetString(). La ligne 150 cre une seconde chane " la C", qui est
ensuite affecte la premire la ligne 151. La ligne 153 afche le rsultat de cette affec-
tation et montre que la surcharge de loprateur daffectation a bien fonctionn.
La ligne 154 cr une troisime chane C appele temp2 et la ligne suivante appelle la
fonction strcpy() pour lui affecter la chane littrale comment ca va?. Loprateur +=
est ensuite utiliser pour concatner le contenu de temp2 la chane s1 et le rsultat est
afch la ligne 158.
La ligne 160 lit le cinquime caractre de s1 laide de loprateur dindexation surcharg
et lafche. Ce caractre reoit la nouvelle valeur x la ligne 16. Cette ligne fait appel
loprateur dindexation non constant. La ligne 162 montre que ce caractre a bien t
modi dans la chane.
Le programme tente alors de lire un caractre aprs la n du tableau. Daprs ce qui
safche, on peut constater que le caractre qui a t renvoy est le dernier, comme
prvu.
Les lignes 166 et 167 crent deux autres objets String et la ligne suivante appelle loprateur
daddition surcharg pour les concatner. Le rsultat est afch en ligne 169.
La ligne 171 cre un nouvel objet String, s4, qui est initialis avec une chane de type C
la ligne suivante laide de loprateur daffectation surcharg. La ligne 173 afche le
C++Livre Page 432 J eudi, 7. mai 2009 9:45 09
Chapitre 13 Tableaux et chanes 433
rsultat. Vous pourriez vous demander pourquoi cette affectation dune chane C a t
autorise alors que loprateur daffectation a t dni pour attendre un paramtre de type
String ?
La rponse est que le compilateur attend effectivement un objet String, mais quil reoit
un tableau de caractres. Par consquent, il vrie sil peut crer un objet String partir
des informations reues. la ligne 12, vous avez dclar un constructeur capable de
crer des chanes partir de tableaux de caractres : le compilateur cre donc un objet
String temporaire partir du tableau de caractres quon lui a transmis, puis le passe
loprateur daffectation. Cette opration est appele transtypage implicite ou promo-
tion. Si vous naviez pas dclar (ni fourni une implmentation) de constructeur prenant
en paramtre un tableau de caractres, cette affectation aurait produit une erreur de compi-
lation.
La classe String du Listing 13.14 devient assez robuste. Vous pouvez galement constater
que ce listing est plus long que les prcdents. Heureusement, la bibliothque C++ stan-
dard fournit une classe String encore plus robuste, que vous pourrez utiliser en incluant la
bibliothque <string>.
Listes chanes et autres structures
Les tableaux sont comme des Tupperware

. Bien quils puissent contenir de grands volu-


mes de donnes, leur taille est xe. Si vous choisissez une botre trop grande, une partie de
lespace ne sera donc pas exploite. linverse, si elle est trop petite, les aliments dbor-
deront et vous serez bien embt.
Le Listing 13.9 proposait une solution ce problme. Toutefois, lorsque lon commence
utiliser de grands tableaux ou lorsque que lon souhaite dplacer, effacer ou insrer des
entres dun tableau, le nombre des allocations et des dsallocations peut tre assez
coteux.
Les listes chanes ont t conues pour rsoudre ce type de problmes. Un