Anda di halaman 1dari 254

LA PROGRAMMATION POUR...

2
 les lves ingnieurs
2
 . . . ou les collgiens

2
 dbutants

2
 . . . ou confirms

Cours de lcole des Ponts ParisTech - 2014/2015


Renaud Keriven et Pascal Monasse
IMAGINE - cole des Ponts ParisTech
monasse@imagine.enpc.fr
Version lectronique et programmes :
http://imagine.enpc.fr/~monasse/Info/

"Ne traitez pas vos ordinateurs comme des tres vivants...


Ils naiment pas a !"

"Cet ordinateur ne fait pas du tout ce que je veux !"


"Exact... Il fait ce que tu lui demandes de faire !"

TABLE DES MATIRES

TABLE DES MATIRES

Table des matires


1

Prambule
1.1 Pourquoi savoir programmer ? .
1.2 Comment apprendre ? . . . . . .
1.2.1 Choix du langage . . . . .
1.2.2 Choix de lenvironnement
1.2.3 Principes et conseils . . .

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

7
10
10
10
11
11

Bonjour, Monde !
2.1 Lordinateur . . . . . . . . . . . . . .
2.1.1 Le micro-processeur . . . . .
2.1.2 La mmoire . . . . . . . . . .
2.1.3 Autres Composants . . . . .
2.2 Systme dexploitation . . . . . . . .
2.3 La Compilation . . . . . . . . . . . .
2.4 Lenvironnement de programmation
2.4.1 Noms de fichiers . . . . . . .
2.4.2 Debuggeur . . . . . . . . . . .
2.4.3 TP . . . . . . . . . . . . . . . .

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

13
15
15
17
19
20
21
23
23
23
24

.
.
.
.
.
.
.
.
.
.
.
.
.
.

25
25
25
29
31
33
35
37
39
39
42
43
44
45
45

Les tableaux
4.1 Premiers tableaux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4.2 Initialisation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4.3 Spcificits des tableaux . . . . . . . . . . . . . . . . . . . . . . . . . . . .

49
49
51
52

.
.
.
.
.

Premiers programmes
3.1 Tout dans le main() ! . . . . . . . . . .
3.1.1 Variables . . . . . . . . . . . . .
3.1.2 Tests . . . . . . . . . . . . . . .
3.1.3 Boucles . . . . . . . . . . . . . .
3.1.4 Rcrations . . . . . . . . . . .
3.2 Fonctions . . . . . . . . . . . . . . . . .
3.2.1 Retour . . . . . . . . . . . . . .
3.2.2 Paramtres . . . . . . . . . . . .
3.2.3 Passage par rfrence . . . . .
3.2.4 Porte, Dclaration, Dfinition
3.2.5 Variables locales et globales . .
3.2.6 Surcharge . . . . . . . . . . . .
3.3 TP . . . . . . . . . . . . . . . . . . . . .
3.4 Fiche de rfrence . . . . . . . . . . . .

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

TABLE DES MATIRES

4.4

4.5
4.6
5

TABLE DES MATIRES

4.3.1 Tableaux et fonctions


4.3.2 Affectation . . . . . .
Rcrations . . . . . . . . . .
4.4.1 Multi-balles . . . . .
4.4.2 Avec des chocs ! . . .
4.4.3 Mlanger les lettres .
TP . . . . . . . . . . . . . . .
Fiche de rfrence . . . . . .

Les structures
5.1 Rvisions . . . . . . . . . .
5.1.1 Erreurs classiques .
5.1.2 Erreurs originales .
5.1.3 Conseils . . . . . .
5.2 Les structures . . . . . . .
5.2.1 Dfinition . . . . .
5.2.2 Utilisation . . . . .
5.3 Rcration : TP . . . . . . .
5.4 Fiche de rfrence . . . . .

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

Plusieurs fichiers !
6.1 Fichiers spars . . . . . . . . . . . . .
6.1.1 Principe . . . . . . . . . . . . .
6.1.2 Avantages . . . . . . . . . . . .
6.1.3 Utilisation dans un autre projet
6.1.4 Fichiers den-ttes . . . . . . .
6.1.5 A ne pas faire... . . . . . . . . .
6.1.6 Implmentation . . . . . . . . .
6.1.7 Inclusions mutuelles . . . . . .
6.2 Oprateurs . . . . . . . . . . . . . . . .
6.3 Rcration : TP suite et fin . . . . . . .
6.4 Fiche de rfrence . . . . . . . . . . . .
La mmoire
7.1 Lappel dune fonction . . . . . . . .
7.1.1 Exemple . . . . . . . . . . . .
7.1.2 Pile des appels et dbuggeur
7.2 Variables Locales . . . . . . . . . . .
7.2.1 Paramtres . . . . . . . . . . .
7.2.2 La pile . . . . . . . . . . . . .
7.3 Fonctions rcursives . . . . . . . . .
7.3.1 Pourquoi a marche ? . . . . .
7.3.2 Efficacit . . . . . . . . . . . .
7.4 Le tas . . . . . . . . . . . . . . . . . .
7.4.1 Limites . . . . . . . . . . . . .
7.4.2 Tableaux de taille variable . .
7.4.3 Essai dexplication . . . . . .
7.5 Loptimiseur . . . . . . . . . . . . . .
7.6 Assertions . . . . . . . . . . . . . . .
2

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

52
54
55
55
57
59
61
61

.
.
.
.
.
.
.
.
.

65
65
65
66
67
67
67
68
69
71

.
.
.
.
.
.
.
.
.
.
.

73
74
74
75
76
76
79
79
80
80
82
82

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

85
85
85
87
89
90
90
90
91
91
93
93
93
95
95
96

TABLE DES MATIRES

7.7
7.8
7.9
8

TABLE DES MATIRES

TP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Fiche de rfrence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Examens sur machine . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

96
97
99

Allocation dynamique
8.1 Tableaux bidimensionnels . . . . .
8.1.1 Principe . . . . . . . . . . .
8.1.2 Limitations . . . . . . . . .
8.1.3 Solution . . . . . . . . . . .
8.2 Allocation dynamique . . . . . . .
8.2.1 Pourquoi a marche ? . . . .
8.2.2 Erreurs classiques . . . . . .
8.2.3 Consquences . . . . . . . .
8.3 Structures et allocation dynamique
8.4 Boucles et continue . . . . . . . .
8.5 TP . . . . . . . . . . . . . . . . . . .
8.6 Fiche de rfrence . . . . . . . . . .

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

101
101
101
102
103
104
104
105
106
107
110
111
111

Premiers objets
9.1 Philosophie . . . . . . . . .
9.2 Exemple simple . . . . . . .
9.3 Visibilit . . . . . . . . . . .
9.4 Exemple des matrices . . . .
9.5 Cas des oprateurs . . . . .
9.6 Interface . . . . . . . . . . .
9.7 Protection . . . . . . . . . .
9.7.1 Principe . . . . . . .
9.7.2 Structures vs Classes
9.7.3 Accesseurs . . . . . .
9.8 TP . . . . . . . . . . . . . . .
9.9 Fiche de rfrence . . . . . .

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

115
115
116
118
118
120
122
123
123
125
125
126
126

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

131
131
132
132
132
134
135
136
137
137
137
138
140
142
142
143
144

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

10 Constructeurs et Destructeurs
10.1 Le problme . . . . . . . . . . . . .
10.2 La solution . . . . . . . . . . . . . .
10.3 Cas gnral . . . . . . . . . . . . . .
10.3.1 Constructeur vide . . . . .
10.3.2 Plusieurs constructeurs . .
10.3.3 Tableaux dobjets . . . . . .
10.4 Objets temporaires . . . . . . . . .
10.5 TP . . . . . . . . . . . . . . . . . . .
10.6 Rfrences Constantes . . . . . . .
10.6.1 Principe . . . . . . . . . . .
10.6.2 Mthodes constantes . . . .
10.7 Destructeur . . . . . . . . . . . . .
10.8 Destructeurs et tableaux . . . . . .
10.9 Constructeur de copie . . . . . . .
10.10Affectation . . . . . . . . . . . . . .
10.11Objets avec allocation dynamique .
3

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

TABLE DES MATIRES

TABLE DES MATIRES

10.11.1 Construction et destruction


10.11.2 Problmes ! . . . . . . . . .
10.11.3 Solution ! . . . . . . . . . . .
10.12Fiche de rfrence . . . . . . . . . .
10.13Devoir crit . . . . . . . . . . . . .

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

11 En vrac...
11.1 Chanes de caratres . . . . . . . . . . .
11.2 Fichiers . . . . . . . . . . . . . . . . . . .
11.2.1 Principe . . . . . . . . . . . . . .
11.2.2 Chanes et fichiers . . . . . . . .
11.2.3 Objets et fichiers . . . . . . . . .
11.3 Valeurs par dfaut . . . . . . . . . . . . .
11.3.1 Principe . . . . . . . . . . . . . .
11.3.2 Utilit . . . . . . . . . . . . . . .
11.3.3 Erreurs frquentes . . . . . . . .
11.4 Accesseurs . . . . . . . . . . . . . . . . .
11.4.1 Rfrence comme type de retour
11.4.2 Utilisation . . . . . . . . . . . . .
11.4.3 operator() . . . . . . . . . . .
11.4.4 Surcharge et mthode constante
11.4.5 "inline" . . . . . . . . . . . . . . .
11.5 Assertions . . . . . . . . . . . . . . . . .
11.6 Types numrs . . . . . . . . . . . . . .
11.7 Fiche de rfrence . . . . . . . . . . . . .
12 En vrac (suite) ...
12.1 Oprateur binaires . . . . .
12.2 Valeur conditionnelle . . . .
12.3 Boucles et break . . . . . .
12.4 Variables statiques . . . . .
12.5 const et tableaux . . . . . .
12.6 template . . . . . . . . . .
12.6.1 Principe . . . . . . .
12.6.2 template et fichiers
12.6.3 Classes . . . . . . . .
12.6.4 STL . . . . . . . . . .
12.7 Fiche de rfrence . . . . . .
12.8 Devoir final . . . . . . . . .

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.

145
145
146
150
153

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

155
155
157
157
158
158
159
159
160
160
160
161
161
162
162
163
165
165
167

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

171
171
172
173
174
175
175
175
177
177
180
182
187

13 Structure de donnes
13.1 Rappels sur les tableaux . . . . . . . . . . . . . . .
13.2 La complexit . . . . . . . . . . . . . . . . . . . . .
13.2.1 Mais quest-ce donc que la complexit ? . .
13.2.2 Comment la mesure-t-on ? . . . . . . . . . .
13.2.3 La notation O . . . . . . . . . . . . . . . . .
13.2.4 P et N P . . . . . . . . . . . . . . . . . . . .
13.2.5 Pourquoi est-ce important ? . . . . . . . . .
13.3 Le vecteur : un tableau encapsul dans une classe

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

189
189
190
190
191
191
191
192
192

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

TABLE DES MATIRES

13.4
13.5
13.6
13.7
13.8
13.9

13.3.1 Usage . . . . . . . . . . .
13.3.2 Complexit . . . . . . .
13.3.3 Gestion mmoire . . . .
La pile, Last In First Out (LIFO)
La file, First In First Out (FIFO)
La liste chane . . . . . . . . .
Rcapitulatif des complexits .
Les itrateurs . . . . . . . . . .
Autres structures . . . . . . . .

TABLE DES MATIRES

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

14 Algorithmes de tri
14.1 Complexit minimale . . . . . . . . . . . . . . .
14.2 Algorithmes quadratiques . . . . . . . . . . . .
14.3 QuickSort . . . . . . . . . . . . . . . . . . . . .
14.4 File de priorit et HeapSort . . . . . . . . . . .
14.4.1 Insertion dans la file de priorit (push)
14.4.2 Retrait de la file de priorit (pop) . . . .
14.4.3 Stockage de la file de priorit . . . . . .
14.4.4 HeapSort . . . . . . . . . . . . . . . . . .
14.5 Conclusion . . . . . . . . . . . . . . . . . . . . .

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

A Travaux Pratiques
A.1 Lenvironnement de programmation . . . . . . . . . . .
A.1.1 Bonjour, Monde ! . . . . . . . . . . . . . . . . . .
A.1.2 Premires erreurs . . . . . . . . . . . . . . . . . .
A.1.3 Debugger . . . . . . . . . . . . . . . . . . . . . .
A.1.4 Sil reste du temps . . . . . . . . . . . . . . . . .
A.1.5 Installer Imagine++ chez soi . . . . . . . . . . . .
A.2 Variables, boucles, conditions, fonctions . . . . . . . . .
A.2.1 Premier programme avec fonctions . . . . . . .
A.2.2 Premier programme graphique avec Imagine++
A.2.3 Jeu de Tennis . . . . . . . . . . . . . . . . . . . .
A.3 Tableaux . . . . . . . . . . . . . . . . . . . . . . . . . . .
A.3.1 Mastermind Texte . . . . . . . . . . . . . . . . .
A.3.2 Mastermind Graphique . . . . . . . . . . . . . .
A.4 Structures . . . . . . . . . . . . . . . . . . . . . . . . . .
A.4.1 Etapes . . . . . . . . . . . . . . . . . . . . . . . .
A.4.2 Aide . . . . . . . . . . . . . . . . . . . . . . . . .
A.4.3 Thorie physique . . . . . . . . . . . . . . . . . .
A.5 Fichiers spars . . . . . . . . . . . . . . . . . . . . . . .
A.5.1 Fonctions outils . . . . . . . . . . . . . . . . . . .
A.5.2 Vecteurs . . . . . . . . . . . . . . . . . . . . . . .
A.5.3 Balle part . . . . . . . . . . . . . . . . . . . . . .
A.5.4 Retour la physique . . . . . . . . . . . . . . . .
A.6 Les tris . . . . . . . . . . . . . . . . . . . . . . . . . . . .
A.6.1 Mlanger un tableau . . . . . . . . . . . . . . . .
A.6.2 Tris quadratiques . . . . . . . . . . . . . . . . . .
A.6.3 Quicksort . . . . . . . . . . . . . . . . . . . . . .
A.6.4 Gros tableaux . . . . . . . . . . . . . . . . . . . .
5

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

192
193
193
193
194
196
197
197
198

.
.
.
.
.
.
.
.
.

199
199
199
200
202
202
203
203
204
204

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

205
205
205
207
208
209
209
210
210
210
212
214
214
216
218
218
220
221
223
223
223
224
224
227
227
228
229
230

TABLE DES MATIRES

TABLE DES MATIRES

A.7 Images . . . . . . . . . . . . . . . . . . . .
A.7.1 Allocation . . . . . . . . . . . . . .
A.7.2 Tableaux statiques . . . . . . . . .
A.7.3 Tableaux dynamiques . . . . . . .
A.7.4 Charger un fichier . . . . . . . . .
A.7.5 Fonctions . . . . . . . . . . . . . .
A.7.6 Structure . . . . . . . . . . . . . . .
A.7.7 Suite et fin . . . . . . . . . . . . . .
A.8 Premiers objets et dessins de fractales . .
A.8.1 Le triangle de Sierpinski . . . . . .
A.8.2 Une classe plutt quune structure
A.8.3 Changer dimplmentation . . . .
A.8.4 Le flocon de neige . . . . . . . . .
A.9 Tron . . . . . . . . . . . . . . . . . . . . . .
A.9.1 Serpent . . . . . . . . . . . . . . . .
A.9.2 Tron . . . . . . . . . . . . . . . . .
A.9.3 Graphismes . . . . . . . . . . . . .
B Imagine++
B.1 Common
B.2 Graphics
B.3 Images .
B.4 LinAlg .

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

C Fiche de rfrence finale

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

231
231
231
232
232
232
233
233
234
234
235
235
236
237
237
238
238

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

241
241
242
243
243
245

1. Prambule

Chapitre 1
Prambule
Note : Ce premier chapitre maladroit correspond ltat desprit dans lequel ce
cours a dbut en 2003, dans une priode o lInformatique avait mauvaise presse
lcole des Ponts. Nous le maintenons ici en tant que tmoin de ce quil faillait faire
alors pour amener les lves ne pas ngliger lInformatique. Si lon ignore la navet
de cette premire rdaction (et le fait que Star Wars nest plus autant la mode !),
lanalyse et les conseils qui suivent restent dactualit.

(Ce premier chapitre tente surtout de motiver les lves ingnieurs dans leur apprentissage
de la programmation. Les enfants qui se trouveraient ici pour apprendre programmer sont
srement dj motivs et peuvent sauter au chapitre suivant ! Profitons-en pour tenir des propos
qui ne les concernent pas...)

Le Matre Programmeur 1 : "Rassure-toi ! Les ordinateurs sont stupides ! Programmer est donc facile."
LApprenti Programmeur 2 : "Matre, les ordinateurs ne sont certes que des machines et les dominer devrait tre ma porte. Et pourtant... Leur manque dintelligence fait justement quil mest pnible den faire ce que je veux. Programmer
exige de la prcision et la moindre erreur est sanctionne par un message incomprhensible, un bug 3 ou mme un crash de la machine. Pourquoi doit-on tre
aussi... prcis ?" Programmer rend maniaque ! Dailleurs, les informaticiens sont tous
maniaques. Et je nai pas envie de devenir comme a...
1. Permettez ce terme ouvertement Lucasien. Il semble plus appropri que lhabituel Gourou souvent
utilis pour dcrire lexpert informaticien. Nous parlons bien ici dun savoir-faire transmettre de Matre
Apprenti et non dune secte...
2. Le jeune Padawan, donc, pour ceux qui connaissent...
3. Je naurai aucun remord dans ce polycopi utiliser les termes habituels des informaticiens... en
essayant videmment de ne pas oublier de les expliquer au passage. Anglicismes souvent incomprhensibles, ils constituent en ralit un argot propre au mtier dinformaticien, argot que doit bien videmment accepter et faire sien lApprenti sous peine de ne rien comprendre au discours de ses collgues
dune part, et demployer des adaptations franaises ridicules ou peu usites dautre part. Naviguer sur
la toile, envoyer un courriel ou avoir un bogue commencent peut-tre devenir des expressions comprhensibles. Mais demandez-donc votre voisin sil reoit beaucoup de pourriels (terme propos pour
traduire "Spams") !

1. Prambule

M.P. : "La prcision est indispensable pour communiquer avec une machine. Cest
lHomme de sadapter. Tu dois faire un effort. En contre-partie tu deviendras
son matre. Rjouis-toi. Bientt, tu pourras crer ces tres obissants que sont les
programmes."
A.P. : "Bien, Matre..." Quel vieux fou ! Pour un peu, il se prendrait pour Dieu. La vrit,
cest quil parle aux machines parce quil ne sait pas parler aux hommes. Il comble avec
ses ordinateurs son manque de contact humain. Linformaticien type... Il ne lui manque
plus que des grosses lunettes et les cheveux gras 4 . "Matre, je ne suis pas sr den avoir
envie. Je ny arriverai pas. Ne le prenez pas mal, mais je crois tre davantage dou
pour les Mathmatiques ! Et puis, quoi savoir programmer me servira-t-il ?"
M.P. : "Les vrais problmes qui se poseront toi, tu ne pourras toujours les rsoudre par les Mathmatiques. Savoir programmer, tu devras !"
A.P. : "Jessaierai..." Je me demande sil a vraiment raison ! Je suis sr quil doit tre nul
en Maths. Voil la vrit !
...

Oublions l ce dialogue aussi caricatural que maladroit. Il montre pourtant clairement la situation. Rsumons :
Pour celui qui sait, programmer :
est un jeu denfant.
est indispensable.
est une activit cratrice et panouissante.
Pour celui qui apprend, programmer :
est difficile.
ne sert rien.
est une activit ingrate qui favorise le renfermement 5 sur soi-mme.
Dans le cas o llve est ingnieur, nous pouvons complter le tableau :
Pour le professeur, apprendre programmer :
devrait tre simple et rapide pour un lve ingnieur.
est plus utile quapprendre davantage de Mathmatiques.
Pour llve, programmer :
est un travail de "technicien 6 " quil naura jamais faire lui-mme.
nest pas aussi noble que les Mathmatiques, bref, nest pas digne de lui.
En fait, les torts sont partags :
Le professeur :
4. Toute ressemblance avec des personnages rels ou imaginaires, etc.
5. Utiliser un ordinateur pour programmer a tout aussi mauvaise presse que de jouer aux jeux vido.
Programmer est pourtant souvent un travail dquipe.
6. avec tout le sens pjoratif que ce terme peut avoir pour lui.

1. Prambule

ne ralise pas que ses lves ont un niveau avanc en maths parce quils en
font depuis plus de dix ans, et quil leur faudra du temps pour apprendre ne
serait-ce que les bases de la programmation. Du temps... et de la pratique,
car, si programmer est effectivement simple en regard de ce que ses lves
savent faire en maths, il ncessite une tournure desprit compltement diffrente et beaucoup de travail personnel devant la machine.
oublie quil a le plus souvent appris seul quand il tait plus jeune, en programmant des choses simples et ludiques 7 . Il devrait donc faire venir ses
lves la programmation par le ct ludique, et non avec les mmes sempiternels exemples 8 .
Llve :
ne se rend pas compte que savoir programmer lui sera utile. Il sagit pourtant dune base qui se retrouve dans tous les langages et mme dans la plupart des logiciels modernes 9 . Et puis, considr comme "le jeune" donc le
moins "allergique" aux ordinateurs, il se verra vraisemblablement confier
son premier poste la ralisation de quelques petits programmes en plus de
ses attributions normales.
sarrange un peu trop facilement dun mpris de bon ton pour la programmation. Il lui est plus ais dapprendre une n-ime branche des mathmatiques que de faire leffort dacqurir par la pratique une nouvelle tournure
desprit.
On laura compris, il est la fois facile et difficile dapprendre programmer. Pour
lingnieur, cela demandera de la motivation et un peu deffort : essentiellement de
mettre ses maths de ct et de retrouver le got des choses basiques. Pour un collgien, motivation et got de leffort seront au rendez-vous. Il lui restera malgr tout
acqurir quelques bases darithmtique et de gomtrie. Comme annonc par le titre
de ce cours, collgien et ingnieur en sont au mme point pour lapprentissage de la
programmation. De plus, et cest un phnomne relativement nouveau, il en est de
mme pour le dbutant et le "geek 10 ". Expliquons-nous : le passionn dinformatique
a aujourdhui tellement de choses faire avec son ordinateur quil sera en gnral incollable sur les jeux, internet, les logiciels graphiques ou musicaux, linstallation ou
la configuration de son systme, lachat du dernier gadget USB la mode, etc. mais
quen contrepartie il sera mauvais programmeur. Il y a quelques annes, il y avait peu
faire avec son ordinateur sinon programmer. Programmer pour combler le manque
de possibilits de lordinateur. Aujourdhui, faire le tour de toutes les possibilits dun
ordinateur est une occupation plein temps ! Ainsi, le "fana info" passe-t-il sa journe se tenir au courant des nouveaux logiciels 11 et en oublie quil pourrait lui aussi
7. Cest une erreur frquente de croire quil intressera ses lves en leur faisant faire des programmes centrs sur les mathmatiques ou le calcul scientifique. De tels programmes leur seront peuttre utiles plus tard, mais ne sont pas forcment motivants. Lalgbre linaire ou lanalyse numrique
sont des domaines passionnants tudier... mais certainement pas programmer. Il faut admettre sans
complexe que programmer un flipper, un master-mind ou un labyrinthe 3D est tout aussi formateur et
plus motivant quinverser une matrice creuse.
8. La liste est longue, mais tellement vraie : quel cours de programmation ne rabche pas les clbres
"factorielle", "suites de Fibonacci", "Quick Sort", etc ?
9. Savoir programmer ne sert pas seulement faire du C++ ou du Java, ni mme du Scilab, du Matlab
ou du Maple : une utilisation avance dExcel ou du Word demande parfois de la programmation !
10. Une rcompense qui me trouve un substitut satisfaisant cette expression consacre.
11. Sans mme dailleurs avoir le temps den creuser convenablement un seul !

1.1. Pourquoi savoir programmer ?

1. Prambule

en crer. En conclusion, collgiens ou ingnieurs, dbutants ou passionns, tous les


lves sont galit. Cest donc sans complexe que lingnieur pourra apprendre
programmer en mme temps que le fils de la voisine.

1.1

Pourquoi savoir programmer ?

Nous venons partiellement de le dire. Rsumons et compltons :


1. Cest la base. Apprendre un langage prcis nest pas du temps perdu car les
mmes concepts se retrouvent dans la plupart des langages. De plus, les logiciels
courants eux-mmes peuvent se programmer.
2. Il est frquent quun stage ou quune embauche en premier poste comporte un
peu de programmation, mme, et peut-tre surtout, dans les milieux o peu de
gens programment.
3. Savoir programmer, cest mieux connatre le matriel et les logiciels, ce qui est
possible techniquement et ce qui ne lest pas. Mme un poste non technique,
cest important pour prendre les bonnes dcisions.

1.2
1.2.1

Comment apprendre ?
Choix du langage

Il faut dabord choisir un langage de programmation. Un ingnieur pourrait videmment tre tent dapprendre programmer en Maple, Matlab, Scilab ou autre. Il
faut quil comprenne quil sagit l doutils spcialiss pour mathmaticien ou ingnieur qui lui seront utiles et qui, certes, se programment, mais pas proprement parler
de langages gnralistes complets. Sans argumenter sur les dfauts respectifs des langages qui en font partie, il nous semble vident quil ne sagit pas du bon choix pour
lapprentissage de la programmation.
En pratique, le choix actuel se fait souvent entre C++ et Java. Bien que Java aie t
conu, entre autres, dans un soucis de simplification du C++ 12 , nous prfrerons C++
pour des raisons pdagogiques :
1. C++ est plus complexe dans son ensemble mais nen connatre que les bases est
dj bien suffisant. Nous ne verrons donc dans ce cours quun sous ensemble du
C++, suffisant en pratique.
2. Plus complet, C++ permet une programmation de haut niveau mais aussi une
programmation simple, adapte au dbutant 13 . C++ permet galement une programmation proche de la machine, ce qui est important pour le spcialiste mais
aussi pour le dbutant, car seule une bonne comprhension de la machine aboutit
une programmation convenable et efficace 14 .
3. C++ est souvent incontournable dans certains milieux, par exemple en finance.
12. Nous ne rduisons videmment pas Java un sous ensemble de C++. Il lui est suprieur sur
certains aspects mais il est dexpressivit plus rduite.
13. Java force un cadre de programmation objet, droutant pour le dbutant.
14. Ne pas comprendre ce que la machine doit faire pour excuter un programme, conduit des programmes inconsidrment gourmands en temps ou mmoire.

10

1. Prambule

1.2. Comment apprendre ?

4. Enfin, certains aspects pratiques et pourtant simples de C++ ont disparu dans
Java 15 .
Encore une fois, rptons que le choix du langage nest pas le plus important et que
lessentiel est dapprendre programmer.

1.2.2

Choix de lenvironnement

Windows et Linux ont leurs partisans, souvent farouchement opposs, tel point
que certains nadmettent pas quil est possible dtre partisan des deux systmes la
fois. Conscients des avantages et des inconvnients de chacun des deux systmes, nous
nen prnons aucun en particulier 16 . Ceci dit, pour des raisons pdagogiques, nous
pensons quun environnement de programmation intgr, cest dire un logiciel unique
permettant de programmer, est prfrable lutilisation de multiples logiciels (diteur,
compilateur, debuggeur, etc.). Cest vrai pour le programmeur confirm, qui trouve
en gnral dans cet environnement des outils puissants, mais cest encore plus crucial
pour le dbutant. Un environnement de programmation, cest :
Toutes les tapes de la programmation regroupes en un seul outil de faon cohrente.
Editer ses fichiers, les transformer en programme, passer en revue ses erreurs,
dtecter les bugs, parcourir la documentation, etc. tout cela avec un seul outil
ergonomique.
Diffrents environnements de dveloppement peuvent tre choisis. Lenvironnement
de rfrence sous Windows est celui de Microsoft, Visual Studio, qui existe en version
gratuite, Express. Cest probablement lun des meilleurs logiciels issus de la firme de
Redmond 17 . Le reste de ce poly est orient vers Visual Studio. Sous Linux, conseillons
KDevelop et sous Mac XCode. Linconvnient est quils nont pas par dfaut les mmes
raccourcis clavier que Visual. Certains fonctionnent sous toutes les plates-formes (Linux, Windows, Mac), mentionnons en particulier QtCreator. Lavantage de ce dernier
est quil utilise par dfaut les mmes raccourcis clavier que Visual Studio, mais quil
comprend en plus le format CMake (dont nous reparlerons dans les TP). Son interface est fortement inspire de KDevelop et XCode. Comme pour le choix du langage,
le choix de lenvironnement nest pas limitant et en connatre un permet de sadapter
facilement nimporte quel autre.

1.2.3

Principes et conseils

Au niveau auquel nous prtendons lenseigner, la programmation ne requiert ni


grande thorie, ni connaissances encyclopdiques. Les concepts utiliss sont rudimentaires mais cest leur mise en oeuvre qui est dlicate. Sil ny avait quun seul conseil
donner, ce serait la rgle des trois "P" :
1. Programmer
2. Programmer
3. Programmer
15. Les oprateurs par exemple.
16. Lidal est en fait davoir les deux "sous la main".
17. Le seul utilisable, diront les mauvaises langues...

11

1.2. Comment apprendre ?

1. Prambule

La pratique est effectivement essentielle. Cest ce qui fait quun enfant a plus de facilits, puisquil a plus de temps. Ajoutons quand mme quelques conseils de base :
1. Samuser. Cest une vidence en matire de pdagogie. Mais cest tellement facile
dans le cas de la programmation, quil serait dommage de passer ct ! Au pire,
si programmer nest pas toujours une partie de plaisir pour tout le monde, il vaut
mieux que le programme obtenu dans la douleur soit intressant pour celui qui
la fait !
2. Bricoler. Ce que nous voulons dire par l, cest quil ne faut pas hsiter ttonner, tester, fouiller, faire, dfaire, casser, etc. Lordinateur est un outil exprimental. Mais sa programmation est elle aussi une activit exprimentale la base.
Mme si le programmeur aguerri trouvera la bonne solution du premier jet, il
est important pour le dbutant dapprendre connatre le langage et loutil de
programmation en jouant avec eux.
3. Faire volontairement des erreurs. Provoquer les erreurs pendant la phase dapprentissage pour mieux les connatre est le meilleur moyen de comprendre beaucoup de choses et aussi de reprer ces erreurs quand elles ne seront plus volontaires.
4. Rester (le) matre 18 (de la machine et de son programme). Que programmer soit
exprimental ne signifie pas pour autant quil faille faire nimporte quoi jusqu
ce que a marche plus ou moins. Il faut avancer progressivement, mthodiquement, en testant au fur et mesure, sans laisser passer la moindre erreur ou imprcision.
5. Debugger. Souvent, la connaissance du debuggeur (loutil pour rechercher les
bugs) est nglige et son apprentissage est repouss au stade avanc. Il sagit
pourtant dun outil essentiel pour comprendre ce qui se passe dans un programme,
mme dpourvu de bugs. Il faut donc le considrer comme essentiel et faisant
partie intgrante de la conception dun programme. L encore, un bon environnement de programmation facilite la tche.

Gardons bien prsents ces quelques principes car il est maintenant temps de...
passer notre premier programme !

18. Le vocabulaire nest pas choisi au hasard : un programme est une suite dordres, de commandes ou
dinstructions. On voit bien qui est le chef !

12

2. Bonjour, Monde !

Chapitre 2
Bonjour, Monde !
(Si certains collgiens sont arrivs ici, ils sont bien courageux ! Lorsque je disais tout
lheure quils pouvaient facilement apprendre programmer, je le pensais vraiment. Par contre,
cest avec un peu doptimisme que jai prtendu quils pouvaient le faire en lisant un polycopi
destin des ingnieurs. Enfin, je suis pris mon propre pige ! Alors, tout hasard, je vais
tenter dexpliquer au passage les mathmatiques qui pourraient leur poser problme.)

Si lon en croit de nombreux manuels de programmation, un premier programme


doit toujours ressembler a :
# i n c l u d e <iostream >
using namespace s t d ;
i n t main ( )
{
cout << " Hello , World ! " << endl ;
return 0;
}
Eh bien, allons-y ! Dcortiquons-le ! Dans ce programme, qui affiche lcran 1 le texte
"Hello, World!", les lignes 1 et 2 sont des instructions magiques 2 qui servent pouvoir utiliser dans la suite cout et endl. La ligne 4 int main() dfinit une fonction appele
main(), qui renvoie 3 un nombre entier. Cette fonction est spciale car cest la fonction
principale dun programme C++, celle qui est appele automatiquement 4 quand le
1. Cette expression, vestige de lpoque o les ordinateurs taient dots dun cran capable de nafficher que des caractres et non des graphiques (courbes, dessins, etc.), signifie aujourdhui que laffichage
se fera dans une fentre simulant lcran dun ordinateur de cette poque. Cette fentre est appele terminal, console, fentre de commande, fentre DOS, xterm, etc. suivant les cas. Souvenons nous avec
un minimum de respect que ctait dj un progrs par rapport la gnration prcdente, dpourvue
dcran et qui utilisait une imprimante pour communiquer avec lhomme... ce qui tait relativement peu
interactif !
2. Entendons par l des instructions que nous nexpliquons pas pour linstant. Il ny a (mal ?)heureusement rien de magique dans la programmation.
3. On dit aussi retourne. A qui renvoie-t-elle cet entier ? Mais celui qui la appele, voyons !
4. Voil, maintenant vous savez qui appelle main(). Dans un programme, les fonctions sappellent
les unes les autres. Mais main() nest appele par personne puisque cest la premire de toutes. (Du
moins en apparence car en ralit le programme a plein de choses faire avant darriver dans main()
et il commence par plusieurs autres fonctions que le programmeur na pas connatre et qui finissent
par appeler main(). Dailleurs, si personne ne lappelait, qui main() retournerait-elle un entier ?)

2. Bonjour, Monde !

programme est lanc 5 . Dlimite par les accolades ({ ligne 5 et } ligne 8), la fonction
main() se termine ligne 7 par return 0; qui lui ordonne de retourner lentier 0. Notons
au passage que toutes les instructions se terminent par un point-virgule ;. Enfin, la
ligne 6, seule ligne "intressante", cout << "Hello, World!"<< endl; affiche, grce la
variable 6 cout qui correspond la sortie console 7 , des donnes spares par des <<. La
premire de ces donnes est la chane de caractres 8 "Hello, World!". La deuxime, endl,
est un retour la ligne 9 .
Ouf ! Que de termes en italique. Que de concepts essayer dexpliquer ! Et pour
un programme aussi simple ! Mais l nest pas le problme. Commencer par expliquer
ce programme, cest tre encore dans le vide, dans le magique, dans labstrait, dans
lapproximatif. Nous ne sommes pas rellement matres de la machine. Taper des instructions et voir ce qui se passe sans comprendre ce qui se passe nest pas raisonnable.
En fait, cest mme trs dommageable pour la suite. On ne donne pas efficacement
dordre quelquun sans comprendre comment il fonctionne ni ce que les ordres donns entranent comme travail. De mme,
on ne programme pas convenablement sans comprendre ce que lordinateur
aura exactement besoin de faire pour excuter ce programme.
Cest toute cette approche qui est nglige quand on commence comme nous venons
de le faire. Donc...
Stop ! Stop ! Stop ! Faux dpart ! On reprend le :

5. Je savais bien que vouloir expliquer tous les barbarismes propres aux informaticiens minterromprait souvent. Mais bon. Donc, un programme dmarre ou est lanc. Aprs quoi, il sexcute ou tourne.
Enfin, il se termine ou meurt.
6. Les donnes sont ranges ou stockes dans des variables qui mmorisent des valeurs. Ces variables ne
sont dailleurs pas toujours variables au sens usuel, puisque certaines sont constantes !
7. Quest-ce que je disais ! On affiche dans une fentre console !
8. En clair, un texte.
9. Ce qui signifie que la suite de laffichage sur la console se fera sur une nouvelle ligne.

14

2. Bonjour, Monde!

2.1. Lordinateur

Chapitre 2 (deuxime essai)


Comment a marche ?
Le problme avec le programme prcdent est quil est trs loin de ce quun ordinateur sait faire naturellement. En fait, un ordinateur ne sait pas faire de C++. Il ne
sait que calculer 10 , transformer des nombres en autres nombres. Bien que peu comprhensible pour le dbutant, un programme en C++ se veut le plus proche possible de
lHomme, tout en restant videmment accessible 11 la machine. Le C++ est un langage trs complet, peut-tre mme trop. Il peut tre relativement proche de la machine
si ncessaire et au contraire de "haut niveau" quand il le faut. La largeur de son spectre
est une des raisons de son succs. Cest aussi ce qui fait que son apprentissage complet
demande un long travail et nous ne verrons ici quun partie restreinte du C++ !

2.1

Lordinateur

Pour savoir ce quun ordinateur sait vraiment faire, il faut commencer par son organe principal : le micro-processeur.

2.1.1

Le micro-processeur

Quel quil soit 12 et quelle que soit sa vitesse 13 , un micro-processeur ne sait faire que
des choses relativement basiques. Sans tre exhaustif, retenons juste ceci :
Il sait excuter une suite ordonne dinstructions.
Il possde un petit nombre de mmoires internes appeles registres.
Il dialogue avec le monde extrieur via de la mmoire 14 en plus grande quantit
que ses registres.
Cette mmoire contient, sous forme de nombres, les instructions excuter et les
donnes sur lesquelles travailler.
Les instructions sont typiquement :
Lire ou crire un nombre dans un registre ou en mmoire.
Effectuer des calculs simples : addition, multiplication, etc.
Tester ou comparer des valeurs et dcider ventuellement de sauter une
autre partie de la suite dinstructions.
10. Un computer, quoi !
11. Cette notion est videmment dpendante de notre savoir faire informatique linstant prsent.
Les premiers langages taient plus loigns de lHomme car plus proches de la machine qui tait alors
rudimentaire, et lon peut envisager que les futurs langages seront plus proches de lHomme.
12. Pentium ou autre
13. Plus exactement la frquence laquelle il excute ses instructions. Aujourdhui lhorloge va environ 3GHz. (Mais attention : une instruction demande plus dun cycle dhorloge !)
14. Aujourdhui, typiquement 1Go (giga-octets), soit 1024 1024 1024 mmoires de 8 bits (mmoires
pouvant stocker des nombres entre 0 et 255).

15

2.1. Lordinateur

2. Bonjour, Monde !

Voici par exemple ce que doit faire le micro-processeur quand on lui demande
dexcuter "c=3a+2b;" en C++, o a,b,c sont trois variables entires :
00415A61

00415A64
00415A67
00415A6A
00415A6D

mov

eax,dword ptr [a] //


//
//
imul eax,eax,3
//
mov ecx,dword ptr [b] //
lea edx,[eax+ecx*2]
//
mov dword ptr [c],edx //
//
//

mettre dans le registre eax


le contenu de ladresse o
est mmorise la variable a
effectuer eax=eax*3
idem mais b dans ecx
effectuer edx=eax+ecx*2
mettre le contenu du registre edx
ladresse o est mmorise la
variable c

Sous lenvironnement Visual Studio que nous utiliserons, ce programme est dsign comme du Code Machine. Le nombre au dbut de chaque ligne est une adresse.
Nous allons en reparler. A part lui, le reste est relativement lisible pour lHomme (attention, cest moi qui ai ajout les remarques sur le cot droit !). Ceci parce quil sagit
dun programme en langage assembleur, cest--dire un langage o chaque instruction est vraiment une instruction du micro-processeur, mais o le nom de ces instructions ainsi que leurs arguments sont explicites. En ralit, le micro-processeur ne comprend pas lassembleur. Comprendre "mov eax,dword ptr [a]" lui demanderait
non seulement de dcoder cette suite de symboles, mais aussi de savoir o est range
la variable a. Le vrai langage du micro-processeur est le langage machine, dans lequel
les instructions sont des nombres. Voici ce que a donne pour notre "c=3a+2b;" :
00415A61
00415A64
00415A67
00415A6A
00415A6D

8B
6B
8B
8D
89

45
C0
4D
14
55

F8
03
EC
48
E0

A part encore une fois la colonne de gauche, chaque suite de nombres 15 correspond
videmment une instruction prcise. Cest tout de suite moins comprhensible 16 !
Notons que chaque micro-processeur son jeu dinstructions ce qui veut dire que la traduction de c=3a+2b; en la suite de nombres 8B45F86BC0038B4DEC8D14488955E0
est propre au Pentium que nous avons utilis pour notre exemple :
Une fois traduit en langage machine pour un micro-processeur donn, un
programme C++ na de sens que pour ce micro-processeur.
Remarquons aussi que les concepteurs du Pentium ont dcid de crer une instruction
spcifique pour calculer edx=eax+ecx2 en une seule fois car elle est trs frquente. Si
on avait demand c=3a+3b;, notre programme serait devenu :
00415A61
00415A64
00415A67
00415A6A
00415A6D
00415A6F

8B
6B
8B
6B
03
89

45
C0
4D
C9
C1
45

F8
03
EC
03
E0

mov
imul
mov
imul
add
mov

eax,dword ptr [a]


eax,eax,3
ecx,dword ptr [b]
ecx,ecx,3
eax,ecx
dword ptr [c],eax

15. Nombres un peu bizarres, certes, puisquil contiennent des lettres. Patience, jeune Padawan ! Nous
en reparlons aussi tout de suite !
16. Et pourtant, les informaticiens programmaient comme cela il ny a pas si longtemps. Ctait dj
trs bien par rapport lpoque antrieure o il fallait programmer en base 2... et beaucoup moins bien
que lorsquon a pu enfin programmer en assembleur !

16

2. Bonjour, Monde !

2.1. Lordinateur

car "lea edx,[eax+ecx*3]" nexiste pas !


Mais revenons nos nombres...

2.1.2

La mmoire

La mmoire interne du micro-processeur est gre comme des registres, un peu


comme les variables du C++, mais en nombre prdfini. Pour stocker 17 la suite dinstructions lui fournir, on utilise de la mmoire en quantit bien plus importante, dsigne en gnral par la mmoire de lordinateur. Il sagit des fameuses "barrettes" 18 de
mmoire que lon achte pour augmenter la capacit de sa machine et dont les prix
fluctuent assez fortement par rapport au reste des composants dun ordinateur. Cette
mmoire est dcoupe en octets. Un octet 19 correspond un nombre binaire de 8 bits 20 ,
soit 28 = 256 valeurs possibles. Pour se reprer dans la mmoire, il nest pas question de donner des noms chaque octet. On numrote simplement les octets et on
obtient ainsi des adresses mmoire. Les nombres 00415A61, etc. vus plus haut sont des
adresses ! Au dbut, ces nombres taient crits en binaire, ce qui tait exactement ce
que comprenait le micro-processeur. Cest devenu draisonnable quand la taille de
la mmoire a dpass les quelques centaines doctets. Le contenu dun octet de mmoire tant lui aussi donn sous la forme dun nombre, on a opt pour un systme
adapt au fait que ces nombres sont sur 8 bits : plutt que dcrire les nombre en binaire, le choix de la base 16 permettait de reprsenter le contenu dun octet sur deux
chiffres (0,1,...,9,A,B,C,D,E,F). Le systme hexadcimal 21 tait adopt... Les conversions
de binaire hexadcimal sont trs simples, chaque chiffre hexadcimal valant pour un
paquet de 4 bits, alors quentre binaire et dcimal, cest moins immdiat. Il est aujourdhui encore utilis quand on dsigne le contenu dun octet ou une adresse 22 . Ainsi,
notre fameux c=3a+2b; devient en mmoire :
17. Encore un anglicisme...
18. Aujourdhui, typiquement une ou plusieurs barrettes pour un total de 1 ou 2Go, on la dj dit.
Souvenons nous avec une larme loeil des premiers PC qui avaient 640Ko (kilo-octet soit 1024 octets),
voire pour les plus ags dentre nous des premiers ordinateurs personnels avec 4Ko, ou mme des
premires cartes programmables avec 256 octets !
19. byte en anglais. Attention donc ne pas confondre byte et bit, surtout dans des abrviations comme
512kb/s donnes pour le dbit dun accs internet... b=bit, B=byte=8 bits
20. Le coin des collgiens : en binaire, ou base 2, on compte avec deux chiffres au lieu de dix dhabitude (cest dire en dcimal ou base 10). Cela donne : 0, 1, 10, 11, 100, 101, 110, 111, ... Ainsi, 111 en
binaire vaut 7 . Chaque chiffre sappelle un bit. On voit facilement quavec un chiffre on compte de 0
1 soit deux nombres possibles ; avec deux chiffres, de 0 3, soit 4 = 2 2 nombres ; avec 3 chiffres, de
0 7, soit 8 = 2 2 2 nombres. Bref avec n bits, on peut coder 2n (2 multipli par lui-mme n fois)
nombres. Je me souviens avoir appris la base 2 en grande section de maternelle avec des cubes en bois !
trange programme scolaire. Et je ne dis pas a pour me trouver une excuse dtre devenu informaticien.
Quoique...
21. Coin des collgiens (suite) : en base 16, ou hexadcimal, on compte avec 16 chiffres. Il faut inventer des chiffres au del de 9 et on prend A,B,C,D,E,F. Quand on compte, cela donne : 0, 1, 2, ..., 9, A, B,
C, D, E, F, 10, 11, 12, 13, ..., 19, 1A, 1B, 1C, ... Ainsi 1F en hexadcimal vaut 31. Avec 1 chiffre, on compte
de 0 15 soit 16 nombres possibles ; avec 2 chiffres, de 0 255 soit 256 = 16 16 nombres possibles, etc.
Un octet peut scrire avec 8 bits en binaire, ou 2 nombres en hexadcimal et va de 0 255, ou 11111111
en binaire, ou FF en hexadcimal.
22. Dans ce cas, sur plus de 2 chiffres : 8 pour les processeurs 32 bits, 16 pour les processeurs 64 bits.

17

2.1. Lordinateur

2. Bonjour, Monde !

adresse mmoire contenu


reprsente
00415A61
8B
00415A62
45
mov eax,dword ptr [a]
00415A63
F8
00415A64
6B
00415A65
C0
imul eax,eax,3
...
...
La mmoire ne sert pas uniquement stocker la suite dinstructions excuter mais
aussi toutes les variables et donnes du programme, les registres du micro-processeur
tant insuffisants. Ainsi nos variables a,b,c sont stockes quelque part en mmoire sur
un nombre doctets suffisant 23 pour reprsenter des nombres entiers (ici 4 octets) et
dans un endroit dcid par le C++, de tel sorte que linstruction 8B45F8 aille bien
chercher la variable a ! Cest un travail pnible, que le C++ fait pour nous et que les
programmeurs faisaient autrefois la main 24 . Bref, on a en plus 25 :
adresse mmoire contenu reprsente
...
...
00500000
a1
00500001
a2
a
00500002
a3
00500003
a4
00500004
b1
00500005
b2
b
00500006
b3
00500007
b4
...
...
o les octets a1 , ..., a4 combins donnent lentier a sur 32 bits. Certains processeurs (dits
big-endian) 26 dcident a = a1 a2 a3 a4 , dautres (little-endian) 27 que a = a4 a3 a2 a1 . Cela
signifie que :
Tout comme pour les instructions, un nombre stock par un microprocesseur dans un fichier peut ne pas tre comprhensible par un autre
micro-processeur qui relit le fichier !
23. Les variables ayant plus de 256 valeurs possibles sont forcment stockes sur plusieurs octets.
Ainsi, avec 4 octets on peut compter en binaire sur 4 8 = 32 bits, soit 232 valeurs possibles (plus de 4
milliards).
24. Ce qui tait le plus pnible ntait pas de dcider o il fallait ranger les variables en mmoire, mais
dajuster les instructions en consquence. Si on se trompait, on risquait dcrire au mauvais endroit de
la mmoire. Au mieux, cela effaait une autre variable ce comportement est encore possible de nos
jours au pire, cela effaait des instructions et le programme pouvait faire de "grosses btises" ceci
est aujourdhui impossible sous Windows ou Linux, et ne concerne plus que certains systmes.
25. Nous faisons ici un horrible mensonge des fins simplificatrices. Dans notre cas, les variables
taient des variables locales la fonction main() donc stockes dans la pile. Elles ne sont pas une
adresse mmoire dfinie lavance de manire absolue mais une adresse relative lemplacement
o la fonction rangera ses variables locales en fonction de ce que le programme aura fait avant. Cela
explique la simplicit de linstruction mov eax,dword ptr [a] dans notre cas. Nous verrons tout
cela plus tard.
26. Comme les PowerPC des vieux Macs
27. Comme les processeurs Intel et AMD

18

2. Bonjour, Monde !

2.1.3

2.1. Lordinateur

Autres Composants

Micro-processeur et mmoire : nous avons vu le principal. Compltons le tableau


avec quelques autres lments importants de lordinateur.
Types de mmoire
La mmoire dont nous parlions jusquici est de la mmoire vive ou RAM. Elle est
rapide 28 mais a la mauvaise ide de seffacer quand on teint lordinateur. Il faut donc
aussi de la mmoire morte ou ROM, cest--dire de la mmoire conservant ses donnes
quand lordinateur est teint mais qui en contre-partie ne peut tre modifie 29 . Cette
mmoire contient en gnral le minimum pour que lordinateur dmarre et excute
une tche prdfinie. Initialement, on y stockait les instructions ncessaires pour que le
programmeur puisse remplir ensuite la RAM avec les instructions de son programme.
Il fallait retaper le programme chaque fois 30 ! On a donc rapidement eu recours des
moyens de stockage pour sauver programmes et donnes lextinction de lordinateur. Il
suffisait alors de mettre en ROM le ncessaire pour grer ces moyens de stockages.
Moyens de stockage
Certains permettent de lire des donnes, dautres den crire, dautres les deux
la fois. Certains ne dlivrent les donnes que dans lordre, de manire squentielle,
dautres, dans lordre que lon veut, de manire alatoire. Ils sont en gnral bien plus
lents que la mmoire et cest srement ce quil faut surtout retenir ! On recopie donc en
RAM la partie des moyens de stockage sur laquelle on travaille.
Faire travailler le micro-processeur avec le disque dur est BEAUCOUP plus
lent quavec la mmoire (1000 fois plus lent en temps daccs, 100 fois plus
en dbit) a
a. Rajoutez un facteur 50 supplmentaire entre la mmoire et la mmoire cache du processeur !

Au dbut, les moyens de stockages taient mcaniques : cartes ou bandes perfores. Puis ils devinrent magntiques : mini-cassettes 31 , disquettes 32 , disques durs 33 ou
bandes magntiques. Aujourdhui, on peut rajouter les CD, DVD, les cartes mmoire,
les "cls USB", etc, etc.
28. Moins que les registres, ou mme que le cache mmoire du processeur, dont nous ne parlerons pas
ici.
29. Il est pnible quune ROM ne puisse tre modifie. Alors, une poque, on utilisait des mmoires
modifiables malgr tout, mais avec du matriel spcialis (EPROMS). Maintenant, on a souvent recours
de la mmoire pouvant se modifier de faon logicielle (mmoire "flashable") ou, pour de trs petites
quantits de donnes, une mmoire consommant peu (CMOS) et complte par une petite pile. Dans
un PC, la mmoire qui sert dmarrer sappelle le BIOS. Il est flashable et ses paramtres de rglage
sont en CMOS. Attention lusure de la pile !
30. A chaque fois quon allumait lordinateur mais aussi chaque fois que le programme plantait et
seffaait lui-mme, cest--dire la plupart du temps !
31. Trs lent et trs peu fiable, mais le quotidien des ordinateurs personnels.
32. Le luxe. Un lecteur de 40Ko cotait 5000F !
33. Les premiers taient de vritables moteurs de voiture, rservs aux importants centres de calcul.

19

2.2. Systme dexploitation

2. Bonjour, Monde !

Priphriques
On appelle encore priphriques diffrents appareils relis lordinateur : clavier,
souris, cran, imprimante, modem, scanner, etc. Ils taient initialement l pour servir
dinterface avec lHomme, comme des entres et des sorties entre le micro-processeur
et la ralit. Maintenant, il est difficile de voir encore les choses de cette faon. Ainsi
les cartes graphiques, qui pouvaient tre considres comme un priphrique allant
avec lcran, sont-elles devenues une partie essentielle de lordinateur, vritables puissances de calcul, tel point que certains programmeur les utilisent pour faire des calculs sans mme afficher quoi que ce soit. Plus encore, cest lordinateur qui est parfois
juste considr comme maillon entre diffrents appareils. Qui appellerait priphrique
un camscope quon relie un ordinateur pour envoyer des vidos sur internet ou les
transfrer sur un DVD ? Ce serait presque lordinateur qui serait un priphrique du
camscope !

2.2

Systme dexploitation

Notre vision jusquici est donc la suivante :


1. Le processeur dmarre avec les instructions prsentes en ROM.
2. Ces instructions lui permettent de lire dautres instructions prsentes sur le disque
dur et quil recopie en RAM.
3. Il excute les instructions en question pour il lire des donnes (entres) prsentes
elles-aussi sur le disque dur et gnrer de nouvelles donnes (sorties). A moins
que les entres ou les sorties ne soient changes via les priphriques.
Assez vite, ce principe a volu :
1. Le contenu du disque dur a t organis en fichiers. Certains fichiers reprsentaient des donnes 34 , dautres des programmes 35 , dautres encore contenaient
eux-mmes des fichiers 36 .
2. Les processeurs devenant plus rapides et les capacits du disque dur plus importantes, on a eu envie de grer plusieurs programmes et den excuter plusieurs :
lun aprs lautre, puis plusieurs en mme temps (multi-tches), puis pour plusieurs utilisateurs en mme temps (multi-utilisateurs) 37 , enfin avec plusieurs processeurs par machine.
Pour grer tout cela, sest dgag le concept de systme dexploitation 38 . Windows, Unix
(dont linux) et MAC/OS sont les plus rpandus. Le systme dexploitation est aujourdhui responsable de grer les fichiers, les interfaces avec les priphriques ou les utilisateurs 39 , mais son rle le plus dlicat est de grer les programmes (ou tches ou
34. Les plus courantes taient les textes, o chaque octet reprsentait un caractre. Ctait le clbre
code ASCII (65 pour A, 66 pour B, etc.). A lre du multimdia, les formats sont aujourdhui nombreux,
concurrents, et plus ou moins normaliss.
35. On parle de fichier excutable...
36. Les rpertoires.
37. Aujourdhui, cest pire. Un programme est souvent lui mme en plusieurs parties sexcutant en
mme temps (les threads). Quant au processeur, il excute en permanence plusieurs instructions en mme
temps (on dit quil est super-scalaire) !
38. Operating System
39. Esprons quun jour les utilisateurs ne seront pas eux-aussi des priphriques !

20

2. Bonjour, Monde !

2.3. La Compilation

process) en train de sexcuter. Il doit pour cela essentiellement faire face deux problmes 40 :
1. Faire travailler le processeur successivement par petites tranches sur les diffrents programmes. Il sagit de donner la main de manire intelligente et quitable, mais aussi de replacer un process interrompu dans la situation quil avait
quitte lors de son interruption.
2. Grer la mmoire ddie chaque process. En pratique, une partie ajustable de
la mmoire est rserve chaque process. La mmoire dun process devient mmoire virtuelle : si un process est dplac un autre endroit de la mmoire physique
(la RAM), il ne sen rend pas compte. On en profite mme pour mettre temporairement hors RAM (donc sur disque dur) un process en veille. On peut aussi
utiliser le disque dur pour quun process utilise plus de mmoire que la mmoire
physique : mais attention, le disque tant trs lent, ce process risque de devenir
lui aussi trs lent.
Lorsquun process besoin de trop de mmoire, il utilise, sans prvenir, le disque dur la place de la mmoire et peut devenir trs lent.
On dit quil swappe (ou pagine). Seule sa lenteur (et le bruit du disque
dur !) permet en gnral de sen rendre compte (on peut alors sen assurer avec le gestionnaire de tche du systme).
Autre progrs : on gre maintenant la mmoire virtuelle de faon sparer les
process entre eux et, au sein dun mme process, la mmoire contenant les instructions de celle contenant les donnes. Il est rigoureusement impossible quun
process bugg puisse modifier ses instructions ou la mmoire dun autre process
en crivant un mauvais endroit de la mmoire 41 .
Avec larrive des systmes dexploitation, les fichiers excutables ont du sadapter
pour de nombreuse raisons de gestion et de partage de la mmoire. En pratique, un
programme excutable linux ne tournera pas sous Windows et rciproquement, mme
sils contiennent tous les deux des instructions pour le mme processeur.
Un fichier excutable est spcifique, non seulement un processeur donn,
mais aussi un systme dexploitation donn.
Au mieux, tout comme les versions successives dune famille de processeur essaient
de continuer comprendre les instructions de leurs prdcesseurs, tout comme les
versions successives dun logiciel essaient de pouvoir lire les donnes produites avec
les versions prcdentes, les diffrentes versions dun systme dexploitation essaient
de pouvoir excuter les programmes faits pour les versions prcdentes. Cest la compatibilit ascendante, que lon paye souvent au prix dune complexit et dune lenteur
accrues.

2.3

La Compilation

Tout en essayant de comprendre ce qui se passe en dessous pour en tirer des informations utiles comme la gestion de la mmoire, nous avons entrevu que transformer
40. Les processeurs ont videmment volu pour aider le systme dexploitation faire cela
efficacement.
41. Il se contente de modifier anarchiquement ses donnes, ce qui est dj pas mal !

21

2.3. La Compilation

2. Bonjour, Monde !

un programme C++ en un fichier excutable est un travail difficile mais utile. Certains logiciels disposant dun langage de programmation comme Maple ou Scilab ne
transforment pas leurs programmes en langage machine. Le travail de traduction est
fait lexcution du programme qui est alors analys au fur et mesure 42 : on parle
alors de langage interprt. Lexcution alors est videmment trs lente. Dautres langages, comme Java, dcident de rsoudre les problmes de portabilit, cest--dire de
dpendance au processeur et au systme, en plaant une couche intermdiaire entre
le processeur et le programme : la machine virtuelle. Cette machine, videmment crite
pour un processeur et un systme donns, peut excuter des programmes dans un
langage machine virtuel 43 , le "byte code". Un programme Java est alors traduit en son
quivalent dans ce langage machine. Le rsultat peut tre excut sur nimporte quelle
machine virtuelle Java. La contrepartie de cette portabilit est videmment une perte
defficacit.
La traduction en code natif ou en byte code dun programme sappelle la compilation 44 . Un langage compil est alors opposer un langage interprt. Dans le cas du C++
et de la plupart des langages compils (Fortran, C, etc), la compilation se fait vers du
code natif. On transforme un fichier source, le programme C++, en un fichier objet, suite
dinstructions en langage machine.
Cependant, le fichier objet ne se suffit pas lui-mme. Des instructions supplmentaires sont ncessaires pour former un fichier excutable complet :
de quoi lancer le main() ! Plus prcisment, tout ce que le process doit faire avant
et aprs lexcution de main().
des fonctions ou variables faisant partie du langage et que le programmeur utilise
sans les reprogrammer lui-mme, comme cout, cout|min()|, etc. Lensemble de
ces instructions constitue ce quon appelle une bibliothque 45 .
des fonctions ou variables programmes par le programmeur lui-mme dans
dautres fichiers source compils par ailleurs en dautres fichiers objet, mais quil
veut utiliser dans son programme actuel.
La synthse de ces fichiers en un fichier excutable sappelle ldition des liens. Le programme qui ralise cette opration est plus souvent appel linker quditeur de liens...

En rsum, la production du fichier excutable se fait de la faon suivante :


1. Compilation : fichier source fichier objet.
2. Link : fichier objet + autres fichiers objets + bibliothque standard ou
autres fichier excutable.
42. mme sil est parfois pr-trait pour acclrer lexcution.
43. Par opposition, le "vrai" langage machine du processeur est alors appel code natif.
44. Les principes de la compilation sont une des matires de base de linformatique, traditionnelle et
trs formatrice. Quand on sait programmer un compilateur, on sait tout programmer (videmment, un
compilateur est un programme ! On le programme avec le compilateur prcdent ! Mme chose pour les
systmes dexploitation...). Elle ncessite un cours part entire et nous nen parlerons pas ici !
45. Une bibliothque est en fait un ensemble de fichiers objets pr-existants regroups en un seul fichier. Il peut sagir de la bibliothque des fonctions faisant partie de C++, appele bibliothque standard,
mais aussi dune bibliothque supplmentaire fournie par un tiers.

22

2. Bonjour, Monde !

2.4

2.4. Lenvironnement de programmation

Lenvironnement de programmation

Lenvironnement de programmation est le logiciel permettant de programmer. Dans


notre cas il sagit de Visual Studio Express. Dans dautres cas, il peut simplement sagir
dun ensemble de programmes. Un environnement contient au minimum un diteur
pour crer les fichiers sources, un compilateur/linker pour crer les excutables, un debuggeur pour traquer les erreurs de programmation, et un gestionnaire de projet pour
grer les diffrents fichiers sources et excutables avec lesquels on travaille.
Nous reportons ici le lecteur au texte du premier TP. En plus de quelques notions
rudimentaires de C++ que nous verrons au chapitre suivant, quelques informations
supplmentaires sont utiles pour le suivre.

2.4.1

Noms de fichiers

Lextension (le suffixe) sert se reprer dans les types de fichier :


Un fichier source C++ se terminera par .cpp 46 .
Un fichier objet sera en .obj (Windows) ou .o (Linux/Mac)
Un fichier excutable en .exe (Windows) ou sans extension (Linux/Mac)
Nous verrons aussi plus loin dans le cours :
Les "en-tte" C++ ou headers servant tre inclus dans un fichier source : fichiers
.h
Les bibliothques (ensembles de fichiers objets archivs en un seul fichier) : fichiers .lib ou .dll (Windows) ou .a ou .so (Linux/Mac)

2.4.2

Debuggeur

Lorsquun programme ne fait pas ce quil faut, on peut essayer de comprendre ce


qui ne va pas en truffant son source dinstructions pour imprimer la valeur de certaines
donnes ou simplement pour suivre son droulement. Ca nest videmment pas trs
pratique. Il est mieux de pouvoir suivre son droulement instruction par instruction et
dafficher la demande la valeur des variables. Cest le rle du debuggeur 47 .
Lorsquun langage est interprt, il est relativement simple de le faire sexcute pas
pas car cest le langage lui-mme qui excute le programme. Dans le cas dun langage
compil, cest le micro-processeur qui excute le programme et on ne peut pas larrter
chaque instruction ! Il faut alors mettre en place des points darrt en modifiant temporairement le code machine du programme pour que le processeur sarrte lorsquil
atteint linstruction correspondant la ligne de source debugger. Si cest compliqu
mettre au point, cest trs simple utiliser, surtout dans un environnement de programmation graphique.
Nous verrons au fur et mesure des TP comment le debuggeur peut aussi inspecter
les appels de fonctions, espionner la modification dune variable, etc.
46. Un fichier en .c sera considr comme du C.
47. Dbogueur en franais !

23

2.4. Lenvironnement de programmation

2.4.3

2. Bonjour, Monde !

TP

Vous devriez maintenant aller faire le TP en annexe A.1. Si la pratique est essentielle, en retenir quelque chose est indispensable ! Vous y trouverez aussi comment
installer Visual sur votre ordinateur (lien http://imagine.enpc.fr/~monasse/
Imagine++ mentionn la fin du TP). Voici donc ce quil faut retenir du TP :
1. Sous Windows, toujours travailler en local et sauvegarder sur le
disque partag, cl USB, etc.
2. Utiliser CMake pour construire une solution Windows.
3. Nettoyer (clean) quand on quitte.
4. Lancer directement une excution sauve et gnre automatiquement.
Attention toutefois de ne pas confirmer lexcution si la gnration
sest mal passe.
5. Double-cliquer sur un message derreur positionne lditeur sur lerreur.
6. Toujours bien indenter.
7. Ne pas laisser passer des warnings !
8. Savoir utiliser le debuggeur.
9. Touches utiles :
F7

Build

F5

Start debugging

F10

Step over

F11

Step inside

Ctrl+K,Ctrl+F

Indent selection

Nous en savons maintenant assez pour apprendre un peu de C++...

24

3. Premiers programmes

Chapitre 3
Premiers programmes
Pars exprimenter au fur et mesure avec notre environnement de programmation, il est
temps dapprendre les premiers rudiments du C++. Nous allons commencer par programmer
nimporte comment... puis nous ajouterons un minimum dorganisation en apprenant faire
des fonctions.

On organise souvent un manuel de programmation de faon logique par rapport


au langage, en diffrents points successifs : les expressions, les fonctions, les variables,
les instructions, etc. Le rsultat est indigeste car il faut alors tre exhaustif sur chaque
point. Nous allons plutt ici essayer de voir les choses telles quelles se prsentent
quand on apprend : progressivement et sur un peu tous les sujets la fois 1 ! Ainsi, ce
nest que dans un autre chapitre que nous verrons la faon dont les fonctions mmorisent leurs variables dans la "pile".

3.1

Tout dans le main() !

Rien dans les mains, rien dans les poches... mais tout dans le main(). Voici comment
un dbutant programme 2 .
Cest dj une tape importante que de programmer au kilomtre, en plaant
lintgralit du programme dans la fonction main(). Lessentiel est avant
tout de faire un programme qui marche !

3.1.1

Variables

Types
Les variables sont des mmoires dans lesquelles sont stockes des valeurs (ou donnes). Une donne ne pouvant tre stocke nimporte comment, il faut chaque fois dcider de la place prise en mmoire (nombre doctets) et du format, cest--dire de la faon
dont les octets utiliss vont reprsenter les valeurs prises par la variable. Nous avons
dj rencontr les int qui sont le plus souvent aujourdhui stocks sur quatre octets,
1. La contre-partie de cette prsentation est que ce polycopi, sil est fait pour tre lu dans lordre, est
peut-tre moins adapt servir de manuel de rfrence. .
2. Et bien des lves, ds que le professeur nest plus derrire !

3.1. Tout dans le main() !

3. Premiers programmes

soit 32 bits, et pouvant prendre 232 = 4294967296 valeurs possibles 3 . Par convention,
les int stockent les nombres entiers relatifs 4 , avec autant de nombres ngatifs que de
nombres positifs 5 , soit, dans le cas de 32 bits 6 , de 2147483648 2147483647 suivant
une certaine correspondance avec le binaire 7 .
Dire quune variable est un int, cest prciser son type. Certains langages nont pas
la notion de type ou essaient de deviner les types des variables. En C++, cest initialement pour prciser la mmoire et le format des variables quelles sont types. Nous
verrons que le compilateur se livre un certain nombre de vrifications de cohrence
de type entre les diffrentes parties dun programme. Ces vrifications, pourtant bien
pratiques, ntaient pas faites dans les premires versions du C, petit frre du C++, car
avant tout, rptons-le :
Prciser un type, cest prciser la place mmoire et le format dune variable.
Le compilateur, sil pourra mettre cette information profit pour dtecter
des erreurs de programmation, en a avant tout besoin pour traduire le source
C++ en langage machine.
Dfinition, Affectation, Initialisation, Constantes
Avant de voir dautres types de variables, regardons sur un exemple la syntaxe
utiliser :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

int i ; / / Dfinition
i =2;
// Affectation
cout << i << " " ;
int j ;
j=i ;
i =1;
/ / Ne m o d i f i e que i , p a s j !
cout << i << " " << j << " " ;
i n t k , l ,m; / / D f i n i t i o n m u l t i p l e
k= l = 3 ;
/ / Affectation multiple
m= 4 ;
cout << k << " " << l << " " << m << " " ;
i n t n=5 , o=n , p=INT_MAX ; / / I n i t i a l i s a t i o n s
cout << n << " " << o << " " << p << endl ;
i n t q=r = 4 ; / / E r r e u r !
const i n t s =12;
s =13; / / Erreur !
Dans ce programme :
3. Nous avons aussi vu que cette simple ide donne dj lieu deux faons dutiliser les 4 octets :
big-endian ou little-endian.
4. Coin des collgiens : cest dire 0, 1, 2, ... mais aussi 1, 2, 3, ...
5. un prs !
6. En fait, les int sadaptent au processeur et un programme compil sur un processeur 64 bits aura
des int sur 64 bits ! Si lon a besoin de savoir dans quel cas on est, le C++ fournit les constantes INT_MIN
et INT_MAX qui sont les valeurs minimales et maximales prises par les int.
7. L, tout le monde fait pareil ! On compte en binaire partir de 0, et arriv 2147483647,
le suivant est -2147483648, puis -2147483647 et ainsi de suite jusqu -1. On a par exemple :
0 = 000...000, 1 = 000...001, 2147483647 = 011...111, 2147483648 = 100...000, 2147483647 =
100..001, 2 = 111...110, 1 = 111...111

26

3. Premiers programmes

3.1. Tout dans le main() !

Les lignes 1 et 2 dfinissent une variable nomme i 8 de type int puis affecte
2 cette variable. La reprsentation binaire de 2 est donc stocke en mmoire
l o le compilateur dcide de placer i. Ce qui suit le "double slash" ( // ) est une
remarque : le compilateur ignore toute la fin de la ligne, ce qui permet de mettre
des commentaires aidant la comprhension du programme.
La ligne 3 affiche la valeur de i puis un espace (sans aller la ligne)
Les lignes 4, 5 et 6 dfinissent un int nomm j , recopie la valeur de i, soit 2,
dans j , puis mmorise 1 dans i. Notez bien que i et j sont bien deux variables
diffrentes : i passe 1 mais j reste 2 !
La ligne 8 nous montre comment dfinir simultanment plusieurs variables du
mme type.
La ligne 9 nous apprend que lon peut affecter des variables simultanment une
mme valeur.
A la ligne 12, des variables sont dfinies et affectes en mme temps. En fait,
on parle plutt de variables initialises : elles prennent une valeur initiale en
mme temps quelles sont dfinies. Notez que, pour des raisons defficacit, les
variables ne sont pas initialises par dfaut : tant quon ne leur a pas affect une
valeur et si elles nont pas t initialises, elles valent nimporte quoi 9 !
Attention toutefois, il est inutile de tenter une initialisation simultane. Cest interdit. La ligne 14 provoque une erreur.
Enfin, on peut rajouter const devant le type dune variable : celle-ci devient alors
constante et on ne peut modifier son contenu. La ligne 15 dfinit une telle variable
et la ligne 16 est une erreur.
En rsum, une fois les lignes 14 et 16 supprimes, ce (passionnant !) programme affiche 10 :
2 1 2 3 3 4 5 5 2147483647
Les noms de variable sont composs uniquement des caractres a z (et majuscules), chiffres et underscore _ (vitez celui-ci, il nest pas trs esthtique), mais ne
peuvent pas commencer par un chiffre. Nutilisez pas de caractres accentus, car cela
pose des problmes de portabilit.
Porte
Dans lexemple prcdent, les variables ont t dfinies au fur et mesure des besoins. Ce nest pas une vidence. Par exemple, le C ne permettait de dfinir les variables
que toutes dun coup au dbut du main(). En C++, on peut dfinir les variables en cours
de route, ce qui permet davantage de clart. Mais attention :
8. Le nom dune variable est aussi appel identificateur. Les messages derreur du compilateur utiliseront plutt ce vocabulaire !
9. Ainsi, un entier ne vaut pas 0 lorsquil est cr et les octets o il est mmoris gardent la valeur quil
avaient avant dtre rquisitionns pour stocker lentier en question. Cest une mauvaise ide dutiliser
la valeur dune variable qui vaut nimporte quoi et un compilateur mettra gnralement un warning si
on utilise une variable avant de lui fournir une valeur !
10. du moins sur une machine 32 bits, cf. remarque prcdente sur INT_MAX

27

3.1. Tout dans le main() !

3. Premiers programmes

les variables "nexistent" (et ne sont donc utilisables) qu partir de la ligne


o elles sont dfinies. Elles ont une dure de vie limite et meurent ds que
lon sort du bloc limit par des accolades auquel elles appartiennent a . Cest
ce quon appelle la porte dune variable.
a. Cest un peu plus compliqu pour les variables globales. Nous verrons a aussi...

Ainsi, en prenant un peu davance sur la syntaxe des tests, que nous allons voir tout
de suite, le programme suivant provoque des erreurs de porte aux lignes 2 et 8 :
int i ;
i= j ; / / Erreur : j n e x i s t e pas encore !
i n t j =2;
i f ( j >1) {
i n t k=3;
j =k ;
}
i =k ; / / E r r e u r : k n e x i s t e p l u s .
Autres types
Nous verrons les diffrents types au fur et mesure. Voici malgr tout les plus
courants :
i n t i =3;
double x = 1 2 . 3 ;
char c= A ;
s t r i n g s= " hop " ;
bool t = t r u e ;

//
//
//
//
//

Entier r e l a t i f
Nombre r e l ( d o u b l e p r c i s i o n )
Caractre
Chane de c a r a c t r e s
B o o l e n ( v r a i ou f a u x )

Les nombres rels sont en gnral approchs par des variables de type double ("double
prcision", ici sur 8 octets). Les caractres sont reprsents par un entier sur un octet (sur certaines machines de -128 127, sur dautres de 0 255), la correspondance
caractre/entier tant celle du code ASCII (65 pour A, 66 pour B, etc.), quil nest heureusement pas besoin de connatre puisque la syntaxe A entre simples guillemets est
traduite en 65 par le compilateur, etc. Les doubles guillemets sont eux rservs aux
"chanes" de caractres 11 . Enfin, les boolens sont des variables qui valent vrai (true)
ou faux ( false ).
Voici, pour information, quelques types supplmentaires :
f l o a t y=1.2 f ;
unsigned i n t j = 4 ;
signed char d= 128;
unsigned char d = 2 5 4 ;
complex<double > z ( 2 , 3 ) ;

//
//
//
//
//

Nombre
Entier
Entier
Entier
Nombre

r e l simple prcision
naturel
r e l a t i f un o c t e t
n a t u r e l un o c t e t
complexe

o lon trouve :
les float , nombres rels moins prcis mais plus courts que les double, ici sur
4 octets (Les curieux pourront explorer la documentation de Visual et voir que
11. Attention, lutilisation des string ncessite un #include<string> au dbut du programme.

28

3. Premiers programmes

3.1. Tout dans le main() !

les float valent au plus FLT\_MAX (ici, environ 3.4e+38 12 ) et que leur valeur la
plus petite strictement positive est FLT\_MIN (ici, environ 1.2e38), de mme
que pour les double les constantes DBL\_MAX et DBL\_MIN valent ici environ
1.8e+308 et 2.2e308),
les unsigned int, entiers positifs utiliss pour aller plus loin que les int dans les
positifs (de 0 UINT_MAX, soit 4294967295 dans notre cas),
les unsigned char, qui vont de 0 255,
les signed char, qui vont de -128 127,
et enfin les nombres complexes 13 .

3.1.2

Tests

Tests simples
Les tests servent excuter telle ou telle instruction en fonction de la valeur dune
ou de plusieurs variables. Ils sont toujours entre parenthses. Le et scrit &&, le
ou ||, la ngation !, lgalit ==, la non-galit !=, et les ingalits >, >=, < et <=.
Si plusieurs instructions doivent tre excutes quand un test est vrai ( if ) ou faux
(else), on utilise des accolades pour les regrouper. Tout cela se comprend facilement
sur lexemple suivant :
i f ( i ==0) / / i e s t i l n u l ?
cout << " i e s t nul " << endl ;
...
i f ( i >2) / / i e s t i l p l u s g r a n d que 2?
j =3;
else
j =5;
/ / S i on e s t i c i , c e s t que i <=2
...
/ / Cas p l u s c o m p l i q u !
i f ( i ! = 3 || ( j ==2 && k ! = 3 ) || ! ( i > j && i >k ) ) {
/ / I c i , i e s t d i f f r e n t d e 3 ou a l o r s
/ / j v a u t 2 e t k e s t d i f f r e n t d e 3 ou a l o r s
/ / on n a p a s i p l u s g r a n d a l a f o i s d e j e t d e k
cout << " Une premire i n s t r u c t i o n " << endl ;
cout << " Une deuxime i n s t r u c t i o n " << endl ;
}
Les variables de type boolen servent mmoriser le rsultat dun test :
bool t = ( ( i ==3)||( j = = 4 ) ) ;
if ( t )
k=5;
12. Coin des collgiens : 1038 ou 1e+38 vaut 1 suivi de 38 zros, 1038 ou 1e38 vaut 0.000...01 avec
37 zros avant le 1. En compliquant : 3.4e+38 vaut 34 suivis de 37 zros (38 chiffres aprs le 3) et 1.2e38
vaut 0.00...012 toujours avec 37 zros entre la virgule et le 1 (le 1 est la place 38).
13. Il est trop tt pour comprendre la syntaxe "objet" de cette dfinition mais il nous parait important
de mentionner ds maintenant que les complexes existent en C++.
Coin des collgiens : pas de panique ! Vous apprendrez ce que sont les nombres complexes plus tard.
Ils ne seront pas utiliss dans ce livre.

29

3.1. Tout dans le main() !

3. Premiers programmes

Enfin, une dernire chose trs importante : penser utiliser == et non = sous peine
davoir des surprises 14 . Cest peut-tre lerreur la plus frquente chez les dbutants.
Elle est heureusement signale aujourdhui par un warning...
Attention : utiliser if ( i==3) ... et non if ( i=3) ... !
Le "switch"
On a parfois besoin de faire telle ou telle chose en fonction des valeurs possibles
dune variable. On utilise alors souvent linstruction switch pour des raisons de clart
de prsentation. Chaque cas possible pour les valeurs de la variable est prcis avec
case et doit se terminer par break 15 . Plusieurs case peuvent tre utiliss pour prciser
un cas multiple. Enfin, le mot cl default, placer en dernier, correspond aux cas non
prciss. Le programme suivant 16 ragit aux touches tapes au clavier et utilise un
switch pour afficher des commentaires passionnants !
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# i n c l u d e <iostream >
using namespace s t d ;
# i n c l u d e <conio . h> / / Non s t a n d a r d !
i n t main ( )
{
bool f i n i = f a l s e ;
char c ;
do {
c=_ g e t c h ( ) ; / / Non s t a n d a r d !
switch ( c ) {
case a :
cout << " Vous avez tap a ! " << endl ;
break ;
case f :
cout << " Vous avez tap f . Au r e v o i r ! " << endl ;
f i n i =true ;
break ;
case e :
case i :
case o :
case u :
case y :
cout << " Vous avez tap une a u t r e v o y e l l e ! " << endl ;
break ;
14. Faire if ( i=3) ... affecte 3 i puis renvoie 3 comme rsultat du test, ce qui est considr comme
vrai car la convention est quun boolen est en fait un entier, faux sil est nul et vrai sil est non nul !
15. Cest une erreur grave et frquente doublier le break. Sans lui, le programme excute aussi les
instructions du cas suivant !
16. Attention, un cin >> c, instruction que nous verrons plus loin, lit bien un caractre au clavier
mais ne ragit pas chaque touche : il attend quon appuie sur la touche Entre pour lire dun coup
toutes les touches frappes ! Rcuprer juste une touche la console nest malheureusement pas standard et nest plus trs utilis dans notre monde dinterfaces graphiques. Sous Windows, il faudra utiliser
_getch() aprs avoir fait un #include <conio.h> (cf. lignes 3 et 10) et sous Unix getch() aprs avoir fait
un #include <curses.h>.

30

3. Premiers programmes

26
27
28
29
30
31
32 }

3.1. Tout dans le main() !

default :
cout << " Vous avez tap a u t r e chose ! " << endl ;
break ;
}
} while ( ! f i n i ) ;
return 0;

Si vous avez tout compris, le switch prcdant ceci est quivalent 17 :


i f ( c== a )
cout << " Vous avez tap
e l s e i f ( c== f ) {
cout << " Vous avez tap
f i n i =true ;
} e l s e i f ( c== e || c== i
cout << " Vous avez tap
else
cout << " Vous avez tap

a ! " << endl ;


f . Au r e v o i r ! " << endl ;
|| c== o || c== u || c== y )
une a u t r e v o y e l l e ! " << endl ;
a u t r e chose ! " << endl ;

Avant tout, rappelons la principale source derreur du switch :


Dans un switch, ne pas oublier les break !
Vous avez pu remarquer cette ligne 2 un peu cryptique. Un namespace est un prfixe pour certains objets. Le prfixe des objets standard du langage est std. Ainsi cout
et endl ont pour nom complet std :: cout et std :: endl. La ligne 2 permet domettre ce
prfixe.

3.1.3

Boucles

Il est difficile de faire un programme qui fait quelque chose sans avoir la possibilit
dexcuter plusieurs fois la mme instruction. Cest le rle des boucles. La plus utilise est le for () , mais a nest pas la plus simple comprendre. Commenons par le
do ... while, qui "tourne en rond" tant quun test est vrai. Le programme suivant attend
que lutilisateur tape au clavier un entier entre 1 et 10, et lui ritre sa question jusqu
obtenir un nombre correct :
1
2
3
4
5
6
7
8
9
10

# i n c l u d e <iostream >
using namespace s t d ;
i n t main ( )
{
int i ;
do { / / Dbut d e l a b o u c l e
cout << "Un nombre e n t r e 1 e t 1 0 , SVP : " ;
c i n >> i ;
} while ( i <1 || i > 1 0 ) ; / / On r e t o u r n e au d b u t d e l a b o u c l e s i
17. On voit bien que le switch nest pas toujours plus clair ni plus court. Cest comme tout, il faut
lutiliser bon escient... Et plus nous connatrons de C++, plus nous devrons nous rappeler cette rgle
et viter de faire des fonctions pour tout, des structures de donnes pour tout, des objets pour tout, des
fichiers spars pour tout, etc.

31

3.1. Tout dans le main() !

11
12
13
14 }

3. Premiers programmes

/ / ce t e s t est vrai
cout << " Merci ! Vous avez tap " << i << endl ;
return 0;

Notez la ligne 9 qui met dans i un nombre tap au clavier. La variable cin est le pendant
en entre ("console in") de la sortie cout.
Vient ensuite le while qui vrifie le test au dbut de la boucle. Le programme suivant affiche les entiers de 1 100 :
i n t i =1;
while ( i <=100) {
cout << i << endl ;
i = i +1;
}
Enfin, on a cre une boucle spciale tant elle est frquente : le for () qui excute
une instruction avant de dmarrer, effectue un test au dbut de chaque tour, comme le
while, et excute une instruction la fin de chaque boucle. Instruction initiale, test et
instruction finale sont spares par un ;, ce qui donne le programme suivant, absolument quivalent au prcdent :
int i ;
f o r ( i = 1 ; i <=100; i = i +1) {
cout << i << endl ;
}
En gnral, le for () est utilis comme dans lexemple prcdent pour effectuer une
boucle avec une variable (un indice) qui prend une srie de valeurs dans un certain
intervalle. On trouvera en fait plutt :
f o r ( i n t i = 1 ; i <=100; i ++)
cout << i << endl ;
quand on sait que :
On peut dfinir la variable dans la premire partie du for () . Attention, cette variable admet le for () pour porte : elle nest plus utilisable en dehors du for () 18 .
i++ est une abbrviation de i=i+1
Puisquil ny a ici quune seule instruction dans la boucle, les accolades taient
inutiles.
On utilise aussi la virgule , pour mettre plusieurs instructions 19 dans linstruction finale du for. Ainsi, le programme suivant part de i=1 et j=100, et augmente i de 2 et
diminue j de 3 chaque tour jusqu ce que leurs valeurs se croisent 20 :
f o r ( i n t i =1 , j = 1 0 0 ; j > i ; i = i +2 , j = j 3)
cout << i << " " << j << endl ;
Notez aussi quon peut abrger i=i+2 en i+=2 et j =j3 en j =3.
18. Les vieux C++ ne permettaient pas de dfinir la variable dans la premire partie du for () . Des C++
un peu moins anciens permettaient de le faire mais la variable survivait au for () !
19. Pour les curieux : a na en fait rien dextraordinaire, car plusieurs instructions spares par une
virgule deviennent en C++ une seule instruction qui consiste excuter lune aprs lautre les diffrentes
instructions ainsi rassembles.
20. Toujours pour les curieux, il sarrte pour i=39 et j=43.

32

3. Premiers programmes

3.1.4

3.1. Tout dans le main() !

Rcrations

Nous pouvons dj faire de nombreux programmes. Par exemple, jouer au juste


prix. Le programme choisit le prix, et lutilisateur devine :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# i n c l u d e <iostream >
# include <cstdlib >
using namespace s t d ;
i n t main ( )
{
i n t n=rand ( ) % 1 0 0 ; / / nombre d e v i n e r e n t r e 0 e t 99
int i ;
do {
cout << " Votre p r i x : " ;
c i n >> i ;
i f ( i >n )
cout << "C e s t moins " << endl ;
e l s e i f ( i <n )
cout << "C e s t plus " << endl ;
else
cout << " Gagne ! " << endl ;
} while ( i ! = n ) ;
return 0;
}
Seule la ligne 7 a besoin dexplications :
la fonction rand() fournit un nombre entier au hasard entre 0 et RAND_MAX. On
a besoin de rajouter #include <cstdlib> pour lutiliser
% est la fonction modulo 21 .
Cest videmment plus intressant, surtout programmer, quand cest le programme
qui devine. Pour cela, il va procder par dichotomie, afin de trouver au plus vite :

1
2
3
4
5
6
7
8
9
10
11
12
13
14

# i n c l u d e <iostream >
using namespace s t d ;
i n t main ( )
{
cout << " C h o i s i s s e z un nombre e n t r e 1 e t 100 " << endl ;
cout << " Repondez par + , ou = " << endl ;
i n t a =1 , b = 1 0 0 ; / / V a l e u r s e x t r m e s
bool trouve= f a l s e ;
do {
i n t c =( a+b ) / 2 ; / / On p r o p o s e l e m i l i e u
cout << " S e r a i t ce " << c << " ? : " ;
char r ;
do
21. Coin des collgiens : compter "modulo N", cest retomber 0 quand on atteint N. Modulo 4, cela
donne : 0,1,2,3,0,1,2,3,0,.... Par exemple 12%10 vaut 2 et 11%3 aussi ! Ici, le modulo 100 sert retomber
entre 0 et 99.

33

3.1. Tout dans le main() !

15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 }

3. Premiers programmes

c i n >> r ;
while ( r ! = = && r ! = + && r ! = ) ;
i f ( r== = )
trouve= t r u e ;
e l s e i f ( r== )
b=c 1; / / C e s t moins , on e s s a i e e n t r e a e t c 1
else
a=c + 1 ; / / C e s t p l u s , on e s s a i e e n t r e c +1 e t b
} while ( ! trouve && ( a<=b ) ) ;
i f ( trouve )
cout << " Quel boss j e s u i s ! " << endl ;
else
cout << " Vous avez t r i c h ! " << endl ;
return 0;

On peut aussi complter le programme "supplmentaire" du TP de lannexe A.1. Il


sagissait dune balle rebondissant dans un carr. (Voir lannexe B pour les instructions
graphiques...)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

# i n c l u d e <Imagine/Graphics . h>
using namespace Imagine ;
i n t main ( )
{
i n t w=300 , h = 2 1 0 ;
openWindow (w, h ) ; / / F e n t r e g r a p h i q u e
i n t i =0 , j = 0 ;
// Position
i n t di =2 , d j = 3 ;
// Vitesse
while ( t r u e ) {
f i l l R e c t ( i , j , 4 , 4 ,RED ) ; / / D e s s i n d e l a b a l l e
m i l l i S l e e p ( 1 0 ) ; / / On a t t e n d un peu . . .
i f ( i +di >w || i +di <0) {
di=di ; / / Rebond h o r i z o n t a l s i on s o r t
}
i n t n i = i +di ; / / N o u v e l l e p o s i t i o n
i f ( j +dj >h || j +dj <0) {
d j=d j ; / / Rebond v e r t i c a l s i on s o r t
}
int nj= j +dj ;
f i l l R e c t ( i , j , 4 , 4 ,WHITE ) ; / / E f f a c e m e n t
i = n i ; / / On c h a n g e d e p o s i t i o n
j =nj ;
}
endGraphics ( ) ;
return 0;
}
Notez ce endGraphics() dont la fonction est dattendre un clic de lutilisateur avant
de terminer le programme, de manire laisser la fentre visible le temps ncessaire.
Cette fonction nest pas standard, et elle est dans le namespace Imagine. La ligne 2
34

3. Premiers programmes

3.2. Fonctions

F IGURE 3.1 Traits et cercles au hasard...


permet de lappeler sans utiliser son nom complet Imagine::endGraphics(). Les autres
fonctions appeles dans ce petit programme (openWindow, fillRect et milliSleep) sont
aussi fournies par Imagine.

3.2

Fonctions

Lorsquon met tout dans le main() on ralise trs vite que lon fait souvent des
copier/coller de bouts de programmes. Si des lignes de programmes commencent se
ressembler, cest quon est vraisemblablement devant loccasion de faire des fonctions.
On le fait pour des raisons de clart, mais aussi pour faire des conomies de frappe au
clavier !
Il faut regrouper les passages identiques en fonctions :
pour obtenir un programme clair...
et pour moins se fatiguer !
Attention bien comprendre quand faire une fonction et ne pas simplement dcouper un programme en petits morceaux sans aucune logique a .
a. ou juste pour faire plaisir au professeur. Mal dcouper un programme est la meilleure
faon de ne plus avoir envie de le faire la fois suivante. Encore une fois, le bon critre est ici
que la bonne solution est gnralement la moins fatiguante.

En fait, pouvoir rutiliser le travail dj fait est le fil conducteur dune bonne programmation. Pour linstant, nous nous contentons, grce aux fonctions, de rutiliser ce que
nous venons de taper quelques lignes plus haut. Plus tard, nous aurons envie de rutiliser ce qui aura t fait dans dautres programmes, ou longtemps auparavant, ou dans
les programmes dautres personnes, ... et nous verrons alors comment faire.
Prenons le programme suivant, qui dessine des traits et des cercles au hasard, et
dont la figure 3.1 montre un rsultat :
1 # i n c l u d e <Imagine/Graphics . h>
2 using namespace Imagine ;
35

3.2. Fonctions

3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

3. Premiers programmes

# include <cstdlib >


using namespace s t d ;
i n t main ( )
{
openWindow ( 3 0 0 , 2 0 0 ) ;
f o r ( i n t i = 0 ; i < 1 5 0 ; i ++) {
i n t x1=rand ( ) % 3 0 0 ; / / P o i n t i n i t i a l
i n t y1=rand ( ) % 2 0 0 ;
i n t x2=rand ( ) % 3 0 0 ; / / P o i n t f i n a l
i n t y2=rand ( ) % 2 0 0 ;
Color c=Color ( rand ( ) % 2 5 6 , rand ( ) % 2 5 6 , rand ( ) % 2 5 6 ) ; / / RVB
drawLine ( x1 , y1 , x2 , y2 , c ) ; / / T r a c d e s e g m e n t
i n t xc=rand ( ) % 3 0 0 ; / / C e n t r e du c e r c l e
i n t yc=rand ( ) % 2 0 0 ;
i n t r c =rand ( ) % 1 0 ;
/ / Rayon
Color c c =Color ( rand ( ) % 2 5 6 , rand ( ) % 2 5 6 , rand ( ) % 2 5 6 ) ; / / RVB
f i l l C i r c l e ( xc , yc , rc , c c ) ; / / C e r c l e
}
endGraphics ( ) ;
return 0;
}
La premire chose qui choque 22 , cest lappel rpt rand() et modulo pour tirer
un nombre au hasard. On aura souvent besoin de tirer des nombres au hasard dans
un certain intervalle et il est naturel de le faire avec une fonction. Au passage, nous
corrigeons une deuxime chose qui choque : les entiers 300 et 200 reviennent souvent.
Si nous voulons changer les dimensions de la fentre, il faudra remplacer dans le programme tous les 300 et tous les 200. Il vaudrait mieux mettre ces valeurs dans des
variables et faire dpendre le reste du programme de ces variables. Cest un dfaut
constant de tous les dbutants et il faut le corriger tout de suite.
Il faut ds le dbut dun programme reprer les paramtres constants utiliss plusieurs reprises et les placer dans des variables dont dpendra le
programme. On gagne alors beaucoup de temps a quand on veut les modifier par la suite.
a. Encore la rgle du moindre effort... Si on fait trop de copier/coller ou de remplacer avec
lditeur, cest mauvais signe !

Bref, notre programme devient :


/ / Nombre e n t r e 0 e t n1
i n t hasard ( i n t n )
{
r e t u r n rand ()%n ;
}
i n t main ( )
22. part videmment la syntaxe "objet" des variables de type Color pour lesquelles on se permet un
Color(r,v,b) bien en avance sur ce que nous sommes censs savoir faire...

36

3. Premiers programmes

3.2. Fonctions

{
c o n s t i n t w=300 , h = 2 0 0 ;
openWindow (w, h ) ;
f o r ( i n t i = 0 ; i < 1 5 0 ; i ++) {
i n t x1=hasard (w) , y1=hasard ( h ) ; / / P o i n t i n i t i a l
i n t x2=hasard (w) , y2=hasard ( h ) ; / / P o i n t f i n a l
Color c=Color ( hasard ( 2 5 6 ) , hasard ( 2 5 6 ) , hasard ( 2 5 6 ) ) ;
drawLine ( x1 , y1 , x2 , y2 , c ) ; / / T r a c d e s e g m e n t
i n t xc=hasard (w) , yc=hasard ( h ) ; / / C e n t r e du c e r c l e
i n t r c =hasard (w/ 2 0 ) ;
/ / Rayon
Color c c =Color ( hasard ( 2 5 6 ) , hasard ( 2 5 6 ) , hasard ( 2 5 6 ) ) ;
f i l l C i r c l e ( xc , yc , rc , c c ) ; / / C e r c l e
}
endGraphics ( ) ;
return 0;
}
On pourrait penser que hasard(w) est aussi long taper que rand()%w et que notre
fonction est inutile. Cest un peu vrai. Mais en pratique, nous navons alors plus
nous souvenir de lexistence de la fonction rand() ni de comment on fait un modulo.
Cest mme mieux que a : nous devenons indpendant de ces deux fonctions, et si
vous voulions tirer des nombres au hasard avec une autre fonction 23 , nous naurions
plus qu modifier la fonction hasard(). Cest encore une rgle importante.
On doit galement faire une fonction quand on veut sparer et factoriser
le travail. Il est ensuite plus facile a de modifier la fonction que toutes les
lignes quelle a remplaces !
a. Moindre effort, toujours !

3.2.1

Retour

Nous venons de dfinir sans lexpliquer une fonction hasard() qui prend un paramtre n de type int et qui retourne un rsultat, de type int lui aussi. Il ny a pas grand
chose savoir de plus, si ce nest que :
1. Une fonction peut ne rien renvoyer. Son type de retour est alors void et il ny a
pas de return la fin. Par exemple :
void dis_bonjour_a_la_dame ( s t r i n g nom_de_la_dame ) {
cout << " Bonjour , Mme " << nom_de_la_dame << " ! " << endl ;
}
...
dis_bonjour_a_la_dame ( " Germaine " ) ;
dis_bonjour_a_la_dame ( " F i t z g e r a l d " ) ;
...
23. Pourquoi vouloir le faire ? Dans notre cas parce que la fonction rand() utilise est suffisante pour
des applications courantes mais pas assez prcise pour des applications mathmatiques. Par exemple,
faire un modulo ne rpartit pas vraiment quitablement les nombres tirs. Enfin, nous avons oubli
dinitialiser le gnrateur alatoire. Si vous le permettez, nous verrons une autre fois ce que cela signifie
et comment le faire en modifiant juste la fonction hasard().

37

3.2. Fonctions

3. Premiers programmes

2. Une fonction peut comporter plusieurs instructions return 24 . Cela permet de sortir quand on en a envie, ce qui est bien plus clair et plus proche de notre faon de
penser :
i n t s i g n e _ a v e c _ u n _ s e u l _ r e t u r n ( double x ) {
int s ;
i f ( x ==0)
s =0;
e l s e i f ( x <0)
s =1;
else
s =1;
return s ;
}
i n t s i g n e _ p l u s _ s i m p l e ( double x ) {
i f ( x <0)
r e t u r n 1;
i f ( x >0) / / N o t e z l a b s e n c e d e e l s e , d e v e n u i n u t i l e !
return 1;
return 0;
}
3. Pour une fonction void, on utilise return sans rien derrire pour un retour en
cours de fonction :
void t e l e p h o n e r _ a v e c _ u n _ s e u l _ r e t u r n ( s t r i n g nom) {
i f ( j_ai_le_telephone ) {
i f (mon telephone_marche ) {
i f ( e s t _ d a n s _ l _ a n n u a i r e (nom ) ) {
i n t numero=numero_telephone (nom ) ;
composer ( numero ) ;
i f ( ca_decroche ) {
parler ( ) ;
raccrocher ( ) ;
}
}
}
}
}
void t e l e p h o n e r _ p l u s _ s i m p l e ( s t r i n g nom) {
i f ( ! j_ai_le_telephone )
return ;
i f ( ! mon telephone_marche )
return ;
i f ( ! e s t _ d a n s _ l _ a n n u a i r e (nom ) )
return ;
i n t numero=numero_telephone (nom ) ;
composer ( numero ) ;
24. Contrairement certains vieux langages, comme le Pascal

38

3. Premiers programmes

3.2. Fonctions

i f ( ! ca_decroche )
return ;
parler ( ) ;
raccrocher ( ) ;
}

3.2.2

Paramtres

Nous navons vu que des fonctions un seul paramtre. Voici comment faire pour
en passer plusieurs ou nen passer aucun :
/ / Nombre e n t r e a e t b
i n t hasard2 ( i n t a , i n t b )
{
r e t u r n a +( rand ( ) % ( ba + 1 ) ) ;
}
/ / Nombre e n t r e 0 e t 1
double hasard3 ( )
{
r e t u r n rand ( ) / double (RAND_MAX) ;
}
...
i n t a=hasard2 ( 1 , 1 0 ) ;
double x=hasard3 ( ) ;
...
Attention bien utiliser x=hasard3() et non simplement x=hasard3 pour appeler cette
fonction sans paramtre. Ce simple programme est aussi loccasion de parler dune
erreur trs frquente : la division de deux nombres entiers donne un nombre entier !
Ainsi, crire double x=1/3; est une erreur car le C++ commence par calculer 1/3 avec
des entiers, ce qui donne 0, puis convertit 0 en double pour le ranger dans x. Il ne sait
pas au moment de calculer 1/3 quon va mettre le rsultat dans un double ! Il faut alors faire
en sorte que le 1 ou le 3 soit une double et crire double x=1.0/3; ou double x=1/3.0;.
Si, comme dans notre cas, on a affaire deux variables de type int, il suffit de convertir
une de ces variables en double avec la syntaxe double (...) que nous verrons plus tard.
1. Fonction sans paramtre : x=hop(); et non x=hop;.
2. Division entire :
double x=1.0/3; et non double x=1/3;
double x=double(i)/j;
double x=double(i/j); a

et

non

double x=i/j;,

ni

mme

a. Cette conversion en double arrive trop tard !

3.2.3

Passage par rfrence

Lorsquune fonction modifie la valeur dun de ses paramtres, et si ce paramtre


tait une variable dans la fonction appelante, alors la variable en question nest pas
modifie. Plus clairement, le programme suivant choue :
39

3.2. Fonctions

3. Premiers programmes

void t r i p l e ( i n t x ) {
x=x 3 ;
}
...
i n t a =2;
triple (a );
cout << a << endl ;
Il affiche 2 et non 6. En fait, le paramtre x de la fonction triple vaut bien 2, puis 6.
Mais son passage 6 ne modifie pas a. Nous verrons plus loin que x est mmoris
un endroit diffrent de a, ce qui explique tout ! Cest la valeur de a qui est passe
la fonction triple () et non pas la variable a ! On parle de passage par valeur. On
peut toutefois faire en sorte que la fonction puisse vraiment modifier son paramtre.
On sagit alors dun passage par rfrence (ou par variable). Il suffit de rajouter un &
derrire le type du paramtre :
void t r i p l e ( i n t& x ) {
x=x 3 ;
}
Gnralement, on choisit lexemple suivant pour justifier le besoin des rfrences :
void echanger1 ( i n t x , i n t y ) {
i n t t =x ;
x=y ;
y= t ;
}
void echanger2 ( i n t& x , i n t& y ) {
i n t t =x ;
x=y ;
y= t ;
}
...
i n t a =2 , b = 3 ;
echanger1 ( a , b ) ;
cout << a << " " << b << " " ;
echanger2 ( a , b ) ;
cout << a << " " << b << endl ;
...
Ce programme affiche 2 3 3 2, echanger1() ne marchant pas.
Une bonne faon de comprendre le passage par rfrence est de considrer que les
variables x et y de echanger1 sont des variables vraiment indpendantes du a et du
b de la fonction appelante, alors quau moment de lappel echanger2, le x et le y
de echanger2 deviennent des "liens" avec a et b. A chaque fois que lon utilise x dans
echanger2, cest en fait a qui est utilise. Pour encore mieux comprendre allez voir le
premier exercice du TP 2 (A.2.1) et sa solution.
En pratique,
on utilise aussi les rfrences pour faire des fonctions retournant plusieurs
valeurs la fois,
et ce, de la faon suivante :
40

3. Premiers programmes

3.2. Fonctions

void un_point ( i n t& x , i n t& y ) {


x=...;
y=...;
}
...
int a , b ;
un_point ( a , b ) ;
...
Ainsi, notre programme de dessin alatoire deviendrait :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

# i n c l u d e <Imagine/Graphics . h>
using namespace Imagine ;
# include <cstdlib >
using namespace s t d ;
/ / Nombre e n t r e 0 e t n1
i n t hasard ( i n t n )
{
r e t u r n rand ()%n ;
}
Color une_couleur ( ) {
r e t u r n Color ( hasard ( 2 5 6 ) , hasard ( 2 5 6 ) , hasard ( 2 5 6 ) ) ;
}
void un_point ( i n t w, i n t h , i n t& x , i n t& y ) {
x=hasard (w) ;
y=hasard ( h ) ;
}
i n t main ( )
{
c o n s t i n t w=300 , h = 2 0 0 ;
openWindow (w, h ) ;
f o r ( i n t i = 0 ; i < 1 5 0 ; i ++) {
i n t x1 , y1 ; / / P o i n t i n i t i a l
un_point (w, h , x1 , y1 ) ;
i n t x2 , y2 ; / / P o i n t f i n a l
un_point (w, h , x2 , y2 ) ;
Color c=une_couleur ( ) ;
drawLine ( x1 , y1 , x2 , y2 , c ) ; / / T r a c d e s e g m e n t
i n t xc , yc ; / / C e n t r e du c e r c l e
un_point (w, h , xc , yc ) ;
i n t r c =hasard (w/ 2 0 ) ;
/ / Rayon
Color c c =une_couleur ( ) ;
f i l l C i r c l e ( xc , yc , rc , c c ) ; / / C e r c l e
}
endGraphics ( ) ;
return 0;
41

3.2. Fonctions

3. Premiers programmes

40 }
Avec le conseil suivant :
penser utiliser directement le rsultat dune fonction et ne pas le mmoriser dans une variable lorsque cest inutile.
Il devient mme :
i n t x1 , y1 ; / / P o i n t i n i t i a l
un_point (w, h , x1 , y1 ) ;
i n t x2 , y2 ; / / P o i n t f i n a l
un_point (w, h , x2 , y2 ) ;
drawLine ( x1 , y1 , x2 , y2 , une_couleur ( ) ) ; / / T r a c d e s e g m e n t
i n t xc , yc ; / / C e n t r e du c e r c l e
un_point (w, h , xc , yc ) ;
i n t r c =hasard (w/ 2 0 ) ;
/ / Rayon
f i l l C i r c l e ( xc , yc , rc , une_couleur ( ) ) ; / / C e r c l e

26
27
28
29
30
31
32
33
34

3.2.4

Porte, Dclaration, Dfinition

Depuis le dbut, nous crons des fonctions en les dfinissant. Il est parfois utile de
ne connatre que le type de retour et les paramtres dune fonction sans pour autant
savoir comment elle est programme, cest--dire sans connatre le corps de la fonction.
Une des raisons de ce besoin est que :
comme les variables, les fonctions ont une porte et ne sont connues que
dans les lignes de source qui lui succdent.
Ainsi, le programme suivant ne compile pas :
1
2
3
4
5
6
7

i n t main ( )
{
f ();
return 0;
}
void f ( ) {
}
car la ligne 3, f () nest pas connue. Il suffit ici de mettre les lignes 6 et 7 avant le main()
pour que le programme compile. Par contre, il est plus difficile de faire compiler :
void f ( )
{
g ( ) ; / / Erreur : g ( ) inconnue
}
void g ( ) {
f ();
}
puisque les deux fonctions on besoin lune de lautre, et quaucun ordre ne conviendra.
Il faut alors connatre la rgle suivante :
42

3. Premiers programmes

3.2. Fonctions

Remplacer le corps dune fonction par un ; sappelle dclarer la fonction.


Dclarer une fonction suffit au compilateur, qui peut "patienter" a jusqu sa dfinition.
a. En ralit, le compilateur na besoin que de la dclaration. Cest le linker qui devra trouver quelque part la dfinition de la fonction, ou plus exactement le rsultat de la compilation
de sa dfinition !

Notre programme prcdent peut donc se compiler avec une ligne de plus :
void g ( ) ;
void f ( )
{
g();
}

/ / D c l a r a t i o n de g

/ / OK: f o n c t i o n d c l a r e

void g ( ) { / / D f i n i t i o n d e g
f ();
}

3.2.5

Variables locales et globales

Nous avons vu section 3.1.1 la porte des variables. La rgle des accolades sapplique videmment aux accolades du corps dune fonction.
Les variables dune fonction sont donc inconnues en dehors de la fonction
On parle alors de variables locales la fonction. Ainsi, le programme suivant est interdit :
void f ( )
{
int x ;
x =3;
}
void g ( ) {
int y ;
y=x ; / / E r r e u r : x i n c o n n u
}
Si vraiment deux fonctions doivent utiliser des variables communes, il faut alors
les "sortir" des fonctions. Elles deviennent alors des variables globales, dont voici un
exemple :
1 int z ; / / globale
2
3 void f ( )
4 {
5
int x ; / / loc ale
43

3.2. Fonctions

6
7
8
9
10
11
12
13
14
15
16
17

3. Premiers programmes

...
i f ( x<z )
...
}
void g ( )
{
int y ; / / loc ale
...
z=y ;
...
}
Lutilisation de variables globales est tolre et parfois justifie. Mais elle constitue une
solution de facilit dont les dbutants abusent et il faut combattre cette tentation ds le
dbut :
les variables globales sont viter au maximum car
elles permettent parfois des communications abusives entre fonctions,
sources de bugs a .
les fonctions qui les utilisent sont souvent peu rutilisables dans des
contextes diffrents.
En gnral, elles sont le signe dune mauvaise faon de traiter le problme.
a. Cest pourquoi les variables globales non constantes ne sont pas tolres chez le dbutant. Voir le programme prcdent o g() parle f () au travers de z.

3.2.6

Surcharge

Il est parfois utile davoir une fonction qui fait des choses diffrentes suivant le type
dargument quon lui passe. Pour cela on peut utiliser la surcharge :
Deux fonctions qui ont des listes de paramtres diffrentes peuvent avoir le
mme nom a . Attention : deux fonctions aux types de retour diffrents mais
aux paramtres identiques ne peuvent avoir le mme nom b .
a. Car alors la faon de les appeler permettra au compilateur de savoir laquelle des fonctions on veut utiliser
b. Car alors le compilateur ne pourra diffrencier leurs appels.

Ainsi, nos fonctions "hasard" de tout lheure peuvent trs bien scrire :
1
2
3
4
5
6
7
8
9

/ / Nombre e n t r e 0 e t n1
i n t hasard ( i n t n ) {
r e t u r n rand ()%n ;
}
/ / Nombre e n t r e a e t b
i n t hasard ( i n t a , i n t b ) {
r e t u r n a +( rand ( ) % ( ba + 1 ) ) ;
}
/ / Nombre e n t r e 0 e t 1
44

3. Premiers programmes

3.3. TP

F IGURE 3.2 Mini tennis...

10 double hasard ( ) {
11
r e t u r n rand ( ) / double (RAND_MAX) ;
12 }
13 . . .
14
i n t i =hasard ( 3 ) ;
// entre 0 et 2
15
i n t j =hasard ( 2 , 4 ) / / e n t r e 2 e t 4
16
double k=hasard ( ) ; / / e n t r e 0 e t 1
17 . . .

3.3

TP

Nous pouvons maintenant aller faire le deuxime TP donn en annexe A.2 afin de
mieux comprendre les fonctions et aussi pour obtenir un mini jeu de tennis (figure 3.2).

3.4

Fiche de rfrence

Nous commenons maintenant nous fabriquer une "fiche de rfrence" qui servira
daide mmoire lorsquon est devant la machine. Nous la complterons aprs chaque
chapitre avec ce qui est vu dans le chapitre, mais aussi pendant le TP correspondant.
Les nouveauts par rapport la fiche prcdente seront en rouge. La fiche finale est en
annexe C.

45

3.4. Fiche de rfrence

3. Premiers programmes

Fiche de rfrence (1/2)


Variables

if (i==0) j=1;

Dfinition :
int i;
int k,l,m;

if (i==0) j=1;
else
j=2;

Affectation :
i=2;
j=i;
k=l=3;

if (i==0) {
j=1;
k=2;
}

Initialisation :
int n=5,o=n;

bool t=(i==0);
if (t)
j=1;

Constantes :
const int s=12;
Porte :
int i;
// i=j; interdit!
int j=2;
i=j; // OK!
if (j>1) {
int k=3;
j=k; // OK!
}
//i=k; interdit!
Types :
int i=3;
double x=12.3;
char c=A;
string s="hop";
bool t=true;
float y=1.2f;
unsigned int j=4;
signed char d=-128;
unsigned char d=25;
complex<double>
z(2,3);
Variables globales :
int n;
const int m=12;
void f() {
n=10;
// OK
int i=m; // OK
...
Conversion :
int i=int(x),j;
float x=float(i)/j;
Tests
Comparaison :
== != < > <= >=
Ngation : !
Combinaisons : && ||

switch (i) {
case 1:
...;
...;
break;
case 2:
case 3:
...;
break;
default:
...;
}

if (x<0)
return -1;
if (x>0)
return 1;
return 0;
}
void afficher(int x,
int y) {
if (x<0 || y<0)
return;
if (x>=w || y>=h)
return;
DrawPoint(x,y,RED);
}
Appel :
int f(int a) { ... }
int g() { ... }
...
int i=f(2),j=g();

do {
...
} while(!ok);

Rfrences :
void swap(int& a,
int& b){
int tmp=a;
a=b;b=tmp;
}
...
int x=3,y=2;
swap(x,y);

int i=1;
while(i<=100) {
...
i=i+1;
}

Surcharge :
int hasard(int n);
int hasard(int a,
int b);
double hasard();

Boucles

for(int i=1;i<=10;i++) Divers


...
i++;
for(int i=1,j=10;j>i;
i--;
i=i+2,j=j-3)
i-=2;
...
j+=3;
Fonctions
j=i%n; // Modulo
Dfinition :
#include <cstdlib>
int plus(int a,int b) {
...
int c=a+b;
i=rand()%n;
return c;
x=rand()/
}
double(RAND_MAX);
void affiche(int a) {
cout << a << endl; Entres/Sorties
}
#include <iostream>
Dclaration :
using namespace std;
int plus(int a,int b);
...
cout <<"I="<<i<<endl;
Retour :
cin >> i >> j;
int signe(double x) {

46

3. Premiers programmes

3.4. Fiche de rfrence

Fiche de rfrence (2/2)


Erreurs frquentes
double x=1/3; // NON!
int i,j;
Pas de dfinition de fonction
x=i/j; // NON!
dans une fonction !
x=double(i/j); //NON!
int q=r=4; // NON!
Imagine++
if (i=2) // NON!
if i==2 // NON!
Voir documentation...
if (i==2) then // NON!
Clavier
for (int i=0,i<100,i++)

Build : F7
// NON!
Debug : F5
int f() {...}
Step over : F10
int i=f; // NON!

47

Step inside : F11


Indent : Ctrl+K,Ctrl+F
Conseils
Nettoyer en quittant.
Erreurs et warnings : cliquer.
Indenter.
Ne pas laisser de warning.
Utiliser le debuggeur.
Faire des fonctions.

4. Les tableaux

Chapitre 4
Les tableaux
Tout en continuant utiliser les fonctions pour les assimiler, nous allons rajouter les tableaux qui, sinon, nous manqueraient rapidement. Nous nirons pas trop vite et ne verrons
pour linstant que les tableaux une dimension et de taille fixe. Nous tudierons dans un autre
chapitre les tableaux de taille variable et les questions de mmoire ("pile" et "tas").

4.1

Premiers tableaux

De mme quon a tout de suite ressenti le besoin davoir des boucles pour faire
plusieurs fois de suite la mme chose, il a t rapidement utile de faire plusieurs fois
la mme chose mais sur des variables diffrentes. Do les tableaux... Ainsi, le programme suivant :
i n t x1 , y1 , u1 , v1 ; / / B a l l e
i n t x2 , y2 , u2 , v2 ; / / B a l l e
i n t x3 , y3 , u3 , v3 ; / / B a l l e
i n t x4 , y4 , u4 , v4 ; / / B a l l e
...
BougeBalle ( x1 , y1 , u1 , v1 ) ;
BougeBalle ( x2 , y2 , u2 , v2 ) ;
BougeBalle ( x3 , y3 , u3 , v3 ) ;
BougeBalle ( x4 , y4 , u4 , v4 ) ;
...

1
2
3
4

pourra avantageusement tre remplac par :


int x [4] ,y[4] ,u[4] ,v [ 4 ] ; / / Balles
...
f o r ( i n t i = 0 ; i < 4 ; i ++)
BougeBalle ( x [ i ] , y [ i ] , u [ i ] , v [ i ] ) ;
...
dans lequel int x[4] dfinit un tableau de 4 variables de type int : x[0], x[1], x[2] et
x[3]. En pratique, le compilateur rserve quelque part en mmoire de quoi stocker les
4 variables en question et gre de quoi faire en sorte que x[ i ] dsigne la bonne variable.
Un autre exemple pour mieux comprendre, qui additionne des double deux par
deux en mmorisant les rsultats :

4.1. Premiers tableaux

4. Les tableaux

double x [ 1 0 0 ] , y [ 1 0 0 ] , z [ 1 0 0 ] ;
...
. . . / / i c i , l e s x [ i ] et y [ i ] prennent des valeurs
...
f o r ( i n t i = 0 ; i < 1 0 0 ; i ++)
z [ i ]= x [ i ]+ y [ i ] ;
...
. . . / / i c i , on u t i l i s e l e s z [ i ]
...
Il y deux choses essentielles retenir.
1. Dabord :
les indices dun tableau t de taille n vont de 0 n-1. Tout accs t[n]
peut provoquer une erreur grave pendant lexcution du programme.
C EST UNE DES ERREURS LES PLUS FRQUENTES EN C++. Soit on va
lire ou crire dans un endroit utilis pour une autre variable a , soit on
accde une zone mmoire illgale et le programme peut "planter" b .
a. Dans lexemple ci-dessus, si on remplaait la boucle pour que i aille de 1 100,
x[100] irait certainement chercher y[0] la place. De mme, z[100] irait peut-tre
chercher la variable i de la boucle, ce qui risquerait de faire ensuite des choses tranges,
i valant nimporte quoi !
b. Ci-dessus, z[i] avec nimporte quoi pour i irait crire en dehors de la zone rserve aux donnes, ce qui stopperait le programme plus ou moins dlicatement !

Dans le dernier exemple, on utilise x[0] x[99]. Lhabitude est de faire une boucle
avec i<100 comme test, plutt que i<=99, ce qui est plus lisible. Mais attention
ne pas mettre i<=100 !
2. Ensuite :
un tableau doit avoir une taille fixe connue la compilation. Cette taille
peut tre un nombre ou une variable constante, mais pas une variable.
Mme si on pense que le compilateur pourrait connatre la taille, il joue au plus
idiot et naccepte que des constantes :
1
2
3
4
5
6
7
8
9
10
11
12
13

double x [ 1 0 ] , y [ 4 ] , z [ 5 ] ;
const i n t n=5;
int i [n ] , j [2n ] , k [n+1];
i n t n1 ;
//
i n t t 1 [ n1 ] ;
//
i n t n2 ;
c i n >> n2 ;
//
//
i n t t 2 [ n2 ] ;
//
i n t n3 ;
n3 = 5 ;
//
//
i n t t 3 [ n3 ] ;
//

/ / OK
/ / OK
n1 n a mme p a s d e v a l e u r
donc ERREUR
n2 p r e n d une v a l e u r , m a i s connue
uniquement l e x c u t i o n
donc ERREUR
n3 p r e n d une v a l e u r , connue
l e x c u t i o n , m a i s . . . non c o n s t a n t e
donc ERREUR ( S I ! )
50

4. Les tableaux

4.2. Initialisation

Connaissant ces deux points, on peut trs facilement utiliser des tableaux. Attention
toutefois :
ne pas utiliser de tableau quand cest inutile, notamment quand on traduit
une formule mathmatique.
P100
1
Je mexplique. Si vous devez calculer s =
i=1 f (i) pour f donne , par exemple
f (i) = 3i + 4, nallez pas crire, comme on le voit parfois :
1
2
3
4
5
6

double f [ 1 0 0 ] ;
f o r ( i n t i = 1 ; i <=100; i ++)
f [ i ]=3 i + 4 ;
double s ;
f o r ( i n t i = 1 ; i <=100; i ++)
s=s+ f [ i ] ;
ni, mme, ayant corrig vos bugs :

5
6
7
8
9
10

double f [ 1 0 0 ] ;
/ / S t o c k e f ( i ) d a n s f [ i 1]
f o r ( i n t i = 1 ; i <=100; i ++)
f [ i 1]=3 i + 4 ;
/ / A t t e n t i o n aux i n d i c e s !
double s = 0 ;
/ / Ca va mieux comme c a !
f o r ( i n t i = 1 ; i <=100; i ++)
s=s+ f [ i 1 ] ;
mais plutt directement sans tableau :

5 double s = 0 ;
6 f o r ( i n t i = 1 ; i <=100; i ++)
7
s=s +(3 i + 4 ) ;
ce qui pargnera, la machine, un tableau (donc de la mmoire et des calculs), et
vous des bugs (donc vos nerfs !).

4.2

Initialisation

Tout comme une variable, un tableau peut tre initialis :


int t [4]={1 ,2 ,3 ,4};
s t r i n g s [ 2 ] = { " hip " , " hop " } ;
Attention, la syntaxe utilise pour linitialisation ne marche pas pour une affectation 2 :
int t [ 2 ] ;
t ={1 ,2}; / / Erreur !
1. Coin des collgiens : cest--dire s = f (1) + f (2) + ... + f (100).
2. Nous verrons plus bas que laffectation ne marche mme pas entre deux tableaux ! Tout ceci sarrangera avec les objets...

51

4.3. Spcificits des tableaux

4.3

4. Les tableaux

Spcificits des tableaux

Les tableaux sont des variables un peu spciales. Ils ne se comportent pas toujours
comme les autres variables 3 ...

4.3.1

Tableaux et fonctions

Tout comme les variables, on a besoin de passer les tableaux en paramtres des
fonctions. La syntaxe utiliser est simple :
void a f f i c h e ( i n t s [ 4 ] ) {
f o r ( i n t i = 0 ; i < 4 ; i ++)
cout << s [ i ] << endl ;
}
...
int t [4]={1 ,2 ,3 ,4};
affiche ( t ) ;
mais il faut savoir deux choses :
Un tableau est toujours pass par rfrence bien quon nutilise pas le
& a .
Une fonction ne peut pas retourner un tableau b .
a. Un void f(int& t[4]) ou toute autre syntaxe est une erreur.
b. On comprendra plus tard pourquoi, par soucis defficacit, les concepteurs du C++ ont
voulu quun tableau ne soit ni pass par valeur, ni retourn.

donc :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

/ / R a p p e l : c e c i ne marche p a s
void a f f e c t e 1 ( i n t x , i n t v a l ) {
x= v a l ;
}
/ / R a p p e l : c e s t c e c i q u i marche !
void a f f e c t e 2 ( i n t& x , i n t v a l ) {
x= v a l ;
}
/ / Une f o n c t i o n q u i marche s a n s &
void r e m p l i t ( i n t s [ 4 ] , i n t v a l ) {
f o r ( i n t i = 0 ; i < 4 ; i ++)
s [ i ]= v a l ;
}
...
i n t a =1;
affecte1 (a , 0 ) ;
/ / a ne s e r a p a s mis 0
3. Il est du coup de plus en plus frquent que les programmeurs utilisent directement des variables
de type vector qui sont des objets implmentant les fonctionnalits des tableaux tout en se comportant
davantage comme des variables standard. Nous prfrons ne pas parler ds maintenant des vector
car leur comprhension ncessite celle des objets et celle des "template". Nous pensons aussi que la
connaissance des tableaux, mme si elle demande un petit effort, est incontournable et aide la comprhension de la gestion de la mmoire.

52

4. Les tableaux

17
18
19
20
21
22

cout << a << endl ;


affecte2 (a , 0 ) ;
cout << a << endl ;
int t [ 4 ] ;
remplit ( t , 0 ) ;
affiche ( t ) ;

4.3. Spcificits des tableaux

// vrification
/ / a s e r a b i e n mis 0
// vrification
/ / Met l e s t [ i ] 0
/ / V r i f i e que l e s t [ i ] v a l e n t 0

et aussi :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

/ / Somme d e deux t a b l e a u x q u i ne c o m p i l e mme p a s


/ / Pour r e t o u r n e r un t a b l e a u
i n t somme1 ( i n t x [ 4 ] , i n t y [ 4 ] ) [ 4 ] { / / on p e u t i m a g i n e r m e t t r e l e
/ / [ 4 ] i c i ou a i l l e u r s :
// rien n y f a i t !
int z [ 4 ] ;
f o r ( i n t i = 0 ; i < 4 ; i ++)
z [ i ]= x [ i ]+ y [ i ] ;
return z ;
}
/ / En p r a t i q u e , on f e r a donc comme a !
/ / Somme d e deux t a b l e a u x q u i marche
void somme2 ( i n t x [ 4 ] , i n t y [ 4 ] , i n t z [ 4 ] )
f o r ( i n t i = 0 ; i < 4 ; i ++)
z [ i ]= x [ i ]+ y [ i ] ; / / OK: z e s t p a s s p a r r f r e n c e !
}
int a [4] ,b [ 4 ] ;
...
/ / r e m p l i s s a g e de a e t b
int c [ 4 ] ;
c=somme1 ( a , b ) ; / / ERREUR
somme2 ( a , b , c ) ; / / OK
Enfin, et cest utilis tout le temps,
Une fonction nest pas tenue de travailler sur une taille de tableau unique...
mais il est impossible de demander un tableau sa taille !
On utilise la syntaxe int t [] dans les paramtres pour un tableau dont on ne prcise
pas la taille. Comme il faut bien parcourir le tableau dans la fonction et quon ne peut
retrouver sa taille, on la passe en paramtre en plus du tableau :

1
2
3
4
5
6
7
8
9
10
11

/ / Une f o n c t i o n q u i ne marche p a s
void a f f i c h e 1 ( i n t t [ ] ) {
f o r ( i n t i = 0 ; i <TAILLE ( t ) ; i ++) / / TAILLE ( t ) n e x i s t e p a s ! ? ? ? ?
cout << t [ i ] << endl ;
}
/ / Comment on f a i t en p r a t i q u e
void a f f i c h e 2 ( i n t t [ ] , i n t n ) {
f o r ( i n t i = 0 ; i <n ; i ++)
cout << t [ i ] << endl ;
}
...
53

4.4. Rcrations

12
13
14
15

4. Les tableaux

int t1 [ 2 ] = { 1 , 2 } ;
int t2 [ 3 ] = { 3 , 4 , 5 } ;
a f f i c h e 2 ( t1 , 2 ) ; / / OK
a f f i c h e 2 ( t2 , 3 ) ; / / OK

4.3.2

Affectation

Cest simple :
Affecter un tableau ne marche pas ! Il faut traiter les lments du tableau un
par un...
Ainsi, le programme :
int s [4]={1 ,2 ,3 ,4} , t [ 4 ] ;
t =s ; / / ERREUR d e c o m p i l a t i o n
ne marche pas et on est oblig de faire :
int s [4]={1 ,2 ,3 ,4} , t [ 4 ] ;
f o r ( i n t i = 0 ; i < 4 ; i ++)
t [ i ]= s [ i ] ; / / OK
Le problme, cest que :
Affecter un tableau ne marche jamais mais ne gnre pas toujours une erreur de compilation, ni mme un warning. Cest le cas entre deux paramtres de fonction. Nous comprendrons plus tard pourquoi et leffet exact
dune telle affectation...
.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

/ / F o n c t i o n q u i ne marche p a s
/ / Mais q u i c o m p i l e t r s b i e n !
void s e t 1 ( i n t s [ 4 ] , i n t t [ 4 ] ) {
t =s ; / / Ne f a i t p a s c e qu i l f a u t !
/ / m a i s c o m p i l e s a n s warning !
}
/ / F o n c t i o n q u i marche ( e t q u i c o m p i l e ! )
void s e t 2 ( i n t s [ 4 ] , i n t t [ 4 ] ) {
f o r ( i n t i = 0 ; i < 4 ; i ++)
t [ i ]= s [ i ] ; / / OK
}
...
int s [4]={1 ,2 ,3 ,4} , t [ 4 ] ;
s e t 1 ( s , t ) ; / / Sans e f f e t
s e t 2 ( s , t ) ; / / OK
...

54

4. Les tableaux

4.4. Rcrations

F IGURE 4.1 Des balles qui rebondissent... (momentanment figes ! Allez sur la page
du cours pour un programme anim !)

4.4

Rcrations

4.4.1

Multi-balles

Nous pouvons maintenant reprendre le programme de la balle qui rebondit, donn


la section 3.1.4, puis amlior avec des fonctions et de constantes lors du TP de lannexe A.2. Grce aux tableaux, il est facile de faire se dplacer plusieurs balles la fois.
Nous tirons aussi la couleur et la position et la vitesse initiales des balles au hasard.
Plusieurs fonctions devraient vous tre inconnues :
Linitialisation du gnrateur alatoire avec srand((unsigned int)time(0)), qui est
explique dans le TP 3 (annexe A.3)
Les fonctions noRefreshBegin et noRefreshEnd qui servent acclrer laffichage
de toutes les balles (voir documentation de Imagine++ annexe B).
Voici le listing du programme (exemple daffichage (malheureusement statique !) figure 4.1) :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# i n c l u d e <Imagine/Graphics . h>
using namespace Imagine ;
# include <cstdlib >
# i n c l u d e <ctime >
using namespace s t d ;
// / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
/ / C o n s t a n t e s du programme
c o n s t i n t width = 2 5 6 ;
/ / Largeur de l a f e n e t r e
const i n t height =256;
/ / Hauteur d e l a f e n e t r e
const i n t b a l l _ s i z e =4;
/ / Rayon d e l a b a l l e
const i n t nb_balls =30;
/ / Nombre d e b a l l e s
// / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
/ / Generateur a l e a t o i r e
/ / A n a p p e l e r qu une f o i s , a v a n t Random ( )
void InitRandom ( )
55

4.4. Rcrations

16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64

4. Les tableaux

{
srand ( ( unsigned i n t ) time ( 0 ) ) ;
}
/ / Entre a e t b
i n t Random ( i n t a , i n t b )
{
r e t u r n a +( rand ( ) % ( ba + 1 ) ) ;
}
// / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
// Position et vitesse aleatoire
void I n i t B a l l e ( i n t &x , i n t &y , i n t &u , i n t &v , Color &c ) {
x=Random ( b a l l _ s i z e , widthb a l l _ s i z e ) ;
y=Random ( b a l l _ s i z e , height b a l l _ s i z e ) ;
u=Random ( 0 , 4 ) ;
v=Random ( 0 , 4 ) ;
c=Color ( byte ( Random ( 0 , 2 5 5 ) ) ,
byte ( Random ( 0 , 2 5 5 ) ) ,
byte ( Random ( 0 , 2 5 5 ) ) ) ;
}
// / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
/ / A f f i c h a g e d une b a l l e
void D e s s i n e B a l l e ( i n t x , i n t y , Color c o l ) {
f i l l R e c t ( xb a l l _ s i z e , yb a l l _ s i z e , 2 b a l l _ s i z e +1 ,2 b a l l _ s i z e +1 , c o l ) ;
}
// / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
/ / D e p l a c e m e n t d une b a l l e
void BougeBalle ( i n t &x , i n t &y , i n t &u , i n t &v ) {
/ / Rebond s u r l e s b o r d s g a u c h e e t d r o i t
i f ( x+u>widthb a l l _ s i z e || x+u< b a l l _ s i z e )
u=u ;
/ / Rebond s u r l e s b o r d s h a u t e t b a s e t c o m p t a g e du s c o r e
i f ( y+v< b a l l _ s i z e || y+v>height b a l l _ s i z e )
v=v ;
/ / Mise a j o u r d e l a p o s i t i o n
x+=u ;
y+=v ;
}
// / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
/ / Fonction p r i n c i p a l e
i n t main ( )
{
/ / Ouverture de l a f e n e t r e
openWindow ( width , h e i g h t ) ;
/ / Position et v i t e s s e des b a l l e s
i n t xb [ n b _ b a l l s ] , yb [ n b _ b a l l s ] , ub [ n b _ b a l l s ] , vb [ n b _ b a l l s ] ;
Color cb [ n b _ b a l l s ] ; / / C o u l e u r s d e s b a l l e s
InitRandom ( ) ;
f o r ( i n t i = 0 ; i < n b _ b a l l s ; i ++) {
I n i t B a l l e ( xb [ i ] , yb [ i ] , ub [ i ] , vb [ i ] , cb [ i ] ) ;
56

4. Les tableaux

65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80 }

4.4. Rcrations

D e s s i n e B a l l e ( xb [ i ] , yb [ i ] , cb [ i ] ) ;
}
/ / Boucle p r i n c i p a l e
while ( t r u e ) {
milliSleep (25);
noRefreshBegin ( ) ;
f o r ( i n t i = 0 ; i < n b _ b a l l s ; i ++) {
D e s s i n e B a l l e ( xb [ i ] , yb [ i ] , White ) ;
BougeBalle ( xb [ i ] , yb [ i ] , ub [ i ] , vb [ i ] ) ;
D e s s i n e B a l l e ( xb [ i ] , yb [ i ] , cb [ i ] ) ;
}
noRefreshEnd ( ) ;
}
endGraphics ( ) ;
return 0;

4.4.2

Avec des chocs !

Il nest ensuite pas trs compliqu de modifier le programme prcdent pour que
les balles rebondissent entre-elles. Le listing ci-aprs a t construit comme suit :
1. Lorsquune balle se dplace, on regarde aussi si elle rencontre une autre balle. Il
faut donc que BougeBalle connaisse les positions des autres balles. On modifie
donc BougeBalle en passant les tableaux complets des positions et des vitesses,
et en prcisant juste lindice de la balle dplacer (lignes 71 et 110). La boucle
de la ligne 78 vrifie ensuite via le test de la ligne 81 si lune des autres balles est
heurte par la balle courante. Auquel cas, on appelle ChocBalles qui modifie les
vitesses des deux balles. Notez les lignes 79 et 80 qui vitent de considrer le choc
dune balle avec elle-mme (nous verrons linstruction continue une autre fois).
2. Les formules du choc de deux balles peuvent se trouver facilement dans un cours
de prpa... ou sur le web. La fonction ChocBalles implmente ces formules. (Notez linclusion du fichier <cmath> pour avoir accs la racine carr sqrt () , aux
sinus et cosinus cos() et sin () , et larc-cosinus acos().
3. On ralise ensuite que les variables entires qui stockent positions et vitesses font
que les erreurs darrondis saccumulent et que les vitesses deviennent nulles ! On
bascule alors toutes les variables concernes en double, en pensant bien les
reconvertir en int lors de laffichage (ligne 37).
Le tout donne un programme bien plus anim. On ne peut videmment constater la
diffrence sur une figure dans un livre. Tlchargez donc le programme sur la page du
cours !
23 / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
24 / / P o s i t i o n e t v i t e s s e a l e a t o i r e
25 void I n i t B a l l e ( double &x , double &y , double &u , double &v , Color &c ) {
26
x=Random ( b a l l _ s i z e , widthb a l l _ s i z e ) ;
27
y=Random ( b a l l _ s i z e , height b a l l _ s i z e ) ;
28
u=Random ( 0 , 4 ) ;
29
v=Random ( 0 , 4 ) ;
57

4.4. Rcrations

30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78

4. Les tableaux

c=Color ( byte ( Random ( 0 , 2 5 5 ) ) ,


byte ( Random ( 0 , 2 5 5 ) ) ,
byte ( Random ( 0 , 2 5 5 ) ) ) ;
}
// / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
/ / A f f i c h a g e d une b a l l e
void D e s s i n e B a l l e ( double x , double y , Color c o l ) {
f i l l R e c t ( i n t ( x) b a l l _ s i z e , i n t ( y) b a l l _ s i z e ,
2 b a l l _ s i z e +1 ,2 b a l l _ s i z e +1 , c o l ) ;
}
// / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
/ / Choc e l a s t i q u e d e deux b a l l e s s p h e r i q u e s
/ / c f l a b o . n t i c . org
# i n c l u d e <cmath>
void C h o c B a l l e s ( double&x1 , double&y1 , double&u1 , double&v1 ,
double&x2 , double&y2 , double&u2 , double&v2 )
{
/ / Distance
double o2o1x=x1x2 , o2o1y=y1y2 ;
double d= s q r t ( o2o1x o2o1x+o2o1y o2o1y ) ;
i f ( d==0) r e t u r n ; / / Mme c e n t r e ?
/ / R e p r e ( o2 , x , y )
double Vx=u1u2 , Vy=v1v2 ;
double V= s q r t ( VxVx+VyVy ) ;
i f (V==0) r e t u r n ; / / Mme v i t e s s e
/ / R e p r e s u i v a n t V ( o2 , i , j )
double i x =Vx/V, i y =Vy/V, j x =iy , j y = i x ;
/ / Hauteur d a t t a q u e
double H=o2o1x j x +o2o1y j y ;
/ / Angle
double th=acos (H/d ) , c=cos ( th ) , s= s i n ( th ) ;
/ / V i t e s s e a p r s c h o c d a n s ( o2 , i , j )
double v 1 i =V c c , v 1 j =V c s , v 2 i =V s s , v 2 j=v 1 j ;
/ / Dans r e p r e d o r i g i n e (O, x , y )
u1= v 1 i i x + v 1 j j x +u2 ;
v1= v 1 i i y + v 1 j j y +v2 ;
u2+= v 2 i i x + v 2 j j x ;
v2+= v 2 i i y + v 2 j j y ;
}
// / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
/ / D e p l a c e m e n t d une b a l l e
void BougeBalle ( double x [ ] , double y [ ] , double u [ ] , double v [ ] , i n t i ) {
/ / Rebond s u r l e s b o r d s g a u c h e e t d r o i t
i f ( x [ i ]+u [ i ] > widthb a l l _ s i z e || x [ i ]+u [ i ] < b a l l _ s i z e )
u [ i ]=u [ i ] ;
/ / Rebond s u r l e s b o r d s h a u t e t b a s e t c o m p t a g e du s c o r e
i f ( y [ i ]+ v [ i ] < b a l l _ s i z e || y [ i ]+ v [ i ] > height b a l l _ s i z e )
v [ i ]=v [ i ] ;
f o r ( i n t j = 0 ; j < n b _ b a l l s ; j ++) {
58

4. Les tableaux

79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117

4.4. Rcrations

i f ( j == i )
continue ;
i f ( abs ( x [ i ]+u [ i ]x [ j ] ) < 2 b a l l _ s i z e
&& abs ( y [ i ]+ v [ i ]y [ j ] ) < 2 b a l l _ s i z e ) {
ChocBalles ( x [ i ] , y [ i ] , u[ i ] , v [ i ] , x [ j ] , y [ j ] , u[ j ] , v [ j ] ) ;
}
}
/ / Mise a j o u r d e l a p o s i t i o n
x [ i ]+=u [ i ] ;
y [ i ]+=v [ i ] ;
}
// / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
/ / Fonction p r i n c i p a l e
i n t main ( )
{
/ / Ouverture de l a f e n e t r e
openWindow ( width , h e i g h t ) ;
/ / Position et v i t e s s e des b a l l e s
double xb [ n b _ b a l l s ] , yb [ n b _ b a l l s ] , ub [ n b _ b a l l s ] , vb [ n b _ b a l l s ] ;
Color cb [ n b _ b a l l s ] ; / / C o u l e u r s d e s b a l l e s
InitRandom ( ) ;
f o r ( i n t i = 0 ; i < n b _ b a l l s ; i ++) {
I n i t B a l l e ( xb [ i ] , yb [ i ] , ub [ i ] , vb [ i ] , cb [ i ] ) ;
D e s s i n e B a l l e ( xb [ i ] , yb [ i ] , cb [ i ] ) ;
}
/ / Boucle p r i n c i p a l e
while ( t r u e ) {
milliSleep (25);
noRefreshBegin ( ) ;
f o r ( i n t i = 0 ; i < n b _ b a l l s ; i ++) {
D e s s i n e B a l l e ( xb [ i ] , yb [ i ] , White ) ;
BougeBalle ( xb , yb , ub , vb , i ) ;
D e s s i n e B a l l e ( xb [ i ] , yb [ i ] , cb [ i ] ) ;
}
noRefreshEnd ( ) ;
}
endGraphics ( ) ;
return 0;
}

4.4.3

Mlanger les lettres

Le programme suivant considre une phrase et permute alatoirement les lettres


intrieures de chaque mot (cest--dire sans toucher aux extrmits des mots). Il utilise
pour cela le type string, chane de caractre, pour lequel s[ i ] renvoie le i-me caractre
de la chane s, et s . size () le nombre de caractres de s (nous expliquerons plus tard la
notation "objet" de cette fonction). La phrase considre ici devient par exemple :
Ctete pteite psahre dreviat erte ecorne libslie puor vorte parvue ceeravu
59

4.4. Rcrations

4. Les tableaux

Lavez vous comprise ? Peu importe ! Cest le listing que vous devez comprendre :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

# i n c l u d e <iostream >
# include <string >
# include <cstdlib >
# i n c l u d e <ctime >
using namespace s t d ;
// / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
/ / Generateur a l e a t o i r e
/ / A n a p p e l e r qu une f o i s , a v a n t Random ( )
void InitRandom ( )
{
srand ( ( unsigned i n t ) time ( 0 ) ) ;
}
/ / Entre a e t b
i n t Random ( i n t a , i n t b )
{
r e t u r n a +( rand ( ) % ( ba + 1 ) ) ;
}
// / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
/ / Permuter l e s l e t t r e s i n t e r i e u r e s de s n f o i s
s t r i n g Melanger ( s t r i n g s , i n t n )
{
int l=int ( s . size ( ) ) ;
i f ( l <=3)
return s ;
s t r i n g t =s ;
f o r ( i n t i = 0 ; i <n ; i ++) {
i n t a=Random ( 1 , l 2 ) ;
int b ;
do
b=Random ( 1 , l 2 ) ;
while ( a==b ) ;
char c= t [ a ] ;
t [ a ]= t [ b ] ; t [ b ]= c ;
}
return t ;
}
i n t main ( )
{
const i n t n=11;
s t r i n g phrase [ n ] = { " C e t t e " , " p e t i t e " , " phrase " , " d e v r a i t " , " e t r e " ,
" encore " , " l i s i b l e " , " pour " , " v o t r e " , " pauvre " ,
" cerveau " } ;
InitRandom ( ) ;
60

4. Les tableaux

4.5. TP

F IGURE 4.2 Master mind...


48
49
50
51
52
53 }

4.5

f o r ( i n t i = 0 ; i <n ; i ++)
cout << Melanger ( phrase [ i ] , 3 ) << " " ;
cout << endl ;
return 0;

TP

Nous pouvons maintenant aller faire le troisime TP donn en annexe A.3 afin de
mieux comprendre les tableaux et aussi pour obtenir un master mind (voir figure 4.2
le rsultat dune partie intressante !).

4.6

Fiche de rfrence

Comme promis, nous compltons, en rouge, la "fiche de rfrence" avec ce qui a t


vu pendant ce chapitre et son TP.

61

4.6. Fiche de rfrence

4. Les tableaux

Fiche de rfrence (1/2)


Variables
Dfinition :
int i;
int k,l,m;
Affectation :
i=2;
j=i;
k=l=3;
Initialisation :
int n=5,o=n;
Constantes :
const int s=12;
Porte :
int i;
// i=j; interdit!
int j=2;
i=j; // OK!
if (j>1) {
int k=3;
j=k; // OK!
}
//i=k; interdit!
Types :
int i=3;
double x=12.3;
char c=A;
string s="hop";
bool t=true;
float y=1.2f;
unsigned int j=4;
signed char d=-128;
unsigned char d=25;
complex<double>
z(2,3);
Variables globales :
int n;
const int m=12;
void f() {
n=10;
// OK
int i=m; // OK
...
Conversion :
int i=int(x),j;
float x=float(i)/j;
Tests
Comparaison :
== != < > <= >=
Ngation : !
Combinaisons : && ||
if (i==0) j=1;
if (i==0) j=1;
else
j=2;

if (i==0) {
j=1;
k=2;
}
bool t=(i==0);
if (t)
j=1;
switch (i) {
case 1:
...;
...;
break;
case 2:
case 3:
...;
break;
default:
...;
}

for(int i=1;i<=10;i++)
...
for(int i=1,j=10;j>i;
i=i+2,j=j-3)
...
Fonctions
Dfinition :
int plus(int a,int b) {
int c=a+b;
return c;
}
void affiche(int a) {
cout << a << endl;
}
Dclaration :
int plus(int a,int b);

Retour :
int signe(double x) {
Dfinition :
if (x<0)
double x[5],y[5];
return -1;
for(int i=0;i<5;i++)
if (x>0)
y[i]=2*x[i];
return 1;
return
0;
const int n=5;
}
int i[n],j[2*n];
void afficher(int x,
Initialisation :
int y) {
int t[4]={1,2,3,4};
if (x<0 || y<0)
string s[2]={"ab","c"};
return;
Affectation :
if (x>=w || y>=h)
int s[3]={1,2,3},t[3];
return;
for (int i=0;i<3;i++)
DrawPoint(x,y,RED);
t[i]=s[i];
}
En paramtre :
Appel :
void init(int t[4]) {
int f(int a) { ... }
for(int i=0;i<4;i++) int g() { ... }
t[i]=0;
...
}
int i=f(2),j=g();
void init(int t[],
Rfrences :
int n) {
for(int i=0;i<n;i++) void swap(int& a,
int& b){
t[i]=0;
int
tmp=a;
}
a=b;b=tmp;
}
Boucles
...
do {
int x=3,y=2;
...
swap(x,y);
} while(!ok);
Tableaux

int i=1;
while(i<=100) {
...
i=i+1;
}

62

Surcharge :
int hasard(int n);
int hasard(int a,
int b);
double hasard();

4. Les tableaux

4.6. Fiche de rfrence

Fiche de rfrence (2/2)


Divers
i++;
i--;
i-=2;
j+=3;
j=i%n; // Modulo
#include <cstdlib>
...
i=rand()%n;
x=rand()/
double(RAND_MAX);
#include <ctime>
...
srand((unsigned int)
time(0));
#include <cmath>
double sqrt(double x);
double cos(double x);
double sin(double x);
double acos(double x);
#include <string>
using namespace std;
string s="hop";
char c=s[0];
int l=s.size();
Entres/Sorties
#include <iostream>
using namespace std;

...
cout <<"I="<<i<<endl;
cin >> i >> j;
Erreurs frquentes

}
int t[4]; t=f();
int s[3]={1,2,3},t[3];
t=s; // NON!

Pas de dfinition de fonction int t[2];


t={1,2}; // NON!
dans une fonction !
int q=r=4; // NON!

Imagine++
if (i=2) // NON!
Voir documentation...
if i==2 // NON!
if (i==2) then // NON! Clavier
for (int i=0,i<100,i++) Build : F7
// NON!
Debug : F5
int f() {...}
int i=f; // NON!
double x=1/3; // NON!
int i,j;
x=i/j; // NON!
x=double(i/j); //NON!

Step over : F10


Step inside : F11
Indent : Ctrl+K,Ctrl+F
Conseils

double x[10],y[10];
for (int i=1;i<=10;i++)
y[i]=2*x[i]; //NON
int n=5;

int t[n]; // NON

int f()[4] { // NON!

int t[4];
...

return t; // NON!

63

Nettoyer en quittant.
Erreurs et warnings : cliquer.
Indenter.
Ne pas laisser de warning.
Utiliser le debuggeur.
Faire des fonctions.
Tableaux : pas pour transcrire
une formule mathmatique !

5. Les structures

Chapitre 5
Les structures
Les fonctions et les boucles nous ont permis de regrouper des instructions identiques. Les
tableaux permettent de grouper des variables de mme type, mais pour manipuler plusieurs variables simultanment, il est tout aussi indispensable des fabriquer des structures de donnes...

5.1

Rvisions

Avant cela, il est utile de nous livrer une petite rvision, qui prendra la forme dun
inventaire des erreurs classiques commises par de nombreux dbutants... et mme de
celles, plus rares mais plus originales, constates chez certains ! Enfin, nous rpterons,
encore et toujours, les mmes conseils.

5.1.1

Erreurs classiques

En vrac :
Mettre un seul = dans les tests : if ( i=2)
Oublier les parenthses : if i==2
Utiliser then : if ( i==2) then
Mettre des virgules dans un for : for ( int i=0,i<100,i++)
Oublier les parenthses quand on appelle une fonction sans paramtre :
int f () { . . . }
...
int i=f ;
Vouloir affecter un tableau un autre :
int s [4]={1 ,2 ,3 ,4} , t [ 4 ] ;
t =s ;

5.1. Rvisions

5.1.2

5. Les structures

Erreurs originales

L, le dbutant ne se trompe plus : il invente carrment avec sans doute le fol espoir
que a existe peut-tre. Souvent, non seulement a nexiste pas, mais en plus a ne colle
ni aux grands principes de la syntaxe du C++, ni mme ce quun compilateur peut
comprendre ! Deux exemples :
Mlanger la syntaxe (si peu !) :
void s e t ( i n t t [ 5 ] ) {
...
}
...
int s [ 5 ] ;
/ / J u s q u e l , t o u t va b i e n !
s e t ( i n t s [ 5 ] ) ; / / L , c e s t quand mme n i m p o r t e q u o i !
alors quil suffit dun :
set ( s ) ;
Vouloir faire plusieurs choses la fois, ou ne pas comprendre quun programme
est une suite dinstructions excuter lune aprs lautre et non pas une forPn
1
mule
Sn . Par exemple, croire que le for est un symbole mathmatique comme 1
ou 1 . Ainsi, pour excuter une instruction quand tous les ok(i) sont vrais, on a
dj vu tenter un :
i f ( f o r ( i n t i = 0 ; i <n ; i ++) ok ( i ) )
...

/ / Du g r a n d a r t . . .

alors quil faut faire :


bool a l l o k = t r u e ;
f o r ( i n t i = 0 ; i <n ; i ++)
a l l o k =( a l l o k && ok ( i ) ) ;
i f ( allok )
...
ou mme mieux (voyez-vous la diffrence ?) :
bool a l l o k = t r u e ;
f o r ( i n t i = 0 ; i <n && a l l o k ; i ++)
a l l o k =( a l l o k && ok ( i ) ) ;
i f ( allok )
...
quon peut finalement simplifier en :
bool a l l o k = t r u e ;
f o r ( i n t i = 0 ; i <n && a l l o k ; i ++)
a l l o k =ok ( i ) ;
i f ( allok )
...
1. Ne me fates pas dire ce que je nai pas dit ! Les informaticiens thoriques considrent parfois les
programmes comme des formules, mais a na rien voir !

66

5. Les structures

5.2. Les structures

Il est comprhensible que le dbutant puisse tre victime de son manque de savoir,
dune mauvaise assimilation des leons prcdentes, de la confusion avec un autre
langage, ou de son imagination dbordante ! Toutefois, il faut bien comprendre quun
langage est finalement lui aussi un programme, limit et conu pour faire des choses
bien prcises. En consquence, il est plus raisonnable dadopter la conduite suivante :
Tout ce qui na pas t annonc comme possible est impossible !

5.1.3

Conseils

Indenter. Indenter. Indenter !


Cliquer sur les messages derreurs et de warnings pour aller directement la
bonne ligne !
Ne pas laisser de warning.
Utiliser le debuggeur.

5.2

Les structures

5.2.1

Dfinition

Si les tableaux permettent de manipuler plusieurs variables dun mme type, les
structures sont utilises pour regrouper plusieurs variables afin de les manipuler comme
une seule. On cre un nouveau type, dont les variables en question deviennent des
"sous-variables" appeles champs de la structure. Voici par exemple un type Point possdant deux champs de type double nomms x et y :
s t r u c t Point {
double x , y ;
};
Les champs se dfinissent avec la syntaxe des variables locales dune fonction. Attention par contre
Ne pas oublier le point virgule aprs laccolade qui ferme la dfinition de la
structure !
Lutilisation est alors simple. La structure est un nouveau type qui se manipule exactement comme les autres, avec la particularit supplmentaire quon accde aux champs
avec un point :
Point a ;
a . x=2.3;
a . y=3.4;
On peut videmment dfinir des champs de diffrents types, et mme des structures
dans des structures :
s t r u c t Cercle {
Point centre ;
double rayon ;
Color c o u l e u r ;
67

5.2. Les structures

5. Les structures

};
Cercle C;
C. centre . x =12.;
C. centre . y =13.;
C . rayon = 1 0 . 4 ;
C . c o u l e u r =Red ;
Lintrt des structures est vident et il faut
Regrouper dans des structures des variables ds quon repre quelles sont
logiquement lies. Si un programme devient pnible parce quon passe
systmatiquement plusieurs paramtres identiques de nombreuses fonctions, alors il est vraisemblable que les paramtres en question puissent
tre avantageusement regroups en une structure. Ce sera plus simple et
plus clair.

5.2.2

Utilisation

Les structures se manipulent comme les autres types 2 . La dfinition, laffectation,


linitialisation, le passage en paramtre, le retour dune fonction : tout est semblable
au comportement des types de base. Seule nouveaut : on utilise des accolades pour
prciser les valeurs des champs en cas dinitialisation 3 . On peut videmment faire
des tableaux de structures... et mme dfinir un champ de type tableau ! Ainsi, les
lignes suivantes se comprennent facilement :
P o i n t a = { 2 . 3 , 3 . 4 } , b=a , c ;
// Initialisations
c=a ;
// Affectations
C e r c l e C= { { 1 2 , 1 3 } , 1 0 . 4 , Red } ; / / I n i t i a l i s a t i o n
...
double d i s t a n c e ( P o i n t a , P o i n t b ) {
/ / Passage par valeur
r e t u r n s q r t ( ( a . xb . x ) ( a . xb . x ) + ( a . yb . y ) ( a . yb . y ) ) ;
}
void a g r a n d i r ( C e r c l e& C , double e c h e l l e ) { / / Par r f r e n c e
C . rayon=C . rayon e c h e l l e ; / / M o d i f i e l e r a y o n
}
Point milieu ( Point a , Point b ) {
// retour
P o i n t M;
M. x =( a . x+b . x ) / 2 ;
M. y =( a . y+b . y ) / 2 ;
r e t u r n M;
}
...
Point P [ 1 0 ] ;
/ / Tableau de s t r u c t u r e s
f o r ( i n t i = 0 ; i < 1 0 ; i ++) {
P [ i ] . x= i ;
P [ i ] . y= f ( i ) ;
}
2. Dailleurs, nous avions bien promis que seuls les tableaux avaient des particularits (passage par
rfrence, pas de retour possible et pas daffectation.
3. Comme pour un tableau !

68

5. Les structures

5.3. Rcration : TP

F IGURE 5.1 Corps clestes et duel...


...
/ / Un d b u t d e j e u d e Yam s
s t r u c t Tirage { / /
i n t de [ 5 ] ;
/ / champ d e t y p e t a b l e a u
};
Tirage lancer ( ) {
Tirage t ;
f o r ( i n t i = 0 ; i < 5 ; i ++)
t . de [ i ]=1+ rand ( ) % 6 ; / / Un d d e 1 6
return t ;
}
...
Tirage t ;
t=lancer ( ) ;
...
Attention, tout comme pour les tableaux, la syntaxe utilise pour linitialisation ne
marche pas pour une affectation 4 :
Point P ;
P={1 ,2}; / / Erreur !
Dailleurs, rptons-le :
Tout ce qui na pas t annonc comme possible est impossible !

5.3

Rcration : TP

Nous pouvons maintenant aller faire le TP donn en annexe A.4 afin de mieux comprendre les structures. Nous ferons mme des tableaux de structures 5 ! Nous obtien4. La situation samliorera avec les objets.
5. Coin des collgiens : il y a dans ce TP des mathmatiques et de la physique pour tudiant de
lenseignement suprieur... mais on peut trs bien faire les programmes en ignorant tout a !

69

5.3. Rcration : TP

5. Les structures

drons un projectile naviguant au milieu des toiles puis un duel dans lespace (figure
5.1) !

70

5. Les structures

5.4

5.4. Fiche de rfrence

Fiche de rfrence

Encore une fois, nous compltons, en rouge, la "fiche de rfrence" avec ce qui a t
vu pendant ce chapitre et son TP.

Fiche de rfrence (1/2)

if (i==0) j=1;

Dfinition :
int plus(int a,int b) {
int c=a+b;
return c;
}
void affiche(int a) {
cout << a << endl;
}

if (i==0) j=1;
else
j=2;

Dclaration :
int plus(int a,int b);

if (i==0) {
j=1;
k=2;
}

Retour :
int signe(double x) {
if (x<0)
return -1;
if (x>0)
return 1;
return 0;
}
void afficher(int x,
int y) {
if (x<0 || y<0)
return;
if (x>=w || y>=h)
return;
DrawPoint(x,y,RED);
}

Variables
Dfinition :
int i;
int k,l,m;
Affectation :
i=2;
j=i;
k=l=3;
Initialisation :
int n=5,o=n;
Constantes :
const int s=12;
Porte :
int i;
// i=j; interdit!
int j=2;
i=j; // OK!
if (j>1) {
int k=3;
j=k; // OK!
}
//i=k; interdit!
Types :
int i=3;
double x=12.3;
char c=A;
string s="hop";
bool t=true;
float y=1.2f;
unsigned int j=4;
signed char d=-128;
unsigned char d=25;
complex<double>
z(2,3);
Variables globales :
int n;
const int m=12;
void f() {
n=10;
// OK
int i=m; // OK
...
Conversion :
int i=int(x),j;
float x=float(i)/j;

Tests
Comparaison :
== != < > <= >=
Ngation : !
Combinaisons : && ||

bool t=(i==0);
if (t)
j=1;
switch (i) {
case 1:
...;
...;
break;
case 2:
case 3:
...;
break;
default:
...;
}
Boucles
do {
...
} while(!ok);

Appel :
int f(int a) { ... }
int g() { ... }
...
int i=f(2),j=g();

Rfrences :
void swap(int& a,
int& b){
int i=1;
int tmp=a;
while(i<=100) {
a=b;b=tmp;
...
}
i=i+1;
...
}
int x=3,y=2;
for(int i=1;i<=10;i++)
swap(x,y);
...
Surcharge :
for(int i=1,j=10;j>i;
int hasard(int n);
i=i+2,j=j-3)
int hasard(int a,
...
int b);
Fonctions
double hasard();

71

5.4. Fiche de rfrence

5. Les structures

Fiche de rfrence (2/2)


#include <cstdlib>
int n=5;
...
int t[n]; // NON
Dfinition :
i=rand()%n;
int f()[4] { // NON!
double x[5],y[5];
x=rand()/
int t[4];
for(int i=0;i<5;i++)
double(RAND_MAX);
...
y[i]=2*x[i];
#include <ctime>
return t; // NON!
...
const int n=5;
}
srand((unsigned int)
int i[n],j[2*n];
int t[4]; t=f();
time(0));
Initialisation :
int s[3]={1,2,3},t[3];
#include <cmath>
int t[4]={1,2,3,4};
t=s; // NON!
double sqrt(double x);
string s[2]={"ab","c"};
double cos(double x); int t[2];
Affectation :
t={1,2}; // NON!
double sin(double x);
int s[3]={1,2,3},t[3];
double acos(double x); struct Point {
for (int i=0;i<3;i++) #include <string>
double x,y;
t[i]=s[i];
using namespace std;
} // NON!
string s="hop";
En paramtre :
Point a;
char c=s[0];
void init(int t[4]) {
a={1,2}; // NON!
int l=s.size();
for(int i=0;i<4;i++)
t[i]=0;
Imagine++
Entres/Sorties
}
#include <iostream>
Voir documentation...
using namespace std;
void init(int t[],
Clavier
...
int n) {
cout
<<"I="<<i<<endl;
for(int i=0;i<n;i++)
Build : F7
cin >> i >> j;
t[i]=0;
Debug : F5
}
Erreurs frquentes
Pas de dfinition de fonction Step over : F10
Structures
dans une fonction !
Step inside : F11
struct Point {

int q=r=4; // NON!


double x,y;
Indent : Ctrl+K,Ctrl+F
if (i=2) // NON!
Color c;
if i==2 // NON!
};
Conseils
if (i==2) then // NON!
...
Nettoyer en quittant.
Point a;
for (int i=0,i<100,i++)
Erreurs et warnings : cliquer.
a.x=2.3; a.y=3.4;
// NON!
a.c=Red;
int f() {...}
Indenter.
Point b={1,2.5,Blue};
int i=f; // NON!
Ne pas laisser de warning.
double x=1/3; // NON!
Divers
Utiliser le debuggeur.
int i,j;
i++;
x=i/j; // NON!
Faire des fonctions.
i--;
x=double(i/j); //NON!
Tableaux : pas pour transcrire
i-=2;
double x[10],y[10];
une formule mathmatique !
j+=3;
for (int i=1;i<=10;i++)
y[i]=2*x[i]; //NON Faire des structures.
j=i%n; // Modulo
Tableaux

72

6. Plusieurs fichiers !

Chapitre 6
Plusieurs fichiers !
Lors du dernier TP, nous avons ralis deux projets quasiment similaires dont seuls les
main() taient diffrents. Modifier aprs coup une des fonctions de la partie commune aux
deux projets ncessiterait daller la modifier dans les deux projets. Nous allons voir maintenant
comment factoriser cette partie commune dans un seul fichier, de faon en simplifier les ventuelles futures modifications. Au passage 1 nous verrons comment dfinir un oprateur sur de
nouveaux types.

Rsumons notre progression dans le savoir-faire du programmeur :


1. Tout programmer dans le main() : cest un dbut et cest dj bien !
2. Faire des fonctions : pour tre plus lisible et ne pas se rpter ! (Axe des instructions)
3. Faire des tableaux et des structures : pour manipuler plusieurs variables la fois.
(Axe des donnes)
Nous rajoutons maintenant :
4. Faire plusieurs fichiers : pour utiliser des parties communes dans diffrents projets ou solutions. (A nouveau, axe des instructions)

F IGURE 6.1 Plusieurs fichiers sources...


1. Toujours cette ide que nous explorons les diffrentes composantes du langages quand le besoin
sen fait sentir.

6.1. Fichiers spars

6.1

6. Plusieurs fichiers !

Fichiers spars

Nous allons rpartir notre code source dans plusieurs fichiers. Mais avant toute
chose :
Pour un maximum de portabilit du code, choisir des noms de fichiers avec
seulement des caractres standard (pas de lettres accentues ni despace)
Dailleurs il est aussi prfrable dviter les accents pour les noms de variables et de
fonctions, tant pis pour la correction du franais...

6.1.1

Principe

Jusqu prsent un seul fichier source contenait notre programme C++. Ce fichier
source tait transform en fichier objet par le compilateur puis le linker compltait le
fichier objet avec les bibliothques du C++ pour en faire un fichier excutable. En fait,
un projet peut contenir plusieurs fichiers sources. Il suffit pour cela de rajouter un
fichier .cpp la liste des sources du projet :
Ouvrir le menu Project/Add New Item ou faire Ctrl+Maj+A ou cliquer sur
Choisir lajout dun fichier Visual C++/Code/C++ file et en prciser le nom
(sans quil soit besoin de prciser .cpp dans le nom)
Ainsi, en rajoutant un fichier C++ hop un projet contenant dj main.cpp, on se
retrouve avec une structure de projet identique celle de la figure 6.1.
Aprs cela, chaque gnration du projet consistera en :
1. Compilation : chaque fichier source est transform en un fichier objet (de mme
nom mais de suffixe .obj). Les fichiers sources sont donc compils indpendamment les uns des autres.
2. Link : les diffrents fichiers objets sont runis (et complts avec les bibliothques
du C++) en un seul fichier excutable (de mme nom que le projet).
Une partie des instructions du fichier principal (celui qui contient main()) peut donc
tre dporte dans un autre fichier. Cette partie sera compile sparment et rintgre
pendant ldition des liens. Se pose alors le problme suivant : comment utiliser dans
le fichier principal ce qui ce trouve dans les autres fichiers ? En effet, nous savions
(cf section 3.2.4) quune fonction ntait "connue" que dans les lignes qui suivaient
sa dfinition ou son ventuelle dclaration. Par "connue", il faut comprendre que le
compilateur sait quil existe ailleurs une fonction de tel nom avec tel type de retour et
tels paramtres. Malheureusement 2 :
une fonction nest pas "connue" en dehors de son fichier. Pour lutiliser dans
un autre fichier, il faut donc ly dclarer !
En clair, nous allons devoir procder ainsi :
Fichier hop.cpp :
2. Heureusement, en fait, car lorsque lon runit des fichiers de provenances multiples, il est prfrable que ce qui se trouve dans les diffrents fichiers ne se mlange pas de faon anarchique...

74

6. Plusieurs fichiers !

6.1. Fichiers spars

// Dfinitions
void f ( i n t x ) {
...
}
int g () {
...
}
/ / Autres f o n c t i o n s
...
Fichier main.cpp :
// Dclarations
void f ( i n t x ) ;
int g ( ) ;
...
i n t main ( ) {
...
// Utilisation
i n t a=g ( ) ;
f (a );
...
Nous pourrions aussi videmment dclarer dans hop.cpp certaines fonctions de main.cpp
pour pouvoir les utiliser. Attention toutefois : si des fichiers sutilisent de faon croise,
cest peut-tre que nous sommes en train de ne pas dcouper les sources convenablement.

6.1.2

Avantages

Notre motivation initiale tait de mettre une partie du code dans un fichier spar
pour lutiliser dans un autre projet. En fait, dcouper son code en plusieurs fichiers a
dautres intrts :
Rendre le code plus lisible et vitant les fichiers trop longs et en regroupant les
fonctions de faon structure.
Acclrer la compilation. Lorsquun programme devient long et complexe, le
temps de compilation nest plus ngligeable. Or, lorsque lon rgnre un projet,
lenvironnement de programmation ne recompile que les fichiers sources qui ont
t modifis depuis la gnration prcdente. Il serait en effet inutile de recompiler un fichier source non modifi pour ainsi obtenir le mme fichier objet 3 ! Donc
changer quelques lignes dans un fichier nentranera pas la compilation de tout
le programme mais seulement du fichier concern 4 .
Attention toutefois ne pas sparer en de trop nombreux fichiers ! Il devient alors plus
compliqu de sy retrouver et de naviguer parmi ces fichiers.
3. Cest en ralit un peu plus compliqu : un source peu dpendre, via des inclusions (cf section
6.1.4), dautres fichiers, qui, eux, peuvent avoir t modifis ! Il faut alors recompiler un fichier dont une
dpendance a t modifie. Visual gre automatiquement ces dpendances (Cest plus compliqu, mais
possible aussi, sous linux...)
4. En fait, Visual est mme capable de ne recompiler que certaines parties du fichier quand les modifications apportes sont simples...

75

6.1. Fichiers spars

6. Plusieurs fichiers !

F IGURE 6.2 Mme source dans deux projets

6.1.3

Utilisation dans un autre projet

Pour utiliser dans un projet 2 un fichier source dun projet 1, il suffit de rajouter le
source en question dans la liste des sources du projet 2 ! Pour cela, aprs avoir slectionn le projet 2 :
Choisir le menu Project/Add Existing Item (ou Alt+Maj+A ou

Slectionner le fichier source.


ou plus simplement :
Avec la souris, glisser/dplacer le fichier entre les deux projets 5 en maintenant
la touche Ctrl enfonce (un + apparat).
On obtient le rsultat figure 6.2.
Attention : il faut bien comprendre que
le fichier ainsi partag reste dans le rpertoire du premier projet
comme le confirme laffichage de ses proprits ( droite figure 6.2). Dans notre exemple,
chaque projet a son propre fichier principal, main.cpp ou main2.cpp, mais il y a un
seul hop.cpp. Modifier le contenu de ce fichier aura des consquences sur les deux
projets la fois. Cest ce que nous voulions !

6.1.4

Fichiers den-ttes

Le fichier spar nous permet de factoriser une partie du source. Toutefois, il faut
taper les dclarations de toutes les fonctions utilises dans chaque fichier principal les
utilisant. Nous pouvons mieux faire 6 . Pour cela, il est temps dexpliquer ce que fait
linstruction #include que nous rencontrons depuis nos dbuts :
La ligne #include "nom" est automatiquement remplace par le contenu
du fichier nom avant de procder la compilation.
Il sagit bien de remplacer par le texte complet du fichier nom comme avec un simple
copier/coller. Cette opration est faite avant la compilation par un programme dont
nous navions pas parl : le pr-processeur. La plupart des lignes commenant par un #
lui seront destines. Nous en verrons dautres. Attention : jusquici nous utilisions une
5. On peut aussi glisser/dplacer le fichier depuis lexplorateur Windows
6. Toujours le moindre effort...

76

6. Plusieurs fichiers !

6.1. Fichiers spars

forme lgrement diffrente : #include <nom>, qui va chercher le fichier nom dans les
rpertoires des bibliothques C++ 7 .
Grce cette possibilit du pr-processeur, il nous suffit de mettre les dclarations
se rapportant au fichier spar dans un troisime fichier et de linclure dans les fichiers principaux. Il est dusage de prendre pour ce fichier supplmentaire le mme
nom que le fichier spar, mais avec lextension .h : on appelle ce fichier un fichier
den-tte 8 . Pour crer ce fichier, faire comme pour le source, mais en choisissant "Visual C++/Code/Header file" au lieu de "Visual C++/Code/C++ file". Voila ce que cela
donne :
Fichier hop.cpp :
// Dfinitions
void f ( i n t x ) {
...
}
int g () {
...
}
/ / Autres f o n c t i o n s
...
Fichier hop.h :
// Dclarations
void f ( i n t x ) ;
int g ( ) ;
Fichier main.cpp du premier projet :
# i n c l u d e " hop . h "
...
i n t main ( ) {
...
// Utilisation
i n t a=g ( ) ;
f (a );
...
Fichier main2.cpp du deuxime projet (il faut prciser lemplacement complet
de len-tte, qui se trouve dans le rpertoire du premier projet 9 ) :
# i n c l u d e " . . / P r o j e c t 1 /hop . h "
...
i n t main ( ) {
...
7. Les fichiers den-tte iostream, etc. sont parfois appels en-ttes systme. Leur nom ne se termine
pas toujours par .h (voir aprs)
8. .h comme header.
9. On peut aussi prciser au compilateur une liste de rpertoires o il peut aller chercher les fichiers
den-tte. Utiliser pour cela les proprits du projet, option "C/C++ / General / Additional
Include Directories" en prcisant "../Project1" comme rpertoire. Aprs quoi, un #include
"hop.h" suffit.

77

6.1. Fichiers spars

6. Plusieurs fichiers !

// Utilisation
f (12);
i n t b=g ( ) ;
...
En fait, pour tre sur que les fonctions dfinies dans hop.cpp sont cohrentes avec
leur dclaration dans hop.h, et bien que a soit pas obligatoire, on inclut aussi lentte dans le source, ce qui donne :
Fichier hop.cpp :
# i n c l u d e " hop . h "
...
// Dfinitions
void f ( i n t x ) {
...
}
int g () {
...
}
/ / Autres f o n c t i o n s
...
En pratique, le fichier den-tte ne contient pas seulement les dclarations des fonctions mais aussi les dfinitions des nouveaux types (comme les structures) utiliss par
le fichier spar. En effet, ces nouveaux types doivent tre connus du fichier spar,
mais aussi du fichier principal. Il faut donc vraiment :
1. Mettre dans len-tte les dclarations des fonctions et les dfinitions
des nouveaux types.
2. Inclure len-tte dans le fichier principal mais aussi dans le fichier spar.
Cela donne par exemple :
Fichier vect.h :
/ / Types
s t r u c t Vecteur {
double x , y ;
};
// Dclarations
double norme ( Vecteur V ) ;
Vecteur plus ( Vecteur A, Vecteur B ) ;
Fichier vect.cpp :
# include " vect . h" / / Fonctions e t t y p e s
// Dfinitions
double norme ( Vecteur V) {
...
}
Vecteur plus ( Vecteur A, Vecteur B ) {
78

6. Plusieurs fichiers !

6.1. Fichiers spars

...
}
/ / Autres f o n c t i o n s
...

Fichier main.cpp du premier :


# include " vect . h"
...
i n t main ( ) {
...
// Utilisation
Vecteur C=plus (A, B ) ;
double n=norme (C ) ;
...

6.1.5

A ne pas faire...

Il est "fortement" conseill de :


1. ne pas dclarer dans len-tte toutes les fonctions du fichier spar mais seulement celles qui seront utilises par le fichier principal. Les fonctions secondaires
nont pas apparatre 10 .
2. ne jamais inclure un fichier spar lui-mme ! Cest gnralement une "grosse
btise" 11 . Donc

pas de #include "vect.cpp" !

6.1.6

Implmentation

Finalement, la philosophie de ce systme est que


10. On devrait mme tout faire pour bien les cacher et pour interdire au fichier principal de les utiliser.
Il serait possible de le faire ds maintenant, mais nous en reparlerons plutt quand nous aborderons les
objets...
11. Une mme fonction peut alors se retrouver dfinie plusieurs fois : dans le fichier spar et dans le
fichier principal qui linclut. Or, sil est possible de dclarer autant de fois que ncessaire une fonction, il
est interdit de la dfinir plusieurs fois (ne pas confondre avec la surcharge qui rend possible lexistence
de fonctions diffrentes sous le mme nom - cf section 3.2.6)

79

6.2. Oprateurs

6. Plusieurs fichiers !

Le fichier spar et son en-tte forment un tout cohrent, implmentant


un certain nombre de fonctionnalits.
Celui qui les utilise, qui nest pas ncessairement celui qui les a programmes, se contente de rajouter ces fichiers son projet, dinclure
len-tte dans ses sources et de profiter de ce que len-tte dclare.
Le fichier den-tte doit tre suffisamment clair et informatif pour que
lutilisateur nait pas regarder le fichier spar lui-mme a .
a. Dailleurs, si lutilisateur le regarde, il peut tre tent de tirer profit de ce qui sy trouve et
dutiliser plus que ce que len-tte dclare. Or, le crateur du fichier spar et de len-tte peut
par la suite tre amen changer dans son source la faon dont il a programm les fonctions
sans pour autant changer leurs fonctionnalits. Lutilisateur qui a "trich" en allant regarder
dans le fichier spar peut alors voir ses programmes ne plus marcher. Il na pas respect la
rgle du jeu qui tait de nutiliser que les fonctions de len-tte sans savoir comment elles sont
implmentes. Nous reparlerons de tout a avec les objets. Nous pourrons alors faire en sorte
que lutilisateur ne triche pas... De toute faon, notre niveau actuel, le crateur et lutilisateur
sont une seule et mme personne. A elle de ne pas tricher !

6.1.7

Inclusions mutuelles

En passant laction, le dbutant dcouvre souvent des problmes non prvus lors
du cours. Il est mme en gnral imbattable pour cela ! Le problme le plus frquent
qui survient avec les fichiers den-tte est celui de linclusion mutuelle. Il arrive que les
fichiers den-tte aient besoin den inclure dautres eux-mmes. Or, si le fichier A.h
inclut B.h et si B.h inclut A.h alors toute inclusion de A.h ou de B.h se solde par une
phnomne dinclusions sans fin qui provoque une erreur 12 . Pour viter cela, on utilise
une instruction du pr-processeur signalant quun fichier dj inclus ne doit plus ltre
nouveau : on ajoute
#pragma once au dbut de chaque fichier den-tte.
Certains compilateurs peuvent ne pas connatre #pragma once. On utilise alors une
astuce que nous donnons sans explication :
Choisir un nom unique propre au fichier den-tte. Par exemple VECT_H pour le
fichier vect.h.
Placer #ifndef VECT_H et #define VECT_H au dbut du fichier vect.h et #endif
la fin.

6.2

Oprateurs

Le C++ permet de dfinir les oprateurs +, -, etc. quand les oprandes sont de nouveaux types. Voici trs succinctement comment faire. Nous laissons au lecteur le soin
de dcouvrir seul quels sont les oprateurs quil est possible de dfinir.
Considrons lexemple suivant qui dfinit un vecteur 13 2D et en implmente laddition :
12. Les pr-processeurs savent heureusement dtecter ce cas de figure.
13. Coin des collgiens : vous ne savez pas ce quest un vecteur... mais vous tes plus forts en programmation que les "vieux". Alors regardez les sources qui suivent et vous saurez ce quest un vecteur
2D !

80

6. Plusieurs fichiers !

6.2. Oprateurs

s t r u c t vect {
double x , y ;
};
v e c t plus ( v e c t m, v e c t n ) {
v e c t p={m. x+n . x ,m. y+n . y } ;
return p ;
}
i n t main ( ) {
vect a ={1 ,2} , b = { 3 , 4 } ;
v e c t c=plus ( a , b ) ;
return 0;
}
Voici comment dfinir le + entre deux vect et ainsi remplacer la fonction plus() :
s t r u c t vect {
double x , y ;
};
v e c t o p e r a t o r +( v e c t m, v e c t n ) {
v e c t p={m. x+n . x ,m. y+n . y } ;
return p ;
}
i n t main ( ) {
vect a ={1 ,2} , b = { 3 , 4 } ;
v e c t c=a+b ;
return 0;
}
Nous pouvons aussi dfinir un produit par un scalaire, un produit scalaire 14 , etc 15 .
/ / P r o d u i t p a r un s c a l a i r e
v e c t o p e r a t o r ( double s , v e c t m) {
v e c t p={ s m. x , s m. y } ;
return p ;
}
/ / Produit s c a l a i r e
double o p e r a t o r ( v e c t m, v e c t n ) {
r e t u r n m. xn . x+m. yn . y ;
}
i n t main ( ) {
vect a ={1 ,2} , b = { 3 , 4 } ;
v e c t c =2a ;
double s=a b ;
return 0;
}
Remarquez que les deux fonctions ainsi dfinies sont diffrentes bien que de mme
nom (operator) car elles prennent des paramtres diffrents (cf surcharge section 3.2.6).
14. Dans ce cas, on utilise a*b et non a.b, le point ntant pas dfinissable car rserv laccs aux
champs de la structure
15. On peut en fait dfinir ce qui existe dj sur les types de base. Attention, il est impossible de
redfinir les oprations des types de base ! Pas question de donner un sens diffrent 1+1.

81

6.3. Rcration : TP suite et fin

6.3

6. Plusieurs fichiers !

Rcration : TP suite et fin

Le programme du TP prcdent tant un exemple parfait de besoin de fichiers spars (structures bien identifies, partages par deux projets), nous vous proposons, dans
le TP A.5 de convertir (et terminer ?) notre programme de simulation de gravitation et
de duel dans lespace !

6.4

Fiche de rfrence

La fiche habituelle...

Fiche de rfrence (1/3)


Variables
Dfinition :
int i;
int k,l,m;
Affectation :
i=2;
j=i;
k=l=3;
Initialisation :
int n=5,o=n;
Constantes :
const int s=12;
Porte :
int i;
// i=j; interdit!
int j=2;
i=j; // OK!
if (j>1) {
int k=3;
j=k; // OK!
}
//i=k; interdit!
Types :
int i=3;
double x=12.3;
char c=A;
string s="hop";
bool t=true;
float y=1.2f;

unsigned int j=4;


signed char d=-128;
unsigned char d=25;
complex<double>
z(2,3);
Variables globales :
int n;
const int m=12;
void f() {
n=10;
// OK
int i=m; // OK
...
Conversion :
int i=int(x),j;
float x=float(i)/j;
Tests
Comparaison :
== != < > <= >=
Ngation : !
Combinaisons : && ||
if (i==0) j=1;
if (i==0) j=1;
else
j=2;
if (i==0) {
j=1;
k=2;
}

82

bool t=(i==0);
if (t)
j=1;
switch (i) {
case 1:
...;
...;
break;
case 2:
case 3:
...;
break;
default:
...;
}
Boucles
do {
...
} while(!ok);
int i=1;
while(i<=100) {
...
i=i+1;
}
for(int i=1;i<=10;i++)
...
for(int i=1,j=10;j>i;
i=i+2,j=j-3)
...

6. Plusieurs fichiers !

6.4. Fiche de rfrence

Fiche de rfrence (2/3)


Fonctions
Oprateurs :
#include "vect.h",
y
vect operator+(
compris dans vect.cpp
Dfinition :
vect A,vect B) { Fonctions : dclarations dans
int plus(int a,int b) {
...
int c=a+b;
le .h, dfinitions dans le
}
return c;
.cpp
...
}
Types : dfinitions dans le .h
vect C=A+B;
void affiche(int a) {
Ne dclarer dans le .h que les
cout << a << endl;
Tableaux
fonctions utiles.
}
Dfinition :
#pragma once au dbut du
Dclaration :
fichier.
double x[5],y[5];
int plus(int a,int b);
for(int i=0;i<5;i++) Ne pas trop dcouper...
Retour :
y[i]=2*x[i];
int signe(double x) {
Divers
if (x<0)
const int n=5;
return -1;
i++;
int i[n],j[2*n];
if (x>0)
i--;
Initialisation :
return 1;
i-=2;
int t[4]={1,2,3,4};
return 0;
j+=3;
string s[2]={"ab","c"};
}
j=i%n; // Modulo
Affectation :
void afficher(int x,
int s[3]={1,2,3},t[3]; #include <cstdlib>
int y) {
...
for (int i=0;i<3;i++)
if (x<0 || y<0)
i=rand()%n;
t[i]=s[i];
return;
x=rand()/
if (x>=w || y>=h) En paramtre :
double(RAND_MAX);
return;
void init(int t[4]) {
DrawPoint(x,y,RED);
#include <ctime>
for(int i=0;i<4;i++)
}
...
t[i]=0;
srand((unsigned int)
Appel :
}
time(0));
int f(int a) { ... }

void
init(int
t[],
int g() { ... }
#include <cmath>
int n) {
...
double sqrt(double x);
for(int i=0;i<n;i++)
int i=f(2),j=g();
double cos(double x);
t[i]=0;
double sin(double x);
Rfrences :
}
double acos(double x);
void swap(int& a,
int& b){
Structures
#include <string>
int tmp=a;
using namespace std;
struct Point {
a=b;b=tmp;
string s="hop";
double x,y;
}
char c=s[0];
Color c;
...
int l=s.size();
};
int x=3,y=2;
...
Entres/Sorties
swap(x,y);
Point a;
#include <iostream>
Surcharge :
a.x=2.3; a.y=3.4;
using namespace std;
int hasard(int n);
a.c=Red;
...
int hasard(int a,
Point b={1,2.5,Blue};
cout <<"I="<<i<<endl;
int b);
Compilation spare
cin >> i >> j;
double hasard();

83

6.4. Fiche de rfrence

6. Plusieurs fichiers !

Fiche de rfrence (3/3)


int f()[4] { // NON!
int t[4];
Pas de dfinition de fonction
...
dans une fonction !
return t; // NON!
int q=r=4; // NON!
}
int t[4]; t=f();
if (i=2) // NON!
if i==2 // NON!
int s[3]={1,2,3},t[3];
if (i==2) then // NON!
t=s; // NON!
Erreurs frquentes

Debug : F5
Step over : F10
Step inside : F11
Indent : Ctrl+K,Ctrl+F
Conseils

for (int i=0,i<100,i++) int t[2];

t={1,2}; // NON!
// NON!

struct Point {
int f() {...}

double x,y;
int i=f; // NON!

} // NON!
double x=1/3; // NON!

Point a;
int i,j;
a={1,2}; // NON!

x=i/j; // NON!
x=double(i/j); //NON! #include "vec.cpp"//NON

double x[10],y[10];
Imagine++
for (int i=1;i<=10;i++)

Voir documentation...
y[i]=2*x[i]; //NON

Clavier
int n=5;
int t[n]; // NON

Build : F7

84

Nettoyer en quittant.
Erreurs et warnings : cliquer.
Indenter.
Ne pas laisser de warning.
Utiliser le debuggeur.
Faire des fonctions.
Tableaux : pas pour transcrire
une formule mathmatique !
Faire des structures.
Faire des fichiers spars.
Le .h doit suffire lutilisateur (qui ne doit pas regarder
le .cpp)

7. La mmoire

Chapitre 7
La mmoire
Il est grand temps de revenir sur la mmoire et son utilisation. Nous pourrons alors mieux
comprendre les variables locales, comment marche exactement lappel dune fonction, les fonctions rcursives, etc. Aprs cela, nous pourrons enfin utiliser des tableaux de taille variable
(sans pour autant rentrer vraiment dans la notion dlicate de pointeur).

7.1

Lappel dune fonction

Il sagit l dune nouvelle occasion pour vous de comprendre enfin ce qui se passe
dans un programme...

7.1.1

Exemple

Considrons le programme suivant :


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# i n c l u d e <iostream >
using namespace s t d ;
void v e r i f i e ( i n t p , i n t q , i n t quo , i n t r e s ) {
i f ( re s <0 || re s >=q || qquo+ r e s ! = p )
cout << " Tiens , c e s t b i z a r r e ! " << endl ;
}
i n t d i v i s e ( i n t a , i n t b , i n t& r ) {
int q ;
q=a/b ;
r=aqb ;
verifie (a ,b,q, r );
return q ;
}
i n t main ( )
{
i n t num, denom ;
do {
cout << " E n t r e z deux e n t i e r s p o s i t i f s : " ;

7.1. Lappel dune fonction

21
22
23
24
25
26
27
28 }

7. La mmoire

c i n >> num >> denom ;


} while (num<=0 || denom< = 0 ) ;
i n t quotient , r e s t e ;
q u o t i e n t = d i v i s e (num, denom , r e s t e ) ;
cout << num << " / " << denom << " = " << q u o t i e n t
<< " ( I l r e s t e " << r e s t e << " ) " << endl ;
return 0;

Calculant le quotient et le reste dune division entire, et vrifiant quils sont corrects,
il nest pas passionnant et surtout inutilement long (en fait, ce sont juste les lignes 11
et 12 qui font tout !). Il sagit par contre dun bon exemple pour illustrer notre propos.
Une bonne faon dexpliquer exhaustivement son droulement est de remplir le tableau suivant, dj rencontr au TP A.2. En ne mettant que les lignes o les variables
changent, en supposant que lutilisateur rentre 23 et 3 au clavier, et en indiant avec
des lettres les diffrentes tapes dune mme ligne 1 , cela donne :
Ligne
18
21
23
24a
9
10
11
12
13a
4
5
7
13b
14
15
24b
25
28

num
?
23
23
23
23
23
23
23
23
23
23
23
23
23
23
23
23

denom
?
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3

quotient

reste

?
?
?
?
?
?
?
?
?
?
?
?
?
7
7

?
?
?
?
?
2
2
2
2
2
2
2
2
2
2

qd

23
23
23
23
23
23
23
23
23
23

3
3
3
3
3
3
3
3
3
3

[reste]
[reste]
[reste]
[reste]
[reste]
[reste]
[reste]
[reste]
[reste]
[reste]

?
7
7
7
7
7
7
7
7

retd

pv

qv

quo

res

23
23

3
3

7
7

2
2

7
7
7

A posteriori, on constate quon a implicitement suppos que lorsque le programme


est en train dexcuter divise () , la fonction main() et ses variables existent encore et
quelles attendent simplement la fin de divise () . Autrement dit :
Un appel de fonction est un mcanisme qui permet de partir excuter momentanment cette fonction puis de retrouver la suite des instructions et les
variables quon avait provisoirement quittes.
Les fonctions sappelant les unes les autres, on se retrouve avec des appels de fonctions imbriqus les uns dans les autres : main() appelle divise () qui lui-mme appelle
verifie () 2 . Plus prcisment, cette imbrication est un empilement et on parle de pile
des appels. Pour mieux comprendre cette pile, nous allons utiliser le debuggeur. Avant
cela, prcisons ce quun informaticien entend par pile.
1. par exemple 24a et 24b
2. Et dailleurs main() a lui-mme t appel par une fonction a laquelle il renvoie un int.

86

7. La mmoire

7.1. Lappel dune fonction

Pile/File
Une pile est une structure permettant de mmoriser des donnes dans laquelle
celles-ci sempilent de telle sorte que celui qui est rang en dernier dans la pile en
est extrait en premier. En anglais, une pile (stack) est aussi appele LIFO (last in
first out 3 ). On y empile (push) et on y dpile (pop) les donnes. Par exemple, aprs
un push(1), un push(2) et un push(3), le premier pop() donnera 3, le deuxime
pop() donnera 2 et un dernier pop() donnera 1.
Pour une file (en anglais queue), cest la mme chose mais le premier arriv est
le premier sorti (FIFO). Par exemple, aprs un push(1), un push(2) et un push(3),
le premier pop() donnera 1, le deuxime pop() donnera 2 et un dernier pop()
donnera 3.

7.1.2

Pile des appels et dbuggeur

Observons donc la figure 7.1 obtenue en lanant notre programme dexemple sous
debuggeur. En regardant la partie gauche de chaque tape, nous pouvons voir la pile
des appels. La partie droite affiche le contenu des variables, paramtres et valeurs de
retour dont nous pouvons constater la cohrence avec le tableau prcdent.
(a) Comme lindique la pile des appels, nous sommes ligne 24 de la fonction main(),
qui se trouve en haut de la pile. Plus profond dans la pile, nous voyons que
main() a t appel par une fonction mainCRTStartup() elle-mme appele par
un trange kernel32.dll 4 . Vrifiez aussi les variables et le fait quelles valent
nimporte quoi (ici -858993460 !) tant quelles ne sont pas initialises ou affectes.
(b) Avanons en pas--pas dtaill (touche F11) jusqu la ligne 12. Nous sommes
dans la fonction divise () , q vient de valoir 7, et la ligne 24 de main() est descendue
dun cran dans la pile des appels.
(c) Nous sommes maintenant la ligne 5 dans verifie () . La pile des appels un niveau de plus, divise () est en attente la ligne 13 et main() toujours en 24. Vrifiez
au passage que la variable q affiche est bien celle de verifie () , qui vaut 3 et non
pas celle de divise () .
(d) Ici, lexcution du programme na pas progress et nous en sommes toujours
la ligne 5. Simplement, Visual offre la possibilit en double-cliquant sur la pile
dappel de regarder ce qui se passe un des niveaux infrieurs, notamment pour
afficher les instructions et les variables de ce niveau. Ici, en cliquant sur la ligne
de divise () dans la fentre de la ligne dappel, nous voyons apparatre la ligne
13 et ses variables dans leur tat alors que le programme est en 5. Entre autres,
le q affich est celui de divise () et vaut 7.
(e) Toujours sans avancer, voici ltat du main() et de ses variables (entre autres, reste
est bien pass 2 depuis la ligne 12 de divise () .
(f) Nous excutons maintenant la suite jusqu nous retrouver en ligne 24 au retour de divise () . Pour cela, on peut faire du pas--pas dtaill, ou simplement
3. Dernier rentr, premier sorti.
4. Ces deux fonctions sont respectivement : (i) la fonction que le compilateur cre pour faire un
certain nombre de choses avant et aprs main() et (ii) la partie de Windows qui lance le programme
lui-mme.

87

7.1. Lappel dune fonction

7. La mmoire

(a)

(b)

(c)

(d)

(e)

(f)

(g)
F IGURE 7.1 Appels de fontions
88

7. La mmoire

pile

top

7.2. Variables Locales

variable
place libre
top pv
place libre
qv
quo
res
a
23
a
b
3
b
qd
7
qd
r
[reste]
r
denom
3
denom
num
23
num
quotient
?
quotient
reste
?
reste
pris par les
...
pris par les
les fonctions
...
les fonctions
avant main()
...
avant main()
pile
variable
valeur
variable

pile

valeur

valeur
23
3
7
2
23
3
7
[reste]
3
23
?
2
...
...
...

place libre

top

denom
num
quotient
reste
pris par les
les fonctions
avant main()

3
23
7
2
...
...
...

F IGURE 7.2 Pile et variables locales. De gauche droite : tape (b) (ligne 12), tape (c)
(ligne 5) et tape (g) (ligne 25/26).
deux fois de suite un pas--pas sortant 5 (Maj-F11) pour relancer jusqu sortir de
verifie () , puis jusqu sortir de divise () . On voit bien quotient, qui est encore
non dfini, et aussi la valeur de retour de divise () , non encore affecte quotient.
(g) Un pas--pas de plus et nous sommes en 25/26. La variable quotient vaut enfin
7.

7.2

Variables Locales

Il va tre important pour la suite de savoir comment les paramtres et les variables
locales sont stocks en mmoire.
5. Step Out ou Maj-F11 ou . Notez aussi possibilit de continuer le programme jusqu une certaine
ligne sans avoir besoin de mettre un point darrt temporaire sur cette ligne mais simplement en cliquant
sur la ligne avec le bouton de droite et en choisissant "excuter jusqu cette ligne" ( )

89

7.3. Fonctions rcursives

7.2.1

7. La mmoire

Paramtres

Pour les paramtres, cest simple :


Les paramtres sont en fait des variables locales ! Leur seule spcificit est
dtre initialiss ds le dbut de la fonction avec les valeurs passes lappel
de la fonction.

7.2.2

La pile

Les variables locales (et donc les paramtres) ne sont pas mmorises des adresses
fixes en mmoire 6 , dcides la compilation. Si on faisait a, les adresses mmoire
en question devraient tre rserves pendant toute lexcution du programme : on ne
pourrait y ranger les variables locales dautres fonctions. La solution retenue est beaucoup plus conome en mmoire 7 :
Les variables locales sont mmorises dans un pile :
Quand une variables locale est cre, elle est rajoute en haut de cette
pile.
Quand elle meurt (en gnral quand on quitte sa fonction) elle est sortie de la pile.
Ainsi, au fur et mesure des appels, les variables locales sempilent : la mmoire est
utilise juste pendant le temps ncessaire. La figure 7.2 montre trois tapes de la pile
pendant lexcution de notre exemple.

7.3

Fonctions rcursives

Un fonction rcursive est une fonction qui sappelle elle-mme. La fonction la plus
classique pour illustrer la rcursivit est la factorielle 8 . Voici une faon simple et rcursive de la programmer :
5 int fact1 ( int n)
6 {
7
i f ( n==1)
8
return 1;
9
r e t u r n n f a c t 1 ( n 1 ) ;
10 }
On remarque videmment que les fonctions rcursives contiennent (en gnral au dbut, et en tout cas avant lappel rcursif !) une condition darrt : ici si n vaut 1, la fonction
retourne directement 1 sans sappeler elle-mme 9 .
6.
7.
8.
9.

Souvenons-nous du chapitre 2.
Et permettra de faire des fonctions rcursives, cf section suivante !
Coin des collgiens : La factorielle dun nombre entier n scrit n! et vaut n! = 1 2 ... n.
Le fait de pouvoir mettre des return au milieu des fonctions est ici bien commode !

90

7. La mmoire

7.3.1

7.3. Fonctions rcursives

Pourquoi a marche ?

Si les fonctions avaient mmoris leurs variables locales des adresses fixes, la
rcursivit naurait pas pu marcher : lappel rcursif aurait cras les valeurs des variables. Par exemple, fact1 (3) aurait cras la valeur 3 mmorise dans n par un 2 en
appelant fact1 (2) ! Cest justement grce la pile que le n de fact1 (2) nest pas le mme
que celui de fact1 (3). Ainsi, lappel fact1 (3) donne-til le tableau suivant :
Ligne
5f act1(3)
9af act1(3)
5f act1(2)
9af act1(2)
5f act1(1)
8f act1(1)
10f act1(1)
9bf act1(2)
10f act1(2)
9bf act1(3)
10f act1(3)

nf act1(3)
3
3
3
3
3
3
3
3
3
3

retf act1(3)

nf act1(2)

2
2
2
2
2
2
6
6

retf act1(2)

nf act1(1)

retf act1(1)

1
1
2
2
2

1
1
1

Ce tableau devient difficile crire maintenant quon sait que les variables locales ne
dpendent pas que de la fonction mais changent chaque appel ! On est aussi oblig
de prciser, pour chaque numro de ligne, quel appel de fonction est concern. Si on
visualise la pile, on comprend mieux pourquoi a marche. Ainsi, arrivs en ligne 8 de
fact1 (1) pour un appel initial fact1 (3), la pile ressemble :
pile

variable
valeur
place libre
top nf act1(1)
1
nf act1(2)
2
nf act1(3)
3
ce que lon peut aisment vrifier avec le dbuggeur. Finalement :
Les fonctions rcursives ne sont pas diffrentes des autres. Cest le systme
dappel des fonctions en gnral qui rend la rcursivit possible.

7.3.2

Efficacit

Une fonction rcursive est simple et lgante crire quand le problme sy prte 10 .
Nous venons de voir quelle nest toujours pas facile suivre ou debugger. Il faut
aussi savoir que
la pile des appels nest pas infinie et mme relativement limite.
Ainsi, le programme suivant
22 / / F a i t d b o r d e r l a p i l e
23 i n t f a c t 3 ( i n t n )
24 {
10. Cest une erreur classique de dbutant que de vouloir abuser du rcursif.

91

7.3. Fonctions rcursives

25
26
27
28 }

7. La mmoire

i f ( n==1)
return 1;
r e t u r n n f a c t 3 ( n + 1 ) ; / / e r r e u r !

dans lequel une erreur sest glisse va sappeler thoriquement linfini et en pratique
sarrtera avec une erreur de dpassement de la pile des appels 11 . Mais la vraie raison
qui fait quon vite parfois le rcursif est qu
appeler une fonction est un mcanisme coteux !
Lorsque le corps dune fonction est suffisamment petit pour que le fait dappeler cette
fonction ne soit pas ngligeable devant le temps pass excuter la fonction elle-mme,
il est prfrable dviter ce mcanisme dappel 12 . Dans le cas dune fonction rcursive,
on essaie donc sil est ncessaire dcrire une version drcursive (ou itrative) de la
fonction. Pour notre factorielle, cela donne :
/ / Version i t r a t i v e
int fact2 ( int n)
{
i n t f =1;
f o r ( i n t i = 2 ; i <=n ; i ++)
f = i ;
return f ;
}
ce qui aprs tout nest pas si terrible.
Enfin, il arrive qucrire une fonction sous forme rcursive ne soit pas utilisable
pour des raisons de complexit. Une exemple classique est la suite de Fibonacci dfinie
par :

f0 = f1 = 1
fn = fn1 + fn2
et qui donne : 1, 1, 2, 3, 5, 8,... En version rcursive :
32 / / T r s l e n t !
33 i n t f i b 1 ( i n t n ) {
34
i f ( n <2)
35
return 1;
36
r e t u r n f i b 1 ( n2)+ f i b 1 ( n 1 ) ;
37 }
cette fonction a la mauvaise ide de sappeler trs souvent : n = 10 appelle n = 9
et n = 8, mais n = 9 appelle lui aussi n = 8 de son ct en plus de n = 7, n = 7
qui lui-mme est appel par tous les n = 8 lancs, etc. Bref, cette fonction devient
rapidement trs lente. Ainsi, pour n = 40, elle sappelle dj 300.000.000 de fois ellemme, ce qui prend un certain temps ! Il est donc raisonnable den programmer une
version drcursive :
39 / / D r c u r s i v e
40 i n t f i b 2 ( i n t n ) {
11. Sous Visual, il sarrte pour n = 5000 environ.
12. Nous verrons dans un autre chapitre les fonctions inline qui rpondent ce problme.

92

7. La mmoire

41
42
43
44
45
46
47
48 }

7.4. Le tas

i n t fnm2 =1 , fnm1 = 1 ;
f o r ( i n t i = 2 ; i <=n ; i ++) {
i n t fn=fnm2+fnm1 ;
fnm2=fnm1 ;
fnm1=fn ;
}
r e t u r n fnm1 ;

Mentionnons aussi quil existe des fonctions suffisamment tordues pour que leur version rcursive ne se contente pas de sappeler un grand nombre de fois en tout, mais
un grand nombre de fois en mme temps, ce qui fait quindpendamment des questions
defficacit, leur version rcursive fait dborder la pile dappels !

7.4

Le tas

La pile nest pas la seule zone de mmoire utilise par les programmes. Il y a aussi
le tas (heap en anglais).

7.4.1

Limites

La pile est limite en taille. La pile dappel ntant pas infinie et les variables locales
ntant pas en nombre illimit, il est raisonnable de rserver une pile de relativement
petite taille. Essayez donc le programme :
32 i n t main ( )
33 {
34
const i n t n=500000;
35
int t [n ] ;
36
...
37 }
Il sexcute avec une erreur : "stack overflow". La variable locale t nest pas trop grande
pour lordinateur 13 : elle est trop grande pour tenir dans la pile. Jusqu prsent, on
savait quon tait limit aux tableaux de taille constante. En ralit, on est aussi limit
aux petits tableaux. Il est donc grand temps dapprendre utiliser le tas !

7.4.2

Tableaux de taille variable

Nous fournissons ici une rgle appliquer en aveugle. Sa comprhension viendra plus tard
si ncessaire.
Lorsquon veut utiliser un tableau de taille variable, il ny a que deux choses faire,
mais elle sont essentielles toutes les deux 14 :
13. 500000x4 soit 2Mo seulement !
14. Et le dbutant oublie toujours la deuxime, ce qui a pour consquence des programmes qui grossissent en quantit de mmoire occupe...

93

7.4. Le tas

7. La mmoire

1. Remplacer int t[n] par int* t=new int[n] (ou lquivalent pour
un autre type que int)
2. Lorsque le tableau doit mourir (en gnral en fin de fonction), rajouter
la ligne delete[] t;
Le non respect de la rgle 2 fait que le tableau reste en mmoire jusqu la fin du programme, ce qui entraine en gnral une croissance anarchique de la mmoire utilise
(on parle de fuite de mmoire). Pour le reste, on ne change rien. Programmer un tableau
de cette faon fait quil est mmoris dans le tas et non plus dans la pile. On fait donc
ainsi :
1. Pour les tableaux de taille variable.
2. Pour les tableaux de grande taille.
Voici ce que cela donne sur un petit programme :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

# i n c l u d e <iostream >
using namespace s t d ;
void r e m p l i t ( i n t t [ ] , i n t n )
{
f o r ( i n t i = 0 ; i <n ; i ++)
t [ i ]= i + 1 ;
}
i n t somme( i n t t [ ] , i n t n )
{
i n t s =0;
f o r ( i n t i = 0 ; i <n ; i ++)
s+= t [ i ] ;
return s ;
}
void f i x e ( )
{
const i n t n=5000;
int t [n ] ;
remplit ( t , n ) ;
i n t s=somme( t , n ) ;
cout << s << " d e v r a i t v a l o i r " << n ( n+1)/2 << endl ;
}
void v a r i a b l e ( )
{
int n;
cout << "Un e n t i e r SVP : " ;
c i n >> n ;
i n t t =new i n t [ n ] ; / / A l l o c a t i o n
remplit ( t , n ) ;
i n t s=somme( t , n ) ;
94

7. La mmoire

35
36
37
38
39
40
41
42
43
44

7.5. Loptimiseur

cout << s << " d e v r a i t v a l o i r " << n ( n+1)/2 << endl ;


delete [ ] t ;
/ / D e s a l l o c a t i o n : ne p a s o u b l i e r !
}
i n t main ( )
{
fixe ( ) ;
variable ( ) ;
return 0;
}

7.4.3

Essai dexplication

Ce qui suit nest pas essentiel pour un dbutant mais peut ventuellement rpondre ses
interrogations. Sil comprend, tant mieux, sinon, quil oublie et se contente pour linstant de la
rgle prcdente !
Pour avoir accs toute la mmoire de lordinateur 15 , on utilise le tas. Le tas est une
zone mmoire que le programme possde et qui peut crotre sil en fait la demande au
systme dexploitation (et sil reste de la mmoire de libre videmment). Pour utiliser le
tas, on appelle une fonction dallocation laquelle on demande de rserver en mmoire
de la place pour un certain nombre de variables. Cest ce que fait new int[n].
Cette fonction retourne ladresse de lemplacement mmoire quelle a rserv. Nous
navons jamais rencontr de type de variable capable de mmoriser une adresse. Il
sagit des pointeurs dont nous reparlerons plus tard. Un pointeur vers de la mmoire
stockant des int est de type int. Do le int t pour mmoriser le retour du new.
Ensuite, un pointeur peut sutiliser comme un tableau, y compris comme paramtre
dune fonction.
Enfin, il ne faut pas oublier de librer la mmoire au moment o le tableau de taille
constante aurait disparu : cest ce que fait la fonction delete [] t qui libre la mmoire
pointe par t.

7.5

Loptimiseur

Mentionnons ici un point important qui tait nglig jusquici, mais que nous allons
utiliser en TP.
Il y a plusieurs faons de traduire en langage machine un source C++. Le rsultat
de la compilation peut donc tre diffrent dun compilateur lautre. Au moment de
compiler, on peut aussi rechercher produire un excutable le plus rapide possible :
on dit que le compilateur optimise le code. En gnral, loptimisation ncessite un plus
grand travail mais aussi des transformations qui font que le code produit nest plus
facilement dbuggable. On choisit donc en pratique entre un code debuggable et un
code optimis.
Jusquici, nous utilisions toujours le compilateur en mode "Debug". Lorsquun programme est au point (et seulement lorsquil lest), on peut basculer le compilateur en
15. Plus exactement ce que le systme dexploitation veut bien attribuer au maximum chaque programme, ce qui est en gnral rglable mais en tout cas moins que la mmoire totale, bien que beaucoup
plus que la taille de la pile.

95

7.6. Assertions

7. La mmoire

mode "Release" pour avoir un programme plus performant. Dans certains cas, les gains
peuvent tre considrables. Un programmeur expriment fait mme en sorte que loptimiseur puisse efficacement faire son travail. Ceci dit, il faut respecter certaines rgles :
Ne pas debugger quand on est en mode Release ( !)
Rester en mode Debug le plus longtemps possible pour bien mettre au point le
programme.
Savoir que les modes Debug et Release crent des fichiers objets et excutables
dans des rpertoires diffrents et donc que lorsquon nettoie les solutions, il faut
le faire pour chacun des deux modes.

7.6

Assertions

Voici une fonction trs utile pour faire des programmes moins buggs ! La fonction
assert () prvient quand un test est faux. Elle prcise le fichier et le numro de ligne o
elle se trouve, offre la possibilit de debugger le programme, etc. Elle ne ralentit pas
les programmes car elle disparat la compilation en mode Release. Cest une fonction
peu connue des dbutants, et cest bien dommage ! Par exemple :
# include <cassert >
...
int n;
c i n >> n ;
assert (n>0);
i n t t =new i n t [ n ] ;

// Allocation

Si lutilisateur entre une valeur ngative ou nulle, les consquences pourraient tre fcheuses. En particulier une valeur ngative de n serait interprte comme un grand
entier (car le [] attend un entier non sign, ainsi -1 serait compris comme le plus grand
int possible) et le new serait probablement un chec. A noter que si n==0, un tableau
nul, lallocation marche. Mais dans ce cas t [0] nexiste mme pas ! La seule chose quon
peut donc faire avec un tableau nul cest le dsallouer avec delete [] t ;. Il est toujours
utile de se prmunir contre une telle exception en vrifiant que la valeur est raisonnable.

7.7

TP

Le TP que nous proposons en A.6 est une illustration des fonctions rcursive et des
tableaux de taille variable. Il consiste en la programmation de quelques faons de trier
des donnes : tri bulle, Quicksort, etc. Afin de rendre le tout plus attrayant, le tri sera
visualis graphiquement et chronomtr (figure 7.3).

96

7. La mmoire

7.8. Fiche de rfrence

F IGURE 7.3 Deux tris en cours dexcution : tri bulle et Quicksort...

7.8

Fiche de rfrence

Fiche de rfrence (1/3)


Variables
Dfinition :
int i;
int k,l,m;
Affectation :
i=2;
j=i;
k=l=3;
Initialisation :
int n=5,o=n;
Constantes :
const int s=12;
Porte :
int i;
// i=j; interdit!
int j=2;
i=j; // OK!
if (j>1) {
int k=3;
j=k; // OK!
}
//i=k; interdit!
Types :
int i=3;
double x=12.3;
char c=A;
string s="hop";
bool t=true;
float y=1.2f;
unsigned int j=4;

signed char d=-128;


unsigned char d=25;
complex<double>
z(2,3);
Variables globales :
int n;
const int m=12;
void f() {
n=10;
// OK
int i=m; // OK
...
Conversion :
int i=int(x),j;
float x=float(i)/j;
Pile/Tas
Tests
Comparaison :
== != < > <= >=
Ngation : !
Combinaisons : && ||
if (i==0) j=1;
if (i==0) j=1;
else
j=2;
if (i==0) {
j=1;
k=2;
}

97

bool t=(i==0);
if (t)
j=1;
switch (i) {
case 1:
...;
...;
break;
case 2:
case 3:
...;
break;
default:
...;
}
Boucles
do {
...
} while(!ok);
int i=1;
while(i<=100) {
...
i=i+1;
}
for(int i=1;i<=10;i++)
...
for(int i=1,j=10;j>i;
i=i+2,j=j-3)
...

7.8. Fiche de rfrence

7. La mmoire

Fiche de rfrence (2/3)


Fonctions
Pile des appels
Ne dclarer dans le .h que les
fonctions utiles.
Dfinition :
Itratif/Rcursif
int plus(int a,int b) {
#pragma once au dbut du
Tableaux
int c=a+b;
fichier.
return c;
Dfinition :
Ne pas trop dcouper...
}

double
x[5],y[5];
void affiche(int a) {
for(int i=0;i<5;i++) Divers
cout << a << endl;
y[i]=2*x[i];
i++;
}
i--;
const int n=5;
Dclaration :
i-=2;
int i[n],j[2*n];
int plus(int a,int b);
j+=3;
Initialisation :
Retour :
j=i%n; // Modulo
int t[4]={1,2,3,4};
int signe(double x) {
string s[2]={"ab","c"}; #include <cstdlib>
if (x<0)
return -1;
...
Affectation :
if (x>0)
i=rand()%n;
int s[3]={1,2,3},t[3];
return 1;
x=rand()/
for (int i=0;i<3;i++)
return 0;
double(RAND_MAX);
t[i]=s[i];
}
#include <ctime>
En paramtre :
void afficher(int x,
...
int y) {
void init(int t[4]) {
srand((unsigned int)
if (x<0 || y<0)
for(int i=0;i<4;i++)
time(0));
return;
t[i]=0;
#include <cmath>
if (x>=w || y>=h)
}
double sqrt(double x);
return;
void init(int t[],
double cos(double x);
DrawPoint(x,y,RED);
int n) {
double sin(double x);
}
for(int i=0;i<n;i++) double acos(double x);
Appel :
t[i]=0;
#include <string>
int f(int a) { ... }
}
using namespace std;
int g() { ... }
Taille variable :
string s="hop";
...
int* t=new int[n];
char c=s[0];
int i=f(2),j=g();
...
int l=s.size();
Rfrences :
delete[] t;
void swap(int& a,
#include <ctime>
int& b){
s=double(clock())
Structures
int tmp=a;
/CLOCKS_PER_SEC;
struct Point {
a=b;b=tmp;
double x,y;
Entres/Sorties
}
Color c;
...
#include <iostream>
};
int x=3,y=2;
using namespace std;
...
swap(x,y);
...
Point a;
Surcharge :
cout <<"I="<<i<<endl;
a.x=2.3; a.y=3.4;
int hasard(int n);
cin >> i >> j;
a.c=Red;
int hasard(int a,
Point b={1,2.5,Blue}; Clavier
int b);
double hasard();
Compilation spare
Build : F7
Oprateurs :
#include "vect.h",
y Debug : F5
vect operator+(
compris dans vect.cpp
Step over : F10
vect A,vect B) {

Fonctions
:
dclarations
dans
...
le .h, dfinitions dans le Step inside : F11
}
Indent : Ctrl+K,Ctrl+F
.cpp
...
Types : dfinitions dans le .h Step out : Maj+F11
vect C=A+B;

98

7. La mmoire

7.9. Examens sur machine

Fiche de rfrence (3/3)


Erreurs frquentes
...

return t; // NON!
Pas de dfinition de fonction

}
dans une fonction !

int t[4]; t=f();


int q=r=4; // NON!
int s[3]={1,2,3},t[3];
if (i=2) // NON!
t=s; // NON!
if i==2 // NON!
if (i==2) then // NON! int t[2];

t={1,2}; // NON!
for (int i=0,i<100,i++)

// NON!
struct Point {

double x,y;
int f() {...}
}
//
NON!
int i=f; // NON!
double x=1/3; // NON! Point a;

a={1,2}; // NON!
int i,j;
x=i/j; // NON!
#include "vec.cpp"//NON
x=double(i/j); //NON!

Imagine++
double x[10],y[10];

for (int i=1;i<=10;i++) Voir documentation...


y[i]=2*x[i]; //NON
Conseils
int n=5;

Nettoyer en quittant.
int t[n]; // NON
Erreurs et warnings : cliquer.
int f()[4] { // NON!
Indenter.
int t[4];

7.9

Ne pas laisser de warning.


Utiliser le debuggeur.
Faire des fonctions.
Tableaux : pas pour transcrire
une formule mathmatique !
Faire des structures.
Faire des fichiers spars.
Le .h doit suffire lutilisateur (qui ne doit pas regarder
le .cpp)
Ne pas abuser du rcursif.
Ne pas oublier delete.
Compiler rgulirement.
Debug/Release : nettoyer les
deux.
#include <cassert>
...
assert(x!=0);
y=1/x;

Examens sur machine

Nous vous conseillons aussi de vous confronter aux examens proposs en annexe.
Vous avez toutes les connaissances ncessaires.

99

8. Allocation dynamique

Chapitre 8
Allocation dynamique
Nous revenons une fois de plus sur lutilisation du tas pour grer des tableaux de taille
variable. Aprs avoir mentionn lexistence de tableaux bidimensionnels de taille fixe, nous dtaillons lallocation dynamique 1 dj vue en 7.4.2 et expliquons enfin les pointeurs, du moins
partiellement. A travers lexemple des matrices (et des images en TP) nous mlangeons structures et allocation dynamique. Il sagira l de notre structure de donne la plus complexe avant
larrive tant attendue - et maintenant justifie - des objets...

8.1
8.1.1

Tableaux bidimensionnels
Principe

Il existe en C++ des tableaux deux dimensions. Leur utilisation est similaire celle
des tableaux standards :
Il faut utiliser des crochets (lignes 1 et 4 du programme ci-dessous). Attention :
[ i ][ j ] et non [ i , j ].
Linitialisation est possible avec des accolades (ligne 5). Attention : accolades imbriques.
Leurs dimensions doivent tre constantes (lignes 6 et 7).

B[0]
1
2
3
i n t A[ 2 ] [ 3 ] ;
f o r ( i n t i = 0 ; i < 2 ; i ++)
f o r ( i n t j = 0 ; j < 3 ; j ++)
B[1]
4
5
6
A[ i ] [ j ]= i + j ;
int B[2][3]={{1 ,2 ,3} ,{4 ,5 ,6}};
c o n s t i n t M=2 ,N= 3 ;
Tableau 2D B [2][3]={{1,2,3},{4,5,6}} .
i n t C[M] [N] ;
Notez que B[0] est le tableau 1D {1,2,3}
et B[1] le tableau 1D {4,5,6} .
La figure ci-dessus montre le tableau B. A noter que B[0] et B[1] sont des tableaux
1D reprsentant les lignes de B.
1
2
3
4
5
6
7

1. cest--dire lallocation de mmoire dans le tas avec new et delete.

8.1. Tableaux bidimensionnels

8.1.2

8. Allocation dynamique

Limitations

Vis--vis des fonctions, les particularits sont les mmes quen 1D :


Impossible de retourner un tableau 2D.
Passage uniquement par variable.
mais avec une restriction supplmentaire :
On est oblig de prciser les dimensions dun tableau 2D paramtre de fonction.
Impossible donc de programmer des fonctions qui peuvent travailler sur des tableaux
de diffrentes tailles comme dans le cas 1D (cf 4.3.1). Cest trs restrictif et explique
que les tableaux 2D ne sont pas toujours utiliss. On peut donc avoir le programme
suivant :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

/ / Passage de paramtre
double t r a c e ( double A [ 2 ] [ 2 ] ) {
double t = 0 ;
f o r ( i n t i = 0 ; i < 2 ; i ++)
t +=A[ i ] [ i ] ;
return t ;
}
/ / Le p a s s a g e e s t t o u j o u r s p a r r f r e n c e . . .
void s e t ( double A [ 2 ] [ 3 ] ) {
f o r ( i n t i = 0 ; i < 2 ; i ++)
f o r ( i n t j = 0 ; j < 3 ; j ++)
A[ i ] [ j ]= i + j ;
}
...
double D[ 2 ] [ 2 ] = { { 1 , 2 } , { 3 , 4 } } ;
double t = t r a c e (D ) ;
double E [ 2 ] [ 3 ] ;
set (E ) ;
...
mais il est impossible de programmer une fonction trace () ou set () qui marche pour
diffrentes tailles de tableaux 2D comme on laurait fait en 1D :

1
2
3
4
5
6
7
8
9
10
11
12

/ / OK
void s e t ( double A[ ] , i n t n , double x ) {
f o r ( i n t i = 0 ; i <n ; i ++)
A[ i ]= x ;
}
/ / NON ! ! ! ! ! ! ! ! ! ! ! ! ! ! !
/ / double A[ ] [ ] est refus
void s e t ( double A [ ] [ ] , double m, double n , double x ) {
f o r ( i n t i = 0 ; i <m; i ++)
f o r ( i n t j = 0 ; j <n ; j ++)
A[ i ] [ j ]= x ;
}
102

8. Allocation dynamique

8.1. Tableaux bidimensionnels

B[0]=1

B[1]=4

B[2]=2

B[3]=5

B[4]=3

B[5]=6

F IGURE 8.1 La matrice B de lexemple prcdent stocke en 1D.

8.1.3

Solution

En pratique, ds que lon doit manipuler des tableaux de dimension 2 (ou plus !) de
diffrentes tailles, on les mmorise dans des tableaux 1D en stockant par exemple les
colonnes les unes aprs les autres pour profiter des avantages des tableaux 1D. Ainsi,
on stockera une matrice A de m lignes de n colonnes dans un tableau T de taille mn
en plaant llment A(i, j) en T (i + mj). La Fig. 8.1 montre le tableau B de lexemple
prcdent stock comme tableau 1D. On peut alors crire :
1 void s e t ( double A[ ] , i n t m, i n t n ) {
2
f o r ( i n t i = 0 ; i <m; i ++)
3
f o r ( i n t j = 0 ; j <n ; j ++)
4
A[ i +m j ]= i + j ;
5 }
6
...
7
double F [ 2 3 ] ;
8
set (F , 2 , 3 ) ;
9
double G[ 3 5 ] ;
10
s e t (G, 3 , 5 ) ;
ou par exemple, ce produit matrice vecteur dans lequel les vecteurs et les matrices sont
stocks dans des tableaux 1D :
1 / / y=Ax
2 void p r o d u i t ( double A[ ] , i n t m, i n t n , double x [ ] , double y [ ] )
3 {
4
f o r ( i n t i = 0 ; i <m; i ++) {
5
y[ i ]=0;
6
f o r ( i n t j = 0 ; j <n ; j ++)
7
y [ i ]+=A[ i +m j ] x [ j ] ;
8
}
9 }
10
11
...
12
double P [ 2 3 ] , x [ 3 ] , y [ 2 ] ;
13
...
14
// P=... x=...
15
p r o d u i t ( P , 2 , 3 , x , y ) ; / / y=Px

103

8.2. Allocation dynamique

8.2

8. Allocation dynamique

Allocation dynamique

Il ny a pas dallocation dynamique possible pour les tableaux 2D. Il faut donc vraiment les mmoriser dans des tableaux 1D comme expliqu ci-dessus pour pouvoir les
allouer dynamiquement dans le tas. Lexemple suivant montre comment faire. Il utilise
la fonction produit() donne ci-dessus sans quil soit besoin de la redfinir :
1
2
3
4
5
6
7
8
9
10
11
12

i n t m, n ;
...
double A=new double [mn ] ;
double x=new double [ n ] ;
double y=new double [m] ;
...
/ / A= . . . x = . . .
p r o d u i t (A,m, n , x , y ) ; / / y=Ax
...
d e l e t e [ ] A;
delete [ ] x ;
delete [ ] y ;

8.2.1

Pourquoi a marche ?

Il est maintenant temps dexpliquer pourquoi, une fois allous, nous pouvons utiliser des tableaux dynamiques exactement comme des tableaux de taille fixe. Il suffit de
comprendre les tapes suivantes :
1. int t [n] dfinit une variable locale, donc de la mmoire dans la pile, capable de
stocker n variables int.
2. int t dfinit une variable de type "pointeur" dint, cest--dire que t peut mmoriser ladresse dune zone mmoire contenant des int.
3. new int[n] alloue dans le tas une zone mmoire pouvant stocker n int et renvoie
ladresse de cette zone. Do le int t=new int[n]
4. delete [] t libre dans le tas ladresse mmorise dans t.
5. Lorsque t est un tableau de taille fixe t [ i ] dsigne son i me lment. Lorsque t
est un pointeur dint, t [ i ] dsigne la variable int stocke i places 2 plus loin en
mmoire que celle situe ladresse t. Ainsi, aprs un int t [n] comme aprs un
int t=new int[n], la syntaxe t [ i ] dsigne bien ce quon veut.
6. Lorsque t est un tableau de taille fixe, la syntaxe t tout court dsigne ladresse
(dans la pile) laquelle le tableau est mmoris. De plus, lorsquune fonction
prend un tableau comme paramtre, la syntaxe int s [] signifie en ralit que s
est ladresse du tableau. Ce qui fait quen fin de compte :
une fonction f ( int s []) est conue pour quon lui passe une adresse s
elle marche videmment avec les tableaux allous dynamiquement qui ne
sont finalement que des adresses
cest plutt lappel f ( t ), avec t tableau de taille fixe, qui sadapte en passant
f ladresse o se trouve le tableau.
2. Ici, une place est videmment le nombre doctets ncessaires au stockage dun int.

104

8. Allocation dynamique

8.2. Allocation dynamique

logiquement, on devrait mme dclarer f par f ( int s) au lieu de f ( int s []) .


Les deux sont en fait possibles et synonymes.
Vous pouvez donc maintenant programmer, en comprenant, ce genre de choses :
1 double somme( double t , i n t n ) { / / S y n t a x e " p o i n t e u r "
2
double s = 0 ;
3
f o r ( i n t i = 0 ; i <n ; i ++)
4
s+= t [ i ] ;
5
return s ;
6 }
7
...
8
int t1 [ 4 ] ;
9
...
10
double s1=somme( t1 , 4 ) ;
11
...
12
i n t t 2 =new i n t [ n ] ;
13
...
14
double s2=somme( t2 , n ) ;
15
...
16
delete [ ] t2 ;

8.2.2

Erreurs classiques

Vous comprenez maintenant aussi les erreurs classiques suivantes (que vous nviterez pas pour autant !).
1. Oublier dallouer :
int t ;
f o r ( i n t i = 0 ; i <n ; i ++)
t[ i ]=...
/ / Horreur : t vaut n importe
/ / q u o i comme a d r e s s e
2. Oublier de dsallouer :
void f ( i n t n ) {
i n t t =new i n t [ n ] ;
...
} / / On o u b l i e d e l e t e [ ] t ;
/ / Chaque a p p e l f ( ) va p e r d r e n i n t d a n s l e t a s !
3. Ne pas dsallouer ce quil faut :
i n t t =new i n t [ n ] ;
i n t s=new i n t [ n ] ;
...
s= t ; / / A i e ! Du coup , s c o n t i e n t l a mme a d r e s s e que t
/ / (On n a p a s r e c o p i l a z o n e p o i n t e p a r t d a n s c e l l e
//
p o i n t e par s ! )
...
d e l e t e [ ] t ; / / OK
d e l e t e [ ] s ; / / C a t a : Non s e u l e m e n t on ne l i b r e p a s l a mmoire
105

8.2. Allocation dynamique

8. Allocation dynamique

/ / i n i t i a l e m e n t m m o r i s e d a n s s , m a i s en p l u s on
/ / d s a l l o u e nouveau c e l l e q u i v i e n t d t r e l i b r e !

8.2.3

Consquences

Quand librer ?
Maintenant que vous avez compris new et delete, vous imaginez bien quon nattend pas toujours la fin de lexistence du tableau pour librer la mmoire. Le plus tt
est le mieux et on libre la mmoire ds que le tableau nest plus utilis :
1 void f ( ) {
2
int t [10];
3
i n t s=new
4
...
5
delete [ ] s ;
6
7
...
8 } / / Par c o n t r e

int [n ] ;
/ / s i s ne s e r t p l u s d a n s l a s u i t e . . .
/ / Autant l i b r e r m a i n t e n a n t . . .
, t a t t e n d c e t t e l i g n e pour mourir .

En fait, le tableau dont ladresse est mmorise dans s est allou ligne 3 et libr ligne
5. La variable s qui mmorise son adresse, elle, est cre ligne 3 et meurt ligne 8 !
Pointeurs et fonctions
Il est frquent que le new et le delete ne se fassent pas dans la mme fonction (attention, du coup, aux oublis !). Ils sont souvent intgrs dans des fonctions. A ce propos,
lorsque des fonctions manipulent des variables de type pointeur, un certain nombre de
questions peuvent se poser. Il suffit de respecter la logique :
Une fonction qui retourne un pointeur se dclare int f ();
1 int alloue ( int n) {
2
r e t u r n new i n t [ n ] ;
3 }
4
....
5
int t=alloue ( 1 0 ) ;
6
...
Un pointeur pass en paramtre une fonction lest par valeur. Ne pas mlanger avec le fait quun tableau est pass par rfrence ! Considrez le programme
suivant :
1 void f ( i n t t , i n t n ) {
2
....
3
t [ i ] = . . . ; / / On m o d i f i e t [ i ] m a i s p a s t !
4
t = . . . / / Une t e l l e l i g n e ne c h a n g e r a i t p a s s
5
/ / dans l a f o n c t i o n a p p e l a n t e
6 }
7 ...
8
i n t s=new i n t [m] ;
9
f ( s ,m) ;
106

8. Allocation dynamique

8.3. Structures et allocation dynamique

En fait, cest parce quon passe ladresse dun tableau quon peut modifier ses lments. Par ignorance, nous disions que les tableaux taient passs par rfrence
en annonant cela comme une exception. Nous pouvons maintenant rectifier :
Un tableau est en fait pass via son adresse. Cette adresse est passe par
valeur. Mais ce mcanisme permet la fonction appele de modifier le
tableau. Dire quun tableau est pass par rfrence tait un abus de
langage simplificateur.
Si on veut vraiment passer le pointeur par rfrence, la syntaxe est logique :
int& t. Un cas typique de besoin est :
1 / / t et n seront modifis ( et plus seulement t [ i ] )
2 void a l l o u e ( i n t & t , i n t& n ) {
3
c i n >> n ; / / n e s t c h o i s i au c l a v i e r
4
t =new i n t [ n ] ;
5 }
6
...
7
int t ;
8
int n;
9
alloue ( t , n ) ; / / t e t n sont a f f e c t s par a l l o u e ( )
10
...
11
d e l e t e [ ] t ; / / Ne p a s o u b l i e r p o u r a u t a n t !
Bizzarerie ? Les lignes 7 et 8 ci-dessus auraient pu scrire int t ,n;. En fait, il faut
remettre une toile devant chaque variable lorsquon dfinit plusieurs pointeurs en
mme-temps. Ainsi, int t , s,u; dfinit deux pointeurs dint (les variables t et u) et
un int (la variable s).

8.3

Structures et allocation dynamique

Passer systmatiquement un tableau et sa taille toutes les fonctions est videmment pnible. Il faut les runir dans une structure. Je vous laisse mditer lexemple
suivant qui pourrait tre un passage dun programme implmentant des matrices 3 et
leur produit :
1
2
3
4
5
6
7
8
9
10

# i n c l u d e <iostream >
# include <string >
using namespace s t d ;
/ / ==================================================
/ / f o n c t i o n s sur l e s m a t r i c e s
/ / p o u r r a i e n t e t r e d a n s un m a t r i c e . h e t un m a t r i c e . cpp
s t r u c t Matrice {
i n t m, n ;
3. Coin des enfants : les matrices et les vecteurs vous sont inconnus. Ca nest pas grave. Comprenez
le source quand mme et rattrapez vous avec le TP qui, lui, joue avec des images.

107

8.3. Structures et allocation dynamique

11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59

8. Allocation dynamique

double t ;
};
M a t r i c e c r e e ( i n t m, i n t n ) {
M a t r i c e M;
M.m=m;
M. n=n ;
M. t =new double [mn ] ;
r e t u r n M;
}
void d e t r u i t ( M a t r i c e M) {
d e l e t e [ ] M. t ;
}
M a t r i c e p r o d u i t ( M a t r i c e A, M a t r i c e B ) {
i f (A. n ! =B .m) {
cout << " E r r e u r ! " << endl ;
exit (1);
}
M a t r i c e C= c r e e (A.m, B . n ) ;
f o r ( i n t i = 0 ; i <A.m; i ++)
f o r ( i n t j = 0 ; j <B . n ; j ++) {
/ / C i j =Ai0 B 0 j+Ai1 B 1 j + . . .
C . t [ i +C .m j ] = 0 ;
f o r ( i n t k = 0 ; k<A. n ; k++)
C . t [ i +C .m j ]+=A. t [ i +A.mk ] B . t [ k+B .m j ] ;
}
return C;
}
void a f f i c h e ( s t r i n g s , M a t r i c e M) {
cout << s << " = " << endl ;
f o r ( i n t i = 0 ; i <M.m; i ++) {
f o r ( i n t j = 0 ; j <M. n ; j ++)
cout << M. t [ i +M.m j ] << " " ;
cout << endl ;
}
}
/ / ==================================================
// Utilisateur
i n t main ( )
{
M a t r i c e A= c r e e ( 2 , 3 ) ;
f o r ( i n t i = 0 ; i < 2 ; i ++)
f o r ( i n t j = 0 ; j < 3 ; j ++)
108

8. Allocation dynamique

60
61
62
63
64
65
66
67
68
69
70
71
72
73 }

8.3. Structures et allocation dynamique

A. t [ i +2 j ]= i + j ;
a f f i c h e ( "A" ,A ) ;
M a t r i c e B= c r e e ( 3 , 5 ) ;
f o r ( i n t i = 0 ; i < 3 ; i ++)
f o r ( i n t j = 0 ; j < 5 ; j ++)
B . t [ i +3 j ]= i + j ;
a f f i c h e ( "B" ,B ) ;
M a t r i c e C= p r o d u i t (A, B ) ;
a f f i c h e ( "C" ,C ) ;
d e t r u i t (C ) ;
detruit (B ) ;
d e t r u i t (A ) ;
return 0;

Lutilisateur na maintenant plus qu savoir quil faut allouer et librer les matrices
en appelant des fonctions mais il na pas savoir ce que font ces fonctions. Dans cette
logique, on pourra rajouter des fonctions pour quil nait pas non plus besoin de savoir
comment les lments de la matrice sont mmoriss. Il na alors mme plus besoin de
savoir que les matrices sont des structures qui ont un champ t ! (Nous nous rapprochons vraiment de la programmation objet...) Bref, on rajoutera en gnral :
10
11
12
13
14
15
16

double g e t ( M a t r i c e M, i n t i , i n t j ) {
r e t u r n M. t [ i +M.m j ] ;
}
void s e t ( M a t r i c e M, i n t i , i n t j , double x ) {
M. t [ i +M.m j ]= x ;
}
que lutilisateur pourra appeler ainsi :

51
52
53

f o r ( i n t i = 0 ; i < 2 ; i ++)
f o r ( i n t j = 0 ; j < 3 ; j ++)
s e t (A, i , j , i + j ) ;
et que celui qui programme les matrices pourra aussi utiliser pour lui :

39 void a f f i c h e ( s t r i n g s , M a t r i c e M) {
40
cout << s << " = " << endl ;
41
f o r ( i n t i = 0 ; i <M.m; i ++) {
42
f o r ( i n t j = 0 ; j <M. n ; j ++)
43
cout << g e t (M, i , j ) << " " ;
44
cout << endl ;
45
}
46 }
Attention, il reste facile dans ce contexte :
Doublier dallouer.
Doublier de dsallouer.
De ne pas dsallouer ce quil faut si on fait A=B entre deux matrices. (Cest alors
deux fois la zone alloue initialement pour B qui est dsalloue lorsquon libre
109

8.4. Boucles et continue

8. Allocation dynamique

A.t

...

A.t

...

A.t

...

A.t

...

B.t

...

B.t

...

B.t

...

B.t

...

A=cree(2,3);
B=cree(2,3);

A=B;

detruit(A);

detruit(B);

F IGURE 8.2 Attention au double delete : le code A=B fait pointer deux fois sur la
mme zone mmoire alors quil ny a plus de pointeur sur le tableau du haut (donc
une fuite mmoire puisquil nest plus possible de la librer). Le detruit(B) libre
une zone mmoire qui lavait dj t, avec des consquences fcheuses...
A et B tandis que la mmoire initiale de A ne le sera jamais, comme on peut le
voir sur la Fig. 8.2).
La programmation objet essaiera de faire en sorte quon ne puisse plus faire ces erreurs.
Elle essaiera aussi de faire en sorte que lutilisateur ne puisse plus savoir ce quil na pas
besoin de savoir, de faon rendre vraiment indpendantes la conception des matrices
et leur utilisation.

8.4

Boucles et continue

Nous utiliserons dans le TP linstruction continue qui est bien pratique. Voici ce
quelle fait : lorsquon la rencontre dans une boucle, toute la fin de la boucle est saute
et on passe au tour suivant. Ainsi :
for ( . . . ) {
...
i f (A)
continue ;
...
i f (B)
continue ;
...
}
est quivalent (et remplace avantageusement au niveau clart et mise en page) :
for ( . . . ) {
...
i f ( ! A) {
...
i f ( ! B) {
...
}
}
}
Ceci est rapprocher de lutilisation du return en milieu de fonction pour vacuer les
cas particuliers (section 7.3).
110

8. Allocation dynamique

8.5. TP

F IGURE 8.3 Deux images et diffrents traitements de la deuxime (ngatif, flou, relief,
dformation, contraste et contours).

8.5

TP

Le TP que nous proposons en A.7 est une illustration de cette faon de manipuler
des tableaux bidimensionnels dynamiques travers des structures de donnes. Pour
changer de nos passionnantes matrices, nous travaillerons avec des images (figure 8.3).

8.6

Fiche de rfrence

Fiche de rfrence (1/3)


Dfinition :
int i;
int k,l,m;
Affectation :
i=2;
int i=1;
j=i;
while(i<=100) {
k=l=3;
...
Initialisation :
i=i+1;
int n=5,o=n;
}

Constantes :
for(int i=1;i<=10;i++)
const
int s=12;
...
for(int i=1,j=10;j>i; Porte :
int i;
i=i+2,j=j-3)
// i=j; interdit!
...
int j=2;
for (int i=...)
i=j; // OK!
for (int j=...) {
if (j>1) {
//saute cas i==j
int k=3;
if (i==j)
j=k; // OK!
continue;
}
...
//i=k; interdit!
}
Types :
Variables
int i=3;
Boucles

do {
...
} while(!ok);

111

double x=12.3;
char c=A;
string s="hop";
bool t=true;
float y=1.2f;
unsigned int j=4;
signed char d=-128;
unsigned char d=25;
complex<double>
z(2,3);
Variables globales :
int n;
const int m=12;
void f() {
n=10;
// OK
int i=m; // OK
...
Conversion :
int i=int(x),j;
float x=float(i)/j;
Pile/Tas

8.6. Fiche de rfrence

8. Allocation dynamique

Fiche de rfrence (2/3)


Clavier
Build : F7
Debug : F5
Step over : F10

Step inside : F11


Indent : Ctrl+K,Ctrl+F
Step out : Maj+F11
Gest. tches : Ctrl+Maj+Ech
Fonctions

}
En paramtre (suite) :
...
void f(int* t,int n){
int x=3,y=2;
t[i]=...
swap(x,y);
}
Surcharge :
int hasard(int n);
void alloue(int*& t){
int hasard(int a,
t=new int[n];
int b);
}
double hasard();
2D :
Oprateurs :
int A[2][3];
vect operator+(
A[i][j]=...;
vect A,vect B) {
int A[2][3]=
...
{{1,2,3},{4,5,6}};
}
void
f(int A[2][2]);
...
vect C=A+B;
2D dans 1D :
int A[2*3];
Pile des appels
A[i+2*j]=...;
Itratif/Rcursif

Dfinition :
int plus(int a,int b) {
int c=a+b;
return c;
}

void affiche(int a) {
cout << a << endl;
Taille variable (suite) :
}
Tableaux
int *t,*s,n;
Dclaration :
Dfinition :
int plus(int a,int b);
double x[5],y[5];
Structures
Retour :
for(int i=0;i<5;i++)
struct Point {
int signe(double x) {
y[i]=2*x[i];
double x,y;
if (x<0)
const int n=5;
Color c;
return -1;
int i[n],j[2*n];
};
if (x>0)
Initialisation :
...
return 1;
int t[4]={1,2,3,4};
Point a;
return 0;
string s[2]={"ab","c"};
a.x=2.3; a.y=3.4;
}
Affectation :
a.c=Red;
void afficher(int x,
int s[3]={1,2,3},t[3];
Point b={1,2.5,Blue};
int y) {
for (int i=0;i<3;i++)
if (x<0 || y<0)
t[i]=s[i];
Compilation spare
return;
if (x>=w || y>=h) En paramtre :
#include "vect.h",
y
return;
void init(int t[4]) {
compris dans vect.cpp
DrawPoint(x,y,RED);
for(int i=0;i<4;i++)
}
t[i]=0;
Fonctions : dclarations dans
}
le .h, dfinitions dans le
Appel :
.cpp
int f(int a) { ... }
void init(int t[],
int g() { ... }
int n) {
Types : dfinitions dans le .h
...
for(int i=0;i<n;i++)
int i=f(2),j=g();
Ne dclarer dans le .h que les
t[i]=0;
fonctions utiles.
}
Rfrences :
Taille variable :
void swap(int& a,
#pragma once au dbut du
int* t=new int[n];
int& b){
fichier.
...
int tmp=a;
delete[] t;
Ne pas trop dcouper...
a=b;b=tmp;

112

8. Allocation dynamique

8.6. Fiche de rfrence

Fiche de rfrence (3/3)


Tests
Comparaison :
== != < > <= >=
Ngation : !
Combinaisons : && ||
if (i==0) j=1;
if (i==0) j=1;
else
j=2;
if (i==0) {
j=1;
k=2;
}
bool t=(i==0);
if (t)
j=1;
switch (i) {
case 1:
...;
...;
break;
case 2:
case 3:
...;
break;
default:
...;
}
Entres/Sorties
#include <iostream>
using namespace std;
...
cout <<"I="<<i<<endl;
cin >> i >> j;
Divers
i++;
i--;
i-=2;
j+=3;
j=i%n; // Modulo
#include <cstdlib>
...
i=rand()%n;
x=rand()/
double(RAND_MAX);
#include <ctime>
...
srand((unsigned int)
time(0));

#include <cmath>

double sqrt(double x);


double cos(double x);

double sin(double x);


double acos(double x);
#include <string>
using namespace std;
string s="hop";

char c=s[0];
int l=s.size();

#include <ctime>
s=double(clock())
/CLOCKS_PER_SEC;
#define _USE_MATH_DEFINES
#include <cmath>

double pi=M_PI;

Point a;
a={1,2}; // NON!
#include "vec.cpp"//NON
void f(int t[][]);//NON
int t[2,3]; // NON!
t[i,j]=...; // NON!
int* t;
t[1]=...; // NON!
int* t=new int[2];
int* s=new int[2];
s=t; // On perd s!
delete[] t;
delete[] s; //Dja fait
int *t,s;// s est int
// et non int*
t=new int[n];
s=new int[n];// NON!

Erreurs frquentes
Pas de dfinition de fonction
dans une fonction !
Imagine++
int q=r=4; // NON!
Voir documentation...
if (i=2) // NON!
if i==2 // NON!
Conseils
if (i==2) then // NON!
Nettoyer en quittant.
for (int i=0,i<100,i++)
Erreurs et warnings : cliquer.
// NON!
int f() {...}

int i=f; // NON!

double x=1/3; // NON!

int i,j;
x=i/j; // NON!

x=double(i/j); //NON!

double x[10],y[10];
for (int i=1;i<=10;i++)
y[i]=2*x[i]; //NON
int n=5;

int t[n]; // NON

int f()[4] { // NON!


int t[4];
...
return t; // NON!
}

int t[4]; t=f();

int s[3]={1,2,3},t[3];

t=s; // NON!
int t[2];
t={1,2}; // NON!

struct Point {
double x,y;
} // NON!

113

Indenter.
Ne pas laisser de warning.
Utiliser le debuggeur.
Faire des fonctions.
Tableaux : pas pour transcrire
une formule mathmatique !
Faire des structures.
Faire des fichiers spars.
Le .h doit suffire lutilisateur (qui ne doit pas regarder
le .cpp)
Ne pas abuser du rcursif.
Ne pas oublier delete.
Compiler rgulirement.
Debug/Release : nettoyer les
deux.
#include <cassert>
...
assert(x!=0);
y=1/x;

9. Premiers objets

Chapitre 9
Premiers objets
Nous abordons maintenant notre dernire tape dans la direction dune meilleure organisation des programmes. Tantt nous structurions davantage les instructions (fonctions, fichiers),
tantt nous nous intressions aux donnes (structures, tableaux). Nous allons maintenant penser donnes et instructions simultanment : cest l lide premire des objets, mme sils possdent de nombreux autres aspects 1 . Enfin, nous justifierons lemploi des objets par la notion
d"interface" 2 .

9.1

Philosophie

Runir les instructions en fonctions ou fichiers est une bonne chose. Runir les donnes en tableaux ou structures aussi. Il arrive que les deux soient lis. Cest dailleurs
ce que nous avons constat naturellement dans les exemples des chapitres prcdents,
dans lesquels un fichier regroupait souvent une structure et un certain nombre de fonctions sy rapportant. Cest dans ce cas quil faut faire des objets.
Lide est simple : un objet est un type de donne possdant un certain nombre de
fonctionnalits propres 3 . Ainsi :
Ce ne sont plus les fonctions qui travaillent sur des donnes. Ce sont les
donnes qui possdent des fonctionnalits.
Ces "fonctionnalits" sont souvent appeles les mthodes de lobjet. En pratique, lutilisation dun objet remplacera ce genre dinstructions :
obj a ;
int i=f ( a ) ;

// fonction f () applique a

par :
obj a ;
i n t i =a . f ( ) ;

/ / a p p e l l a mthode f ( ) de a

1. Le plus important tant lhritage, que nous ne verrons pas dans ce cours, prfrant nous consacrer
dautres aspects du C++ plus indispensables et ngligs jusquici...
2. Nous exposerons une faon simple de crer des interfaces. Un programmeur C++ expriment
utilisera plutt de lhritage et des fonctions virtuelles pures, ce qui dpasse largement ce cours !
3. Il arrive mme parfois quun objet regroupe des fonctionnalits sans pour autant stocker la
moindre donne. Nous nutiliserons pas ici cette faon de prsenter les choses, dont le dbutant pourrait
rapidement abuser.

9.2. Exemple simple

9. Premiers objets

Vous lavez compris, il sagit ni plus ni moins de "ranger" les fonctions dans les
objets. Attention, crions tout de suite haut et fort qu
il ne faut pas abuser des objets, surtout lorsquon est dbutant. Les dangers
sont en effet :
de voir des objets l o il ny en na pas. Instructions et donnes ne
sont pas toujours lies.
de mal penser lorganisation des donnes ou des instructions en objets.
Un conseil donc : quand a devient trop compliqu pour vous, abandonnez
les objets.
Ce qui ne veut pas dire quun dbutant ne doit pas faire dobjets. Des petits objets
dans des cas simples sont toujours une bonne ide. Mais seule lexprience permet
de correctement organiser son programme, avec les bons objets, les bonnes fonctions,
etc. Un exemple simple : lorsquune fonction travaille sur deux types de donnes, le
dbutant voudra souvent sacharner en faire malgr tout une mthode de lun des
deux objets, et transformer :
obj1 a ;
obj2 b ;
int i=f (a , b ) ;

// f () applique a et b

en :
obj1 a ;
obj2 b ;
i n t i =a . f ( b ) ;

/ / mthode f ( ) de a a p p l i q u e b
/ / Estc e b i e n l a c h o s e f a i r e ????

Seuls un peu de recul et dexprience permettent de rester simple quand il le faut. Le


premier code tait le plus logique : la fonction f () na souvent rien faire chez a, ni
chez b.

9.2

Exemple simple

On laura compris dans les exemples prcdents, les mthodes des objets sont considres comme faisant partie du type de lobjet, au mme titre que ses champs. Dailleurs,
les champs dun objet sont parfois appels membres de lobjet, et ses mthodes des fonctions membres. Voici ce que cela donne en C++ :
s t r u c t obj {
int x ;
int f ( ) ;
int g( int y ) ;
};
...
i n t main ( ) {
obj a ;
a . x =3;
i n t i =a . f ( ) ;

/ / champ x
/ / mthode f ( )
/ / mthode g ( )

116

9. Premiers objets

9.2. Exemple simple

i n t j =a . g ( 2 ) ;
...
Il y a juste un dtail, mais dimportance : la dfinition de la structure obj ci-dessus ne
fait que dclarer les mthodes. Elles ne sont dfinies nulle part dans le code prcdent.
Pour les dfinir, on fait comme pour les fonctions habituelles, sauf que
pour permettre plusieurs objets davoir les mmes noms de mthodes, on
prfixe leur dfinition par le nom de lobjet suivi de :: a .
a. Ce mcanisme existe aussi pour les fonctions usuelles. Ce sont les espaces de nom, que
nous avons rencontrs et contourns immdiatement avec using namespace std pour ne
pas avoir crire std::cout ...

Voici comment cela scrit :


s t r u c t obj1 {
int x ;
/ / champ x
int f ( ) ;
/ / mthode f ( ) ( d c l a r a t i o n )
int g( int y ) ;
/ / mthode g ( ) ( d c l a r a t i o n )
};
s t r u c t obj2 {
double x ;
/ / champ x
double f ( ) ;
/ / mthode f ( ) ( d c l a r a t i o n )
};
...
i n t obj1 : : f ( ) {
/ / mthode f ( ) de o b j 1 ( d f i n i t i o n )
...
return . . .
}
i n t obj1 : : g ( i n t y ) {
/ / mthode g ( ) de o b j 1 ( d f i n i t i o n )
...
return . . .
}
double o b j 2 : : f ( ) {
/ / mthode f ( ) de o b j 2 ( d f i n i t i o n )
...
return . . .
}
...
i n t main ( ) {
obj1 a ;
obj2 b ;
a . x =3;
/ / l e champ x d e a e s t i n t
b . x = 3 . 5 ; / / c e l u i de b e s t d o u b l e
i n t i =a . f ( ) ;
/ / m t h o d e f ( ) d e a ( donc o b j 1 : : f ( ) )
i n t j =a . g ( 2 ) ;
/ / m t h o d e g ( ) d e a ( donc o b j 1 : : g ( ) )
double y=b . f ( ) ; / / m t h o d e f ( ) d e b ( donc o b j 2 : : f ( ) )
...

117

9.3. Visibilit

9.3

9. Premiers objets

Visibilit

Il y a une rgle que nous navons pas vue sur les espaces de nom mais que nous
pouvons facilement comprendre : quand on est "dans" un espace de nom, on peut
utiliser toutes les variables et fonctions de cet espace sans prciser lespace en question.
Ainsi, ceux qui ont programm cout et endl ont dfini lespace std puis se sont "placs
lintrieur" de cet espace pour programmer sans avoir mettre std:: partout devant
cout, cin, endl et les autres... Cest suivant cette mme logique, que
dans ses mthodes, un objet accde directement ses champs et ses autres
mthodes, cest--dire sans rien mettre devant a !
a. Vous verrez peut-tre parfois traner le mot cl this qui est utile certains moment en
C++ et que les programmeurs venant de Java mettent partout en se trompant dailleurs sur
son type. Vous nen naurez en gnral pas besoin.

Par exemple, la fonction obj1::f() ci-dessus pourrait scrire :


1 i n t obj1 : : f ( )
2
i n t i =g ( 3 ) ;
3
4
i n t j =x+ i ;
5
6
return j ;
7 }
8 ...
9 i n t main ( ) {
10
o b j 1 a1 , a2 ;
11
i n t i 1 =a1 . f
12
13
i n t i 2 =a2 . f
14

{
//
//
//
//

();
();

/ / mthode f ( ) de o b j 1 ( d f i n i t i o n )
mthode g ( ) de l o b j e t dont l a mthode f ( ) e s t
en t r a i n d e s e x c u t e r
champ x d e l o b j e t d o n t l a m t h o d e f ( ) e s t
en t r a i n d e s e x c u t e r

//
//
//
//

C e t a p p e l va
e t a1 . x l i g n e
C e t a p p e l va
e t a2 . x l i g n e

u t i l i s e r a1 . g ( ) l i g n e 2
4
u t i l i s e r l i g n e 2 a2 . g ( )
4

Il est dailleurs normal quun objet accde simplement ses champs depuis ses mthodes, car
si un objet nutilise pas ses champs dans une mthode, cest probablement
quon est en train de ranger dans cet objet une fonction qui na rien voir
avec lui (cf abus mentionn plus haut)

9.4

Exemple des matrices

En programmation, un exemple de source vaut mieux quun long discours. Si jusquici vous naviguiez dans le vague, les choses devraient maintenant sclaircir ! Voil
donc ce que devient notre exemple du chapitre 8 avec des objets :
# i n c l u d e <iostream >
# include <string >
using namespace s t d ;
/ / ==================================================
/ / f o n c t i o n s sur l e s m a t r i c e s
118

9. Premiers objets

9.4. Exemple des matrices

/ / p o u r r a i e n t e t r e d a n s un m a t r i c e . h e t m a t r i c e . cpp
/ / ========= d e c l a r a t i o n s ( d a n s l e . h )
s t r u c t Matrice {
i n t m, n ;
double t ;
void c r e e ( i n t m1, i n t n1 ) ;
void d e t r u i t ( ) ;
double g e t ( i n t i , i n t j ) ;
void s e t ( i n t i , i n t j , double x ) ;
void a f f i c h e ( s t r i n g s ) ;
};
M a t r i c e o p e r a t o r ( M a t r i c e A, M a t r i c e B ) ;
/ / ========= d f i n i t i o n s ( d a n s l e . cpp )
void M a t r i c e : : c r e e ( i n t m1, i n t n1 ) {
/ / N o t e z que l e s p a r a m e t r e s ne s a p p e l l e n t p l u s m e t n
/ / p o u r ne p a s m l a n g e r a v e c l e s champs !
m=m1 ;
n=n1 ;
t =new double [mn ] ;
}
void M a t r i c e : : d e t r u i t ( ) {
delete [ ] t ;
}
double M a t r i c e : : g e t ( i n t i , i n t j ) {
r e t u r n t [ i +m j ] ;
}
void M a t r i c e : : s e t ( i n t i , i n t j , double x ) {
t [ i +m j ]= x ;
}
void M a t r i c e : : a f f i c h e ( s t r i n g s ) {
cout << s << " = " << endl ;
f o r ( i n t i = 0 ; i <m; i ++) {
f o r ( i n t j = 0 ; j <n ; j ++)
cout << g e t ( i , j ) << " " ;
cout << endl ;
}
}
M a t r i c e o p e r a t o r ( M a t r i c e A, M a t r i c e B ) {
i f (A. n ! =B .m) {
cout << " E r r e u r ! " << endl ;
exit (1);
}
119

9.5. Cas des oprateurs

9. Premiers objets

Matrice C;
C . c r e e (A.m, B . n ) ;
f o r ( i n t i = 0 ; i <A.m; i ++)
f o r ( i n t j = 0 ; j <B . n ; j ++) {
/ / C i j =Ai0 B 0 j+Ai1 B 1 j + . . .
C. set ( i , j , 0 ) ;
f o r ( i n t k = 0 ; k<A. n ; k++)
C. set ( i , j ,
C . g e t ( i , j )+A. g e t ( i , k ) B . g e t ( k , j ) ) ;
}
return C;
}
/ / ==================== main ===========
i n t main ( )
{
M a t r i c e A;
A. c r e e ( 2 , 3 ) ;
f o r ( i n t i = 0 ; i < 2 ; i ++)
f o r ( i n t j = 0 ; j < 3 ; j ++)
A. s e t ( i , j , i + j ) ;
A. a f f i c h e ( "A" ) ;
Matrice B ;
B . cree ( 3 , 5 ) ;
f o r ( i n t i = 0 ; i < 3 ; i ++)
f o r ( i n t j = 0 ; j < 5 ; j ++)
B. set ( i , j , i+j ) ;
B . a f f i c h e ( "B" ) ;
M a t r i c e C=AB ;
C . a f f i c h e ( "C" ) ;
C. detruit ( ) ;
B. detruit ( ) ;
A. d e t r u i t ( ) ;
return 0;
}

9.5

Cas des oprateurs

Il est un peu dommage que loprateur ne soit pas dans lobjet Matrice. Pour y
remdier, on adopte la convention suivante :
Soit A un objet. Sil possde une mthode operatorop(objB B), alors
AopB appellera cette mthode pour tout B de type objB.
En clair, le programme :
s t r u c t objA {
...
120

9. Premiers objets

9.5. Cas des oprateurs

};
s t r u c t objB {
...
};
i n t o p e r a t o r +( objA A, objB B ) {
...
}
...
i n t main ( ) {
objA A;
objB B ;
i n t i =A+B ; / / a p p e l l e o p e r a t o r +(A, B )
...
peut aussi scrire :
s t r u c t objA {
...
i n t o p e r a t o r +( objB B ) ;
};
s t r u c t objB {
...
};
i n t objA : : o p e r a t o r +( objB B ) {
...
}
...
i n t main ( ) {
objA A;
objB B ;
i n t i =A+B ; / / a p p e l l e m a i n t e n a n t A . o p e r a t o r +(B )
...
ce qui pour nos matrices donne :
s t r u c t Matrice {
...
Matrice operator ( Matrice B ) ;
};
...
/ / AB a p p e l l e A . o p e r a t o r ( B ) donc t o u s
/ / l e s champs e t f o n c t i o n s u t i l i s s d i r e c t e m e n t
/ / c o n c e r n e n t ce qui t a i t p r f i x prcdemment par A.
Matrice Matrice : : operator ( Matrice B) {
/ / On e s t d a n s l o b j e t A du AB a p p e l
i f ( n ! =B .m) { / / Le n d e A
cout << " E r r e u r ! " << endl ;
exit (1);
}
Matrice C;
C . c r e e (m, B . n ) ;
121

9.6. Interface

9. Premiers objets

f o r ( i n t i = 0 ; i <m; i ++)
f o r ( i n t j = 0 ; j <B . n ; j ++) {
/ / C i j =Ai0 B 0 j+Ai1 B 1 j + . . .
C. set ( i , j , 0 ) ;
f o r ( i n t k = 0 ; k<n ; k++)
/ / g e t ( i , j ) s e r a c e l u i de A
C. set ( i , j ,
C . g e t ( i , j )+ g e t ( i , k ) B . g e t ( k , j ) ) ;
}
return C;
}
Notez aussi que largument de loprateur na en fait pas besoin dtre un objet.
Ainsi pour crire le produit B=A2, il suffira de crer la mthode :
M a t r i c e M a t r i c e : : o p e r a t o r ( double lambda ) {
...
}
...
B=A 2 ; / / A p p e l l e A . o p e r a t o r ( 2 )
Par contre, pour crire B=2A, on ne pourra pas crer :
M a t r i c e double : : o p e r a t o r ( M a t r i c e A) / / IMPOSSIBLE
/ / d o u b l e n e s t p a s un o b j e t !
car cela reviendrait dfinir une mthode pour le type double, qui nest pas un objet 4 . Il faudra simplement se contenter dun oprateur standard, qui, dailleurs, sera
bien inspir dappeler la mthode Matrice::operator(double lambda) si elle est dj
programme :
M a t r i c e o p e r a t o r ( double lambda , M a t r i c e A) {
r e t u r n Alambda ; / / d f i n i prc demm ent , donc r i e n r e p r o g r a m m e r !
}
...
B=2A; / / a p p e l l e o p e r a t o r ( 2 ,A) q u i a p p e l l e s o n t o u r
/ / A. o p e r a t o r (2)
Nous verrons au chapitre suivant dautres oprateurs utiles dans le cas des objets...

9.6

Interface

Si on regarde bien le main() de notre exemple de matrice, on saperoit quil nutilise


plus les champs des Matrice mais seulement leurs mthodes. En fait, seule la partie
s t r u c t Matrice {
void c r e e ( i n t m1, i n t n1 ) ;
void d e t r u i t ( ) ;
double g e t ( i n t i , i n t j ) ;
void s e t ( i n t i , i n t j , double x ) ;
4. et de toute faon nappartient pas au programmeur !

122

9. Premiers objets

9.7. Protection

void a f f i c h e ( s t r i n g s ) ;
Matrice operator ( Matrice B ) ;
};
intresse lutilisateur. Que les dimensions soient dans des champs int m et int n et que
les lments soient dans un champ double t ne le concerne plus : cest le problme de
celui qui programme les matrices. Si ce dernier trouve un autre moyen 5 de stocker un
tableau bidimensionnel de double, libre lui de le faire. En fait
Si lutilisateur des Matrice se conforme aux dclarations des mthodes
ci-dessus, leur concepteur peut les programmer comme il lentend. Il peut
mme les reprogrammer ensuite dune autre faon : les programmes de lutilisateur marcheront toujours ! Cest le concept mme dune interface :
Le concepteur et lutilisateur des objets se mettent daccord sur les mthodes qui doivent exister.
Le concepteur les programme : il implmente a linterface.
Lutilisateur les utilise de son ct.
Le concepteur peut y retoucher sans gner lutilisateur.
En particulier le fichier den-tte de lobjet est le seul qui intresse lutilisateur. Cest lui qui prcise linterface, sans rentrer dans les dtails dimplmentation. Bref, relies uniquement par linterface, utilisation et implmentation deviennent indpendantes b .
a. Il se trouve en gnral face au difficile problme du choix de limplmentation : certaines
faons de stocker les donnes peuvent rendre efficaces certaines mthodes au dtriment de
certaines autres, ou bien consommer plus ou moins de mmoire, etc. Bref, cest lui qui doit
grer les problmes dalgorithmique. Cest aussi en gnral ce qui fait que, pour une mme
interface, un utilisateur prfrera telle ou telle implmentation : le concepteur devra aussi
faire face la concurrence !
b. Ce qui est sr, cest que les deux y gagnent : le concepteur peut amliorer son implmentation sans gner lutilisateur, lutilisateur peut changer pour une implmentation concurrente
sans avoir retoucher son programme.

9.7
9.7.1

Protection
Principe

Tout cela est bien beau, mais les dtails dimplmentation ne sont pas entirement
cachs : la dfinition de la structure dans le fichier den-tte fait apparatre les champs
utiliss pour limplmentation. Du coup, lutilisateur peut-tre tent des les utiliser !
Rien ne lempche en effet des faire des btises :
M a t r i c e A;
A. c r e e ( 3 , 2 ) ;
A.m= 4 ; / / A i e ! L e s a c c s v o n t t r e f a u x !
ou tout simplement de prfrer ne pas sembter en remplaant
5. Et il en existe ! Par exemple pour stocker efficacement des matrices creuses, cest--dire celles dont
la plupart des lments sont nuls. Ou bien, en utilisant des objets implmentant dj des tableaux de faon sre et efficace, comme il en existe dj en C++ standard ou dans des bibliothques complmentaires
disponibles sur le WEB. Etc, etc.

123

9.7. Protection

9. Premiers objets

f o r ( i n t i = 0 ; i < 3 ; i ++)
f o r ( i n t j = 0 ; j < 2 ; j ++)
A. s e t ( i , j , 0 ) ;
par
f o r ( i n t i = 0 ; i < 6 ; i ++)
A. t [ i ] = 0 ; / / H o r r e u r ! Et s i on i m p l m e n t e a u t r e m e n t ?
Dans ce cas, lutilisation nest plus indpendante de limplmentation et on a perdu
une grande partie de lintrt de la programmation objet... Cest ici quintervient la
possibilit dempcher lutilisateur daccder certains champs ou mme certaines
mthodes. Pour cela :
1. Remplacer struct par class : tous les champs et les mthodes deviennent privs : seules les mthodes de lobjet lui-mme ou de tout
autre objet du mme type a peuvent les utiliser.
2. Placer la dclaration public: dans la dfinition de lobjet pour dbuter la zone b partir de laquelle seront dclars les champs et mthodes
publics, cest--dire accessibles tous.
a. Bref, les mthodes de la classe en question !
b. On pourrait nouveau dclarer des passages privs avec private:, puis publics, etc. Il
existe aussi des passages protgs, notion qui dpasse ce cours...

Voici un exemple :
c l a s s obj {
int x , y ;
void a_moi ( ) ;
public :
int z ;
void pour_tous ( ) ;
void une_autre ( o b j A ) ;
};
void o b j : : a_moi ( ) {
x=..;
/ / OK
..=y;
/ / OK
z=..;
/ / OK
}
void o b j : : pour_tous ( ) {
x=..;
/ / OK
a_moi ( ) ; / / OK
}
void o b j : : une_autre ( o b j A) {
x=A. x ;
/ / OK
A. a_moi ( ) ; / / OK
}
...
i n t main ( ) {
o b j A, B ;
A. x = . . ;
/ / NON!
124

9. Premiers objets

A. z = . . ;
A. a_moi ( ) ;
A. pour_tous ( ) ;
A. une_autre ( B ) ;

9.7. Protection

//
//
//
//

OK
NON!
OK
OK

Dans le cas de nos matrices, que nous avions dj bien programmes, il suffit de les
dfinir comme suit :
c l a s s Matrice {
i n t m, n ;
double t ;
public :
void c r e e ( i n t m1, i n t n1 ) ;
void d e t r u i t ( ) ;
double g e t ( i n t i , i n t j ) ;
void s e t ( i n t i , i n t j , double x ) ;
void a f f i c h e ( s t r i n g s ) ;
Matrice operator ( Matrice B ) ;
};
pour empcher une utilisation dpendante de limplmentation.

9.7.2

Structures vs Classes

Notez que, finalement, une structure est une classe o tout est public... Les anciens
programmeurs C pensent souvent tort que les structures du C++ sont les mmes
quen C, cest--dire quelles ne sont pas des objets et quelles nont pas de mthode 6 .

9.7.3

Accesseurs

Les mthodes get () et set () qui permettent daccder en lecture (get) ou en criture
(set) notre classe, sont appeles accesseurs. Maintenant que nos champs sont tous privs, lutilisateur na plus la possibilit de retrouver les dimensions dune matrice. On
rajoutera donc deux accesseurs en lecture vers ces dimensions :
i n t M a t r i c e : : nbLin ( ) {
r e t u r n m;
}
i n t M a t r i c e : : nbCol ( ) {
return n ;
}
...
i n t main ( ) {
...
f o r ( i n t i = 0 ; i <A. nbLin ( ) ; i ++)
f o r ( i n t j = 0 ; j <A. nbCol ( ) ; j ++)
A. s e t ( i , j , 0 ) ;
...
6. sans compter quils les dclarent souvent comme en C avec dinutiles typedef. Mais bon, ceci ne
devrait pas vous concerner !

125

9.8. TP

9. Premiers objets

mais pas en criture, ce qui est cohrent avec le fait que changer m en cours de route
rendrait fausses les fonctions utilisant t [ i+mj] !

F IGURE 9.1 Fractales

9.8

TP

Vous devriez maintenant pouvoir faire le TP en A.8 qui dessine quelques courbes
fractales (figure 9.1) en illustrant le concept dobjet..

9.9

Fiche de rfrence

Fiche de rfrence (1/4)


Boucles
for(int i=1,j=10;j>i;
i=i+2,j=j-3)
do {
...
...
} while(!ok);
for (int i=...)
int i=1;
for (int j=...) {
while(i<=100) {
//saute cas i==j
...
if (i==j)
i=i+1;
continue;
}
...
for(int i=1;i<=10;i++)
}
...

126

Clavier
Build : F7
Debug : F5
Step over : F10
Step inside : F11
Indent : Ctrl+K,Ctrl+F
Step out : Maj+F11
Gest. tches : Ctrl+Maj+Ech

9. Premiers objets

9.9. Fiche de rfrence

Fiche de rfrence (2/4)


Variables
Dfinition :
int i;
int k,l,m;
Affectation :
i=2;
j=i;
k=l=3;
Initialisation :
int n=5,o=n;
Constantes :
const int s=12;
Porte :
int i;
// i=j; interdit!
int j=2;
i=j; // OK!
if (j>1) {
int k=3;
j=k; // OK!
}
//i=k; interdit!
Types :
int i=3;
double x=12.3;
char c=A;
string s="hop";
bool t=true;
float y=1.2f;
unsigned int j=4;
signed char d=-128;
unsigned char d=25;
complex<double>
z(2,3);
Variables globales :
int n;
const int m=12;
void f() {
n=10;
// OK
int i=m; // OK
...
Conversion :
int i=int(x),j;
float x=float(i)/j;
Pile/Tas

Dclaration :
Initialisation :
int plus(int a,int b);
int t[4]={1,2,3,4};
string s[2]={"ab","c"};
Retour :
int signe(double x) { Affectation :
if (x<0)
int s[3]={1,2,3},t[3];
return -1;
for (int i=0;i<3;i++)
if (x>0)
t[i]=s[i];
return 1;
En paramtre :
return 0;
void init(int t[4]) {
}
for(int i=0;i<4;i++)
void afficher(int x,
t[i]=0;
int y) {
}
if (x<0 || y<0)
return;
void init(int t[],
if (x>=w || y>=h)
int n) {
return;
for(int i=0;i<n;i++)
DrawPoint(x,y,RED);
t[i]=0;
}
}
Appel :
int f(int a) { ... }
int g() { ... }
...
int i=f(2),j=g();
Rfrences :
void swap(int& a,
int& b){
int tmp=a;
a=b;b=tmp;
}
...
int x=3,y=2;
swap(x,y);
Surcharge :
int hasard(int n);
int hasard(int a,
int b);
double hasard();

Taille variable :
int* t=new int[n];
...
delete[] t;
En paramtre (suite) :
void f(int* t,int n){
t[i]=...
}
void alloue(int*& t){
t=new int[n];
}
2D :
int A[2][3];
A[i][j]=...;
int A[2][3]=
{{1,2,3},{4,5,6}};
void f(int A[2][2]);

2D dans 1D :
int A[2*3];
Oprateurs :
A[i+2*j]=...;
vect operator+(
vect A,vect B) { Taille variable (suite) :
...
int *t,*s,n;
}
Structures
...
vect C=A+B;
struct Point {
Pile des appels

double x,y;
Color c;

Itratif/Rcursif
};
Fonctions
...
Tableaux
Dfinition :
Point a;
int plus(int a,int b) { Dfinition :
a.x=2.3; a.y=3.4;
int c=a+b;
double x[5],y[5];
a.c=Red;
return c;
for(int i=0;i<5;i++)
Point b={1,2.5,Blue};
}
y[i]=2*x[i];
void affiche(int a) {
Une structure est un objet en const int n=5;
tirement public ( cf obcout << a << endl;
int i[n],j[2*n];
}
jets !)

127

9.9. Fiche de rfrence

9. Premiers objets

Fiche de rfrence (3/4)


Objets
struct obj {
int x;
// champ
int f(); // mthode
int g(int y);
};
int obj::f() {
int i=g(3); // mon g
int j=x+i; // mon x
return j;
}
...
int main() {
obj a;
a.x=3;
int i=a.f();

Compilation spare
#include "vect.h",
compris dans vect.cpp

Pas de dfinition de fonction


dans une fonction !
y int q=r=4; // NON!

Fonctions : dclarations dans


le .h, dfinitions dans le
.cpp

Types : dfinitions dans le .h

if (i=2) // NON!
if i==2 // NON!
if (i==2) then // NON!
for (int i=0,i<100,i++)
// NON!

Ne dclarer dans le .h que les int f() {...}


fonctions utiles.
int i=f; // NON!
#pragma once au dbut du double x=1/3; // NON!
fichier.
int i,j;
x=i/j; // NON!
Ne pas trop dcouper...
x=double(i/j); //NON!
Tests
double x[10],y[10];
Comparaison :
for (int i=1;i<=10;i++)
class obj {
== != < > <= >=
y[i]=2*x[i]; //NON
int x,y;
void a_moi();
Ngation : !
int n=5;
public:
int t[n]; // NON
Combinaisons : && ||
int z;
int f()[4] { // NON!
if (i==0) j=1;
void pour_tous();
int t[4];
void un_autre(obj A); if (i==0) j=1;
...
else
j=2;
};
return t; // NON!
void obj::a_moi() {
}
x=..;
// OK
if (i==0) {
int t[4]; t=f();
..=y;
// OK
j=1;
int s[3]={1,2,3},t[3];
z=..;
// OK
k=2;
t=s; // NON!
}
}
void obj::pour_tous() {
int t[2];
bool t=(i==0);
x=..;
// OK
t={1,2}; // NON!
if (t)
a_moi(); // OK
j=1;
struct Point {
}
double x,y;
void une_autre(obj A) { switch (i) {
}
//
NON!
case 1:
x=A.x;
// OK
...;
Point a;
A.a_moi(); // OK
...;
a={1,2}; // NON!
}
break;
...
#include "vec.cpp"//NON
case 2:
int main() {
void f(int t[][]);//NON
case 3:
obj A,B;
int t[2,3]; // NON!
...;
A.x=..;
//NON
t[i,j]=...; // NON!
break;
A.z=..;
//OK
default:
int* t;
A.a_moi();
//NON
...;
t[1]=...; // NON!
A.pour_tous(); //OK
}
A.une_autre(B); //OK
int* t=new int[2];
int* s=new int[2];
class obj {
Entres/Sorties
s=t; // On perd s!
obj operator+(obj B); #include <iostream>
delete[] t;
};
using namespace std;
delete[] s; //Dja fait
...
...
int main() {
cout <<"I="<<i<<endl; int *t,s;// s est int
// et non int*
obj A,B,C;
cin >> i >> j;
t=new int[n];
C=A+B;
s=new int[n];// NON!
// C=A.operator+(B) Erreurs frquentes

128

9. Premiers objets

9.9. Fiche de rfrence

Fiche de rfrence (4/4)


Divers
i++;
i--;
i-=2;
j+=3;
j=i%n; // Modulo
#include <cstdlib>
...
i=rand()%n;
x=rand()/
double(RAND_MAX);
#include <ctime>
...
srand((unsigned int)
time(0));
#include <cmath>
double sqrt(double x);
double cos(double x);
double sin(double x);
double acos(double x);
#include <string>
using namespace std;

string s="hop";

char c=s[0];

int l=s.size();

#include <ctime>
s=double(clock())
/CLOCKS_PER_SEC;

#define _USE_MATH_DEFINES
#include <cmath>

double pi=M_PI;

Imagine++

Voir documentation...
Conseils
Nettoyer en quittant.
Erreurs et warnings : cliquer.
Indenter.
Ne pas laisser de warning.
Utiliser le debuggeur.
Faire des fonctions.
Tableaux : pas pour transcrire
une formule mathmatique !

129

Faire des structures.


Faire des fichiers spars.
Le .h doit suffire lutilisateur (qui ne doit pas regarder
le .cpp)
Ne pas abuser du rcursif.
Ne pas oublier delete.
Compiler rgulirement.
Debug/Release : nettoyer les
deux.

#include <cassert>
...
assert(x!=0);
y=1/x;
Faire des objets.
Ne pas toujours faire des objets !
Penser interface / implmentation / utilisation.

10. Constructeurs et Destructeurs

Chapitre 10
Constructeurs et Destructeurs
Dans ce long chapitre, nous allons voir comment le C++ offre la possibilit dintervenir
sur ce qui se passe la naissance et la mort dun objet. Ce mcanisme essentiel repose sur la
notion de constructeur et de destructeur. Ces notions sont trs utiles, mme pour le dbutant
qui devra au moins connatre leur forme la plus simple. Nous poursuivrons par un aspect bien
pratique du C++, tant pour lefficacit des programmes que pour la dcouverte de bugs la
compilation : une autre utilisation du const. Enfin, pour les plus avancs, nous expliquerons
aussi comment les problmes de gestion du tas peuvent tre ainsi automatiss.

10.1

Le problme

Avec lapparition des objets, nous avons transform :


s t r u c t point {
int x , y ;
};
...
point a ;
a . x =2; a . y=3;
i =a . x ; j =a . y ;
en :
c l a s s point {
int x , y ;
public :
void g e t ( i n t&X , i n t&Y ) ;
void s e t ( i n t X , i n t Y ) ;
};
...
point a ;
a . set (2 ,3);
a . get ( i , j ) ;
Consquence :
point a = { 2 , 3 } ;

10.2. La solution

10. Constructeurs et Destructeurs

est maintenant impossible. On ne peut remplir les champs privs dun objet, mme
linitialisation, car cela permettrait daccder en criture une partie prive 1 !

10.2

La solution

La solution est la notion de constructeur :


c l a s s point {
int x , y ;
public :
point ( i n t X, i n t Y ) ;
};
p o i n t : : p o i n t ( i n t X , i n t Y) {
x=X ;
y=Y ;
}
...
point a ( 2 , 3 ) ;
Un constructeur est une mthode dont le nom est le nom de la classe ellemme. Il ne retourne rien mais son type de retour nest pas void : il na pas
de type de retour. Le constructeur est appel la cration de lobjet et ses
paramtres sont passs avec la syntaxe ci-dessus. Il est impossible dappeler
un constructeur sur un objet dj cr a .
a. Ce qui explique quil nest pas besoin de lui prciser un type de retour.

Ici, cest le constructeur point :: point(int X,int Y) qui est dfini. Notez bien quil
est impossible dappeler un constructeur sur un objet dj contruit :
p o i n t a ( 1 , 2 ) ; / / OK! V a l e u r s i n i t i a l e s
/ / On ne f a i t p a s comme a p o u r c h a n g e r l e s champs d e a .
a . p o i n t ( 3 , 4 ) ; / / ERREUR!
/ / Mais p l u t t comme a .
a . set (3 ,4);
/ / OK!

10.3

Cas gnral

10.3.1

Constructeur vide

Lorsquun objet est cre sans rien prciser, cest le construteur vide qui est appel,
cest--dire celui sans paramtre. Ainsi, le programme :
c l a s s obj {
public :
obj ( ) ;
1. En ralit, il y a une autre raison, plus profonde et trop difficile expliquer ici, qui fait quen
gnral, ds quon programme des objets, cette faon dinitialiser devient impossible.

132

10. Constructeurs et Destructeurs

10.3. Cas gnral

};
obj : : obj ( ) {
cout << " h e l l o " << endl ;
}
...
obj a ; / / a p p e l l e l e c o n s t r u c t e u r par d f a u t
affiche "hello".
Le constructeur vide obj::obj() est appel chaque fois quon construit
un objet sans prciser de paramtre. Font exception les paramtres des fonctions et leur valeur de retour qui, eux, sont construits comme des recopies
des objets passs en paramtre ou retourns a .
a. Nous allons voir plus loin cette construction par copie.

Ainsi, le programme :
# i n c l u d e <iostream >
using namespace s t d ;
c l a s s obj {
public :
obj ( ) ;
};
obj : : obj ( ) {
cout << " o b j " ;
}
void f ( o b j d ) {
}
obj g ( ) {
obj e ;
cout << 6 << " " ;
return e ;
}
i n t main ( )
{
cout << 0 << " " ;
obj a ;
cout << 1 << " " ;
f o r ( i n t i = 2 ; i <=4; i ++) {
obj b ;
cout << i << " " ;
}
f (a );
cout << 5 << " " ;
a=g ( ) ;
133

10.3. Cas gnral

10. Constructeurs et Destructeurs

return 0;
}
affiche :
0 obj 1 obj 2 obj 3 obj 4 5 obj 6
Bien reprer les deux objets non construits avec obj :: obj () : le paramtre d de f () , copie
de a, et la valeur de retour de g(), copie de e.

10.3.2

Plusieurs constructeurs

Un objet peut avoir plusieurs constructeurs.


c l a s s point {
int x , y ;
public :
point ( i n t X, i n t Y ) ;
point ( i n t V ) ;
};
p o i n t : : p o i n t ( i n t X , i n t Y) {
x=X ;
y=Y ;
}
p o i n t : : p o i n t ( i n t V) {
x=y=V ;
}
...
p o i n t a ( 2 , 3 ) ; / / c o n s t r u i t a v e c p o i n t (X, Y)
point b ( 4 ) ;
/ / c o n s t r u i t a v e c p o i n t (V)
Il faut cependant retenir la chose suivante :
Si on ne dfinit aucun constructeur, tout se passe comme sil ny avait quun
constructeur vide ne faisant rien. Mais attention : ds quon dfinit soi-mme
un constructeur, le constructeur vide nexiste plus, sauf si on le redfinit soimme.
Par exemple, le programme :
c l a s s point {
int x , y ;
};
...
point a ;
a . set (2 ,3);
point b ;

/ / OK

devient, avec un constructeur, un programme qui ne se compile plus :


c l a s s point {
int x , y ;
public :
point ( i n t X, i n t Y ) ;
134

10. Constructeurs et Destructeurs

10.3. Cas gnral

};
p o i n t : : p o i n t ( i n t X , i n t Y) {
x=X ;
y=Y ;
}
...
p o i n t a ( 2 , 3 ) ; / / c o n s t r u i t a v e c p o i n t (X, Y)
point b ;
/ / ERREUR! p o i n t ( ) n e x i s t e p l u s
et il faut alors rajouter un constructeur vide, mme sil ne fait rien :
c l a s s point {
int x , y ;
public :
point ( ) ;
point ( i n t X, i n t Y ) ;
};
point : : point ( ) {
}
p o i n t : : p o i n t ( i n t X , i n t Y) {
x=X ;
y=Y ;
}
...
p o i n t a ( 2 , 3 ) ; / / c o n s t r u i t a v e c p o i n t (X, Y)
point b ;
/ / OK! c o n s t r u i t a v e c p o i n t ( )

10.3.3

Tableaux dobjets

Il nest pas possible de spcifier globalement quel constructeur est appel pour les
lments dun tableau. Cest toujours le constructeur vide qui est appel...
point t [ 3 ] ; / / C o n s t r u i t 3 f o i s avec l e c o n s t r u c t e u r v i d e
/ / s u r c h a c u n d e s l m e n t s du t a b l e a u
p o i n t s=new p o i n t [ n ] ; / / Idem , n f o i s
p o i n t u=new p o i n t ( 1 , 2 ) [ n ] ; / / ERREUR e t HORREUR!
/ / Un e s s a i d e c o n s t r u i r e l e s u [ i ]
/ / avec point ( 1 , 2 ) , qui n e x i s t e pas
Il faudra donc crire :
p o i n t u=new p o i n t [ n ] ;
f o r ( i n t i = 0 ; i <n ; i ++)
u[ i ] . set ( 1 , 2 ) ;
ce qui nest pas vraiment identique car on construit alors les points vide puis on les
affecte.
Par contre, il est possible dcrire :
point t [ 3 ] = { point ( 1 , 2 ) , point ( 2 , 3 ) , point ( 3 , 4 ) } ;
ce qui nest videmment pas faisable pour un tableau de taille variable.
135

10.4. Objets temporaires

10.4

10. Constructeurs et Destructeurs

Objets temporaires

On peut, en appelant soi-mme un constructeur a , construire un objet sans


quil soit rang dans une variable. En fait il sagit dun objet temporaire
sans nom de variable et qui meurt le plus tt possible.
a. Attention, nous avions dj dit quon ne pouvait pas appeler un constructeur dun objet
dj construit. Ici, cest autre chose : on appelle un constructeur sans prciser dobjet !

Ainsi, le programme :
void f ( p o i n t p )
...
}
point g ( ) {
point e ( 1 , 2 ) ;
return e ;
}
...
point a ( 3 , 4 ) ;
f (a );
point b ;
b=g ( ) ;
point c ( 5 , 6 ) ;
b=c ;

/ / pour l e r e t o u r n e r

/ / uniquement pour p o u v o i r a p p e l e r f ( )

/ / on p o u r r a i t a v o i r e n v i e d e f a i r e
/ / a pour m e t t r e b ( 5 , 6 )

peut largement sallger, en ne stockant pas dans des variables les points pour lesquels
ce ntait pas utile :
1
2
3
4
5
6
7
8
9
10
11
12
13

void f ( p o i n t p ) {
...
}
point g ( ) {
return point ( 1 , 2 ) ; / / r e t o u r n e d i r e c t e m e n t
/ / l objet temporaire point (1 ,2)
}
...
f ( p o i n t ( 3 , 4 ) ) ; / / P a s s e d i r e c t e m e n t l o b j . temp . p o i n t ( 3 , 4 )
point b ;
b=g ( ) ;
b= p o i n t ( 5 , 6 ) ;
/ / a f f e c t e directement b l objet
/ / temporaire point (5 ,6)
Attention la ligne 12 : elle est utile quand b existe dj mais bien comprendre quon
construit un point (5,6) temporaire qui est ensuite affect b. On ne remplit pas b
directement avec (5,6) comme on le ferait avec un b. set (5,6) .
Attention aussi lerreur suivante, trs frquente. Il ne faut pas crire
p o i n t p= p o i n t ( 1 , 2 ) ; / / NON! ! ! ! ! ! !
mais plutt
point p ( 1 , 2 ) ;

/ / OUI !
136

10. Constructeurs et Destructeurs

10.5. TP

Lutilit de ces objets temporaires est visible sur un exemple rel :


p o i n t p o i n t : : o p e r a t o r +( p o i n t b ) {
p o i n t c ( x+b . x , y+b . y ) ;
return c ;
}
...
point a ( 1 , 2 ) , b ( 2 , 3 ) ;
c=a+ f ( b ) ;
scrira plutt :
p o i n t p o i n t : : o p e r a t o r +( p o i n t b ) {
r e t u r n p o i n t ( x+b . x , y+b . y ) ;
}
...
c= p o i n t ( 1 , 2 ) + f ( p o i n t ( 2 , 3 ) ) ;

F IGURE 10.1 Jeu de Tron.

10.5

TP

Nous pouvons faire une pause et aller faire le TP que nous proposons en A.9. Il
sagit de programmer le jeu de motos de Tron (figure 10.1).

10.6

Rfrences Constantes

10.6.1

Principe

Lorsquon passe un objet en paramtre une fonction, il est recopi. Cette recopie
est source dinefficacit. Ainsi, dans le programme suivant :
c o n s t i n t N= 1 0 0 0 ;
c l a s s vecteur {
double t [N] ;
...
};
c l a s s matrice {
double t [N] [N] ;
...
137

10.6. Rfrences Constantes

10. Constructeurs et Destructeurs

};
/ / r s o u t AX=B
void s o l v e ( m a t r i c e A, v e c t e u r B , v e c t e u r& X) {
...
}
...
vecteur b , x ;
matrice a ;
...
s o l v e ( a , b , x ) ; / / r s o u t ax=b
les variables A et B de la fonction solve() sont des copies des objets a et b de la fonction
appelante. Notez bien que, pass par rfrence, le paramtre X nest pas une copie car
il sagit juste dun lien vers la variable x.
La recopie de a dans A nest pas une trs bonne chose. La variable a fait dans notre
cas pas moins de 8 millions doctets : les recopier dans A prend du temps ! Mme pour
des objets un peu moins volumineux, si une fonction est appele souvent, cette recopie
peut ralentir le programme. Lorsquune fonction est courte, il nest pas rare non plus
que ce temps de recopie soit suprieur celui pass dans la fonction !
Lide est alors, pour des objets volumineux, de les passer eux-aussi par rfrence,
mme si la fonction na pas les modifier ! Il suffit donc de dfinir la fonction solve()
ainsi :
void s o l v e ( m a t r i c e& A, v e c t e u r& B , v e c t e u r& X) {
...
pour acclrer le programme.
Cependant, cette solution nest pas sans danger. Rien ne garantit en effet que solve
ne modifie pas ses paramtres A et B. Il est donc possible, suivant la faon dont solve
est programme, quen sortie de solve(a,b,x), a et b eux-mmes aient t modifis,
alors que prcdemment ctaient leurs copies A et B qui ltaient. Cest videmment
gnant ! Le C++ offre heureusement la possibilit de demander au compilateur de vrifier
quune variable passe par rfrence nest pas modifie par la fonction. Il suffit de rajouter
const au bon endroit :
void s o l v e ( c o n s t m a t r i c e& A, c o n s t v e c t e u r& B , v e c t e u r& X) {
...
Si quelque part dans solve (ou dans les sous-fonctions appeles par solve !), la variable
A ou la variable B est modifie, alors il y aura erreur de compilation. La rgle est donc :
Lorsquun paramtre obj o dune fonction est de taille importante a , cest
une bonne ide de le remplacer par const obj& o.
a. En ralit, le programme sen trouvera acclr pour la plupart des objets courants.

10.6.2

Mthodes constantes

Considrons le programme suivant :


void g ( i n t& x ) {
cout << x << endl ;
}
138

10. Constructeurs et Destructeurs

10.6. Rfrences Constantes

void f ( c o n s t i n t& y ) {
double z=y ; / / OK ne m o d i f i e p a s y
g(y ) ;
/ / OK?
}
...
i n t a =1;
f (a );
La fonction f () ne modifie pas son paramtre y et tout va bien. Imaginons une deuxime
version de g() :
void g ( i n t& x ) {
x ++;
}
Alors y serait modifie dans f () cause de lappel g(). Le programme ne se compilerait videmment pas... En ralit, la premire version de g() serait refuse elle aussi
car
pour savoir si une sous-fonction modifie ou non un des paramtres dune
fonction, le compilateur ne se base que sur la dclaration de cette sousfonction et non sur sa dfinition complte a .
a. Le C++ nessaie pas de deviner lui-mme si une fonction modifie ses paramtres
puisque la logique est que le programmeur indique lui-mme avec const ce quil veut faire,
et que le compilateur vrifie que le programme est bien cohrent.

Bref, notre premier programme ne se compilerait pas non plus car lappel g(y) avec
const int& y impose que g() soit dclare void g(const int& x). Le bon programme est
donc :
void g ( c o n s t i n t& x ) {
cout << x << endl ;
}
void f ( c o n s t i n t& y ) {
double z=y ; / / OK ne m o d i f i e p a s y
g(y ) ;
/ / OK! Pas b e s o i n d a l l e r r e g a r d e r d a n s g ( )
}
...
i n t a =1;
f (a );
Avec les objets, nous avons besoin dune nouvelle notion. En effet, considrons
maintenant :
void f ( c o n s t o b j& o ) {
o . g ( ) ; / / OK?
}
Il faut indiquer au compilateur si la mthode g() modifie ou non lobjet o. Cela se fait
avec la syntaxe suivante :
c l a s s obj {
...
void g ( ) c o n s t ;
139

10.7. Destructeur

10. Constructeurs et Destructeurs

...
};
void o b j : : g ( ) c o n s t {
...
}
void f ( c o n s t o b j& o ) {
o . g ( ) ; / / OK! Mthode c o n s t a n t e
}
Cela nest finalement pas compliqu :
On prcise quune mthode est constante, cest--dire quelle ne modifie pas
son objet, en plaant const derrire les parenthses de sa dclaration et de
sa dfinition.
On pourrait se demander si toutes ces complications sont bien ncessaires, notre
point de dpart tant juste le passage rapide de paramtres en utilisant les rfrences.
En ralit, placer des const dans les mthodes est une trs bonne chose. Il ne faut pas
le vivre comme une corve de plus, mais comme une faon de prciser sa pense :
"suis-je ou non en train dajouter une mthode qui modifie lobjets ?". Le compilateur
va ensuite vrifier pour nous la cohrence de ce const avec tout le reste. Ceci a deux
effets importants :
Dcouverte de bugs la compilation. (On pensait quun objet ntait pas modifi
et il lest.)
Optimisation du programme 2 .

La fin du chapitre peut tre considre comme difficile. Il est toutefois recommand de la comprendre, mme si la matrise et la mise en application de ce qui sy trouve est laisse aux plus
avancs.

10.7

Destructeur

Lorsquun objet meurt, une autre de ses mthodes est appele : le destructeur.
Le destructeur :
est appel quand lobjet meurt.
porte le nom de la classe prcd de .
comme les constructeurs, na pas de type.
na pas de paramtres (Il ny a donc quun seul destructeur par classe.)
Un exemple sera plus parlant. Rajoutons un destructeur au programme de la section
10.3 :
2. Lorsque le compilateur sait quun objet reste constant pendant une partie du programme, il peut
viter daller le relire chaque fois. Le const est donc une information prcieuse pour la partie optimisation du compilateur.

140

10. Constructeurs et Destructeurs

10.7. Destructeur

# i n c l u d e <iostream >
using namespace s t d ;
c l a s s obj {
public :
obj ( ) ;
~obj ( ) ;
};
obj : : obj ( ) {
cout << " o b j " ;
}
obj : : ~ obj ( ) {
cout << " d e s t " ;
}
void f ( o b j d ) {
}
obj g ( ) {
obj e ;
cout << 6 << " " ;
return e ;
}
i n t main ( )
{
cout << 0 << " " ;
obj a ;
cout << 1 << " " ;
f o r ( i n t i = 2 ; i <=4; i ++) {
obj b ;
cout << i << " " ;
}
f (a );
cout << 5 << " " ;
a=g ( ) ;
return 0;
}
Il affiche maintenant :
0 obj 1 obj 2 dest obj 3 dest obj 4 dest dest 5 obj 6 dest dest dest
Reprez bien quel moment les objets sont dtruits. Constatez aussi quil y a des appels au destructeur pour les objets qui sont construits par copie et pour lesquels nous
navons pas encore parl du constructeur...
141

10.8. Destructeurs et tableaux

10.8

10. Constructeurs et Destructeurs

Destructeurs et tableaux

Le destructeur est appel pour tous les lments du tableau. Ainsi,


1
2
3
4

i f ( a==b ) {
obj t [ 1 0 ] ;
...
}
appellera 10 fois le constructeur vide en ligne 2 et dix fois le destructeur en ligne 4.
Dans le cas dun tableau dynamique, cest au moment du delete [] que les destructeurs
sont appels (avant la dsallocation du tableau !).
i f ( a==b ) {
o b j t =new o b j [ n ] ;
...
delete [ ] t ;
}

/ / n appels obj ()
/ / n appels ~obj ( ) ;

Attention : il est possible dcrire delete t sans les []. Cest une erreur !
Cette syntaxe est rserve une autre utilisation du new/delete. Lutiliser
ici a pour consquence de bien dsallouer le tas, mais doublier dappeler les
destructeurs sur les t[i]
.

10.9

Constructeur de copie

Voyons enfin ce fameux constructeur. Il na rien de mystrieux. Il sagit dun constructeur prenant en paramtre un autre objet, en gnral en rfrence constante.
Le constructeur de copie :
Se dclare : obj::obj(const obj& o);
Est utilis videmment par :
obj a;
obj b(a); // b partir de a
Mais aussi par :
obj a;
obj b=a; // b partir de a, synonyme de b(a)
ne pas confondre avec :
obj a,b;
b=a; // ceci nest pas un constructeur!
Et aussi pour construire les paramtres des fonctions et leur valeur de
retour.
Notre programme exemple est enfin complet. En rajoutant :
o b j : : o b j ( c o n s t o b j& o )
cout << " copy " ;
}

142

10. Constructeurs et Destructeurs

10.10. Affectation

il affiche :
0 o b j 1 o b j 2 d e s t o b j 3 d e s t o b j 4 d e s t copy d e s t 5 o b j 6 copy d e s t
dest dest
Nous avons enfin autant dappels aux constructeurs quau destructeur !
Il reste malgr tout savoir une chose sur ce constructeur, dont nous comprendrons
limportance par la suite :
Lorsquil nest pas programm explicitement, le constructeur par copie recopie tous les champs de lobjet copier dans lobjet construit.
Remarquez aussi que lorsquon dfinit soi-mme un constructeur, le constructeur vide
par dfaut nexiste plus mais le constructeur de copie par dfaut existe toujours !

10.10

Affectation

Il reste en fait une dernire chose quil est possible de reprogrammer pour un objet :
laffectation. Si laffectation nest pas reprogramme, alors elle se fait naturellement par
recopie des champs. Pour la reprogrammer, on a recours loprateur =. Ainsi a=b, se
lit a.operator=(b) si jamais celui-ci existe. Rajoutons donc :
void o b j : : o p e r a t o r =( c o n s t o b j&o ) {
cout << " = " ;
}
notre programme, et il affiche :
0 o b j 1 o b j 2 d e s t o b j 3 d e s t o b j 4 d e s t copy d e s t 5 o b j 6 copy d e s t
= dest dest
On raffine en gnral un peu. Linstruction a=b=c; entre trois entiers marche pour
deux raisons :
Elle se lit a=(b=c);
Linstruction b=c affecte c b et retourne la valeur de c
Pour pouvoir faire la mme chose entre trois objets, on reprogrammera plutt laffectation ainsi :
o b j o b j : : o p e r a t o r =( c o n s t o b j&o ) {
cout << " = " ;
return o ;
}
...
obj a , b , c ;
a=b=c ; / / OK c a r a =( b=c )
ou mme ainsi, ce qui dpasse nos connaissances actuelles, mais que nous prconisons
car cela vite de recopier un objet au moment du return :
c o n s t o b j& o b j : : o p e r a t o r =( c o n s t o b j&o ) {
cout << " = " ;
return o ;
}
143

10.11. Objets avec allocation dynamique

10. Constructeurs et Destructeurs

...
obj a , b , c ;
a=b=c ; / / OK c a r a =( b=c )
Un dernier conseil :
Attention ne pas abuser ! Il nest utile de reprogrammer le constructeur
par copie et loprateur daffectation que lorsquon veut quils fassent autre
chose que leur comportement par dfaut a !
a. Contrairement au constructeur vide, qui, lui, nexiste plus ds quon dfinit un autre
constructeur, et quil est donc en gnral indispensable de reprogrammer, mme pour reproduire son comportement par dfaut

10.11

Objets avec allocation dynamique

Tout ce que nous venons de voir est un peu abstrait. Nous allons enfin dcouvrir
quoi a sert. Considrons le programme suivant :
# i n c l u d e <iostream >
using namespace s t d ;
c l as s vect {
int n;
double t ;
public :
void a l l o u e ( i n t N) ;
void l i b e r e ( ) ;
};
void v e c t : : a l l o u e ( i n t N) {
n=N;
t =new double [ n ] ;
}
void v e c t : : l i b e r e ( ) {
delete [ ] t ;
}
i n t main ( )
{
vect v ;
v . alloue ( 1 0 ) ;
...
v. libere ( ) ;
return 0;
}

144

10. Constructeurs et Destructeurs

10.11.1

10.11. Objets avec allocation dynamique

Construction et destruction

Il apparat videmment que les constructeurs et les destructeurs sont l pour nous
aider :
# i n c l u d e <iostream >
using namespace s t d ;
c l as s vect {
int n;
double t ;
public :
v e c t ( i n t N) ;
~vect ( ) ;
};
v e c t : : v e c t ( i n t N) {
n=N;
t =new double [ n ] ;
}
vect : : ~ vect ( ) {
delete [ ] t ;
}
i n t main ( )
{
vect v ( 1 0 ) ;
...
return 0;
}
Grce aux constructeurs et au destructeur, nous pouvons enfin laisser les
allocations et les dsallocations se faire toutes seules !

10.11.2

Problmes !

Le malheur est que cette faon de faire va nous entraner assez loin pour des dbutants. Nous allons devoir affronter deux types de problmes.
Un problme simple
Puisquil ny a quun seul destructeur pour plusieurs constructeurs, il va falloir faire
attention ce qui se passe dans le destructeur. Rajoutons par exemple un constructeur
vide :
vect : : vect ( ) {
}
alors la destruction dun objet cr vide va vouloir dsallouer un champ t absurde. Il
faudra donc faire, par exemple :
145

10.11. Objets avec allocation dynamique

10. Constructeurs et Destructeurs

vect : : vect ( ) {
n=0;
}
vect : : ~ vect ( ) {
i f (n!=0)
delete [ ] t ;
}

Des problmes compliqus


Le programme suivant ne marche pas :
i n t main ( )
{
v e c t v ( 1 0 ) ,w( 1 0 ) ;
w=v ;
return 0;
}
Pourquoi ? Parce que laffectation par dfaut recopie les champs de v dans ceux de
w. Du coup, v et w se retrouvent avec les mmes champs t ! Non seulement ils iront
utiliser les mmes valeurs, do certainement des rsultats faux, mais en plus une mme
zone du tas va tre dsalloue deux fois, tandis quune autre ne le sera pas 3 !
Il faut alors reprogrammer laffectation, ce qui nest pas trivial. On dcide en gnral de rallouer la mmoire et de recopier les lments du tableau :
c o n s t v e c t& v e c t : : o p e r a t o r =( c o n s t v e c t& v ) {
i f (n!=0)
d e l e t e [ ] t ; / / On s e d e s a l l o u e s i n e c e s s a i r e
n=v . n ;
i f (n!=0) {
t =new double [ n ] ; / / R e a l l o c a t i o n e t r e c o p i e
f o r ( i n t i = 0 ; i <n ; i ++)
t [ i ]= v . t [ i ] ;
}
return v ;
}
Cette version ne marche dailleurs pas si on fait v=v car alors v est dsallou avant
dtre recopi dans lui-mme, ce qui provoque une lecture dans une zone qui vient
dtre dsalloue 4 .

10.11.3

Solution !

Des problmes identiques se posent pour le constructeur de copie... Ceci dit, en


factorisant le travail faire dans quelques petites fonctions prives, la solution nest
3. Ne pas dsallouer provoque videmment des fuites de mmoire. Dsallouer deux fois provoque
dans certains cas une erreur. Cest le cas en mode Debug sous Visual, ce qui aide reprer les bugs !
4. Il suffit de rajouter un test (&v==this) pour reprer ce cas, ce qui nous dpasse un petit peu...

146

10. Constructeurs et Destructeurs

10.11. Objets avec allocation dynamique

pas si complique. Nous vous la soumettons en bloc. Elle peut mme servir de schma
pour la plupart des objets similaires 5 :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

# i n c l u d e <iostream >
using namespace s t d ;
c l a s s vect {
/ / champs
int n;
double t ;
// fonctions prives
void a l l o c ( i n t N) ;
void k i l l ( ) ;
void copy ( c o n s t v e c t& v ) ;
public :
// constructeurs " obligatoires "
vect ( ) ;
v e c t ( c o n s t v e c t& v ) ;
// destructeur
~vect ( ) ;
// affectation
c o n s t v e c t& o p e r a t o r =( c o n s t v e c t& v ) ;
/ / constructeurs supplmentaires
v e c t ( i n t N) ;
};
void v e c t : : a l l o c ( i n t N) {
n=N;
i f (n!=0)
t =new double [ n ] ;
}
void v e c t : : k i l l ( ) {
i f (n!=0)
delete [ ] t ;
}
void v e c t : : copy ( c o n s t v e c t& v ) {
alloc (v.n ) ;
f o r ( i n t i = 0 ; i <n ; i ++) / / OK mme s i n==0
t [ i ]= v . t [ i ] ;
}
vect : : vect ( ) {
alloc (0);
}
5. Ceci nest que le premier pas vers une srie de faon de grer les objets. Doit-on recopier les tableaux ? Les partager en faisant en sorte que le dernier utilisateur soit charg de dsallouer ? Etc, etc.

147

10.11. Objets avec allocation dynamique

45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80

10. Constructeurs et Destructeurs

v e c t : : v e c t ( c o n s t v e c t& v ) {
copy ( v ) ;
}
vect : : ~ vect ( ) {
kill ();
}
c o n s t v e c t& v e c t : : o p e r a t o r =( c o n s t v e c t& v ) {
i f ( t h i s !=&v ) {
kill ();
copy ( v ) ;
}
return v ;
}
v e c t : : v e c t ( i n t N) {
a l l o c (N) ;
}
/ / Pour t e s t e r c o n s t r u c t e u r d e c o p i e
vect f ( vect a ) {
return a ;
}
/ / Pour t e s t e r l e r e s t e
i n t main ( )
{
vect a , b ( 1 0 ) , c ( 1 2 ) ,d ;
a=b ;
a=a ;
a=c ;
a=d ;
a= f ( a ) ;
b= f ( b ) ;
return 0;
}

148

10. Constructeurs et Destructeurs

10.11. Objets avec allocation dynamique

149

10.12. Fiche de rfrence

10.12

10. Constructeurs et Destructeurs

Fiche de rfrence

Fiche de rfrence (1/4)


Affectation :
i=2;
do {
j=i;
...
k=l=3;
} while(!ok);
Initialisation :
int i=1;
int n=5,o=n;
while(i<=100) {
Constantes :
...
const int s=12;
i=i+1;
Porte :
}
int i;
for(int i=1;i<=10;i++)
// i=j; interdit!
...
int j=2;
i=j; // OK!
for(int i=1,j=10;j>i;
if (j>1) {
i=i+2,j=j-3)
int k=3;
...
j=k; // OK!
for (int i=...)
}
for (int j=...) {
//i=k; interdit!
//saute cas i==j

Types :
if (i==j)
int
i=3;
continue;
double
x=12.3;
...
char
c=A;
}
string s="hop";
bool t=true;
Clavier
float y=1.2f;
Build : F7
unsigned int j=4;
Debug : F5
signed char d=-128;
unsigned char d=25;
Step over : F10
complex<double>
z(2,3);
Step inside : F11
Boucles

Indent : Ctrl+K,Ctrl+F
Step out : Maj+F11
Gest. tches : Ctrl+Maj+Ech
Structures
struct Point {
double x,y;
Color c;
};
...
Point a;
a.x=2.3; a.y=3.4;
a.c=Red;
Point b={1,2.5,Blue};

Variables globales :
int n;
const int m=12;
void f() {
n=10;
// OK
int i=m; // OK
...
Conversion :
int i=int(x),j;
float x=float(i)/j;
Pile/Tas

Retour :
int signe(double x) {
if (x<0)
return -1;
if (x>0)
return 1;
return 0;
}
void afficher(int x,
int y) {
if (x<0 || y<0)
return;
if (x>=w || y>=h)
return;
DrawPoint(x,y,RED);
}
Appel :
int f(int a) { ... }
int g() { ... }
...
int i=f(2),j=g();
Rfrences :
void swap(int& a,
int& b){
int tmp=a;
a=b;b=tmp;
}
...
int x=3,y=2;
swap(x,y);
Surcharge :
int hasard(int n);
int hasard(int a,
int b);
double hasard();
Oprateurs :
vect operator+(
vect A,vect B) {
...
}
...
vect C=A+B;

Fonctions

Dfinition :
int plus(int a,int b) {
Une structure est un objet enint c=a+b;

tirement public ( cf obreturn c;


jets !)
}
void affiche(int a) {
Variables
cout << a << endl;
}
Dfinition :
int i;
Dclaration :
int k,l,m;
int plus(int a,int b);

150

Pile des appels


Itratif/Rcursif
Rfrences constantes (pour
un passage rapide) :
void f(const obj& x){
...
}
void g(const obj& x){
f(x); // OK
}

10. Constructeurs et Destructeurs

10.12. Fiche de rfrence

Fiche de rfrence (2/4)


Tableaux

int g(int y);


x.f(); // OK
};
}
Dfinition :
int obj::f() {
Constructeur :
double x[5],y[5];
int i=g(3); // mon g
class point {
for(int i=0;i<5;i++)
int j=x+i; // mon x
int x,y;
y[i]=2*x[i];
return j;
public:
const int n=5;
}
point(int X,int Y);
int i[n],j[2*n];
...
};
int main() {
Initialisation :
point::point(int X,
obj a;
int t[4]={1,2,3,4};
int Y){
a.x=3;
string s[2]={"ab","c"};
x=X;
int i=a.f();
y=Y;
Affectation :
}
int s[3]={1,2,3},t[3]; class obj {
...
int x,y;
for (int i=0;i<3;i++)
point a(2,3);
void a_moi();
t[i]=s[i];
public:
Constructeur vide :
En paramtre :
int z;
obj::obj() {
void init(int t[4]) {
void pour_tous();
...
for(int i=0;i<4;i++)
void un_autre(obj A);
}
t[i]=0;
};
...
}
void obj::a_moi() {
obj a;
void init(int t[],
x=..;
// OK
Objets temporaires :
int n) {
..=y;
// OK
point point::operator+(
for(int i=0;i<n;i++)
z=..;
// OK
point b) {
t[i]=0;
}
return
point(x+b.x,
}
void obj::pour_tous() {
y+b.y);
x=..;
// OK
Taille variable :
}
a_moi(); // OK
int* t=new int[n];
...
}
...
c=point(1,2)
void une_autre(obj A) {
delete[] t;
+f(point(2,3));
x=A.x;
// OK
En paramtre (suite) :
Destructeur :
A.a_moi(); // OK
void f(int* t,int n){
obj::~obj() {
}
t[i]=...
...
...
}
}
int main() {
void alloue(int*& t){
obj A,B;
Constructeur de copie :
t=new int[n];
A.x=..;
//NON
obj::obj(const obj& o){
}
A.z=..;
//OK
...
A.a_moi();
//NON
}
2D :
A.pour_tous(); //OK
Utilis par :
int A[2][3];
A.une_autre(B); //OK
- obj b(a);
A[i][j]=...;
- obj b=a;
int A[2][3]=
class obj {
// mieux que obj b;b=a;
{{1,2,3},{4,5,6}};
obj operator+(obj B);
- paramtres des fonctions
void f(int A[2][2]);
};
- valeur de retour
...
2D dans 1D :
int main() {
Affectation :
int A[2*3];
obj A,B,C;
obj& obj::operator=(
A[i+2*j]=...;
C=A+B;
const obj&o){
Taille variable (suite) :
// C=A.operator+(B)
...
int *t,*s,n;
return *this;
Mthodes constantes :
}
Objets
void obj::f() const{
struct obj {
int x;
// champ
int f(); // mthode

...
}
void g(const obj& x){

151

Objets avec allocation dynamique automatique : cf section 10.11

10.12. Fiche de rfrence

10. Constructeurs et Destructeurs

Fiche de rfrence (3/4)


Compilation spare
#include "vect.h",
compris dans vect.cpp

break;
default:
...;
}

Fonctions : dclarations dans


le .h, dfinitions dans le Entres/Sorties
.cpp
#include <iostream>
using namespace std;
Types : dfinitions dans le .h
...
Ne dclarer dans le .h que les
cout <<"I="<<i<<endl;
fonctions utiles.
cin >> i >> j;
#pragma once au dbut du
Divers
fichier.
i++;
Ne pas trop dcouper...
i--;
Tests
i-=2;
j+=3;
Comparaison :
== != < > <= >=
j=i%n; // Modulo
#include <cstdlib>
Ngation : !
...
Combinaisons : && ||
i=rand()%n;
if (i==0) j=1;
x=rand()/
if (i==0) j=1;
double(RAND_MAX);
else
j=2;
#include <ctime>
...
srand((unsigned int)
if (i==0) {
time(0));
j=1;
k=2;
#include <cmath>
}
double sqrt(double x);
double cos(double x);
bool t=(i==0);
double sin(double x);
if (t)
double acos(double x);
j=1;
#include <string>
switch (i) {
using namespace std;
case 1:
string s="hop";
...;
char c=s[0];
...;
int l=s.size();
break;
#include <ctime>
case 2:
s=double(clock())
case 3:
/CLOCKS_PER_SEC;
...;

152

#define _USE_MATH_DEFINES
#include <cmath>
double pi=M_PI;
Imagine++
Voir documentation...
Conseils
Nettoyer en quittant.
Erreurs et warnings : cliquer.
Indenter.
Ne pas laisser de warning.
Utiliser le debuggeur.
Faire des fonctions.
Tableaux : pas pour transcrire
une formule mathmatique !
Faire des structures.
Faire des fichiers spars.
Le .h doit suffire lutilisateur (qui ne doit pas regarder
le .cpp)
Ne pas abuser du rcursif.
Ne pas oublier delete.
Compiler rgulirement.
Debug/Release : nettoyer les
deux.
#include <cassert>
...
assert(x!=0);
y=1/x;
Faire des objets.
Ne pas toujours faire des objets !
Penser interface / implmentation / utilisation.

10. Constructeurs et Destructeurs

10.13. Devoir crit

Fiche de rfrence (4/4)


int f()[4] { // NON!

int t[4];
...
return t; // NON!
int q=r=4; // NON!
}
if (i=2) // NON!
int t[4]; t=f();

if i==2 // NON!
int s[3]={1,2,3},t[3];
if (i==2) then // NON!
t=s; // NON!
for (int i=0,i<100,i++) int t[2];
// NON!
t={1,2}; // NON!

int f() {...}


struct Point {
int i=f; // NON!
double x,y;
} // NON!
double x=1/3; // NON!
int i,j;
Point a;
x=i/j; // NON!
a={1,2}; // NON!
x=double(i/j); //NON! #include "vec.cpp"//NON
double x[10],y[10];
void f(int t[][]);//NON

for (int i=1;i<=10;i++)


int t[2,3]; // NON!
y[i]=2*x[i]; //NON
t[i,j]=...; // NON!
int n=5;
int* t;

int t[n]; // NON


t[1]=...; // NON!

Erreurs frquentes

Pas de dfinition de fonction


dans une fonction !

10.13

int* t=new int[2];


int* s=new int[2];
s=t; // On perd s!
delete[] t;
delete[] s; //Dja fait
int *t,s;// s est int
// et non int*
t=new int[n];
s=new int[n];// NON!
class point {
int x,y;
public:
...
};
...
point a={2,3}; // NON
point p=point(1,2);//NON
point p(1,2);
// OUI
obj* t=new obj[n];
delete t; // manque []

Devoir crit

Vous pouvez maintenant vous confronter aux devoirs crits proposs en annexe.
Vous avez toutes les connaissances ncessaires...

153

11. En vrac...

Chapitre 11
En vrac...
Nous commenons avec ce chapitre un tour de tout ce qui est utile et mme souvent indispensable et que nous navons pas encore vu : chanes de caractres, fichiers, etc. Encore une
fois, nous ne verrons pas tout de manire exhaustive, mais les fonctions les plus couramment
utilises.

Vous en connaissez suffisamment pour raliser de nombreux programmes. Ce qui


vous manque en gnral ici, cest la pratique. Aprs avoir affront les exercices tout
faits, vous ralisez que, livrs vous-mme, il vous est difficile de vous en sortir. Alors
lancez-vous ! Tentez de programmer lun des projets proposs sur la page WEB du
cours. Vous constaterez rapidement quil vous manque aussi quelques fonctions ou
types de variables usuels. Ce chapitre est l pour y remdier...

11.1

Chanes de caratres

Les chanes de caractres sont les variables stockant des suites de caractres, cest-dire du texte. Nous les avons dj rencontres :
# include <string >
using namespace s t d ;
...
s t r i n g s= " hop " ;
char c=s [ 0 ] ;
i n t l =s . s i z e ( ) ;
Compltons :
1. Les chanes peuvent tres compares. Cest lordre alphabtique qui est videmment utilis :
if
if
if
if
if
if

( s1==s1 )
( s1 ! = s2 )
( s1 <s2 )
( s1 >s2 )
( s1 >=s2 )
( s1 <=s2 )

...
...
...
...
...
...

11.1. Chanes de caratres

11. En vrac...

2. On peut chercher un caractre dans un chane :


s i z e _ t i =s . f i n d ( h ) ;
//
s i z e _ t j =s . f i n d ( h , 3 ) ; / /
//
//

position
position
partir
position

de
de
de
3,

h dans s ?
h dans s
la
en i g n o r a n t s [ 0 ] s [ 2 ]

Attention cest le type size_t 1 qui est utilis et non int. Considrez-le comme
un entier mais pour lequel C++ choisit lui-mme sur combien doctets il faut
le mmoriser...
Si le caractre nest pas trouv, find retourne 1.
3. On peut aussi chercher une sous-chane :
s i z e _ t i =s . f i n d ( " hop " ) ;
/ / o e s t " hop " d a n s s ?
s i z e _ t j =s . f i n d ( " hop " , 3 ) ; / / o e s t " hop " d a n s s p a r t i r d e l a
/ / p o s i t i o n 3?
4. Ajouter une chane la fin dune autre :
s t r i n g a= " comment " ;
s t r i n g b= " a va , l e s amis ? " ;
s t r i n g t x t =a+ " " +b ;
5. Extraire une sous chane :
s t r i n g s1= " un deux t r o i s " ;
s t r i n g s2= s t r i n g ( s1 , 3 , 4 ) ; / / s o u s c h a n e d e l o n g u e u r 4
/ / commenant en s 1 [ 3 ] ( i c i " deux " )
6. Attention : la rcupration dune string au clavier coupe la chane si lon appuie
sur la touche "Entre" mais aussi au premier espace rencontr. Ainsi, si lon tape
"bonjour les amis", le programme :
string s ;
c i n >> s ; / / J u s q u " E n t r e " ou un e s p a c e
rcuprera "bonjour" comme valeur de s (et ventuellement "les" puis "amis" si
lon programme dautres cin>>t...). Pour rcuprer la ligne complte, espaces
compris, il faudra faire un
g e t l i n e ( cin , s ) ;

/ / Toute l a l i g n e jusqu " Entre "

On pourra ventuellement prciser un autre caractre que la fin de ligne :


g e t l i n e ( cin , s , : ) ;

/ / Tout j u s q u un : ( non c o m p r i s )

7. Convertir une string en une chane au format C : le C mmorise ses chanes dans
des tableaux de caractres termins par un 0. Certaines fonctions prennent encore
en paramtre un char ou un const char 2 . Il faudra alors leur passer s . c_str ()
pour convertir une variable s de type string (cf section 11.2.2).
s t r i n g s= " hop hop " ;
c o n s t char t =s . c _ s t r ( ) ;
Vous trouverez dautres fonctions dans laide en ligne de Visual, ou tout simplement
proposes par Visual quand vous utiliserez les string.
1. En ralit, il faut utiliser le type string::size_type.
2. Nous navons pas encore vu le rle de const avec les tableaux.

156

11. En vrac...

11.2

Fichiers

11.2.1

Principe

11.2. Fichiers

Pour lire et crire dans un fichier, on procde exactement comme avec cout et cin.
On cre simplement une variable de type ofstream pour crire dans un fichier, ou de
type ifstream pour lire...
1. Voici comment faire :
# i n c l u d e <fstream >
using namespace s t d ;
...
o f s t r e a m f ( " hop . t x t " ) ;
f << 1 << << 2 . 3 << << " s a l u t " << endl ;
f . close ( ) ;
i f s t r e a m g ( " hop . t x t " ) ;
int i ;
double x ;
string s ;
g >> i >> x >> s ;
g . close ( ) ;
2. Il est bon de vrifier que louverture sest bien passe. Une erreur frquente est
de prciser un mauvais nom de fichier : le fichier nest alors pas ouvert :
i f s t r e a m g ( " . . / data/hop . t x t " ) ;
i f ( ! g . is_open ( ) ) {
cout << " help ! " << endl ;
return 1;
}
On peut aussi avoir besoin de savoir si on est arriv au bout du fichier :
do {
...
} while ( ! ( g . e o f ( ) ) ;
3. Un fichier peut souvrir aprs construction :
ofstream f ;
f . open ( " hop . t x t " ) ;
...
4. Moins frquent, mais trs utile connatre : on peut crire dans un fichier directement la suite doctets en mmoire qui correspond une variable ou un tableau.
Le fichier est alors moins volumineux, lcriture et la lecture plus rapides (pas
besoin de traduire un nombre en une suite de caractres ou linverse !)
double x [ 1 0 ] ;
double y ;
o f s t r e a m f ( " hop . bin " , i o s : : b i n a r y ) ;
f . w r i t e ( ( c o n s t char ) x , 1 0 s i z e o f ( double ) ) ;
157

11.2. Fichiers

11. En vrac...

f . w r i t e ( ( c o n s t char )&y , s i z e o f ( double ) ) ;


f . close ( ) ;
...
i f s t r e a m g ( " hop . bin " , i o s : : b i n a r y ) ;
g . read ( ( char ) x , 1 0 s i z e o f ( double ) ) ;
g . read ( ( c o n s t char )&y , s i z e o f ( double ) ) ;
g . close ( ) ;
Attention ne pas oublier le "mode douverture" ios :: binary

11.2.2

Chanes et fichiers

1. Pour ouvrir un fichier, il faut prciser le nom avec une chane au format C. Do
la conversion...
void l i r e ( s t r i n g nom) {
i f s t r e a m f (nom . c _ s t r ( ) ) ;
...
}

/ / Conversion o b l i g a t o i r e . . .

2. Pour lire une chane avec des espaces, mme chose quavec cin :
getline (g , s ) ;
getline (g , s , : ) ;
3. Enfin, un peu technique mais trs pratique : les stringstream qui sont des chanes
simulant des fichiers virtuels. On les utilise notamment pour convertir une chane
en nombre ou linverse :
# i n c l u d e <sstream >
using namespace s t d ;
s t r i n g s= " 12 " ;
stringstream f ;
int i ;
/ / Chane v e r s e n t i e r
f << s ; / / On c r i t l a c h a n e
f >> i ; / / On r e l i t un e n t i e r ! ( i v a u t 1 2 )
i ++;
/ / Entier vers chane
f . c l e a r ( ) ; / / Ne p a s o u b l i e r s i on a d j u t i l i s f
f << i ; / / On c r i t un e n t i e r
f >> s ; / / On r e l i t une c h a n e ( s v a u t " 1 3 " )

11.2.3

Objets et fichiers

Le grand intrt des << et >> 3 est la possibilit de les redfinir pour des objets !
Cest technique, mais il suffit de recopier ! Voici comment :
3. Ils ont lair un peu pnibles utiliser pour le programmeur habitu au printf et scanf du C. On
voit ici enfin leur puissance !

158

11. En vrac...

11.3. Valeurs par dfaut

s t r u c t point {
int x , y ;
};
ostream& o p e r a t o r < <( ostream& f
f << p . x << << p . y ; / /
//
//
return f ;
}

, c o n s t p o i n t& p ) {
ou q u o i que c e s o i t d a u t r e !
( on a d c i d i c i d c r i r e l e s deux
c o o r d o n n e s s p a r e s p a r un e s p a c e . . . )

i s t r e a m& o p e r a t o r > >( i s t r e a m& f , p o i n t& p ) {


f >> p . x >> p . y ; / / ou q u o i que c e s o i t d a u t r e !
return f ;
}
...
point p ;
c i n >> p ;
cout << p ;
o f s t r e a m f ( " . . / hop . t x t " ) ;
f << p ;
...
i f s t r e a m g ( " . . / hop . t x t " ) ;
g >> p ;

11.3

Valeurs par dfaut

11.3.1

Principe

Souvent utile ! On peut donner des valeurs par dfaut aux derniers paramtres
dune fonction, valeurs quils prendront sils ne sont pas prciss lappel :
void f ( i n t a , i n t b =0 , i n t c =0) {
// ...
}
void g ( ) {
f (12);
/ / Appelle f (12 ,0 ,0);
f (10 ,2);
/ / Appelle f (10 ,2 ,0);
f (1 ,2 ,3); / / Appelle f (1 ,2 ,3);
}
Sil y a dclaration puis dfinition, on ne prcise les valeurs par dfaut que dans la
dclaration :
void f ( i n t a , i n t b = 0 ) ; / / d c l a r a t i o n
void g ( ) {
f (12);
/ / Appelle f (12 ,0);
f (10 ,2); / / Appelle f (10 ,2);
159

11.4. Accesseurs

11. En vrac...

}
void f ( i n t a , i n t b ) { / / ne p a s r e p r c i s e r i c i l e b p a r d f a u t . . .
// ...
}

11.3.2

Utilit

En gnral, on part dune fonction :


int f ( int a , int b) {
...
}
Puis, on veut lui rajouter un comportement spcial dans un certain cas :
i n t f ( i n t a , i n t b , bool s p e c i a l ) {
...
}
Plutt que de transformer tous les anciens appels f (.,.) en f (.,., false ), il suffit de
faire :
i n t f ( i n t a , i n t b , bool s p e c i a l = f a l s e ) {
...
}
pour laisser les anciens appels inchangs, et uniquement appeler f (.,., true) dans les
futurs cas particuliers qui vont se prsenter.

11.3.3

Erreurs frquentes

Voici les erreurs frquentes lorsquon veut utiliser des valeurs par dfaut :
1. Vouloir en donner aux paramtres au milieu de la liste :
void f ( i n t a , i n t b =3 , i n t c ) { / / NON! L e s d e r n i e r s p a r a m t r e s
/ / Pas c e u x du m i l i e u !
}
2. Engendrer des problmes de surcharge :
void f ( i n t a ) {
...
}
void f ( i n t a , i n t b =0) { / / P r o b l m e d e s u r c h a r g e !
...
/ / On ne s a u r a p a s r s o u d r e f ( 1 )
}

11.4

Accesseurs

Voici, en cinq tapes, les points utiles connatre pour faire des accesseurs pratiques
et efficaces.
160

11. En vrac...

11.4.1

11.4. Accesseurs

Rfrence comme type de retour

Voici une erreur souvent rencontre, qui fait hurler ceux qui comprennent ce qui se
passe :
int i ; / / Variable globale
int f () {
return i ;
}
...
f ( ) = 3 ; / / Ne v e u t r i e n d i r e ( p a s p l u s que 2=3)
On ne range pas une valeur dans le retour dune fonction, de mme quon ncrit pas
2=3 ! En fait, si ! Cest possible. Mais uniquement si la fonction retourne une rfrence,
donc un "lien" vers une variable :
int i ; / / Variable globale
i n t& f ( ) {
return i ;
}
...
f ( ) = 3 ; / / OK! Met 3 d a n s i !
Attention : apprendre a un dbutant est trs dangereux. En gnral, il se dpche
de commettre lhorreur suivante :
i n t& f ( ) {
i n t i ; / / Var . l o c a l e
r e t u r n i ; / / r f r e n c e v e r s une v a r i a b l e q u i va m o u r i r !
/ / C EST GRAVE!
}
...
f ( ) = 3 ; / / NON! ! ! Le i n e x i s t e p l u s . Que vat i l s e p a s s e r ? !

11.4.2

Utilisation

Mme si un objet nest pas une variable globale, un champ de cet objet ne meurt
pas en sortant dune de ses mthodes ! On peut, partant du programme :
c l a s s point {
double x [N] ;
public :
void s e t ( i n t i , double v ) ;
};
void p o i n t : : s e t ( i n t i , double v ) {
x [ i ]= v ;
}
...
point p ;
p. set ( 1 , 2 . 3 ) ;
le transformer en :
161

11.4. Accesseurs

11. En vrac...

c l a s s point {
double x [N] ;
public :
double& element ( i n t i ) ;
};
double& p o i n t : : element ( i n t i ) {
return x [ i ] ;
}
...
point p ;
p . element ( 1 ) = 2 . 3 ;

11.4.3 operator()
Etape suivante : ceci devient encore plus utile quand on connat operator() qui permet de redfinir les parenthses :
c l a s s point {
double x [N] ;
public :
double& o p e r a t o r ( ) ( i n t i ) ;
};
double& p o i n t : : o p e r a t o r ( ) ( i n t i ) {
return x [ i ] ;
}
...
point p ;
p ( 1 ) = 2 . 3 ; / / J o l i , non ?
Notez que lon peut passer plusieurs paramtres, ce qui est utile par exemple pour
les matrices :
c l a s s mat {
double x [MN] ;
public :
double& o p e r a t o r ( ) ( i n t i , i n t j ) ;
};
double& mat : : o p e r a t o r ( ) ( i n t i , i n t j ) {
r e t u r n x [ i +M j ] ;
}
...
mat A;
A( 1 , 2 ) = 2 . 3 ;

11.4.4

Surcharge et mthode constante

Nous sommes maintenant face un problme : le programme prcdent ne permet


pas dcrire :
void f ( mat& A) {
162

11. En vrac...

11.4. Accesseurs

A( 1 , 1 ) = 2 ; / / OK
}
void f ( c o n s t mat& A) {
double x=A( 1 , 1 ) ; / / NON! Le c o m p i l a t e u r ne s a i t p a s que
/ / c e t t e l i g n e ne m o d i f i e r a p a s A!
}
car la mthode operator() nest pas constante. Il y a heureusement une solution : programmer deux accesseurs, en profitant du fait quentre une mthode et une mthode
constante, il y a surcharge possible, mme si elles ont les mmes paramtres ! Cela
donne :
c l a s s mat {
double x [MN] ;
public :
/ / Mme nom , mmes p a r a m t r e s , m a i s l une e s t c o n s t !
/ / Donc s u r c h a r g e p o s s i b l e
double& o p e r a t o r ( ) ( i n t i , i n t j ) ;
double o p e r a t o r ( ) ( i n t i , i n t j ) c o n s t ;
};
double mat : : o p e r a t o r ( ) ( i n t i , i n t j ) c o n s t {
r e t u r n x [ i +M j ] ;
}
double& mat : : o p e r a t o r ( ) ( i n t i , i n t j ) {
r e t u r n x [ i +M j ] ;
}
void f ( mat& A) {
A( 1 , 1 ) = 2 ; / / OK, a p p e l l e l e p r e m i e r o p e r a t o r ( )
}
void f ( c o n s t mat& A) {
double x=A( 1 , 1 ) ; / / OK, a p p e l l e l e d e u x i m e
}

11.4.5

"inline"

Principe
Dernire tape : appeler une fonction et rcuprer sa valeur de retour est un mcanisme
complexe, donc long. Appeler A(i, j ) au lieu de faire A.x[i+Mj] est une grande perte de
temps : on passe plus de temps appeler la fonction A.operator()(i , j ) et rcuprer
sa valeur de retour, qu excuter la fonction elle-mme ! Cela pourrait nous conduire
retourner aux structures en oubliant les classes ! 4
Il existe un moyen de supprimer ce mcanisme dappel en faisant en sorte que le
corps de la fonction soit recopi dans le code appelant lui-mme. Pour cela, il faut
dclarer la fonction inline. Par exemple :
4. Les programmeurs C pourraient aussi tre tents de programmer des "macros" (ie. des raccourcis
avec des #define, ce que nous navons pas appris faire). Celles-ci sont moins puissantes que les
inline car elles ne vrifient pas les types, ne permettent pas daccder aux champs privs, etc. Le
programmeur C++ les utilisera avec parcimonie !

163

11.4. Accesseurs

11. En vrac...

i n l i n e double s q r ( double x ) {
r e t u r n xx ;
}
...
double y= s q r ( z 3 ) ;
fait exactement comme si on avait crit y=(z3)(z3), sans quil ny ait dappel de
fonction !
Prcautions
Bien comprendre ce qui suit :
Une fonction inline est recompile chaque ligne qui lappelle, ce qui ralentit la
compilation et augmente la taille du programme !
inline est donc rserv aux fonctions courtes pour lesquelles lappel est pnalisant par rapport au corps de la fonction !
Si la fonction tait dclare dans un .h et dfinie dans un .cpp, il faut maintenant
la mettre entirement dans le .h car lutilisateur de la fonction a besoin de la
dfinition pour remplacer lappel de la fonction par son corps !
Pour pouvoir excuter les fonctions pas pas sous debuggeur, les fonctions inline
sont compiles comme des fonctions normales en mode Debug. Seul le mode Release profitera donc de lacclration.
Cas des mthodes
Dans le cas dune mthode, il faut bien penser la mettre dans le ficher .h si la
classe tait dfinie en plusieurs fichiers. Cest le moment de rvler ce que nous gardions cach :
Il est possible de

DFINIR UNE MTHODE ENTIREMENT DANS LA DFINI -

TION DE LA CLASSE ,

au lieu de seulement ly dclarer puis placer sa dfinition en dehors de celle de la classe. Cependant, ceci nest pas obligatoire a , ralentit la compilation et va lencontre de lide quil faut masquer
le contenu des mthodes lutilisateur dune classe. Cest donc RSERV
AUX PETITES FONCTIONS, en gnral de type inline.
a. Contrairement ce quil faut faire en Java ! Encore une source de mauvaises habitudes
pour le programmeur Java qui se met C++...

Voici ce que cela donne en pratique :


c l a s s mat {
double x [MN] ;
public :
i n l i n e double& o p e r a t o r ( ) ( i n t i , i n t j ) {
r e t u r n x [ i +M j ] ;
}
i n l i n e double o p e r a t o r ( ) ( i n t i , i n t j ) c o n s t {
r e t u r n x [ i +M j ] ;
}
};
164

11. En vrac...

11.5

11.5. Assertions

Assertions

Rappelons lexistence de la fonction assert () vue en 7.6. Il ne faut pas hsiter sen
servir car elle facilite la comprhension du code (rpond la question quels sont les
prsupposs ce point du programme ?) et facilite le diagnostic des erreurs. Sachant
quelle ne cote rien en mode Release (car non compile), il ne faut pas se priver de
lutiliser. Voici par exemple comment rendre srs nos accesseurs :
# include <cassert >
c l a s s mat {
double x [MN] ;
public :
i n l i n e double& o p e r a t o r ( ) ( i n t i , i n t j ) {
a s s e r t ( i >=0 && i <M && j >=0 && j <N) ;
r e t u r n x [ i +M j ] ;
}
i n l i n e double o p e r a t o r ( ) ( i n t i , i n t j ) c o n s t {
a s s e r t ( i >=0 && i <M && j >=0 && j <N) ;
r e t u r n x [ i +M j ] ;
}
};

11.6

Types numrs

Cest une bonne ide de passer par des constantes pour rendre un programme plus
lisible :
c o n s t i n t nord =0 , e s t =1 , sud =2 , o u e s t = 3 ;
void avance ( i n t d i r e c t i o n ) ;
mais il est maladroit de faire ainsi ! Il vaut mieux connatre lexistence des types numrs :
enum Dir { nord , e s t , sud , o u e s t } ;
void avance ( Dir d i r e c t i o n ) ;
Il sagit bien de dfinir un nouveau type, qui, en ralit, masque des entiers. Une prcision : on peut forcer certaines valeurs si besoin. Comme ceci :
enum Code { C10 =200 ,
C11 =231 ,
C12 =240 ,
C13 ,
/ / Vaudra 241
C14 } ; / /
"
242

Voil. Cest tout pour aujourdhui ! Nous continuerons au prochain chapitre. Il est donc
temps de retrouver notre clbre fiche de rfrence...
165

11.6. Types numrs

11. En vrac...

166

11. En vrac...

11.7

11.7. Fiche de rfrence

Fiche de rfrence

Fiche de rfrence (1/4)


Boucles
do {
...
} while(!ok);
int i=1;
while(i<=100) {
...
i=i+1;
}
for(int i=1;i<=10;i++)
...
for(int i=1,j=10;j>i;
i=i+2,j=j-3)
...
for (int i=...)
for (int j=...) {
//saute cas i==j
if (i==j)
continue;
...
}
Clavier
Build : F7
Debug : F5
Step over : F10

Step inside : F11


Indent : Ctrl+K,Ctrl+F
Step out : Maj+F11
Gest. tches : Ctrl+Maj+Ech

Structures
struct Point {
double x,y;
Color c;
};
...
Point a;
a.x=2.3; a.y=3.4;
a.c=Red;
Point b={1,2.5,Blue};
Une structure est un objet entirement public ( cf objets !)
Variables
Dfinition :
int i;
int k,l,m;
Affectation :
i=2;
j=i;
k=l=3;

Initialisation :
int n=5,o=n;
Constantes :
const int s=12;
Porte :
int i;
// i=j; interdit!
int j=2;
i=j; // OK!
if (j>1) {
int k=3;
j=k; // OK!
}
//i=k; interdit!
Types :
int i=3;
double x=12.3;
char c=A;
string s="hop";
bool t=true;
float y=1.2f;
unsigned int j=4;
signed char d=-128;
unsigned char d=25;
complex<double>
z(2,3);
Variables globales :
int n;
const int m=12;
void f() {
n=10;
// OK
int i=m; // OK
...

if (i==0) {
j=1;
k=2;
}
bool t=(i==0);
if (t)
j=1;
switch (i) {
case 1:
...;
...;
break;
case 2:
case 3:
...;
break;
default:
...;
}
Conseils
Nettoyer en quittant.
Erreurs et warnings : cliquer.
Indenter.
Ne pas laisser de warning.
Utiliser le debuggeur.
Faire des fonctions.
Tableaux : pas pour transcrire
une formule mathmatique !
Faire des structures.
Faire des fichiers spars.

Conversion :
int i=int(x),j;
float x=float(i)/j;

Le .h doit suffire lutilisateur (qui ne doit pas regarder


le .cpp)

Pile/Tas

Ne pas abuser du rcursif.

Type numr :
enum Dir{nord,est,
sud,ouest};
void avance(Dir d);

Ne pas oublier delete.

Tests

Compiler rgulirement.
Debug/Release : nettoyer les
deux.

Ngation : !

#include <cassert>
...
assert(x!=0);
y=1/x;

Combinaisons : && ||

Faire des objets.

if (i==0) j=1;

Ne pas toujours faire des objets !

Comparaison :
== != < > <= >=

if (i==0) j=1;
else
j=2;

167

Penser interface / implmentation / utilisation.

11.7. Fiche de rfrence

11. En vrac...

Fiche de rfrence (2/4)


Fonctions
Dfinition :
int plus(int a,int b) {
int c=a+b;

return c;

}
void affiche(int a) {
cout << a << endl;
}
Dclaration :
int plus(int a,int b);
Retour :
int signe(double x) {
if (x<0)

return -1;
if (x>0)
return 1;
return 0;
}
void afficher(int x,
int y) {
if (x<0 || y<0)
return;
if (x>=w || y>=h)
return;
DrawPoint(x,y,RED);
}
Appel :
int f(int a) { ... }
int g() { ... }
...
int i=f(2),j=g();
Rfrences :
void swap(int& a,
int& b){
int tmp=a;
a=b;b=tmp;
}
...
int x=3,y=2;
swap(x,y);
Surcharge :
int hasard(int n);
int hasard(int a,
int b);
double hasard();

}
...
vect C=A+B;
Pile des appels
Itratif/Rcursif
Rfrences constantes (pour
un passage rapide) :
void f(const obj& x){
...
}
void g(const obj& x){
f(x); // OK
}

for (int i=0;i<3;i++)


t[i]=s[i];
En paramtre :
void init(int t[4]) {
for(int i=0;i<4;i++)
t[i]=0;
}
void init(int t[],
int n) {
for(int i=0;i<n;i++)
t[i]=0;
}

Taille variable :
Valeurs par dfaut :
int* t=new int[n];
void f(int a,int b=0);
...
void g() {
delete[] t;
f(12); // f(12,0);
En paramtre (suite) :
f(10,2);// f(10,2);
void f(int* t,int n){
}
t[i]=...
void f(int a,int b) {
}
// ...
}
void alloue(int*& t){
t=new int[n];
Inline (appel rapide) :
}
inline double sqr(
double x)
{ 2D :
return x*x;
int A[2][3];
}
A[i][j]=...;
...
int A[2][3]=
double y=sqr(z-3);
{{1,2,3},{4,5,6}};
void f(int A[2][2]);
Rfrence en retour :
int i; // Var. globale 2D dans 1D :
int& f() {
int A[2*3];
return i;
A[i+2*j]=...;
}
Taille variable (suite) :
...
int *t,*s,n;
f()=3; // i=3!
Compilation spare

Tableaux
Dfinition :

#include "vect.h",
compris dans vect.cpp

double x[5],y[5];
for(int i=0;i<5;i++) Fonctions : dclarations dans
le .h, dfinitions dans le
y[i]=2*x[i];
.cpp
const int n=5;
Types : dfinitions dans le .h
int i[n],j[2*n];
Ne dclarer dans le .h que les
Initialisation :
fonctions utiles.
int t[4]={1,2,3,4};

Oprateurs :
string s[2]={"ab","c"}; #pragma once au dbut du
vect operator+(
fichier.
vect A,vect B) { Affectation :
int s[3]={1,2,3},t[3]; Ne pas trop dcouper...
...

168

11. En vrac...

11.7. Fiche de rfrence

Fiche de rfrence (3/4)


Objets

Constructeur :
struct obj {
class point {
int x;
// champ
int x,y;
int f(); // mthode
public:
int g(int y);
point(int X,int Y);
};
};
int obj::f() {
point::point(int X,
int i=g(3); // mon g
int Y){
int j=x+i; // mon x
x=X;
return j;
y=Y;
}
}
...
...
int main() {
point a(2,3);
obj a;
a.x=3;
Constructeur vide :
int i=a.f();
obj::obj() {
class obj {
int x,y;
void a_moi();
public:
int z;

void pour_tous();
void un_autre(obj A);
};
void obj::a_moi() {
x=..;
// OK
..=y;
// OK
z=..;
// OK
}
void obj::pour_tous() {

x=..;
// OK
a_moi(); // OK
}
void une_autre(obj A) {

x=A.x;
// OK
A.a_moi(); // OK
}
...
int main() {
obj A,B;
A.x=..;
//NON
A.z=..;
//OK
A.a_moi();
//NON
A.pour_tous(); //OK
A.une_autre(B); //OK

...
}
...
obj a;

assert(i>=0 ...);
return x[i+M*j];
}
double operator()
(int i,int j)const{
assert(i>=0 ...);
return x[i+M*j];
}
...
Divers
i++;
i--;
i-=2;
j+=3;
j=i%n; // Modulo
#include <cstdlib>
...
i=rand()%n;
x=rand()/
double(RAND_MAX);

Objets temporaires :
point point::operator+(
point b) {
return point(x+b.x,
y+b.y);
}
...

c=point(1,2)
+f(point(2,3));
Destructeur :
obj::~obj() {
...
}

#include <ctime>
...
srand((unsigned int)
time(0));
#include <cmath>
double sqrt(double x);
double cos(double x);
double sin(double x);
double acos(double x);

#include <string>
using namespace std;
string s="hop";
Constructeur de copie :
char c=s[0];
obj::obj(const obj& o){
int l=s.size();
...
if (s1==s1) ...
}
if (s1!=s2) ...
Utilis par :
if (s1<s2) ...
- obj b(a);
size_t i=s.find(h),
- obj b=a;
j=s.find(h,3);
// mieux que obj b;b=a;
k=s.find("hop");
- paramtres des fonctions
l=s.find("hop",3);
- valeur de retour
a="comment";
Affectation :
b="a va?";
obj&
obj::operator=(
class obj {
txt=a+" "+b;
const obj&o){
obj operator+(obj B);
s1="un deux trois";
...
};
s2=string(s1,3,4);
return *this;
...
getline(cin,s);
}
int main() {
getline(cin,s,:);
obj A,B,C;
Objets avec allocation dynaconst char *t=s.c_str();
C=A+B;
mique automatique : cf sec// C=A.operator+(B)
tion 10.11
#include <ctime>
Mthodes constantes :
Accesseurs :
s=double(clock())
void obj::f() const{
class mat {
/CLOCKS_PER_SEC;
...
double *x;
#define _USE_MATH_DEFINES
}
public:
#include <cmath>
void g(const obj& x){
double& operator()
double pi=M_PI;
x.f(); // OK
(int169
i,int j){

11.7. Fiche de rfrence

11. En vrac...

Fiche de rfrence (4/4)


f.clear();
Point a;
f
<<
i;
a={1,2}; // NON!
#include <iostream>
f
>>
s;
using namespace std;
#include "vec.cpp"//NON
ostream& operator<<(
...
void f(int t[][]);//NON
ostream& f,
cout <<"I="<<i<<endl;
int t[2,3]; // NON!
const point&p) {
cin >> i >> j;
t[i,j]=...; // NON!
f<<p.x<< << p.y;
#include <fstream>
int* t;
return f;
using namespace std;
t[1]=...; // NON!
}
ofstream f("hop.txt");
istream& operator>>(
int* t=new int[2];
f << 1 << << 2.3;
istream& f,point& p){
int* s=new int[2];
f.close();
f>>p.x>>p.y;
s=t; // On perd s!
ifstream g("hop.txt");
return f;
delete[] t;
if (!g.is_open()) {
}
delete[] s; //Dja fait
return 1;
}
Erreurs frquentes
int *t,s;// s est int
int i;
// et non int*
Pas de dfinition de fonction
double x;
t=new int[n];
dans une fonction !
g >> i >> x;
s=new int[n];// NON!
int q=r=4; // NON!
g.close();
class point {
if (i=2) // NON!
do {
int x,y;
if i==2 // NON!
...
public:
if (i==2) then // NON!
} while (!(g.eof());
...
for (int i=0,i<100,i++)
ofstream f;
};
// NON!
f.open("hop.txt");
...
int f() {...}
point a={2,3}; // NON
double x[10],y;
int i=f; // NON!
ofstream f("hop.bin",
double x=1/3; // NON! point p=point(1,2);//NON
ios::binary);
point p(1,2);
// OUI
int i,j;
f.write((const char*)x,
x=i/j; // NON!
obj* t=new obj[n];
10*sizeof(double));
x=double(i/j); //NON!
delete t; // manque []
f.write((const char*)&y,

double
x[10],y[10];
//NON!
sizeof(double));
for (int i=1;i<=10;i++)
void f(int a=2,int b);
f.close();
y[i]=2*x[i]; //NON
ifstream g("hop.bin",
void f(int a,int b=0);
ios::binary); int n=5;
void f(int a);// NON!
int t[n]; // NON
g.read((char*)x,
Ne pas tout mettre inline !
10*sizeof(double)); int f()[4] { // NON!
g.read((const char*)&y,
int t[4];
int f() {
sizeof(double));
...
...
g.close();
return t; // NON!
}
}
f()=3; // HORREUR!
string s;
int
t[4];
t=f();
ifstream f(s.c_str());
int& f() {
int s[3]={1,2,3},t[3];
int i;
#include <sstream>
t=s;
//
NON!
return i;
using namespace std;

int
t[2];
}
stringstream f;
t={1,2}; // NON!
f()=3; // NON!
// Chane vers entier
struct Point {
f << s;
Imagine++
double x,y;
f >> i;
} // NON!
Voir documentation...
// Entier vers chane

Entres/Sorties

170

12. En vrac (suite) ...

Chapitre 12
En vrac (suite) ...
Nous continuons dans ce chapitre un inventaire de diverses choses utiles. Parmi elles, les
structures de donnes de la STL (Standard Template Library) ncessiteront la comprhension
des template. Nous aborderons donc cet aspect intressant du C++

Toujours sous forme dun inventaire, le dbut de ce chapitre sera un peu en vrac,
mais nous nous limiterons toujours quelques aspects pratiques du C++ souvent utiliss donc souvent rencontrs dans les programmes des autres ! La fin du chapitre est
plus utile et plus fondamentale : nous y abordons les template, ou programmation
gnrique.

12.1

Oprateur binaires

Parmi les erreurs classiques, il y a videmment celle qui consiste remplacer


i f ( i ==0)
...
par
i f ( i =0) / / NON! ! !
...
qui range 0 dans i puis considre 0 comme un boolen, cest dire false . Une autre
erreur frquente consiste crire
i f ( i ==0 & j ==2) / / NON! ! !
...
au lieu de
i f ( i ==0 && j ==2)
...
Cette erreur nest possible que parce que & existe. Il sagit de oprateur binaire "ET"
sur des entiers. Il est dfini ainsi : effectuer a&b revient considrer lcriture de a et
de b en binaire puis effectuer un "ET" bit par bit (avec la table 1&1 donne 1 ; 1&0, 0&1
et 0&0 donnent 0). Par exemple : 13&10 vaut 8 car en binaire 1101&1010 vaut 1000.
Il existe ainsi toute une panoplie doprateurs binaires :

12.2. Valeur conditionnelle

symbole utilisation
&
a&b
|
a|b
^
a^b
>>
a>>n

<<

a<<n

~a

12. En vrac (suite) ...

nom
et
ou
ou exclusif
dcalage
droite

rsultat
1&1=1, 0 sinon
0|0=0, 1 sinon
1^0=0^1=1, 0 sinon
dcale les bits de a n fois vers la
droite et comble gauche avec des 0
(les n premiers de droite sont perdus)
dcalage dcale les bits de a n fois vers la
gauche
gauche et comble droite avec des 0
complment ~1=0, ~0=1

exemple
13&10=8
13|10=15
13^10=7
13>>2=3

5<<2=20
~13=14

Remarques :
Ces instructions sont particulirement rapides car simples pour le processeur.
Le fait que a^b existe est aussi source de bugs (il ne sagit pas de la fonction
puissance !)
Le rsultat de ~ dpend en fait du type : si par exemple i est un entier non sign
sur 8 bits valant 13, alors ~i vaut 242, car ~00001101 vaut 11110010.
En pratique, tout cela ne sert pas faire joli ou savant, mais manipuler les nombres
bit par bit. Ainsi, il arrive souvent quon utilise un int pour mmoriser un certain
nombre de proprits en utilisant le moins possible de mmoire avec la convention
que la proprit n est vraie ssi le neme bit de lentier est 1. Un seul entier de 32 bits
pourra par ainsi mmoriser 32 proprits l o il aurait fallu utiliser 32 variables de
type bool. Voici comment on utilise les oprateurs ci-dessus pour manipuler les bits en
question :
i|=(1<<n)
i&=~(1<<n)
i^=(1<<n)
if ( i&(1<<n))

passe 1 le bit n de i
passe 0 le bit n de i
inverse le bit n de i
vrai ssi le bit n de i est 1

Il existe aussi dautres utilisations frquentes des oprateurs binaires, non pour des
raisons de gain de place, mais pour des raisons de rapidit :
(1<<n)
( i>>1)
( i>>n)
( i&255)

12.2

vaut 2n (sinon il faudrait faire int (pow(2.,n)) !)


calcule i/2 rapidement
calcule i/2n rapidement
calcule i%256 rapidement (idem pour toute puissance de 2)

Valeur conditionnelle

Il arrive quon ait choisir entre deux valeurs en fonction du rsultat dun test. Une
construction utile est alors :
( t e s t )? val1 : val2
qui vaut val1 si test est vrai et val2 sinon. Ainsi
i f ( x>y )
maxi=x ;
else
maxi=y ;
172

12. En vrac (suite) ...

12.3. Boucles et break

pourra tre remplac par :


maxi =( x>y ) ? x : y ;
Il ne faut pas abuser de cette construction sous peine de programme illisible !

12.3

Boucles et break

Nous avons dj rencontr la section 8.4 linstruction continue qui saute la fin
dune boucle et passe au tour daprs. Trs utile aussi, la commande break sort de la boucle
en ignorant tout ce quil restait y faire. Ainsi le programme :
bool a r r e t e r = f a l s e ;
f o r ( i n t i = 0 ; i <N && ! a r r e t e r ; i ++) {
A;
i f (B)
a r r e t e r=true ;
else {
C;
i f (D)
a r r e t e r=true ;
else {
E;
}
}
}
devient de faon plus lisible et plus naturelle :
f o r ( i n t i = 0 ; i <N; i ++) {
A;
i f (B)
break ;
C;
i f (D)
break ;
E;
}
Questions rcurrentes des dbutants :
1. break ne sort pas dun if !
if

(...) {
...;
if ( . . . )
break ; / / NON! ! ! Ne s o r t p a s du i f ! ( m a i s v e n t u e l l e m e n t
/ / d un f o r q u i s e r a i t a u t o u r . . . )
...

}
2. break ne sort que de la boucle courante, pas des boucles autour :
173

12.4. Variables statiques

12. En vrac (suite) ...

f o r ( i n t i = 0 ; i <N; i ++) {
...
f o r ( i n t j = 0 ; j <M; j ++) {
...
if ( . . . )
break ; / / t e r m i n e l a b o u c l e en j e t p a s s e donc
/ / en l i g n e 10 ( p a s en l i g n e 1 2 )
...
}
...
}
...
3. break et continue marchent videmment avec while et do ... while de la
mme faon quavec for.

12.4

Variables statiques

Il arrive souvent quon utilise une variable globale pour mmoriser de faon permanente une valeur qui nintresse quune seule fonction :
/ / F o n c t i o n random q u i a p p e l l e s r a n d ( ) t o u t e s e u l e
/ / au p r e m i e r a p p e l . . .
bool f i r s t = t r u e ;
double random ( ) {
if ( first ) {
first=false ;
srand ( ( unsigned i n t ) time ( 0 ) ) ;
}
r e t u r n double ( rand ( ) ) /RAND_MAX;
}
Le danger est alors que tout le reste du programme voie cette variable globale et lutilise ou la confonde avec une autre variable globale. Il est possible de cacher cette variable
dans la fonction grce au mot cl static plac devant la variable :
/ / F o n c t i o n random q u i a p p e l l e s r a n d ( ) t o u t e s e u l e
/ / au p r e m i e r a p p e l . . . a v e c s a v a r i a b l e g l o b a l e
/ / masque l i n t r i e u r
double random ( ) {
s t a t i c bool f i r s t = t r u e ; / / Ne p a s o u b l i e r s t a t i c !
if ( first ) {
first=false ;
srand ( ( unsigned i n t ) time ( 0 ) ) ;
}
r e t u r n double ( rand ( ) ) /RAND_MAX;
}
Attention : il sagit bien dune variable globale et non dune variable locale. Une
variable locale mourrait la sortie de la fonction, ce qui dans lexemple prcdent
donnerait un comportement non dsir !
174

12. En vrac (suite) ...

12.5. const et tableaux

NB : Il est aussi possible de cacher une variable globale dans une classe, toujours
grce static . Nous ne verrons pas comment et renvoyons le lecteur la documentation du C++.

12.5

const et tableaux

Nous avons vu malgr nous const char comme paramtre de certaines fonctions
(ouverture de fichier par exemple). Il nous faut donc lexpliquer : il ne sagit pas dun
pointeur de char qui serait constant mais dun pointeur vers des char qui sont constants ! Il
faut donc retenir que :
plac devant un tableau, const signifie que ce sont les lments du tableau
qui ne peuvent tre modifis.
Cette possibilit de prciser quun tableau ne peut tre modifi est dautant plus importante quun tableau est toujours pass en rfrence : sans le const, on ne pourrait
assurer cette prservation des valeurs :
void f ( i n t t [ 4 ] ) {
...
}
void g ( c o n s t i n t t [ 4 ] ) {
...
}
void h ( c o n s t i n t t , i n t n ) {
...
}
...
int a [ 4 ] ;
f (a );
/ / m o d i f i e p e u t t r e a [ ]
g(a ) ;
/ / ne m o d i f i e p a s a [ ]
h ( a , 4 ) ; / / ne m o d i f i e p a s a [ ]
...

12.6

template

12.6.1

Principe

Considrons la fonction classique pour changer deux variables :


void echange ( i n t& a , i n t& b ) {
i n t tmp ;
tmp=a ;
a=b ;
b=tmp ;
}
175

12.6. template

12. En vrac (suite) ...

...
int i , j ;
...
echange ( i , j ) ;
Si nous devions maintenant changer deux variables de type double, il faudrait rcrire une autre fonction echange(), identique aux dfinitions de type prs. Heureusement, le C++ offre la possibilit de dfinir une fonction avec un type gnrique, un peu
comme un type variable, que le compilateur devra "instancier" au moment de lappel
de la fonction en un type prcis. Cette "programmation gnrique" se fait en dfinissant
un "template" :
/ / Echange deux v a r i a b l e s d e n i m p o r t e q u e l t y p e T
t e m p l a t e <typename T>
void echange ( T& a , T& b ) {
T tmp ;
tmp=a ;
a=b ;
b=tmp ;
}
...
i n t a =2 , b = 3 ;
double x = 2 . 1 , y = 2 . 3 ;
echange ( a , b ) ; / / " i n s t a n c i e " T en i n t
echange ( x , y ) ; / / " i n s t a n c i e " T en d o u b l e
...
Autre exemple :
/ / Maximum d e deux v a r i a b l e s ( a c o n d i t i o n que o p e r a t o r > ( ) e x i s t e
/ / pour l e t y p e T)
t e m p l a t e <typename T>
T maxi ( T a , T b ) {
r e t u r n ( a>b ) ? a : b ;
}
La dclaration typename T prcise le type gnrique. On peut en prciser plusieurs :
/ / C h e r c h e e 1 d a n s l e t a b l e a u t a b 1 e t met
/ / d a n s e 2 l e l e m e n t d e t a b 2 d e meme i n d i c e
/ / R e n v o i e f a l s e s i non t r o u v
t e m p l a t e <typename T1 , typename T2>
bool cherche ( T1 e1 , T2& e2 , c o n s t T1 tab1 , c o n s t T2 tab2 , i n t n ) {
f o r ( i n t i = 0 ; i <n ; i ++)
i f ( t a b 1 [ i ]== e1 ) {
e2= t a b 2 [ i ] ;
return true ;
}
return f a l s e ;
}
...
s t r i n g noms [ 3 ] = { " j e a n " , " p i e r r e " , " paul " } ;
i n t ages [ 3 ] = { 2 1 , 2 5 , 1 5 } ;
176

12. En vrac (suite) ...

12.6. template

...
s t r i n g nm= " p i e r r e " ;
i n t ag ;
i f ( cherche (nm, ag , noms , ages , 3 ) )
cout << nm << " a " << ag << " ans " << endl ;
...

12.6.2

template et fichiers

Il faut bien comprendre que


Le compilateur ne fabrique pas une fonction "magique" qui arrive travailler sur plusieurs types ! Il cre en ralit autant de fonctions quil y a
dutilisations de la fonction gnrique avec des types diffrents (ie. dinstanciations)
Pour ces raisons :
1. Faire des fonctions template ralentit la compilation et augmente la taille des programmes.
2. On ne peut plus mettre la dclaration dans un fichier den-tte et la dfinition
dans un fichier .cpp, car tous les fichiers utilisateurs doivent connatre aussi la
dfinition. Du coup, la rgle est de tout mettre dans le fichier den-tte 1 .

12.6.3

Classes

Il est frquent quune dfinition de classe soit encore plus utile si elle est gnrique.
Cest possible. Mais attention ! Dans le cas des fonctions, cest le compilateur qui dtermine tout seul quels types sont utiliss. Dans le cas des classes, cest lutilisateur qui
doit prciser en permanence avec la syntaxe obj<type> le type utilis :
/ / P a i r e d e deux v a r i a b l e s d e t y p e T
t e m p l a t e <typename T>
class paire {
T x[2];
public :
// constructeurs
paire ( ) ;
p a i r e ( T A, T B ) ;
// accesseurs
T operator ( ) ( i n t i ) const ;
T& o p e r a t o r ( ) ( i n t i ) ;
};
t e m p l a t e <typename T>
p a i r e <T > : : p a i r e ( ) {
1. Ceci est gnant et va lencontre du principe consistant mettre les dclarations dans le .h et
masquer les dfinitions dans le .cpp. Cette remarque a dj t formule pour les fonctions inline. Le
langage prvoit une solution avec le mot cl export, mais les compilateurs actuels nimplmentent pas
encore cette fonctionnalit !

177

12.6. template

12. En vrac (suite) ...

}
t e m p l a t e <typename T>
p a i r e <T > : : p a i r e ( T A, T B ) {
x [ 0 ] =A; x [ 1 ] = B ;
}
t e m p l a t e <typename T>
T p a i r e <T > : : o p e r a t o r ( ) ( i n t i ) c o n s t {
a s s e r t ( i ==0 || i = = 1 ) ;
return x [ i ] ;
}
t e m p l a t e <typename T>
T& p a i r e <T > : : o p e r a t o r ( ) ( i n t i ) {
a s s e r t ( i ==0 || i = = 1 ) ;
return x [ i ] ;
}
...
paire <int > p ( 1 , 2 ) , r ;
i n t i =p ( 1 ) ;
p a i r e <double > q ;
q(1)=2.2;
...
Dans le cas de la classe trs simple ci-dessus, on aura recours aux fonctions inline vues
en 11.4.5 :
/ / P a i r e d e deux v a r i a b l e s d e t y p e T
/ / F o n c t i o n s c o u r t e s e t r a p i d e s en i n l i n e
t e m p l a t e <typename T>
class paire {
T x[2];
public :
// constructeurs
inline paire ( ) { }
i n l i n e p a i r e ( T A, T B ) { x [ 0 ] =A; x [ 1 ] = B ; }
// accesseurs
i n l i n e T operator ( ) ( i n t i ) const {
a s s e r t ( i ==0 || i = = 1 ) ;
return x [ i ] ;
}
i n l i n e T& o p e r a t o r ( ) ( i n t i ) {
a s s e r t ( i ==0 || i = = 1 ) ;
return x [ i ] ;
}
};
Lorsque plusieurs types sont gnriques, on les spare par une virgule :
/ / P a i r e d e deux v a r i a b l e s d e t y p e s d i f f r e n t s
178

12. En vrac (suite) ...

12.6. template

t e m p l a t e <typename S , typename T>


class paire {
public :
/ / Tout en p u b l i c p o u r s i m p l i f i e r
S x;
T y;
// constructeurs
inline paire ( ) { }
i n l i n e p a i r e ( S X , T Y) { x=X ; y=Y ; }
};
...
p a i r e < i n t , double > P ( 1 , 2 . 3 ) ;
p a i r e < s t r i n g , i n t > Q;
Q. x= " p i e r r e " ;
Q. y = 2 5 ;
...
Enfin, on peut aussi rendre gnrique le choix dun entier :
/ / nu p l e t d e v a r i a b l e s d e t y p e T
/ / A t t e n t i o n : c h a q u e n u p l e t <T , N> s e r a un t y p e d i f f r e n t
t e m p l a t e <typename T , i n t N>
c l a s s nuplet {
T x [N] ;
public :
// accesseurs
i n l i n e T operator ( ) ( i n t i ) const {
a s s e r t ( i >=0 && i <N) ;
return x [ i ] ;
}
i n l i n e T& o p e r a t o r ( ) ( i n t i ) {
a s s e r t ( i >=0 && i <N) ;
return x [ i ] ;
}
};
...
nuplet < i n t ,4 > A;
A( 1 ) = 3 ;
nuplet < s t r i n g ,2 > B ;
B(1)= " pierre " ;
...
Les fonctions doivent videmment sadapter :
t e m p l a t e <typename T , i n t N>
T somme( nuplet <T ,N> u ) {
T s=u ( 0 ) ;
f o r ( i n t i = 1 ; i <N; i ++)
s+=u ( i ) ;
return s ;
}
179

12.6. template

12. En vrac (suite) ...

...
nuplet <double ,3 > C ;
...
cout << somme(C) << endl ;
...
Au regard de tout a, on pourrait tre tent de mettre des template partout. Et bien,
non !
Les templates sont dlicats programmer, longs compiler, etc. Il ne faut
pas en abuser ! Il vaut mieux plutt commencer des classes ou des fonctions
sans template. On ne les rajoute que lorsquapparat le besoin de rutiliser
lexistant avec des types diffrents. Et rptons-le encore une fois : le compilateur cre une nouvelle classe ou une nouvelle fonction chaque nouvelle
valeur (instanciation) des types ou des entiers gnriques a .
a. Les nuplets ci-dessus, nont donc rien--voir avec des tableaux de taille variables. Tout
se passe comme si on avait programm des tableaux de taille constante pour plusieurs valeurs
de la taille.

12.6.4

STL

Les template sont dlicats programmer, mais pas utiliser. Le C++ offre un certain nombre de fonctions et de classes utilisant les template. Cet ensemble est communment dsign sous le nom de STL (Standard Template Library). Vous en trouverez
la documentation complte sous Visual ou dfaut sur Internet. Nous exposons cidessous quelques exemples qui devraient pouvoir servir de point de dpart et faciliter
la comprhension de la documentation.
Des fonctions simples comme min et max sont dfinies de faon gnrique :
i n t i =max ( 1 , 3 ) ;
double x=min ( 1 . 2 , 3 . 4 ) ;
Attention : une erreur classique consiste appeler max(1,2.3) : le compilateur linterprte comme le max dun int et dun double ce qui provoque une erreur ! Il faut taper
max(1.,2.3).
Les complexes sont eux-aussi gnriques, laissant variable le choix du type de leurs
parties relle et imaginaire :
# i n c l u d e <complex>
using namespace s t d ;
...
complex<double > z1 ( 1 . 1 , 3 . 4 ) , z2 ( 1 , 0 ) , z3 ;
z3=z1+z2 ;
cout << z3 << endl ;
double a=z3 . r e a l ( ) , b=z3 . imag ( ) ;
double m=abs ( z3 ) ;
/ / module
double th=arg ( z3 ) ; / / argument
Les couples sont aussi offerts par la STL :
p a i r < i n t , s t r i n g > P ( 2 , " hop " ) ;
P . f i r s t =3;
P . second= " hop " ;
180

12. En vrac (suite) ...

12.6. template

Enfin, un certain nombre de structures de donnes sont fournies et sutilisent suivant


un mme schma. Voyons lexemple des listes :
# include < l i s t >
using namespace s t d ;
...
l i s t <int > l ;
l . p u sh _ f ro n t ( 2 ) ;
l . p u sh _ f ro n t ( 3 ) ;
l . push_back ( 4 ) ;
l . p u sh _ f ro n t ( 5 ) ;
l . p u sh _ f ro n t ( 2 ) ;

//
//
//
//
//
//

l =[]
l =[2]
l =[3 ,2]
l =[3 ,2 ,4]
l =[5 ,3 ,2 ,4]
l =[2 ,5 ,3 ,2 ,4]

Pour dsigner un emplacement dans une liste, on utilise un itrateur. Pour dsigner un
emplacement en lecture seulement, on utilise un itrateur constant. Le sert ensuite
accder llment situ lemplacement dsign par litrateur. Seule difficult : le
type de ces itrateurs est un peu compliqu taper 2 :
l i s t <int > : : c o n s t _ i t e r a t o r i t ;
i t = l . begin ( ) ; / / P o i n t e v e r s l e d b u t d e l a l i s t e
cout << i t << endl ; / / a f f i c h e 2
i t = l . f i n d ( 3 ) ; / / P o i n t e v e r s l e n d r o i t ou s e t r o u v e
/ / l e premier 3
i f ( i t ! = l . end ( ) )
cout << " 3 e s t dans l a l i s t e " << endl ;
l i s t <int > : : i t e r a t o r i t 2 ;
i t 2 = l . f i n d ( 3 ) ; / / P o i n t e v e r s l e n d r o i t ou s e t r o u v e
/ / l e premier 3
i t =6; / / maintenant l =[2 ,5 ,6 ,2 ,4]
Les itrateurs servent galement parcourir les listes (do leur nom !) :
/ / P a r c o u r t e t a f f i c h e une l i s t e
t e m p l a t e <typename T>
void a f f i c h e ( l i s t <T> l ) {
cout << " [ " ;
f o r ( l i s t <T > : : c o n s t _ i t e r a t o r i t = l . begin ( ) ; i t ! = l . end ( ) ; i t ++)
cout << i t << ;
cout << ] << endl ;
}
/ / R e m p l a c e a p a r b d a n s une l i s t e
t e m p l a t e <typename T>
void remplace ( l i s t <T>& l , T a , T b ) {
f o r ( l i s t <T > : : i t e r a t o r i t = l . begin ( ) ; i t ! = l . end ( ) ; i t ++)
i f ( i t ==a )
i t =b ;
}
...
affiche ( l ) ;
2. Nous navons pas vu comment dfinir de nouveaux types cachs dans des classes ! Cest ce qui est
fait ici...

181

12.7. Fiche de rfrence

12. En vrac (suite) ...

remplace ( l , 2 , 1 ) ; / / m a i n t e n a n t l = [ 1 , 5 , 3 , 1 , 4 ]
...
Enfin, on peut appeler des algorithmes comme le tri de la liste :
l . sort ( ) ;
affiche ( l ) ;
Sur le mme principe que les listes, vous trouverez dans la STL :
Les piles ou stack (Last In First Out).
Les files ou queue (First In First Out).
Les ensembles ou set (pas deux fois le mme lment).
Les vecteurs ou vector (tableaux de taille variable).
Les tas ou heap (arbres binaires de recherche).
Les tables ou map (table de correspondance cl/valeur).
Et quelques autres encore...

12.7

Fiche de rfrence

Fiche de rfrence (1/6)


Boucles

if (t[i]==s){
// quitte boucle
break;
}
...

do {
...
} while(!ok);
int i=1;
while(i<=100) {
...
i=i+1;
}

}
Clavier
Build : F7

Debug : F5
for(int i=1;i<=10;i++)
Step over : F10
...
for(int i=1,j=10;j>i; Step inside : F11
i=i+2,j=j-3)
Indent : Ctrl+K,Ctrl+F
...
Step out : Maj+F11
for (int i=...)
Gest. tches : Ctrl+Maj+Ech
for (int j=...) {
//saute cas i==j
Tests
if (i==j)
Comparaison :
continue;
== != < > <= >=
...
Ngation : !
}
for (int i=...) {
...

if (i==0) j=1;
else
j=2;
if (i==0) {
j=1;
k=2;
}
bool t=(i==0);
if (t)
j=1;

Combinaisons : && ||

switch (i) {
case 1:
...;
...;
break;
case 2:
case 3:
...;
break;
default:
...;
}

if (i==0) j=1;

mx=(x>y)?x:y;

182

12. En vrac (suite) ...

12.7. Fiche de rfrence

Fiche de rfrence (2/6)


Fonctions
Itratif/Rcursif

Rfrences constantes (pour


Dfinition :
un passage rapide) :
int plus(int a,int b) {

void f(const obj& x){


int c=a+b;
...
return c;
}

}
void g(const obj& x){
void affiche(int a) {
f(x); // OK
cout << a << endl;
}
}
Valeurs par dfaut :
Dclaration :
void f(int a,int b=0);
int plus(int a,int b);
void g() {
Retour :
f(12); // f(12,0);
int signe(double x) {
f(10,2);// f(10,2);
if (x<0)
}
return -1;
void f(int a,int b) {
if (x>0)

// ...
return 1;
}
return 0;

Inline (appel rapide) :


}
inline
double sqr(
void afficher(int x,
double x) {
int y) {
return
x
x;
if (x<0 || y<0)
*
}
return;
...
if (x>=w || y>=h)
double y=sqr(z-3);
return;
DrawPoint(x,y,RED); Rfrence en retour :
}
int i; // Var. globale
int& f() {
Appel :
return i;
int f(int a) { ... }

}
int g() { ... }
...
...
f()=3; // i=3!
int i=f(2),j=g();
Rfrences :
Structures
void swap(int& a,
struct Point {
int& b){
double x,y;
int tmp=a;
Color c;

a=b;b=tmp;
};
}
...
...
Point a;
int x=3,y=2;
a.x=2.3; a.y=3.4;

swap(x,y);
a.c=Red;
Surcharge :
Point b={1,2.5,Blue};
int hasard(int n);
Une structure est un objet enint hasard(int a,
tirement public ( cf obint b);
jets !)
double hasard();

Variables
Oprateurs :
vect operator+(
Dfinition :
vect A,vect B) {
int i;
...
int k,l,m;
}
Affectation :
...
i=2;
vect C=A+B;
j=i;
k=l=3;
Pile des appels

183

Initialisation :
int n=5,o=n;
Constantes :
const int s=12;
Porte :
int i;
// i=j; interdit!
int j=2;
i=j; // OK!
if (j>1) {
int k=3;
j=k; // OK!
}
//i=k; interdit!
Types :
int i=3;
double x=12.3;
char c=A;
string s="hop";
bool t=true;
float y=1.2f;
unsigned int j=4;
signed char d=-128;
unsigned char d=25;
complex<double>
z(2,3);
Variables globales :
int n;
const int m=12;
void f() {
n=10;
// OK
int i=m; // OK
...
Conversion :
int i=int(x),j;
float x=float(i)/j;
Pile/Tas
Type numr :
enum Dir{nord,est,
sud,ouest};
void avance(Dir d);
Variables statiques :
int f() {
static bool first=true;
if (first) {
first=false;
...
}
...
}

12.7. Fiche de rfrence

12. En vrac (suite) ...

Fiche de rfrence (3/6)


Objets
struct obj {
int x;
// champ
int f(); // mthode
int g(int y);
};
int obj::f() {
int i=g(3); // mon g
int j=x+i; // mon x
return j;
}
...
int main() {
obj a;
a.x=3;
int i=a.f();
class obj {

int x,y;
void a_moi();
public:
int z;
void pour_tous();
void un_autre(obj A);
};
void obj::a_moi() {
x=..;
// OK
..=y;
// OK
z=..;
// OK
}
void obj::pour_tous() {
x=..;
// OK
a_moi(); // OK

}
void une_autre(obj A) {
x=A.x;
// OK
A.a_moi(); // OK

}
...
int main() {
obj A,B;
A.x=..;
//NON
A.z=..;
//OK
A.a_moi();
//NON
A.pour_tous(); //OK
A.une_autre(B); //OK
class obj {
obj operator+(obj B);
};
...
int main() {
obj A,B,C;
C=A+B;
// C=A.operator+(B)
Mthodes constantes :
void obj::f() const{
...

void g(const obj& x){


double *x;
x.f(); // OK
public:
}
double& operator()
(int i,int j){
Constructeur :
assert(i>=0 ...);
class point {
return x[i+M*j];
int x,y;
}
public:
double operator()
point(int X,int Y);
(int i,int j)const{
};
assert(i>=0 ...);
point::point(int X,
return x[i+M*j];
int Y){
}
x=X;
...
y=Y;
}
Compilation spare
...
#include "vect.h",
y
point a(2,3);
compris
dans
vect.cpp
Constructeur vide :
Fonctions : dclarations dans
obj::obj() {
le .h, dfinitions dans le
...
.cpp
}
...
Types : dfinitions dans le .h
obj a;
Ne dclarer dans le .h que les
Objets temporaires :
fonctions utiles.
point point::operator+(
point b) { #pragma once au dbut du
fichier.
return point(x+b.x,
y+b.y); Ne pas trop dcouper...
}
...
STL
c=point(1,2)
min,max,...
+f(point(2,3));
complex<double> z;
Destructeur :
obj::~obj() {
pair<int,string> p;
...
p.first=2;
}
p.second="hop";
Constructeur de copie :
#include<list>
obj::obj(const obj& o){
using namespace std;
...
...
}
list<int> l;
Utilis par :
l.push_front(1);
- obj b(a);
...
- obj b=a;
if (l.find(3)!=l.end())
// mieux que obj b;b=a;
...
- paramtres des fonctions
list<int>::const_iterator
- valeur de retour
it;
Affectation :
for
(it=l.begin();
obj& obj::operator=(
it!=l.end();it++)
const obj&o){
s+=
*it;
...
return *this;
list<int>::iterator it
}
for (it=l.begin();
it!=l.end();it++)
Objets avec allocation dynaif
(
*it==2)
mique automatique : cf secit=4;
*
tion 10.11
stack, queue, heap,
Accesseurs :
map, set, vector...
class mat {

184

12. En vrac (suite) ...

12.7. Fiche de rfrence

Fiche de rfrence (4/6)


Template

..
};
...
hop<3> A;
...

f << s;
f >> i;
// Entier vers chane
f.clear();
f << i;
f >> s;

Fonctions :
// A mettre dans LE
// fichier qui lutilise
// ou dans un .h
template <typename T> Entres/Sorties
ostream& operator<<(
T maxi(T a,T b) {
#include <iostream>
ostream& f,
...
using namespace std;
const point&p) {
}
...
f<<p.x<< << p.y;
...
cout <<"I="<<i<<endl;
return f;
// Le type est trouv
cin >> i >> j;
}
// tout seul!
#include <fstream>
istream& operator>>(
maxi(1,2);
//int
using namespace std;
istream& f,point& p){
maxi(.2,.3); //double
ofstream f("hop.txt");
f>>p.x>>p.y;
maxi("a","c");//string
f << 1 << << 2.3;
return f;
f.close();
}
Objets :
ifstream g("hop.txt");
template <typename T>
Conseils
if (!g.is_open()) {
class paire {
return 1;
Nettoyer en quittant.
T x[2];
}
public:
Erreurs et warnings : cliquer.
int i;
paire() {}
double x;
Indenter.
paire(T a,T b) {
g >> i >> x;
Ne pas laisser de warning.
x[0]=a;x[1]=b;
g.close();
}
Utiliser le debuggeur.
do {
T add()const;
Faire des fonctions.
...
};
} while (!(g.eof());
Tableaux : pas pour transcrire
...
une formule mathmatique !

ofstream
f;
template <typename T>
f.open("hop.txt");
T paire<T>::add()const{
Faire des structures.
return x[0]+x[1];
double x[10],y;
Faire des fichiers spars.
}
ofstream f("hop.bin",
...
ios::binary); Le .h doit suffire lutilisateur (qui ne doit pas regarder
// Le type doit tre
f.write((const char*)x,
le .cpp)
// prcis!
10*sizeof(double));
paire<int> a(1,2);
f.write((const char*)&y, Ne pas abuser du rcursif.
int s=a.somme();
sizeof(double));
Ne pas oublier delete.
paire<double> b;
f.close();
...
ifstream g("hop.bin", Compiler rgulirement.
ios::binary); Debug/Release : nettoyer les
Multiples :
g.read((char
deux.
*)x,
template <typename T,
10
sizeof(double));
*
typename S>
#include <cassert>
g.read((const char*)&y,
class hop {
...
sizeof(double));
...
assert(x!=0);
g.close();
};
y=1/x;
...
string s;
Faire des objets.
hop<int,string> A;
ifstream f(s.c_str());
...
Ne pas toujours faire des ob #include <sstream>
jets !
using namespace std;
Entiers :
template <int N>
class hop {

stringstream f;
// Chane vers entier

185

Penser interface / implmentation / utilisation.

12.7. Fiche de rfrence

12. En vrac (suite) ...

Fiche de rfrence (5/6)


set(i,1) :
reset(i,1) :
test(i,1) :
flip(i,1) :

Divers
i++;
i--;
i-=2;
j+=3;
j=i%n; // Modulo
#include <cstdlib>
...
i=rand()%n;
x=rand()/
double(RAND_MAX);
#include <ctime>
...
srand((unsigned int)
time(0));

#include <string>

using namespace std;


string s="hop";
char c=s[0];

int l=s.size();
if (s1==s1) ...
if (s1!=s2) ...

if (s1<s2) ...
size_t i=s.find(h),
j=s.find(h,3);
k=s.find("hop");
l=s.find("hop",3);
a="comment";

b="a va?";
txt=a+" "+b;

s1="un deux trois";


s2=string(s1,3,4);
getline(cin,s);

getline(cin,s,:);
const char *t=s.c_str();

#define _USE_MATH_DEFINES

#include <cmath>
double pi=M_PI;
Oprateurs binaires
and :
a&b
or :
a|b
xor :
a^b
right shift :
a>>n
left shift :
a<<n
complement : ~a
exemples :

class point {
int x,y;
Pas de dfinition de fonction
public:
dans une fonction !
...
};
int q=r=4; // NON!
...
if (i=2) // NON!
point a={2,3}; // NON
if i==2 // NON!

point
p=point(1,2);//NON
if (i==2) then // NON!
point p(1,2);
// OUI
for (int i=0,i<100,i++)
obj* t=new obj[n];
// NON!
delete t; // manque []
int f() {...}
//NON!
int i=f; // NON!
void f(int a=2,int b);
double x=1/3; // NON!
void f(int a,int b=0);
int i,j;
void f(int a);// NON!
x=i/j; // NON!
x=double(i/j); //NON! Ne pas tout mettre inline !

Erreurs frquentes

#include <cmath>
double sqrt(double x);
double cos(double x);
double sin(double x);
double acos(double x);

#include <ctime>
s=double(clock())
/CLOCKS_PER_SEC;

i|=(1<<n)
int *t,s;// s est int
i&=~(1<<n)
// et non int*
if (i&(1<<n))
t=new int[n];
i^=(1<<n)
s=new int[n];// NON!

double x[10],y[10];
int f() {
for (int i=1;i<=10;i++)
...
y[i]=2*x[i]; //NON
}
f()=3; // HORREUR!
int n=5;
int t[n]; // NON
int f()[4] { // NON!
int t[4];
...
return t; // NON!
}
int t[4]; t=f();

int& f() {
int i;
return i;
}
f()=3; // NON!
if (i>0 & i<n) // NON
if (i<0 | i>n) // NON

int s[3]={1,2,3},t[3]; if (...) {


...
t=s; // NON!
if (...)
int t[2];
break; // Non,
t={1,2}; // NON!
// boucles seulement
struct Point {
}
double x,y;
for (i ...)
} // NON!
for (j ...) {
Point a;
...
a={1,2}; // NON!
if (...)
break;//NON, quitte
#include "vec.cpp"//NON
// juste la boucle j
void f(int t[][]);//NON
int i;
int t[2,3]; // NON!
double x;
t[i,j]=...; // NON!
j=max(i,0);//OK
int* t;
y=max(x,0);//NON!
t[1]=...; // NON!
// 0.0 et non 0: max
int* t=new int[2];
// est un template STL
int* s=new int[2];
s=t; // On perd s!
Imagine++
delete[] t;
delete[] s; //Dja fait Voir documentation...

186

12. En vrac (suite) ...

12.8. Devoir final

Fiche de rfrence (6/6)


Tableaux

2D :
int A[2][3];
A[i][j]=...;
void init(int t[],
int A[2][3]=
int n) {
{{1,2,3},{4,5,6}};
for(int i=0;i<n;i++)
void
f(int A[2][2]);
t[i]=0;
t[i]=0;

Dfinition :
double x[5],y[5];
for(int i=0;i<5;i++)
y[i]=2*x[i];
const int n=5;
int i[n],j[2*n];
Initialisation :
int t[4]={1,2,3,4};
string s[2]={"ab","c"};

}
Taille variable :
int* t=new int[n];
...
delete[] t;

2D dans 1D :
int A[2*3];
A[i+2*j]=...;
Taille variable (suite) :
int *t,*s,n;

En paramtre (fin) :
Affectation :
En paramtre (suite) :
void f(const int* t,
int s[3]={1,2,3},t[3];
void f(int* t,int n){
int n) {
for (int i=0;i<3;i++)
t[i]=...
...
t[i]=s[i];
}
s+=t[i]; // OK
En paramtre :
void alloue(int*& t){
...
t=new int[n];
void init(int t[4]) {
t[i]=...; // NON!
}
for(int i=0;i<4;i++)
}

12.8

Devoir final

Vous pouvez enfin vous confronter aux devoirs complets des annales.

187

13. Structure de donnes

Chapitre 13
Structure de donnes
Les ordinateurs sont l pour nous affranchir des tches fastidieuses ou rptitives. Mais les
rptitions se retrouvent mme dans nos programmes. Ainsi, nous sommes souvent amens
rassembler une collection de donnes dans un tableau. Nous savons dj utiliser les tableaux
C++, mais notre usage en est primitif : ce ne sont pas des classes, donc nous devons tout faire
la main. En fait, il ny a pas une structure canonique satisfaisant tout le monde. Suivant
lusage quon en fait, une manire de grer la collection peut tre prfrable une autre. Ce
problme se rencontre si frquemment que le C++ propose par dfaut des classes. Pour la plus
grande gnricit possible, ces classes sont template et font partie de la STL, dont nous avons
dj rapidement parl. Ce chapitre na pas pour but de documenter les dtails de ces classes,
mais plutt dexpliquer les forces et faiblesses de chacune et la faon dont elles ont pu tre
implmentes.

13.1

Rappels sur les tableaux

Nous connaissons maintenant bien les tableaux, mais rappelons quelques caractristiques importantes :
les tableaux sont de taille fixe et ne peuvent tre tendus aprs leur cration.
les tableaux statiques doivent avoir une taille connue par le compilateur.
les tableaux dynamiques peuvent avoir une taille qui nest connue qu lexcution, mais il faut alors soccuper soi-mme de la mmoire.
aucune facilit nest offerte pour copier un tableau.
Ainsi, il faut presque tout faire soi-mme ! Par exemple, pour insrer un lment il faut
dcaler tous les suivants tout en sassurant que la place est suffisante. On ne peut pas
non plus demander sa taille un tableau... Mme si cest suffisant pour faire tout ce
quon veut, ces limitations ne facilitent pas la tche du programmeur et alourdissent le
code.
Lide dans ce cas est bien sr de mettre toutes ces fonctionnalits dans une classe,
dont les mthodes se chargent des traitements bas niveau (allocation mmoire, copie,
etc.). De fait, cest ce qui est propos dans la Standard Template Library (STL), qui est
partie intgrante du C++. En ralit, il y a mme plusieurs classes pour cela ! Mais pourquoi donc ? En fait ces classes sutilisent presque de la mme faon (interface proche)
mais leur implmentation est totalement diffrente. Suivant lusage quon veut en faire,
lune sera plus efficace que les autres.

13.2. La complexit

13.2

13. Structure de donnes

La complexit

Avant de regarder de plus prs les diffrentes classes, il convient de prciser ce


quon entend par complexit.

13.2.1

Mais quest-ce donc que la complexit ?

Cest le nombre doprations quil faut effectuer pour rsoudre un problme et la


taille de la mmoire ncessaire. On parle de complexit en temps dans le premier cas et
de complexit en espace dans le second. On est souvent amen trouver un quilibre
entre les deux, quand on ne peut pas rduire lun et lautre. Dans ce cas le principe gnral est le suivant : la mmoire est bon march, le temps est prcieux. Plus concrtement, ajouter de la mmoire une machine est facile, par contre la puissance de calcul
est limite (et ajouter des processeurs nest pas une option viable en gnral, car il
faut rcrire le programme pour lexploiter et beaucoup de problmes ne sont pas ou
difficilement paralllisables). Prenons un exemple simple : tracer lhistogramme dune
image. Voici une faon de faire :
int histo [256];
f o r ( i n t i = 0 ; i < 2 5 6 ; i ++)
histo [ i ] = 0;
f o r ( i n t i = 0 ; i < image . h e i g h t ( ) ; i ++)
f o r ( i n t j = 0 ; j < image . width ( ) ; j ++)
++ h i s t o [ image ( j , i ) ] ;
f o r ( i n t i = 0 ; i < 2 5 6 ; i ++)
drawRect ( i , 0 , 1 , h i s t o [ i ] ) ;
On voit quen plus de parcourir deux fois le tableau histo (ce qui ne dpend pas de la
taille de limage), on ne lit quune fois chaque pixel de limage. Sa complexit est donc
de lordre de N , le nombre de pixels. Considrons maintenant une variante :
f o r ( i n t c = 0 ; c < 2 5 6 ; c ++) {
i n t h=0;
f o r ( i n t i = 0 ; i < image . h e i g h t ( ) ; i ++)
f o r ( i n t j = 0 ; j < image . width ( ) ; j ++)
i f ( image ( j , i ) == c )
++h ;
drawRect ( i , 0 , 1 , h ) ;
}
La diffrence est quon a conomis en mmoire un tableau de 256 entiers. Du coup,
on lit chaque pixel de limage 256 fois. On a une complexit en temps de 256N , soit un
facteur 256 par rapport limplmentation prcdente. On prfrera sans ambigut la
premire solution.
Nous considrerons ici surtout la complexit en temps, car la mmoire nest pas un
facteur dterminant dans les problmes que nous traiterons.
190

13. Structure de donnes

13.2. La complexit

13.2.2

Comment la mesure-t-on ?

13.2.3

La notation O

La question de la complexit se pose quand on a des problmes de grande dimension. Sur des jeux de donnes petits, tous les algorithmes se valent ou presque. On
sintresse donc au cas o le nombre dlments devient grand, cest--dire quand N
tend vers linfini. On utilise dans ce cas la notation commode O(f (N )) o f est une
fonction croissante. Cela signifie que le nombre doprations est born par C.f (N ) o
C est une constante qui ne dpend pas de N . Bien sr la constante C a un rle, mais on
sintresse avant tout la fonction f puisque cest le facteur dterminant. Les algorithmes dhistogramme de la section ci-dessus sont tous deux en O(N ) bien que leur
constante soit trs diffrente. Voici quelques exemples de complexit quon rencontre
souvent par ordre de complexit croissante :
O(1) (f (N ) = 1 pour tout N ) signifie un algorithme qui seffectue en temps
constant indpendamment de la taille des donnes.
O(log N ) est un algorithme rapide, puisquil na pas besoin de lire toutes les donnes. Il nest possible que si celles-ci ont dj une certaine structure impose.
Exemple : recherche par dichotomie dans un tableau tri.
O(N ) (f est la fonction identit) dsigne un algorithme linaire, proportionnel au
nombre dlments.
O(N log N ) se rencontre frquemment, et ces algorithmes sont considrs comme
rapides. La fonction log augmentant trs lentement, cest lgrement plus que
linaire 1 . Ainsi la FFT 2 , un algorithme clbre de calcul de la transforme de
Fourier, peut tout fait sappliquer une image de 10 Mpixels, cest--dire N =
107 .
O(N 2 ) est un algorithme quadratique. Il peut rester acceptable pour des N pas
trop grands.
O(2N ) est une complexit exponentielle (f (N ) = eN log 2 ). Cest un algorithme qui
nest pratique que pour des problmes de petite dimension.

13.2.4

P et N P

Nous nentrerons pas dans des considrations dinformatique thorique, mais beaucoup ont entendu parler de la question P = N P ? et du million de dollars offert par
linstitut Clay pour sa rsolution. Pour faire bref, la classe P dsigne des problmes
pour lesquels il existe un algorithme polynomial (f est un polynme) et N P est un
problme pour lequel on ne connat pas de tel algorithme (ce qui ne veut pas dire quil
nen existe pas !) par exemple un problme de combinatoire pour lequel on ne sait pas
faire mieux que tester toutes les combinaisons. Les problmes N P ne sont pas solubles
exactement en temps raisonnable 3 lorsque N est grand. Mais cela ne veut pas dire
quon ne peut pas trouver une solution approche en temps polynmial.
1. Il sagit souvent du logarithme en base 2, mais celui-ci ne diffrant du logarithme nprien que
par un facteur constant, nous navons pas besoin de prciser dans la notation O
2. Fast Fourier Transform, notez le Fast
3. ou plus exactement sils le sont, on ne sait pas comment !

191

13.3. Le vecteur : un tableau encapsul dans une classe

13.2.5

13. Structure de donnes

Pourquoi est-ce important ?

Un algorithme exponentiel est inutilisable pour de grandes donnes. Mais parmi


les algorithmes polynmiaux, on privilgiera celui de complexit minimale, car il nous
fera gagner du temps 4 . De nombreuses architectures ajustent leur frquence en fonction de lutilisation du CPU, donc consomment moins dnergie quand le processeur a
peu faire, comme par exemple la technologie SpeedStep chez Intel. Cela a des consquences sur la batterie de votre ordinateur portable, sur la quantit dnergie consomme, sur le dgagement de chaleur du processeur, sur le bruit produit suivant que le
ventilateur se met en marche... Utilisons donc des algorithmes efficaces, que le processeur puisse se remettre rapidement en mode basse frquence, cela participe la lutte
contre le rchauffement climatique !
Attention, il faut quand mme se garder de la tentation de vouloir optimiser trop
tt. Il faut dabord identifier la partie coteuse en temps, et elle nest pas toujours o
lon croit, et chercher optimiser celle-ci. Car malheureusement les algorithmes efficaces en temps sont souvent plus complexes du point de vue de la programmation et
le temps gagn en calcul est perdu en temps de programmation, traque des bugs et
maintenance.

13.3

Le vecteur : un tableau encapsul dans une classe

13.3.1

Usage

Il sagit de la classe std :: vector (ou vector aprs using namespace std;) et il sagit
dun tableau encapsul tout simplement dans une classe, une diffrence de taille : il se
charge de grer sa mmoire et lajuste dynamiquement la demande. On lobtient par
un #include <vector>. Un vecteur de double se construit ainsi et ajouter les lments
se fait par la mthode push_back :
v e c t o r <double > v e c t ;
v e c t . push_back ( 3 . 1 4 1 6 ) ;
v e c t . push_back ( 2 . 7 1 8 ) ;
cout << v e c t . s i z e ( ) << " elements : " << v e c t [ 0 ] << " " << v e c t [ 1 ] <<endl ;
Remarquons que :
Nous navons pas eu besoin de dclarer le nombre dlments que nous mettrons
dans vect lors de sa construction.
Nous pouvons retrouver la taille du tableau tout moment avec la mthode size.
On accde aux lments du vecteur comme pour un tableau, ce qui est possible
grce la dfinition de loprateur [] pour la classe vector.
Il y a cependant des erreurs viter :
s t d : : v e c t o r <double > v e c t ;
v e c t [ 0 ] = 3 . 1 4 1 6 ; / / NON, v e c t [ 0 ] n e x i s t e p a s
v e c t . push_back ( 2 . 0 ) ; / / OK, v e c t [ 0 ] = = 2
v e c t [ 0 ] = 3 . 1 4 1 6 ; / / C e s t bon m a i n t e n a n t , v e c t [ 0 ] e x i s t e
Loprateur = et le constructeur par recopie sont dfinis comme il faut, mais justement,
il faut viter de lappeler inutilement :
4. et notre temps sur Terre est limit...

192

13. Structure de donnes

13.4. La pile, Last In First Out (LIFO)

void p r i n t ( s t d : : v e c t o r <double > v e c t ) { / / E v i t e r : c o n s t r . p a r c o p i e


f o r ( i n t i = 0 ; i < v e c t . s i z e ( ) ; i ++)
s t d : : cout << v e c t [ i ] << " " ;
s t d : : cout << s t d : : endl ;
}
Le paramtre vect de la fonction print est pass par valeur, ce qui appelle donc un
constructeur par recopie. Celui-ci alloue la mmoire ncessaire et recopie le paramtre
pass en entre de print, ce qui est inutile. Il vaut bien mieux passer par rfrence,
mais constante pour bien indiquer au compilateur quon ne souhaite pas modifier le
tableau :
void p r i n t ( c o n s t s t d : : v e c t o r <double>& v e c t ) { / / T r s b i e n
...

13.3.2

Complexit

Intressons-nous la complexit des oprations lmentaires sur le vecteur. Lopration lmentaire est ici le nombre de lectures ou dcritures dans une case du tableau.
Lajout dun lment la fin du tableau se fait en O(1) de mme que le retrait du dernier
lment (pop_back). Par contre, lajout dun lment en position i ( insert ) ncessite de
dcaler tous les lments qui le suivent, cest--dire N i, donc jusqu O(N ). Le retrait
dun lment (erase) galement. Par contre la lecture ou lcriture dans une case quelconque du tableau se fait en O(1). On voit que le vecteur est particulirement efficace
quand on ne fait quajouter des lments et que lordre na pas dimportance, 5 ce qui
est le cas dans la plupart des cas.

13.3.3

Gestion mmoire

Nous avons pass sous silence la faon dont la classe gre sa mmoire. Cest pourtant crucial pour son efficacit. Ainsi, si la fonction push_back a besoin de rallouer
de la mmoire et de recopier dans le nouveau tableau les lments dj existants, on
passe du O(1) annonc O(N ). Pour viter donc de rallouer trop souvent, le principe
nonc plus haut est appliqu : la mmoire est bon march, on peut se permettre den
abuser (un peu). Cela se traduit par le choix suivant : chaque fois que la mmoire est
pleine au moment o on veut ajouter un lment, on alloue une place mmoire double
de la prcdente. Ainsi si on veut ajouter N lments, on a besoin de seulement log2 N
allocations mmoires. Par contre, si la mmoire est pleine et quon na besoin de najouter quun lment, on aura gach N 1 emplacements mmoire, mais cest un choix
assum.

13.4

La pile, Last In First Out (LIFO)

Rappelons le principe de la pile : le dernier lment ajout est le premier enlev. On


voit que la pile simplmente tout fait naturellement avec un vecteur. Le haut de la
pile est le dernier lment, celui dindice size()1, quon peut aussi consulter avec la
mthode back(). Lajout dun lment se fait donc par push_back, et le retrait du haut
5. mais il est toujours possible de trier le vecteur aprs lavoir rempli, voir le chapitre suivant

193

13.5. La file, First In First Out (FIFO)

13. Structure de donnes

push(5)
fin

debut

pop()
fin

debut

fin

debut

F IGURE 13.1 Fonctionnement dune file. Gauche : le contenu de la file est [1,2,3,4].
Milieu : aprs push(5) : [1,2,3,4,5]. Droite : aprs pop() : [2,3,4,5]. A noter que le 1
reste dans le tableau, mais est ignor puisquil nest pas entre debut et fin.
de la pile par pop_back, oprations en O(1). Pour retrouver le vocabulaire spcifique
et standard de la pile, la STL propose quand mme std :: stack :
s t d : : s t a c k <double > s ;
s . push ( 3 . 1 4 1 6 ) ;
s . push ( 2 . 7 1 8 ) ;
cout << s . top ( ) << s t d : : endl ; / / A f f i c h e 2 . 7 1 8
s . pop ( ) ;
cout << s . top ( ) << s t d : : endl ; / / A f f i c h e 3 . 1 4 1 6
s . pop ( ) ;
a s s e r t ( s . s i z e ( ) = = 0 ) ; / / Ou b i e n a s s e r t ( s . empty ( ) ) ;
Pour raison defficacit 6 , il y a une lgre diffrence avec linterface de pile que
nous avons dj rencontre : la mthode pop ne renvoie pas llment en haut de la pile,
en fait elle ne renvoie rien (void). Cet lment est donc perdu aprs lappel pop,
mais on pouvait toujours la rcuprer en appelant pop juste avant.

13.5

La file, First In First Out (FIFO)

Contrairement la pile, la file correspond la file dattente habituelle : premier


arriv, premier servi ! Nous pouvons stocker les lments dans un vecteur, mais si
le push_back est en O(1), le pop_front (cette mthode nexiste pas en fait) serait
en O(N ). En dautres termes, le push de la file serait en O(1) et le pop en O(N ). Ce
nest donc pas une bonne solution. Par contre, aprs avoir retir le premier lment, il
suffit de se souvenir que le nouveau premier se trouve en fait tre le deuxime etc. Ce
principe est illustr Fig. 13.1. Voici une possibilit dimplmentation simplifie :
class File {
s t d : : v e c t o r <double > v ;
i n t debut , f i n ;
i n t modulo ( i n t i ) c o n s t ;
public :
File ( ) ;
bool empty ( ) c o n s t ;
double f r o n t ( ) c o n s t ;
void push ( double d ) ;
double pop ( ) ;
};
6. En effet cela obligerait crer une variable locale dans la mthode pour y recopier la valeur en haut
de pile et retourner cette valeur lutilisateur, donc une deuxime recopie. Cela nest gure troublant
pour un type de base, mais devient plus gnant pour un type complexe, par exemple une image.

194

13. Structure de donnes

13.5. La file, First In First Out (FIFO)

/ / Constructeur
File : : File () {
debut= f i n = 0 ;
}
/ / Indique si la f i l e est vide
bool F i l e : : empty ( ) c o n s t {
r e t u r n ( debut== f i n ) ;
}
/ / T t e de l a f i l e , p r o c h a i n c l i e n t
double F i l e : : f r o n t ( ) c o n s t {
r e t u r n v [ debut ] ;
}
/ / A r i t h m t h i q u e modulo v . s i z e ( )
i n t F i l e : : modulo ( i n t i ) c o n s t {
i f ( v . c a p a c i t y ( ) == 0 )
return 0;
r e t u r n ( i%v . c a p a c i t y ( ) ) ;
}
/ / A j o u t d un l m e n t
void F i l e : : push ( double d ) {
i n t f i n 2 = modulo ( f i n + 1 ) ;
i f ( f i n 2 ==debut ) { / / Le s e r p e n t s e mord l a q u e u e !
s t d : : v e c t o r <double > v2 ; / / R e c o p i e du t a b l e a u
f o r ( i n t i =debut ; i ! = f i n ; i =modulo ( i + 1 ) )
v2 . push_back ( v [ i ] ) ;
v = v2 ;
v . reserve (v . capacity ( ) + 2 ) ;
debut = 0 ; / / On r e m e t l a t t e l i n d i c e 0
fin2 = v . size ()+1;
}
i f ( f i n == v . s i z e ( ) )
v . push_back ( d ) ;
else
v[ fin ] = d;
fin = fin2 ;
}
/ / R e t r a i t de l l m e n t de t t e
double F i l e : : pop ( ) {
double d = f r o n t ( ) ;
debut = modulo ( debut + 1 ) ;
return d ;
}
Notons que :
195

13.6. La liste chane

13. Structure de donnes

On utilise les mthodes reserve et capacity qui concernent le nombre dlments


dans le tableau allou (qui peut tre plus grand que le nombre dlments rels).
On fait toutes les oprations modulo v.capacity(), ce qui permet de rutiliser les
cases libres par un pop() prcdent et conomise de la mmoire. Ceci est fait
via la mthode modulo qui na pas besoin dtre publique puisque cest un dtail
dimplmentation.
La mthode push est complique par le fait quelle doit rinitialiser un tableau
plus grand si v est plein. Si cest le cas, on rinitialise la tte lindice 0 du tableau.
Nous avons prsent cette implmentation pour un tableau de double. Bien entendu, une implmentation gnrique serait template, mais alors moins lisible.
La variable fin indique lindice suivant la queue. Cet indice doit tre infrieur
v.capacity(), do lutilisation de reserve.
Limplmentation est un peu technique, mais montre quil est possible de programmer la file efficacement en une cinquantaine de lignes de code : push et pop
sont en O(1).
Il existe une classe template std :: deque (pour double-ended queue) qui implmente
compltement une file, bien que la gestion de la mmoire soit diffrente de celle propose ici.

13.6

La liste chane

Les structures que nous avons vues jusquici ntaient efficaces que pour les ajouts
et retraits dlments au dbut et/ou fin de tableau, pas au milieu. Par contre, laccs
un lment dindice donn se faisait en O(1). En fait, il y a possibilit dinverser les
choses : la liste chane permet dajouter et retirer un lment nimporte o en O(1)
mais ne permet laccs un lment quelconque quen O(N ). Pour cela, on stocke avec
chaque lment les indices du suivant next et prcdent prev dans le tableau.
s t r u c t chainon {
i n t prev , next ;
double v a l ;
};
Pour insrer llement d quon stocke lindice j juste aprs llment stock lindice i, il suffit de faire :
t [ j ] . val = d ;
t [ j ] . prev = i ;
t [ j ] . next = t [ i ] . next ;
t [ i ] . next = j ;
i f ( t [ j ] . next ! = 1) t [ t [ j ] . next ] . prev = j ;
Pour retirer llment stock en i, il suffit de reconnecter ensemble les suivant et prcdent :
i f ( t [ i ] . prev ! = 1) t [ t [ i ] . prev ] . next = t [ i ] . next ;
i f ( t [ i ] . next ! = 1) t [ t [ i ] . next ] . prev = t [ i ] . prev ;
Lindice 1 est un marqueur de dbut ou de fin de liste. Cette faon de faire laisse
des trous dans le tableau t. supposant que lindice du premier lment est i0, pour
trouver llment list[i] on doit faire
196

13. Structure de donnes

13.7. Rcapitulatif des complexits

int i i = i0 ;
f o r ( i n t j = 0 ; j < i ; j ++)
i i = t [ i i ] . next ;
double d = t [ i i ] . v a l ;
En ralit, les champs next et prev sont des pointeurs, cest--dire des adresses en
mmoire. Nous avons vit au maximum de parler de pointeurs dans ce cours, il suffit
de savoir quun pointeur indique o trouver un lment en mmoire. La STL fournit
une classe template std :: list qui implmente une liste.

13.7

Rcapitulatif des complexits


push_back
pop_back
push_front
pop_front
tab[ i ]
insert
erase

vecteur pile
O(1)
O(1)1
O(1)
O(1)2
O(N)3
n/a
O(N)3
n/a
O(1)
n/a4
O(N)
n/a
O(N)
n/a

file
O(1)1
n/a
n/a
O(1)2
n/a4
n/a
n/a

liste
O(1)
O(1)
O(1)
O(1)
O(N)
O(1)
O(1)

push_back sappelle en fait push pour pile et file


pop_back/pop_front sappellent en fait pop pour pile et file
3
Ces mthodes ne font pas partie de linterface du vecteur mais la fonctionnalit quivalente peut tre obtenue par insert ou erase.
4
Ces mthodes ne font pas partie de linterface de la file ou de la pile, mais peuvent
tre ajoutes avec complexit O(1) dans limplmentation que nous avons vue.
2

13.8

Les itrateurs

En ralit, pour indiquer un emplacement dans le tableau, comme par exemple


pour linsertion ou le retrait dun lment, les classes de la STL utilisent des itrateurs. Ceux-ci permettent dnumrer les lments ou de dsigner un emplacement.
Ils sont assez similaires des pointeurs. Lintrt de les utiliser cest quil nest pas ncessaire de dfinir tous les algorithmes sur toutes les classes. Il suffit de faire une fois
lalgorithme et daccder aux lments du tableau uniquement par lintermdiaire des
itrateurs, et non par un indice. Les itrateurs servent donc surtout de lien entre les
structures et les algorithmes quon peut leur appliquer. Voici un exemple dutilisation
de ces itrateurs :
s t d : : v e c t o r <double > : : c o n s t _ i t e r a t o r i t = v e c t . begin ( ) ;
f o r ( ; i t ! = v e c t . end ( ) ; ++ i t )
s t d : : cout << i t << " " ;
s t d : : cout << s t d : : endl ;
La mthode begin renvoie un itrateur sur le premier lment du tableau et end pointe
une case plus loin que le dernier lment (donc attention de ne pas essayer daccder
llment point par cet itrateur). Loprateur ++ fait passer llment suivant et
loprateur * retourne llment point par litrateur (ici un double).
197

13.9. Autres structures

13. Structure de donnes

Si on veut modifier les lments, il faut utiliser iterator au lieu de const_iterator ,


mais cela nest possible que si vect nest pas const.

13.9

Autres structures

Dautres structures trs classiques car souvent utiles ont t passes sous silence
dans ce chapitre. Elles ne sont pas toutes proposes par dfaut dans la STL, mais certaines pourraient ltre dans le futur. Citons entre autres :
Les ensembles (std :: set), qui sont semblables ceux des maths dans le sens
quune mme valeur ne peut pas tre prsente plusieurs fois.
Les fonctions (std :: map), qui associent une cl (lensemble des cls doit tre
totalement ordonn) une valeur, et permettent de retrouver efficacement la valeur
associe une cl. La STL en propose une implmentation avec std :: map.
La table de hachache, qui permet une recherche efficace pourvu quon ait une
mthode pour grouper les objets en un petit nombre de classes ordonnes, typiquement une fonction associant un nombre entier chaque lment.
La file de priorit, qui maintient un ensemble ordonn mais autorise linsertion
ou le retrait efficaces dlments. Nous en parlerons dans le prochain chapitre.
Les graphes, forms de sommets et dartes (paires de sommets). Beaucoup de
problmes se formulent facilement avec des graphes.
Les arbres, qui sont des graphes particuliers.

198

14. Algorithmes de tri

Chapitre 14
Algorithmes de tri
Maintenant que nous savons ranger intelligemment nos donnes (voir le chapitre prcdent), il faut souvent les trier. Ici, nous allons voir trois algorithmes de tris, et la conclusion
nest plus a dpend ce quon veut, mais nous avons un gagnant en pratique, QuickSort.
Cest celui-ci qui est implment dans la STL.

14.1

Complexit minimale

Un algorithme de tri gnral et fonctionnant quel que soit la liste de valeurs en entre doit retrouver la permutation permettant dnumerer les lments dans lordre.
Il y a N ! permutations possibles. Lopration lmentaire est la comparaison de deux
lments. Ayant compar deux lments, on se retrouve alors au mieux avec deux tableaux de N/2 lments. Nous verrons que cest ce qui se passe en gnral avec QuickSort, et que la complexit rsultante est O(N log N ).
En fait, cela ne signifie pas quil nest pas possible de faire mieux, mais uniquement
dans le cas o des contraintes sur les valeurs en entre sont connues. Par exemple, si
les nombres en entre prennent des valeurs parmi un petit nombre de valeurs possibles, on peut trs bien faire un histogramme en O(N ). En supposant que les valeurs
possibles sont les entiers entre 0 et 255 :
int histo [256];
f o r ( i n t i = 0 ; i < 2 5 6 ; i ++)
histo [ i ] = 0;
f o r ( i n t i = 0 ; i < N; i ++)
++ h i s t o [ t a b [ i ] ] ;
i n t k=0;
f o r ( i n t i = 0 ; i < 2 5 6 ; i ++)
f o r ( i n t j = 0 ; j < h i s t o [ i ] ; j ++)
t a b [ k++] = i ;

14.2

Algorithmes quadratiques

Il est extrment simple dimaginer un algorithme quadratique de tri. Par exemple,


on peut chercher la valeur minimale v0 dans le tableau en O(N ) (et on compte son

14.3. QuickSort

14. Algorithmes de tri

nombre n0 doccurences). Puis on cherche la valeur minimale v1 parmi les lments


qui restent et ainsi de suite. On parcourt donc N fois le tableau et la complexit est
donc en O(N 2 ). Notons la ressemblance avec le tri par histogramme propos ci-dessus.
La seule diffrence est que les valeurs v0 , v1 , etc. ne sont pas connues lavance et
doivent donc tre trouves.
En fait, un algorithme encore plus simple programmer et bien connu est le tri
bulles. Chaque valeur reprsente le poids dune bulle et on voit les bulles les plus
lgres remonter (ou plutt aller vers le dbut du tableau). On parcourt le tableau et
on permute un lment avec son suivant sils ne sont pas dans lordre. On itre N fois
cette boucle et en fin de compte le tableau est tri :
f o r ( i n t i = 0 ; i < N; i ++)
f o r ( i n t j = 0 ; j +1 < N; j ++)
i f ( tab [ j ] > tab [ j +1])
swap ( t a b [ j ] , t a b [ j + 1 ] ) ;
Cet algorithme fait clairement N (N 1) soit O(N 2 ) comparaisons. En fait on peut faire
mieux en observant que dans la boucle dindice j, ds quon arrive sur llment le
plus grand, on le fait descendre jusquau bout du tableau. Donc aprs un premier parcours, llment le plus grand est en bout et donc sa position finale. Il est donc inutile
par la suite de comparer tab[N2] et tab[N1], et ainsi de suite. Nous pouvons donc
remplacer la boucle en j par
f o r ( i n t j = 0 ; j + i +1 < N; j ++)
...
On fait ainsi (N 1) + (N 2) + ... + 1 comparaisons, soit N (N 1)/2. Avec cette
modification modeste, nous avons divis par 2 la complexit, mais cela reste du O(N 2 ).
De nombreux autres algorithmes simples, tels que le tri par insertion ou le tri par
slection, sont simplement des variantes et restent en O(N 2 ).

14.3

QuickSort

Nous allons tudier la complexit de QuickSort et constater quelle est optimale.


Rappelons brivement le principe :
1. Nous slectionnons un lment, appel pivot, et cherchons sa place dfinitive
dans le tableau final.
2. Ceci spare le tableau en deux parties, la gauche et la droite du pivot, et nous
itrons sur chacun de ces sous-tableaux.
Ltape cruciale est de trouver la position du pivot, ce qui peut se faire en N oprations
de la faon suivante :
Nous maintienons un compteur i partant du dbut du tableau et lincrmentons
tant que t[i] est infrieur au pivot.
Nous avons un deuxime compteur j qui part de la fin et que nous dcrmentons
tant que t[j] est suprieur au pivot.
Si i < j, nous permutons t[i] et t[j] et continuons la progression des indices.
Lorsque i == j, la place du pivot est i.
200

14. Algorithmes de tri

14.3. QuickSort

Dans la pratique, il faut faire bien attention de ne pas risquer de dpasser du tableau et
cela demande du soin. Llment pivot est un lment du tableau, souvent le dernier.
Nous voyons que cette tape du pivot ncessite un parcours du tableau, donc N 1
comparaisons. Nous parlons ici des comparaisons entre lments du tableau t, pas
dindices i et j, qui ne sont pas de mme nature : ceux-ci sont des entiers, les lments
du tableau peuvent tre dun type complexe avec une relation dordre coteuse calculer. Ce sont ces dernires quil faut chercher minimiser. Si nous notons CN la complexit et que nous nous retrouvons avec un pivot en position i, nous avons la relation
de rcurrence :
CN = (N 1) + Ci + CN i1
Lindice i dpend des donnes du tableau, mais nous pouvons supposer quen moyenne
il sagit du milieu du tableau. Nous crivons donc
CN = N + 2CN/2 .
En utilisant la relation de rcurrence pour CN/2 , nous obtenons
CN = N + 2(N/2 + 2CN/4 ) = 2N + 4CN/4 .
En allant un cran plus loin, il vient
CN = 3N + 8CN/8 .
Le processus sarrte lorsque lindice de C droite de la complexit devient 1, et alors
CN = (log N )N + N C1 .
Le rsultat est donc CN = O(N log N ). Pour le vrifier rigoureusement, il faut en fait
montrer par rcurrence que pour N une puissance de 2, la suite CN = N log2 N satisfait
la relation de rcurrence
C2N = 2N + 2CN ,
ce qui nest pas difficile.
Le QuickSort fonctionne sur le principe du divide and conquer (diviser pour
mieux rgner). Limportant est de diviser quitablement le tableau, cest--dire que
i = N/2. Examinons maintenant ce qui se passe pour une violation extrme de cette
hypothse, o i = 0 (premire position) ou i = N 1 (dernire position). La relation de
rcurrence devient
CN = (N 1) + CN 1 .
Si cela se reproduit chaque tape, le rsultat devient
CN = (N 1) + (N 2) + + 1 = O(N 2 ).
Il sagit du cas le pire, qui se produit quand le tableau est dj tri ou bien en ordre inverse. Nous voyons que ce cas nest pas meilleur quun tri bulles. Malheureusement,
ce cas le pire nest pas si rare en pratique. Deux techniques courantes sont utilises
pour viter ce cas dfavorable :
1. Choisir comme pivot un lment tir au hasard dans le tableau plutt que le
premier ou dernier.
2. Choisir comme pivot llment du milieu (mdian) entre le permier, le dernier et
llment la moiti du tableau.
201

14.4. File de priorit et HeapSort

14. Algorithmes de tri

10
7
5

8
3

10

10

7
5

8
9

F IGURE 14.1 Fonction push dans la file de priorit


Ce choix de pivot se fait en temps constant, donc a un cot ngligeable dans le cas
normal o il serait inutile. Cela nest vrai que lorsquon peut accder un lment
quelconque en O(1), ce qui est valable pour un tableau ordinaire ou un vecteur, mais
faux pour une liste, comme nous lavons vu au chapitre prcdant.
Il est possible de se demander ce quil en est entre ces deux extrmes : pivot au
milieu ou pivot une extrmit. En fait il est possible de montrer que la complexit
moyenne est O(N log N ), mme si le cas le pire reste O(N 2 ). QuickSort est implment
par la fonction std :: sort de la STL (utiliser #include <algorithm>).
Nous allons voir quen fait il existe un algorithme de tri, HeapSort, qui reste optimal
dans le pire cas O(N log N ).

14.4

File de priorit et HeapSort

Nous avons vu la file FIFO au chapitre prcdant. En fait, nous allons modifier cette
file en considrant que tous les lments ne sont pas gaux mais que chacun a une
priorit. Llment de plus grande priorit sera en tte, cest--dire le premier sortir.
Une solution simple qui vient lesprit est de maintenir tout moment les lments de
la file trie par ordre de priorit. En fait, ce nest pas pratique :
1. Si on peut accder un lment arbitraire de la file en O(1), on peut trouver
lemplacement dun lment ajouter par dichotomie en O(log N ) comparaisons,
mais alors linsertion se fait en O(N ) copies dlments dans le tableau.
2. Si on peut insrer llment en O(1), comme pour une liste, il faut alors O(N )
comparaisons pour trouver lemplacement o linsrer.
En ralit, il suffit de sassurer qu tout moment nous pouvons accder facilement
llment de plus grande priorit, sans quil soit ncessaire de tout trier. Une structure
darbre binaire vrifiant le principe suivant est bien adapte : tout parent a une priorit
au moins aussi grande que ses deux enfants. Il est alors facile de voir que la racine est
llment de plus grande priorit. Voyons dabord comment insrer un lment.

14.4.1

Insertion dans la file de priorit (push)

Nous commenons par ajouter le nouvel lment comme feuille de larbre profondeur minimale, puis nous rparons larbre : sil est plus grand que son parent, nous
permutons les deux, comparons au nouveau parent et ainsi de suite jusqu arriver
la racine ou un parent qui a plus grande priorit (Fig. 14.1). Larbre tant quilibr, il
a une profondeur log2 N et donc O(log N ) comparaisons sont ncessaires.
202

14. Algorithmes de tri

14.4. File de priorit et HeapSort

10
6

7
5

7
3

3
6

6
3

F IGURE 14.2 Fonction pop dans la file de priorit

14.4.2

Retrait de la file de priorit (pop)

Llment en tte est la racine de larbre. Nous retirons llment la dernire feuille
de larbre pour la mettre la racine et rparons larbre : si la racine a plus faible priorit
que le plus grand des (un ou deux) enfants, nous permutons les deux et comparons
aux deux nouveaux enfants et ainsi de suite (Fig. 14.2). Le processus sarrte lorsque
nous atteignons une feuille ou bien lorsque llment a plus grande priorit que ses enfants. Larbre tant quilibr, nous descendons llment au plus jusqu la profondeur
maximale, soit O(log N ).

14.4.3

Stockage de la file de priorit

Pour stocker cet arbre binaire quilibr, il est trs efficace dutiliser un simple tableau. Pour une fois, nous considrerons que le premier lment du tableau est dindice 1 et non 0. Le C++ nautorise pas cela, mais plutt que de jouer sur les indices, il
est plus facile dignorer simplement lindice 0 du tableau. La racine est donc en 1, ses
enfants en 2 et 3, les enfants de 2 en 4 et 5 et ceux de 3 en 6 et 7, etc. Les enfants de
llment dindice i sont en 2i et 2i + 1. Son parent en indice i/2 (quotient de la division
euclidienne). Voici donc lalgorithme push :
void F i l e P r i o r i t e : : push ( double d ) {
int i=size ( ) ;
v . push_back ( d ) ;
while ( i >1 && v [ i ] > v [ i / 2 ] ) {
s t d : : swap ( v [ i ] , v [ i / 2 ] ) ;
i /= 2 ;
}
}
La fonction pop est lgrement plus complique :
double F i l e P r i o r i t e : : pop ( ) {
double d = v [ 1 ] ;
v [ 1 ] = v . back ( ) ;
v . pop_back ( ) ;
int i = 1;
while ( 2 i < v . s i z e ( ) ) {
i n t j = i ; / / On c h e r c h e l e max d e s e n f a n t s
i f ( v [ 2 i ] > v [ j ] ) / / Gauche
j = 2 i ;
i f ( 2 i +1 < v . s i z e ( ) && v [ 2 i +1] > v [ j ] ) / / D r o i t e
j = 2 i + 1 ;
i f ( i == j ) break ;
203

14.5. Conclusion

14. Algorithmes de tri

s t d : : swap ( v [ i ] , v [ j ] ) ;
i = j;
}
return d ;
}

14.4.4

HeapSort

Le principe de HeapSort est juste dutiliser la file de priorit, appele heap en anglais : les lments sont ajouts dans la file puis retirs les uns aprs les autres.
double HeapSort ( s t d : : v e c t o r <double>& v ) {
FilePriorite f ;
f o r ( i n t i = 0 ; i < v . s i z e ( ) ; i ++)
f . push ( v [ i ] ) ;
f o r ( i n t j =v . s i z e ( ) 1 ; j >= 0 ; j )
v [ j ] = f . pop ( ) ;
}
Mettre les N lments dans la file ncessite O(N log N ) comparaisons et les retirer galement O(N log N ). A noter quil sagit du cas moyen aussi bien que du pire cas. Cet
algorithme est donc de complexit asymptotique optimale dans tous les cas.

14.5

Conclusion

QuickSort et HeapSort sont tous deux en O(N log N ) dans le cas moyen. Cependant
QuickSort est en O(N 2 ) au pire. Mais HeapSort ncessite de la mmoire supplmentaire pour la file de priorit et le cas le pire de QuickSort peut tre rendu trs improbable par slection du pivot comme expliqu plus haut.

204

A. Travaux Pratiques

Annexe A
Travaux Pratiques
Note : les corrigs seront disponibles sur la page web du cours aprs chaque TP.

A.1

Lenvironnement de programmation

A.1.1

Bonjour, Monde !

1. Connexion :
Se connecter sous Windows ou Linux.
2. Projet :
Tlcharger larchive Tp1_Initial.zip sur la page du cours, la dcompresser
sur le bureau. Le fichier CMakeLists.txt dcrit le projet. La ligne qui nous
intresse est la suivante :
a d d _e xe cu ta bl e ( Tp1 Tp1 . cpp )
Elle indique que le programme sappellera Tp1 (Tp1.exe sous Windows) et que
le code source pour le construire est dans le fichier Tp1.cpp.
(a) Visual (donc sous Windows) nest pas capable dinterprter le fichier CMakeLists.txt,
il faut utiliser le programme Cmake au pralable. Lancer donc ce programme,
et aller chercher comme rpertoire "source code" Tp1_Initial par le bouton "Browse source...". Cest une bonne ide de sparer les fichiers gnrs des sources : slectionner dans la ligne suivante un nouveau rpertoire "Build". Cliquer "Generate" et slectionner le bon gnerateur (Visual
Studio 2008, sur les machines de lcole). Si tout sest bien pass, on peut
fermer Cmake et ouvrir le fichier "solution" (extension .sln) dans le rpertoire Build, ce qui lance "Visual Studio"
(b) Sous Linux, lancer Kdevelop, le menu "Open Project" et choisir le CMakeLists.txt.
Les choix proposs par dfaut (en particulier le mode "Debug") sont corrects.
Kdevelop comprend le format CMakeLists et est capable de lancer Cmake
lui-mme.
3. Programmation :
(a) Rajouter cout << "Hello" << endl; sur la ligne avant return 0;
4. Gnration :
(a) Dans la fentre "Solution explorer" de Visual Studio, rechercher et afficher le
fichier Tp1.cpp.

A.1. Lenvironnement de programmation

A. Travaux Pratiques

(b) "Build/Build solution", ou "F7" ou bouton correspondant.


(c) Vrifier lexistence dun fichier Tp1 (Tp1.exe sous Windows) dans Build/Tp1
(Build/Tp1/Debug sous Windows).
5. Excution :
(a) Sous Kdevelop, il faut commencer par aller dans le menu "Configure launches",
ajouter avec le bouton "+" et slectionner lexcutable. On peut alors le lancer
avec le bouton "Execute".
(b) Lancer le programme (sous Visual) avec "Debug/Start Without Debugging"
(ou
"Ctrl+F5" ou bouton correspondant). Il faut dabord lui prciser quel programme lancer (clic droit sur le projet Tp1 dans lexplorateur de solution,
"Slectionner comme projet de dmarrage") Une fentre fonds noir "console"
souvre, dans laquelle le programme sexcute, et la fentre console reste ouverte jusqu lappui dune touche du clavier.
(c) Vrifier quon a en fait cr un programme indpendant quon peut lancer
dans une fentre de commande :
Essayer de le lancer depuis le gestionnaire de fichiers standard : le programme se referme tout de suite !
Dans les menus Windows : "Dmarrer/Excuter"
"Ouvrir: cmd"
Taper "D:", 1 puis
"cd \Documents and Settings\login\Bureau\Tp1\Tp1\Debug"
(apprenez profiter de la compltion automatique avec la touche TAB).
Vrifier la prsence de Tp1.exe avec la commande "dir".
Taper "Tp1".
Touche utile (Visual) : Ctrl+F5 =

= Start without debugging

6. Fichiers :
On a dj suivi la cration des fichiers principaux au fur et mesure. Constater la
prsence de Tp1.obj qui est la compilation de Tp1.cpp (que le linker a ensuite
utilis pour crer Tp1.exe). Voir aussi la prsence de nombreux fichiers de travail.
Quelle est la taille du rpertoire de Build de Tp1 (clic droit + proprits) ?
7. Nettoyage :
Supprimer les fichiers de travail et les rsultats de la gnration avec "Build /
Clean solution" puis fermer Visual Studio. Quelle est la nouvelle taille du
rpertoire ?
8. Compression :
Sous Windows, en cliquant droite sur le rpertoire Tp1, fabriquer une archive
comprime Tp1.zip (ou Tp1.7z suivant la machine). Attention il faut quitter
Visual Studio avant de comprimer. Il peut sinon y avoir une erreur ou certains
fichiers trop importants peuvent subsister.
1. Sur certaines machines, il faut en fait aller sur C :, vrifiez en regardant o est install votre projet

206

A. Travaux Pratiques

A.1. Lenvironnement de programmation

Il faut quitter Visual Studio avant de comprimer.


9. Envoi :
Envoyer le fichier par mail son responsable de PC en indiquant bien le nom de
son binme et en mettant [ENPCInfo1A] dans le sujet.

A.1.2

Premires erreurs

1. Modifier le programme :
Modifier le programme en changeant par exemple la phrase affiche.
(a) Tester une nouvelle gnration/excution. Vrifier que Visual Studio sauve
le fichier automatiquement avant de gnrer.
(b) Modifier nouveau le programme. Tester directement une excution. Visual
Studio demande automatiquement une gnration !
Lancer directement une excution sauve et gnre automatiquement. Attention toutefois de ne pas confirmer lexcution si la gnration sest mal passe.
2. Erreurs de compilation
Provoquer, constater et apprendre reconnatre quelques erreurs de compilation :
(a) includ au lieu de include
(b) iostrem au lieu de iostream
(c) Oublier le ; aprs std
(d) inte au lieu de int
(e) cou au lieu de cout
(f) Oublier les guillemets " fermant la chane "Hello ... "
(g) Rajouter une ligne i=3; avant le return.
A ce propos, il est utile de dcouvrir que :
Double-cliquer sur un message derreur positionne lditeur sur lerreur.
3. Erreur de linker
Il est un peu tt pour russir mettre le linker en erreur. Il est pourtant indispensable de savoir diffrencier ses messages de ceux du compilateur. En gnral,
le linker indiquera une erreur sil ne trouve pas une fonction ou des variables
parce quil manque un fichier objet ou une bibliothque. Cest aussi une erreur
sil trouve deux fois la mme fonction...
(a) Rajouter une ligne f(2); avant le return et faire Ctrl+F7. Cest pour linstant une erreur de compilation.
(b) Corriger lerreur de compilation en rajoutant une ligne (pour linstant "magique")
void f ( int i ); avant la ligne avec main. Compiler sans linker : il ny a plus
derreur. Gnrer le programme : le linker constate labsence dune fonction
f() utilise par la fonction main() quil ne trouve nulle part.
207

A.1. Lenvironnement de programmation

A. Travaux Pratiques

4. Indentations :
Avec toutes ces modifications, le programme ne doit plus tre correctement "indent". Cest pourtant essentiel pour une bonne comprhension et reprer dventuelle erreur de parenthses, accolades, etc. Le menu Edit/Advanced fournit de
quoi bien indenter.
Pour reprer des erreurs, toujours bien indenter.
Touche utile : Ctrl+K,Ctrl+F = indenter la zone slectionne.
Touche utile : Ctrl+A,Ctrl+K,Ctrl+F = tout indenter.
5. Warnings du compilateur
En modifiant le main(), provoquer les warnings suivants : 2
(a) int i ;
i =2.5;
cout << i << endl;
Excuter pour voir le rsultat.
(b) int i ;
i=4;
if ( i=3) cout << "salut" << endl;
Excuter !
(c) int i , j ;
j =i ;
Excuter (rpondre "abandonner" !)
(d) Provoquer le warning inverse : variable dclare mais non utilise.
(e) Ajouter exit; comme premire instruction de main. Appeler une fonction
en oubliant les arguments arrive souvent ! Excuter pour voir. Corriger en
mettant exit (0); . Il y a maintenant un autre warning. Pourquoi ? (La fonction exit () quitte le programme en urgence !)
Il est trs formellement dconseill de laisser passer des warnings ! Il
faut les corriger au fur et mesure. Une option du compilateur propose
mme de les considrer comme des erreurs !

A.1.3

Debugger

Savoir utiliser le debuggeur est essentiel. Il doit sagir du premier rflexe en


prsence dun programme incorrect. Cest un vritable moyen dinvestigation, plus simple et plus puissant que de truffer son programme dinstructions supplmentaires destines espionner son droulement.
1. Taper le main suivant.
i n t main ( )
{
int i , j , k ;
2. Certains de ces warnings ne se manifestent quen niveau dexigence le plus lev : pour le mettre
en place, clic droit sur le projet dans la fentre de gauche, menu Proprits, onglet C++, slectionner
le warning level 4 (3, moins exigent, est le dfaut).

208

A. Travaux Pratiques

A.1. Lenvironnement de programmation

i =2;
j =3 i ;
i f ( j ==5)
k=3;
else
k=2;
return 0;
}
2. Lancer le programme "sous le debuggeur" avec Build/Start ou F5 ou le bouton correspondant. Que se passe-til ?
3. Placer un "point darrt" en cliquant dans la colonne de gauche la hauteur de la
ligne i=2; puis relancer le debuggeur.
4. Avancer en "Step over" avec F10 ou le bouton correspondant et suivre les valeurs des variables (dans la fentre spciale ou en plaant (sans cliquer !) la souris
sur la variable).
5. A tout moment, on peut interrompre lexcution avec Stop ou Maj+F5. Arrter
lexcution avant datteindre la fin de main sous peine de partir dans la fonction
qui a appel main !
6. Placer un deuxime point darrt et voir que F5 redmarre le programme jusquau prochain point darrt rencontr.
7. Ajouter i=max(j,k); avant le return. Utiliser "Step into" ou F11 ou le bouton
correspondant quand le curseur est sur cette ligne. Constater la diffrence avec
F10.
8. Enfin, pour voir quoi ressemble du code machine, excuter jusqu un point
darrt puis faire
Debug/Windows/Disassembly. On peut aussi dans ce mme menu voir les
registres du micro-processeur. Il arrive quon se retrouve dans la fentre "code
machine" sans lavoir demand quand on debugge un programme pour lequel
on na plus le fichier source. Cet affichage est en fait trs utile pour vrifier ce que
fait le compilateur et voir sil optimise bien.

Touches utiles :

A.1.4

F5

Debug

Maj+F5

Stop

F10

Step over

F11

Step inside

Sil reste du temps

Tlcharger le programme supplmentaire Tp1_Final.zip sur la page du cours


(http://imagine.enpc.fr/~monasse/Info), jouer avec... et le complter !

A.1.5

Installer Imagine++ chez soi

Allez voir sur http://imagine.enpc.fr/~monasse/Imagine++. Si vous utilisez MacOS, apprenez faire lquivalent de ce TP sous XCode. Vous devrez utiliser
CMake pour crer un projet XCode.
209

A.2. Variables, boucles, conditions, fonctions

A. Travaux Pratiques

A.2

Variables, boucles, conditions, fonctions

A.2.1

Premier programme avec fonctions

1. Rcuprer le programme exemple :


Tlcharger larchive Tp2.zip sur la page du cours, la dcompresser sur le bureau et ouvrir la solution dans Visual. Etudier le projet Hop dont voici les sources :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

# i n c l u d e <iostream >
using namespace s t d ;
i n t plus ( i n t a , i n t b )
{
int c ;
c=a+b ;
return c ;
}
void t r i p l e 1 ( i n t a )
{
a=a 3 ;
}
void t r i p l e 2 ( i n t& a )
{
a=a 3 ;
}
i n t main ( )
{
i n t i , j =2 , k ;
i =3;
k=plus ( i , j ) ;
triple1 ( i );
triple2 ( i );
return 0;
}

2. Debugger :
Excuter le programme pas pas et tudier la faon dont les variables changent.

A.2.2

Premier programme graphique avec Imagine++

Dans ce TP et les suivants, nous utiliserons la librairie graphique dImagine++ (cf


annexe du polycopi). Elle permet de grer trs simplement le fentrage, le dessin, et
les entres-sorties clavier et souris.
1. Programme de dpart :
Etudier le programme du projet Tennis dont voici le source :
1 # i n c l u d e <Imagine/Graphics . h>
210

A. Travaux Pratiques

2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

A.2. Variables, boucles, conditions, fonctions

using namespace Imagine ;


...
// / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
/ / Fonction p r i n c i p a l e
i n t main ( )
{
/ / Ouverture de l a f e n e t r e
openWindow ( 2 5 6 , 2 5 6 ) ;
/ / P o s i t i o n e t v i t e s s e de l a b a l l e
i n t xb =128 ,
yb =20 ,
ub =2 ,
vb = 3 ;
/ / Boucle p r i n c i p a l e
while ( t r u e ) {
/ / A f f i c h a g e de l a b a l l e
f i l l R e c t ( xb 3 ,yb 3 , 7 , 7 , Red ) ;
/ / Temporisation
milliSleep (20);
/ / E f f a c e m e n t de l a b a l l e
f i l l R e c t ( xb 3 ,yb 3 , 7 , 7 , White ) ;
/ / Rebond
i f ( xb+ub >253)
ub=ub ;
/ / Mise a j o u r d e l a p o s i t i o n d e l a b a l l e
xb+=ub ;
yb+=vb ;
}
endGraphics ( ) ;
return 0;
}
Ne pas sintresser la fonction Clavier()
Gnrer puis excuter la solution. Que se passe-t-il ?

2. Aide de Visual Studio A tout moment, la touche F1 permet daccder la documentation du mot cl situ sous le curseur. Tester cette fonctionnalit sur les mots-cls
if , while et return.
Touche utile : F1 = Accder la documentation
3. Comprendre le fonctionnement du programme :
Identifier la boucle principale du programme. Elle se dcompose ainsi :
(a) Affichage de la balle
(b) Temporisation de quelques millisecondes pour que la balle ne se dplace pas
trop vite
(c) Effacement de la balle
211

A.2. Variables, boucles, conditions, fonctions

A. Travaux Pratiques

(d) Gestion des rebonds


(e) Mise jour des coordonnes de la balle
Pourquoi la ligne comportant linstruction while suscite-t-elle un warning ? A
quoi sert la condition formule par linstruction if ?
4. Gestion de tous les rebonds :
Complter le programme afin de grer tous les rebonds. Par exemple, il faut inverser la vitesse horizontale ub quand la balle va toucher les bords gauche ou
droit de la fentre.
5. Variables globales :
Doubler la hauteur de la fentre. Modifier la taille de la balle. Cela ncessite de
modifier le code plusieurs endroits. Aussi, la place de valeurs numriques
en dur, il vaut mieux dfinir des variables. Afin de simplifier et bien que a ne
soit pas toujours conseill, utiliser des variables globales constantes. Pour cela,
insrer tout de suite aprs les deux lignes dinclude le code suivant
c o n s t i n t width = 2 5 6 ;
/ / Largeur de l a f e n e t r e
const i n t height = 256;
/ / Hauteur d e l a f e n e t r e
c o n s t i n t b a l l _ s i z e = 3 ; / / Rayon d e l a b a l l e
et reformuler les valeurs numriques du programmes laide de ces variables.
Le mot cl const indique que ces variables ne peuvent tre modifies aprs leur
initialisation. Essayer de rajouter la ligne width=300; au dbut de la fonction main
et constater que cela provoque une erreur de compilation.
6. Utilisation de fonctions :
La balle est dessine deux fois au cours de la boucle, la premire fois en rouge
et la deuxime fois en blanc pour leffacer. Ici le dessin de la balle ne ncessite
quune ligne mais cela pourrait tre beaucoup plus si sa forme tait plus complexe. Aussi, pour que le programme soit mieux structur et plus lisible, et que
le code comporte le moins possible de duplications, regrouper laffichage de la
balle et son effacement dans une fonction DessineBalle dfinie avant la fonction
main :
void D e s s i n e B a l l e ( i n t x , i n t y , Color c o l ) {
...
}
De mme, dfinir une fonction
void BougeBalle(int &x,int &y,int &u,int &v)
pour grer les rebonds et le dplacement de la balle.

A.2.3

Jeu de Tennis

Nous allons rendre ce programme plus ludique en y ajoutant deux raquettes se


dplaant horizontalement en haut et en bas de lcran, et commandes par les touches
du clavier.
1. Affichage des raquettes :
Ajouter dans la fonction main des variables xr1,yr1,xr2,yr2 ddies la position
des deux raquettes. Puis dfinir une fonction DessineRaquette en prenant modle
sur DessineBalle. Placer les appels de ces fonctions aux endroits appropris dans
la boucle principale.
212

A. Travaux Pratiques

A.2. Variables, boucles, conditions, fonctions

F IGURE A.1 Mini tennis...


2. Gestion du clavier :
La gestion du clavier est ralise pour vous par la fonction Clavier dont nous
ignorerons le contenu pour linstant. Cette fonction nous permet de savoir directement si une des touches qui nous intressent (q et s pour le dplacement de la
premire raquette, k et l pour la deuxime) sont enfonces ou non. Cette fonction, Clavier(int& sens1, int& sens2), retourne dans sens1 et sens2, les valeurs 0,
-1 ou 1 (0 : pas de dplacement, -1 : vers la gauche, 1 : vers la droite).
3. Dplacement des raquettes :
Coder le dplacement dune raquette dans une fonction
void BougeRaquette(int &x, int sens)
puis appeler cette fonction dans la boucle principale pour chacune des deux raquettes. Evidemment, faire en sorte que les raquettes ne puissent sortir de la fentre.
4. Rebonds sur les raquettes :
Sinspirer de la gestion des rebonds de la balle. Ici il faut non seulement vrifier si
la balle va atteindre le bas ou le haut de lcran mais aussi si elle est assez proche
en abscisse de la raquette correspondante.
5. Comptage et affichage du score :
Modifier la fonction BougeBalle afin de comptabiliser le score des deux joueurs
et lafficher dans la console.
6. Pour ceux qui ont fini :
Lors dun rebond sur la raquette, modifier linclinaison de la trajectoire de la balle
en fonction de la vitesse de la raquette ou de lendroit de frappe.
Vous devriez avoir obtenu un programme ressemblant celui de la figure A.1.

213

A.3. Tableaux

A. Travaux Pratiques

F IGURE A.2 Master mind la console...

A.3

Tableaux

Dans ce TP, nous allons programmer un jeu de Mastermind, o lutilisateur doit


deviner une combinaison gnre alatoirement par lordinateur. Le joueur dispose
dun nombre dtermin dessais. A chaque essai dune combinaison, lordinateur fournit deux indices : le nombre de pions correctement placs et le nombre de pions de la
bonne couleur mais incorrectement positionns.

A.3.1

Mastermind Texte

1. Rcuprer la solution de dpart :


Tlcharger larchive Tp3_Initial.zip sur la page du cours, la dcompresser dans un rpertoire faisant apparatre les noms des deux lves et ouvrir la
solution MasterMind dans Visual Studio. Etudier le projet Mastermind.
2. Reprsenter une combinaison :
Nous prendrons ici une combinaison de 5 pions de 4 couleurs diffrentes. La
couleur dun pion sera reprsente par un entier compris entre 0 et 3. Pour une
combinaison de 5 pions, nous allons donc utiliser un tableau de 5 entiers.
i n t combin [ 5 ] ; / / t a b l e a u d e 5 e n t i e r s
3. Afficher une combinaison :
Programmer une fonction permettant dafficher une combinaison donne lcran.
La manire la plus simple de faire consistera faire afficher les diffrents chiffres
de la combinaison sur une mme ligne les uns la suite des autres.
4. Gnrer une combinaison alatoirement :
En dbut de partie, lordinateur doit gnrer alatoirement une combinaison
faire deviner lutilisateur. Nous allons pour cela utiliser les fonctions dclares
dans le fichier cstdlib , notamment la fonction rand() permettant de gnrer un
nombre au hasard entre 0 et RAND_MAX. Afin dobtenir un nombre entre 0 et n,
on procdera de la manire suivante :
x = rand ()%n ;
214

A. Travaux Pratiques

A.3. Tableaux

Pour que la squence de nombres gnre ne soit pas la mme dune fois sur
lautre, il est ncessaire dinitialiser le gnrateur avec une graine variable. La
manire la plus simple de procder consiste utiliser lheure courante. La fonction time() dclare dans le fichier ctime permet de lobtenir.
En fin de compte, la fonction suivante nous permet donc de gnrer une combinaison :
# include <cstdlib >
# i n c l u d e <ctime >
using namespace s t d ;
void genereCombinaison ( i n t combin [ 5 ] )
{
srand ( ( unsigned i n t ) time ( 0 ) ) ; / / i n i t i a l i s a t i o n
//
du g e n e r a t e u r
f o r ( i n t i = 0 ; i < 5 ; ++ i )
combin [ i ] = rand ( ) % 4 ;
/ / a p p e l s au g e n e r a t e u r
}
5. Changer la complexit du jeu :
Rapidement, vous allez devenir des experts en Mastermind. Vous allez alors vouloir augmenter la difficult. Il suffira alors dallonger la longueur de la combinaison, ou daugmenter le nombre de couleurs possibles. Cela vous est dores et dj
trs facile si vous avez pens dfinir une constante globale pour chacune de ces
deux grandeurs. Si ce nest pas le cas, il est grand temps de le faire. Dfinissez
par exemple :
c o n s t i n t nbcases = 5 ;
c o n s t i n t nbcoul = 4 ;

/ / longueur de l a combinaison
/ / nombre d e c o u l e u r s d i f f r e n t e s

Reprenez le code que vous avez dj crit en utilisant ces constantes. Il est trs
important de stocker les paramtres constants dans des variables, cela fait gagner
beaucoup de temps lorsque lon veut les modifier.
6. Saisie dune combinaison au clavier :
La fonction suivante, que nous vous demanderons dadmettre, saisit une chane
de caractres (string) au clavier et remplit le tableau combi[] avec les chiffres que
les nbcases premiers caractres de la chane reprsentent.
void getCombinaison ( i n t combi [ nbcases ] )
{
cout << " Votre e s s a i : " ;
string s ;
c i n >> s ;
f o r ( i n t i = 0 ; i <nbcases ; i ++)
combi [ i ]= s [ i ] 0 ;
}
Dans le cadre de notre Mastermind, il sagit de modifier cette fonction pour
quelle contrle que la chane rentre est bien de bonne taille et que les chiffres
sont bien entre 0 et nbcoul1. Lessai devra tre redemand jusqu ce que la
combinaison soit valide. On utilisera entre autres la fonction s . size () qui re215

A.3. Tableaux

A. Travaux Pratiques

tourne la taille de la chane s (la syntaxe de cette fonction sera comprise plus
tard dans le cours...)
7. Traitement dune combinaison :
Il faudrait maintenant programmer une fonction comparant une combinaison
donne avec la combinaison trouver. Cette fonction devrait renvoyer deux valeurs : le nombre de pions de la bonne valeur bien placs, puis, dans les pions
restant, le nombre de pions de la bonne valeur mais mal placs.
Par exemple, si la combinaison trouver est 02113 :
00000 : 1 pion bien plac (0xxxx), 0 pion mal plac (xxxxx)
20000 : 0 pion bien plac (xxxxx), 2 pions mal placs (20xxx)
13133 : 2 pions bien placs (xx1x3), 1 pion mal plac (1xxxx)
13113 : 3 pions bien placs (xx113), 0 pion mal plac (xxxxx)
12113 : 4 pions bien placs (x2113), 0 pion mal plac (xxxxx)
...
Pour commencer et pouvoir tout de suite tester le jeu, programmer une fonction
renvoyant uniquement le nombre de pions bien placs.
8. Boucle de jeu : Nous avons maintenant notre disposition toutes les briques ncessaires 3 , il ny a plus qu les assembler pour former un jeu de mastermind.
Pensez par ailleurs ajouter la dtection de la victoire (quand tous les pions sont
bien placs), et celle de la dfaite (quand un nombre limite dessais a t dpass).
9. Version complte : Complter la fonction de traitement dune combinaison pour
quelle renvoie galement le nombre de pions mal placs.

A.3.2

Mastermind Graphique

Le jeu de Mastermind que nous venons de raliser reste malgr tout trs peu convivial. Nous allons y remdier en y ajoutant une interface graphique.
1. Etude du projet de dpart :
Passer dans le projet MastermindGraphique. Penser le dfinir comme projet de dmarrage pour que ce soit lui qui se lance lexcution (son nom doit
apparatre en gras dans la liste des projets).
Les fonctions graphiques sont dj dfinies. Elles fonctionnent selon un principe
de division de la fentre graphique en lignes. La fonction :
void a f f i c h e C o m b i n a i s o n ( i n t combi [ nbcases ] , i n t n ) ;
permet dafficher la combinaison combi sur la ligne n. Au dbut du programme,
on laisse en haut de la fentre graphique autant de lignes libres que le joueur a
dessais pour y afficher le droulement du jeu. On affiche en bas de la fentre
graphique un mini mode demploi qui rsume les correspondances entre touches
et couleurs.
2. Mastermind graphique :
Rinsrer dans ce projet les fonctions de gnration alatoire dune combinaison
et de comparaison de deux comparaisons crites prcdemment. Puis reprogrammer la boucle principale du jeu en utilisant laffichage graphique.
3. mme si la fonction de traitement dune combinaison est pour linstant incomplte

216

A. Travaux Pratiques

A.3. Tableaux

F IGURE A.3 Master mind graphique...


3. Ultime amlioration :
On souhaite pouvoir effacer une couleur aprs lavoir tape, au cas o lon se
serait tromp. Etudier les fonctions
int Clavier ( ) ;
et
void getCombinaison ( i n t [ ] , i n t ) ;
La premire prend dj en compte la touche Retour arrire, mais pas la seconde
qui la considre comme une erreur de frappe. Modifier cette dernire en consquence.

217

A.4. Structures

A.4

A. Travaux Pratiques

Structures

Avertissement : Dans ce TP, nous allons faire voluer des corps soumis la gravitation, puis leur faire subir des chocs lastiques. Il sagit dun long TP qui nous occupera
deux semaines. Les sections 11 et 15 ne sont donnes que pour les lves les plus
laise et ne seront abordes quen deuxime semaine. En section A.4.2 sont dcrites
quelques-unes des fonctions utiliser, et en A.4.3 leur justification physique.

A.4.1

Etapes

Mouvement de translation
1. Pour commencer, tudier le projet :
Tlcharger le fichier TP4.zip sur la page habituelle, le dcompresser et lancer Visual C++. Parcourir le projet, en sattardant sur les variables globales et la
fonction main (inutile de regarder le contenu des fonctions dj dfinies mais
non utilises). Le programme fait voluer un point (x, y) selon un mouvement
de translation constante (vx, vy), et affiche rgulirement un disque centr en
ce point. Pour ce faire, afin de leffacer, on retient la position du disque au dernier affichage (dans ox et oy) ; par ailleurs, deux instructions commenant par
NoRefresh sont places autour des instructions graphiques afin dacclrer laffichage.
2. Utiliser une structure :
Modifier le programme de faon utiliser une structure Balle renfermant toute
linformation sur le disque (position, vitesse, rayon, couleur).
3. Fonctions daffichage :
Crer (et utiliser) une fonction void AfficheBalle(Balle D) affichant le disque D,
et une autre
void EffaceBalle(Balle D) leffaant.
4. Faire bouger proprement le disque :
Pour faire voluer la position du disque, remplacer les instructions correspondantes dj prsentes dans main par un appel une fonction qui modifie les
coordonnes dune Balle, en leur ajoutant la vitesse de la Balle multiplie par
un certain pas de temps dfini en variable globale (dt = 1 pour linstant).
Gravitation
5. volution par acclration :
Crer (et utiliser) une fonction qui modifie la vitesse dune Balle de faon lui
faire subir une attraction constante ax = 0 et ay = 0.0005. Indice : procder comme
prcdemment, cest--dire ajouter 0.0005 dt vy ...
6. Ajouter un soleil :
On souhaite ne plus avoir une gravit uniforme. Ajouter un champ dcrivant la
masse la structure Balle. Crer un soleil (de type Balle), jaune, fixe (ie de
vitesse nulle) au milieu de la fentre, de masse 10 et de rayon 4 pixels (la masse
de la plante qui bouge tant de 1). Lafficher.
7. Acclration gravitationnelle :
Crer (et utiliser la place de la gravitation uniforme) une fonction qui prend en
218

A. Travaux Pratiques

A.4. Structures

argument la plante et le soleil, et qui fait voluer la position de la plante. Rappel

de physique : lacclration prendre en compte est G mS /r3


r , avec ici G = 1
(Vous aurez sans doute besoin de la fonction double sqrt(double x), qui retourne
la racine carre de x). Ne pas oublier le facteur dt... Faire tourner et observer.
Essayez diverses initialisations de la plante (par exemple x = largeur/2, y =
hauteur/3, vx = 1 , vy = 0). Notez que lexpression de lacclration devient
trs grande lorsque r sapproche de 0 ; on prendra donc garde ne pas utiliser ce
terme lorsque r devient trop petit.
8. Initialisation alatoire :
Crer (et utiliser la place des conditions initiales donnes pour le soleil) une
fonction initialisant une Balle, sa position tant dans la fentre, sa vitesse nulle,
son rayon entre 5 et 15, et sa masse valant le rayon divis par 20. Vous aurez
probablement besoin de la fonction Random...
9. Des soleils par milliers...
Placer 10 soleils alatoirement (et en tenir compte laffichage, dans le calcul du
dplacement de lastrode...).
10. Diminuer le pas de temps de calcul :
Afin dviter les erreurs dues la discrtisation du temps, diminuer le pas de
temps dt, pour le fixer 0.01 (voire 0.001 si la machine est assez puissante).
Rgler la frquence daffichage en consquence (inversement proportionnelle
dt). Lancer plusieurs fois le programme.
Chocs lastiques simples
11. Faire rebondir lastrode :
Faire subir des chocs lastiques lastrode chaque fois quil sapproche trop
dun soleil, de faon ce quil ne rentre plus dedans (fonction ChocSimple), et
rtablir dt une valeur plus leve, par exemple 0.1 (modifier la frquence daffichage en consquent). Pour savoir si deux corps sont sur le point dentrer en
collision, utiliser la fonction Collision.
Jeu de tir
(figure A.4 droite)
12. Ouvrir un nouveau projet :
Afin de partir dans deux voies diffrentes et travailler proprement, nous allons
ajouter un nouveau projet Imagine++, appel Duel, dans cette mme solution.
Recopier (par exemple par copier/coller) intgralement le contenu du fichier
Tp4.cpp dans un fichier Duel.cpp. Une fois cette copie faite, modifier le fichier
CMakeLists.txt en ajoutant deux lignes indiquant que lexcutable Duel dpend de Duel.cpp et utilise la bibliothque Graphics dImagine++.
13. vous de jouer !
Transformer le projet Duel, laide des fonctions qui y sont dj prsentes, en un
jeu de tir, deux joueurs. Chacun des deux joueurs a une position fixe, et divers
soleils sont placs alatoirement dans lcran. Chaque joueur, tour de rle, peut
lancer une Balle avec la vitesse initiale de son choix, la balle subissant les effets
de gravitation des divers soleils, et disparaissant au bout de 250 pas de temps
219

A.4. Structures

A. Travaux Pratiques

F IGURE A.4 Corps clestes et jeu de tir...


daffichage. Le gagnant est le premier qui russit atteindre lautre... Conseils
pratiques : positionner symtriquement les joueurs par rapport au centre, de prfrence mi-hauteur en laissant une marge dun huitime de la largeur sur le
ct ; utiliser la fonction GetMouse pour connatre la position de la souris ; en dduire la vitesse dsire par le joueur en retranchant ces coordonnes celles du
centre de la boule lancer, et en multipliant par un facteur 0.00025.
14. Amliorations :
Faire en sorte quil y ait systmatiquement un gros soleil au centre de lcran (de
masse non ncessairement consquente) afin dempcher les tirs directs.
15. Initialisation correcte :
Modifier la fonction de placement des soleils de faon ce que les soleils ne sintersectent pas initialement, et quils soient une distance minimale de 100 pixels
des emplacements des joueurs.
Chocs lastiques
(figure A.4 gauche)
16. Tout faire voluer, tout faire rebondir :
On retourne dans le projet Gravitation. Tout faire bouger, y compris les soleils.
Utiliser, pour les chocs lastiques, la fonction Chocs (qui fait rebondir les deux
corps). Faire en sorte que lors de linitialisation les soleils ne sintersectent pas.

A.4.2

Aide

Fonctions fournies :
void InitRandom ( ) ;
est excuter une fois avant le premier appel Random.
double Random ( double a , double b ) ;
renvoie un double alatoirement entre a et b (compris). Excuter une fois InitRandom();
avant la premire utilisation de cette fonction.
220

A. Travaux Pratiques

A.4. Structures

void ChocSimple ( double x , double y , double &vx , double &vy , double m,


double x2 , double y2 , double vx2 , double vy2 ) ;
fait rebondir la premire particule, de coordonnes (x, y), de vitesse (vx, vy) et de
masse m, sur la deuxime, de coordonnes (x2, y2) et de vitesse (vx2, vy2), sans dplacer la deuxime.
void Choc ( double x , double y , double &vx , double &vy , double m,
double x2 , double y2 , double &vx2 , double &vy2 , double m2 ) ;
fait rebondir les deux particules lune contre lautre.
bool C o l l i s i o n ( double x1 , double y1 , double vx1 , double vy1 , double r1 ,
double x2 , double y2 , double vx2 , double vy2 , double r 2 ) ;
renvoie true si le corps de coordonnes (x1, y1), de vitesse (vx1, vy1) et de rayon r1
est sur le point dentrer en collision avec le corps de coordonnes (x2, y2), de vitesse
(vx2, vy2) et de rayon r2, et false sinon.

A.4.3

Thorie physique

NB : Cette section nest donne que pour expliquer le contenu des fonctions prprogrammes fournies avec lnonc. Elle peut tre ignore en premire lecture.
Acclration
La somme des forces exerces sur un corps A est gale au produit de sa masse par
lacclration de son centre de gravit.
X

F i/A = mA
a G(A)
i

Gravitation universelle
Soient deux corps A et B. Alors A subit une force dattraction

1
u BA .
F B/A = GmA mB 2
dA,B
Chocs lastiques
Soient A et B deux particules rentrant en collision. Connaissant tous les paramtres
avant le choc, comment dterminer leur valeur aprs ? En fait, seule la vitesse des particules reste calculer, puisque dans linstant du choc, les positions ne changent pas.
Durant un choc dit lastique, trois quantits sont conserves :

1. la quantit de mouvement P = m
v +m
v
A

2. le moment cintique M = mA
rA
v A + mB
rB
v B (qui est un rel dans le
cas dun mouvement plan).
3. lnergie cintique Ec = 21 mA vA2 + 12 mB vB2 .
Ce qui fait 4 quations pour 4 inconnues.
221

A.4. Structures

A. Travaux Pratiques

Rsolution du choc
On se place dans le rfrentiel du centre de masse. On a alors, tout instant :

1. P = 0 (par dfinition de ce rfrentiel), do mA


v A = mB
v B.

2. M = ( r A r B ) mA v A , do, en notant r = r A r B , M =
r mA
v A.
3. 2Ec = mA (1 +

mA
)vA2 .
mB

La constance de Ec nous informe que dans ce repre, la norme des vitesses est
conserve, et la constance du moment cintique que les vitesses varient paralllement


r . Si lon veut que les vitesses varient effectivement, il ne nous reste plus quune

possibilit : mutliplier par 1 la composante des


v i selon
r . Ce qui fournit un algorithme simple de rebond.
Dcider de limminence dun choc
On ne peut pas se contenter, lors de lvolution pas pas des coordonnes des
disques, de dcider quun choc aura lieu entre t et t + dt rien quen estimant la distance
entre les deux disques candidats la collision linstant t, ni mme en prenant en plus
en considration cette distance linstant t + dt, car, si la vitesse est trop leve, un
disque peut dj avoir travers lautre et en tre ressorti en t + dt... La solution consiste
expliciter le minimum de la distance entre les disques en fonction du temps, variant
entre t et t + dt.

Soit N (u) = (
r A (u)
r B (u))2 le carr de la distance en question. On a :

N (u) = (
r A (t)
r B (t) + (u t)(
v A (t)
v B (t)))2
Ce qui donne, avec des notations supplmentaires :

N (u) =
r (t)2 + 2(u t)
r (t)
v (t) + (u t)2
v (t)2
La norme, toujours positive, est minimale au point u tel que u N (u) = 0, soit :
(tm t) =

r (t)
v (t)

2
v (t)

Donc :
1. si tm < t, le minimum est atteint en t,
2. si t < tm < t + dt, le minimum est atteint en tm ;
3. sinon, t + dt < tm , le minimum est atteint en t + dt.
Ce qui nous donne explicitement et simplement la plus petite distance atteinte entre
les deux corps entre t et t + dt.

222

A. Travaux Pratiques

A.5

A.5. Fichiers spars

Fichiers spars

Nous allons poursuivre dans ce TP les simulations de gravitation et de chocs lastiques entames la semaine dernire, en sparant dans diffrents fichiers les diffrentes
fonctions et structures utilises.
1. De bonnes bases :
Tlcharger le fichier Tp5.zip sur la page habituelle, le dcompresser et lancer
Visual C++. Le projet Gravitation contient une solution partielle au TP4 (jusqu
la question 7, incluse). Si vous avez t plus loin et/ou si vous prfrez rutiliser votre propre solution, vous pouvez quitter Visual C++, remplacer le fichier
Gravitation.cpp par celui que vous aurez rcupr dans votre TP4, et relancer Visual C++.

A.5.1

Fonctions outils

2. Un fichier de dfinitions...
Ajouter un nouveau fichier source nomm Tools.cpp au projet avec "Fichier
/ Ajouter un nouvel lment / Fichier C++". Y placer les fonctions
fournies lavance au dbut du TP4 (InitRandom, Random, Choc, ChocSimple
et Collision), en les retirant de Gravitation.cpp. Ne pas oublier les lignes suivantes, que lon pourra retirer de Gravitation.cpp :
# include <cstdlib >
# i n c l u d e <ctime >
using namespace s t d ;
Attention, le nouveau fichier est cr dans le dossier contenant le fichier "solution" (extension sln) de Visual Studio qui se trouve dans le "build directory"
de CMake. Vous devez donc dplacer ce fichier dans le dossier des sources (le
mme que Gravitation.cpp). Noubliez pas de modifier les arguments de
AddImagineExecutable du CMakeLists.txt pour y ajouter Tools.cpp.
Vous navez pas besoin de relancer CMake ou de fermer Visual Studio. Celui-ci
va dtecter le changement du CMakeLists.txt et vous proposer de recharger
le projet. Acceptez sa proposition, il relance CMake en coulisses.
3. ... et un fichier de dclarations
Ajouter un nouveau fichier den-tte nomm Tools.h. Inclure la protection contre
la double inclusion vue en cours (#pragma once). Y placer les dclarations des
fonctions mises dans Tools.cpp, ainsi que la dfinition de dt, en retirant celle-ci
de main. Rajouter au dbut de Tools.cpp et de Gravitation.cpp un #include "Tools.h".

A.5.2

Vecteurs

4. Structure Vector :
Crer dans un nouveau fichier Vector.h une structure reprsentant un vecteur
du plan, avec deux membres de type double. Ne pas oublier le mcanisme de
protection contre la double inclusion. Dclarer (et non dfinir) les oprateurs et
fonction suivants :
223

A.5. Fichiers spars

Vector
Vector
double
Vector
Vector

A. Travaux Pratiques

o p e r a t o r +( Vector a , Vector b ) ;
o p e r a t o r ( Vector a , Vector b ) ;
norme2 ( Vector a ) ;
o p e r a t o r ( Vector a , double lambda ) ;
o p e r a t o r ( double lambda , Vector a ) ;

//
//
//
//
//

Somme d e deux v e c t e u r s
D i f f r e n c e d e deux v e c t e
Norme e u c l i d i e n n e d un v
M u l t i p l i c a t i o n p a r un s c
M u l t i p l i c a t i o n p a r un s c

5. Fonctions et oprateurs sur les Vector :


Crer un nouveau fichier Vector.cpp. Mettre un #include du fichier den-tte
correspondant et dfinir les oprateurs qui y sont dclars (Rappel : sqrt est
dfini dans le fichier den-tte systme <cmath> ; ne pas oublier non plus le
using namespace std; qui permet dutiliser cette fonction). Astuce : une fois quune
version de operator est dfinie, la deuxime version peut utiliser la premire
dans sa dfinition...
6. Vecteur vitesse et vecteur position :
Systmatiquement remplacer dans Gravitation.cpp les vitesses et positions
par des objets de type Vector (y compris dans la dfinition de la structure Balle).
Utiliser autant que possible les oprateurs et fonction dfinis dans Vector.cpp.

A.5.3

Balle part

7. Structure Balle :
Dplacer la structure Balle dans un nouveau fichier den-tte Balle.h. Puisque
Balle utilise les types Vector et Color, il faut aussi ajouter ces lignes :
# i n c l u d e <Imagine/Graphics . h>
using namespace Imagine ;
# i n c l u d e " Vector . h "

8. Fonctions associes :
Dplacer toutes les fonctions annexes prenant des Balle en paramtres dans un
nouveau fichier Balle.cpp. Il ne devrait plus rester dans Gravitation.cpp
dautre fonction que main. Dclarer dans Balle.h les fonctions dfinies dans
Balle.cpp. Ajouter les #include ncessaires dans ce dernier fichier et dans Gravitation.cp
et faire les adaptations ncessaires (par exemple, si des fonctions utilisent largeur
ou hauteur, comme ces constantes ne sont dfinies que dans Gravitation.cpp,
il faut les passer en argument...)

A.5.4

Retour la physique

9. Des soleils par milliers... :


Placer 10 soleils alatoirement (et en tenir compte laffichage, dans le calcul du
dplacement de lastrode...).
10. Diminuer le pas de temps de calcul :
Afin dviter les erreurs dues la discrtisation du temps, diminuer le pas de
temps dt, pour le fixer 0.01 (voire 0.001 si la machine est assez puissante).
Rgler la frquence daffichage en consquence (inversement proportionnelle
dt). Lancer plusieurs fois le programme.
224

A. Travaux Pratiques

A.5. Fichiers spars

Chocs lastiques simples


11. Faire rebondir lastrode :
Faire subir des chocs lastiques lastrode chaque fois quil sapproche trop
dun soleil, de faon ce quil ne rentre plus dedans (fonction ChocSimple),
et rtablir dt une valeur plus leve, par exemple 0.1 (modifier la frquence
daffichage en consquent). Pour savoir si deux corps sont sur le point dentrer
en collision, utiliser la fonction Collision.

Jeu de tir
12. Ouvrir un nouveau projet :
Afin de partir dans deux voies diffrentes et travailler proprement, ajouter un
nouveau projet appel Duel, dans cette mme solution. On ajoute un dossier
Duel au mme niveau que Gravitation et on modifie le CMakeLists.txt.
13. Ne pas refaire deux fois le travail : Comme nous aurons besoins des mmes fonctions
dans ce projet que dans le projet Gravitation, ajouter au projet (sans en crer de
nouveaux !) les fichiers Vector.h, Vector.cpp, Balle.h, Balle.cpp, Tools.h,
Tools.cpp. Les fichiers sont les mmes que dans le projet Gravitation, ils ne sont
pas recopis. Mettre au dbut de Duel.cpp (fichier placer dans le rpertoire
Duel) les #include correspondants. Essayer de compiler Duel.cpp. Comme le
compilateur narrive pas trouver les fichiers inclus, qui ne sont pas dans le
mme rpertoire, il faut lui indiquer o les trouver. (#include " ../ Gravitation/Tools.h"
par exemple). Pour le CMakeLists.txt du rpertoire Duel, inspirez-vous de celui de Gravitation.
14. vous de jouer !
Transformer le projet Duel, laide des fonctions dfinies auparavant, en un jeu
de tir, deux joueurs. Chacun des deux joueurs a une position fixe, et divers
soleils sont placs alatoirement dans lcran. Chaque joueur, tour de rle, peut
lancer une Balle avec la vitesse initiale de son choix, la balle subissant les effets
de gravitation des divers soleils, et disparaissant au bout de 250 pas de temps
daffichage. Le gagnant est le premier qui russit atteindre lautre... Conseils
pratiques : positionner symtriquement les joueurs par rapport au centre, de prfrence mi-hauteur en laissant une marge dun huitime de la largeur sur le
ct ; utiliser la fonction GetMouse pour connatre la position de la souris ; en
dduire la vitesse dsire par le joueur en retranchant ces coordonnes celles
du centre de la boule lancer, et en multipliant par un facteur 0.00025.
15. Amliorations :
Faire en sorte quil y ait systmatiquement un gros soleil au centre de lcran (de
masse non ncessairement consquente) afin dempcher les tirs directs.
16. Initialisation correcte :
Modifier la fonction de placement des soleils de faon ce que les soleils ne sintersectent pas initialement, et quils soient une distance minimale de 100 pixels
des emplacements des joueurs.
225

A.5. Fichiers spars

A. Travaux Pratiques

F IGURE A.5 Corps clestes et jeu de tir...


Chocs lastiques
17. Tout faire voluer, tout faire rebondir :
On retourne dans le projet Gravitation. Tout faire bouger, y compris les soleils.
Utiliser, pour les chocs lastiques, la fonction Chocs (qui fait rebondir les deux
corps). Faire en sorte que lors de linitialisation les soleils ne sintersectent pas.

226

A. Travaux Pratiques

A.6. Les tris

F IGURE A.6 Deux tris en cours dexcution : tri bulle et Quicksort...

A.6

Les tris

Dans ce TP, nous allons programmer quelques algorithmes de tri dlments dans
un tableau. Pour que cela soit interactif, et que lon puisse voir comment se passent
chacun des tris, une interface graphique a t programme, qui affiche le tableau et
permet de visualiser les oprations quon ralise dessus (figure A.6).

A.6.1

Mlanger un tableau

1. Pour commencer, tudier le projet :


Tlcharger le fichier Tp6_Initial.zip sur la page habituelle, le dcompresser
et lancer Visual C++. Le projet est spar entre le fichier main.cpp, dans lequel
on programmera les algorithmes de tri, et le couple (tools.cpp, tools.h), qui
gre linterface graphique et quelques fonctions utiles la comparaison des diffrents tris.
Ouvrir tools.h pour dcouvrir les fonctions de cette interface graphique, puis
main.cpp pour voir comment elles sont utilises (les lignes commentes sont
l pour montrer comment vous utiliserez les fonctions melange_tableau et
tri_selection que vous allez programmer).
Excuter le projet. Pour linstant, il ne fait quinitialiser un tableau et lafficher.
2. Accs un tableau, change de 2 valeurs
Pour que les oprations que vous effectuerez sur les tableaux soient affiches
automatiquement, il faudra que vous utilisiez uniquement les fonctions valeur
et echange dclares dans tools.h (ne pas accder directement au tableau avec
T[i]).
Entranez-vous les utiliser dans la fonction main(), en accdant une valeur du
tableau T0, et en permutant 2 de ses lments.
3. Scuriser les accs tableau
Pour sassrer quon ne sort pas des bornes du tableaux, ajouter des appels
la fonction assert (vue en cours) dans valeur et echange. Constater ce qui se
passe lexcution lorsquon appelle par exemple
227

A.6. Les tris

A. Travaux Pratiques

v a l e u r ( T0 , t a i l l e , t a i l l e ) ;
4. Mlanger un tableau
Une fonction dclare dans tools.h nexiste pas encore dans tools.cpp : la
fonction
void melange_tableau ( double T [ ] , i n t t a i l l e )
qui, comme son nom lindique, mlange un tableau. Ajoutez-la (dans tools.cpp
bien sr)
Ide : le mlange le plus rapide (et le plus efficace) consiste parcourir le tableau
une fois, et permuter chacun des lments avec un autre choisi au hasard (utiliser la fonction int random(int a) dfinie dans tools.cpp pour tirer un entier
entre 0 et a-1).

A.6.2

Tris quadratiques

Les 3 algorithmes de tri qui suivent trient un tableau en temps 0(n2 ), cest dire
que le temps pour trier un tableau est proportionnel au carr de la taille de ce
tableau.
Si vous avez peu de temps, vous pouvez ne faire quun ou deux des trois, pour
pouvoir toucher galement au tri Quicksort.
5. Tri slection
Cest le tri le plus naf. Il consiste parcourir le tableau une premire fois pour
trouver le plus petit lment, mettre cet lment au dbut (par une permutation),
parcourir une seconde fois le tableau (de la 2me la dernire case) pour trouver
le second plus petit lment, le placer en 2me position, et ainsi de suite...
Programmer la fonction void tri_selection (double T[], int taille ). Vous pouvez alors dcommenter les lignes commentes dans main() et excuter.
6. Tri insertion
En gnral, cest peu prs lalgorithme quutilise un tre humain pour trier un
paquet de cartes, des fiches...
Il consiste ajouter un un les lments non tris au bon endroit parmi ceux qui
sont dj tris : si ncessaire on change les 2 premiers lments du tableau pour
les mettre dans lordre, puis (si ncessaire) on dplace le 3me lment vers la
gauche, par des changes de proche en proche, jusqu ce quil soit la bonne
position par rapport aux 2 premiers, puis le 4me, et ainsi de suite...
Programmer void tri_insertion (double T[], int taille ) et regarder comment a
se passe.
7. Tri bulle
Le tri bulle consiste parcourir n fois le tableau, et chaque fois quon est sur un
lment, on lchange avec son voisin de droite si ce dernier est plus petit que lui.
Programmer tri_bulle(double T[], int taille) et regarder comment
a se passe. Constater quon nest pas oblig de parcourir tout le tableau chaque
fois et amliorer la fonction.
228

A. Travaux Pratiques

A.6.3

A.6. Les tris

Quicksort

Lalgorithme :
le tri Quicksort adopte la stratgie diviser pour rgner qui consiste rduire le
problme du tri dun tableau de taille n aux tris de 2 tableaux de taille n2 :
on choisit un lment dans le tableau (on le prend en gnral au hasard, mais
par commodit on prendra ici le premier lment du tableau) quon appelle pivot. On spare ensuite les autres lments entre ceux infrieurs au pivot et ceux
suprieurs au pivot. Il ny a plus alors qu trier ces deux moitis.
8. Pivot
Crer une fonction int pivot(double T[], int taille ) qui prend comme pivot le premier lment du tableau, change les lments du tableau de manire ce quon aie dabord les lments infrieurs au pivot, puis le pivot,
puis les lments suprieurs au pivot, et qui renvoie la nouvelle position du
pivot.
Ne pas oublier dexcuter la fonction pour vrifier quelle marche bien !
Ide :
on utilise 2 index qui parcourent le tableau, le premier partir du 2me
lment (le 1er tant le pivot) et avanant vers la droite, le second
partir du dernier lment et avanant vers la gauche
le premier index sarrte sur le premier lment suprieur au pivot quil
rencontre, et le second index, sur le premier lment infrieur au pivot
quil rencontre
on change alors les 2 lments, et les 2 index continuent davancer, et
ainsi de suite
quand les 2 index se rencontrent, gauche de lintersection tous les lments sont infrieurs au pivot, et droite ils sont suprieurs
on change le pivot (en 1re position) avec le dernier des lments infrieurs pour obtenir ce quon dsire
Attention, des diffrences apparemment anodines dans la faon de programmer cette fonction peuvent la faire chouer dans des cas particuliers : demandezvous ce qui se passe si le pivot est la valeur minimale ou la valeur maximale
du tableau.
9. Fonctions rcursives
Le principe mme de la statgie diviser pour rgner implique que la fonction qui
effectue le tri quicksort va sappeler elle-mme pour trier une sous-partie du tableau. En pratique, on va utiliser 2 arguments debut et fin qui indiquent quon
ne ralise le tri quentre les indices debut et fin.
Changer la fonction pivot en lui ajoutant ces 2 arguments, et en ne la faisant
effectivement travailler que entre ces 2 indices (par exemple, le pivot est initialement lindice debut)
10. Quicksort
On peut maintenant crire une fonction
void quicksort_recursif (double T[], int taille , int debut, int fin ),
qui contient lalgorithme, ainsi que la fonction
void quicksort(double T [], int taille ),
229

A.6. Les tris

A. Travaux Pratiques

qui ne fait quappeler cette dernire, mais qui sera celle quon utilisera dans
main() (car plus simple).

A.6.4

Gros tableaux

Maintenant quon a vu graphiquement comment marchent ces algorithmes, il est


intressant de les faire fonctionner et de les comparer sur des tableaux de grande
taille.
11. Tableaux de taille variable
Si on veut tester nos algorithmes sur des tableaux de grande taille, il faut utiliser
des tableaux de taille variable. Remplacer toutes les lignes de type double t[ taille ];
par double t = new double[taille], et la fin des fonctions o se trouvent ces dclarations, ajouter la ligne delete [] t ;.
Dautre part il faut dsactiver laffichage :
init_tools (512, false ).
12. Nombre de lectures et dcriture
Pour comparer plus rigoureusement 2 algorithmes, on peut comparer le nombre
de lectures du tableau et le nombre dcritures dans le tableau.
Crer 2 variables globales dans tools.cpp et modifier les fonctions init_tri,
valeur, echange et fin_tri pour initialiser, compter et afficher le nombre de
lectures et dcritures. Au fait, en combien doprations seffectue en moyenne
Quicksort ?
13. Temps de calcul
Il est intressant galement davoir les temps de calcul exacts. Pour cela, on peut
enregistrer dans une nouvelle variable globale timer0 le temps quil est avant le
tri :
timer0 = double(clock())/CLOCKS_PER_SEC ;
et la retrancher au temps quil est aprs le tri (modifier les fonctions init_tri
et fin_tri pour faire ce calcul et lafficher).
14. Mode Release
On peut changer le mode de compilation en le passant de Debug Release. Vrifier que lexcution des algorithmes est effectivement bien plus rapide en mode
Release !

230

A. Travaux Pratiques

A.7

A.7. Images

Images

F IGURE A.7 Deux images et diffrents traitements de la deuxime (ngatif, flou, relief,
dformation, contraste et contours).
Dans ce TP, nous allons jouer avec les tableaux bidimensionnels statiques (mais
stocks dans des tableaux 1D) puis dynamiques. Pour changer de nos passionnantes
matrices, nous travaillerons avec des images (figure A.7).

A.7.1

Allocation

1. Rcuprer le projet :
Tlcharger le fichier Tp7_Initial.zip sur la page habituelle, le dcompresser
et lancer Visual C++.
2. Saturer la mmoire :
Rien voir avec ce quon va faire aprs mais il faut lavoir fait une fois... Faire,
dans une boucle infinie, des allocations de 1000000 entiers sans dsallouer et regarder la taille du process grandir. (Utiliser Ctrl+Shift+Echap pour accder
au gestionnaire de tches). Compiler en mode Release pour utiliser la "vraie" gestion du tas (Le mode Debug utilise une gestion spcifique qui aide trouver les
bugs et se comporte diffremment...)

A.7.2

Tableaux statiques

3. Niveaux de gris :
Une image noir et blanc est reprsente par un tableau de pixels de dimensions
constantes W=300 et H=200. Chaque pixel (i,j) est un byte (entier de 0 255)
allant de 0 pour le noir 255 pour le blanc. Lorigine est en haut gauche, i est
lhorizontale et j la verticale. Dans un tableau de byte mono-dimensionnel t de
taille W*H mmorisant le pixel (i,j) en t[i+W*j] :
231

A.7. Images

A. Travaux Pratiques

Stocker une image noire et lafficher avec putGreyImage(0,0,t,W,H).


Idem avec une image blanche.
Idem avec un dgrad du noir au blanc (attention aux conversions entre
byte et double).
Idem avec t(i, j) = 128 + 128 sin(4i/W ) sin(4j/H) (cf figure A.7). Utiliser
# d e f i n e _USE_MATH_DEFINES
# i n c l u d e <cmath>
pour avoir les fonctions et les constantes mathmatiques : M_PI vaut .
4. Couleurs :
Afficher, avec putColorImage(0,0,r,g,b,W,H), une image en couleur stocke dans trois tableaux r, g et b (rouge, vert, bleu). Utiliser la fonction click()
pour attendre que lutilisateur clique avec la souris entre laffichage prcdent et
ce nouvel affichage.

A.7.3

Tableaux dynamiques

5. Dimensions au clavier :
Modifier le programme prcdent pour que W et H ne soient plus des constantes
mais des valeurs entres au clavier. Ne pas oublier de dsallouer.

A.7.4

Charger un fichier

6. Image couleur :
La fonction loadColorImage("ppd.jpg",r,g,b,W,H); charge le fichier "ppd.jpg"
qui est dans le rpertoire du projet, alloue elle-mme les tableaux r,g,b, les remplit avec les pixels de limage, et affecte aussi W et H en consquence. Attention :
ne pas oublier de dsallouer les tableaux r,g,b avec delete [] aprs usage.
Charger cette image et lafficher. Ne pas oublier les dsallocations.
7. Image noir et blanc :
La fonction loadGreyImage("ppd.jpg",t,W,H) fait la mme chose mais convertit limage en noir et blanc. Afficher limage en noir et blanc...

A.7.5

Fonctions

8. Dcouper le travail :
On ne garde plus que la partie noir et blanc du programme. Faire des fonctions
pour allouer, dtruire, afficher et charger les images :
byte AlloueImage ( i n t W, i n t H) ;
void DetruitImage ( byte I ) ;
void AfficheImage ( byte I , i n t W, i n t H) ;
byte ChargeImage ( char name , i n t &W, i n t &H) ;
9. Fichiers :
Crer un image.cpp et un image.h en consquence...
232

A. Travaux Pratiques

A.7.6

A.7. Images

Structure

10. Principe :
Modifier le programme prcdent pour utiliser une structure :
s t r u c t Image {
byte t ;
i n t w, h ;
};
AlloueImage() et ChargeImage() pourront retourner des Image.
11. Indpendance :
Pour ne plus avoir savoir comment les pixels sont stocks, rajouter :
byte Get ( Image I , i n t i , i n t j ) ;
void S e t ( Image I , i n t i , i n t j , byte g ) ;
12. Traitements :
Ajouter dans main.cpp diffrentes fonctions de modification des images
Image
Image
Image
Image
Image

N e g a t i f ( Image I ) ;
Flou ( Image I ) ;
R e l i e f ( Image I ) ;
Contours ( Image I , double s e u i l ) ;
Deforme ( Image I ) ;

et les utiliser :
(a) Negatif : changer le noir en blanc et vise-versa par une transformation
affine.
(b) Flou : chaque pixel devient la moyenne de lui-mme et de ses 8 voisins.
Attention aux pixels du bords qui nont pas tous leurs voisins (on pourra ne
pas moyenner ceux-l et en profiter pour utiliser linstruction continue !).
(c) Relief : la drive suivant une diagonale donne une impression dombres
projetes par une lumire rasante.
Approcher cette drive par diffrence finie : elle est proportionnelle
I(i + 1, j + 1) I(i 1, j 1).
Sarranger pour en faire une image allant de 0 255.
(d) Contours : calculer par diffrences finies la drive horizontale dx = (I(i +
1, j) I(i
p 1, j))/2 et la drive verticale dy , puis la norme du gradient
|I| = d2x + d2y et afficher en blanc les points o cette norme est suprieure
un seuil.
(e) Deforme : Construire une nouvelle image sur le principe J(i, j) = I(f (i, j))
avec f bien choisie. On pourra utiliser un sinus pour aller de 0 W-1 et de 0
H-1 de faon non linaire.

A.7.7

Suite et fin

13. Sil reste du temps, samuser :


Rtrcir une image.
Au lieu du ngatif, on peut par exemple changer le contraste. Comment ?
233

A.8. Premiers objets et dessins de fractales

A.8

A. Travaux Pratiques

Premiers objets et dessins de fractales

F IGURE A.8 Fractales...


Dans ce TP, nous allons nous essayer la programmation objet. Nous allons transformer une structure vecteur en une classe et lutiliser pour dessiner des courbes fractales (figure A.8).

A.8.1

Le triangle de Sierpinski

1. Rcuprer le projet :
Tlcharger le fichier Tp8_Initial.zip sur la page habituelle, le dcompresser et lancer Visual C++. Etudier la structure Vector dfinie dans les fichiers
Vector.cpp et Vector.h.
2. Interfaage avec Imagine++ :
La structure Vector ne comporte pas de fonction daffichage graphique. Ajouter
dans main.cpp des fonctions drawLine et drawTriangle prenant des Vector
en paramtres. Il suffit de rebondir sur la fonction
void drawLine ( i n t x1 , i n t y1 , i n t x2 , i n t y2 , c o n s t Color& c , i n t pen_w ) }
dImagine++. Le dernier paramtre contrle lpaisseur du trait.
3. Triangle de Sierpinski :
Cest la figure fractale choisie par lENPC pour son logo. La figure ci-dessous
illustre sa construction.
Ecrire une fonction rcursive pour dessiner le triangle de Sierpinski. Cette fonc-

F IGURE A.9 Construction du triangle de Sierpinski.


tion prendra en paramtres les trois points du triangle en cours et lepaisseur du
234

A. Travaux Pratiques

A.8. Premiers objets et dessins de fractales

trait. Les trois sous-triangles seront dessins avec un trait plus fin. Ne pas oublier
la condition darrt de la rcursion !
Utiliser cette fonction dans le main en lui fournissant un triangle initial dpaisseur 6.

A.8.2

Une classe plutt quune structure

4. Classe vecteur :
Transformer la structure Vector en une classe. Y incorporer toutes les fonctions
et les oprateurs. Passer en public le strict ncessaire. Faire les modifications ncessaires dans main.cpp.
5. Accesseurs pour les membres :
Rajouter des accesseurs en lecture et en criture pour les membres, et les utiliser
systmatiquement dans le programme principal. Lide est de cacher aux utilisateurs de la classe Vector les dtails de son implmentation.
6. Dessin rcursif dun arbre :
Nous allons maintenant dessiner un arbre. Pour cela il faut partir dun tronc et
remplacer la deuxime moiti de chaque branche par deux branches de mme
longueur formant un angle de 20 degrs avec la branche mre. La figure cidessous illustre le rsultat obtenu pour diffrentes profondeurs de rcursion.
Ecrire une fonction rcursive pour dessiner une telle courbe. Vous aurez besoin

F IGURE A.10 Construction de larbre.


de la fonction Rotate de la classe Vector.

A.8.3

Changer dimplmentation

7. Deuxime implmentation :
Modifier limplmentation de la classe Vector en remplaant les membres double x,y;
par un tableau double coord[2];. Quelles sont les modifications apporter dans
main.cpp ?
8. Vecteurs de dimension suprieure :
Lavantage de cette dernire implmentation est quelle se gnralise aisment
des vecteurs de dimension suprieure. Placer une constante globale DIM gale
2 au dbut de Vector.h et rendre la classe Vector indpendante de la dimension.
NB : la fonction Rotate et les accesseurs que nous avons crits ne se gnralisent pas directement aux dimensions suprieures. Les laisser tels quels pour
linstant...
235

A.8. Premiers objets et dessins de fractales

A.8.4

A. Travaux Pratiques

Le flocon de neige

9. Courbe de Koch :
Cette courbe fractale sobtient en partant dun segment et en remplaant le deuxime
tiers de chaque segment par deux segments formant la pointe dun triangle quilatral.
Ecrire une fonction rcursive pour dessiner une courbe de Koch.

F IGURE A.11 Construction de la courbe de Koch.


10. Flocon de neige :
Il sobtient en construisant une courbe de Koch partir de chacun des cts dun
triangle quilatral.

236

A. Travaux Pratiques

A.9. Tron

F IGURE A.12 Jeu de Tron.

A.9

Tron

Dans ce TP, nous allons programmer le jeu TRON. Il sagit dun jeu 2 joueurs,
dans lequel chaque joueur pilote un mobile qui se dplace vitesse constante et laisse
derrire lui une trace infranchissable. Le premier joueur qui percute sa propre trace ou
celle de son adversaire a perdu.

A.9.1

Serpent

Nous allons procder en deux temps. Dabord programmer un jeu de Serpent


un joueur. Le programme serpent.exe vous donne une ide du rsultat recherch.
Dans ce jeu, le joueur pilote un Serpent qui sallonge petit petit (dun lment tous
les x tours, avec la convention que la longueur totale est borne nmax lments). Il
sagit de ne pas se rentrer dedans ni de percuter les murs.
La solution de dpart comporte deux fichiers, utils.h et utils.cpp, qui contiennent
une structure point (quil faudra ventuellement toffer de mthodes utiles) et une
fonction destine rcuprer les touches clavier pour linteraction avec les joueurs.
Il sagit ici de concevoir un objet Serpent dot des mthodes adquates, plus une
fonction jeu_1p exploitant les capacits du Serpent pour reproduire le comportement
dsir. On pourra dans un premier temps ne pas grer les collisions (avec le bord et
avec lui-mme), et ne les rajouter que dans un second temps. Votre travail se dcompose en 6 tapes :
1. (sur papier) Dfinir linterface de la classe Serpent (cest--dire lister toutes les
fonctionnalits ncessaires).
2. (sur papier) Rflchir limplmentation de la classe Serpent : comment stocker les
donnes ? comment programmer les diffrentes mthodes ? (lire en prliminaire
les remarques du paragraphe suivant).
3. Dans un fichier serpent.h, crire la dclaration de votre classe Serpent : ses membres,
ses mthodes, ce qui est public, ce qui ne lest pas.
4. Soumettre le rsultat de vos rflexions votre enseignant pour valider avec lui les choix
retenus.
5. Implmenter la classe Serpent (cest--dire programmer les mthodes que vous
avez dclares).
237

A.9. Tron

A. Travaux Pratiques

6. Programmer la fonction jeu_1p utilisant un Serpent.


Remarque : Dans le fichier utils.h sont dfinis :
1. 4 entiers gauche, bas, haut, droite de telle manire que :
(a) la fonction x (x + 1)%4 transforme gauche en bas, bas en droite, droite en
haut et haut en gauche ; cette fonction correspond donc un quart de tour
dans le sens trigonomtrique.
(b) la fonction x (x 1)%4 transforme gauche en haut, haut en droite, droite
en bas et bas en gauche ; cette fonction correspond donc un quart de tour
dans le sens des aiguilles dune montre.
2. un tableau de 4 points dir de telle manire que, moyennant la dfinition dune
fonction permettant de faire la somme de deux points, la fonction p p + dir[d]
renvoie :
(a) pour d=gauche le point correspondant au dcalage de p de 1 vers la gauche.
(b) pour d=haut le point correspondant au dcalage de p de 1 vers la haut.
(c) pour d=droite le point correspondant au dcalage de p de 1 vers la droite.
(d) pour d=bas le point correspondant au dcalage de p de 1 vers la bas.

A.9.2

Tron

A partir du jeu de Serpent ralis prcdemment, nous allons facilement pouvoir


implmenter le jeu Tron. Le programme tron.exe vous donne une ide du rsultat
recherch. Le principe de ce jeu est que chaque joueur pilote une moto qui laisse derrire elle une trace infranchissable. Le but est de survivre plus longtemps que le joueur
adverse.
1. Passage deux joueurs.
A partir de la fonction jeu_1p, crer une fonction jeu_2p implmentant un
jeu de serpent 2 joueurs. On utilisera pour ce joueur les touches S, X, D et F.
La fonction Clavier() renverra donc les entiers int ( S ), int ( X), int ( D) et
int ( F). Remarque : on ne grera quune touche par tour, soit un seul appel la
fonction Clavier() par tour.
2. Ultimes rglages
(a) Grer la collision entre les deux serpents.
(b) Le principe de Tron est que la trace des mobiles reste. Pour implmenter
cela, il suffit dallonger nos serpents chaque tour.

A.9.3

Graphismes

Petit bonus pour les rapides : nous allons voir comment grer des graphismes un
peu plus sympas que les rectangles uniformes que nous avons utiliss jusquici. Lobjectif est de remplacer le carr de tte par une image que lon dplace chaque tour.
Nous allons utiliser pour cela les NativeBitmap dImagine++, qui sont des images
affichage rapide. Pour charger une image dans une NativeBitmap on procde ainsi :
238

A. Travaux Pratiques

A.9. Tron

/ / E n t i e r s p a s s s p a r r f r e n c e l o r s du c h a r g e m e n t d e l i m a g e p o u r
/ / qu y s o i e n t s t o c k e s l a l a r g e u r e t l a h a u t e u r d e l i m a g e
i n t w, h ;
/ / Chargement d e l i m a g e
byte rgb ;
loadColorImage ( " n o m _ f i c h i e r . bmp" , rgb ,w, h ) ;
/ / D c l a r a t i o n de l a NativeBitmap
NativeBitmap ma_native_bitmap (w, h ) ;
/ / On p l a c e l i m a g e d a n s l a N a t i v e B i t m a p
ma_native_bitmap . setColorImage ( 0 , 0 , rgb ,w, h ) ;
Laffichage dune NativeBitmap lcran se fait alors avec la mthode :
void putNativeBitmap ( i n t x , i n t y , NativeBitmap nb )
1. Remplacer dans le serpent laffichage de la tte par laffichage dune image. On
pourra utiliser les images moto_blue.bmp et moto_red.bmp fournies.
2. Utiliser limage explosion.bmp lors de la mort dun des joueurs.

239

B. Imagine++

Annexe B
Imagine++
Imagine++ est un ensemble de bibliothques permettant de faire simplement du
graphisme et de lalgbre linaire. Elles sappuient pour cela sur les projets Qt (graphisme 2D et 3D) et Eigen (algbre linaire). Ceux-ci proposent une richesse de possibilits bien plus importantes que Imagine++ mais en contrepartie Imagine++ est
plus simple utiliser.
Imagine++ est un logiciel libre, vous pouvez donc lutiliser et le distribuer votre
guise (et gratuitement !), mais si vous le distribuez sous une forme modifie vous devez offrir selon les mmes termes les sources de vos modifications. Une documentation
complte est disponible sur la page Web dImagine++, dtaillant linsallation et lutilisation.
Pour utiliser un module, par exemple Images, un fichier source doit linclure
# i n c l u d e <Imagine/Images . h>
pour une compilation correcte, et votre fichier CMakeLists.txt doit comporter
ImagineUseModule ( MonProgramme Images )
pour que ldition de liens (link) russisse.
Tout est englob dans un namespace Imagine, donc si vous voulez viter de prfixer les noms par Imagine:: vous devez utiliser dans votre code
using namespace Imagine ;

B.1

Common

Le module Common dfinit entre autres la classe Color code par un mlange de
rouge, vert et bleu, la quantit de chacun code par un entier entre 0 et 255 :
Color n o i r = Color ( 0 , 0 , 0 ) ;
Color b l a n c = Color ( 2 5 5 , 2 5 5 , 2 5 5 ) ;
Color rouge = Color ( 2 5 5 , 0 , 0 ) ;
Un certain nombre de constantes de ce type sont dj dfinies : BLACK, WHITE, RED,
GREEN, BLUE, CYAN, MAGENTA, YELLOW.
Un type byte (synonyme de unsigned char) est dfini pour coder une valeur
entire entre 0 et 255.
Trs pratique, srcPath fait prcder la chane de caractre argument par le chemin
complet du rpertoire contenant le fichier source. Lquivalent pour un argument de
type string est stringSrcPath :

B.2. Graphics

B. Imagine++

c o n s t char f i c h i e r = s r c P a t h ( " m o n _ f i c h i e r . t x t " ) ;


s t r i n g s = " mon_fichier . t x t " ;
s = stringSrcPath ( s ) ;
En dautres termes, le fichier sera trouv quel que soit lemplacement de lexcutable.
La classe template FArray sutilise pour des tableaux de taille petite et connue
la compilation (allocation statique). Pour des tableaux de taille non connue la compilation (allocation dynamique), utiliser Array. Pour les matrices et vecteurs, utiliser
FMatrix et FVector, utilisant lallocation statique comme indiqu par le prfixe F
(fixed). Les quivalents dynamiques sont dans LinAlg.

B.2

Graphics

Le module Graphics propose du dessin en 2D et 3D. Les coordonnes 2D sont en


pixel, laxe des x est vers la droite et laxe des y vers le bas (attention, ce nest pas le
sens mathmatique usuel !). Le point (0,0) est donc le coin haut-gauche de la fentre
(pour les fonctions de trac) ou de lcran (pour openWindow).
openWindow ( 5 0 0 , 5 0 0 ) ; / / T a i l l e d e f e n t r e
drawRect ( 1 0 , 1 0 , 4 8 0 , 4 8 0 ,RED ) ; / / Coin haut g a u c h e ( 1 0 , 1 0 ) ,
/ / l a r g e u r 4 8 0 , h a u t e u r 480
drawLine ( 1 0 , 1 0 , 4 9 0 , 4 9 0 ,BLUE ) ; / / D i a g o n a l e
Window w = openWindow ( 1 0 0 , 1 0 0 ) ; / / A u t r e f e n t r e
setActiveWindow (w) ; / / S l e c t i o n p o u r l e s p r o c h a i n s t r a c s
drawString ( 1 0 , 1 0 , "Du t e x t e " , MAGENTA) ; / / M e t t r e du t e x t e
endGraphics ( ) ; / / A t t e n d un c l i c s o u r i s a v a n t d e f e r m e r l e s f e n t r e s
Si on a beaucoup de dessins faire la suite et quon veut nafficher que le rsultat
final (cest plus esthtique), on encadre le code de trac par :
noRefreshBegin ( ) ;
...
noRefreshEnd ( ) ;
Pour faire une animation, il est utile de faire une petite pause entre les images pour
rguler la cadence :
m i l l i S l e e p ( 5 0 ) ; / / Temps en m i l l i s e c o n d e s
On peut charger une image (loadGreyImage, loadColorImage) ou sauvegarder (saveGreyImage, saveColorImage) dans un fichier. Attention, ces fonctions
allouent de la mmoire quil ne faut pas oublier de librer aprs usage.
byte g ;
i n t l a r g e u r , hauteur ;
i f ( ! loadGreyImage ( s r c P a t h ( " image . j p g " ) , g , l a r g e u r , hauteur ) ) {
c e r r << " I m p o s s i b l e d o u v r i r l e f i c h i e r "
<< s r c P a t h ( " image . j p g " ) << endl ;
exit (1);
}
/ / D e s s i n e a v e c c o i n h a u t g a u c h e en ( 0 , 0 ) :
putGreyImage ( 0 , 0 , g , l a r g e u r , hauteur ) ;
d e l e t e [ ] g ; / / Ne p a s o u b l i e r !
242

B. Imagine++

B.3. Images

A noter srcPath, dfini dans Common, qui indique de chercher dans le dossier contenant les fichiers source.
En fait, pour viter de grer soi-mme la mmoire des images, il existe une classe
ddie cela :

B.3

Images

Le module Images gre le chargement, la manipulation et la sauvegarde des images.


Image<byte > im ; / / Image en n i v e a u x d e g r i s
i f ( ! load ( im , s r c P a t h ( " f i c h i e r _ i m a g e . png " ) ) ) {
c e r r << " I m p o s s i b l e d o u v r i r l e f i c h i e r "
<< s r c P a t h ( f i c h i e r _ i m a g e . png ) << endl ;
exit (1);
}
d i s p l a y ( im ) ; / / D e s s i n e d a n s l a f e n t r e a c t i v e
im ( 0 , 0 ) = 1 2 8 ; / / Met l e p i x e l en g r i s
save ( im , " f i c h i e r _ i m a g e 2 . png " ) ; / / S a u v e g a r d e l i m a g e d a n s un f i c h i e r
Attention : la recopie et laffectation (oprateur =) sont des oprations peu coteuses qui en fait ne font quun lien entre les images, sans relle copie. Ce qui fait que
la modification de lune affecte lautre :
Image<Color > im1 ( 1 0 0 , 1 0 0 ) ;
Image<Color > im2 = im1 ; / / C o n s t r u c t e u r p a r r e c o p i e
im1 ( 1 0 , 1 0 ) = CYAN;
a s s e r t ( im2 ( 1 0 , 1 0 ) == CYAN) ; / / im2 a t a f f e c t e
Pour faire une vraie copie plutt quun lien, on utilise :
im2 = im1 . c l o n e ( ) ;
im1 ( 1 0 , 1 0 ) = CYAN; / / N a f f e c t e p a s im2
Ainsi, si on passe une image comme paramtre dune fonction, puisque cest le
constructeur par copie qui est appel, tout se passe comme si on avait pass par rfrence :
void f ( Image<Color > im ) { / / P a s s a g e p a r v a l e u r
im ( 1 0 , 1 0 ) = CYAN;
}
f ( im1 ) ;

/ / M o d i f i e quand mme l e p i x e l ( 1 0 , 1 0 )

Une erreur courante est de chercher lire ou crire des coordonnes au-del des
bornes du tableau, typiquement une erreur dans un indice de boucle.

B.4

LinAlg

Le module LinAlg propose lalgbre linaire avec des classes matrice et vecteur.
Matrix < f l o a t > I ( 2 , 2 ) ; / / T a i l l e 2 x2
I . f i l l (0.0 f ) ; / / Matrice nulle
I (0 ,0)= I (1 ,1)=1.0 f ; / / Matrice i d e n t i t
243

B.4. LinAlg

B. Imagine++

cout << " det ( I )= " << det ( I ) << endl ; / / D t e r m i n a n t


Les oprateurs daddition (matrice+matrice, vecteur+vecteur), soustraction (matricematrice, vecteur-vecteur) et multiplication (matrice*matrice, matrice*vecteur) sont bien
sr dfinis.
Comme pour les images, attention de ne pas sortir des bornes en accdant aux
lments des matrices et vecteurs !
Une fonction trs utile est linSolve pour rsoudre un systme linaire.

244

C. Fiche de rfrence finale

C. Fiche de rfrence finale

Annexe C
Fiche de rfrence finale
Fiche de rfrence (1/5)
Variables
Dfinition :
int i;
int k,l,m;
Affectation :
i=2;
j=i;
k=l=3;
Initialisation :
int n=5,o=n;
Constantes :
const int s=12;
Porte :
int i;
// i=j; interdit!
int j=2;
i=j; // OK!
if (j>1) {
int k=3;
j=k; // OK!
}
//i=k; interdit!
Types :
int i=3;
double x=12.3;
char c=A;
string s="hop";
bool t=true;
float y=1.2f;
unsigned int j=4;
signed char d=-128;
unsigned char d=25;
complex<double>
z(2,3);
Variables globales :
int n;
const int m=12;
void f() {
n=10;
// OK

int i=m; // OK
...
Conversion :
int i=int(x),j;
float x=float(i)/j;
Pile/Tas
Type numr :
enum Dir{nord,est,
sud,ouest};
void avance(Dir d);

...;
break;
case 2:
case 3:
...;
break;
default:
...;
}
mx=(x>y)?x:y;

Boucles
Variables statiques :
int f() {
do {
static bool first=true;
...
if (first) {
} while(!ok);
first=false;
int i=1;
...
while(i<=100) {
}
...
...
i=i+1;
}
}
Tests
for(int i=1;i<=10;i++)
Comparaison :
== != < > <= >=
Ngation : !
Combinaisons : && ||
if (i==0) j=1;
if (i==0) j=1;
else
j=2;
if (i==0) {
j=1;
k=2;
}
bool t=(i==0);
if (t)
j=1;
switch (i) {
case 1:
...;

246

...
for(int i=1,j=10;j>i;
i=i+2,j=j-3)
...
for (int i=...)
for (int j=...) {
//saute cas i==j
if (i==j)
continue;
...
}
for (int i=...) {
...
if (t[i]==s){
// quitte boucle
break;
}
...
}

C. Fiche de rfrence finale

Fiche de rfrence (2/5)


Fonctions
Pile des appels
t[i]=0;
}
Dfinition :
Itratif/Rcursif
int plus(int a,int b) {
void init(int t[],
Rfrences constantes (pour
int c=a+b;
int n) {
un passage rapide) :
return c;
for(int
i=0;i<n;i++)
void f(const obj& x){
}
t[i]=0;
...
void affiche(int a) {
}
}
cout << a << endl;
void g(const obj& x){ Taille variable :
}
f(x); // OK
int* t=new int[n];
Dclaration :
}
...
int plus(int a,int b);
delete[] t;
Valeurs par dfaut :
Retour :
void f(int a,int b=0); En paramtre (suite) :
int signe(double x) {
void g() {
void f(int* t,int n){
if (x<0)
f(12); // f(12,0);
t[i]=...
return -1;
f(10,2);// f(10,2);
}
if (x>0)
}
return 1;
void alloue(int*& t){
void f(int a,int b) {
return 0;
t=new int[n];
// ...
}
}
}
void afficher(int x,
2D :
int y) { Inline (appel rapide) :
int A[2][3];
inline double sqr(
if (x<0 || y<0)
double x) { A[i][j]=...;
return;
int A[2][3]=
return x*x;
if (x>=w || y>=h)
{{1,2,3},{4,5,6}};
}
return;
void
f(int A[2][2]);
...
DrawPoint(x,y,RED);
double y=sqr(z-3); 2D dans 1D :
}
int A[2*3];
Rfrence en retour :
Appel :
A[i+2*j]=...;
int i; // Var. globale
int f(int a) { ... }
int& f() {
Taille variable (suite) :
int g() { ... }
return
i;
int *t,*s,n;
...
}
int i=f(2),j=g();
En paramtre (fin) :
...
Rfrences :
void f(const int* t,
f()=3; // i=3!
void swap(int& a,
int n) {
int& b){
...
Tableaux
int tmp=a;
s+=t[i]; // OK
Dfinition :
a=b;b=tmp;
...
double x[5],y[5];
}
t[i]=...; // NON!
for(int i=0;i<5;i++)
...
}
y[i]=2*x[i];
int x=3,y=2;
Structures
swap(x,y);
const int n=5;
struct Point {
Surcharge :
int i[n],j[2*n];
double x,y;
int hasard(int n);
Initialisation :
Color c;
int hasard(int a,
int t[4]={1,2,3,4};
};
int b);
string s[2]={"ab","c"};
...
double hasard();
Affectation :
Point a;
Oprateurs :
int s[3]={1,2,3},t[3];
a.x=2.3; a.y=3.4;
vect operator+(
for (int i=0;i<3;i++)
a.c=Red;
vect A,vect B) {
t[i]=s[i];
Point b={1,2.5,Blue};
...

En
paramtre
:
Une structure est un objet en}
...
vect C=A+B;

void init(int t[4]) {


for(int i=0;i<4;i++)

247

tirement public ( cf objets !)

C. Fiche de rfrence finale

Fiche de rfrence (3/5)


Objets
struct obj {
int x;
// champ
int f(); // mthode
int g(int y);
};
int obj::f() {
int i=g(3); // mon g
int j=x+i; // mon x
return j;
}
...
int main() {
obj a;
a.x=3;
int i=a.f();
class obj {

int x,y;
void a_moi();
public:
int z;
void pour_tous();
void un_autre(obj A);
};
void obj::a_moi() {
x=..;
// OK
..=y;
// OK
z=..;
// OK
}
void obj::pour_tous() {
x=..;
// OK
a_moi(); // OK

}
void une_autre(obj A) {
x=A.x;
// OK
A.a_moi(); // OK

}
...
int main() {
obj A,B;
A.x=..;
//NON
A.z=..;
//OK
A.a_moi();
//NON
A.pour_tous(); //OK
A.une_autre(B); //OK
class obj {
obj operator+(obj B);
};
...
int main() {
obj A,B,C;
C=A+B;
// C=A.operator+(B)
Mthodes constantes :
void obj::f() const{
...

void g(const obj& x){


double *x;
x.f(); // OK
public:
}
double& operator()
(int i,int j){
Constructeur :
assert(i>=0 ...);
class point {
return x[i+M*j];
int x,y;
}
public:
double operator()
point(int X,int Y);
(int i,int j)const{
};
assert(i>=0 ...);
point::point(int X,
return x[i+M*j];
int Y){
}
x=X;
...
y=Y;
}
Compilation spare
...
#include "vect.h",
y
point a(2,3);
compris
dans
vect.cpp
Constructeur vide :
Fonctions : dclarations dans
obj::obj() {
le .h, dfinitions dans le
...
.cpp
}
...
Types : dfinitions dans le .h
obj a;
Ne dclarer dans le .h que les
Objets temporaires :
fonctions utiles.
point point::operator+(
point b) { #pragma once au dbut du
fichier.
return point(x+b.x,
y+b.y); Ne pas trop dcouper...
}
...
STL
c=point(1,2)
min,max,...
+f(point(2,3));
complex<double> z;
Destructeur :
obj::~obj() {
pair<int,string> p;
...
p.first=2;
}
p.second="hop";
Constructeur de copie :
#include<list>
obj::obj(const obj& o){
using namespace std;
...
...
}
list<int> l;
Utilis par :
l.push_front(1);
- obj b(a);
...
- obj b=a;
if (l.find(3)!=l.end())
// mieux que obj b;b=a;
...
- paramtres des fonctions
list<int>::const_iterator
- valeur de retour
it;
Affectation :
for
(it=l.begin();
obj& obj::operator=(
it!=l.end();it++)
const obj&o){
s+=
*it;
...
return *this;
list<int>::iterator it
}
for (it=l.begin();
it!=l.end();it++)
Objets avec allocation dynaif
(
*it==2)
mique automatique : cf secit=4;
*
tion 10.11
stack, queue, heap,
Accesseurs :
map, set, vector...
class mat {

248

C. Fiche de rfrence finale

Fiche de rfrence (4/5)


Entres/Sorties
#include <iostream>
using namespace std;
...
cout <<"I="<<i<<endl;
cin >> i >> j;

f<<p.x<< << p.y;


return f;
}

istream& operator>>(
istream& f,point& p){
f>>p.x>>p.y;
return f;
}

hop<int,string> A;
...
Entiers :
template <int N>
class hop {
..
};
...
hop<3> A;
...

#include <fstream>
using namespace std;
ofstream f("hop.txt"); Template
f << 1 << << 2.3;
Fonctions :
f.close();
Conseils
// A mettre dans LE
ifstream g("hop.txt");
//
fichier
qui
lutilise
Nettoyer en quittant.
if (!g.is_open()) {
// ou dans un .h
return 1;
Erreurs et warnings : cliquer.
template <typename T>
}
Indenter.
T maxi(T a,T b) {
int i;
...
Ne pas laisser de warning.
double x;
}
g >> i >> x;
Utiliser le debuggeur.
...
g.close();
// Le type est trouv Faire des fonctions.
do {
Tableaux : pas pour transcrire
// tout seul!
...
une formule mathmatique !
maxi(1,2);
//int
} while (!(g.eof());
maxi(.2,.3); //double Faire des structures.
ofstream f;
maxi("a","c");//string
Faire des fichiers spars.
f.open("hop.txt");
Le .h doit suffire lutilisa double x[10],y;
Objets :
teur (qui ne doit pas regarder
ofstream f("hop.bin",
template <typename T>
le .cpp)
ios::binary); class paire {
f.write((const char*)x,
Ne pas abuser du rcursif.
T x[2];
10*sizeof(double));
public:
Ne pas oublier delete.
f.write((const char*)&y,
paire() {}
Compiler rgulirement.
sizeof(double));
paire(T a,T b) {
f.close();
Debug/Release : nettoyer les
x[0]=a;x[1]=b;
ifstream g("hop.bin",
deux.
}
ios::binary);
T add()const;
#include <cassert>
g.read((char*)x,
};
...
10*sizeof(double));
...
assert(x!=0);
g.read((const char*)&y,
template <typename T>
y=1/x;
sizeof(double));
T paire<T>::add()const{
Faire des objets.
g.close();
return x[0]+x[1];
Ne pas toujours faire des ob string s;
}
jets !
ifstream f(s.c_str());
...
//
Le
type
doit
tre
Penser interface / implmen #include <sstream>
// prcis!
tation / utilisation.
using namespace std;
paire<int> a(1,2);
stringstream f;
Clavier
int s=a.somme();
// Chane vers entier
paire<double>
b;
f << s;
Build : F7
...
f >> i;
Debug : F5
// Entier vers chane Multiples :
Step over : F10
f.clear();
template <typename T,
f << i;
typename S> Step inside : F11
f >> s;
class hop {
Indent : Ctrl+K,Ctrl+F
...
ostream& operator<<(
Step out : Maj+F11
};
ostream& f,
Gest. tches : Ctrl+Maj+Ech
...
const point&p) {

249

C. Fiche de rfrence finale

Fiche de rfrence (5/5)


Divers
i++;
i--;
i-=2;
j+=3;

exemples :
set(i,1) :
reset(i,1) :
test(i,1) :
flip(i,1) :

i|=(1<<n)
i&=~(1<<n)
if (i&(1<<n))
i^=(1<<n)

j=i%n; // Modulo

int *t,s;// s est int


// et non int*
t=new int[n];
s=new int[n];// NON!
class point {
int x,y;
public:
...
};
...
point a={2,3}; // NON

Erreurs frquentes
#include <cstdlib>
Pas de dfinition de fonction
...
dans une fonction !
i=rand()%n;
int q=r=4; // NON!
x=rand()/
if (i=2) // NON!
double(RAND_MAX);
if i==2 // NON!
point p=point(1,2);//NON
#include <ctime>
if (i==2) then // NON!
point p(1,2);
// OUI
...
for (int i=0,i<100,i++) obj* t=new obj[n];
srand((unsigned int)
// NON!
delete t; // manque []
time(0));
int f() {...}

//NON!
#include <cmath>
int i=f; // NON!
void f(int a=2,int b);
double sqrt(double x);

double
x=1/3;
//
NON!
void f(int a,int b=0);
double cos(double x);
int
i,j;
void f(int a);// NON!
double sin(double x);
x=i/j;
//
NON!
double acos(double x);
x=double(i/j); //NON! Ne pas tout mettre inline !
#include <string>
int f() {
double x[10],y[10];
using namespace std;
...
for (int i=1;i<=10;i++)
string s="hop";
}
y[i]=2*x[i]; //NON
char c=s[0];
f()=3; // HORREUR!
int n=5;
int l=s.size();
int& f() {
int t[n]; // NON
if (s1==s1) ...
int i;
int f()[4] { // NON!
if (s1!=s2) ...
return i;
int t[4];
if (s1<s2) ...
}
...
size_t i=s.find(h),
f()=3; // NON!
return t; // NON!
j=s.find(h,3);
if (i>0 & i<n) // NON
}
k=s.find("hop");
if (i<0 | i>n) // NON
int
t[4];
t=f();
l=s.find("hop",3);
a="comment";
int s[3]={1,2,3},t[3]; if (...) {
b="a va?";
...
t=s; // NON!
txt=a+" "+b;
if (...)
int t[2];
s1="un deux trois";
break; // Non,
t={1,2}; // NON!
s2=string(s1,3,4);
// boucles seulement
struct Point {
getline(cin,s);
}
double x,y;
getline(cin,s,:);
for (i ...)
const char *t=s.c_str(); } // NON!
for (j ...) {
Point a;
#include <ctime>
...
a={1,2}; // NON!
s=double(clock())
if (...)
#include "vec.cpp"//NON
/CLOCKS_PER_SEC;
break;//NON, quitte
void f(int t[][]);//NON
// juste la boucle j
#define _USE_MATH_DEFINES
int t[2,3]; // NON!
#include <cmath>
int i;
t[i,j]=...; // NON!
double pi=M_PI;
double x;
int* t;
j=max(i,0);//OK
Oprateurs binaires
t[1]=...; // NON!
y=max(x,0);//NON!
and :
a&b
// 0.0 et non 0: max
int* t=new int[2];
or :
a|b
// est un template STL
int* s=new int[2];
xor :
a^b
s=t; // On perd s!
right shift :
a>>n
Imagine++
delete[] t;
left shift :
a<<n
delete[] s; //Dja fait Voir documentation...
complement : ~a

250

Anda mungkin juga menyukai