2
les lves ingnieurs
2
. . . ou les collgiens
2
dbutants
2
. . . ou confirms
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 . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
4.4
4.5
4.6
5
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
7.7
7.8
7.9
8
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
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
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
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
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 . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
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
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 .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
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. Prambule
1.1
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
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
11
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.)
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
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
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
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
2.1.2
La mmoire
17
2.1. Lordinateur
2. Bonjour, Monde !
18
2. Bonjour, Monde !
2.1.3
2.1. Lordinateur
Autres Composants
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. 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
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...
22
2. Bonjour, Monde !
2.4
Lenvironnement de programmation
2.4.1
Noms de fichiers
2.4.2
Debuggeur
23
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
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.
3.1
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. 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
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. Premiers programmes
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
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. 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 }
default :
cout << " Vous avez tap a u t r e chose ! " << endl ;
break ;
}
} while ( ! f i n i ) ;
return 0;
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
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
Rcrations
# 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
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;
# 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
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
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
3.2.3
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
# 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
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
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
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
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. Premiers programmes
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
46
3. Premiers programmes
Build : F7
// NON!
Debug : F5
int f() {...}
Step over : F10
int i=f; // NON!
47
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
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
51
4.3
4. Les 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
// 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
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
# 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
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
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
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
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
61
4. Les tableaux
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
...
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!
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!
double x[10],y[10];
for (int i=1;i<=10;i++)
y[i]=2*x[i]; //NON
int n=5;
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 . . .
66
5. 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
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. 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
68
5. Les structures
5.3. Rcration : TP
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
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.
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. Les structures
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.
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 !
// 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. Plusieurs fichiers !
6.1.3
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
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 !
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. 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 !
...
}
/ / Autres f o n c t i o n s
...
6.1.5
A ne pas faire...
6.1.6
Implmentation
79
6.2. Oprateurs
6. Plusieurs fichiers !
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
6. Plusieurs fichiers !
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...
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 !
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. Plusieurs fichiers !
Debug : F5
Step over : F10
Step inside : F11
Indent : Ctrl+K,Ctrl+F
Conseils
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
Il sagit l dune nouvelle occasion pour vous de comprendre enfin ce qui se passe
dans un programme...
7.1.1
Exemple
# 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 : " ;
21
22
23
24
25
26
27
28 }
7. La mmoire
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
86
7. La mmoire
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
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. La mmoire
(a)
(b)
(c)
(d)
(e)
(f)
(g)
F IGURE 7.1 Appels de fontions
88
7. La mmoire
pile
top
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.2.1
7. La mmoire
Paramtres
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
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
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
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
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
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. La mmoire
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
return t; // NON!
Pas de dfinition de fonction
}
dans une fonction !
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];
Nettoyer en quittant.
int t[n]; // NON
Erreurs et warnings : cliquer.
int f()[4] { // NON!
Indenter.
int t[4];
7.9
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
8.1.2
8. Allocation dynamique
Limitations
/ / 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
B[0]=1
B[1]=4
B[2]=2
B[3]=5
B[4]=3
B[5]=6
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
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.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. 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
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
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
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 }
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. 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
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. Allocation dynamique
}
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
#include <cmath>
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,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 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. 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 ????
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
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 ...
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.
{
//
//
//
//
();
();
/ / 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
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
/ / 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. 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
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
};
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
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] !
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
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
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. Premiers objets
Compilation spare
#include "vect.h",
compris dans vect.cpp
if (i=2) // NON!
if i==2 // NON!
if (i==2) then // NON!
for (int i=0,i<100,i++)
// NON!
128
9. Premiers objets
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
#include <cassert>
...
assert(x!=0);
y=1/x;
Faire des objets.
Ne pas toujours faire des objets !
Penser interface / implmentation / utilisation.
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
10.2. La solution
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
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
};
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
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
/ / OK
};
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
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.5. TP
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
};
/ / 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
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
...
};
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.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
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.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
...
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
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.11.1
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
vect : : vect ( ) {
n=0;
}
vect : : ~ vect ( ) {
i f (n!=0)
delete [ ] t ;
}
10.11.3
Solution !
146
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
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
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
149
10.12
Fiche de rfrence
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;
150
...
}
void g(const obj& x){
151
break;
default:
...;
}
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.
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!
Erreurs frquentes
10.13
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.
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. En vrac...
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 ) ;
/ / 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...
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...
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 . . . )
11.3
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
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
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
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
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++...
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. En vrac...
166
11. En vrac...
11.7
Fiche de rfrence
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;
Pile/Tas
Type numr :
enum Dir{nord,est,
sud,ouest};
void avance(Dir d);
Tests
Compiler rgulirement.
Debug/Release : nettoyer les
deux.
Ngation : !
#include <cassert>
...
assert(x!=0);
y=1/x;
Combinaisons : && ||
if (i==0) j=1;
Comparaison :
== != < > <= >=
if (i==0) j=1;
else
j=2;
167
11. En vrac...
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
}
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...
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. En vrac...
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
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
symbole utilisation
&
a&b
|
a|b
^
a^b
>>
a>>n
<<
a<<n
~a
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
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.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
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
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
12.6. template
...
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.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
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
}
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.6. template
12.6. template
...
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.6. template
//
//
//
//
//
//
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
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
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
}
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;
}
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;
...
}
...
}
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{
...
184
..
};
...
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
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>
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;
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
186
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
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
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
La complexit
13.2.1
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.2.5
13.3
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.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
193
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
194
/ / 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
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
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
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)
13.8
Les itrateurs
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
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
14.3. QuickSort
14.3
QuickSort
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
10
7
5
8
3
10
10
7
5
8
9
14.4
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
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
10
6
7
5
7
3
3
6
6
3
14.4.2
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
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
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. Travaux Pratiques
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.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. 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
208
A. Travaux Pratiques
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
A.1.5
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. Travaux Pratiques
A.2
A.2.1
# 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
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
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. Travaux Pratiques
A.2.3
Jeu de Tennis
A. Travaux Pratiques
213
A.3. Tableaux
A. Travaux Pratiques
A.3
Tableaux
A.3.1
Mastermind Texte
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
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
A.4. Structures
A. Travaux Pratiques
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
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 :
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
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
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
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
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
A. Travaux Pratiques
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. Travaux Pratiques
226
A. Travaux Pratiques
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
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
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. Travaux Pratiques
qui ne fait quappeler cette dernire, mais qui sera celle quon utilisera dans
main() (car plus simple).
A.6.4
Gros tableaux
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
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
A.8
A. Travaux Pratiques
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-
A. Travaux Pratiques
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
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
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.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.
236
A. Travaux Pratiques
A.9. 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
A.9. Tron
A. Travaux Pratiques
A.9.2
Tron
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++
B.2
Graphics
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
/ / 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++
244
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;
}
...
}
En
paramtre
:
Une structure est un objet en}
...
vect C=A+B;
247
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{
...
248
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
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
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