You are on page 1of 343

LA PROGRAMMATION POUR...

les lves ingnieurs


. . . ou les collgiens
dbutants
. . . ou conrms
Cours de lcole des Ponts ParisTech - 2012/2013
Renaud Keriven et Pascal Monasse
IMAGINE - cole des Ponts ParisTech
{keriven,monasse}@imagine.enpc.fr
Version lectronique et programmes :
http://imagine.enpc.fr/~monasse/Info/
"Ne traitez pas vos ordinateurs comme des tres vivants...
Ils naiment pas a !"
"Cet ordinateur ne fait pas du tout ce que je veux !"
"Exact... Il fait ce que tu lui demandes de faire !"
TABLE DES MATIRES TABLE DES MATIRES
Table des matires
1 Prambule 9
1.1 Pourquoi savoir programmer ? . . . . . . . . . . . . . . . . . . . . . . . . 11
1.2 Comment apprendre ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
1.2.1 Choix du langage . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
1.2.2 Choix de lenvironnement . . . . . . . . . . . . . . . . . . . . . . . 13
1.2.3 Principes et conseils . . . . . . . . . . . . . . . . . . . . . . . . . . 13
2 Bonjour, Monde ! 15
2.1 Lordinateur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
2.1.1 Le micro-processeur . . . . . . . . . . . . . . . . . . . . . . . . . . 17
2.1.2 La mmoire . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
2.1.3 Autres Composants . . . . . . . . . . . . . . . . . . . . . . . . . . 20
2.2 Systme dexploitation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
2.3 La Compilation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
2.4 Lenvironnement de programmation . . . . . . . . . . . . . . . . . . . . . 24
2.4.1 Noms de chiers . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
2.4.2 Debuggeur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
2.4.3 TP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
3 Premiers programmes 27
3.1 Tout dans le main()! . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
3.1.1 Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
3.1.2 Tests . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
3.1.3 Boucles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
3.1.4 Rcrations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
3.2 Fonctions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
3.2.1 Retour . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
3.2.2 Paramtres . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
3.2.3 Passage par rfrence . . . . . . . . . . . . . . . . . . . . . . . . . 41
3.2.4 Porte, Dclaration, Dnition . . . . . . . . . . . . . . . . . . . . 44
3.2.5 Variables locales et globales . . . . . . . . . . . . . . . . . . . . . . 45
3.2.6 Surcharge . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
3.3 TP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
3.4 Fiche de rfrence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
4 Les tableaux 51
4.1 Premiers tableaux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
4.2 Initialisation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
4.3 Spcicits des tableaux . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
TABLE DES MATIRES TABLE DES MATIRES
4.3.1 Tableaux et fonctions . . . . . . . . . . . . . . . . . . . . . . . . . . 54
4.3.2 Affectation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
4.4 Rcrations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
4.4.1 Multi-balles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
4.4.2 Avec des chocs ! . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
4.4.3 Mlanger les lettres . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
4.5 TP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
4.6 Fiche de rfrence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
5 Les structures 67
5.1 Rvisions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
5.1.1 Erreurs classiques . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
5.1.2 Erreurs originales . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
5.1.3 Conseils . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
5.2 Les structures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
5.2.1 Dnition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
5.2.2 Utilisation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
5.3 Rcration : TP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
5.4 Fiche de rfrence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
6 Plusieurs chiers ! 75
6.1 Fichiers spars . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
6.1.1 Principe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
6.1.2 Avantages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
6.1.3 Utilisation dans un autre projet . . . . . . . . . . . . . . . . . . . . 78
6.1.4 Fichiers den-ttes . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
6.1.5 A ne pas faire... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
6.1.6 Implmentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
6.1.7 Inclusions mutuelles . . . . . . . . . . . . . . . . . . . . . . . . . . 82
6.2 Oprateurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
6.3 Rcration : TP suite et n . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
6.4 Fiche de rfrence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
7 La mmoire 87
7.1 Lappel dune fonction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
7.1.1 Exemple . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
7.1.2 Pile des appels et dbuggeur . . . . . . . . . . . . . . . . . . . . . 89
7.2 Variables Locales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
7.2.1 Paramtres . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92
7.2.2 La pile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92
7.3 Fonctions rcursives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92
7.3.1 Pourquoi a marche ? . . . . . . . . . . . . . . . . . . . . . . . . . . 93
7.3.2 Efcacit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
7.4 Le tas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
7.4.1 Limites . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
7.4.2 Tableaux de taille variable . . . . . . . . . . . . . . . . . . . . . . . 95
7.4.3 Essai dexplication . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
7.5 Loptimiseur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
7.6 Assertions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
2
TABLE DES MATIRES TABLE DES MATIRES
7.7 TP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
7.8 Fiche de rfrence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99
7.9 Examens sur machine . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
8 Allocation dynamique 103
8.1 Tableaux bidimensionnels . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
8.1.1 Principe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
8.1.2 Limitations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
8.1.3 Solution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
8.2 Allocation dynamique . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106
8.2.1 Pourquoi a marche ? . . . . . . . . . . . . . . . . . . . . . . . . . . 106
8.2.2 Erreurs classiques . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107
8.2.3 Consquences . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108
8.3 Structures et allocation dynamique . . . . . . . . . . . . . . . . . . . . . . 109
8.4 Boucles et continue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112
8.5 TP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112
8.6 Fiche de rfrence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113
9 Premiers objets 117
9.1 Philosophie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117
9.2 Exemple simple . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118
9.3 Visibilit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
9.4 Exemple des matrices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120
9.5 Cas des oprateurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122
9.6 Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124
9.7 Protection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125
9.7.1 Principe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125
9.7.2 Structures vs Classes . . . . . . . . . . . . . . . . . . . . . . . . . . 127
9.7.3 Accesseurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127
9.8 TP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128
9.9 Fiche de rfrence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128
10 Constructeurs et Destructeurs 133
10.1 Le problme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133
10.2 La solution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134
10.3 Cas gnral . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134
10.3.1 Constructeur vide . . . . . . . . . . . . . . . . . . . . . . . . . . . 134
10.3.2 Plusieurs constructeurs . . . . . . . . . . . . . . . . . . . . . . . . 136
10.3.3 Tableaux dobjets . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137
10.4 Objets temporaires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138
10.5 TP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139
10.6 Rfrences Constantes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139
10.6.1 Principe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139
10.6.2 Mthodes constantes . . . . . . . . . . . . . . . . . . . . . . . . . . 140
10.7 Destructeur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142
10.8 Destructeurs et tableaux . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144
10.9 Constructeur de copie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144
10.10Affectation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145
10.11Objets avec allocation dynamique . . . . . . . . . . . . . . . . . . . . . . . 146
3
TABLE DES MATIRES TABLE DES MATIRES
10.11.1 Construction et destruction . . . . . . . . . . . . . . . . . . . . . . 146
10.11.2 Problmes ! . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147
10.11.3 Solution! . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148
10.12Fiche de rfrence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151
10.13Devoir crit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154
11 En vrac... 155
11.1 Chanes de caratres . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155
11.2 Fichiers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157
11.2.1 Principe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157
11.2.2 Chanes et chiers . . . . . . . . . . . . . . . . . . . . . . . . . . . 158
11.2.3 Objets et chiers . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158
11.3 Valeurs par dfaut . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159
11.3.1 Principe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159
11.3.2 Utilit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160
11.3.3 Erreurs frquentes . . . . . . . . . . . . . . . . . . . . . . . . . . . 160
11.4 Accesseurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160
11.4.1 Rfrence comme type de retour . . . . . . . . . . . . . . . . . . . 161
11.4.2 Utilisation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
11.4.3 operator() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162
11.4.4 Surcharge et mthode constante . . . . . . . . . . . . . . . . . . . 162
11.4.5 "inline" . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163
11.5 Assertions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165
11.6 Types numrs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165
11.7 Fiche de rfrence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166
12 En vrac (suite) ... 171
12.1 Oprateur binaires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171
12.2 Valeur conditionnelle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172
12.3 Boucles et break . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173
12.4 Variables statiques . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174
12.5 const et tableaux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175
12.6 template . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175
12.6.1 Principe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175
12.6.2 template et chiers . . . . . . . . . . . . . . . . . . . . . . . . . . 177
12.6.3 Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177
12.6.4 STL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180
12.7 Fiche de rfrence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182
12.8 Devoir nal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187
13 Structure de donnes 189
13.1 Rappels sur les tableaux . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189
13.2 La complexit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190
13.2.1 Mais quest-ce donc que la complexit ? . . . . . . . . . . . . . . . 190
13.2.2 Comment la mesure-t-on? . . . . . . . . . . . . . . . . . . . . . . . 191
13.2.3 La notation O . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191
13.2.4 P et NP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191
13.2.5 Pourquoi est-ce important ? . . . . . . . . . . . . . . . . . . . . . . 192
13.3 Le vecteur : un tableau encapsul dans une classe . . . . . . . . . . . . . 192
4
TABLE DES MATIRES TABLE DES MATIRES
13.3.1 Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192
13.3.2 Complexit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193
13.3.3 Gestion mmoire . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193
13.4 La pile, Last In First Out (LIFO) . . . . . . . . . . . . . . . . . . . . . . . . 193
13.5 La le, First In First Out (FIFO) . . . . . . . . . . . . . . . . . . . . . . . . 194
13.6 La liste chane . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196
13.7 Rcapitulatif des complexits . . . . . . . . . . . . . . . . . . . . . . . . . 197
13.8 Les itrateurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 197
13.9 Autres structures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 198
14 Algorithmes de tri 199
14.1 Complexit minimale . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199
14.2 Algorithmes quadratiques . . . . . . . . . . . . . . . . . . . . . . . . . . . 199
14.3 QuickSort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 200
14.4 File de priorit et HeapSort . . . . . . . . . . . . . . . . . . . . . . . . . . 202
14.4.1 Insertion dans la le de priorit (push) . . . . . . . . . . . . . . . 202
14.4.2 Retrait de la le de priorit (pop) . . . . . . . . . . . . . . . . . . . 203
14.4.3 Stockage de la le de priorit . . . . . . . . . . . . . . . . . . . . . 203
14.4.4 HeapSort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204
14.5 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204
A Travaux Pratiques 205
A.1 Lenvironnement de programmation . . . . . . . . . . . . . . . . . . . . . 205
A.1.1 Bonjour, Monde ! . . . . . . . . . . . . . . . . . . . . . . . . . . . . 205
A.1.2 Premires erreurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . 207
A.1.3 Debugger . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209
A.1.4 Sil reste du temps . . . . . . . . . . . . . . . . . . . . . . . . . . . 210
A.1.5 Installer Imagine++ chez soi . . . . . . . . . . . . . . . . . . . . . . 210
A.2 Variables, boucles, conditions, fonctions . . . . . . . . . . . . . . . . . . . 211
A.2.1 Premier programme avec fonctions . . . . . . . . . . . . . . . . . 211
A.2.2 Premier programme graphique avec Imagine++ . . . . . . . . . . 211
A.2.3 Jeu de Tennis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 213
A.3 Tableaux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215
A.3.1 Mastermind Texte . . . . . . . . . . . . . . . . . . . . . . . . . . . 215
A.3.2 Mastermind Graphique . . . . . . . . . . . . . . . . . . . . . . . . 217
A.4 Structures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 219
A.4.1 Etapes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 219
A.4.2 Aide . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221
A.4.3 Thorie physique . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222
A.5 Fichiers spars . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 224
A.5.1 Fonctions outils . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 224
A.5.2 Vecteurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 224
A.5.3 Balle part . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 225
A.5.4 Retour la physique . . . . . . . . . . . . . . . . . . . . . . . . . . 225
A.6 Les tris . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 227
A.6.1 Mlanger un tableau . . . . . . . . . . . . . . . . . . . . . . . . . . 227
A.6.2 Tris quadratiques . . . . . . . . . . . . . . . . . . . . . . . . . . . . 228
A.6.3 Quicksort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 229
A.6.4 Gros tableaux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 230
5
TABLE DES MATIRES TABLE DES MATIRES
A.7 Images . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231
A.7.1 Allocation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231
A.7.2 Tableaux statiques . . . . . . . . . . . . . . . . . . . . . . . . . . . 231
A.7.3 Tableaux dynamiques . . . . . . . . . . . . . . . . . . . . . . . . . 232
A.7.4 Charger un chier . . . . . . . . . . . . . . . . . . . . . . . . . . . 232
A.7.5 Fonctions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 232
A.7.6 Structure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 233
A.7.7 Suite et n . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 233
A.8 Premiers objets et dessins de fractales . . . . . . . . . . . . . . . . . . . . 234
A.8.1 Le triangle de Sierpinski . . . . . . . . . . . . . . . . . . . . . . . . 234
A.8.2 Une classe plutt quune structure . . . . . . . . . . . . . . . . . . 235
A.8.3 Changer dimplmentation . . . . . . . . . . . . . . . . . . . . . . 235
A.8.4 Le ocon de neige . . . . . . . . . . . . . . . . . . . . . . . . . . . 236
A.9 Tron . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 237
A.9.1 Serpent . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 237
A.9.2 Tron . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 238
A.9.3 Graphismes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 238
B Examens 241
B.1 Examen sur machine 2011 : nonc . . . . . . . . . . . . . . . . . . . . . . 241
B.1.1 Automate cellulaire . . . . . . . . . . . . . . . . . . . . . . . . . . . 241
B.1.2 Travail demand . . . . . . . . . . . . . . . . . . . . . . . . . . . . 242
B.2 Examen sur machine 2010 : nonc . . . . . . . . . . . . . . . . . . . . . . 244
B.2.1 Visualisation 3D par z-buffer . . . . . . . . . . . . . . . . . . . . . 244
B.2.2 Travail demand . . . . . . . . . . . . . . . . . . . . . . . . . . . . 244
B.2.3 Annexe : les formules . . . . . . . . . . . . . . . . . . . . . . . . . 247
B.3 Examen sur machine 2009 : nonc . . . . . . . . . . . . . . . . . . . . . . 249
B.3.1 Spirale dUlam . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 249
B.3.2 Travail demand . . . . . . . . . . . . . . . . . . . . . . . . . . . . 249
B.4 Examen sur machine 2008 : nonc . . . . . . . . . . . . . . . . . . . . . . 251
B.4.1 Ensemble de Mandelbrot . . . . . . . . . . . . . . . . . . . . . . . 251
B.4.2 Travail demand . . . . . . . . . . . . . . . . . . . . . . . . . . . . 251
B.5 Examen sur machine 2007 : nonc . . . . . . . . . . . . . . . . . . . . . . 253
B.5.1 Chemins entre deux points . . . . . . . . . . . . . . . . . . . . . . 253
B.5.2 Travail demand . . . . . . . . . . . . . . . . . . . . . . . . . . . . 253
B.6 Examen sur machine 2006 : nonc . . . . . . . . . . . . . . . . . . . . . . 254
B.6.1 Voyageur de commerce par recuit simul . . . . . . . . . . . . . . 254
B.6.2 Travail demand . . . . . . . . . . . . . . . . . . . . . . . . . . . . 254
B.7 Examen sur machine 2005 : nonc . . . . . . . . . . . . . . . . . . . . . . 256
B.7.1 Construction du Modle 3D . . . . . . . . . . . . . . . . . . . . . . 256
B.7.2 Projection : du 3D au 2D . . . . . . . . . . . . . . . . . . . . . . . . 256
B.7.3 Afchage lcran . . . . . . . . . . . . . . . . . . . . . . . . . . . 257
B.7.4 Animation du ttradre . . . . . . . . . . . . . . . . . . . . . . . . 257
B.7.5 Un modle plus labor . . . . . . . . . . . . . . . . . . . . . . . . 258
B.8 Examen sur machine 2004 : nonc . . . . . . . . . . . . . . . . . . . . . . 259
B.8.1 Calcul de lexponentielle dun nombre complexe . . . . . . . . . . 259
B.8.2 Compression RLE . . . . . . . . . . . . . . . . . . . . . . . . . . . 259
B.9 Examen sur machine 2003 : nonc . . . . . . . . . . . . . . . . . . . . . . 263
B.9.1 Crible dratosthne . . . . . . . . . . . . . . . . . . . . . . . . . . 263
6
TABLE DES MATIRES TABLE DES MATIRES
B.9.2 Calcul de par la mthode de Monte Carlo . . . . . . . . . . . . . 263
B.9.3 Serpent . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 265
B.10 Devoir maison 2007 : nonc . . . . . . . . . . . . . . . . . . . . . . . . . . 267
B.11 Devoir maison 2006 : nonc . . . . . . . . . . . . . . . . . . . . . . . . . . 269
B.11.1 Enonc Tours de Hanoi . . . . . . . . . . . . . . . . . . . . . . . 269
B.12 Devoir maison 2004 : nonc . . . . . . . . . . . . . . . . . . . . . . . . . . 272
B.12.1 Tableau dexcution . . . . . . . . . . . . . . . . . . . . . . . . . . 272
B.12.2 Constructeurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 273
B.12.3 Le compte est bon . . . . . . . . . . . . . . . . . . . . . . . . . . . 275
B.13 Devoir maison 2003 : nonc . . . . . . . . . . . . . . . . . . . . . . . . . . 278
B.13.1 Tableau dexcution . . . . . . . . . . . . . . . . . . . . . . . . . . 278
B.13.2 Grands entiers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 279
B.13.3 Constructeurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 279
B.14 Devoir surveill 2011 : nonc . . . . . . . . . . . . . . . . . . . . . . . . . 282
B.14.1 Liste avec sauts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 282
B.14.2 Travail demand . . . . . . . . . . . . . . . . . . . . . . . . . . . . 283
B.15 Devoir surveill 2010 : nonc . . . . . . . . . . . . . . . . . . . . . . . . . 286
B.15.1 Programmation : machine registres . . . . . . . . . . . . . . . . . 286
B.15.2 Algorithmique : ensemble sans doublon . . . . . . . . . . . . . . . 288
B.16 Devoir surveill 2009 : nonc . . . . . . . . . . . . . . . . . . . . . . . . . 290
B.16.1 Quafche ce programme ? . . . . . . . . . . . . . . . . . . . . . . 290
B.16.2 Programmation : enrichir la classe polynme . . . . . . . . . . . . 293
B.16.3 Algorithmie : problme de slection . . . . . . . . . . . . . . . . . 293
B.17 Devoir surveill 2008 : nonc . . . . . . . . . . . . . . . . . . . . . . . . . 294
B.17.1 Erreurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 294
B.17.2 Problme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 296
B.18 Devoir surveill 2007 : nonc . . . . . . . . . . . . . . . . . . . . . . . . . 298
B.18.1 Groupes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 298
B.18.2 Anniversaires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 300
B.19 Devoir surveill 2006 : nonc . . . . . . . . . . . . . . . . . . . . . . . . . 301
B.19.1 Erreurs corriger . . . . . . . . . . . . . . . . . . . . . . . . . . . . 301
B.19.2 Quafche ce programme ? . . . . . . . . . . . . . . . . . . . . . . 302
B.19.3 Tableau dexcution . . . . . . . . . . . . . . . . . . . . . . . . . . 304
B.19.4 Huit dames . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 306
B.20 Devoir surveill 2005 : nonc . . . . . . . . . . . . . . . . . . . . . . . . . 308
B.20.1 Erreurs corriger . . . . . . . . . . . . . . . . . . . . . . . . . . . . 308
B.20.2 Quafche ce programme ? . . . . . . . . . . . . . . . . . . . . . . 308
B.20.3 Tableau dexcution . . . . . . . . . . . . . . . . . . . . . . . . . . 310
B.20.4 Rsolveur de Sudoku . . . . . . . . . . . . . . . . . . . . . . . . . . 311
B.21 Devoir surveill 2004 : nonc . . . . . . . . . . . . . . . . . . . . . . . . . 314
B.21.1 Erreurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 314
B.21.2 Quafche ce programme ? . . . . . . . . . . . . . . . . . . . . . . 315
B.21.3 Chemins dans un graphe . . . . . . . . . . . . . . . . . . . . . . . 318
B.21.4 Tours de Hano . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 319
B.21.5 Table de hachage . . . . . . . . . . . . . . . . . . . . . . . . . . . . 322
B.22 Devoir surveill 2003 : nonc . . . . . . . . . . . . . . . . . . . . . . . . . 324
B.22.1 Tableau dexcution . . . . . . . . . . . . . . . . . . . . . . . . . . 324
B.22.2 Erreurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 325
B.22.3 Quafche ce programme ? . . . . . . . . . . . . . . . . . . . . . . 326
7
TABLE DES MATIRES TABLE DES MATIRES
B.22.4 Le jeu du Pendu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 328
B.22.5 Programme mystre . . . . . . . . . . . . . . . . . . . . . . . . . . 329
C Imagine++ 331
C.1 Common . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 331
C.2 Graphics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 331
C.3 Image . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 332
C.4 LinAlg . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 333
D Fiche de rfrence nale 335
8
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 ! Protons-en pour tenir des propos
qui ne les concernent pas...)

Le Matre Programmeur
1
: "Rassure-toi ! Les ordinateurs sont stupides ! Program-
mer est donc facile."
LApprenti Programmeur
2
: "Matre, les ordinateurs ne sont certes que des ma-
chines et les dominer devrait tre ma porte. Et pourtant... Leur manque din-
telligence 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 incom-
prhensible, 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 incompr-
hensibles, ils constituent en ralit un argot propre au mtier dinformaticien, argot que doit bien vi-
demment 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 com-
prhensibles. 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 r-
soudre 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 claire-


ment 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 difcile.
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 :
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.
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.
10
1. Prambule 1.1. Pourquoi savoir programmer ?
oublie quil a le plus souvent appris seul quand il tait plus jeune, en program-
mant 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 "al-
lergique" aux ordinateurs, il se verra vraisemblablement coner 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 programma-
tion. 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 difcile 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 coll-
gien, 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 in-
collable sur les jeux, internet, les logiciels graphiques ou musicaux, linstallation ou
la conguration 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 jour-
ne se tenir au courant des nouveaux logiciels
11
et en oublie quil pourrait lui aussi
en crer. En conclusion, collgiens ou ingnieurs, dbutants ou passionns, tous les
lves sont galit. Cest donc sans complexe que lingnieur pourra apprendre
programmer en mme temps que le ls de la voisine.
1.1 Pourquoi savoir programmer ?
Nous venons partiellement de le dire. Rsumons et compltons :
7. Cest une erreur frquente de croire quil intressera ses lves en leur faisant faire des pro-
grammes centrs sur les mathmatiques ou le calcul scientique. De tels programmes leur seront peut-
tre 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 ipper, 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 !
11
1.2. Comment apprendre ? 1. Prambule
1. Cest la base. Apprendre un langage prcis nest pas du temps perdu car les
mmes concepts se retrouvent dans la plupart des langages. De plus, les logiciels
courants eux-mmes peuvent se programmer.
2. Il est frquent quun stage ou quune embauche en premier poste comporte un
peu de programmation, mme, et peut-tre surtout, dans les milieux o peu de
gens programment.
3. Savoir programmer, cest mieux connatre le matriel et les logiciels, ce qui est
possible techniquement et ce qui ne lest pas. Mme un poste non technique,
cest important pour prendre les bonnes dcisions.
1.2 Comment apprendre ?
1.2.1 Choix du langage
Il faut dabord choisir un langage de programmation. Un ingnieur pourrait vi-
demment tre tent dapprendre programmer en Maple, Matlab, Scilab ou autre. Il
faut quil comprenne quil sagit l doutils spcialiss pour mathmaticien ou ing-
nieur qui lui seront utiles et qui, certes, se programment, mais pas proprement parler
de langages gnralistes complets. Sans argumenter sur les dfauts respectifs des lan-
gages 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 simplication 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 sufsant. Nous ne verrons donc dans ce cours quun sous ensemble du
C++, sufsant 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 pro-
grammation 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 efcace
14
.
3. C++ est souvent incontournable dans certains milieux, par exemple en nance.
4. Enn, 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.
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 pro-
grammes inconsidrment gourmands en temps ou mmoire.
15. Les oprateurs par exemple.
12
1. Prambule 1.2. Comment apprendre ?
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 conrm, 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 co-
hrente.
Editer ses chiers, les transformer en programme, passer en revue ses erreurs,
dtecter les bugs, parcourir la documentation, etc. tout cela avec un seul outil
ergonomique.
Sans arrire pense de principe, nous avons opt pour lenvironnement de Microsoft,
Visual Studio, la fois simple et puissant. Il est le plus utilis des produits commer-
ciaux. Il en existe quelques quivalents gratuits sous linux, mais pas encore sufsam-
ment aboutis pour nous faire hsiter. Cest donc le choix de Visual Studio et ce choix
seul qui est la raison de lutilisation de Windows au dtriment de linux... Mieux en-
core, il existe maintenant une version de Visual gratuite : Visual Express. Comme pour
le choix du langage, le choix de lenvironnement nest pas limitant et en connatre un
permet de sadapter facilement nimporte quel autre.
1.2.3 Principes et conseils
Au niveau auquel nous prtendons lenseigner, la programmation ne requiert ni
grande thorie, ni connaissances encyclopdiques. Les concepts utiliss sont rudimen-
taires mais cest leur mise en oeuvre qui est dlicate. Sil ny avait quun seul conseil
donner, ce serait la rgle des trois "P" :
1. Programmer
2. Programmer
3. Programmer
La pratique est effectivement essentielle. Cest ce qui fait quun enfant a plus de facili-
ts, 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 tton-
ner, tester, fouiller, faire, dfaire, casser, etc. Lordinateur est un outil exprimen-
tal. Mais sa programmation est elle aussi une activit exprimentale la base.
Mme si le programmeur aguerri trouvera la bonne solution du premier jet, il
16. Lidal est en fait davoir les deux "sous la main".
13
1.2. Comment apprendre ? 1. Prambule
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 dap-
prentissage pour mieux les connatre est le meilleur moyen de comprendre beau-
coup de choses et aussi de reprer ces erreurs quand elles ne seront plus volon-
taires.
4. Rester (le) matre
17
(de la machine et de son programme). Que programmer soit
exprimental ne signie pas pour autant quil faille faire nimporte quoi jusqu
ce que a marche plus ou moins. Il faut avancer progressivement, mthodique-
ment, en testant au fur et mesure, sans laisser passer la moindre erreur ou im-
prcision.
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 environ-
nement de programmation facilite la tche.

Gardons bien prsents ces quelques principes car il est maintenant temps de...
passer notre premier programme !
17. Le vocabulaire nest pas choisi au hasard : un programme est une suite dordres, de commandes ou
dinstructions. On voit bien qui est le chef !
14
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. Enn, je suis pris mon propre pige ! Alors, tout hasard, je vais
tenter dexpliquer au passage les mathmatiques qui pourraient leur poser problme.)

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


doit toujours ressembler a :
# i ncl ude <i ostream>
usi ng namespace st d ;
i nt main ( )
{
cout << " Hello , World ! " << endl ;
ret urn 0;
}
Eh bien, allons-y ! Dcortiquons-le ! Dans ce programme, qui afche lcran
1
le texte
"Hello, World!", les lignes 1 et 2 sont des instructions magiques
2
qui servent pou-
voir utiliser dans la suite cout et endl. La ligne 4 int main() dnit une fonction appele
main(), qui renvoie
3
un nombre entier. Cette fonction est spciale car cest la fonction
principale dun programme C++, celle qui est appele automatiquement
4
quand le
1. Cette expression, vestige de lpoque o les ordinateurs taient dots dun cran capable de naf-
cher que des caractres et non des graphiques (courbes, dessins, etc.), signie aujourdhui que lafchage
se fera dans une fentre simulant lcran dun ordinateur de cette poque. Cette fentre est appele ter-
minal, console, fentre de commande, fentre DOS, xterm, etc. suivant les cas. Souvenons nous avec
un minimum de respect que ctait dj un progrs par rapport la gnration prcdente, dpourvue
dcran et qui utilisait une imprimante pour communiquer avec lhomme... ce qui tait relativement peu
interactif !
2. Entendons par l des instructions que nous nexpliquons pas pour linstant. Il ny a (mal ?)-
heureusement rien de magique dans la programmation.
3. On dit aussi retourne. A qui renvoie-t-elle cet entier ? Mais celui qui la appele, voyons !
4. Voil, maintenant vous savez qui appelle main(). Dans un programme, les fonctions sappellent
les unes les autres. Mais main() nest appele par personne puisque cest la premire de toutes. (Du
moins en apparence car en ralit le programme a plein de choses faire avant darriver dans main()
et il commence par plusieurs autres fonctions que le programmeur na pas connatre et qui nissent
par appeler main(). Dailleurs, si personne ne lappelait, qui main() retournerait-elle un entier ?)
2. Bonjour, Monde !
programme est lanc
5
. Dlimite par les accolades ({ ligne 5 et } ligne 8), la fonction
main() se termine ligne 7 par return 0; qui lui ordonne de retourner lentier 0. Notons
au passage que toutes les instructions se terminent par un point-virgule ;. Enn, la
ligne 6, seule ligne "intressante", cout << "Hello, World!"<< endl; afche, 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 ins-
tructions 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 efcacement
dordre quelquun sans comprendre comment il fonctionne ni ce que les ordres don-
ns 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 minterrom-
prait souvent. Mais bon. Donc, un programme dmarre ou est lanc. Aprs quoi, il sexcute ou tourne.
Enn, 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 afche dans une fentre console !
8. En clair, un texte.
9. Ce qui signie que la suite de lafchage sur la console se fera sur une nouvelle ligne.
16
2. Bonjour, Monde! 2.1. Lordinateur
Chapitre 2 (deuxime essai)
Comment a marche ?
Le problme avec le programme prcdent est quil est trs loin de ce quun ordi-
nateur sait faire naturellement. En fait, un ordinateur ne sait pas faire de C++. Il ne
sait que calculer
10
, transformer des nombres en autres nombres. Bien que peu compr-
hensible pour le dbutant, un programme en C++ se veut le plus proche possible de
lHomme, tout en restant videmment accessible
11
la machine. Le C++ est un lan-
gage trs complet, peut-tre mme trop. Il peut tre relativement proche de la machine
si ncessaire et au contraire de "haut niveau" quand il le faut. La largeur de son spectre
est une des raisons de son succs. Cest aussi ce qui fait que son apprentissage complet
demande un long travail et nous ne verrons ici quun partie restreinte du C++!
2.1 Lordinateur
Pour savoir ce quun ordinateur sait vraiment faire, il faut commencer par son or-
gane 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.
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 :
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 envi-
ron 3GHz. (Mais attention : une instruction demande plus dun cycle dhorloge !)
14. Aujourdhui, typiquement 1Go (giga-octets), soit 102410241024 mmoires de 8 bits (mmoires
pouvant stocker des nombres entre 0 et 255).
17
2.1. Lordinateur 2. Bonjour, Monde !
00415A61 mov eax,dword ptr [a] // mettre dans le registre eax
// le contenu de ladresse o
// est mmorise la variable a
00415A64 imul eax,eax,3 // effectuer eax=eax
*
3
00415A67 mov ecx,dword ptr [b] // idem mais b dans ecx
00415A6A lea edx,[eax+ecx
*
2] // effectuer edx=eax+ecx
*
2
00415A6D mov dword ptr [c],edx // mettre le contenu du registre edx
// ladresse o est mmorise la
// variable c
Sous lenvironnement Visual Studio que nous utiliserons, ce programme est dsi-
gn 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 (at-
tention, 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 instruc-
tion est vraiment une instruction du micro-processeur, mais o le nom de ces instruc-
tions ainsi que leurs arguments sont explicites. En ralit, le micro-processeur ne com-
prend 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 8B 45 F8
00415A64 6B C0 03
00415A67 8B 4D EC
00415A6A 8D 14 48
00415A6D 89 55 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 tra-
duction 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
spcique 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 8B 45 F8 mov eax,dword ptr [a]
00415A64 6B C0 03 imul eax,eax,3
00415A67 8B 4D EC mov ecx,dword ptr [b]
00415A6A 6B C9 03 imul ecx,ecx,3
00415A6D 03 C1 add eax,ecx
00415A6F 89 45 E0 mov dword ptr [c],eax
car "lea edx,[eax+ecx
*
3]" nexiste pas !
Mais revenons nos nombres...
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 enn programmer en assembleur !
18
2. Bonjour, Monde ! 2.1. Lordinateur
2.1.2 La mmoire
La mmoire interne du micro-processeur est gre comme des registres, un peu
comme les variables du C++, mais en nombre prdni. Pour stocker
17
la suite dins-
tructions lui fournir, on utilise de la mmoire en quantit bien plus importante, d-
signe en gnral par la mmoire de lordinateur. Il sagit des fameuses "barrettes"
18
de
mmoire que lon achte pour augmenter la capacit de sa machine et dont les prix
uctuent assez fortement par rapport au reste des composants dun ordinateur. Cette
mmoire est dcoupe en octets. Un octet
19
correspond un nombre binaire de 8 bits
20
,
soit 2
8
= 256 valeurs possibles. Pour se reprer dans la mmoire, il nest pas ques-
tion de donner des noms chaque octet. On numrote simplement les octets et on
obtient ainsi des adresses mmoire. Les nombres 00415A61, etc. vus plus haut sont des
adresses ! Au dbut, ces nombres taient crits en binaire, ce qui tait exactement ce
que comprenait le micro-processeur. Cest devenu draisonnable quand la taille de
la mmoire a dpass les quelques centaines doctets. Le contenu dun octet de m-
moire tant lui aussi donn sous la forme dun nombre, on a opt pour un systme
adapt au fait que ces nombres sont sur 8 bits : plutt que dcrire les nombre en bi-
naire, le choix de la base 16 permettait de reprsenter le contenu dun octet sur deux
chiffres (0,1,...,9,A,B,C,D,E,F). Le systme hexadcimal
21
tait adopt... Les conversions
de binaire hexadcimal sont trs simples, chaque chiffre hexadcimal valant pour un
paquet de 4 bits, alors quentre binaire et dcimal, cest moins immdiat. Il est aujour-
dhui encore utilis quand on dsigne le contenu dun octet ou une adresse
22
. Ainsi,
notre fameux c=3a+2b; devient en mmoire :
adresse mmoire contenu reprsente
00415A61 8B
00415A62 45 mov eax,dword ptr [a]
00415A63 F8
00415A64 6B
00415A65 C0 imul eax,eax,3
... ...
17. Encore un anglicisme...
18. Aujourdhui, typiquement une ou plusieurs barrettes pour un total de 1 ou 2Go, on la dj dit.
Souvenons nous avec une larme loeil des premiers PC qui avaient 640Ko (kilo-octet soit 1024 octets),
voire pour les plus ags dentre nous des premiers ordinateurs personnels avec 4Ko, ou mme des
premires cartes programmables avec 256 octets !
19. byte en anglais. Attention donc ne pas confondre byte et bit, surtout dans des abrviations comme
512kb/s donnes pour le dbit dun accs internet... b=bit, B=byte=8 bits
20. Le coin des collgiens : en binaire, ou base 2, on compte avec deux chiffres au lieu de dix dha-
bitude (cest dire en dcimal ou base 10). Cela donne : 0, 1, 10, 11, 100, 101, 110, 111, ... Ainsi, 111 en
binaire vaut 7 . Chaque chiffre sappelle un bit. On voit facilement quavec un chiffre on compte de 0
1 soit deux nombres possibles ; avec deux chiffres, de 0 3, soit 4 = 2 2 nombres ; avec 3 chiffres, de
0 7, soit 8 = 2 2 2 nombres. Bref avec n bits, on peut coder 2
n
(2 multipli par lui-mme n fois)
nombres. Je me souviens avoir appris la base 2 en grande section de maternelle avec des cubes en bois !
trange programme scolaire. Et je ne dis pas a pour me trouver une excuse dtre devenu informaticien.
Quoique...
21. Coin des collgiens (suite) : en base 16, ou hexadcimal, on compte avec 16 chiffres. Il faut inven-
ter des chiffres au del de 9 et on prend A,B,C,D,E,F. Quand on compte, cela donne : 0, 1, 2, ..., 9, A, B,
C, D, E, F, 10, 11, 12, 13, ..., 19, 1A, 1B, 1C, ... Ainsi 1F en hexadcimal vaut 31. Avec 1 chiffre, on compte
de 0 15 soit 16 nombres possibles ; avec 2 chiffres, de 0 255 soit 256 = 16 16 nombres possibles, etc.
Un octet peut scrire avec 8 bits en binaire, ou 2 nombres en hexadcimal et va de 0 255, ou 11111111
en binaire, ou FF en hexadcimal.
22. Dans ce cas, sur plus de 2 chiffres : 8 pour les processeurs 32 bits, 16 pour les processeurs 64 bits.
19
2.1. Lordinateur 2. Bonjour, Monde !
La mmoire ne sert pas uniquement stocker la suite dinstructions excuter mais
aussi toutes les variables et donnes du programme, les registres du micro-processeur
tant insufsants. Ainsi nos variables a, b,c sont stockes quelque part en mmoire sur
un nombre doctets sufsant
23
pour reprsenter des nombres entiers (ici 4 octets) et
dans un endroit dcid par le C++, de tel sorte que linstruction 8B45F8 aille bien
chercher la variable a ! Cest un travail pnible, que le C++ fait pour nous et que les
programmeurs faisaient autrefois la main
24
. Bref, on a en plus
25
:
adresse mmoire contenu reprsente
... ...
00500000 a
1
00500001 a
2
a
00500002 a
3
00500003 a
4
00500004 b
1
00500005 b
2
b
00500006 b
3
00500007 b
4
... ...
o les octets a
1
, ..., a
4
combins donnent lentier a sur 32 bits. Certains processeurs (dits
big-endian)
26
dcident a = a
1
a
2
a
3
a
4
, dautres (little-endian)
27
que a = a
4
a
3
a
2
a
1
. Cela
signie que :
Tout comme pour les instructions, un nombre stock par un micro-
processeur dans un chier peut ne pas tre comprhensible par un autre
micro-processeur qui relit le chier !
2.1.3 Autres Composants
Micro-processeur et mmoire : nous avons vu le principal. Compltons le tableau
avec quelques autres lments importants de lordinateur.
23. Les variables ayant plus de 256 valeurs possibles sont forcment stockes sur plusieurs octets.
Ainsi, avec 4 octets on peut compter en binaire sur 4 8 = 32 bits, soit 2
32
valeurs possibles (plus de 4
milliards).
24. Ce qui tait le plus pnible ntait pas de dcider o il fallait ranger les variables en mmoire, mais
dajuster les instructions en consquence. Si on se trompait, on risquait dcrire au mauvais endroit de
la mmoire. Au mieux, cela effaait une autre variable ce comportement est encore possible de nos
jours au pire, cela effaait des instructions et le programme pouvait faire de "grosses btises" ceci
est aujourdhui impossible sous Windows ou Linux, et ne concerne plus que certains systmes.
25. Nous faisons ici un horrible mensonge des ns simplicatrices. Dans notre cas, les variables
taient des variables locales la fonction main() donc stockes dans la pile. Elles ne sont pas une
adresse mmoire dnie lavance de manire absolue mais une adresse relative lemplacement
o la fonction rangera ses variables locales en fonction de ce que le programme aura fait avant. Cela
explique la simplicit de linstruction mov eax,dword ptr [a] dans notre cas. Nous verrons tout
cela plus tard.
26. Comme les PowerPC des vieux Macs
27. Comme les processeurs Intel et AMD
20
2. Bonjour, Monde ! 2.1. Lordinateur
Types de mmoire
La mmoire dont nous parlions jusquici est de la mmoire vive ou RAM. Elle est
rapide
28
mais a la mauvaise ide de seffacer quand on teint lordinateur. Il faut donc
aussi de la mmoire morte ou ROM, cest--dire de la mmoire conservant ses donnes
quand lordinateur est teint mais qui en contre-partie ne peut tre modie
29
. Cette
mmoire contient en gnral le minimum pour que lordinateur dmarre et excute
une tche prdnie. Initialement, on y stockait les instructions ncessaires pour que le
programmeur puisse remplir ensuite la RAM avec les instructions de son programme.
Il fallait retaper le programme chaque fois
30
! On a donc rapidement eu recours des
moyens de stockage pour sauver programmes et donnes lextinction de lordinateur. Il
sufsait alors de mettre en ROM le ncessaire pour grer ces moyens de stockages.
Moyens de stockage
Certains permettent de lire des donnes, dautres den crire, dautres les deux
la fois. Certains ne dlivrent les donnes que dans lordre, de manire squentielle,
dautres, dans lordre que lon veut, de manire alatoire. Ils sont en gnral bien plus
lents que la mmoire et cest srement ce quil faut surtout retenir ! On recopie donc en
RAM la partie des moyens de stockage sur laquelle on travaille.
Faire travailler le micro-processeur avec le disque dur est BEAUCOUP plus
lent quavec la mmoire (1000 fois plus lent en temps daccs, 100 fois plus
en dbit)
a
a. Rajoutez un facteur 50 supplmentaire entre la mmoire et la mmoire cache du proces-
seur !
Au dbut, les moyens de stockages taient mcaniques : cartes ou bandes perfo-
res. 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.
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 difcile de voir encore les choses de cette faon. Ainsi
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 modie. Alors, une poque, on utilisait des mmoires
modiables malgr tout, mais avec du matriel spcialis (EPROMS). Maintenant, on a souvent recours
de la mmoire pouvant se modier de faon logicielle (mmoire "ashable") 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 ashable 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 able, 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.
21
2.2. Systme dexploitation 2. Bonjour, Monde !
les cartes graphiques, qui pouvaient tre considres comme un priphrique allant
avec lcran, sont-elles devenues une partie essentielle de lordinateur, vritables puis-
sances de calcul, tel point que certains programmeur les utilisent pour faire des cal-
culs sans mme afcher quoi que ce soit. Plus encore, cest lordinateur qui est parfois
juste considr comme maillon entre diffrents appareils. Qui appellerait priphrique
un camscope quon relie un ordinateur pour envoyer des vidos sur internet ou les
transfrer sur un DVD? Ce serait presque lordinateur qui serait un priphrique du
camscope !
2.2 Systme dexploitation
Notre vision jusquici est donc la suivante :
1. Le processeur dmarre avec les instructions prsentes en ROM.
2. Ces instructions lui permettent de lire dautres instructions prsentes sur le disque
dur et quil recopie en RAM.
3. Il excute les instructions en question pour il lire des donnes (entres) prsentes
elles-aussi sur le disque dur et gnrer de nouvelles donnes (sorties). A moins
que les entres ou les sorties ne soient changes via les priphriques.
Assez vite, ce principe a volu :
1. Le contenu du disque dur a t organis en chiers. Certains chiers reprsen-
taient des donnes
34
, dautres des programmes
35
, dautres encore contenaient
eux-mmes des chiers
36
.
2. Les processeurs devenant plus rapides et les capacits du disque dur plus impor-
tantes, on a eu envie de grer plusieurs programmes et den excuter plusieurs :
lun aprs lautre, puis plusieurs en mme temps (multi-tches), puis pour plu-
sieurs utilisateurs en mme temps (multi-utilisateurs)
37
, enn avec plusieurs pro-
cesseurs par machine.
Pour grer tout cela, sest dgag le concept de systme dexploitation
38
. Windows, Unix
(dont linux) et MAC/OS sont les plus rpandus. Le systme dexploitation est aujour-
dhui responsable de grer les chiers, les interfaces avec les priphriques ou les uti-
lisateurs
39
, mais son rle le plus dlicat est de grer les programmes (ou tches ou
process) en train de sexcuter. Il doit pour cela essentiellement faire face deux pro-
blmes
40
:
34. Les plus courantes taient les textes, o chaque octet reprsentait un caractre. Ctait le clbre
code ASCII (65 pour A, 66 pour B, etc.). A lre du multimdia, les formats sont aujourdhui nombreux,
concurrents, et plus ou moins normaliss.
35. On parle de chier excutable...
36. Les rpertoires.
37. Aujourdhui, cest pire. Un programme est souvent lui mme en plusieurs parties sexcutant en
mme temps (les threads). Quant au processeur, il excute en permanence plusieurs instructions en mme
temps (on dit quil est super-scalaire) !
38. Operating System
39. Esprons quun jour les utilisateurs ne seront pas eux-aussi des priphriques !
40. Les processeurs ont videmment volu pour aider le systme dexploitation faire cela
efcacement.
22
2. Bonjour, Monde ! 2.3. La Compilation
1. Faire travailler le processeur successivement par petites tranches sur les diff-
rents programmes. Il sagit de donner la main de manire intelligente et qui-
table, 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 m-
moire virtuelle : si un process est dplac un autre endroit de la mmoire physique
(la RAM), il ne sen rend pas compte. On en prote mme pour mettre tempo-
rairement 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 prve-
nir, 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 as-
surer 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 ins-
tructions de celle contenant les donnes. Il est rigoureusement impossible quun
process bugg puisse modier ses instructions ou la mmoire dun autre process
en crivant un mauvais endroit de la mmoire
41
.
Avec larrive des systmes dexploitation, les chiers 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 chier excutable est spcique, 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 com-
patibilit 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 infor-
mations utiles comme la gestion de la mmoire, nous avons entrevu que transformer
un programme C++ en un chier excutable est un travail difcile mais utile. Cer-
tains logiciels disposant dun langage de programmation comme Maple ou Scilab ne
transforment pas leurs programmes en langage machine. Le travail de traduction est
41. Il se contente de modier anarchiquement ses donnes, ce qui est dj pas mal !
23
2.4. Lenvironnement de programmation 2. Bonjour, Monde !
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 lan-
gages, 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
defcacit.
La traduction en code natif ou en byte code dun programme sappelle la compila-
tion
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 chier source, le programme C++, en un chier objet, suite
dinstructions en langage machine.
Cependant, le chier objet ne se suft pas lui-mme. Des instructions supplmen-
taires sont ncessaires pour former un chier 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 chiers source compils par ailleurs en dautres chiers objet, mais quil
veut utiliser dans son programme actuel.
La synthse de ces chiers en un chier excutable sappelle ldition des liens. Le pro-
gramme qui ralise cette opration est plus souvent appel linker quditeur de liens...
En rsum, la production du chier excutable se fait de la faon suivante :
1. Compilation : chier source chier objet.
2. Link : chier objet + autres chiers objets + bibliothque standard ou
autres chier excutable.
2.4 Lenvironnement de programmation
Lenvironnement de programmation est le logiciel permettant de programmer. Dans
notre cas il sagit de Visual Studio Express. Dans dautres cas, il peut simplement sagir
dun ensemble de programmes. Un environnement contient au minimum un diteur
pour crer les chiers sources, un compilateur/linker pour crer les excutables, un de-
42. mme sil est parfois pr-trait pour acclrer lexcution.
43. Par opposition, le "vrai" langage machine du processeur est alors appel code natif.
44. Les principes de la compilation sont une des matires de base de linformatique, traditionnelle et
trs formatrice. Quand on sait programmer un compilateur, on sait tout programmer (videmment, un
compilateur est un programme ! On le programme avec le compilateur prcdent ! Mme chose pour les
systmes dexploitation...). Elle ncessite un cours part entire et nous nen parlerons pas ici !
45. Une bibliothque est en fait un ensemble de chiers objets pr-existants regroups en un seul -
chier. Il peut sagir de la bibliothque des fonctions faisant partie de C++, appele bibliothque standard,
mais aussi dune bibliothque supplmentaire fournie par un tiers.
24
2. Bonjour, Monde ! 2.4. Lenvironnement de programmation
buggeur pour traquer les erreurs de programmation, et un gestionnaire de projet pour
grer les diffrents chiers sources et excutables avec lesquels on travaille.
Nous reportons ici le lecteur au texte du premier TP. En plus de quelques notions
rudimentaires de C++ que nous verrons au chapitre suivant, quelques informations
supplmentaires sont utiles pour le suivre.
2.4.1 Noms de chiers
Sous Windows, lextension (le sufxe) sert se reprer dans les types de chier :
Un chier source C++ se terminera par .cpp
46
.
Un chier objet sera en .obj
Un chier excutable en .exe
Nous verrons aussi plus loin dans le cours :
Les "en-tte" C++ ou headers servant tre inclus dans un chier source : chiers
.h
Les bibliothques (ensembles de chiers objets archivs en un seul chier) : chier
.lib ou .dll
2.4.2 Debuggeur
Lorsquun programme ne fait pas ce quil faut, on peut essayer de comprendre ce
qui ne va pas en truffant son source dinstructions pour imprimer la valeur de certaines
donnes ou simplement pour suivre son droulement. Ca nest videmment pas trs
pratique. Il est mieux de pouvoir suivre son droulement instruction par instruction et
dafcher la demande la valeur des variables. Cest le rle du debuggeur
47
.
Lorsquun langage est interprt, il est relativement simple de le faire sexcute pas
pas car cest le langage lui-mme qui excute le programme. Dans le cas dun langage
compil, cest le micro-processeur qui excute le programme et on ne peut pas larrter
chaque instruction! Il faut alors mettre en place des points darrt en modiant tem-
porairement le code machine du programme pour que le processeur sarrte lorsquil
atteint linstruction correspondant la ligne de source debugger. Si cest compliqu
mettre au point, cest trs simple utiliser, surtout dans un environnement de pro-
grammation graphique.
Nous verrons au fur et mesure des TP comment le debuggeur peut aussi inspecter
les appels de fonctions, espionner la modication dune variable, etc.
2.4.3 TP
Vous devriez maintenant aller faire le TP en annexe A.1. Si la pratique est essen-
tielle, 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 n du TP). Voici donc ce quil faut retenir du TP :
46. Un chier en .c sera considr comme du C. Diffrence avec linux : un chier en .C sera aussi
trait comme du C et non comme du C++!
47. Dbogueur en franais !
25
2.4. Lenvironnement de programmation 2. Bonjour, Monde !
1. Toujours travailler en local et sauvegarder sur le disque partag, cl
USB, etc.
2. Type de projet utilis : Imagine++ Project
3. Nettoyer ses solutions quand on quitte.
4. Lancer directement une excution sauve et gnre automatiquement.
Attention toutefois de ne pas conrmer lexcution si la gnration
sest mal passe.
5. Double-cliquer sur un message derreur positionne lditeur sur ler-
reur.
6. Toujours bien indenter.
7. Ne pas laisser passer des warnings !
8. Savoir utiliser le debuggeur.
9. Touches utiles :
F7 = = Build
F5 = = Start debugging
F10 = = Step over
F11 = = Step inside
Ctrl+K,Ctrl+F = Indent selection

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


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

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


au langage, en diffrents points successifs : les expressions, les fonctions, les variables,
les instructions, etc. Le rsultat est indigeste car il faut alors tre exhaustif sur chaque
point. Nous allons plutt ici essayer de voir les choses telles quelles se prsentent
quand on apprend : progressivement et sur un peu tous les sujets la fois
1
! Ainsi, ce
nest que dans un autre chapitre que nous verrons la faon dont les fonctions mmo-
risent leurs variables dans la "pile".
3.1 Tout dans le main()!
Rien dans les mains, rien dans les poches... mais tout dans le main(). Voici comment
un dbutant programme
2
.
Cest dj une tape importante que de programmer au kilomtre, en plaant
lintgralit du programme dans la fonction main(). Lessentiel est avant
tout de faire un programme qui marche !
3.1.1 Variables
Types
Les variables sont des mmoires dans lesquelles sont stockes des valeurs (ou don-
nes). Une donne ne pouvant tre stocke nimporte comment, il faut chaque fois d-
cider de la place prise en mmoire (nombre doctets) et du format, cest--dire de la faon
dont les octets utiliss vont reprsenter les valeurs prises par la variable. Nous avons
dj rencontr les int qui sont le plus souvent aujourdhui stocks sur quatre octets,
1. La contre-partie de cette prsentation est que ce polycopi, sil est fait pour tre lu dans lordre, est
peut-tre moins adapt servir de manuel de rfrence. .
2. Et bien des lves, ds que le professeur nest plus derrire !
3.1. Tout dans le main()! 3. Premiers programmes
soit 32 bits, et pouvant prendre 2
32
= 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 initia-
lement pour prciser la mmoire et le format des variables quelles sont types. Nous
verrons que le compilateur se livre un certain nombre de vrications de cohrence
de type entre les diffrentes parties dun programme. Ces vrications, 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 prot pour dtecter
des erreurs de programmation, en a avant tout besoin pour traduire le source
C++ en langage machine.
Dnition, Affectation, Initialisation, Constantes
Avant de voir dautres types de variables, regardons sur un exemple la syntaxe
utiliser :
1 i nt i ; / / D f i n i t i o n
2 i =2; / / Af f e c t a t i o n
3 cout << i << " " ;
4 i nt j ;
5 j =i ;
6 i =1; / / Ne mo d i f i e que i , pas j !
7 cout << i << " " << j << " " ;
8 i nt k , l ,m; / / D f i n i t i o n mul t i p l e
9 k=l =3; / / Af f e c t a t i o n mul t i p l e
10 m=4;
11 cout << k << " " << l << " " << m << " " ;
12 i nt n=5 , o=n , p=INT_MAX; / / I n i t i a l i s a t i o n s
13 cout << n << " " << o << " " << p << endl ;
14 i nt q=r =4; / / Er r e ur !
15 const i nt s =12;
16 s =13; / / Er r e ur !
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
28
3. Premiers programmes 3.1. Tout dans le main()!
Les lignes 1 et 2 dnissent 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 n de la ligne, ce qui permet de mettre
des commentaires aidant la comprhension du programme.
La ligne 3 afche la valeur de i puis un espace (sans aller la ligne)
Les lignes 4, 5 et 6 dnissent 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 dnir 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 dnies et affectes en mme temps. En fait,
on parle plutt de variables initialises : elles prennent une valeur initiale en
mme temps quelles sont dnies. Notez que, pour des raisons defcacit, 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 in-
terdit. La ligne 14 provoque une erreur.
Enn, on peut rajouter const devant le type dune variable : celle-ci devient alors
constante et on ne peut modier son contenu. La ligne 15 dnit une telle variable
et la ligne 16 est une erreur.
En rsum, une fois les lignes 14 et 16 supprimes, ce (passionnant !) programme af-
che
10
:
2 1 2 3 3 4 5 5 2147483647
Les noms de variable sont composs uniquement des caractres a z (et majus-
cules), 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 dnies au fur et mesure des be-
soins. Ce nest pas une vidence. Par exemple, le Cne permettait de dnir les variables
que toutes dun coup au dbut du main(). En C++, on peut dnir les variables en cours
de route, ce qui permet davantage de clart. Mais attention :
8. Le nom dune variable est aussi appel identicateur. Les messages derreur du compilateur utilise-
ront 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
29
3.1. Tout dans le main()! 3. Premiers programmes
les variables "nexistent" (et ne sont donc utilisables) qu partir de la ligne
o elles sont dnies. Elles ont une dure de vie limite et meurent ds que
lon sort du bloc limit par des accolades auquel elles appartiennent
a
. Cest
ce quon appelle la porte dune variable.
a. Cest un peu plus compliqu pour les variables globales. Nous verrons a aussi...
Ainsi, en prenant un peu davance sur la syntaxe des tests, que nous allons voir tout
de suite, le programme suivant provoque des erreurs de porte aux lignes 2 et 8 :
i nt i ;
i =j ; / / Er r e ur : j n e x i s t e pas e nc o r e !
i nt j =2;
i f ( j >1) {
i nt k=3;
j =k ;
}
i =k ; / / Er r e ur : k n e x i s t e pl us .
Autres types
Nous verrons les diffrents types au fur et mesure. Voici malgr tout les plus
courants :
i nt i =3; / / Ent i e r r e l a t i f
double x =12. 3; / / Nombre r e l ( do ubl e p r c i s i o n )
char c= A ; / / Ca r a c t r e
s t r i ng s=" hop" ; / / Cha ne de c a r a c t r e s
bool t =t r ue ; / / Bo o l e n ( v r a i ou f aux )
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 oc-
tet (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 heu-
reusement 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
. Enn, 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 ; / / Nombre r e l s i mpl e p r c i s i o n
unsigned i nt j =4; / / Ent i e r na t ur e l
si gned char d=128; / / Ent i e r r e l a t i f un o c t e t
unsigned char d=254; / / Ent i e r na t ur e l un o c t e t
complex<double> z ( 2 , 3 ) ; / / Nombre c ompl e xe
o lon trouve :
les oat , 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.
30
3. Premiers programmes 3.1. Tout dans le main()!
les oat 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 enn 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 nul ?
cout << " i es t nul " << endl ;
. . .
i f ( i >2) / / i e s t i l pl us grand que 2?
j =3;
e l s e
j =5; / / Si on e s t i c i , c e s t que i <=2
. . .
/ / Cas pl us c ompl i qu !
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 de 3 ou a l o r s
/ / j vaut 2 e t k e s t d i f f r e n t de 3 ou a l o r s
/ / on n a pas i pl us grand a l a f o i s de j e t de k
cout << "Une premi re i ns t r uc t i on " << endl ;
cout << "Une deuxime i ns t r uc t i on " << endl ;
}
Les variables de type boolen servent mmoriser le rsultat dun test :
bool t = ( ( i ==3)||( j ==4) ) ;
i f ( t )
k=5;
12. Coin des collgiens : 10
38
ou 1e+38 vaut 1 suivi de 38 zros, 10
38
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 dnition 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.
31
3.1. Tout dans le main()! 3. Premiers programmes
Enn, 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. Enn, 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 afcher des commentaires passionnants !
1 # i ncl ude <i ostream>
2 usi ng namespace st d ;
3 # i ncl ude <coni o . h> / / Non s t a nda r d !
4
5 i nt main ( )
6 {
7 bool f i n i =f a l s e ;
8 char c ;
9 do {
10 c=_get ch ( ) ; / / Non s t a nda r d !
11 swi t ch ( c ) {
12 case a :
13 cout << " Vous avez t ap a ! " << endl ;
14 break ;
15 case f :
16 cout << " Vous avez t ap f . Au r evoi r ! " << endl ;
17 f i n i =t r ue ;
18 break ;
19 case e :
20 case i :
21 case o :
22 case u :
23 case y :
24 cout << " Vous avez t ap une aut re voyel l e ! " << endl ;
25 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 stan-
dard 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>.
32
3. Premiers programmes 3.1. Tout dans le main()!
26 def aul t :
27 cout << " Vous avez t ap aut re chose ! " << endl ;
28 break ;
29 }
30 } while ( ! f i n i ) ;
31 ret urn 0;
32 }
Si vous avez tout compris, le switch prcdant ceci est quivalent
17
:
i f ( c== a )
cout << " Vous avez t ap a ! " << endl ;
e l s e i f ( c== f ) {
cout << " Vous avez t ap f . Au r evoi r ! " << endl ;
f i n i =t r ue ;
} e l s e i f ( c== e || c== i || c== o || c== u || c== y )
cout << " Vous avez t ap une aut re voyel l e ! " << endl ;
e l s e
cout << " Vous avez t ap aut re chose ! " << endl ;
Avant tout, rappelons la principale source derreur du switch :
Dans un switch, ne pas oublier les break!
Vous avez pu remarquer cette ligne 2 un peu cryptique. Un namespace est un pr-
xe pour certains objets. Le prxe des objets standard du langage est std. Ainsi cout
et endl ont pour nom complet std :: cout et std :: endl. La ligne 2 permet domettre ce
prxe.
3.1.3 Boucles
Il est difcile 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 utili-
se 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 # i ncl ude <i ostream>
2 usi ng namespace st d ;
3
4 i nt main ( )
5 {
6 i nt i ;
7 do { / / Dbut de l a b o uc l e
8 cout << "Un nombre ent r e 1 et 10 , SVP: " ;
9 ci n >> i ;
10 } while ( i <1 || i >10) ; / / On r e t o ur ne au d but de l a b o uc 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
chiers spars pour tout, etc.
33
3.1. Tout dans le main()! 3. Premiers programmes
11 / / c e t e s t e s t v r a i
12 cout << " Merci ! Vous avez t ap " << i << endl ;
13 ret urn 0;
14 }
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 vrie le test au dbut de la boucle. Le programme sui-
vant afche les entiers de 1 100 :
i nt i =1;
whi le ( i <=100) {
cout << i << endl ;
i =i +1;
}
Enn, 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 n de chaque boucle. Instruction initiale, test et
instruction nale sont spares par un ; , ce qui donne le programme suivant, absolu-
ment quivalent au prcdent :
i nt i ;
f or ( 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 or ( i nt i =1; i <=100; i ++)
cout << i << endl ;
quand on sait que :
On peut dnir la variable dans la premire partie du for () . Attention, cette va-
riable 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 -
nale 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 or ( i nt i =1 , j =100; 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 dnir 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.
34
3. Premiers programmes 3.1. Tout dans le main()!
3.1.4 Rcrations
Nous pouvons dj faire de nombreux programmes. Par exemple, jouer au juste
prix. Le programme choisit le prix, et lutilisateur devine :
1 # i ncl ude <i ostream>
2 # i ncl ude <c s t dl i b >
3 usi ng namespace st d ;
4
5 i nt main ( )
6 {
7 i nt n=rand( ) %100; / / nombre de vi ne r e nt r e 0 e t 99
8 i nt i ;
9 do {
10 cout << " Votre pr i x : " ;
11 ci n >> i ;
12 i f ( i >n)
13 cout << "C es t moins " << endl ;
14 e l s e i f ( i <n)
15 cout << "C es t pl us " << endl ;
16 e l s e
17 cout << " Gagne ! " << endl ;
18 } while ( i ! =n ) ;
19 ret urn 0;
20 }
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, an de trouver au plus vite :
1 #i ncl ude <i ostream>
2 usi ng namespace st d ;
3
4 i nt main ( )
5 {
6 cout << " Choi si ssez un nombre ent r e 1 et 100 " << endl ;
7 cout << " Repondez par +, ou =" << endl ;
8 i nt a=1 , b=100; / / Val e ur s e xt r me s
9 bool t rouve=f a l s e ;
10 do {
11 i nt c =( a+b ) /2; / / On pr o po s e l e mi l i e u
12 cout << " Ser ai t ce " << c << " ? : " ;
13 char r ;
14 do
15 ci n >> r ;
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.
35
3.1. Tout dans le main()! 3. Premiers programmes
16 while ( r ! = = && r ! = + && r ! = ) ;
17 i f ( r== = )
18 t rouve=t r ue ;
19 e l s e i f ( r== )
20 b=c 1; / / C e s t moins , on e s s a i e e nt r e a e t c1
21 e l s e
22 a=c +1; / / C e s t pl us , on e s s a i e e nt r e c +1 e t b
23 } while ( ! t rouve && ( a<=b ) ) ;
24 i f ( t rouve )
25 cout << " Quel boss j e s ui s ! " << endl ;
26 e l s e
27 cout << " Vous avez t r i c h ! " << endl ;
28 ret urn 0;
29 }
On peut aussi complter le programme "supplmentaire" du TP de lannexe A.1. Il
sagissait dune balle rebondissant dans un carr. (Voir lannexe C pour les instructions
graphiques...)
1 # i ncl ude <Imagine/Graphi cs . h>
2 usi ng namespace Imagine ;
3
4 i nt main ( )
5 {
6 i nt w=300 , h=210;
7 openWindow(w, h ) ; / / Fe n t r e gr a phi que
8 i nt i =0 , j =0; / / Po s i t i o n
9 i nt di =2 , dj =3; / / Vi t e s s e
10 while ( t r ue ) {
11 f i l l Re c t ( i , j , 4 , 4 , RED) ; / / De s s i n de l a b a l l e
12 mi l l i Sl ee p ( 1 0 ) ; / / On a t t e nd un peu . . .
13 i f ( i +di >w || i +di <0) {
14 di=di ; / / Rebond h o r i z o n t a l s i on s o r t
15 }
16 i nt ni =i +di ; / / Nouve l l e p o s i t i o n
17 i f ( j +dj >h || j +dj <0) {
18 dj =dj ; / / Rebond v e r t i c a l s i on s o r t
19 }
20 i nt nj =j +dj ;
21 f i l l Re c t ( i , j , 4 , 4 , WHITE) ; / / Ef f a c e me nt
22 i =ni ; / / On change de p o s i t i o n
23 j =nj ;
24 }
25 endGraphics ( ) ;
26 ret urn 0;
27 }
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
permet de lappeler sans utiliser son nom complet Imagine::endGraphics(). Les autres
36
3. Premiers programmes 3.2. Fonctions
FIGURE 3.1 Traits et cercles au hasard...
fonctions appeles dans ce petit programme (openWindow, llRect et milliSleep) sont
aussi fournies par Imagine.
3.2 Fonctions
Lorsquon met tout dans le main() on ralise trs vite que lon fait souvent des
copier/coller de bouts de programmes. Si des lignes de programmes commencent se
ressembler, cest quon est vraisemblablement devant loccasion de faire des fonctions.
On le fait pour des raisons de clart, mais aussi pour faire des conomies de frappe au
clavier !
Il faut regrouper les passages identiques en fonctions :
pour obtenir un programme clair...
et pour moins se fatiguer !
Attention bien comprendre quand faire une fonction et ne pas simple-
ment 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 l conducteur dune bonne program-
mation. 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 ruti-
liser 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 gure 3.1 montre un rsultat :
1 # i ncl ude <Imagine/Graphi cs . h>
2 usi ng namespace Imagine ;
3 # i ncl ude <c s t dl i b >
4 usi ng namespace st d ;
37
3.2. Fonctions 3. Premiers programmes
5
6 i nt main ( )
7 {
8 openWindow( 3 0 0 , 2 0 0 ) ;
9 f or ( i nt i =0; i <150; i ++) {
10 i nt x1=rand( ) %300; / / Poi nt i n i t i a l
11 i nt y1=rand( ) %200;
12 i nt x2=rand( ) %300; / / Poi nt f i n a l
13 i nt y2=rand( ) %200;
14 Color c=Color ( rand( ) %256 , rand( ) %256 , rand ( ) %256) ; / / RVB
15 drawLine ( x1 , y1 , x2 , y2 , c ) ; / / Tr ac de segment
16 i nt xc=rand( ) %300; / / Ce nt r e du c e r c l e
17 i nt yc=rand( ) %200;
18 i nt r c=rand( ) %10; / / Rayon
19 Color cc=Color ( rand( ) %256 , rand( ) %256 , rand ( ) %256) ; / / RVB
20 f i l l Ci r c l e ( xc , yc , rc , cc ) ; / / Ce r c l e
21 }
22 endGraphics ( ) ;
23 ret urn 0;
24 }
La premire chose qui choque
22
, cest lappel rpt rand() et modulo pour tirer
un nombre au hasard. On aura souvent besoin de tirer des nombres au hasard dans
un certain intervalle et il est naturel de le faire avec une fonction. Au passage, nous
corrigeons une deuxime chose qui choque : les entiers 300 et 200 reviennent souvent.
Si nous voulons changer les dimensions de la fentre, il faudra remplacer dans le pro-
gramme tous les 300 et tous les 200. Il vaudrait mieux mettre ces valeurs dans des
variables et faire dpendre le reste du programme de ces variables. Cest un dfaut
constant de tous les dbutants et il faut le corriger tout de suite.
Il faut ds le dbut dun programme reprer les paramtres constants uti-
liss plusieurs reprises et les placer dans des variables dont dpendra le
programme. On gagne alors beaucoup de temps
a
quand on veut les modi-
er par la suite.
a. Encore la rgle du moindre effort... Si on fait trop de copier/coller ou de remplacer avec
lditeur, cest mauvais signe !
Bref, notre programme devient :
/ / Nombre e nt r e 0 e t n1
i nt hasard ( i nt n)
{
ret urn rand()%n ;
}
i nt main ( )
{
const i nt w=300 , h=200;
22. part videmment la syntaxe "objet" des variables de type Color pour lesquelles on se permet un
Color(r,v,b) bien en avance sur ce que nous sommes censs savoir faire...
38
3. Premiers programmes 3.2. Fonctions
openWindow(w, h ) ;
f or ( i nt i =0; i <150; i ++) {
i nt x1=hasard (w) , y1=hasard ( h ) ; / / Poi nt i n i t i a l
i nt x2=hasard (w) , y2=hasard ( h ) ; / / Poi nt f i n a l
Color c=Color ( hasard ( 256) , hasard ( 256) , hasard ( 2 5 6 ) ) ;
drawLine ( x1 , y1 , x2 , y2 , c ) ; / / Tr ac de segment
i nt xc=hasard (w) , yc=hasard ( h ) ; / / Ce nt r e du c e r c l e
i nt r c=hasard (w/20) ; / / Rayon
Color cc=Color ( hasard ( 256) , hasard ( 256) , hasard ( 2 5 6 ) ) ;
f i l l Ci r c l e ( xc , yc , rc , cc ) ; / / Ce r c l e
}
endGraphics ( ) ;
ret urn 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 modier 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 modier la fonction que toutes les
lignes quelle a remplaces !
a. Moindre effort, toujours !
3.2.1 Retour
Nous venons de dnir sans lexpliquer une fonction hasard() qui prend un para-
mtre 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 n. Par exemple :
void di s_bonj our_a_l a_dame ( s t r i ng nom_de_la_dame ) {
cout << " Bonj our , Mme " << nom_de_la_dame << " ! " << endl ;
}
. . .
di s_bonj our_a_l a_dame ( " Germaine " ) ;
di s_bonj our_a_l a_dame ( " Fi t zger al d " ) ;
. . .
23. Pourquoi vouloir le faire ? Dans notre cas parce que la fonction rand() utilise est sufsante 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. Enn, nous avons oubli
dinitialiser le gnrateur alatoire. Si vous le permettez, nous verrons une autre fois ce que cela signie
et comment le faire en modiant juste la fonction hasard().
39
3.2. Fonctions 3. Premiers programmes
2. Une fonction peut comporter plusieurs instructions return
24
. Cela permet de sor-
tir quand on en a envie, ce qui est bien plus clair et plus proche de notre faon de
penser :
i nt si gne_avec_un_seul _ret urn ( double x ) {
i nt s ;
i f ( x==0)
s =0;
e l s e i f ( x<0)
s =1;
e l s e
s =1;
ret urn s ;
}
i nt si gne_pl us_si mpl e ( double x ) {
i f ( x<0)
ret urn 1;
i f ( x>0) / / Not ez l a b s e nc e de e l s e , devenu i n u t i l e !
ret urn 1;
ret urn 0;
}
3. Pour une fonction void, on utilise return sans rien derrire pour un retour en
cours de fonction :
void t el ephoner_avec_un_seul _ret urn ( s t r i ng nom) {
i f ( j _ai _l e_t el ephone ) {
i f (mon telephone_marche ) {
i f ( est _dans_l _annuai re (nom) ) {
i nt numero=numero_telephone (nom) ;
composer ( numero ) ;
i f ( ca_decroche ) {
par l er ( ) ;
r accr ocher ( ) ;
}
}
}
}
}
void t el ephoner_pl us_si mpl e ( s t r i ng nom) {
i f ( ! j _ai _l e_t el ephone )
ret urn ;
i f ( ! mon telephone_marche )
ret urn ;
i f ( ! est _dans_l _annuai re (nom) )
ret urn ;
i nt numero=numero_telephone (nom) ;
composer ( numero ) ;
24. Contrairement certains vieux langages, comme le Pascal
40
3. Premiers programmes 3.2. Fonctions
i f ( ! ca_decroche )
ret urn ;
par l er ( ) ;
r accr ocher ( ) ;
}
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 nt r e a e t b
i nt hasard2 ( i nt a , i nt b)
{
ret urn a+( rand( ) %( ba +1 ) ) ;
}
/ / Nombre e nt r e 0 e t 1
double hasard3 ( )
{
ret urn rand ( ) / double (RAND_MAX) ;
}
. . .
i nt 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 suft 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; et non double x=i/j;, ni mme
double x=double(i/j);
a
a. Cette conversion en double arrive trop tard!
3.2.3 Passage par rfrence
Lorsquune fonction modie la valeur dun de ses paramtres, et si ce paramtre
tait une variable dans la fonction appelante, alors la variable en question nest pas
modie. Plus clairement, le programme suivant choue :
41
3.2. Fonctions 3. Premiers programmes
void t r i pl e ( i nt x ) {
x=x 3;
}
. . .
i nt a =2;
t r i pl e ( a ) ;
cout << a << endl ;
Il afche 2 et non 6. En fait, le paramtre x de la fonction triple vaut bien 2, puis 6.
Mais son passage 6 ne modie 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 modier son paramtre.
On sagit alors dun passage par rfrence (ou par variable). Il suft de rajouter un &
derrire le type du paramtre :
void t r i pl e ( i nt& x ) {
x=x 3;
}
Gnralement, on choisit lexemple suivant pour justier le besoin des rfrences :
void echanger1 ( i nt x , i nt y) {
i nt t =x ;
x=y ;
y=t ;
}
void echanger2 ( i nt& x , i nt& y) {
i nt t =x ;
x=y ;
y=t ;
}
. . .
i nt a=2 , b=3;
echanger1 ( a , b ) ;
cout << a << " " << b << " " ;
echanger2 ( a , b ) ;
cout << a << " " << b << endl ;
. . .
Ce programme afche 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 :
42
3. Premiers programmes 3.2. Fonctions
void un_point ( i nt& x , i nt& y) {
x = . . . ;
y = . . . ;
}
. . .
i nt a , b ;
un_point ( a , b ) ;
. . .
Ainsi, notre programme de dessin alatoire deviendrait :
1 # i ncl ude <Imagine/Graphi cs . h>
2 usi ng namespace Imagine ;
3 # i ncl ude <c s t dl i b >
4 usi ng namespace st d ;
5
6 / / Nombre e nt r e 0 e t n1
7 i nt hasard ( i nt n)
8 {
9 ret urn rand()%n ;
10 }
11
12 Color une_coul eur ( ) {
13 ret urn Color ( hasard ( 256) , hasard ( 256) , hasard ( 2 5 6 ) ) ;
14 }
15
16 void un_point ( i nt w, i nt h , i nt& x , i nt& y ) {
17 x=hasard (w) ;
18 y=hasard ( h ) ;
19 }
20
21 i nt main ( )
22 {
23 const i nt w=300 , h=200;
24 openWindow(w, h ) ;
25 f or ( i nt i =0; i <150; i ++) {
26 i nt x1 , y1 ; / / Poi nt i n i t i a l
27 un_point (w, h , x1 , y1 ) ;
28 i nt x2 , y2 ; / / Poi nt f i n a l
29 un_point (w, h , x2 , y2 ) ;
30 Color c=une_coul eur ( ) ;
31 drawLine ( x1 , y1 , x2 , y2 , c ) ; / / Tr ac de segment
32 i nt xc , yc ; / / Ce nt r e du c e r c l e
33 un_point (w, h , xc , yc ) ;
34 i nt r c=hasard (w/20) ; / / Rayon
35 Color cc=une_coul eur ( ) ;
36 f i l l Ci r c l e ( xc , yc , rc , cc ) ; / / Ce r c l e
37 }
38 endGraphics ( ) ;
39 ret urn 0;
43
3.2. Fonctions 3. Premiers programmes
40 }
Avec le conseil suivant :
penser utiliser directement le rsultat dune fonction et ne pas le mmori-
ser dans une variable lorsque cest inutile.
Il devient mme :
26 i nt x1 , y1 ; / / Poi nt i n i t i a l
27 un_point (w, h , x1 , y1 ) ;
28 i nt x2 , y2 ; / / Poi nt f i n a l
29 un_point (w, h , x2 , y2 ) ;
30 drawLine ( x1 , y1 , x2 , y2 , une_coul eur ( ) ) ; / / Tr ac de segment
31 i nt xc , yc ; / / Ce nt r e du c e r c l e
32 un_point (w, h , xc , yc ) ;
33 i nt r c=hasard (w/20) ; / / Rayon
34 f i l l Ci r c l e ( xc , yc , rc , une_coul eur ( ) ) ; / / Ce r c l e
3.2.4 Porte, Dclaration, Dnition
Depuis le dbut, nous crons des fonctions en les dnissant. 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 i nt main ( )
2 {
3 f ( ) ;
4 ret urn 0 ;
5 }
6 void f ( ) {
7 }
car la ligne 3, f () nest pas connue. Il suft ici de mettre les lignes 6 et 7 avant le main()
pour que le programme compile. Par contre, il est plus difcile de faire compiler :
void f ( )
{
g ( ) ; / / Er r e ur : g ( ) i nconnue
}
void g ( ) {
f ( ) ;
}
puisque les deux fonctions on besoin lune de lautre, et quaucun ordre ne conviendra.
Il faut alors connatre la rgle suivante :
44
3. Premiers programmes 3.2. Fonctions
Remplacer le corps dune fonction par un; sappelle dclarer la fonction.
Dclarer une fonction suft au compilateur, qui peut "patienter"
a
jusqu
sa dnition.
a. En ralit, le compilateur na besoin que de la dclaration. Cest le linker qui devra trou-
ver quelque part la dnition de la fonction, ou plus exactement le rsultat de la compilation
de sa dnition!
Notre programme prcdent peut donc se compiler avec une ligne de plus :
void g ( ) ; / / D c l a r a t i o n de g
void f ( )
{
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 de g
f ( ) ;
}
3.2.5 Variables locales et globales
Nous avons vu section 3.1.1 la porte des variables. La rgle des accolades sap-
plique 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 in-
terdit :
void f ( )
{
i nt x ;
x=3;
}
void g ( ) {
i nt y ;
y=x ; / / Er r e ur : x i nconnu
}
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 i nt z ; / / g l o b a l e
2
3 void f ( )
4 {
5 i nt x ; / / l o c a l e
6 . . .
45
3.2. Fonctions 3. Premiers programmes
7 i f ( x<z )
8 . . .
9 }
10
11 void g ( )
12 {
13 i nt y ; / / l o c a l e
14 . . .
15 z=y ;
16 . . .
17 }
Lutilisation de variables globales est tolre et parfois justie. 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 dbu-
tant. 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 fonc-
tions 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 / / Nombre e nt r e 0 e t n1
2 i nt hasard ( i nt n) {
3 ret urn rand()%n ;
4 }
5 / / Nombre e nt r e a e t b
6 i nt hasard ( i nt a , i nt b) {
7 ret urn a+( rand( ) %( ba +1 ) ) ;
8 }
9 / / Nombre e nt r e 0 e t 1
10 double hasard ( ) {
11 ret urn rand ( ) / double (RAND_MAX) ;
46
3. Premiers programmes 3.3. TP
FIGURE 3.2 Mini tennis...
12 }
13 . . .
14 i nt i =hasard ( 3 ) ; / / e nt r e 0 e t 2
15 i nt j =hasard ( 2 , 4 ) / / e nt r e 2 e t 4
16 double k=hasard ( ) ; / / e nt r e 0 e t 1
17 . . .
3.3 TP
Nous pouvons maintenant aller faire le deuxime TP donn en annexe A.2 an de
mieux comprendre les fonctions et aussi pour obtenir un mini jeu de tennis (gure 3.2).
3.4 Fiche de rfrence
Nous commenons maintenant nous fabriquer une "che 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 che prcdente seront en rouge. La che nale est en
annexe D.
47
3.4. Fiche de rfrence 3. Premiers programmes
Fiche de rfrence (1/2)
Variables
Dnition :
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=254;
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);
int i,j;
double x=double(i)/j;
Tests
Comparaison :
== != < > <= >=
Ngation : !
Combinaisons : && ||
if (i==0)
j=1;
if (i==0)
j=1;
else
j=2;
if (i==0) {
j=1;
k=2;
}
bool t=(i==0);
if (t)
j=1;
switch (i) {
case 1:
...;
...;
break;
case 2:
case 3:
...;
break;
default:
...;
}
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)
...
Fonctions
Dnition :
int plus(int a,int b) {
int c=a+b;
return c;
}
void affiche(int a) {
cout << a << endl;
}
Dclaration :
int plus(int a,int b);
Retour :
int signe(double x) {
if (x<0)
return -1;
if (x>0)
return 1;
return 0;
}
void afficher(int x,
int y) {
if (x<0 || y<0)
return;
if (x>=w || y>=h)
return;
DrawPoint(x,y,RED);
}
Appel :
int f(int a) { ... }
int g() { ... }
...
int i=f(2),j=g();
Rfrences :
void swap(int& a,
int& b){
int tmp=a;
a=b;b=tmp;
}
...
int x=3,y=2;
swap(x,y);
Surcharge :
int hasard(int n);
int hasard(int a,
int b);
double hasard();
Divers
i++;
i--;
i-=2;
j+=3;
j=i%n; // Modulo
#include <cstdlib>
...
i=rand()%n;
x=rand()/
double(RAND_MAX);
Entres/Sorties
#include <iostream>
using namespace std;
...
cout <<"I=" <<i <<endl;
cin >> i >> j;
Erreurs frquentes
Pas de dnition de fonction
dans une fonction!
int q=r=4; // NON!
if (i=2) // NON!
if i==2 // NON!
if (i==2) then // NON!
for (int i=0,i<100,i++)
// NON!
int f() {...}
...
int i=f; // NON!
double x=1/3; // NON!
int i,j;
double x;
x=i/j; // NON!
x=double(i/j); //NON!
48
3. Premiers programmes 3.4. Fiche de rfrence
Fiche de rfrence (2/2)
Imagine++
Voir documentation...
Clavier
Build : F7
Debug : F5
Step over : F10
Step inside : F11
Indent : Ctrl+K,Ctrl+F
Conseils
Travailler en local
Imagine++ Project
Nettoyer en quittant.
Erreurs et warnings : cliquer.
Indenter.
Ne pas laisser de warning.
Utiliser le debuggeur.
Faire des fonctions.
49
4. Les tableaux
Chapitre 4
Les tableaux
Tout en continuant utiliser les fonctions pour les assimiler, nous allons rajouter les ta-
bleaux qui, sinon, nous manqueraient rapidement. Nous nirons pas trop vite et ne verrons
pour linstant que les tableaux une dimension et de taille xe. 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 pro-
gramme suivant :
i nt x1 , y1 , u1 , v1 ; / / Ba l l e 1
i nt x2 , y2 , u2 , v2 ; / / Ba l l e 2
i nt x3 , y3 , u3 , v3 ; / / Ba l l e 3
i nt x4 , y4 , u4 , v4 ; / / Ba l l e 4
. . .
BougeBal l e ( x1 , y1 , u1 , v1 ) ;
BougeBal l e ( x2 , y2 , u2 , v2 ) ;
BougeBal l e ( x3 , y3 , u3 , v3 ) ;
BougeBal l e ( x4 , y4 , u4 , v4 ) ;
. . .
pourra avantageusement tre remplac par :
i nt x [ 4 ] , y [ 4 ] , u[ 4 ] , v [ 4 ] ; / / Ba l l e s
. . .
f or ( i nt i =0; i <4; i ++)
BougeBal l e ( x [ i ] , y[ i ] , u[ i ] , v[ i ] ) ;
. . .
dans lequel int x[4] dnit un tableau de 4 variables de type int : x[0], x[1], x[2] et
x[3]. En pratique, le compilateur rserve quelque part en mmoire de quoi stocker les
4 variables en question et gre de quoi faire en sorte que x[ i ] dsigne la bonne variable.
Un autre exemple pour mieux comprendre, qui additionne des double deux par
deux en mmorisant les rsultats :
4.1. Premiers tableaux 4. Les tableaux
double x [ 100] , y [ 100] , z [ 1 0 0 ] ;
. . .
. . . / / i c i , l e s x [ i ] e t y [ i ] pr e nne nt de s v a l e ur s
. . .
f or ( i nt i =0; i <100; 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.
CEST 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 r-
serve 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 xe 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 double x [ 1 0 ] , y [ 4 ] , z [ 5 ] ; / / OK
2 const i nt n=5;
3 i nt i [ n] , j [ 2n] , k[ n+1] ; / / OK
4 i nt n1 ; / / n1 n a mme pas de va l e ur
5 i nt t 1 [ n1 ] ; / / donc ERREUR
6 i nt n2 ;
7 ci n >> n2 ; / / n2 prend une val e ur , mai s connue
8 / / uni quement l e x c ut i o n
9 i nt t 2 [ n2 ] ; / / donc ERREUR
10 i nt n3 ;
11 n3=5; / / n3 prend une val e ur , connue
12 / / l e x c ut i o n , mai s . . . non c o ns t a nt e
13 i nt t 3 [ n3 ] ; / / donc ERREUR ( SI ! )
Connaissant ces deux points, on peut trs facilement utiliser des tableaux. Attention
toutefois :
52
4. Les tableaux 4.2. Initialisation
ne pas utiliser de tableau quand cest inutile, notamment quand on traduit
une formule mathmatique.
Je mexplique. Si vous devez calculer s =

100
i=1
f(i) pour f donne
1
, par exemple
f(i) = 3i + 4, nallez pas crire, comme on le voit parfois :
1 double f [ 1 0 0 ] ;
2 f or ( i nt i =1; i <=100; i ++)
3 f [ i ]=3 i +4;
4 double s ;
5 f or ( i nt i =1; i <=100; i ++)
6 s=s+f [ i ] ;
ni, mme, ayant corrig vos bugs :
5 double f [ 1 0 0 ] ; / / St o c k e f ( i ) dans f [ i 1]
6 f or ( i nt i =1; i <=100; i ++)
7 f [ i 1]=3 i +4; / / At t e nt i o n aux i n d i c e s !
8 double s =0; / / Ca va mieux comme ca !
9 f or ( i nt i =1; i <=100; i ++)
10 s=s+f [ i 1] ;
mais plutt directement sans tableau :
5 double s =0;
6 f or ( i nt i =1; i <=100; i ++)
7 s=s +(3 i +4) ;
ce qui pargnera, la machine, un tableau (donc de la mmoire et des calculs), et
vous des bugs (donc vos nerfs !).
4.2 Initialisation
Tout comme une variable, un tableau peut tre initialis :
i nt t [ 4 ] ={ 1 , 2 , 3 , 4 } ;
s t r i ng s [ 2] ={ " hip " , " hop" } ;
Attention, la syntaxe utilise pour linitialisation ne marche pas pour une affecta-
tion
2
:
i nt t [ 2 ] ;
t ={ 1 , 2 } ; / / Er r e ur !
4.3 Spcicits des tableaux
Les tableaux sont des variables un peu spciales. Ils ne se comportent pas toujours
comme les autres variables
3
...
1. Coin des collgiens : cest--dire s = f(1) + f(2) + ... + f(100).
2. Nous verrons plus bas que laffectation ne marche mme pas entre deux tableaux ! Tout ceci sar-
rangera avec les objets...
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
53
4.3. Spcicits des tableaux 4. Les tableaux
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 he ( i nt s [ 4 ] ) {
f or ( i nt i =0; i <4; i ++)
cout << s [ i ] << endl ;
}
. . .
i nt t [ 4 ] ={ 1 , 2 , 3 , 4 } ;
a f f i c he ( 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 defcacit, les concepteurs du C++ ont
voulu quun tableau ne soit ni pass par valeur, ni retourn.
donc :
1 / / Rappe l : c e c i ne marche pas
2 void a f f e c t e 1 ( i nt x , i nt val ) {
3 x=val ;
4 }
5 / / Rappe l : c e s t c e c i qui marche !
6 void a f f e c t e 2 ( i nt& x , i nt val ) {
7 x=val ;
8 }
9 / / Une f o n c t i o n qui marche s ans &
10 void rempl i t ( i nt s [ 4 ] , i nt val ) {
11 f or ( i nt i =0; i <4; i ++)
12 s [ i ]= val ;
13 }
14 . . .
15 i nt a =1;
16 a f f e c t e 1 ( a , 0 ) ; / / a ne s e r a pas mi s 0
17 cout << a << endl ; / / v r i f i c a t i o n
18 a f f e c t e 2 ( a , 0 ) ; / / a s e r a b i e n mi s 0
19 cout << a << endl ; / / v r i f i c a t i o n
20 i nt t [ 4 ] ;
21 rempl i t ( t , 0 ) ; / / Met l e s t [ i ] 0
22 a f f i c he ( t ) ; / / V r i f i e que l e s t [ i ] v a l e nt 0
et aussi :
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 compr-
hension de la gestion de la mmoire.
54
4. Les tableaux 4.3. Spcicits des tableaux
1 / / Somme de deux t a b l e a ux qui ne c o mpi l e mme pas
2 / / Pour r e t o ur ne r un t a b l e a u
3 i nt somme1( i nt x [ 4 ] , i nt y [ 4 ] ) [ 4 ] { / / on pe ut i magi ne r me t t r e l e
4 / / [ 4 ] i c i ou a i l l e u r s :
5 / / r i e n n y f a i t !
6 i nt z [ 4 ] ;
7 f or ( i nt i =0; i <4; i ++)
8 z [ i ]=x [ i ]+y[ i ] ;
9 ret urn z ;
10 }
11 / / En pr a t i q ue , on f e r a donc comme a !
12 / / Somme de deux t a b l e a ux qui marche
13 void somme2( i nt x [ 4 ] , i nt y [ 4 ] , i nt z [ 4 ] )
14 f or ( i nt i =0; i <4; i ++)
15 z [ i ]=x [ i ]+y[ i ] ; / / OK: z e s t pa s s par r f r e n c e !
16 }
17
18 i nt a [ 4 ] , b [ 4 ] ;
19 . . . / / r e mp l i s s a g e de a e t b
20 i nt c [ 4 ] ;
21 c=somme1( a , b ) ; / / ERREUR
22 somme2( a , b , c ) ; / / OK
Enn, et cest utilis tout le temps,
Une fonction nest pas tenue de travailler sur une taille de tableau unique...
mais il est impossible de demander un tableau sa taille !
On utilise la syntaxe int t [] dans les paramtres pour un tableau dont on ne prcise
pas la taille. Comme il faut bien parcourir le tableau dans la fonction et quon ne peut
retrouver sa taille, on la passe en paramtre en plus du tableau :
1 / / Une f o n c t i o n qui ne marche pas
2 void af f i c he 1 ( i nt t [ ] ) {
3 f or ( i nt i =0; i <TAILLE( t ) ; i ++) / / TAILLE( t ) n e x i s t e pas ! ????
4 cout << t [ i ] << endl ;
5 }
6 / / Comment on f a i t en p r a t i q ue
7 void af f i c he 2 ( i nt t [ ] , i nt n) {
8 f or ( i nt i =0; i <n ; i ++)
9 cout << t [ i ] << endl ;
10 }
11 . . .
12 i nt t 1 [ 2 ] ={ 1 , 2 } ;
13 i nt t 2 [ 3 ] ={ 3 , 4 , 5 } ;
14 af f i c he 2 ( t1 , 2 ) ; / / OK
15 af f i c he 2 ( t2 , 3 ) ; / / OK
4.3.2 Affectation
Cest simple :
55
4.4. Rcrations 4. Les tableaux
Affecter un tableau ne marche pas ! Il faut traiter les lments du tableau un
par un...
Ainsi, le programme :
i nt s [ 4 ] ={ 1 , 2 , 3 , 4 } , t [ 4 ] ;
t =s ; / / ERREUR de c o mp i l a t i o n
ne marche pas et on est oblig de faire :
i nt s [ 4 ] ={ 1 , 2 , 3 , 4 } , t [ 4 ] ;
f or ( i nt 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 er-
reur de compilation, ni mme un warning. Cest le cas entre deux para-
mtres de fonction. Nous comprendrons plus tard pourquoi et leffet exact
dune telle affectation...
.
1 / / Fo nc t i o n qui ne marche pas
2 / / Mais qui c o mpi l e t r s b i e n !
3 void s et 1 ( i nt s [ 4 ] , i nt t [ 4 ] ) {
4 t =s ; / / Ne f a i t pas c e qu i l f a ut !
5 / / mai s c o mpi l e s ans warni ng !
6 }
7 / / Fo nc t i o n qui marche ( e t qui c o mpi l e ! )
8 void s et 2 ( i nt s [ 4 ] , i nt t [ 4 ] ) {
9 f or ( i nt i =0; i <4; i ++)
10 t [ i ]=s [ i ] ; / / OK
11 }
12 . . .
13 i nt s [ 4 ] ={ 1 , 2 , 3 , 4 } , t [ 4 ] ;
14 s et 1 ( s , t ) ; / / Sans e f f e t
15 s et 2 ( s , t ) ; / / OK
16 . . .
4.4 Rcrations
4.4.1 Multi-balles
Nous pouvons maintenant reprendre le programme de la balle qui rebondit, donn
la section 3.1.4, puis amlior avec des fonctions et de constantes lors du TP de lan-
nexe A.2. Grce aux tableaux, il est facile de faire se dplacer plusieurs balles la fois.
Nous tirons aussi la couleur et la position et la vitesse initiales des balles au hasard.
Plusieurs fonctions devraient vous tre inconnues :
Linitialisation du gnrateur alatoire avec srand((unsigned int)time(0)), qui est
explique dans le TP 3 (annexe A.3)
Les fonctions noRefreshBegin et noRefreshEnd qui servent acclrer lafchage
de toutes les balles (voir documentation de Imagine++ annexe C).
56
4. Les tableaux 4.4. Rcrations
FIGURE 4.1 Des balles qui rebondissent... (momentanment ges ! Allez sur la page
du cours pour un programme anim !)
Voici le listing du programme (exemple dafchage (malheureusement statique !) -
gure 4.1) :
1 # i ncl ude <Imagine/Graphi cs . h>
2 usi ng namespace Imagine ;
3 # i ncl ude <c s t dl i b >
4 # i ncl ude <ctime >
5 usi ng namespace st d ;
6 / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
7 / / Co ns t a nt e s du programme
8 const i nt width=256; / / Lar ge ur de l a f e n e t r e
9 const i nt hei ght =256; / / Hauteur de l a f e n e t r e
10 const i nt ba l l _ s i z e =4; / / Rayon de l a b a l l e
11 const i nt nb_bal l s =30; / / Nombre de b a l l e s
12 / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
13 / / Ge ne r at e ur a l e a t o i r e
14 / / A n a p p e l e r qu une f o i s , avant Random ( )
15 void InitRandom ( )
16 {
17 srand ( ( unsigned i nt ) time ( 0 ) ) ;
18 }
19 / / Ent r e a e t b
20 i nt Random( i nt a , i nt b)
21 {
22 ret urn a+( rand( ) %( ba +1 ) ) ;
23 }
24 / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
25 / / Po s i t i o n e t v i t e s s e a l e a t o i r e
26 void I ni t Ba l l e ( i nt &x , i nt &y , i nt &u, i nt &v , Color &c ) {
27 x=Random( bal l _s i ze , widthba l l _ s i z e ) ;
28 y=Random( bal l _s i ze , hei ght ba l l _ s i z e ) ;
29 u=Random( 0 , 4 ) ;
57
4.4. Rcrations 4. Les tableaux
30 v=Random( 0 , 4 ) ;
31 c=Color ( byte ( Random( 0 , 2 5 5 ) ) ,
32 byte ( Random( 0 , 2 5 5 ) ) ,
33 byte ( Random( 0 , 2 5 5 ) ) ) ;
34 }
35 / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
36 / / Af f i c h a g e d une b a l l e
37 void Dessi neBal l e ( i nt x , i nt y , Color col ) {
38 f i l l Re c t ( xbal l _s i ze , ybal l _s i ze , 2 ba l l _ s i z e +1 , 2 ba l l _ s i z e +1 , col ) ;
39 }
40 / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
41 / / Depl acement d une b a l l e
42 void BougeBal l e ( i nt &x , i nt &y , i nt &u, i nt &v) {
43 / / Rebond sur l e s bo r ds gauche e t d r o i t
44 i f ( x+u>widthba l l _ s i z e || x+u<ba l l _ s i z e )
45 u=u;
46 / / Rebond sur l e s bo r ds haut e t bas e t compt age du s c o r e
47 i f ( y+v<ba l l _ s i z e || y+v>hei ght ba l l _ s i z e )
48 v=v ;
49 / / Mise a j o ur de l a p o s i t i o n
50 x+=u;
51 y+=v ;
52 }
53 / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
54 / / Fo nc t i o n p r i n c i p a l e
55 i nt main ( )
56 {
57 / / Ouvert ure de l a f e n e t r e
58 openWindow( width , hei ght ) ;
59 / / Po s i t i o n e t v i t e s s e de s b a l l e s
60 i nt xb [ nb_bal l s ] , yb[ nb_bal l s ] , ub[ nb_bal l s ] , vb[ nb_bal l s ] ;
61 Color cb [ nb_bal l s ] ; / / Coul e ur s de s b a l l e s
62 InitRandom ( ) ;
63 f or ( i nt i =0; i <nb_bal l s ; i ++) {
64 I ni t Ba l l e ( xb [ i ] , yb[ i ] , ub[ i ] , vb[ i ] , cb [ i ] ) ;
65 Dessi neBal l e ( xb [ i ] , yb[ i ] , cb [ i ] ) ;
66 }
67 / / Bouc l e p r i n c i p a l e
68 while ( t r ue ) {
69 mi l l i Sl ee p ( 2 5 ) ;
70 noRefreshBegi n ( ) ;
71 f or ( i nt i =0; i <nb_bal l s ; i ++) {
72 Dessi neBal l e ( xb [ i ] , yb[ i ] , White ) ;
73 BougeBal l e ( xb [ i ] , yb[ i ] , ub[ i ] , vb[ i ] ) ;
74 Dessi neBal l e ( xb [ i ] , yb[ i ] , cb [ i ] ) ;
75 }
76 noRefreshEnd ( ) ;
77 }
78 endGraphics ( ) ;
58
4. Les tableaux 4.4. Rcrations
79 ret urn 0;
80 }
4.4.2 Avec des chocs !
Il nest ensuite pas trs compliqu de modier 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 modie
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 vrie 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 modie 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. (No-
tez linclusion du chier <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 lafchage (ligne 37).
Le tout donne un programme bien plus anim. On ne peut videmment constater la
diffrence sur une gure dans un livre. Tlchargez donc le programme sur la page du
cours !
23 / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
24 / / Po 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 ni t Ba l l e ( double &x , double &y , double &u, double &v , Color &c ) {
26 x=Random( bal l _s i ze , widthba l l _ s i z e ) ;
27 y=Random( bal l _s i ze , hei ght ba l l _ s i z e ) ;
28 u=Random( 0 , 4 ) ;
29 v=Random( 0 , 4 ) ;
30 c=Color ( byte ( Random( 0 , 2 5 5 ) ) ,
31 byte ( Random( 0 , 2 5 5 ) ) ,
32 byte ( Random( 0 , 2 5 5 ) ) ) ;
33 }
34 / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
35 / / Af f i c h a g e d une b a l l e
36 void Dessi neBal l e ( double x , double y , Color col ) {
37 f i l l Re c t ( i nt ( x)bal l _s i ze , i nt ( y)bal l _s i ze ,
38 2 ba l l _ s i z e +1 , 2 ba l l _ s i z e +1 , col ) ;
39 }
40 / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
41 / / Choc e l a s t i q u e de deux b a l l e s s p h e r i q ue s
42 / / c f l a b o . nt i c . or g
43 # i ncl ude <cmath>
59
4.4. Rcrations 4. Les tableaux
44 void ChocBal l es ( double&x1 , double&y1 , double&u1 , double&v1 ,
45 double&x2 , double&y2 , double&u2 , double&v2 )
46 {
47 / / Di s t a nc e
48 double o2o1x=x1x2 , o2o1y=y1y2 ;
49 double d=s qr t ( o2o1xo2o1x+o2o1yo2o1y ) ;
50 i f ( d==0) ret urn ; / / Mme c e nt r e ?
51 / / Re p r e ( o2 , x , y )
52 double Vx=u1u2 , Vy=v1v2 ;
53 double V=s qr t ( VxVx+VyVy ) ;
54 i f (V==0) ret urn ; / / Mme v i t e s s e
55 / / Re p r e s ui va nt V ( o2 , i , j )
56 double i x=Vx/V, i y=Vy/V, j x=i y , j y=i x ;
57 / / Hauteur d a t t a q ue
58 double H=o2o1x j x+o2o1y j y ;
59 / / Angl e
60 double th=acos (H/d) , c=cos ( th ) , s=si n ( th ) ;
61 / / Vi t e s s e a pr s c hoc dans ( o2 , i , j )
62 double v1i =Vcc , v1j =Vcs , v2i =Vs s , v2j =v1j ;
63 / / Dans r e p r e d o r i g i n e (O, x , y )
64 u1=v1i i x+v1j j x+u2 ;
65 v1=v1i i y+v1j j y+v2 ;
66 u2+=v2i i x+v2j j x ;
67 v2+=v2i i y+v2j j y ;
68 }
69 / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
70 / / Depl acement d une b a l l e
71 void BougeBal l e ( double x [ ] , double y [ ] , double u[ ] , double v [ ] , i nt i ) {
72 / / Rebond sur l e s bo r ds gauche e t d r o i t
73 i f ( x [ i ]+u[ i ] >widthba l l _ s i z e || x [ i ]+u[ i ] < ba l l _ s i z e )
74 u[ i ]=u[ i ] ;
75 / / Rebond sur l e s bo r ds haut e t bas e t compt age du s c o r e
76 i f ( y[ i ]+v[ i ] < ba l l _ s i z e || y[ i ]+v[ i ] >hei ght ba l l _ s i z e )
77 v[ i ]=v[ i ] ;
78 f or ( i nt j =0; j <nb_bal l s ; j ++) {
79 i f ( j ==i )
80 cont i nue ;
81 i f ( abs ( x [ i ]+u[ i ]x [ j ] ) <2 ba l l _ s i z e
82 && abs ( y[ i ]+v[ i ]y[ j ] ) <2 ba l l _ s i z e ) {
83 ChocBal l es ( x [ i ] , y[ i ] , u[ i ] , v[ i ] , x [ j ] , y[ j ] , u[ j ] , v[ j ] ) ;
84 }
85 }
86 / / Mise a j o ur de l a p o s i t i o n
87 x [ i ]+=u[ i ] ;
88 y[ i ]+=v[ i ] ;
89 }
90 / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
91 / / Fo nc t i o n p r i n c i p a l e
92 i nt main ( )
60
4. Les tableaux 4.4. Rcrations
93 {
94 / / Ouvert ure de l a f e n e t r e
95 openWindow( width , hei ght ) ;
96 / / Po s i t i o n e t v i t e s s e de s b a l l e s
97 double xb [ nb_bal l s ] , yb[ nb_bal l s ] , ub[ nb_bal l s ] , vb[ nb_bal l s ] ;
98 Color cb [ nb_bal l s ] ; / / Coul e ur s de s b a l l e s
99 InitRandom ( ) ;
100 f or ( i nt i =0; i <nb_bal l s ; i ++) {
101 I ni t Ba l l e ( xb [ i ] , yb[ i ] , ub[ i ] , vb[ i ] , cb [ i ] ) ;
102 Dessi neBal l e ( xb [ i ] , yb[ i ] , cb [ i ] ) ;
103 }
104 / / Bouc l e p r i n c i p a l e
105 while ( t r ue ) {
106 mi l l i Sl e ep ( 2 5 ) ;
107 noRefreshBegi n ( ) ;
108 f or ( i nt i =0; i <nb_bal l s ; i ++) {
109 Dessi neBal l e ( xb [ i ] , yb[ i ] , White ) ;
110 BougeBal l e ( xb , yb , ub , vb , i ) ;
111 Dessi neBal l e ( xb [ i ] , yb[ i ] , cb [ i ] ) ;
112 }
113 noRefreshEnd ( ) ;
114 }
115 endGraphics ( ) ;
116 ret urn 0 ;
117 }
4.4.3 Mlanger les lettres
Le programme suivant considre une phrase et permute alatoirement les lettres
intrieures de chaque mot (cest--dire sans toucher aux extrmits des mots). Il utilise
pour cela le type string, chane de caractre, pour lequel s[ i ] renvoie le i-me caractre
de la chane s, et s. size () le nombre de caractres de s (nous expliquerons plus tard la
notation "objet" de cette fonction). La phrase considre ici devient par exemple :
Ctete pteite psahre dreviat erte ecorne libslie puor vorte parvue ceeravu
Lavez vous comprise ? Peu importe ! Cest le listing que vous devez comprendre :
1 # i ncl ude <i ostream>
2 # i ncl ude <s t r i ng >
3 # i ncl ude <c s t dl i b >
4 # i ncl ude <ctime >
5 usi ng namespace st d ;
6
7 / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
8 / / Ge ne r at e ur a l e a t o i r e
9 / / A n a p p e l e r qu une f o i s , avant Random ( )
10 void InitRandom ( )
11 {
12 srand ( ( unsigned i nt ) time ( 0 ) ) ;
61
4.5. TP 4. Les tableaux
13 }
14 / / Ent r e a e t b
15 i nt Random( i nt a , i nt b)
16 {
17 ret urn a+( rand( ) %( ba +1 ) ) ;
18 }
19
20 / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
21 / / Permut er 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
22 s t r i ng Melanger ( s t r i ng s , i nt n)
23 {
24 i nt l =i nt ( s . s i ze ( ) ) ;
25 i f ( l <=3)
26 ret urn s ;
27 s t r i ng t =s ;
28 f or ( i nt i =0; i <n ; i ++) {
29 i nt a=Random( 1 , l 2) ;
30 i nt b ;
31 do
32 b=Random( 1 , l 2) ;
33 while ( a==b ) ;
34 char c=t [ a ] ;
35 t [ a]= t [ b ] ; t [ b]=c ;
36 }
37 ret urn t ;
38 }
39
40 i nt main ( )
41 {
42 const i nt n=11;
43 s t r i ng phrase [ n] ={ " Cet t e " , " pe t i t e " , " phrase " , " devr ai t " , " e t r e " ,
44 " encore " , " l i s i b l e " , " pour " , " vot re " , " pauvre " ,
45 " cerveau " } ;
46
47 InitRandom ( ) ;
48 f or ( i nt i =0; i <n ; i ++)
49 cout << Melanger ( phrase [ i ] , 3 ) << " " ;
50 cout << endl ;
51
52 ret urn 0;
53 }
4.5 TP
Nous pouvons maintenant aller faire le troisime TP donn en annexe A.3 an de
mieux comprendre les tableaux et aussi pour obtenir un master mind (voir gure 4.2
le rsultat dune partie intressante !).
62
4. Les tableaux 4.6. Fiche de rfrence
FIGURE 4.2 Master mind...
4.6 Fiche de rfrence
Comme promis, nous compltons, en rouge, la "che de rfrence" avec ce qui a t
vu pendant ce chapitre et son TP.
Fiche de rfrence (1/3)
Variables
Dnition :
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=254;
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);
int i,j;
double x=double(i)/j;
63
4.6. Fiche de rfrence 4. Les tableaux
Fiche de rfrence (2/3)
Tests
Comparaison :
== != < > <= >=
Ngation : !
Combinaisons : && ||
if (i==0)
j=1;
if (i==0)
j=1;
else
j=2;
if (i==0) {
j=1;
k=2;
}
bool t=(i==0);
if (t)
j=1;
switch (i) {
case 1:
...;
...;
break;
case 2:
case 3:
...;
break;
default:
...;
}
Tableaux
Dnition :
double x[10],y[10];
for(int i=0;i<10;i++)
y[i]=2
*
x[i];
const int n=5;
int i[n],j[2
*
n]; //OK
Initialisation :
int t[4]={1,2,3,4};
string s[2]={"ab","c"};
Affectation :
int s[3]={1,2,3},t[3];
for (int i=0;i<3;i++)
t[i]=s[i];
En paramtre :
void init(int t[4]) {
for(int i=0;i<4;i++)
t[i]=0;
}
void init(int t[],
int n) {
for(int i=0;i<n;i++)
t[i]=0;
}
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)
...
Fonctions
Dnition :
int plus(int a,int b) {
int c=a+b;
return c;
}
void affiche(int a) {
cout << a << endl;
}
Dclaration :
int plus(int a,int b);
Retour :
int signe(double x) {
if (x<0)
return -1;
if (x>0)
return 1;
return 0;
}
void afficher(int x,
int y) {
if (x<0 || y<0)
return;
if (x>=w || y>=h)
return;
DrawPoint(x,y,RED);
}
Appel :
int f(int a) { ... }
int g() { ... }
...
int i=f(2),j=g();
Rfrences :
void swap(int& a,
int& b){
int tmp=a;
a=b;b=tmp;
}
...
int x=3,y=2;
swap(x,y);
Surcharge :
int hasard(int n);
int hasard(int a,
int b);
double hasard();
Divers
i++;
i--;
i-=2;
j+=3;
j=i%n; // Modulo
#include <cstdlib>
...
i=rand()%n;
x=rand()/
double(RAND_MAX);
#include <ctime>
...
srand((unsigned int)
time(0));
#include <cmath>
double sqrt(double x);
double cos(double x);
double sin(double x);
double acos(double x);
#include <string>
using namespace std;
string s="hop";
char c=s[0];
int l=s.size();
Entres/Sorties
#include <iostream>
using namespace std;
...
cout <<"I=" <<i <<endl;
cin >> i >> j;
64
4. Les tableaux 4.6. Fiche de rfrence
Fiche de rfrence (3/3)
Erreurs frquentes
Pas de dnition de fonction
dans une fonction!
int q=r=4; // NON!
if (i=2) // NON!
if i==2 // NON!
if (i==2) then // NON!
for (int i=0,i<100,i++)
// NON!
int f() {...}
...
int i=f; // NON!
double x=1/3; // NON!
int i,j;
double x;
x=i/j; // NON!
x=double(i/j); //NON!
double x[10],y[10];
for (int i=1;i<=10;i++)
y[i]=2
*
x[i]; //NON
int n=5;
int t[n]; // NON
int f()[4] { // NON!
int t[4];
...
return t; // NON!
}
int t[4]; t=f();
int s[3]={1,2,3},t[3];
t=s; // NON!
int t[2];
t={1,2}; // NON!
Imagine++
Voir documentation...
Clavier
Build : F7
Debug : F5
Step over : F10
Step inside : F11
Indent : Ctrl+K,Ctrl+F
Conseils
Travailler en local
Imagine++ Project
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 !
65
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 va-
riables 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 ! Enn, 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 :
i nt f ( ) { . . . }
. . .
i nt i =f ;
Vouloir affecter un tableau un autre :
i nt s [ 4 ] ={ 1 , 2 , 3 , 4 } , t [ 4 ] ;
t =s ;
5.1.2 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 :
5.1. Rvisions 5. Les structures
Mlanger la syntaxe (si peu!) :
void s et ( i nt t [ 5 ] ) {
. . .
}
. . .
i nt s [ 5 ] ; / / J us que l , t o ut va b i e n !
s et ( i nt s [ 5 ] ) ; / / L , c e s t quand mme n i mpo r t e quoi !
alors quil suft dun :
s et ( 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 for-
mule
1
. Par exemple, croire que le for est un symbole mathmatique comme

n
1
ou

n
1
. Ainsi, pour excuter une instruction quand tous les ok(i ) sont vrais, on a
dj vu tenter un :
i f ( f or ( i nt i =0; i <n ; i ++) ok ( i ) ) / / Du grand a r t . . .
. . .
alors quil faut faire :
bool al l ok=t r ue ;
f or ( i nt i =0; i <n ; i ++)
al l ok =( al l ok && ok ( i ) ) ;
i f ( al l ok )
. . .
ou mme mieux (voyez-vous la diffrence ?) :
bool al l ok=t r ue ;
f or ( i nt i =0; i <n && al l ok ; i ++)
al l ok =( al l ok && ok ( i ) ) ;
i f ( al l ok )
. . .
quon peut nalement simplier en :
bool al l ok=t r ue ;
f or ( i nt i =0; i <n && al l ok ; i ++)
al l ok=ok ( i ) ;
i f ( al l ok )
. . .
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 nalement 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 !
1. Ne me fates pas dire ce que je nai pas dit ! Les informaticiens thoriques considrent parfois les
programmes comme des formules, mais a na rien voir !
68
5. Les structures 5.2. Les structures
5.1.3 Conseils
Indenter. Indenter. Indenter !
Cliquer sur les messages derreurs et de warnings pour aller directement la
bonne ligne !
Ne pas laisser de warning.
Utiliser le debuggeur.
5.2 Les structures
5.2.1 Dnition
Si les tableaux permettent de manipuler plusieurs variables dun mme type, les
structures sont utilises pour regrouper plusieurs variables an 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 pos-
sdant deux champs de type double nomms x et y :
s t r uc t Poi nt {
double x , y ;
} ;
Les champs se dnissent avec la syntaxe des variables locales dune fonction. Atten-
tion par contre
Ne pas oublier le point virgule aprs laccolade qui ferme la dnition de la
structure !
Lutilisation est alors simple. La structure est un nouveau type qui se manipule exacte-
ment comme les autres, avec la particularit supplmentaire quon accde aux champs
avec un point :
Poi nt a ;
a . x =2. 3;
a . y =3. 4;
On peut videmment dnir des champs de diffrents types, et mme des structures
dans des structures :
s t r uc t Cercl e {
Poi nt cent r e ;
double rayon ;
Color coul eur ;
} ;
Cercl e C;
C. cent r e . x =12. ;
C. cent r e . y =13. ;
C. rayon =10. 4;
C. coul eur=Red ;
Lintrt des structures est vident et il faut
69
5.2. Les structures 5. Les structures
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 fonc-
tions, alors il est vraisemblable que les paramtres en question puissent
tre avantageusement regroups en une structure. Ce sera plus simple et
plus clair.
5.2.2 Utilisation
Les structures se manipulent comme les autres types
2
. La dnition, laffectation,
linitialisation, le passage en paramtre, le retour dune fonction : tout est semblable
au comportement des types de base. Seule nouveaut : on utilise des accolades pour
prciser les valeurs des champs en cas dinitialisation
3
. On peut videmment faire
des tableaux de structures... et mme dnir un champ de type tableau! Ainsi, les
lignes suivantes se comprennent facilement :
Poi nt a ={ 2 . 3 , 3 . 4 } , b=a , c ; / / I n i t i a l i s a t i o n s
c=a ; / / Af f e c t a t i o n s
Cercl e C={ { 1 2 , 1 3 } , 1 0 . 4 , Red } ; / / I n i t i a l i s a t i o n
. . .
double di st ance ( Poi nt a , Poi nt b) { / / Pas s age par va l e ur
ret urn s qr t ( ( a . xb . x ) ( a . xb . x ) +( a . yb . y ) ( a . yb . y ) ) ;
}
void agrandi r ( Cercl e& C, double e c he l l e ) { / / Par r f r e n c e
C. rayon=C. rayon e c he l l e ; / / Mo di f i e l e rayon
}
Poi nt mi l i eu ( Poi nt a , Poi nt b) { / / r e t o ur
Poi nt M;
M. x=( a . x+b . x ) /2;
M. y=( a . y+b . y) /2;
ret urn M;
}
. . .
Poi nt P[ 1 0 ] ; / / Tabl e au de s t r u c t u r e s
f or ( i nt i =0; i <10; i ++) {
P[ i ] . x=i ;
P[ i ] . y=f ( i ) ;
}
. . .
/ / Un d but de j e u de Yam s
s t r uc t Ti rage { / /
i nt de [ 5 ] ; / / champ de t ype t a b l e a u
} ;
Ti rage l ancer ( ) {
Ti rage t ;
f or ( i nt i =0; i <5; i ++)
2. Dailleurs, nous avions bien promis que seuls les tableaux avaient des particularits (passage par
rfrence, pas de retour possible et pas daffectation.
3. Comme pour un tableau!
70
5. Les structures 5.3. Rcration : TP
FIGURE 5.1 Corps clestes et duel...
t . de [ i ]=1+rand ( ) %6; / / Un d de 1 6
ret urn t ;
}
. . .
Ti rage t ;
t =l ancer ( ) ;
. . .
Attention, tout comme pour les tableaux, la syntaxe utilise pour linitialisation ne
marche pas pour une affectation
4
:
Poi nt P;
P={ 1 , 2 } ; / / Er r e ur !
Dailleurs, rptons-le :
Tout ce qui na pas t annonc comme possible est impossible !
5.3 Rcration : TP
Nous pouvons maintenant aller faire le TP donn en annexe A.4 an de mieux com-
prendre les structures. Nous ferons mme des tableaux de structures
5
! Nous obtien-
drons un projectile naviguant au milieu des toiles puis un duel dans lespace (gure
5.1) !
4. 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 !
71
5.4. Fiche de rfrence 5. Les structures
5.4 Fiche de rfrence
Encore une fois, nous compltons, en rouge, la "che de rfrence" avec ce qui a t
vu pendant ce chapitre et son TP.
Fiche de rfrence (1/2)
Variables
Dnition :
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=254;
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);
int i,j;
double x=double(i)/j;
Tests
Comparaison :
== != < > <= >=
Ngation : !
Combinaisons : && ||
if (i==0)
j=1;
if (i==0)
j=1;
else
j=2;
if (i==0) {
j=1;
k=2;
}
bool t=(i==0);
if (t)
j=1;
switch (i) {
case 1:
...;
...;
break;
case 2:
case 3:
...;
break;
default:
...;
}
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)
...
Fonctions
Dnition :
int plus(int a,int b) {
int c=a+b;
return c;
}
void affiche(int a) {
cout << a << endl;
}
Dclaration :
int plus(int a,int b);
Retour :
int signe(double x) {
if (x<0)
return -1;
if (x>0)
return 1;
return 0;
}
void afficher(int x,
int y) {
if (x<0 || y<0)
return;
if (x>=w || y>=h)
return;
DrawPoint(x,y,RED);
}
Appel :
int f(int a) { ... }
int g() { ... }
...
int i=f(2),j=g();
Rfrences :
void swap(int& a,
int& b){
int tmp=a;
a=b;b=tmp;
}
...
int x=3,y=2;
swap(x,y);
Surcharge :
int hasard(int n);
int hasard(int a,
int b);
double hasard();
72
5. Les structures 5.4. Fiche de rfrence
Fiche de rfrence (2/2)
Tableaux
Dnition :
double x[10],y[10];
for(int i=0;i<10;i++)
y[i]=2
*
x[i];
const int n=5;
int i[n],j[2
*
n]; //OK
Initialisation :
int t[4]={1,2,3,4};
string s[2]={"ab","c"};
Affectation :
int s[3]={1,2,3},t[3];
for (int i=0;i<3;i++)
t[i]=s[i];
En paramtre :
void init(int t[4]) {
for(int i=0;i<4;i++)
t[i]=0;
}
void init(int t[],
int n) {
for(int i=0;i<n;i++)
t[i]=0;
}
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};
Divers
i++;
i--;
i-=2;
j+=3;
j=i%n; // Modulo
#include <cstdlib>
...
i=rand()%n;
x=rand()/
double(RAND_MAX);
#include <ctime>
...
srand((unsigned int)
time(0));
#include <cmath>
double sqrt(double x);
double cos(double x);
double sin(double x);
double acos(double x);
#include <string>
using namespace std;
string s="hop";
char c=s[0];
int l=s.size();
Entres/Sorties
#include <iostream>
using namespace std;
...
cout <<"I=" <<i <<endl;
cin >> i >> j;
Erreurs frquentes
Pas de dnition de fonction
dans une fonction!
int q=r=4; // NON!
if (i=2) // NON!
if i==2 // NON!
if (i==2) then // NON!
for (int i=0,i<100,i++)
// NON!
int f() {...}
...
int i=f; // NON!
double x=1/3; // NON!
int i,j;
double x;
x=i/j; // NON!
x=double(i/j); //NON!
double x[10],y[10];
for (int i=1;i<=10;i++)
y[i]=2
*
x[i]; //NON
int n=5;
int t[n]; // NON
int f()[4] { // NON!
int t[4];
...
return t; // NON!
}
int t[4]; t=f();
int s[3]={1,2,3},t[3];
t=s; // NON!
int t[2];
t={1,2}; // NON!
struct Point {
double x,y;
} // NON!
Point a;
a={1,2}; // NON!
Imagine++
Voir documentation...
Clavier
Build : F7
Debug : F5
Step over : F10
Step inside : F11
Indent : Ctrl+K,Ctrl+F
Conseils
Travailler en local
Imagine++ Project
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.
73
6. Plusieurs chiers !
Chapitre 6
Plusieurs chiers !
Lors du dernier TP, nous avons ralis deux projets quasiment similaires dont seuls les
main() taient diffrents. Modier aprs coup une des fonctions de la partie commune aux
deux projets ncessiterait daller la modier dans les deux projets. Nous allons voir maintenant
comment factoriser cette partie commune dans un seul chier, de faon en simplier les ven-
tuelles futures modications. Au passage
1
nous verrons comment dnir un oprateur sur de
nouveaux types.

Rsumons notre progression dans le savoir-faire du programmeur :


1. Tout programmer dans le main() : cest un dbut et cest dj bien!
2. Faire des fonctions : pour tre plus lisible et ne pas se rpter ! (Axe des instruc-
tions)
3. Faire des tableaux et des structures : pour manipuler plusieurs variables la fois.
(Axe des donnes)
Nous rajoutons maintenant :
4. Faire plusieurs chiers : pour utiliser des parties communes dans diffrents pro-
jets ou solutions. (A nouveau, axe des instructions)
FIGURE 6.1 Plusieurs chiers sources...
1. Toujours cette ide que nous explorons les diffrentes composantes du langages quand le besoin
sen fait sentir.
6.1. Fichiers spars 6. Plusieurs chiers !
6.1 Fichiers spars
Nous allons rpartir notre code source dans plusieurs chiers. Mais avant toute
chose :
Pour un maximum de portabilit du code, choisir des noms de chiers 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 chier source contenait notre programme C++. Ce chier
source tait transform en chier objet par le compilateur puis le linker compltait le
chier objet avec les bibliothques du C++ pour en faire un chier excutable. En fait,
un projet peut contenir plusieurs chiers sources. Il suft pour cela de rajouter un
chier .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 chier Visual C++/Code/C++ le et en prciser le nom
(sans quil soit besoin de prciser .cpp dans le nom)
Ainsi, en rajoutant un chier C++ hop un projet contenant dj main.cpp, on se
retrouve avec une structure de projet identique celle de la gure 6.1.
Aprs cela, chaque gnration du projet consistera en :
1. Compilation : chaque chier source est transform en un chier objet (de mme
nom mais de sufxe .obj). Les chiers sources sont donc compils indpendam-
ment les uns des autres.
2. Link : les diffrents chiers objets sont runis (et complts avec les bibliothques
du C++) en un seul chier excutable (de mme nom que le projet).
Une partie des instructions du chier principal (celui qui contient main()) peut donc
tre dporte dans un autre chier. Cette partie sera compile sparment et rintgre
pendant ldition des liens. Se pose alors le problme suivant : comment utiliser dans
le chier principal ce qui ce trouve dans les autres chiers ? En effet, nous savions
(cf section 3.2.4) quune fonction ntait "connue" que dans les lignes qui suivaient
sa dnition 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 chier. Pour lutiliser dans
un autre chier, il faut donc ly dclarer !
En clair, nous allons devoir procder ainsi :
Fichier hop.cpp :
2. Heureusement, en fait, car lorsque lon runit des chiers de provenances multiples, il est prf-
rable que ce qui se trouve dans les diffrents chiers ne se mlange pas de faon anarchique...
76
6. Plusieurs chiers ! 6.1. Fichiers spars
/ / D f i n i t i o n s
void f ( i nt x ) {
. . .
}
i nt g ( ) {
. . .
}
/ / Aut r e s f o n c t i o n s
. . .
Fichier main.cpp :
/ / D c l a r a t i o ns
void f ( i nt x ) ;
i nt g ( ) ;
. . .
i nt main ( ) {
. . .
/ / Ut i l i s a t i o n
i nt 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 chiers sutilisent de faon croise,
cest peut-tre que nous sommes en train de ne pas dcouper les sources convenable-
ment.
6.1.2 Avantages
Notre motivation initiale tait de mettre une partie du code dans un chier spar
pour lutiliser dans un autre projet. En fait, dcouper son code en plusieurs chiers a
dautres intrts :
Rendre le code plus lisible et vitant les chiers 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 chiers sources qui ont
t modis depuis la gnration prcdente. Il serait en effet inutile de recompi-
ler un chier source non modi pour ainsi obtenir le mme chier objet
3
! Donc
changer quelques lignes dans un chier nentranera pas la compilation de tout
le programme mais seulement du chier concern
4
.
Attention toutefois ne pas sparer en de trop nombreux chiers ! Il devient alors plus
compliqu de sy retrouver et de naviguer parmi ces chiers.
3. Cest en ralit un peu plus compliqu : un source peu dpendre, via des inclusions (cf section
6.1.4), dautres chiers, qui, eux, peuvent avoir t modis ! Il faut alors recompiler un chier dont une
dpendance a t modie. 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 chier quand les modi-
cations apportes sont simples...
77
6.1. Fichiers spars 6. Plusieurs chiers !
FIGURE 6.2 Mme source dans deux projets
6.1.3 Utilisation dans un autre projet
Pour utiliser dans un projet 2 un chier source dun projet 1, il suft de rajouter le
source en question dans la liste des sources du projet 2 ! Pour cela, aprs avoir slec-
tionn le projet 2 :
Choisir le menu Project/Add Existing Item (ou Alt+Maj+A ou )
Slectionner le chier source.
ou plus simplement :
Avec la souris, glisser/dplacer le chier entre les deux projets
5
en maintenant
la touche Ctrl enfonce (un + apparat).
On obtient le rsultat gure 6.2.
Attention : il faut bien comprendre que
le chier ainsi partag reste dans le rpertoire du premier projet
comme le conrme lafchage de ses proprits ( droite gure 6.2). Dans notre exemple,
chaque projet a son propre chier principal, main.cpp ou main2.cpp, mais il y a un
seul hop.cpp. Modier le contenu de ce chier aura des consquences sur les deux
projets la fois. Cest ce que nous voulions !
6.1.4 Fichiers den-ttes
Le chier spar nous permet de factoriser une partie du source. Toutefois, il faut
taper les dclarations de toutes les fonctions utilises dans chaque chier 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 chier nom avant de procder la compilation.
Il sagit bien de remplacer par le texte complet du chier 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 chier depuis lexplorateur Windows
6. Toujours le moindre effort...
78
6. Plusieurs chiers ! 6.1. Fichiers spars
forme lgrement diffrente : #include <nom>, qui va chercher le chier nom dans les
rpertoires des bibliothques C++
7
.
Grce cette possibilit du pr-processeur, il nous suft de mettre les dclarations
se rapportant au chier spar dans un troisime chier et de linclure dans les -
chiers principaux. Il est dusage de prendre pour ce chier supplmentaire le mme
nom que le chier spar, mais avec lextension .h : on appelle ce chier un chier
den-tte
8
. Pour crer ce chier, faire comme pour le source, mais en choisissant "Vi-
sual C++/Code/Header le" au lieu de "Visual C++/Code/C++ le". Voila ce que cela
donne :
Fichier hop.cpp :
/ / D f i n i t i o n s
void f ( i nt x ) {
. . .
}
i nt g ( ) {
. . .
}
/ / Aut r e s f o n c t i o n s
. . .
Fichier hop.h :
/ / D c l a r a t i o ns
void f ( i nt x ) ;
i nt g ( ) ;
Fichier main.cpp du premier projet :
# i ncl ude " hop . h"
. . .
i nt main ( ) {
. . .
/ / Ut i l i s a t i o n
i nt 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 ncl ude " . . / Pr oj ec t 1/hop . h"
. . .
i nt main ( ) {
. . .
/ / Ut i l i s a t i o n
f ( 1 2 ) ;
7. Les chiers 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 chiers
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" suft.
79
6.1. Fichiers spars 6. Plusieurs chiers !
i nt b=g ( ) ;
. . .
En fait, pour tre sur que les fonctions dnies dans hop.cpp sont cohrentes avec
leur dclaration dans hop.h, et bien que a soit pas obligatoire, on inclut aussi len-
tte dans le source, ce qui donne :
Fichier hop.cpp :
# i ncl ude " hop . h"
. . .
/ / D f i n i t i o n s
void f ( i nt x ) {
. . .
}
i nt g ( ) {
. . .
}
/ / Aut r e s f o n c t i o n s
. . .
En pratique, le chier den-tte ne contient pas seulement les dclarations des fonc-
tions mais aussi les dnitions des nouveaux types (comme les structures) utiliss par
le chier spar. En effet, ces nouveaux types doivent tre connus du chier spar,
mais aussi du chier principal. Il faut donc vraiment :
1. Mettre dans len-tte les dclarations des fonctions et les dnitions
des nouveaux types.
2. Inclure len-tte dans le chier principal mais aussi dans le chier s-
par.
Cela donne par exemple :
Fichier vect.h :
/ / Types
s t r uc t Vecteur {
double x , y ;
} ;
/ / D c l a r a t i o ns
double norme ( Vecteur V) ;
Vecteur pl us ( Vecteur A, Vecteur B ) ;
Fichier vect.cpp :
# i ncl ude " vect . h" / / Fo nc t i o ns e t t ype s
/ / D f i n i t i o n s
double norme ( Vecteur V) {
. . .
}
Vecteur pl us ( Vecteur A, Vecteur B) {
. . .
}
/ / Aut r e s f o n c t i o n s
. . .
80
6. Plusieurs chiers ! 6.1. Fichiers spars
Fichier main.cpp du premier :
# i ncl ude " vect . h"
. . .
i nt main ( ) {
. . .
/ / Ut i l i s a t i o n
Vecteur C=pl us (A, B ) ;
double n=norme (C) ;
. . .
6.1.5 A ne pas faire...
Il est "fortement" conseill de :
1. ne pas dclarer dans len-tte toutes les fonctions du chier spar mais seule-
ment celles qui seront utilises par le chier principal. Les fonctions secondaires
nont pas apparatre
10
.
2. ne jamais inclure un chier spar lui-mme ! Cest gnralement une "grosse
btise"
11
. Donc
pas de #include "vect.cpp"!
6.1.6 Implmentation
Finalement, la philosophie de ce systme est que
Le chier spar et son en-tte forment un tout cohrent, implmentant
un certain nombre de fonctionnalits.
Celui qui les utilise, qui nest pas ncessairement celui qui les a program-
mes, se contente de rajouter ces chiers son projet, dinclure len-tte
dans ses sources et de proter de ce que len-tte dclare.
Le chier den-tte doit tre sufsamment clair et informatif pour que
lutilisateur nait pas regarder le chier spar lui-mme
a
.
a. Dailleurs, si lutilisateur le regarde, il peut tre tent de tirer prot de ce qui sy trouve et
dutiliser plus que ce que len-tte dclare. Or, le crateur du chier spar et de len-tte peut
par la suite tre amen changer dans son source la faon dont il a programm les fonctions
sans pour autant changer leurs fonctionnalits. Lutilisateur qui a "trich" en allant regarder
dans le chier spar peut alors voir ses programmes ne plus marcher. Il na pas respect la
rgle du jeu qui tait de nutiliser que les fonctions de len-tte sans savoir comment elles sont
implmentes. Nous reparlerons de tout a avec les objets. Nous pourrons alors faire en sorte
que lutilisateur ne triche pas... De toute faon, notre niveau actuel, le crateur et lutilisateur
sont une seule et mme personne. A elle de ne pas tricher !
10. On devrait mme tout faire pour bien les cacher et pour interdire au chier principal de les utiliser.
Il serait possible de le faire ds maintenant, mais nous en reparlerons plutt quand nous aborderons les
objets...
11. Une mme fonction peut alors se retrouver dnie plusieurs fois : dans le chier spar et dans le
chier principal qui linclut. Or, sil est possible de dclarer autant de fois que ncessaire une fonction, il
est interdit de la dnir plusieurs fois (ne pas confondre avec la surcharge qui rend possible lexistence
de fonctions diffrentes sous le mme nom - cf section 3.2.6)
81
6.2. Oprateurs 6. Plusieurs chiers !
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 chiers den-tte est celui de linclusion mutuelle. Il arrive que les
chiers den-tte aient besoin den inclure dautres eux-mmes. Or, si le chier 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 n qui provoque une erreur
12
. Pour viter cela, on utilise
une instruction du pr-processeur signalant quun chier dj inclus ne doit plus ltre
nouveau : on ajoute
#pragma once au dbut de chaque chier 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 chier den-tte. Par exemple VECT_H pour le
chier vect.h.
Placer #ifndef VECT_Het #dene VECT_Hau dbut du chier vect.h et #endif
la n.
6.2 Oprateurs
Le C++ permet de dnir les oprateurs +, -, etc. quand les oprandes sont de nou-
veaux types. Voici trs succinctement comment faire. Nous laissons au lecteur le soin
de dcouvrir seul quels sont les oprateurs quil est possible de dnir.
Considrons lexemple suivant qui dnit un vecteur
13
2D et en implmente lad-
dition :
s t r uc t vect {
double x , y ;
} ;
vect pl us ( vect m, vect n) {
vect p={m. x+n . x ,m. y+n . y } ;
ret urn p;
}
i nt main ( ) {
vect a ={ 1 , 2} , b ={ 3 , 4 } ;
vect c=pl us ( a , b ) ;
ret urn 0;
}
Voici comment dnir le + entre deux vect et ainsi remplacer la fonction plus() :
s t r uc t vect {
double x , y ;
} ;
vect operat or +( vect m, vect n) {
12. Les pr-processeurs savent heureusement dtecter ce cas de gure.
13. Coin des collgiens : vous ne savez pas ce quest un vecteur... mais vous tes plus forts en pro-
grammation que les "vieux". Alors regardez les sources qui suivent et vous saurez ce quest un vecteur
2D!
82
6. Plusieurs chiers ! 6.3. Rcration : TP suite et n
vect p={m. x+n . x ,m. y+n . y } ;
ret urn p;
}
i nt main ( ) {
vect a ={ 1 , 2} , b ={ 3 , 4 } ;
vect c=a+b ;
ret urn 0;
}
Nous pouvons aussi dnir un produit par un scalaire, un produit scalaire
14
, etc
15
.
/ / Pr o dui t par un s c a l a i r e
vect operat or ( double s , vect m) {
vect p={ s m. x , s m. y } ;
ret urn p;
}
/ / Pr o dui t s c a l a i r e
double operat or ( vect m, vect n) {
ret urn m. xn . x+m. yn . y ;
}
i nt main ( ) {
vect a ={ 1 , 2} , b ={ 3 , 4 } ;
vect c=2a ;
double s=ab ;
ret urn 0;
}
Remarquez que les deux fonctions ainsi dnies sont diffrentes bien que de mme
nom(operator) car elles prennent des paramtres diffrents (cf surcharge section 3.2.6).
6.3 Rcration : TP suite et n
Le programme du TP prcdent tant un exemple parfait de besoin de chiers spa-
rs (structures bien identies, 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 che habituelle...
14. Dans ce cas, on utilise a
*
b et non a.b, le point ntant pas dnissable car rserv laccs aux
champs de la structure
15. On peut en fait dnir ce qui existe dj sur les types de base. Attention, il est impossible de
rednir les oprations des types de base ! Pas question de donner un sens diffrent 1+1.
83
6.4. Fiche de rfrence 6. Plusieurs chiers !
Fiche de rfrence (1/2)
Variables
Dnition :
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=254;
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);
int i,j;
double x=double(i)/j;
Tests
Comparaison :
== != < > <= >=
Ngation : !
Combinaisons : && ||
if (i==0)
j=1;
if (i==0)
j=1;
else
j=2;
if (i==0) {
j=1;
k=2;
}
bool t=(i==0);
if (t)
j=1;
switch (i) {
case 1:
...;
...;
break;
case 2:
case 3:
...;
break;
default:
...;
}
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)
...
Fonctions
Dnition :
int plus(int a,int b) {
int c=a+b;
return c;
}
void affiche(int a) {
cout << a << endl;
}
Dclaration :
int plus(int a,int b);
Retour :
int signe(double x) {
if (x<0)
return -1;
if (x>0)
return 1;
return 0;
}
void afficher(int x,
int y) {
if (x<0 || y<0)
return;
if (x>=w || y>=h)
return;
DrawPoint(x,y,RED);
}
Appel :
int f(int a) { ... }
int g() { ... }
...
int i=f(2),j=g();
Rfrences :
void swap(int& a,
int& b){
int tmp=a;
a=b;b=tmp;
}
...
int x=3,y=2;
swap(x,y);
Surcharge :
int hasard(int n);
int hasard(int a,
int b);
double hasard();
Oprateurs :
vect operator+(
vect A,vect B) {
...
}
...
vect C=A+B;
84
6. Plusieurs chiers ! 6.4. Fiche de rfrence
Fiche de rfrence (2/2)
Tableaux
Dnition :
double x[10],y[10];
for(int i=0;i<10;i++)
y[i]=2
*
x[i];
const int n=5;
int i[n],j[2
*
n]; //OK
Initialisation :
int t[4]={1,2,3,4};
string s[2]={"ab","c"};
Affectation :
int s[3]={1,2,3},t[3];
for (int i=0;i<3;i++)
t[i]=s[i];
En paramtre :
void init(int t[4]) {
for(int i=0;i<4;i++)
t[i]=0;
}
void init(int t[],
int n) {
for(int i=0;i<n;i++)
t[i]=0;
}
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};
Compilation spare
#include "vect.h", y
compris dans vect.cpp
Fonctions : dclarations dans
le .h, dnitions dans le .cpp
Types : dnitions dans le .h
Ne dclarer dans le .h que les
fonctions utiles.
#pragma once au dbut du
chier.
Ne pas trop dcouper...
Divers
i++;
i--;
i-=2;
j+=3;
j=i%n; // Modulo
#include <cstdlib>
...
i=rand()%n;
x=rand()/
double(RAND_MAX);
#include <ctime>
...
srand((unsigned int)
time(0));
#include <cmath>
double sqrt(double x);
double cos(double x);
double sin(double x);
double acos(double x);
#include <string>
using namespace std;
string s="hop";
char c=s[0];
int l=s.size();
Entres/Sorties
#include <iostream>
using namespace std;
...
cout <<"I=" <<i <<endl;
cin >> i >> j;
Erreurs frquentes
Pas de dnition de fonction
dans une fonction!
int q=r=4; // NON!
if (i=2) // NON!
if i==2 // NON!
if (i==2) then // NON!
for (int i=0,i<100,i++)
// NON!
int f() {...}
...
int i=f; // NON!
double x=1/3; // NON!
int i,j;
double x;
x=i/j; // NON!
x=double(i/j); //NON!
double x[10],y[10];
for (int i=1;i<=10;i++)
y[i]=2
*
x[i]; //NON
int n=5;
int t[n]; // NON
int f()[4] { // NON!
int t[4];
...
return t; // NON!
}
int t[4]; t=f();
int s[3]={1,2,3},t[3];
t=s; // NON!
int t[2];
t={1,2}; // NON!
struct Point {
double x,y;
} // NON!
Point a;
a={1,2}; // NON!
#include "vec.cpp"//NON
Imagine++
Voir documentation...
Clavier
Build : F7
Debug : F5
Step over : F10
Step inside : F11
Indent : Ctrl+K,Ctrl+F
Add New It. : Ctrl+Maj+A
Add Exist. It. : Alt+Maj+A
Conseils
Travailler en local
Imagine++ Project
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 chiers spars.
Le .h doit sufre lutilisa-
teur (qui ne doit pas regarder
le .cpp)
85
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 fonc-
tions rcursives, etc. Aprs cela, nous pourrons enn utiliser des tableaux de taille variable
(sans pour autant rentrer vraiment dans la notion dlicate de pointeur).

7.1 Lappel dune fonction


Il sagit l dune nouvelle occasion pour vous de comprendre enn ce qui se passe
dans un programme...
7.1.1 Exemple
Considrons le programme suivant :
1 # i ncl ude <i ostream>
2 usi ng namespace st d ;
3
4 void v e r i f i e ( i nt p, i nt q , i nt quo , i nt r es ) {
5 i f ( res <0 || res >=q || qquo+r es ! =p)
6 cout << " Tiens , c es t bi zar r e ! " << endl ;
7 }
8
9 i nt di vi s e ( i nt a , i nt b , i nt& r ) {
10 i nt q ;
11 q=a/b ;
12 r=aqb ;
13 v e r i f i e ( a , b , q , r ) ;
14 ret urn q ;
15 }
16 i nt main ( )
17 {
18 i nt num, denom;
19 do {
20 cout << " Ent rez deux e nt i e r s po s i t i f s : " ;
7.1. Lappel dune fonction 7. La mmoire
21 ci n >> num >> denom;
22 } while (num<=0 || denom<=0) ;
23 i nt quot i ent , r e s t e ;
24 quot i ent =di vi s e (num, denom, r e s t e ) ;
25 cout << num << "/" << denom << " = " << quot i ent
26 << " ( I l r e s t e " << r e s t e << " ) " << endl ;
27 ret urn 0;
28 }
Calculant le quotient et le reste dune division entire, et vriant 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 ta-
bleau 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 num denom quotient reste a b r q
d
ret
d
p
v
q
v
quo res
18 ? ?
21 23 3
23 23 3 ? ?
24a 23 3 ? ?
9 23 3 ? ? 23 3 [reste]
10 23 3 ? ? 23 3 [reste] ?
11 23 3 ? ? 23 3 [reste] 7
12 23 3 ? 2 23 3 [reste] 7
13a 23 3 ? 2 23 3 [reste] 7
4 23 3 ? 2 23 3 [reste] 7 23 3 7 2
5 23 3 ? 2 23 3 [reste] 7 23 3 7 2
7 23 3 ? 2 23 3 [reste] 7
13b 23 3 ? 2 23 3 [reste] 7
14 23 3 ? 2 23 3 [reste] 7 7
15 23 3 ? 2 7
24b 23 3 7 2 7
25 23 3 7 2
28
A posteriori, on constate quon a implicitement suppos que lorsque le programme
est en train dexcuter divise() , la fonction main() et ses variables existent encore et
quelles attendent simplement la n de divise() . Autrement dit :
Un appel de fonction est un mcanisme qui permet de partir excuter mo-
mentanment cette fonction puis de retrouver la suite des instructions et les
variables quon avait provisoirement quittes.
Les fonctions sappelant les unes les autres, on se retrouve avec des appels de fonc-
tions imbriqus les uns dans les autres : main() appelle divise() qui lui-mme appelle
verie ()
2
. Plus prcisment, cette imbrication est un empilement et on parle de pile
des appels. Pour mieux comprendre cette pile, nous allons utiliser le debuggeur. Avant
cela, prcisons ce quun informaticien entend par pile.
1. par exemple 24a et 24b
2. Et dailleurs main() a lui-mme t appel par une fonction a laquelle il renvoie un int.
88
7. La mmoire 7.1. Lappel dune fonction
Pile/File
Une pile est une structure permettant de mmoriser des donnes dans laquelle
celles-ci sempilent de telle sorte que celui qui est rang en dernier dans la pile en
est extrait en premier. En anglais, une pile (stack) est aussi appele LIFO (last in
rst 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 le (en anglais queue), cest la mme chose mais le premier arriv est
le premier sorti (FIFO). Par exemple, aprs un push(1), un push(2) et un push(3),
le premier pop() donnera 1, le deuxime pop() donnera 2 et un dernier pop()
donnera 3.
7.1.2 Pile des appels et dbuggeur
Observons donc la gure 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 afche 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
. Vriez 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 verie () . La pile des appels un ni-
veau de plus, divise() est en attente la ligne 13 et main() toujours en 24. Vriez
au passage que la variable q afche est bien celle de verie () , 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
afcher 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 afch 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 re-
tour 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.
89
7.1. Lappel dune fonction 7. La mmoire
(a)
(b)
(c)
(d)
(e)
(f)
(g)
FIGURE 7.1 Appels de fontions
90
7. La mmoire 7.2. Variables Locales
pile variable valeur
place libre
top a 23
b 3
q
d
7
r [reste]
denom 3
num 23
quotient ?
reste ?
pris par les ...
les fonctions ...
avant main() ...
pile variable valeur
place libre
top p
v
23
q
v
3
quo 7
res 2
a 23
b 3
q
d
7
r [reste]
denom 3
num 23
quotient ?
reste 2
pris par les ...
les fonctions ...
avant main() ...
pile variable valeur
place libre
top denom 3
num 23
quotient 7
reste 2
pris par les ...
les fonctions ...
avant main() ...
FIGURE 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
verie () , puis jusqu sortir de divise() . On voit bien quotient, qui est encore
non dni, 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 enn
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" ( )
91
7.3. Fonctions rcursives 7. La mmoire
7.2.1 Paramtres
Pour les paramtres, cest simple :
Les paramtres sont en fait des variables locales ! Leur seule spcicit est
dtre initialiss ds le dbut de la fonction avec les valeurs passes lappel
de la fonction.
7.2.2 La pile
Les variables locales (et donc les paramtres) ne sont pas mmorises des adresses
xes 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 beau-
coup 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 gure 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 rcur-
sive de la programmer :
5 i nt f ac t 1 ( i nt n)
6 {
7 i f ( n==1)
8 ret urn 1;
9 ret urn n f ac t 1 ( n1) ;
10 }
On remarque videmment que les fonctions rcursives contiennent (en gnral au d-
but, 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. Souvenons-nous du chapitre 2.
7. Et permettra de faire des fonctions rcursives, cf section suivante !
8. Coin des collgiens : La factorielle dun nombre entier n scrit n! et vaut n! = 1 2 ... n.
9. Le fait de pouvoir mettre des return au milieu des fonctions est ici bien commode !
92
7. La mmoire 7.3. Fonctions rcursives
7.3.1 Pourquoi a marche ?
Si les fonctions avaient mmoris leurs variables locales des adresses xes, la
rcursivit naurait pas pu marcher : lappel rcursif aurait cras les valeurs des va-
riables. 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 n
fact1(3)
ret
fact1(3)
n
fact1(2)
ret
fact1(2)
n
fact1(1)
ret
fact1(1)
5
fact1(3)
3
9a
fact1(3)
3
5
fact1(2)
3 2
9a
fact1(2)
3 2
5
fact1(1)
3 2 1
8
fact1(1)
3 2 1 1
10
fact1(1)
3 2 1
9b
fact1(2)
3 2 2 1
10
fact1(2)
3 2
9b
fact1(3)
3 6 2
10
fact1(3)
6
Ce tableau devient difcile 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 n
fact1(1)
1
n
fact1(2)
2
n
fact1(3)
3
ce que lon peut aisment vrier 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 Efcacit
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 innie et mme relativement limite.
Ainsi, le programme suivant
22 / / Fa i t d b o r de r l a p i l e
23 i nt f ac t 3 ( i nt n)
24 {
10. Cest une erreur classique de dbutant que de vouloir abuser du rcursif.
93
7.3. Fonctions rcursives 7. La mmoire
25 i f ( n==1)
26 ret urn 1;
27 ret urn n f ac t 3 ( n+1) ; / / e r r e ur !
28 }
dans lequel une erreur sest glisse va sappeler thoriquement linni 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 sufsamment 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 :
/ / Ve r s i on i t r a t i v e
i nt f ac t 2 ( i nt n)
{
i nt f =1;
f or ( i nt i =2; i <=n ; i ++)
f = i ;
ret urn f ;
}
ce qui aprs tout nest pas si terrible.
Enn, 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 dnie
par :
_
f
0
= f
1
= 1
f
n
= f
n1
+ f
n2
et qui donne : 1, 1, 2, 3, 5, 8,... En version rcursive :
32 / / Tr s l e n t !
33 i nt f i b1 ( i nt n) {
34 i f ( n<2)
35 ret urn 1;
36 ret urn f i b1 ( n2)+ f i b1 ( n1) ;
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 elle-
mme, ce qui prend un certain temps ! Il est donc raisonnable den programmer une
version drcursive :
39 / / D r c ur s i v e
40 i nt f i b2 ( i nt n) {
11. Sous Visual, il sarrte pour n = 5000 environ.
12. Nous verrons dans un autre chapitre les fonctions inline qui rpondent ce problme.
94
7. La mmoire 7.4. Le tas
41 i nt fnm2=1 , fnm1=1;
42 f or ( i nt i =2; i <=n ; i ++) {
43 i nt fn=fnm2+fnm1 ;
44 fnm2=fnm1 ;
45 fnm1=fn ;
46 }
47 ret urn fnm1 ;
48 }
Mentionnons aussi quil existe des fonctions sufsamment tordues pour que leur ver-
sion 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
defcacit, 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 innie 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 nt main ( )
33 {
34 const i nt n=500000;
35 i nt t [ n ] ;
36 . . .
37 }
Il sexcute avec une erreur : "stack overow". La variable locale t nest pas trop grande
pour lordinateur
13
: elle est trop grande pour tenir dans la pile. Jusqu prsent, on
savait quon tait limit aux tableaux de taille constante. En ralit, on est aussi limit
aux petits tableaux. Il est donc grand temps dapprendre utiliser le tas !
7.4.2 Tableaux de taille variable
Nous fournissons ici une rgle appliquer en aveugle. Sa comprhension viendra plus tard
si ncessaire.
Lorsquon veut utiliser un tableau de taille variable, il ny a que deux choses faire,
mais elle sont essentielles toutes les deux
14
:
13. 500000x4 soit 2Mo seulement !
14. Et le dbutant oublie toujours la deuxime, ce qui a pour consquence des programmes qui gros-
sissent en quantit de mmoire occupe...
95
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 n de fonction), rajouter
la ligne delete[] t;
Le non respect de la rgle 2 fait que le tableau reste en mmoire jusqu la n du pro-
gramme, 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 # i ncl ude <i ostream>
2 usi ng namespace st d ;
3
4 void rempl i t ( i nt t [ ] , i nt n)
5 {
6 f or ( i nt i =0; i <n ; i ++)
7 t [ i ]= i +1;
8 }
9
10 i nt somme( i nt t [ ] , i nt n)
11 {
12 i nt s =0;
13 f or ( i nt i =0; i <n ; i ++)
14 s+=t [ i ] ;
15 ret urn s ;
16 }
17
18 void f i x e ( )
19 {
20 const i nt n=5000;
21 i nt t [ n ] ;
22 rempl i t ( t , n ) ;
23 i nt s=somme( t , n ) ;
24 cout << s << " devr ai t val oi r " << n( n+1)/2 << endl ;
25 }
26
27 void var i abl e ( )
28 {
29 i nt n ;
30 cout << "Un e nt i e r SVP: " ;
31 ci n >> n ;
32 i nt t =new i nt [ n ] ; / / Al l o c a t i o n
33 rempl i t ( t , n ) ;
34 i nt s=somme( t , n ) ;
96
7. La mmoire 7.5. Loptimiseur
35 cout << s << " devr ai t val oi r " << n( n+1)/2 << endl ;
36 del et e [ ] t ; / / De s a l l o c a t i o n : ne pas o u b l i e r !
37 }
38
39 i nt main ( )
40 {
41 f i x e ( ) ;
42 var i abl e ( ) ;
43 ret urn 0;
44 }
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.
Enn, 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 pro-
gramme 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 pro-
gramme, 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.
97
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 lop-
timiseur puisse efcacement 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 chiers 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 chier 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 :
# i ncl ude <c as s er t >
. . .
i nt n ;
ci n >> n ;
as s e r t ( n>0) ;
i nt t =new i nt [ n ] ; / / Al l o c a t i o n
Si lutilisateur entre une valeur ngative ou nulle, les consquences pourraient tre f-
cheuses. 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 vriant que la valeur est raison-
nable.
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. An de rendre le tout plus attrayant, le tri sera
visualis graphiquement et chronomtr (gure 7.3).
98
7. La mmoire 7.8. Fiche de rfrence
FIGURE 7.3 Deux tris en cours dexcution : tri bulle et Quicksort...
7.8 Fiche de rfrence
Fiche de rfrence (1/3)
Variables
Dnition :
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=254;
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);
int i,j;
double x=double(i)/j;
Pile/Tas
Tests
Comparaison :
== != < > <= >=
Ngation : !
Combinaisons : && ||
if (i==0)
j=1;
if (i==0)
j=1;
else
j=2;
if (i==0) {
j=1;
k=2;
}
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)
...
99
7.8. Fiche de rfrence 7. La mmoire
Fiche de rfrence (2/3)
Fonctions
Dnition :
int plus(int a,int b) {
int c=a+b;
return c;
}
void affiche(int a) {
cout << a << endl;
}
Dclaration :
int plus(int a,int b);
Retour :
int signe(double x) {
if (x<0)
return -1;
if (x>0)
return 1;
return 0;
}
void afficher(int x,
int y) {
if (x<0 || y<0)
return;
if (x>=w || y>=h)
return;
DrawPoint(x,y,RED);
}
Appel :
int f(int a) { ... }
int g() { ... }
...
int i=f(2),j=g();
Rfrences :
void swap(int& a,
int& b){
int tmp=a;
a=b;b=tmp;
}
...
int x=3,y=2;
swap(x,y);
Surcharge :
int hasard(int n);
int hasard(int a,
int b);
double hasard();
Oprateurs :
vect operator+(
vect A,vect B) {
...
}
...
vect C=A+B;
Pile des appels
Itratif/Rcursif
Tableaux
Dnition :
double x[10],y[10];
for(int i=0;i<10;i++)
y[i]=2
*
x[i];
const int n=5;
int i[n],j[2
*
n]; //OK
Initialisation :
int t[4]={1,2,3,4};
string s[2]={"ab","c"};
Affectation :
int s[3]={1,2,3},t[3];
for (int i=0;i<3;i++)
t[i]=s[i];
En paramtre :
void init(int t[4]) {
for(int i=0;i<4;i++)
t[i]=0;
}
void init(int t[],
int n) {
for(int i=0;i<n;i++)
t[i]=0;
}
Taille variable :
int
*
t=new int[n];
...
delete[] t;
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};
Compilation spare
#include "vect.h", y
compris dans vect.cpp
Fonctions : dclarations dans
le .h, dnitions dans le .cpp
Types : dnitions dans le .h
Ne dclarer dans le .h que les
fonctions utiles.
#pragma once au dbut du
chier.
Ne pas trop dcouper...
Divers
i++;
i--;
i-=2;
j+=3;
j=i%n; // Modulo
#include <cstdlib>
...
i=rand()%n;
x=rand()/
double(RAND_MAX);
#include <ctime>
...
srand((unsigned int)
time(0));
#include <cmath>
double sqrt(double x);
double cos(double x);
double sin(double x);
double acos(double x);
#include <string>
using namespace std;
string s="hop";
char c=s[0];
int l=s.size();
#include <ctime>
s=double(clock())
/CLOCKS_PER_SEC;
Entres/Sorties
#include <iostream>
using namespace std;
...
cout <<"I=" <<i <<endl;
cin >> i >> j;
Clavier
Build : F7
Debug : F5
Step over : F10
Step inside : F11
Indent : Ctrl+K,Ctrl+F
Add New It. : Ctrl+Maj+A
Add Exist. It. : Alt+Maj+A
Step out : Maj+F11
Run to curs. : Click droit
Compltion : Alt+
100
7. La mmoire 7.9. Examens sur machine
Fiche de rfrence (3/3)
Erreurs frquentes
Pas de dnition de fonction
dans une fonction!
int q=r=4; // NON!
if (i=2) // NON!
if i==2 // NON!
if (i==2) then // NON!
for (int i=0,i<100,i++)
// NON!
int f() {...}
...
int i=f; // NON!
double x=1/3; // NON!
int i,j;
double x;
x=i/j; // NON!
x=double(i/j); //NON!
double x[10],y[10];
for (int i=1;i<=10;i++)
y[i]=2
*
x[i]; //NON
int n=5;
int t[n]; // NON
int f()[4] { // NON!
int t[4];
...
return t; // NON!
}
int t[4]; t=f();
int s[3]={1,2,3},t[3];
t=s; // NON!
int t[2];
t={1,2}; // NON!
struct Point {
double x,y;
} // NON!
Point a;
a={1,2}; // NON!
#include "vec.cpp"//NON
Imagine++
Voir documentation...
Conseils
Travailler en local
Imagine++ Project
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 chiers spars.
Le .h doit sufre lutilisa-
teur (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;
7.9 Examens sur machine
Nous vous conseillons aussi de vous confronter aux examens proposs en annexe.
Vous avez toutes les connaissances ncessaires.
101
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 xe, nous d-
taillons lallocation dynamique
1
dj vue en 7.4.2 et expliquons enn les pointeurs, du moins
partiellement. A travers lexemple des matrices (et des images en TP) nous mlangeons struc-
tures et allocation dynamique. Il sagira l de notre structure de donne la plus complexe avant
larrive tant attendue - et maintenant justie - des objets...

8.1 Tableaux bidimensionnels


8.1.1 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 im-
briques.
Leurs dimensions doivent tre constantes (lignes 6 et 7).
1 i nt A[ 2 ] [ 3 ] ;
2 f or ( i nt i =0; i <2; i ++)
3 f or ( i nt j =0; j <3; j ++)
4 A[ i ] [ j ]= i +j ;
5 i nt B[ 2 ] [ 3 ] ={ { 1 , 2 , 3 } , { 4 , 5 , 6 } } ;
6 const i nt M=2 ,N=3;
7 i nt C[M] [N] ;
1 2 3
4 6 5
B[0]
B[1]
Tableau 2D B [2][3]={{1,2,3},{4,5,6}} .
Notez que B[0] est le tableau 1D {1,2,3}
et B[1] le tableau 1D {4,5,6} .
La gure ci-dessus montre le tableau B. A noter que B[0] et B[1] sont des tableaux
1D reprsentant les lignes de B.
1. cest--dire lallocation de mmoire dans le tas avec new et delete.
8.1. Tableaux bidimensionnels 8. Allocation dynamique
8.1.2 Limitations
Vis--vis des fonctions, les particularits sont les mmes quen 1D :
Impossible de retourner un tableau 2D.
Passage uniquement par variable.
mais avec une restriction supplmentaire :
On est oblig de prciser les dimensions dun tableau 2Dparamtre de fonc-
tion.
Impossible donc de programmer des fonctions qui peuvent travailler sur des tableaux
de diffrentes tailles comme dans le cas 1D (cf 4.3.1). Cest trs restrictif et explique
que les tableaux 2D ne sont pas toujours utiliss. On peut donc avoir le programme
suivant :
1 / / Pas s age de pa r a m t r e
2 double t r ac e ( double A[ 2 ] [ 2 ] ) {
3 double t =0;
4 f or ( i nt i =0; i <2; i ++)
5 t +=A[ i ] [ i ] ;
6 ret urn t ;
7 }
8
9 / / Le pa s s a ge e s t t o uj o ur s par r f r e n c e . . .
10 void s et ( double A[ 2 ] [ 3 ] ) {
11 f or ( i nt i =0; i <2; i ++)
12 f or ( i nt j =0; j <3; j ++)
13 A[ i ] [ j ]= i +j ;
14 }
15
16 . . .
17 double D[ 2 ] [ 2 ] = { { 1 , 2 } , { 3 , 4 } } ;
18 double t =t r ac e (D) ;
19 double E [ 2 ] [ 3 ] ;
20 s et ( E ) ;
21 . . .
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 / / OK
2 void s et ( double A[ ] , i nt n , double x ) {
3 f or ( i nt i =0; i <n ; i ++)
4 A[ i ]=x ;
5 }
6 / / NON! ! ! ! ! ! ! ! ! ! ! ! ! ! !
7 / / do ubl e A[ ] [ ] e s t r e f u s
8 void s et ( double A[ ] [ ] , double m, double n , double x ) {
9 f or ( i nt i =0; i <m; i ++)
10 f or ( i nt j =0; j <n ; j ++)
11 A[ i ] [ j ]=x ;
12 }
104
8. Allocation dynamique 8.1. Tableaux bidimensionnels
B[0]=1
B[1]=4
B[2]=2
B[3]=5
B[4]=3
B[5]=6
FIGURE 8.1 La matrice B de lexemple prcdent stocke en 1D.
8.1.3 Solution
En pratique, ds que lon doit manipuler des tableaux de dimension 2 (ou plus !) de
diffrentes tailles, on les mmorise dans des tableaux 1D en stockant par exemple les
colonnes les unes aprs les autres pour proter 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 et ( double A[ ] , i nt m, i nt n) {
2 f or ( i nt i =0; i <m; i ++)
3 f or ( i nt j =0; j <n ; j ++)
4 A[ i +m j ]= i +j ;
5 }
6 . . .
7 double F [ 2 3 ] ;
8 s et ( F , 2 , 3 ) ;
9 double G[ 3 5 ] ;
10 s et (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 produi t ( double A[ ] , i nt m, i nt n , double x [ ] , double y [ ] )
3 {
4 f or ( i nt i =0; i <m; i ++) {
5 y[ i ] =0;
6 f or ( i nt j =0; j <n ; j ++)
7 y[ i ]+=A[ i +m j ] x [ j ] ;
8 }
9 }
10
11 . . .
12 double P[ 23] , x [ 3 ] , y [ 2 ] ;
13 . . .
14 / / P = . . . x = . . .
15 produi t ( P, 2 , 3 , x , y ) ; / / y=Px
105
8.2. Allocation dynamique 8. Allocation dynamique
8.2 Allocation dynamique
Il ny a pas dallocation dynamique possible pour les tableaux 2D. Il faut donc vrai-
ment 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 rednir :
1 i nt m, n ;
2 . . .
3 double A=new double [mn ] ;
4 double x=new double [ n ] ;
5 double y=new double [m] ;
6 . . .
7 / / A= . . . x = . . .
8 produi t (A, m, n , x , y ) ; / / y=Ax
9 . . .
10 del et e [ ] A;
11 del et e [ ] x ;
12 del et e [ ] y ;
8.2.1 Pourquoi a marche ?
Il est maintenant temps dexpliquer pourquoi, une fois allous, nous pouvons utili-
ser des tableaux dynamiques exactement comme des tableaux de taille xe. Il suft de
comprendre les tapes suivantes :
1. int t [n] dnit une variable locale, donc de la mmoire dans la pile, capable de
stocker n variables int.
2. int t dnit une variable de type "pointeur" dint, cest--dire que t peut m-
moriser 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 xe 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 xe, 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 [] signie en ralit que s
est ladresse du tableau. Ce qui fait quen n 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
nalement que des adresses
cest plutt lappel f ( t ), avec t tableau de taille xe, 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.
106
8. Allocation dynamique 8.2. Allocation dynamique
logiquement, on devrait mme dclarer f par f ( int s) au lieu de f ( int s []) .
Les deux sont en fait possibles et synonymes.
Vous pouvez donc maintenant programmer, en comprenant, ce genre de choses :
1 double somme( double t , i nt n) { / / Synt axe " p o i nt e ur "
2 double s =0;
3 f or ( i nt i =0; i <n ; i ++)
4 s+=t [ i ] ;
5 ret urn s ;
6 }
7 . . .
8 i nt t 1 [ 4 ] ;
9 . . .
10 double s1=somme( t1 , 4 ) ;
11 . . .
12 i nt t 2=new i nt [ n ] ;
13 . . .
14 double s2=somme( t2 , n ) ;
15 . . .
16 del et e [ ] t 2 ;
8.2.2 Erreurs classiques
Vous comprenez maintenant aussi les erreurs classiques suivantes (que vous nvi-
terez pas pour autant !).
1. Oublier dallouer :
i nt t ;
f or ( i nt i =0; i <n ; i ++)
t [ i ] = . . . / / Horreur : t vaut n i mpo r t e
/ / quoi comme a d r e s s e
2. Oublier de dsallouer :
void f ( i nt n) {
i nt t =new i nt [ n ] ;
. . .
} / / On o u b l i e d e l e t e [ ] t ;
/ / Chaque a ppe l f ( ) va pe r dr e n i nt dans l e t a s !
3. Ne pas dsallouer ce quil faut :
i nt t =new i nt [ n ] ;
i nt s=new i nt [ n ] ;
. . .
s=t ; / / Ai 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 pas r e c o p i l a zone p o i nt e par t dans c e l l e
/ / p o i nt e par s ! )
. . .
del et e [ ] t ; / / OK
del et e [ ] s ; / / Cat a : Non s e ul e me nt on ne l i b r e pas l a mmoi re
107
8.2. Allocation dynamique 8. Allocation dynamique
/ / i n i t i a l e me n t m mor i s e dans s , mai s en pl us on
/ / d s a l l o u e nouveau c e l l e qui v i e nt 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 nat-
tend pas toujours la n 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 i nt t [ 1 0 ] ;
3 i nt s=new i nt [ n ] ;
4 . . .
5 del et e [ ] s ; / / s i s ne s e r t pl us dans l a s u i t e . . .
6 / / Autant l i b r e r mai nt e nant . . .
7 . . .
8 } / / Par c o nt r e , t a t t e nd c e t t e l i g n e pour mouri r .
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 newet le delete ne se fassent pas dans la mme fonction (atten-
tion, 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 suft de respecter la logique :
Une fonction qui retourne un pointeur se dclare int f ();
1 i nt al l oue ( i nt n) {
2 ret urn new i nt [ n ] ;
3 }
4 . . . .
5 i nt t =al l oue ( 1 0 ) ;
6 . . .
Un pointeur pass en paramtre une fonction lest par valeur. Ne pas mlan-
ger avec le fait quun tableau est pass par rfrence ! Considrez le programme
suivant :
1 void f ( i nt t , i nt n) {
2 . . . .
3 t [ i ] = . . . ; / / On mo d i f i e t [ i ] mai s pas t !
4 t = . . . / / Une t e l l e l i g n e ne c h a ng e r a i t pas s
5 / / dans l a f o n c t i o n a p p e l a nt e
6 }
7 . . .
8 i nt s=new i nt [m] ;
9 f ( s ,m) ;
108
8. Allocation dynamique 8.3. Structures et allocation dynamique
En fait, cest parce quon passe ladresse dun tableau quon peut modier ses l-
ments. Par ignorance, nous disions que les tableaux taient passs par rfrence
en annonant cela comme une exception. Nous pouvons maintenant rectier :
Un tableau est en fait pass via son adresse. Cette adresse est passe par
valeur. Mais ce mcanisme permet la fonction appele de modier le
tableau. Dire quun tableau est pass par rfrence tait un abus de
langage simplicateur.
Si on veut vraiment passer le pointeur par rfrence, la syntaxe est logique :
int& t. Un cas typique de besoin est :
1 / / t e t n s e r o nt mo d i f i s ( e t pl us s e ul e me nt t [ i ] )
2 void al l oue ( i nt & t , i nt& n) {
3 ci n >> n ; / / n e s t c h o i s i au c l a v i e r
4 t =new i nt [ n ] ;
5 }
6 . . .
7 i nt t ;
8 i nt n ;
9 al l oue ( t , n ) ; / / t e t n s o nt a f f e c t s par a l l o u e ( )
10 . . .
11 del et e [ ] t ; / / Ne pas o u b l i e r pour aut a nt !
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 dnit plusieurs pointeurs en
mme-temps. Ainsi, int t , s,u; dnit deux pointeurs dint (les variables t et u) et
un int (la variable s).
8.3 Structures et allocation dynamique
Passer systmatiquement un tableau et sa taille toutes les fonctions est videm-
ment 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 # i ncl ude <i ostream>
2 # i ncl ude <s t r i ng >
3 usi ng namespace st d ;
4
5 / / ==================================================
6 / / f o n c t i o n s sur l e s ma t r i c e s
7 / / p o ur r a i e nt e t r e dans un ma t r i c e . h e t un ma t r i c e . cpp
8
9 s t r uc t Mat ri ce {
10 i nt m, n ;
11 double t ;
12 } ;
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.
109
8.3. Structures et allocation dynamique 8. Allocation dynamique
13
14 Mat ri ce cr ee ( i nt m, i nt n) {
15 Mat ri ce M;
16 M.m=m;
17 M. n=n ;
18 M. t =new double [mn ] ;
19 ret urn M;
20 }
21
22 void de t r ui t ( Mat ri ce M) {
23 del et e [ ] M. t ;
24 }
25
26 Mat ri ce produi t ( Mat ri ce A, Mat ri ce B) {
27 i f (A. n! =B.m) {
28 cout << " Erreur ! " << endl ;
29 e x i t ( 1 ) ;
30 }
31 Mat ri ce C=cr ee (A. m, B. n ) ;
32 f or ( i nt i =0; i <A.m; i ++)
33 f or ( i nt j =0; j <B. n ; j ++) {
34 / / Ci j =Ai0 B0j +Ai1 B1j + . . .
35 C. t [ i +C.m j ] =0;
36 f or ( i nt k=0; k<A. n ; k++)
37 C. t [ i +C.m j ]+=A. t [ i +A.mk] B. t [ k+B.m j ] ;
38
39 }
40 ret urn C;
41 }
42
43 void a f f i c he ( s t r i ng s , Mat ri ce M) {
44 cout << s << " =" << endl ;
45 f or ( i nt i =0; i <M.m; i ++) {
46 f or ( i nt j =0; j <M. n ; j ++)
47 cout << M. t [ i +M.m j ] << " " ;
48 cout << endl ;
49 }
50 }
51
52 / / ==================================================
53 / / Ut i l i s a t e u r
54
55 i nt main ( )
56 {
57 Mat ri ce A=cr ee ( 2 , 3 ) ;
58 f or ( i nt i =0; i <2; i ++)
59 f or ( i nt j =0; j <3; j ++)
60 A. t [ i +2 j ]= i +j ;
61 a f f i c he ( "A" ,A) ;
110
8. Allocation dynamique 8.3. Structures et allocation dynamique
62 Mat ri ce B=cr ee ( 3 , 5 ) ;
63 f or ( i nt i =0; i <3; i ++)
64 f or ( i nt j =0; j <5; j ++)
65 B. t [ i +3 j ]= i +j ;
66 a f f i c he ( "B" , B ) ;
67 Mat ri ce C=produi t (A, B ) ;
68 a f f i c he ( "C" ,C) ;
69 de t r ui t (C) ;
70 de t r ui t ( B ) ;
71 de t r ui t (A) ;
72 ret urn 0;
73 }
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 rappro-
chons vraiment de la programmation objet...) Bref, on rajoutera en gnral :
10 double get ( Mat ri ce M, i nt i , i nt j ) {
11 ret urn M. t [ i +M.m j ] ;
12 }
13
14 void s et ( Mat ri ce M, i nt i , i nt j , double x ) {
15 M. t [ i +M.m j ]=x ;
16 }
que lutilisateur pourra appeler ainsi :
51 f or ( i nt i =0; i <2; i ++)
52 f or ( i nt j =0; j <3; j ++)
53 s et (A, i , j , i +j ) ;
et que celui qui programme les matrices pourra aussi utiliser pour lui :
39 void a f f i c he ( s t r i ng s , Mat ri ce M) {
40 cout << s << " =" << endl ;
41 f or ( i nt i =0; i <M.m; i ++) {
42 f or ( i nt j =0; j <M. n ; j ++)
43 cout << get (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
A et B tandis que la mmoire initiale de A ne le sera jamais, comme on peut le
voir sur la Fig. 8.2).
111
8.4. Boucles et continue 8. Allocation dynamique
A.t
B.t
A.t
B.t
A.t
B.t
detruit(A);
A.t
B.t
A=cree(2,3);
B=cree(2,3);
A=B; detruit(B);
...
...
...
...
...
...
...
...
FIGURE 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...
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 n de la boucle est saute
et on passe au tour suivant. Ainsi :
f or ( . . . ) {
. . .
i f (A)
cont i nue ;
. . .
i f ( B)
cont i nue ;
. . .
}
est quivalent (et remplace avantageusement au niveau clart et mise en page) :
f or ( . . . ) {
. . .
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).
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
112
8. Allocation dynamique 8.6. Fiche de rfrence
FIGURE 8.3 Deux images et diffrents traitements de la deuxime (ngatif, ou, relief,
dformation, contraste et contours).
changer de nos passionnantes matrices, nous travaillerons avec des images (gure 8.3).
8.6 Fiche de rfrence
Fiche de rfrence (1/3)
Boucles
do {
...
} while (!ok);
int i=1;
while (i<=100) {
...
i=i+1;
}
for (int i=1;i<=10;i++)
...
for (int i=1,j=10;j>i;
i=i+2,j=j-3)
...
for (int i=...)
for (int j=...) {
//saute le cas i==j
if (i==j)
continue;
...
}
Variables
Dnition :
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=254;
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);
int i,j;
double x=double(i)/j;
Pile/Tas
Clavier
Build : F7
Debug : F5
Step over : F10
Step inside : F11
Indent : Ctrl+K,Ctrl+F
Add New It. : Ctrl+Maj+A
Add Exist. It. : Alt+Maj+A
Step out : Maj+F11
Run to curs. : Click droit
Compltion : Alt+
Gest. tches : Ctrl+Maj+Ech
113
8.6. Fiche de rfrence 8. Allocation dynamique
Fiche de rfrence (2/3)
Fonctions
Dnition :
int plus(int a,int b) {
int c=a+b;
return c;
}
void affiche(int a) {
cout << a << endl;
}
Dclaration :
int plus(int a,int b);
Retour :
int signe(double x) {
if (x<0)
return -1;
if (x>0)
return 1;
return 0;
}
void afficher(int x,
int y) {
if (x<0 || y<0)
return;
if (x>=w || y>=h)
return;
DrawPoint(x,y,RED);
}
Appel :
int f(int a) { ... }
int g() { ... }
...
int i=f(2),j=g();
Rfrences :
void swap(int& a,
int& b){
int tmp=a;
a=b;b=tmp;
}
...
int x=3,y=2;
swap(x,y);
Surcharge :
int hasard(int n);
int hasard(int a,
int b);
double hasard();
Oprateurs :
vect operator+(
vect A,vect B) {
...
}
...
vect C=A+B;
Pile des appels
Itratif/Rcursif
Tableaux
Dnition :
double x[10],y[10];
for(int i=0;i<10;i++)
y[i]=2
*
x[i];
const int n=5;
int i[n],j[2
*
n]; //OK
Initialisation :
int t[4]={1,2,3,4};
string s[2]={"ab","c"};
Affectation :
int s[3]={1,2,3},t[3];
for (int i=0;i<3;i++)
t[i]=s[i];
En paramtre :
void init(int t[4]) {
for(int i=0;i<4;i++)
t[i]=0;
}
void init(int t[],
int n) {
for(int i=0;i<n;i++)
t[i]=0;
}
Taille variable :
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];
A[i+2
*
j]=...;
Taille variable (suite) :
int
*
t,
*
s,n;
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};
Compilation spare
#include "vect.h", y
compris dans vect.cpp
Fonctions : dclarations dans
le .h, dnitions dans le .cpp
Types : dnitions dans le .h
Ne dclarer dans le .h que les
fonctions utiles.
#pragma once au dbut du
chier.
Ne pas trop dcouper...
Tests
Comparaison :
== != < > <= >=
Ngation : !
Combinaisons : && ||
if (i==0)
j=1;
if (i==0)
j=1;
else
j=2;
if (i==0) {
j=1;
k=2;
}
bool t=(i==0);
if (t)
j=1;
switch (i) {
case 1:
...;
...;
break;
case 2:
case 3:
...;
break;
default:
...;
}
Entres/Sorties
#include <iostream>
using namespace std;
...
cout <<"I=" <<i <<endl;
cin >> i >> j;
114
8. Allocation dynamique 8.6. Fiche de rfrence
Fiche de rfrence (3/3)
Divers
i++;
i--;
i-=2;
j+=3;
j=i%n; // Modulo
#include <cstdlib>
...
i=rand()%n;
x=rand()/
double(RAND_MAX);
#include <ctime>
...
srand((unsigned int)
time(0));
#include <cmath>
double sqrt(double x);
double cos(double x);
double sin(double x);
double acos(double x);
#include <string>
using namespace std;
string s="hop";
char c=s[0];
int l=s.size();
#include <ctime>
s=double(clock())
/CLOCKS_PER_SEC;
#define _USE_MATH_DEFINES
#include <cmath>
double pi=M_PI;
Erreurs frquentes
Pas de dnition de fonction
dans une fonction!
int q=r=4; // NON!
if (i=2) // NON!
if i==2 // NON!
if (i==2) then // NON!
for (int i=0,i<100,i++)
// NON!
int f() {...}
...
int i=f; // NON!
double x=1/3; // NON!
int i,j;
double x;
x=i/j; // NON!
x=double(i/j); //NON!
double x[10],y[10];
for (int i=1;i<=10;i++)
y[i]=2
*
x[i]; //NON
int n=5;
int t[n]; // NON
int f()[4] { // NON!
int t[4];
...
return t; // NON!
}
int t[4]; t=f();
int s[3]={1,2,3},t[3];
t=s; // NON!
int t[2];
t={1,2}; // NON!
struct Point {
double x,y;
} // NON!
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!
Imagine++
Voir documentation...
Conseils
Travailler en local
Imagine++ Project
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 chiers spars.
Le .h doit sufre lutilisa-
teur (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;
115
9. Premiers objets
Chapitre 9
Premiers objets
Nous abordons maintenant notre dernire tape dans la direction dune meilleure organisa-
tion des programmes. Tantt nous structurions davantage les instructions (fonctions, chiers),
tantt nous nous intressions aux donnes (structures, tableaux). Nous allons maintenant pen-
ser donnes et instructions simultanment : cest l lide premire des objets, mme sils pos-
sdent de nombreux autres aspects
1
. Enn, nous justierons lemploi des objets par la notion
d"interface"
2
.

9.1 Philosophie
Runir les instructions en fonctions ou chiers est une bonne chose. Runir les don-
nes 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 chier regroupait souvent une structure et un certain nombre de fonc-
tions 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, luti-
lisation dun objet remplacera ce genre dinstructions :
obj a ;
i nt i =f ( a ) ; / / f o n c t i o n f ( ) a p p l i q u e a
par :
obj a ;
i nt i =a . f ( ) ; / / a ppe l l a mt hode f ( ) de a
1. Le plus important tant lhritage, que nous ne verrons pas dans ce cours, prfrant nous consacrer
dautres aspects du C++ plus indispensables et ngligs jusquici...
2. Nous exposerons une faon simple de crer des interfaces. Un programmeur C++ expriment
utilisera plutt de lhritage et des fonctions virtuelles pures, ce qui dpasse largement ce cours !
3. Il arrive mme parfois quun objet regroupe des fonctionnalits sans pour autant stocker la
moindre donne. Nous nutiliserons pas ici cette faon de prsenter les choses, dont le dbutant pourrait
rapidement abuser.
9.2. Exemple simple 9. Premiers objets
Vous lavez compris, il sagit ni plus ni moins de "ranger" les fonctions dans les
objets. Attention, crions tout de suite haut et fort qu
il ne faut pas abuser des objets, surtout lorsquon est dbutant. Les dangers
sont en effet :
de voir des objets l o il ny en na pas. Instructions et donnes ne sont
pas toujours lies.
de mal penser lorganisation des donnes ou des instructions en objets.
Un conseil donc : quand a devient trop compliqu pour vous, abandonnez
les objets.
Ce qui ne veut pas dire quun dbutant ne doit pas faire dobjets. Des petits objets
dans des cas simples sont toujours une bonne ide. Mais seule lexprience permet
de correctement organiser son programme, avec les bons objets, les bonnes fonctions,
etc. Un exemple simple : lorsquune fonction travaille sur deux types de donnes, le
dbutant voudra souvent sacharner en faire malgr tout une mthode de lun des
deux objets, et transformer :
obj 1 a ;
obj 2 b ;
i nt i =f ( a , b ) ; / / f ( ) a p p l i q u e a e t b
en :
obj 1 a ;
obj 2 b ;
i nt i =a . f ( b ) ; / / mt hode f ( ) de a a p p l i q u e b
/ / Estc e b i e n l a c ho s e f a i r e ????
Seuls un peu de recul et dexprience permettent de rester simple quand il le faut. Le
premier code tait le plus logique : la fonction f () na souvent rien faire chez a, ni
chez b.
9.2 Exemple simple
On laura compris dans les exemples prcdents, les mthodes des objets sont consi-
dres 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 fonc-
tions membres. Voici ce que cela donne en C++ :
s t r uc t obj {
i nt x ; / / champ x
i nt f ( ) ; / / mt hode f ( )
i nt g( i nt y ) ; / / mt hode g ( )
} ;
. . .
i nt main ( ) {
obj a ;
a . x=3;
i nt i =a . f ( ) ;
i nt j =a . g ( 2 ) ;
. . .
118
9. Premiers objets 9.3. Visibilit
Il y a juste un dtail, mais dimportance : la dnition de la structure obj ci-dessus ne
fait que dclarer les mthodes. Elles ne sont dnies nulle part dans le code prcdent.
Pour les dnir, on fait comme pour les fonctions habituelles, sauf que
pour permettre plusieurs objets davoir les mmes noms de mthodes, on
prxe leur dnition par le nom de lobjet suivi de ::
a
.
a. Ce mcanisme existe aussi pour les fonctions usuelles. Ce sont les espaces de nom, que
nous avons rencontrs et contourns immdiatement avec using namespace std pour ne
pas avoir crire std::cout ...
Voici comment cela scrit :
s t r uc t obj 1 {
i nt x ; / / champ x
i nt f ( ) ; / / mt hode f ( ) ( d c l a r a t i o n )
i nt g( i nt y ) ; / / mt hode g ( ) ( d c l a r a t i o n )
} ;
s t r uc t obj 2 {
double x ; / / champ x
double f ( ) ; / / mt hode f ( ) ( d c l a r a t i o n )
} ;
. . .
i nt obj 1 : : f ( ) { / / mt hode f ( ) de o b j 1 ( d f i n i t i o n )
. . .
ret urn . . .
}
i nt obj 1 : : g( i nt y) { / / mt hode g ( ) de o b j 1 ( d f i n i t i o n )
. . .
ret urn . . .
}
double obj 2 : : f ( ) { / / mt hode f ( ) de o b j 2 ( d f i n i t i o n )
. . .
ret urn . . .
}
. . .
i nt main ( ) {
obj 1 a ;
obj 2 b ;
a . x=3; / / l e champ x de a e s t i nt
b . x =3. 5; / / c e l u i de b e s t do ubl e
i nt i =a . f ( ) ; / / mt hode f ( ) de a ( donc o b j 1 : : f ( ) )
i nt j =a . g ( 2 ) ; / / mt hode g ( ) de a ( donc o b j 1 : : g ( ) )
double y=b . f ( ) ; / / mt hode f ( ) de b ( donc o b j 2 : : f ( ) )
. . .
9.3 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
119
9.4. Exemple des matrices 9. Premiers objets
utiliser toutes les variables et fonctions de cet espace sans prciser lespace en question.
Ainsi, ceux qui ont programm cout et endl ont dni lespace std puis se sont "placs
lintrieur" de cet espace pour programmer sans avoir mettre std:: partout devant
cout, cin, endl et les autres... Cest suivant cette mme logique, que
dans ses mthodes, un objet accde directement ses champs et ses autres
mthodes, cest--dire sans rien mettre devant
a
!
a. Vous verrez peut-tre parfois traner le mot cl this qui est utile certains moment en
C++ et que les programmeurs venant de Java mettent partout en se trompant dailleurs sur
son type. Vous nen naurez en gnral pas besoin.
Par exemple, la fonction obj1::f() ci-dessus pourrait scrire :
1 i nt obj 1 : : f ( ) { / / mt hode f ( ) de o b j 1 ( d f i n i t i o n )
2 i nt i =g ( 3 ) ; / / mt hode g ( ) de l o b j e t dont l a mt hode f ( ) e s t
3 / / en t r a i n de s e x c ut e r
4 i nt j =x+i ; / / champ x de l o b j e t dont l a mt hode f ( ) e s t
5 / / en t r a i n de s e x c ut e r
6 ret urn j ;
7 }
8 . . .
9 i nt main ( ) {
10 obj 1 a1 , a2 ;
11 i nt i 1=a1 . f ( ) ; / / Cet a ppe l va u t i l i s e r a1 . g ( ) l i g ne 2
12 / / e t a1 . x l i g n e 4
13 i nt i 2=a2 . f ( ) ; / / Cet a ppe l va u t i l i s e r l i g ne 2 a2 . g ( )
14 / / e t a2 . x l i g n e 4
Il est dailleurs normal quun objet accde simplement ses champs depuis ses m-
thodes, car
si un objet nutilise pas ses champs dans une mthode, cest probablement
quon est en train de ranger dans cet objet une fonction qui na rien voir
avec lui (cf abus mentionn plus haut)
9.4 Exemple des matrices
En programmation, un exemple de source vaut mieux quun long discours. Si jus-
quici vous naviguiez dans le vague, les choses devraient maintenant sclaircir ! Voil
donc ce que devient notre exemple du chapitre 8 avec des objets :
# i ncl ude <i ostream>
# i ncl ude <s t r i ng >
usi ng namespace st d ;
/ / ==================================================
/ / f o n c t i o n s sur l e s ma t r i c e s
/ / p o ur r a i e nt e t r e dans un ma t r i c e . h e t ma t r i c e . cpp
/ / ========= d e c l a r a t i o n s ( dans l e . h )
s t r uc t Mat ri ce {
120
9. Premiers objets 9.4. Exemple des matrices
i nt m, n ;
double t ;
void cr ee ( i nt m1, i nt n1 ) ;
void de t r ui t ( ) ;
double get ( i nt i , i nt j ) ;
void s et ( i nt i , i nt j , double x ) ;
void a f f i c he ( s t r i ng s ) ;
} ;
Mat ri ce operat or ( Mat ri ce A, Mat ri ce B ) ;
/ / ========= d f i n i t i o n s ( dans l e . cpp )
void Mat ri ce : : cr ee ( i nt m1, i nt n1 ) {
/ / Not ez que l e s pa r a me t r e s ne s a p p e l l e n t pl us m e t n
/ / pour ne pas m l ange r ave c l e s champs !
m=m1;
n=n1 ;
t =new double [mn ] ;
}
void Mat ri ce : : de t r ui t ( ) {
del et e [ ] t ;
}
double Mat ri ce : : get ( i nt i , i nt j ) {
ret urn t [ i +m j ] ;
}
void Mat ri ce : : s et ( i nt i , i nt j , double x ) {
t [ i +m j ]=x ;
}
void Mat ri ce : : a f f i c he ( s t r i ng s ) {
cout << s << " =" << endl ;
f or ( i nt i =0; i <m; i ++) {
f or ( i nt j =0; j <n ; j ++)
cout << get ( i , j ) << " " ;
cout << endl ;
}
}
Mat ri ce operat or ( Mat ri ce A, Mat ri ce B) {
i f (A. n! =B.m) {
cout << " Erreur ! " << endl ;
e x i t ( 1 ) ;
}
Mat ri ce C;
C. cr ee (A. m, B. n ) ;
f or ( i nt i =0; i <A.m; i ++)
f or ( i nt j =0; j <B. n ; j ++) {
121
9.5. Cas des oprateurs 9. Premiers objets
/ / Ci j =Ai0 B0j +Ai1 B1j + . . .
C. s et ( i , j , 0 ) ;
f or ( i nt k=0; k<A. n ; k++)
C. s et ( i , j ,
C. get ( i , j )+A. get ( i , k) B. get ( k , j ) ) ;
}
ret urn C;
}
/ / ==================== main ===========
i nt main ( )
{
Mat ri ce A;
A. cr ee ( 2 , 3 ) ;
f or ( i nt i =0; i <2; i ++)
f or ( i nt j =0; j <3; j ++)
A. s et ( i , j , i +j ) ;
A. a f f i c he ( "A" ) ;
Mat ri ce B;
B. cr ee ( 3 , 5 ) ;
f or ( i nt i =0; i <3; i ++)
f or ( i nt j =0; j <5; j ++)
B. s et ( i , j , i +j ) ;
B. a f f i c he ( "B" ) ;
Mat ri ce C=AB;
C. a f f i c he ( "C" ) ;
C. de t r ui t ( ) ;
B. de t r ui t ( ) ;
A. de t r ui t ( ) ;
ret urn 0;
}
9.5 Cas des oprateurs
Il est un peu dommage que loprateur ne soit pas dans lobjet Matrice. Pour y
remdier, on adopte la convention suivante :
Soit A un objet. Sil possde une mthode operatorop(objB B), alors
AopB appellera cette mthode pour tout B de type objB.
En clair, le programme :
s t r uc t objA {
. . .
} ;
s t r uc t obj B {
. . .
} ;
122
9. Premiers objets 9.5. Cas des oprateurs
i nt operat or +( objA A, obj B B) {
. . .
}
. . .
i nt main ( ) {
objA A;
obj B B;
i nt 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 uc t objA {
. . .
i nt operat or +( obj B B ) ;
} ;
s t r uc t obj B {
. . .
} ;
i nt objA : : operat or +( obj B B) {
. . .
}
. . .
i nt main ( ) {
objA A;
obj B B;
i nt i =A+B; / / a p p e l l e mai nt e nant A. o p e r a t o r +(B)
. . .
ce qui pour nos matrices donne :
s t r uc t Mat ri ce {
. . .
Mat ri ce operat or ( Mat ri ce B ) ;
} ;
. . .
/ / AB a p p e l l e A. o p e r a t o r ( B) donc t o us
/ / 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 me nt
/ / c o nc e r ne nt c e qui t a i t p r f i x prcdemment par A.
Mat ri ce Mat ri ce : : operat or ( Mat ri ce B) {
/ / On e s t dans l o b j e t A du AB a p p e l
i f ( n! =B.m) { / / Le n de A
cout << " Erreur ! " << endl ;
e x i t ( 1 ) ;
}
Mat ri ce C;
C. cr ee (m, B. n ) ;
f or ( i nt i =0; i <m; i ++)
f or ( i nt j =0; j <B. n ; j ++) {
/ / Ci j =Ai0 B0j +Ai1 B1j + . . .
C. s et ( i , j , 0 ) ;
123
9.6. Interface 9. Premiers objets
f or ( i nt k=0; k<n ; k++)
/ / ge t ( i , j ) s e r a c e l u i de A
C. s et ( i , j ,
C. get ( i , j )+ get ( i , k) B. get ( k , j ) ) ;
}
ret urn C;
}
Notez aussi que largument de loprateur na en fait pas besoin dtre un objet.
Ainsi pour crire le produit B=A2, il sufra de crer la mthode :
Mat ri ce Mat ri ce : : operat or ( double lambda ) {
. . .
}
. . .
B=A2; / / Appe l l e A. o p e r a t o r ( 2)
Par contre, pour crire B=2A, on ne pourra pas crer :
Mat ri ce double : : operat or ( Mat ri ce A) / / IMPOSSIBLE
/ / do ubl e n e s t pas un o b j e t !
car cela reviendrait dnir une mthode pour le type double, qui nest pas un ob-
jet
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 :
Mat ri ce operat or ( double lambda , Mat ri ce A) {
ret urn Alambda ; / / d f i n i prcdemment , donc r i e n reprogrammer !
}
. . .
B=2A; / / a p p e l l e o p e r a t o r ( 2 , A) qui a p p e l l e son t o ur
/ / A. o p e r a t o r ( 2)
Nous verrons au chapitre suivant dautres oprateurs utiles dans le cas des objets...
9.6 Interface
Si on regarde bien le main() de notre exemple de matrice, on saperoit quil nutilise
plus les champs des Matrice mais seulement leurs mthodes. En fait, seule la partie
s t r uc t Mat ri ce {
void cr ee ( i nt m1, i nt n1 ) ;
void de t r ui t ( ) ;
double get ( i nt i , i nt j ) ;
void s et ( i nt i , i nt j , double x ) ;
void a f f i c he ( s t r i ng s ) ;
Mat ri ce operat or ( Mat ri ce B ) ;
} ;
4. et de toute faon nappartient pas au programmeur !
124
9. Premiers objets 9.7. Protection
intresse lutilisateur. Que les dimensions soient dans des champs int met 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 luti-
lisateur marcheront toujours ! Cest le concept mme dune interface :
Le concepteur et lutilisateur des objets se mettent daccord sur les m-
thodes 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 chier den-tte de lobjet est le seul qui intresse lutilisa-
teur. Cest lui qui prcise linterface, sans rentrer dans les dtails dimpl-
mentation. Bref, relies uniquement par linterface, utilisation et implmen-
tation deviennent indpendantes
b
.
a. Il se trouve en gnral face au difcile problme du choix de limplmentation : certaines
faons de stocker les donnes peuvent rendre efcaces 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 implmen-
tation sans gner lutilisateur, lutilisateur peut changer pour une implmentation concurrente
sans avoir retoucher son programme.
9.7 Protection
9.7.1 Principe
Tout cela est bien beau, mais les dtails dimplmentation ne sont pas entirement
cachs : la dnition de la structure dans le chier 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 :
Mat ri ce A;
A. cr ee ( 3 , 2 ) ;
A.m=4; / / Ai e ! Le s a c c s vont t r e f aux !
ou tout simplement de prfrer ne pas sembter en remplaant
f or ( i nt i =0; i <3; i ++)
f or ( i nt j =0; j <2; j ++)
A. s et ( i , j , 0 ) ;
par
5. Et il en existe ! Par exemple pour stocker efcacement des matrices creuses, cest--dire celles dont
la plupart des lments sont nuls. Ou bien, en utilisant des objets implmentant dj des tableaux de fa-
on sre et efcace, comme il en existe dj en C++ standard ou dans des bibliothques complmentaires
disponibles sur le WEB. Etc, etc.
125
9.7. Protection 9. Premiers objets
f or ( i nt i =0; i <6; i ++)
A. t [ i ] =0; / / Horreur ! Et s i on i mpl me nt e aut r e me nt ?
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 de-
viennent 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 dnition de lobjet pour dbu-
ter 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 {
i nt x , y ;
void a_moi ( ) ;
publ i c :
i nt z ;
void pour_tous ( ) ;
void une_autre ( obj A) ;
} ;
void obj : : a_moi ( ) {
x = . . ; / / OK
. . = y ; / / OK
z = . . ; / / OK
}
void obj : : pour_tous ( ) {
x = . . ; / / OK
a_moi ( ) ; / / OK
}
void obj : : une_autre ( obj A) {
x=A. x ; / / OK
A. a_moi ( ) ; / / OK
}
. . .
i nt main ( ) {
obj A, B;
A. x = . . ; / / NON!
A. z = . . ; / / OK
A. a_moi ( ) ; / / NON!
A. pour_tous ( ) ; / / OK
A. une_autre ( B ) ; / / OK
126
9. Premiers objets 9.7. Protection
Dans le cas de nos matrices, que nous avions dj bien programmes, il suft de les
dnir comme suit :
c l a s s Mat ri ce {
i nt m, n ;
double t ;
publ i c :
void cr ee ( i nt m1, i nt n1 ) ;
void de t r ui t ( ) ;
double get ( i nt i , i nt j ) ;
void s et ( i nt i , i nt j , double x ) ;
void a f f i c he ( s t r i ng s ) ;
Mat ri ce operat or ( Mat ri ce B ) ;
} ;
pour empcher une utilisation dpendante de limplmentation.
9.7.2 Structures vs Classes
Notez que, nalement, 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 pri-
vs, lutilisateur na plus la possibilit de retrouver les dimensions dune matrice. On
rajoutera donc deux accesseurs en lecture vers ces dimensions :
i nt Mat ri ce : : nbLin ( ) {
ret urn m;
}
i nt Mat ri ce : : nbCol ( ) {
ret urn n ;
}
. . .
i nt main ( ) {
. . .
f or ( i nt i =0; i <A. nbLin ( ) ; i ++)
f or ( i nt j =0; j <A. nbCol ( ) ; j ++)
A. s et ( i , j , 0 ) ;
. . .
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] !
6. sans compter quils les dclarent souvent comme en C avec dinutiles typedef. Mais bon, ceci ne
devrait pas vous concerner !
127
9.8. TP 9. Premiers objets
FIGURE 9.1 Fractales
9.8 TP
Vous devriez maintenant pouvoir faire le TP en A.8 qui dessine quelques courbes
fractales (gure 9.1) en illustrant le concept dobjet..
9.9 Fiche de rfrence
Fiche de rfrence (1/4)
Boucles
do {
...
} while (!ok);
int i=1;
while (i<=100) {
...
i=i+1;
}
for (int i=1;i<=10;i++)
...
for (int i=1,j=10;j>i;
i=i+2,j=j-3)
...
for (int i=...)
for (int j=...) {
//saute le cas i==j
if (i==j)
continue;
...
}
Clavier
Build : F7
Debug : F5
Step over : F10
Step inside : F11
Indent : Ctrl+K,Ctrl+F
Add New It. : Ctrl+Maj+A
Add Exist. It. : Alt+Maj+A
Step out : Maj+F11
Run to curs. : Click droit
Compltion : Alt+
Gest. tches : Ctrl+Maj+Ech
128
9. Premiers objets 9.9. Fiche de rfrence
Fiche de rfrence (2/4)
Variables
Dnition :
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=254;
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);
int i,j;
double x=double(i)/j;
Pile/Tas
Fonctions
Dnition :
int plus(int a,int b) {
int c=a+b;
return c;
}
void affiche(int a) {
cout << a << endl;
}
Dclaration :
int plus(int a,int b);
Retour :
int signe(double x) {
if (x<0)
return -1;
if (x>0)
return 1;
return 0;
}
void afficher(int x,
int y) {
if (x<0 || y<0)
return;
if (x>=w || y>=h)
return;
DrawPoint(x,y,RED);
}
Appel :
int f(int a) { ... }
int g() { ... }
...
int i=f(2),j=g();
Rfrences :
void swap(int& a,
int& b){
int tmp=a;
a=b;b=tmp;
}
...
int x=3,y=2;
swap(x,y);
Surcharge :
int hasard(int n);
int hasard(int a,
int b);
double hasard();
Oprateurs :
vect operator+(
vect A,vect B) {
...
}
...
vect C=A+B;
Pile des appels
Itratif/Rcursif
Tableaux
Dnition :
double x[10],y[10];
for(int i=0;i<10;i++)
y[i]=2
*
x[i];
const int n=5;
int i[n],j[2
*
n]; //OK
Initialisation :
int t[4]={1,2,3,4};
string s[2]={"ab","c"};
Affectation :
int s[3]={1,2,3},t[3];
for (int i=0;i<3;i++)
t[i]=s[i];
En paramtre :
void init(int t[4]) {
for(int i=0;i<4;i++)
t[i]=0;
}
void init(int t[],
int n) {
for(int i=0;i<n;i++)
t[i]=0;
}
Taille variable :
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];
A[i+2
*
j]=...;
Taille variable (suite) :
int
*
t,
*
s,n;
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 en-
tirement public (cf objets !)
129
9.9. Fiche de rfrence 9. Premiers objets
Fiche de rfrence (3/4)
Objets
struct obj {
int x; // champ
int f(); // mthode
int g(int y);
};
int obj::f() {
int i=g(3); // mon g
int j=x+i; // mon x
return j;
}
...
int main() {
obj a;
a.x=3;
int i=a.f();
class obj {
int x,y;
void a_moi();
public:
int z;
void pour_tous();
void un_autre(obj A);
};
void obj::a_moi() {
x=..; // OK
..=y; // OK
z=..; // OK
}
void obj::pour_tous() {
x=..; // OK
a_moi(); // OK
}
void une_autre(obj A) {
x=A.x; // OK
A.a_moi(); // OK
}
...
int main() {
obj A,B;
A.x=..; //NON
A.z=..; //OK
A.a_moi(); //NON
A.pour_tous(); //OK
A.une_autre(B); //OK
class obj {
obj operator+(obj B);
};
...
int main() {
obj A,B,C;
C=A+B;
// C=A.operator+(B)
Compilation spare
#include "vect.h", y
compris dans vect.cpp
Fonctions : dclarations dans
le .h, dnitions dans le .cpp
Types : dnitions dans le .h
Ne dclarer dans le .h que les
fonctions utiles.
#pragma once au dbut du
chier.
Ne pas trop dcouper...
Tests
Comparaison :
== != < > <= >=
Ngation : !
Combinaisons : && ||
if (i==0)
j=1;
if (i==0)
j=1;
else
j=2;
if (i==0) {
j=1;
k=2;
}
bool t=(i==0);
if (t)
j=1;
switch (i) {
case 1:
...;
...;
break;
case 2:
case 3:
...;
break;
default:
...;
}
Entres/Sorties
#include <iostream>
using namespace std;
...
cout <<"I=" <<i <<endl;
cin >> i >> j;
Erreurs frquentes
Pas de dnition de fonction
dans une fonction!
int q=r=4; // NON!
if (i=2) // NON!
if i==2 // NON!
if (i==2) then // NON!
for (int i=0,i<100,i++)
// NON!
int f() {...}
...
int i=f; // NON!
double x=1/3; // NON!
int i,j;
double x;
x=i/j; // NON!
x=double(i/j); //NON!
double x[10],y[10];
for (int i=1;i<=10;i++)
y[i]=2
*
x[i]; //NON
int n=5;
int t[n]; // NON
int f()[4] { // NON!
int t[4];
...
return t; // NON!
}
int t[4]; t=f();
int s[3]={1,2,3},t[3];
t=s; // NON!
int t[2];
t={1,2}; // NON!
struct Point {
double x,y;
} // NON!
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!
130
9. Premiers objets 9.9. Fiche de rfrence
Fiche de rfrence (4/4)
Divers
i++;
i--;
i-=2;
j+=3;
j=i%n; // Modulo
#include <cstdlib>
...
i=rand()%n;
x=rand()/
double(RAND_MAX);
#include <ctime>
...
srand((unsigned int)
time(0));
#include <cmath>
double sqrt(double x);
double cos(double x);
double sin(double x);
double acos(double x);
#include <string>
using namespace std;
string s="hop";
char c=s[0];
int l=s.size();
#include <ctime>
s=double(clock())
/CLOCKS_PER_SEC;
#define _USE_MATH_DEFINES
#include <cmath>
double pi=M_PI;
Imagine++
Voir documentation...
Conseils
Travailler en local
Imagine++ Project
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 chiers spars.
Le .h doit sufre lutilisa-
teur (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 ob-
jets !
Penser interface / implmen-
tation / utilisation.
131
10. Constructeurs et Destructeurs
Chapitre 10
Constructeurs et Destructeurs
Dans ce long chapitre, nous allons voir comment le C++ offre la possibilit dintervenir
sur ce qui se passe la naissance et la mort dun objet. Ce mcanisme essentiel repose sur la
notion de constructeur et de destructeur. Ces notions sont trs utiles, mme pour le dbutant
qui devra au moins connatre leur forme la plus simple. Nous poursuivrons par un aspect bien
pratique du C++, tant pour lefcacit des programmes que pour la dcouverte de bugs la
compilation : une autre utilisation du const. Enn, pour les plus avancs, nous expliquerons
aussi comment les problmes de gestion du tas peuvent tre ainsi automatiss.

10.1 Le problme
Avec lapparition des objets, nous avons transform :
s t r uc t poi nt {
i nt x , y ;
} ;
. . .
poi nt a ;
a . x=2; a . y=3;
i =a . x ; j =a . y ;
en :
c l a s s poi nt {
i nt x , y ;
publ i c :
void get ( i nt&X, i nt&Y) ;
void s et ( i nt X, i nt Y) ;
} ;
. . .
poi nt a ;
a . s et ( 2 , 3 ) ;
a . get ( i , j ) ;
Consquence :
poi nt a ={ 2 , 3 } ;
10.2. La solution 10. Constructeurs et Destructeurs
est maintenant impossible. On ne peut remplir les champs privs dun objet, mme
linitialisation, car cela permettrait daccder en criture une partie prive
1
!
10.2 La solution
La solution est la notion de constructeur :
c l a s s poi nt {
i nt x , y ;
publ i c :
poi nt ( i nt X, i nt Y) ;
} ;
poi nt : : poi nt ( i nt X, i nt Y) {
x=X;
y=Y;
}
. . .
poi nt a ( 2 , 3 ) ;
Un constructeur est une mthode dont le nom est le nom de la classe elle-
mme. Il ne retourne rien mais son type de retour nest pas void : il na pas
de type de retour. Le constructeur est appel la cration de lobjet et ses
paramtres sont passs avec la syntaxe ci-dessus. Il est impossible dappeler
un constructeur sur un objet dj cr
a
.
a. Ce qui explique quil nest pas besoin de lui prciser un type de retour.
Ici, cest le constructeur point :: point(int X,int Y) qui est dni. Notez bien quil
est impossible dappeler un constructeur sur un objet dj contruit :
poi nt a ( 1 , 2 ) ; / / OK! Val e ur s i n i t i a l e s
/ / On ne f a i t pas comme a pour c hange r l e s champs de a .
a . poi nt ( 3 , 4 ) ; / / ERREUR!
/ / Mais p l ut t comme a .
a . s et ( 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 {
publ i c :
obj ( ) ;
1. En ralit, il y a une autre raison, plus profonde et trop difcile expliquer ici, qui fait quen
gnral, ds quon programme des objets, cette faon dinitialiser devient impossible.
134
10. Constructeurs et Destructeurs 10.3. Cas gnral
} ;
obj : : obj ( ) {
cout << " hel l o " << endl ;
}
. . .
obj a ; / / a p p e l l e l e c o ns t r uc t e ur par d f a ut
afche "hello".
Le constructeur vide obj::obj() est appel chaque fois quon construit
un objet sans prciser de paramtre. Font exception les paramtres des fonc-
tions 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 ncl ude <i ostream>
usi ng namespace st d ;
c l a s s obj {
publ i c :
obj ( ) ;
} ;
obj : : obj ( ) {
cout << " obj " ;
}
void f ( obj d) {
}
obj g ( ) {
obj e ;
cout << 6 << " " ;
ret urn e ;
}
i nt main ( )
{
cout << 0 << " " ;
obj a ;
cout << 1 << " " ;
f or ( i nt i =2; i <=4; i ++) {
obj b ;
cout << i << " " ;
}
f ( a ) ;
cout << 5 << " " ;
a=g ( ) ;
135
10.3. Cas gnral 10. Constructeurs et Destructeurs
ret urn 0;
}
afche :
0 obj 1 obj 2 obj 3 obj 4 5 obj 6
Bien reprer les deux objets non construits avec obj :: obj () : le paramtre d de f () , copie
de a, et la valeur de retour de g(), copie de e.
10.3.2 Plusieurs constructeurs
Un objet peut avoir plusieurs constructeurs.
c l a s s poi nt {
i nt x , y ;
publ i c :
poi nt ( i nt X, i nt Y) ;
poi nt ( i nt V) ;
} ;
poi nt : : poi nt ( i nt X, i nt Y) {
x=X;
y=Y;
}
poi nt : : poi nt ( i nt V) {
x=y=V;
}
. . .
poi nt a ( 2 , 3 ) ; / / c o n s t r u i t ave c po i nt (X, Y)
poi nt b ( 4 ) ; / / c o n s t r u i t ave c po i nt (V)
Il faut cependant retenir la chose suivante :
Si on ne dnit aucun constructeur, tout se passe comme sil ny avait quun
constructeur vide ne faisant rien. Mais attention : ds quon dnit soi-mme
un constructeur, le constructeur vide nexiste plus, sauf si on le rednit soi-
mme.
Par exemple, le programme :
c l a s s poi nt {
i nt x , y ;
} ;
. . .
poi nt a ;
a . s et ( 2 , 3 ) ;
poi nt b ; / / OK
devient, avec un constructeur, un programme qui ne se compile plus :
c l a s s poi nt {
i nt x , y ;
publ i c :
poi nt ( i nt X, i nt Y) ;
136
10. Constructeurs et Destructeurs 10.3. Cas gnral
} ;
poi nt : : poi nt ( i nt X, i nt Y) {
x=X;
y=Y;
}
. . .
poi nt a ( 2 , 3 ) ; / / c o n s t r u i t ave c po i nt (X, Y)
poi nt b ; / / ERREUR! po i nt ( ) n e x i s t e pl us
et il faut alors rajouter un constructeur vide, mme sil ne fait rien :
c l a s s poi nt {
i nt x , y ;
publ i c :
poi nt ( ) ;
poi nt ( i nt X, i nt Y) ;
} ;
poi nt : : poi nt ( ) {
}
poi nt : : poi nt ( i nt X, i nt Y) {
x=X;
y=Y;
}
. . .
poi nt a ( 2 , 3 ) ; / / c o n s t r u i t ave c po i nt (X, Y)
poi nt b ; / / OK! c o n s t r u i t ave c po i nt ( )
10.3.3 Tableaux dobjets
Il nest pas possible de spcier globalement quel constructeur est appel pour les
lments dun tableau. Cest toujours le constructeur vide qui est appel...
poi nt t [ 3 ] ; / / Co ns t r ui t 3 f o i s ave c l e c o ns t r uc t e ur vi de
/ / sur chacun de s l me nt s du t a b l e a u
poi nt s=new poi nt [ n ] ; / / Idem , n f o i s
poi nt u=new poi nt ( 1 , 2 ) [ n ] ; / / ERREUR e t HORREUR!
/ / Un e s s a i de c o n s t r u i r e l e s u[ i ]
/ / ave c po i nt ( 1 , 2 ) , qui n e x i s t e pas
Il faudra donc crire :
poi nt u=new poi nt [ n ] ;
f or ( i nt i =0; i <n ; i ++)
u[ i ] . s et ( 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 :
poi nt t [ 3] ={ poi nt ( 1 , 2 ) , poi nt ( 2 , 3 ) , poi nt ( 3 , 4 ) } ;
ce qui nest videmment pas faisable pour un tableau de taille variable.
137
10.4. Objets temporaires 10. Constructeurs et Destructeurs
10.4 Objets temporaires
On peut, en appelant soi-mme un constructeur
a
, construire un objet sans
quil soit rang dans une variable. En fait il sagit dun objet temporaire
sans nom de variable et qui meurt le plus tt possible.
a. Attention, nous avions dj dit quon ne pouvait pas appeler un constructeur dun objet
dj construit. Ici, cest autre chose : on appelle un constructeur sans prciser dobjet !
Ainsi, le programme :
void f ( poi nt p) {
. . .
}
poi nt g ( ) {
poi nt e ( 1 , 2 ) ; / / pour l e r e t o ur ne r
ret urn e ;
}
. . .
poi nt a ( 3 , 4 ) ; / / uni quement pour po uvo i r a p p e l e r f ( )
f ( a ) ;
poi nt b ;
b=g ( ) ;
poi nt c ( 5 , 6 ) ; / / on p o ur r a i t a v o i r e nvi e de f a i r e
b=c ; / / a pour me 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 void f ( poi nt p) {
2 . . .
3 }
4 poi nt g ( ) {
5 ret urn poi nt ( 1 , 2 ) ; / / r e t o ur ne d i r e c t e me nt
6 / / l o b j e t t e mp o r a i r e po i nt ( 1 , 2 )
7 }
8 . . .
9 f ( poi nt ( 3 , 4 ) ) ; / / Pas s e d i r e c t e me nt l o b j . temp . po i nt ( 3 , 4 )
10 poi nt b ;
11 b=g ( ) ;
12 b=poi nt ( 5 , 6 ) ; / / a f f e c t e d i r e c t e me nt b l o b j e t
13 / / t e mp o r a i r e po i nt ( 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
poi nt p=poi nt ( 1 , 2 ) ; / / NON! ! ! ! ! ! !
mais plutt
poi nt p( 1 , 2 ) ; / / OUI !
138
10. Constructeurs et Destructeurs 10.5. TP
Lutilit de ces objets temporaires est visible sur un exemple rel :
poi nt poi nt : : operat or +( poi nt b) {
poi nt c ( x+b . x , y+b . y ) ;
ret urn c ;
}
. . .
poi nt a ( 1 , 2 ) , b ( 2 , 3 ) ;
c=a+f ( b ) ;
scrira plutt :
poi nt poi nt : : operat or +( poi nt b) {
ret urn poi nt ( x+b . x , y+b . y ) ;
}
. . .
c=poi nt ( 1 , 2) + f ( poi nt ( 2 , 3 ) ) ;
FIGURE 10.1 Jeu de Tron.
10.5 TP
Nous pouvons faire une pause et aller faire le TP que nous proposons en A.9. Il
sagit de programmer le jeu de motos de Tron (gure 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 dinefcacit. Ainsi, dans le programme suivant :
const i nt N=1000;
c l a s s vect eur {
double t [N] ;
. . .
} ;
c l a s s mat ri ce {
double t [N] [N] ;
. . .
139
10.6. Rfrences Constantes 10. Constructeurs et Destructeurs
} ;
/ / r s o ut AX=B
void sol ve ( mat ri ce A, vect eur B, vect eur& X) {
. . .
}
. . .
vect eur b , x ;
mat ri ce a ;
. . .
sol ve ( a , b , x ) ; / / r s o ut 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 modier ! Il suft donc de dnir la fonction solve()
ainsi :
void sol ve ( mat ri ce& A, vect eur& B, vect eur& X) {
. . .
pour acclrer le programme.
Cependant, cette solution nest pas sans danger. Rien ne garantit en effet que solve
ne modie 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 modis,
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 vrier
quune variable passe par rfrence nest pas modie par la fonction. Il suft de rajouter
const au bon endroit :
void sol ve ( const mat ri ce& A, const vect eur& B, vect eur& X) {
. . .
Si quelque part dans solve (ou dans les sous-fonctions appeles par solve !), la variable
A ou la variable B est modie, alors il y aura erreur de compilation. La rgle est donc :
Lorsquun paramtre obj o dune fonction est de taille importante
a
, cest
une bonne ide de le remplacer par const obj& o.
a. En ralit, le programme sen trouvera acclr pour la plupart des objets courants.
10.6.2 Mthodes constantes
Considrons le programme suivant :
void g( i nt& x ) {
cout << x << endl ;
}
140
10. Constructeurs et Destructeurs 10.6. Rfrences Constantes
void f ( const i nt& y) {
double z=y ; / / OK ne mo d i f i e pas y
g( y ) ; / / OK?
}
. . .
i nt a =1;
f ( a ) ;
La fonction f () ne modie pas son paramtre y et tout va bien. Imaginons une deuxime
version de g() :
void g( i nt& x ) {
x++;
}
Alors y serait modie dans f () cause de lappel g(). Le programme ne se compi-
lerait videmment pas... En ralit, la premire version de g() serait refuse elle aussi
car
pour savoir si une sous-fonction modie ou non un des paramtres dune
fonction, le compilateur ne se base que sur la dclaration de cette sous-
fonction et non sur sa dnition complte
a
.
a. Le C++ nessaie pas de deviner lui-mme si une fonction modie ses paramtres
puisque la logique est que le programmeur indique lui-mme avec const ce quil veut faire,
et que le compilateur vrie 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( const i nt& x ) {
cout << x << endl ;
}
void f ( const i nt& y) {
double z=y ; / / OK ne mo d i f i e pas 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 dans g ( )
}
. . .
i nt a =1;
f ( a ) ;
Avec les objets, nous avons besoin dune nouvelle notion. En effet, considrons
maintenant :
void f ( const obj& o) {
o . g ( ) ; / / OK?
}
Il faut indiquer au compilateur si la mthode g() modie ou non lobjet o. Cela se fait
avec la syntaxe suivante :
c l a s s obj {
. . .
void g ( ) const ;
141
10.7. Destructeur 10. Constructeurs et Destructeurs
. . .
} ;
void obj : : g ( ) const {
. . .
}
void f ( const obj& o) {
o . g ( ) ; / / OK! Mthode c o ns t a nt e
}
Cela nest nalement pas compliqu :
On prcise quune mthode est constante, cest--dire quelle ne modie pas
son objet, en plaant const derrire les parenthses de sa dclaration et de
sa dnition.
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 modie lobjets ?". Le compilateur
va ensuite vrier 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 modi
et il lest.)
Optimisation du programme
2
.

La n du chapitre peut tre considre comme difcile. Il est toutefois recommand de la com-
prendre, 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 optimi-
sation du compilateur.
142
10. Constructeurs et Destructeurs 10.7. Destructeur
# i ncl ude <i ostream>
usi ng namespace st d ;
c l a s s obj {
publ i c :
obj ( ) ;
~obj ( ) ;
} ;
obj : : obj ( ) {
cout << " obj " ;
}
obj : : ~ obj ( ) {
cout << " dest " ;
}
void f ( obj d) {
}
obj g ( ) {
obj e ;
cout << 6 << " " ;
ret urn e ;
}
i nt main ( )
{
cout << 0 << " " ;
obj a ;
cout << 1 << " " ;
f or ( i nt i =2; i <=4; i ++) {
obj b ;
cout << i << " " ;
}
f ( a ) ;
cout << 5 << " " ;
a=g ( ) ;
ret urn 0;
}
Il afche 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 ap-
pels au destructeur pour les objets qui sont construits par copie et pour lesquels nous
navons pas encore parl du constructeur...
143
10.8. Destructeurs et tableaux 10. Constructeurs et Destructeurs
10.8 Destructeurs et tableaux
Le destructeur est appel pour tous les lments du tableau. Ainsi,
1 i f ( a==b) {
2 obj t [ 1 0 ] ;
3 . . .
4 }
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) {
obj t =new obj [ n ] ; / / n a p p e l s o b j ( )
. . .
del et e [ ] t ; / / n a p p e l s ~o b j ( ) ;
}
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 enn ce fameux constructeur. Il na rien de mystrieux. Il sagit dun construc-
teur 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 enn complet. En rajoutant :
obj : : obj ( const obj& o) {
cout << " copy " ;
}
il afche :
144
10. Constructeurs et Destructeurs 10.10. Affectation
0 obj 1 obj 2 dest obj 3 dest obj 4 dest copy dest 5 obj 6 copy dest
dest dest
Nous avons enn 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 re-
copie tous les champs de lobjet copier dans lobjet construit.
Remarquez aussi que lorsquon dnit 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 obj : : operat or =( const obj&o) {
cout << " = " ;
}
notre programme, et il afche :
0 obj 1 obj 2 dest obj 3 dest obj 4 dest copy dest 5 obj 6 copy dest
= dest dest
On rafne 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 laffec-
tation ainsi :
obj obj : : operat or =( const obj&o) {
cout << " = " ;
ret urn 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 :
const obj& obj : : operat or =( const obj&o) {
cout << " = " ;
ret urn o ;
}
. . .
obj a , b , c ;
a=b=c ; / / OK c a r a =( b=c )
145
10.11. Objets avec allocation dynamique 10. Constructeurs et Destructeurs
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 dnit un autre
constructeur, et quil est donc en gnral indispensable de reprogrammer, mme pour repro-
duire son comportement par dfaut
10.11 Objets avec allocation dynamique
Tout ce que nous venons de voir est un peu abstrait. Nous allons enn dcouvrir
quoi a sert. Considrons le programme suivant :
# i ncl ude <i ostream>
usi ng namespace st d ;
c l a s s vect {
i nt n ;
double t ;
publ i c :
void al l oue ( i nt N) ;
void l i be r e ( ) ;
} ;
void vect : : al l oue ( i nt N) {
n=N;
t =new double [ n ] ;
}
void vect : : l i be r e ( ) {
del et e [ ] t ;
}
i nt main ( )
{
vect v ;
v . al l oue ( 1 0 ) ;
. . .
v . l i be r e ( ) ;
ret urn 0;
}
10.11.1 Construction et destruction
Il apparat videmment que les constructeurs et les destructeurs sont l pour nous
aider :
# i ncl ude <i ostream>
146
10. Constructeurs et Destructeurs 10.11. Objets avec allocation dynamique
usi ng namespace st d ;
c l a s s vect {
i nt n ;
double t ;
publ i c :
vect ( i nt N) ;
~vect ( ) ;
} ;
vect : : vect ( i nt N) {
n=N;
t =new double [ n ] ;
}
vect : : ~ vect ( ) {
del et e [ ] t ;
}
i nt main ( )
{
vect v ( 1 0 ) ;
. . .
ret urn 0;
}
Grce aux constructeurs et au destructeur, nous pouvons enn 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 dbu-
tants. 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 :
vect : : vect ( ) {
n=0;
}
vect : : ~ vect ( ) {
147
10.11. Objets avec allocation dynamique 10. Constructeurs et Destructeurs
i f ( n! =0)
del et e [ ] t ;
}
Des problmes compliqus
Le programme suivant ne marche pas :
i nt main ( )
{
vect v ( 1 0 ) ,w( 1 0 ) ;
w=v ;
ret urn 0;
}
Pourquoi ? Parce que laffectation par dfaut recopie les champs de v dans ceux de
w. Du coup, v et w se retrouvent avec les mmes champs t ! Non seulement ils iront
utiliser les mmes valeurs, do certainement des rsultats faux, mais en plus une mme
zone du tas va tre dsalloue deux fois, tandis quune autre ne le sera pas
3
!
Il faut alors reprogrammer laffectation, ce qui nest pas trivial. On dcide en gn-
ral de rallouer la mmoire et de recopier les lments du tableau :
const vect& vect : : operat or =( const vect& v) {
i f ( n! =0)
del et e [ ] t ; / / On s e d e s a l l o u e s i n e c e s s a i r e
n=v . n ;
i f ( n! =0) {
t =new double [ n ] ; / / Re a l l o c a t i o n e t r e c o p i e
f or ( i nt i =0; i <n ; i ++)
t [ i ]=v . t [ i ] ;
}
ret urn v ;
}
Cette version ne marche dailleurs pas si on fait v=v car alors v est dsallou avant
dtre recopi dans lui-mme, ce qui provoque une lecture dans une zone qui vient
dtre dsalloue
4
.
10.11.3 Solution!
Des problmes identiques se posent pour le constructeur de copie... Ceci dit, en
factorisant le travail faire dans quelques petites fonctions prives, la solution nest
pas si complique. Nous vous la soumettons en bloc. Elle peut mme servir de schma
pour la plupart des objets similaires
5
:
3. Ne pas dsallouer provoque videmment des fuites de mmoire. Dsallouer deux fois provoque
dans certains cas une erreur. Cest le cas en mode Debug sous Visual, ce qui aide reprer les bugs !
4. Il suft de rajouter un test (&v==this) pour reprer ce cas, ce qui nous dpasse un petit peu...
5. Ceci nest que le premier pas vers une srie de faon de grer les objets. Doit-on recopier les ta-
bleaux ? Les partager en faisant en sorte que le dernier utilisateur soit charg de dsallouer ? Etc, etc.
148
10. Constructeurs et Destructeurs 10.11. Objets avec allocation dynamique
1 # i ncl ude <i ostream>
2 usi ng namespace st d ;
3
4 c l a s s vect {
5 / / champs
6 i nt n ;
7 double t ;
8 / / f o n c t i o n s p r i v e s
9 void a l l oc ( i nt N) ;
10 void k i l l ( ) ;
11 void copy ( const vect& v ) ;
12 publ i c :
13 / / c o ns t r uc t e ur s " o b l i g a t o i r e s "
14 vect ( ) ;
15 vect ( const vect& v ) ;
16 / / d e s t r uc t e ur
17 ~vect ( ) ;
18 / / a f f e c t a t i o n
19 const vect& operat or =( const vect& v ) ;
20 / / c o ns t r uc t e ur s s up p l me nt a i r e s
21 vect ( i nt N) ;
22 } ;
23
24 void vect : : a l l oc ( i nt N) {
25 n=N;
26 i f ( n! =0)
27 t =new double [ n ] ;
28 }
29
30 void vect : : k i l l ( ) {
31 i f ( n! =0)
32 del et e [ ] t ;
33 }
34
35 void vect : : copy ( const vect& v) {
36 a l l oc ( v . n ) ;
37 f or ( i nt i =0; i <n ; i ++) / / OK mme s i n==0
38 t [ i ]=v . t [ i ] ;
39 }
40
41 vect : : vect ( ) {
42 a l l oc ( 0 ) ;
43 }
44
45 vect : : vect ( const vect& v) {
46 copy ( v ) ;
47 }
48
49 vect : : ~ vect ( ) {
149
10.11. Objets avec allocation dynamique 10. Constructeurs et Destructeurs
50 k i l l ( ) ;
51 }
52
53 const vect& vect : : operat or =( const vect& v) {
54 i f ( t hi s !=&v) {
55 k i l l ( ) ;
56 copy ( v ) ;
57 }
58 ret urn v ;
59 }
60
61 vect : : vect ( i nt N) {
62 a l l oc (N) ;
63 }
64
65 / / Pour t e s t e r c o ns t r uc t e ur de c o p i e
66 vect f ( vect a ) {
67 ret urn a ;
68 }
69 / / Pour t e s t e r l e r e s t e
70 i nt main ( )
71 {
72 vect a , b ( 1 0 ) , c ( 1 2 ) , d;
73 a=b ;
74 a=a ;
75 a=c ;
76 a=d;
77 a=f ( a ) ;
78 b=f ( b ) ;
79 ret urn 0;
80 }
150
10. Constructeurs et Destructeurs 10.12. Fiche de rfrence
10.12 Fiche de rfrence
Fiche de rfrence (1/3)
Boucles
do {
...
} while (!ok);
int i=1;
while (i<=100) {
...
i=i+1;
}
for (int i=1;i<=10;i++)
...
for (int i=1,j=10;j>i;
i=i+2,j=j-3)
...
for (int i=...)
for (int j=...) {
//saute le cas i==j
if (i==j)
continue;
...
}
Clavier
Build : F7
Debug : F5
Step over : F10
Step inside : F11
Indent : Ctrl+K,Ctrl+F
Add New It. : Ctrl+Maj+A
Add Exist. It. : Alt+Maj+A
Step out : Maj+F11
Run to curs. : Click droit
Compltion : Alt+
Gest. tches : Ctrl+Maj+Ech
Structures
struct Point {
double x,y;
Color c;
};
...
Point a;
a.x=2.3; a.y=3.4;
a.c=Red;
Point b={1,2.5,Blue};
Une structure est un objet en-
tirement public (cf objets !)
Variables
Dnition :
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=254;
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);
int i,j;
double x=double(i)/j;
Pile/Tas
Fonctions
Dnition :
int plus(int a,int b) {
int c=a+b;
return c;
}
void affiche(int a) {
cout << a << endl;
}
Dclaration :
int plus(int a,int b);
Retour :
int signe(double x) {
if (x<0)
return -1;
if (x>0)
return 1;
return 0;
}
void afficher(int x,
int y) {
if (x<0 || y<0)
return;
if (x>=w || y>=h)
return;
DrawPoint(x,y,RED);
}
Appel :
int f(int a) { ... }
int g() { ... }
...
int i=f(2),j=g();
Rfrences :
void swap(int& a,
int& b){
int tmp=a;
a=b;b=tmp;
}
...
int x=3,y=2;
swap(x,y);
Surcharge :
int hasard(int n);
int hasard(int a,
int b);
double hasard();
Oprateurs :
vect operator+(
vect A,vect B) {
...
}
...
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
}
151
10.12. Fiche de rfrence 10. Constructeurs et Destructeurs
Fiche de rfrence (2/3)
Tableaux
Dnition :
double x[10],y[10];
for(int i=0;i<10;i++)
y[i]=2
*
x[i];
const int n=5;
int i[n],j[2
*
n]; //OK
Initialisation :
int t[4]={1,2,3,4};
string s[2]={"ab","c"};
Affectation :
int s[3]={1,2,3},t[3];
for (int i=0;i<3;i++)
t[i]=s[i];
En paramtre :
void init(int t[4]) {
for(int i=0;i<4;i++)
t[i]=0;
}
void init(int t[],
int n) {
for(int i=0;i<n;i++)
t[i]=0;
}
Taille variable :
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];
A[i+2
*
j]=...;
Taille variable (suite) :
int
*
t,
*
s,n;
Objets
struct obj {
int x; // champ
int f(); // mthode
int g(int y);
};
int obj::f() {
int i=g(3); // mon g
int j=x+i; // mon x
return j;
}
...
int main() {
obj a;
a.x=3;
int i=a.f();
class obj {
int x,y;
void a_moi();
public:
int z;
void pour_tous();
void un_autre(obj A);
};
void obj::a_moi() {
x=..; // OK
..=y; // OK
z=..; // OK
}
void obj::pour_tous() {
x=..; // OK
a_moi(); // OK
}
void une_autre(obj A) {
x=A.x; // OK
A.a_moi(); // OK
}
...
int main() {
obj A,B;
A.x=..; //NON
A.z=..; //OK
A.a_moi(); //NON
A.pour_tous(); //OK
A.une_autre(B); //OK
class obj {
obj operator+(obj B);
};
...
int main() {
obj A,B,C;
C=A+B;
// C=A.operator+(B)
Mthodes constantes :
void obj::f() const{
...
}
void g(const obj& x){
x.f(); // OK
}
Constructeur :
class point {
int x,y;
public:
point(int X,int Y);
};
point::point(int X,
int Y){
x=X;
y=Y;
}
...
point a(2,3);
Constructeur vide :
obj::obj() {
...
}
...
obj a;
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() {
...
}
Constructeur de copie :
obj::obj(const obj& o){
...
}
Utilis par :
- obj b(a);
- obj b=a;
// mieux que obj b;b=a;
- paramtres des fonctions
- valeur de retour
Affectation :
obj& obj::operator=(
const obj&o){
...
return
*
this;
}
Objets avec allocation dyna-
mique automatique : cf section
10.11
Compilation spare
#include "vect.h", y
compris dans vect.cpp
Fonctions : dclarations dans
le .h, dnitions dans le .cpp
Types : dnitions dans le .h
Ne dclarer dans le .h que les
fonctions utiles.
#pragma once au dbut du
chier.
Ne pas trop dcouper...
152
10. Constructeurs et Destructeurs 10.12. Fiche de rfrence
Fiche de rfrence (3/3)
Tests
Comparaison :
== != < > <= >=
Ngation : !
Combinaisons : && ||
if (i==0)
j=1;
if (i==0)
j=1;
else
j=2;
if (i==0) {
j=1;
k=2;
}
bool t=(i==0);
if (t)
j=1;
switch (i) {
case 1:
...;
...;
break;
case 2:
case 3:
...;
break;
default:
...;
}
Entres/Sorties
#include <iostream>
using namespace std;
...
cout <<"I=" <<i <<endl;
cin >> i >> j;
Divers
i++;
i--;
i-=2;
j+=3;
j=i%n; // Modulo
#include <cstdlib>
...
i=rand()%n;
x=rand()/
double(RAND_MAX);
#include <ctime>
...
srand((unsigned int)
time(0));
#include <cmath>
double sqrt(double x);
double cos(double x);
double sin(double x);
double acos(double x);
#include <string>
using namespace std;
string s="hop";
char c=s[0];
int l=s.size();
#include <ctime>
s=double(clock())
/CLOCKS_PER_SEC;
#define _USE_MATH_DEFINES
#include <cmath>
double pi=M_PI;
Imagine++
Voir documentation...
Conseils
Travailler en local
Imagine++ Project
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 chiers spars.
Le .h doit sufre lutilisa-
teur (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 ob-
jets !
Penser interface / implmen-
tation / utilisation.
Erreurs frquentes
Pas de dnition de fonction
dans une fonction!
int q=r=4; // NON!
if (i=2) // NON!
if i==2 // NON!
if (i==2) then // NON!
for (int i=0,i<100,i++)
// NON!
int f() {...}
...
int i=f; // NON!
double x=1/3; // NON!
int i,j;
double x;
x=i/j; // NON!
x=double(i/j); //NON!
double x[10],y[10];
for (int i=1;i<=10;i++)
y[i]=2
*
x[i]; //NON
int n=5;
int t[n]; // NON
int f()[4] { // NON!
int t[4];
...
return t; // NON!
}
int t[4]; t=f();
int s[3]={1,2,3},t[3];
t=s; // NON!
int t[2];
t={1,2}; // NON!
struct Point {
double x,y;
} // NON!
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!
class point {
int x,y;
public:
...
};
...
point a={2,3}; // NON
Oublier de rednir le
constructeur vide.
point p=point(1,2);//NON
point p(1,2); // OUI
obj
*
t=new obj[n];
...
delete t; // manque []
153
10.13. Devoir crit 10. Constructeurs et Destructeurs
10.13 Devoir crit
Vous pouvez maintenant vous confronter aux devoirs crits proposs en annexe,
par exemple B.13 et B.12. Vous avez toutes les connaissances ncessaires...
154
11. En vrac...
Chapitre 11
En vrac...
Nous commenons avec ce chapitre un tour de tout ce qui est utile et mme souvent in-
dispensable et que nous navons pas encore vu : chanes de caractres, chiers, etc. Encore une
fois, nous ne verrons pas tout de manire exhaustive, mais les fonctions les plus couramment
utilises.

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


vous manque en gnral ici, cest la pratique. Aprs avoir affront les exercices tout
faits, vous ralisez que, livrs vous-mme, il vous est difcile de vous en sortir. Alors
lancez-vous ! Tentez de programmer lun des projets proposs sur la page WEB du
cours. Vous constaterez rapidement quil vous manque aussi quelques fonctions ou
types de variables usuels. Ce chapitre est l pour y remdier...
11.1 Chanes de caratres
Les chanes de caractres sont les variables stockant des suites de caractres, cest--
dire du texte. Nous les avons dj rencontres :
# i ncl ude <s t r i ng >
usi ng namespace st d ;
. . .
s t r i ng s=" hop" ;
char c=s [ 0 ] ;
i nt l =s . s i ze ( ) ;
Compltons :
1. Les chanes peuvent tres compares. Cest lordre alphabtique qui est videm-
ment utilis :
i f ( s1==s1 ) . . .
i f ( s1 ! = s2 ) . . .
i f ( s1<s2 ) . . .
i f ( s1>s2 ) . . .
i f ( s1>=s2 ) . . .
i f ( s1<=s2 ) . . .
11.1. Chanes de caratres 11. En vrac...
2. On peut chercher un caractre dans un chane :
s i z e _ t i =s . f i nd ( h ) ; / / p o s i t i o n de h dans s ?
s i z e _ t j =s . f i nd ( h , 3 ) ; / / p o s i t i o n de h dans s
/ / p a r t i r de l a
/ / p o s i t i o n 3 , en i gno r a nt 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, nd retourne 1.
3. On peut aussi chercher une sous-chane :
s i z e _ t i =s . f i nd ( " hop" ) ; / / o e s t "hop" dans s ?
s i z e _ t j =s . f i nd ( " hop" , 3 ) ; / / o e s t "hop" dans s p a r t i r de l a
/ / p o s i t i o n 3?
4. Ajouter une chane la n dune autre :
s t r i ng a=" comment " ;
s t r i ng b=" a va , l e s amis ? " ;
s t r i ng t x t =a+" " +b ;
5. Extraire une sous chane :
s t r i ng s1=" un deux t r o i s " ;
s t r i ng s2=s t r i ng ( s1 , 3 , 4 ) ; / / s ous c ha ne de l ongue ur 4
/ / commenant en s1 [ 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 :
s t r i ng s ;
ci n >> s ; / / J us qu " Ent 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
ge t l i ne ( ci n , s ) ; / / Tout e l a l i g n e j us qu " Ent r e "
On pourra ventuellement prciser un autre caractre que la n de ligne :
ge t l i ne ( ci n , s , : ) ; / / Tout j us qu un : ( non c ompr 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 ng s=" hop hop" ;
const 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 Fichiers
11.2.1 Principe
Pour lire et crire dans un chier, on procde exactement comme avec cout et cin.
On cre simplement une variable de type ofstream pour crire dans un chier, ou de
type ifstream pour lire...
1. Voici comment faire :
# i ncl ude <fstream>
usi ng namespace st d ;
. . .
of st ream f ( " hop . t x t " ) ;
f << 1 << << 2. 3 << << " s al ut " << endl ;
f . c l os e ( ) ;
i f s t r eam g( " hop . t x t " ) ;
i nt i ;
double x ;
s t r i ng s ;
g >> i >> x >> s ;
g . c l os e ( ) ;
2. Il est bon de vrier que louverture sest bien passe. Une erreur frquente est
de prciser un mauvais nom de chier : le chier nest alors pas ouvert :
i f s t r eam g( " . . / data/hop . t x t " ) ;
i f ( ! g . i s_open ( ) ) {
cout << " hel p ! " << endl ;
ret urn 1;
}
On peut aussi avoir besoin de savoir si on est arriv au bout du chier :
do {
. . .
} while ( ! ( g . eof ( ) ) ;
3. Un chier peut souvrir aprs construction :
of st ream f ;
f . open( " hop . t x t " ) ;
. . .
4. Moins frquent, mais trs utile connatre : on peut crire dans un chier direc-
tement la suite doctets en mmoire qui correspond une variable ou un tableau.
Le chier 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 ;
of st ream f ( " hop . bi n " , i os : : bi nary ) ;
f . wri t e ( ( const char ) x , 10 s i ze of ( double ) ) ;
157
11.2. Fichiers 11. En vrac...
f . wri t e ( ( const char )&y , s i ze of ( double ) ) ;
f . c l os e ( ) ;
. . .
i f s t r eam g( " hop . bi n " , i os : : bi nary ) ;
g . read ( ( char ) x , 10 s i ze of ( double ) ) ;
g . read ( ( const char )&y , s i ze of ( double ) ) ;
g . c l os e ( ) ;
Attention ne pas oublier le "mode douverture" ios :: binary
11.2.2 Chanes et chiers
1. Pour ouvrir un chier, il faut prciser le nom avec une chane au format C. Do
la conversion...
void l i r e ( s t r i ng nom) {
i f s t r eam f (nom. c _ s t r ( ) ) ; / / Conve r s i on o b l i g a t o i r e . . .
. . .
}
2. Pour lire une chane avec des espaces, mme chose quavec cin :
ge t l i ne ( g , s ) ;
ge t l i ne ( g , s , : ) ;
3. Enn, un peu technique mais trs pratique : les stringstream qui sont des chanes
simulant des chiers virtuels. On les utilise notamment pour convertir une chane
en nombre ou linverse :
# i ncl ude <sstream>
usi ng namespace st d ;
s t r i ng s=" 12 " ;
st r i ngst r eam f ;
i nt i ;
/ / Cha ne ve r s e n t i e r
f << s ; / / On c r i t l a c ha ne
f >> i ; / / On r e l i t un e n t i e r ! ( i vaut 12)
i ++;
/ / Ent i e r ve r s c ha ne
f . c l e ar ( ) ; / / Ne pas 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 ha ne ( s vaut "13")
11.2.3 Objets et chiers
Le grand intrt des << et >>
3
est la possibilit de les rednir pour des objets !
Cest technique, mais il suft 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 enn leur puissance !
158
11. En vrac... 11.3. Valeurs par dfaut
s t r uc t poi nt {
i nt x , y ;
} ;
ostream& operat or <<( ostream& f , const poi nt& p) {
f << p. x << << p. y ; / / ou quoi que c e s o i t d a ut 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 do nn e s s p a r e s par un e s p a c e . . . )
ret urn f ;
}
i st ream& operat or >>( i st ream& f , poi nt& p) {
f >> p. x >> p. y ; / / ou quoi que c e s o i t d a ut r e !
ret urn f ;
}
. . .
poi nt p;
ci n >> p;
cout << p;
of st ream f ( " . . / hop . t x t " ) ;
f << p;
. . .
i f s t r eam g( " . . / hop . t x t " ) ;
g >> p;
11.3 Valeurs par dfaut
11.3.1 Principe
Souvent utile ! On peut donner des valeurs par dfaut aux derniers paramtres
dune fonction, valeurs quils prendront sils ne sont pas prciss lappel :
void f ( i nt a , i nt b=0 , i nt c =0) {
/ / . . .
}
void g ( ) {
f ( 1 2 ) ; / / Appe l l e f ( 1 2 , 0 , 0 ) ;
f ( 1 0 , 2 ) ; / / Appe l l e f ( 1 0 , 2 , 0 ) ;
f ( 1 , 2 , 3 ) ; / / Appe l l e f ( 1 , 2 , 3 ) ;
}
Sil y a dclaration puis dnition, on ne prcise les valeurs par dfaut que dans la
dclaration :
void f ( i nt a , i nt b =0) ; / / d c l a r a t i o n
void g ( ) {
f ( 1 2 ) ; / / Appe l l e f ( 1 2 , 0 ) ;
f ( 1 0 , 2 ) ; / / Appe l l e f ( 1 0 , 2 ) ;
159
11.4. Accesseurs 11. En vrac...
}
void f ( i nt a , i nt b) { / / ne pas re p r c i s e r i c i l e b par d f a ut . . .
/ / . . .
}
11.3.2 Utilit
En gnral, on part dune fonction :
i nt f ( i nt a , i nt b) {
. . .
}
Puis, on veut lui rajouter un comportement spcial dans un certain cas :
i nt f ( i nt a , i nt b , bool s pe c i al ) {
. . .
}
Plutt que de transformer tous les anciens appels f (.,.) en f (.,., false ), il suft de
faire :
i nt f ( i nt a , i nt b , bool s pe c i al =f a l s e ) {
. . .
}
pour laisser les anciens appels inchangs, et uniquement appeler f (.,., true) dans les
futurs cas particuliers qui vont se prsenter.
11.3.3 Erreurs frquentes
Voici les erreurs frquentes lorsquon veut utiliser des valeurs par dfaut :
1. Vouloir en donner aux paramtres au milieu de la liste :
void f ( i nt a , i nt b=3 , i nt c ) { / / NON! Le s d e r n i e r s pa r a m t r e s
/ / Pas ceux du mi l i e u !
}
2. Engendrer des problmes de surcharge :
void f ( i nt a ) {
. . .
}
void f ( i nt a , i nt b=0) { / / Pr obl me de s ur c ha r ge !
. . . / / On ne s aur a pas r s o udr e f ( 1 )
}
11.4 Accesseurs
Voici, en cinq tapes, les points utiles connatre pour faire des accesseurs pratiques
et efcaces.
160
11. En vrac... 11.4. Accesseurs
11.4.1 Rfrence comme type de retour
Voici une erreur souvent rencontre, qui fait hurler ceux qui comprennent ce qui se
passe :
i nt i ; / / Va r i a b l e g l o b a l e
i nt f ( ) {
ret urn i ;
}
. . .
f ( ) =3 ; / / Ne ve ut r i e n d i r e ( pas pl us 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 :
i nt i ; / / Va r i a b l e g l o b a l e
i nt& f ( ) {
ret urn i ;
}
. . .
f ( ) =3 ; / / OK! Met 3 dans i !
Attention : apprendre a un dbutant est trs dangereux. En gnral, il se dpche
de commettre lhorreur suivante :
i nt& f ( ) {
i nt i ; / / Var . l o c a l e
ret urn i ; / / r f r e n c e ve r s une v a r i a b l e qui va mouri r !
/ / C EST GRAVE!
}
. . .
f ( ) =3 ; / / NON! ! ! Le i n e x i s t e pl us . 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 poi nt {
double x [N] ;
publ i c :
void s et ( i nt i , double v ) ;
} ;
void poi nt : : s et ( i nt i , double v) {
x [ i ]=v ;
}
. . .
poi nt p;
p. s et ( 1 , 2 . 3 ) ;
le transformer en :
161
11.4. Accesseurs 11. En vrac...
c l a s s poi nt {
double x [N] ;
publ i c :
double& element ( i nt i ) ;
} ;
double& poi nt : : element ( i nt i ) {
ret urn x [ i ] ;
}
. . .
poi nt p;
p. element ( 1 ) =2 . 3 ;
11.4.3 operator()
Etape suivante : ceci devient encore plus utile quand on connat operator() qui per-
met de rednir les parenthses :
c l a s s poi nt {
double x [N] ;
publ i c :
double& operat or ( ) ( i nt i ) ;
} ;
double& poi nt : : operat or ( ) ( i nt i ) {
ret urn x [ i ] ;
}
. . .
poi nt 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] ;
publ i c :
double& operat or ( ) ( i nt i , i nt j ) ;
} ;
double& mat : : operat or ( ) ( i nt i , i nt j ) {
ret urn x [ i +M j ] ;
}
. . .
mat A;
A( 1 , 2 ) =2 . 3 ;
11.4.4 Surcharge et mthode constante
Nous sommes maintenant face un problme : le programme prcdent ne permet
pas dcrire :
void f ( mat& A) {
162
11. En vrac... 11.4. Accesseurs
A( 1 , 1 ) =2 ; / / OK
}
void f ( const mat& A) {
double x=A( 1 , 1 ) ; / / NON! Le c o mpi l a t e ur ne s a i t pas que
/ / c e t t e l i g n e ne mo d i f i e r a pas A!
}
car la mthode operator() nest pas constante. Il y a heureusement une solution : pro-
grammer deux accesseurs, en protant 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] ;
publ i c :
/ / Mme nom, mmes par am t r e s , mai s l une e s t c o ns t !
/ / Donc s ur c ha r ge p o s s i b l e
double& operat or ( ) ( i nt i , i nt j ) ;
double operat or ( ) ( i nt i , i nt j ) const ;
} ;
double mat : : operat or ( ) ( i nt i , i nt j ) const {
ret urn x [ i +M j ] ;
}
double& mat : : operat or ( ) ( i nt i , i nt j ) {
ret urn x [ i +M j ] ;
}
void f ( mat& A) {
A( 1 , 1 ) =2 ; / / OK, a p p e l l e l e pr e mi e r o p e r a t o r ( )
}
void f ( const mat& A) {
double x=A( 1 , 1 ) ; / / OK, a p p e l l e l e deuxi me
}
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 vrient 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 nl i ne double sqr ( double x ) {
ret urn xx ;
}
. . .
double y=sqr ( 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 pna-
lisant par rapport au corps de la fonction!
Si la fonction tait dclare dans un .h et dnie dans un .cpp, il faut maintenant
la mettre entirement dans le .h car lutilisateur de la fonction a besoin de la
dnition 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 Re-
lease protera donc de lacclration.
Cas des mthodes
Dans le cas dune mthode, il faut bien penser la mettre dans le cher .h si la
classe tait dnie en plusieurs chiers. Cest le moment de rvler ce que nous gar-
dions cach :
Il est possible de DFINIR UNE MTHODE ENTIREMENT DANS LA DFINI-
TION DE LA CLASSE, au lieu de seulement ly dclarer puis placer sa d-
nition en dehors de celle de la classe. Cependant, ceci nest pas obliga-
toire
a
, ralentit la compilation et va lencontre de lide quil faut masquer
le contenu des mthodes lutilisateur dune classe. Cest donc RSERV
AUX PETITES FONCTIONS, en gnral de type inline.
a. Contrairement ce quil faut faire en Java ! Encore une source de mauvaises habitudes
pour le programmeur Java qui se met C++...
Voici ce que cela donne en pratique :
c l a s s mat {
double x [MN] ;
publ i c :
i nl i ne double& operat or ( ) ( i nt i , i nt j ) {
ret urn x [ i +M j ] ;
}
i nl i ne double operat or ( ) ( i nt i , i nt j ) const {
ret urn x [ i +M j ] ;
}
} ;
164
11. En vrac... 11.5. Assertions
11.5 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 :
# i ncl ude <c as s er t >
c l a s s mat {
double x [MN] ;
publ i c :
i nl i ne double& operat or ( ) ( i nt i , i nt j ) {
as s e r t ( i >=0 && i <M && j >=0 && j <N) ;
ret urn x [ i +M j ] ;
}
i nl i ne double operat or ( ) ( i nt i , i nt j ) const {
as s e r t ( i >=0 && i <M && j >=0 && j <N) ;
ret urn x [ i +M j ] ;
}
} ;
11.6 Types numrs
Cest une bonne ide de passer par des constantes pour rendre un programme plus
lisible :
const i nt nord=0 , es t =1 , sud=2 , ouest =3;
void avance ( i nt di r ec t i on ) ;
mais il est maladroit de faire ainsi ! Il vaut mieux connatre lexistence des types num-
rs :
enum Dir { nord , est , sud , ouest } ;
void avance ( Dir di r ec t i on ) ;
Il sagit bien de dnir un nouveau type, qui, en ralit, masque des entiers. Une pr-
cision : 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 che de rfrence...
165
11.7. Fiche de rfrence 11. En vrac...
11.7 Fiche de rfrence
Fiche de rfrence (1/4)
Boucles
do {
...
} while (!ok);
int i=1;
while (i<=100) {
...
i=i+1;
}
for (int i=1;i<=10;i++)
...
for (int i=1,j=10;j>i;
i=i+2,j=j-3)
...
for (int i=...)
for (int j=...) {
//saute le cas i==j
if (i==j)
continue;
...
}
Clavier
Build : F7
Debug : F5
Step over : F10
Step inside : F11
Indent : Ctrl+K,Ctrl+F
Add New It. : Ctrl+Maj+A
Add Exist. It. : Alt+Maj+A
Step out : Maj+F11
Run to curs. : Click droit
Compltion : Alt+
Gest. tches : Ctrl+Maj+Ech
Structures
struct Point {
double x,y;
Color c;
};
...
Point a;
a.x=2.3; a.y=3.4;
a.c=Red;
Point b={1,2.5,Blue};
Une structure est un objet en-
tirement public (cf objets !)
Variables
Dnition :
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=254;
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);
int i,j;
double x=double(i)/j;
Pile/Tas
Type numr :
enum Dir{nord,est,
sud,ouest};
void avance(Dir d);
Tests
Comparaison :
== != < > <= >=
Ngation : !
Combinaisons : && ||
if (i==0)
j=1;
if (i==0)
j=1;
else
j=2;
if (i==0) {
j=1;
k=2;
}
bool t=(i==0);
if (t)
j=1;
switch (i) {
case 1:
...;
...;
break;
case 2:
case 3:
...;
break;
default:
...;
}
Conseils
Travailler en local
Imagine++ Project
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 chiers spars.
Le .h doit sufre lutilisa-
teur (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 ob-
jets !
Penser interface / implmen-
tation / utilisation.
166
11. En vrac... 11.7. Fiche de rfrence
Fiche de rfrence (2/4)
Fonctions
Dnition :
int plus(int a,int b) {
int c=a+b;
return c;
}
void affiche(int a) {
cout << a << endl;
}
Dclaration :
int plus(int a,int b);
Retour :
int signe(double x) {
if (x<0)
return -1;
if (x>0)
return 1;
return 0;
}
void afficher(int x,
int y) {
if (x<0 || y<0)
return;
if (x>=w || y>=h)
return;
DrawPoint(x,y,RED);
}
Appel :
int f(int a) { ... }
int g() { ... }
...
int i=f(2),j=g();
Rfrences :
void swap(int& a,
int& b){
int tmp=a;
a=b;b=tmp;
}
...
int x=3,y=2;
swap(x,y);
Surcharge :
int hasard(int n);
int hasard(int a,
int b);
double hasard();
Oprateurs :
vect operator+(
vect A,vect B) {
...
}
...
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
}
Valeurs par dfaut :
void f(int a,int b=0);
void g() {
f(12); // f(12,0);
f(10,2);// f(10,2);
}
void f(int a,int b) {
// ...
}
Inline (appel rapide) :
inline double sqr(
double x) {
return x
*
x;
}
...
double y=sqr(z-3);
Rfrence en retour :
int i; // Var. globale
int& f() {
return i;
}
...
f()=3; // i=3!
Tableaux
Dnition :
double x[10],y[10];
for(int i=0;i<10;i++)
y[i]=2
*
x[i];
const int n=5;
int i[n],j[2
*
n]; //OK
Initialisation :
int t[4]={1,2,3,4};
string s[2]={"ab","c"};
Affectation :
int s[3]={1,2,3},t[3];
for (int i=0;i<3;i++)
t[i]=s[i];
En paramtre :
void init(int t[4]) {
for(int i=0;i<4;i++)
t[i]=0;
}
void init(int t[],
int n) {
for(int i=0;i<n;i++)
t[i]=0;
}
Taille variable :
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];
A[i+2
*
j]=...;
Taille variable (suite) :
int
*
t,
*
s,n;
Compilation spare
#include "vect.h", y
compris dans vect.cpp
Fonctions : dclarations dans
le .h, dnitions dans le .cpp
Types : dnitions dans le .h
Ne dclarer dans le .h que les
fonctions utiles.
#pragma once au dbut du
chier.
Ne pas trop dcouper...
167
11.7. Fiche de rfrence 11. En vrac...
Fiche de rfrence (3/4)
Objets
struct obj {
int x; // champ
int f(); // mthode
int g(int y);
};
int obj::f() {
int i=g(3); // mon g
int j=x+i; // mon x
return j;
}
...
int main() {
obj a;
a.x=3;
int i=a.f();
class obj {
int x,y;
void a_moi();
public:
int z;
void pour_tous();
void un_autre(obj A);
};
void obj::a_moi() {
x=..; // OK
..=y; // OK
z=..; // OK
}
void obj::pour_tous() {
x=..; // OK
a_moi(); // OK
}
void une_autre(obj A) {
x=A.x; // OK
A.a_moi(); // OK
}
...
int main() {
obj A,B;
A.x=..; //NON
A.z=..; //OK
A.a_moi(); //NON
A.pour_tous(); //OK
A.une_autre(B); //OK
class obj {
obj operator+(obj B);
};
...
int main() {
obj A,B,C;
C=A+B;
// C=A.operator+(B)
Mthodes constantes :
void obj::f() const{
...
}
void g(const obj& x){
x.f(); // OK
}
Constructeur :
class point {
int x,y;
public:
point(int X,int Y);
};
point::point(int X,
int Y){
x=X;
y=Y;
}
...
point a(2,3);
Constructeur vide :
obj::obj() {
...
}
...
obj a;
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() {
...
}
Constructeur de copie :
obj::obj(const obj& o){
...
}
Utilis par :
- obj b(a);
- obj b=a;
// mieux que obj b;b=a;
- paramtres des fonctions
- valeur de retour
Affectation :
obj& obj::operator=(
const obj&o){
...
return
*
this;
}
Objets avec allocation dyna-
mique automatique : cf section
10.11
Accesseurs :
class mat {
double
*
x;
public:
double& operator()
(int i,int j){
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);
#include <ctime>
...
srand((unsigned int)
time(0));
#include <cmath>
double sqrt(double x);
double cos(double x);
double sin(double x);
double acos(double x);
#include <string>
using namespace std;
string s="hop";
char c=s[0];
int l=s.size();
if (s1==s1) ...
if (s1!=s2) ...
if (s1<s2) ...
size_t i=s.find(h),
j=s.find(h,3);
k=s.find("hop");
l=s.find("hop",3);
a="comment";
b="a va?";
txt=a+" "+b;
s1="un deux trois";
s2=string(s1,3,4);
getline(cin,s);
getline(cin,s,:);
const char
*
t=s.c_str();
#include <ctime>
s=double(clock())
/CLOCKS_PER_SEC;
#define _USE_MATH_DEFINES
#include <cmath>
double pi=M_PI;
168
11. En vrac... 11.7. Fiche de rfrence
Fiche de rfrence (4/4)
Entres/Sorties
#include <iostream>
using namespace std;
...
cout <<"I=" <<i <<endl;
cin >> i >> j;
#include <fstream>
using namespace std;
ofstream f("hop.txt");
f << 1 << << 2.3;
f.close();
ifstream g("hop.txt");
if (!g.is_open()) {
return 1;
}
int i;
double x;
g >> i >> x;
g.close();
do {
...
} while (!(g.eof());
ofstream f;
f.open("hop.txt");
double x[10],y;
ofstream f("hop.bin",
ios::binary);
f.write((const char
*
)x,
10
*
sizeof(double));
f.write((const char
*
)&y,
sizeof(double));
f.close();
ifstream g("hop.bin",
ios::binary);
g.read((char
*
)x,
10
*
sizeof(double));
g.read((const char
*
)&y,
sizeof(double));
g.close();
string nom;
ifstream f(nom.c_str());
#include <sstream>
using namespace std;
stringstream f;
// Chane vers entier
f << s;
f >> i;
// Entier vers chane
f.clear();
f << i;
f >> s;
ostream& operator<<(
ostream& f,
const point&p) {
f<<p.x<< << p.y;
return f;
}
istream& operator>>(
istream& f,point& p){
f>>p.x>>p.y;
return f;
}
Erreurs frquentes
Pas de dnition de fonction
dans une fonction!
int q=r=4; // NON!
if (i=2) // NON!
if i==2 // NON!
if (i==2) then // NON!
for (int i=0,i<100,i++)
// NON!
int f() {...}
...
int i=f; // NON!
double x=1/3; // NON!
int i,j;
double x;
x=i/j; // NON!
x=double(i/j); //NON!
double x[10],y[10];
for (int i=1;i<=10;i++)
y[i]=2
*
x[i]; //NON
int n=5;
int t[n]; // NON
int f()[4] { // NON!
int t[4];
...
return t; // NON!
}
int t[4]; t=f();
int s[3]={1,2,3},t[3];
t=s; // NON!
int t[2];
t={1,2}; // NON!
struct Point {
double x,y;
} // NON!
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!
class point {
int x,y;
public:
...
};
...
point a={2,3}; // NON
Oublier de rednir le
constructeur vide.
point p=point(1,2);//NON
point p(1,2); // OUI
obj
*
t=new obj[n];
...
delete t; // manque []
//NON!
void f(int a=2,int b);
void f(int a,int b=0);
void f(int a);// NON!
Ne pas tout mettre inline!
int f() {
...
}
...
f()=3; // HORREUR!
int& f() {
int i;
return i;
}
...
f()=3; // NON!
Imagine++
Voir documentation...
169
12. En vrac (suite) ...
Chapitre 12
En vrac (suite) ...
Nous continuons dans ce chapitre un inventaire de diverses choses utiles. Parmi elles, les
structures de donnes de la STL (Standard Template Library) ncessiteront la comprhension
des template. Nous aborderons donc cet aspect intressant du C++

Toujours sous forme dun inventaire, le dbut de ce chapitre sera un peu en vrac,
mais nous nous limiterons toujours quelques aspects pratiques du C++ souvent uti-
liss donc souvent rencontrs dans les programmes des autres ! La n du chapitre est
plus utile et plus fondamentale : nous y abordons les template, ou programmation
gnrique.
12.1 Oprateur binaires
Parmi les erreurs classiques, il y a videmment celle qui consiste remplacer
i f ( i ==0)
. . .
par
i f ( i =0) / / NON! ! !
. . .
qui range 0 dans i puis considre 0 comme un boolen, cest dire false. Une autre
erreur frquente consiste crire
i f ( i ==0 & j ==2) / / NON! ! !
. . .
au lieu de
i f ( i ==0 && j ==2)
. . .
Cette erreur nest possible que parce que & existe. Il sagit de oprateur binaire "ET"
sur des entiers. Il est dni ainsi : effectuer a&b revient considrer lcriture de a et
de b en binaire puis effectuer un "ET" bit par bit (avec la table 1&1 donne 1 ; 1&0, 0&1
et 0&0 donnent 0). Par exemple : 13&10 vaut 8 car en binaire 1101&1010 vaut 1000.
Il existe ainsi toute une panoplie doprateurs binaires :
12.2. Valeur conditionnelle 12. En vrac (suite) ...
symbole utilisation nom rsultat exemple
& a&b et 1&1=1, 0 sinon 13&10=8
| a|b ou 0|0=0, 1 sinon 13|10=15
^ a^b ou exclusif 1^0=0^1=1, 0 sinon 13^10=7
>> a>>n dcalage dcale les bits de a n fois vers la 13>>2=3
droite droite et comble gauche avec des 0
(les n premiers de droite sont perdus)
<< a<<n dcalage dcale les bits de a n fois vers la 5<<2=20
gauche gauche et comble droite avec des 0
~ ~a complment ~1=0, ~0=1 ~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 n
eme
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) passe 1 le bit n de i
i&=~(1<<n) passe 0 le bit n de i
i^=(1<<n) inverse le bit n de i
if ( i&(1<<n)) 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) vaut 2
n
(sinon il faudrait faire int (pow(2.,n)) !)
( i>>1) calcule i/2 rapidement
( i>>n) calcule i/2
n
rapidement
( i&255) calcule i%256 rapidement (idem pour toute puissance de 2)
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 ) ? val 1 : val 2
qui vaut val1 si test est vrai et val2 sinon. Ainsi
i f ( x>y)
maxi=x ;
e l s e
maxi=y ;
172
12. En vrac (suite) ... 12.3. Boucles et break
pourra tre remplac par :
maxi =( x>y) ? x : y ;
Il ne faut pas abuser de cette construction sous peine de programme illisible !
12.3 Boucles et break
Nous avons dj rencontr la section 8.4 linstruction continue qui saute la n
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 or ( i nt i =0; i <N && ! a r r e t e r ; i ++) {
A;
i f ( B)
a r r e t e r =t r ue ;
e l s e {
C;
i f (D)
a r r e t e r =t r ue ;
e l s e {
E;
}
}
}
devient de faon plus lisible et plus naturelle :
f or ( i nt 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!
i f ( . . . ) {
. . . ;
i f ( . . . )
break ; / / NON! ! ! Ne s o r t pas du i f ! ( mai s v e nt ue l l e me nt
/ / d un f o r qui s e r a i t aut our . . . )
. . .
}
2. break ne sort que de la boucle courante, pas des boucles autour :
173
12.4. Variables statiques 12. En vrac (suite) ...
f or ( i nt i =0; i <N; i ++) {
. . .
f or ( i nt j =0; j <M; j ++) {
. . .
i f ( . . . )
break ; / / t e r mi ne l a b o uc l e en j e t pa s s e donc
/ / en l i g n e 10 ( pas en l i g n e 12)
. . .
}
. . .
}
. . .
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 per-
manente une valeur qui nintresse quune seule fonction :
/ / Fo nc t i o n random qui a p p e l l e s r and ( ) t o ut e s e u l e
/ / au pr e mi e r a ppe l . . .
bool f i r s t =t r ue ;
double random( ) {
i f ( f i r s t ) {
f i r s t =f a l s e ;
srand ( ( unsigned i nt ) time ( 0 ) ) ;
}
ret urn double ( rand ( ) ) /RAND_MAX;
}
Le danger est alors que tout le reste du programme voie cette variable globale et luti-
lise 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 :
/ / Fo nc t i o n random qui a p p e l l e s r and ( ) t o ut e s e u l e
/ / au pr e mi e r a ppe l . . . ave 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 ue ; / / Ne pas o u b l i e r s t a t i c !
i f ( f i r s t ) {
f i r s t =f a l s e ;
srand ( ( unsigned i nt ) time ( 0 ) ) ;
}
ret urn double ( rand ( ) ) /RAND_MAX;
}
Attention : il sagit bien dune variable globale et non dune variable locale. Une
variable locale mourrait la sortie de la fonction, ce qui dans lexemple prcdent
donnerait un comportement non dsir !
174
12. En vrac (suite) ... 12.5. const et tableaux
NB : Il est aussi possible de cacher une variable globale dans une classe, toujours
grce static . Nous ne verrons pas comment et renvoyons le lecteur la documenta-
tion du C++.
12.5 const et tableaux
Nous avons vu malgr nous const char comme paramtre de certaines fonctions
(ouverture de chier 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 signie que ce sont les lments du tableau
qui ne peuvent tre modis.
Cette possibilit de prciser quun tableau ne peut tre modi est dautant plus im-
portante quun tableau est toujours pass en rfrence : sans le const, on ne pourrait
assurer cette prservation des valeurs :
void f ( i nt t [ 4 ] ) {
. . .
}
void g( const i nt t [ 4 ] ) {
. . .
}
void h( const i nt t , i nt n) {
. . .
}
. . .
i nt a [ 4 ] ;
f ( a ) ; / / mo d i f i e peut t r e a [ ]
g( a ) ; / / ne mo d i f i e pas a [ ]
h( a , 4 ) ; / / ne mo d i f i e pas a [ ]
. . .
12.6 template
12.6.1 Principe
Considrons la fonction classique pour changer deux variables :
void echange ( i nt& a , i nt& b) {
i nt tmp;
tmp=a ;
a=b ;
b=tmp;
}
175
12.6. template 12. En vrac (suite) ...
. . .
i nt i , j ;
. . .
echange ( i , j ) ;
Si nous devions maintenant changer deux variables de type double, il faudrait r-
crire une autre fonction echange(), identique aux dnitions de type prs. Heureuse-
ment, le C++ offre la possibilit de dnir 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 dnissant
un "template" :
/ / Echange deux v a r i a b l e s de n i mpo r t e q ue l t ype T
t empl at e <typename T>
void echange ( T& a , T& b) {
T tmp;
tmp=a ;
a=b ;
b=tmp;
}
. . .
i nt 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 nt
echange ( x , y ) ; / / " i n s t a n c i e " T en do ubl e
. . .
Autre exemple :
/ / Maximum de deux v a r i a b l e s ( a c o nd i t i o n que o pe r a t o r >( ) e x i s t e
/ / pour l e t ype T)
t empl at e <typename T>
T maxi ( T a , T b) {
ret urn ( a>b) ? a : b ;
}
La dclaration typename T prcise le type gnrique. On peut en prciser plusieurs :
/ / Che r che e1 dans l e t a b l e a u t ab1 e t met
/ / dans e2 l e l e me nt de t ab2 de meme i n d i c e
/ / Re nvoi e f a l s e s i non t r o uv
t empl at e <typename T1 , typename T2>
bool cherche ( T1 e1 , T2& e2 , const T1 tab1 , const T2 tab2 , i nt n) {
f or ( i nt i =0; i <n ; i ++)
i f ( t ab1 [ i ]==e1 ) {
e2=t ab2 [ i ] ;
ret urn t r ue ;
}
ret urn f a l s e ;
}
. . .
s t r i ng noms [ 3] ={ " j ean " , " pi er r e " , " paul " } ;
i nt ages [ 3 ] ={ 2 1 , 2 5 , 1 5 } ;
176
12. En vrac (suite) ... 12.6. template
. . .
s t r i ng nm=" pi er r e " ;
i nt ag ;
i f ( cherche (nm, ag , noms , ages , 3 ) )
cout << nm << " a " << ag << " ans " << endl ;
. . .
12.6.2 template et chiers
Il faut bien comprendre que
Le compilateur ne fabrique pas une fonction "magique" qui arrive tra-
vailler sur plusieurs types ! Il cre en ralit autant de fonctions quil y a
dutilisations de la fonction gnrique avec des types diffrents (ie. dins-
tanciations)
Pour ces raisons :
1. Faire des fonctions template ralentit la compilation et augmente la taille des pro-
grammes.
2. On ne peut plus mettre la dclaration dans un chier den-tte et la dnition
dans un chier .cpp, car tous les chiers utilisateurs doivent connatre aussi la
dnition. Du coup, la rgle est de tout mettre dans le chier den-tte
1
.
12.6.3 Classes
Il est frquent quune dnition de classe soit encore plus utile si elle est gnrique.
Cest possible. Mais attention! Dans le cas des fonctions, cest le compilateur qui d-
termine 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 :
/ / Pa i r e de deux v a r i a b l e s de t ype T
t empl at e <typename T>
c l a s s pai r e {
T x [ 2 ] ;
publ i c :
/ / c o ns t r uc t e ur s
pai r e ( ) ;
pai r e ( T A, T B ) ;
/ / a c c e s s e u r s
T operat or ( ) ( i nt i ) const ;
T& operat or ( ) ( i nt i ) ;
} ;
t empl at e <typename T>
pai re <T>: : pai r e ( ) {
1. Ceci est gnant et va lencontre du principe consistant mettre les dclarations dans le .h et
masquer les dnitions dans le .cpp. Cette remarque a dj t formule pour les fonctions inline. Le
langage prvoit une solution avec le mot cl export, mais les compilateurs actuels nimplmentent pas
encore cette fonctionnalit !
177
12.6. template 12. En vrac (suite) ...
}
t empl at e <typename T>
pai re <T>: : pai r e ( T A, T B) {
x [ 0] =A; x [ 1] =B;
}
t empl at e <typename T>
T pai re <T>: : operat or ( ) ( i nt i ) const {
as s e r t ( i ==0 || i ==1) ;
ret urn x [ i ] ;
}
t empl at e <typename T>
T& pai re <T>: : operat or ( ) ( i nt i ) {
as s e r t ( i ==0 || i ==1) ;
ret urn x [ i ] ;
}
. . .
pai re <i nt > p( 1 , 2 ) , r ;
i nt i =p ( 1 ) ;
pai re <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 :
/ / Pa i r e de deux v a r i a b l e s de t ype T
/ / Fo nc t i o ns c o ur t e s e t r a p i d e s en i n l i n e
t empl at e <typename T>
c l a s s pai r e {
T x [ 2 ] ;
publ i c :
/ / c o ns t r uc t e ur s
i nl i ne pai r e ( ) { }
i nl i ne pai r e ( T A, T B) { x [ 0] =A; x [ 1] =B; }
/ / a c c e s s e u r s
i nl i ne T operat or ( ) ( i nt i ) const {
as s e r t ( i ==0 || i ==1) ;
ret urn x [ i ] ;
}
i nl i ne T& operat or ( ) ( i nt i ) {
as s e r t ( i ==0 || i ==1) ;
ret urn x [ i ] ;
}
} ;
Lorsque plusieurs types sont gnriques, on les spare par une virgule :
/ / Pa i r e de deux v a r i a b l e s de t ype s d i f f r e n t s
178
12. En vrac (suite) ... 12.6. template
t empl at e <typename S , typename T>
c l a s s pai r e {
publ i c :
/ / Tout en p ub l i c pour s i mp l i f i e r
S x ;
T y ;
/ / c o ns t r uc t e ur s
i nl i ne pai r e ( ) { }
i nl i ne pai r e ( S X, T Y) { x=X; y=Y; }
} ;
. . .
pai re <i nt , double> P ( 1 , 2 . 3 ) ;
pai re <s t r i ng , i nt > Q;
Q. x=" pi er r e " ;
Q. y=25;
. . .
Enn, on peut aussi rendre gnrique le choix dun entier :
/ / nup l e t de v a r i a b l e s de t ype T
/ / At t e nt i o n : chaque nupl e t <T, N> s e r a un t ype d i f f r e n t
t empl at e <typename T, i nt N>
c l a s s nupl et {
T x [N] ;
publ i c :
/ / a c c e s s e u r s
i nl i ne T operat or ( ) ( i nt i ) const {
as s e r t ( i >=0 && i <N) ;
ret urn x [ i ] ;
}
i nl i ne T& operat or ( ) ( i nt i ) {
as s e r t ( i >=0 && i <N) ;
ret urn x [ i ] ;
}
} ;
. . .
nupl et <i nt , 4 > A;
A( 1 ) =3 ;
nupl et <s t r i ng , 2 > B;
B( 1) = " pi er r e " ;
. . .
Les fonctions doivent videmment sadapter :
t empl at e <typename T, i nt N>
T somme( nupl et <T, N> u) {
T s=u ( 0 ) ;
f or ( i nt i =1; i <N; i ++)
s+=u( i ) ;
ret urn s ;
}
179
12.6. template 12. En vrac (suite) ...
. . .
nupl et <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 compi-
lateur 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 cer-
tain nombre de fonctions et de classes utilisant les template. Cet ensemble est commu-
nment dsign sous le nom de STL (Standard Template Library). Vous en trouverez
la documentation complte sous Visual ou dfaut sur Internet. Nous exposons ci-
dessous 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 dnies de faon gnrique :
i nt i =max ( 1 , 3 ) ;
double x=min ( 1 . 2 , 3 . 4 ) ;
Attention : une erreur classique consiste appeler max(1,2.3) : le compilateur linter-
prte 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 ncl ude <complex>
usi ng namespace st d ;
. . .
complex<double> z1 ( 1 . 1 , 3 . 4 ) , z2 ( 1 , 0 ) , z3 ;
z3=z1+z2 ;
cout << z3 << endl ;
double a=z3 . r e al ( ) , b=z3 . imag ( ) ;
double m=abs ( z3 ) ; / / modul e
double th=arg ( z3 ) ; / / argument
Les couples sont aussi offerts par la STL :
pai r <i nt , s t r i ng > P( 2 , " hop" ) ;
P. f i r s t =3;
P. second=" hop" ;
180
12. En vrac (suite) ... 12.6. template
Enn, un certain nombre de structures de donnes sont fournies et sutilisent suivant
un mme schma. Voyons lexemple des listes :
# i ncl ude <l i s t >
usi ng namespace st d ;
. . .
l i s t <i nt > l ; / / l =[ ]
l . push_f ront ( 2 ) ; / / l =[ 2]
l . push_f ront ( 3 ) ; / / l =[ 3 , 2]
l . push_back ( 4 ) ; / / l =[ 3 , 2 , 4]
l . push_f ront ( 5 ) ; / / l =[ 5 , 3 , 2 , 4]
l . push_f ront ( 2 ) ; / / 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 difcult : le
type de ces itrateurs est un peu compliqu taper
2
:
l i s t <i nt >: : c ons t _ i t e r a t or i t ;
i t =l . begi n ( ) ; / / Po i nt e ve r s l e d but de l a l i s t e
cout << i t << endl ; / / a f f i c h e 2
i t =l . f i nd ( 3 ) ; / / Po i nt e ve r s l e nd r o i t ou s e t r o uve
/ / l e pr e mi e r 3
i f ( i t ! = l . end ( ) )
cout << " 3 es t dans l a l i s t e " << endl ;
l i s t <i nt >: : i t e r a t o r i t 2 ;
i t 2 =l . f i nd ( 3 ) ; / / Po i nt e ve r s l e nd r o i t ou s e t r o uve
/ / l e pr e mi e r 3
i t =6; / / mai nt e nant l =[ 2 , 5 , 6 , 2 , 4]
Les itrateurs servent galement parcourir les listes (do leur nom!) :
/ / Pa r c o ur t e t a f f i c h e une l i s t e
t empl at e <typename T>
void a f f i c he ( l i s t <T> l ) {
cout << " [ " ;
f or ( l i s t <T>: : c ons t _ i t e r a t or i t =l . begi n ( ) ; i t ! = l . end ( ) ; i t ++)
cout << i t << ;
cout << ] << endl ;
}
/ / Rempl ace a par b dans une l i s t e
t empl at e <typename T>
void remplace ( l i s t <T>& l , T a , T b) {
f or ( l i s t <T>: : i t e r a t o r i t =l . begi n ( ) ; i t ! = l . end ( ) ; i t ++)
i f ( i t ==a )
i t =b ;
}
. . .
a f f i c he ( l ) ;
2. Nous navons pas vu comment dnir de nouveaux types cachs dans des classes ! Cest ce qui est
fait ici...
181
12.7. Fiche de rfrence 12. En vrac (suite) ...
remplace ( l , 2 , 1 ) ; / / mai nt e nant l =[ 1 , 5 , 3 , 1 , 4]
. . .
Enn, on peut appeler des algorithmes comme le tri de la liste :
l . s or t ( ) ;
a f f i c he ( l ) ;
Sur le mme principe que les listes, vous trouverez dans la STL :
Les piles ou stack (Last In First Out).
Les les ou queue (First In First Out).
Les ensembles ou set (pas deux fois le mme lment).
Les vecteurs ou vector (tableaux de taille variable).
Les tas ou heap (arbres binaires de recherche).
Les tables ou map (table de correspondance cl/valeur).
Et quelques autres encore...
12.7 Fiche de rfrence
Fiche de rfrence (1/6)
Boucles
do {
...
} while (!ok);
int i=1;
while (i<=100) {
...
i=i+1;
}
for (int i=1;i<=10;i++)
...
for (int i=1,j=10;j>i;
i=i+2,j=j-3)
...
for (int i=...)
for (int j=...) {
//saute le cas i==j
if (i==j)
continue;
...
}
for (int i=...) {
...
if (t[i]==s){
...
// quitte la boucle
break;
}
...
}
Clavier
Build : F7
Debug : F5
Step over : F10
Step inside : F11
Indent : Ctrl+K,Ctrl+F
Add New It. : Ctrl+Maj+A
Add Exist. It. : Alt+Maj+A
Step out : Maj+F11
Run to curs. : Click droit
Compltion : Alt+
Gest. tches : Ctrl+Maj+Ech
Tests
Comparaison :
== != < > <= >=
Ngation : !
Combinaisons : && ||
if (i==0)
j=1;
if (i==0)
j=1;
else
j=2;
if (i==0) {
j=1;
k=2;
}
bool t=(i==0);
if (t)
j=1;
switch (i) {
case 1:
...;
...;
break;
case 2:
case 3:
...;
break;
default:
...;
}
mx=(x>y)?x:y;
182
12. En vrac (suite) ... 12.7. Fiche de rfrence
Fiche de rfrence (2/6)
Fonctions
Dnition :
int plus(int a,int b) {
int c=a+b;
return c;
}
void affiche(int a) {
cout << a << endl;
}
Dclaration :
int plus(int a,int b);
Retour :
int signe(double x) {
if (x<0)
return -1;
if (x>0)
return 1;
return 0;
}
void afficher(int x,
int y) {
if (x<0 || y<0)
return;
if (x>=w || y>=h)
return;
DrawPoint(x,y,RED);
}
Appel :
int f(int a) { ... }
int g() { ... }
...
int i=f(2),j=g();
Rfrences :
void swap(int& a,
int& b){
int tmp=a;
a=b;b=tmp;
}
...
int x=3,y=2;
swap(x,y);
Surcharge :
int hasard(int n);
int hasard(int a,
int b);
double hasard();
Oprateurs :
vect operator+(
vect A,vect B) {
...
}
...
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
}
Valeurs par dfaut :
void f(int a,int b=0);
void g() {
f(12); // f(12,0);
f(10,2);// f(10,2);
}
void f(int a,int b) {
// ...
}
Inline (appel rapide) :
inline double sqr(
double x) {
return x
*
x;
}
...
double y=sqr(z-3);
Rfrence en retour :
int i; // Var. globale
int& f() {
return i;
}
...
f()=3; // i=3!
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 en-
tirement public (cf objets !)
Variables
Dnition :
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=254;
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);
int i,j;
double x=double(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;
...
}
...
}
183
12.7. Fiche de rfrence 12. En vrac (suite) ...
Fiche de rfrence (3/6)
Objets
struct obj {
int x; // champ
int f(); // mthode
int g(int y);
};
int obj::f() {
int i=g(3); // mon g
int j=x+i; // mon x
return j;
}
...
int main() {
obj a;
a.x=3;
int i=a.f();
class obj {
int x,y;
void a_moi();
public:
int z;
void pour_tous();
void un_autre(obj A);
};
void obj::a_moi() {
x=..; // OK
..=y; // OK
z=..; // OK
}
void obj::pour_tous() {
x=..; // OK
a_moi(); // OK
}
void une_autre(obj A) {
x=A.x; // OK
A.a_moi(); // OK
}
...
int main() {
obj A,B;
A.x=..; //NON
A.z=..; //OK
A.a_moi(); //NON
A.pour_tous(); //OK
A.une_autre(B); //OK
class obj {
obj operator+(obj B);
};
...
int main() {
obj A,B,C;
C=A+B;
// C=A.operator+(B)
Mthodes constantes :
void obj::f() const{
...
}
void g(const obj& x){
x.f(); // OK
}
Constructeur :
class point {
int x,y;
public:
point(int X,int Y);
};
point::point(int X,
int Y){
x=X;
y=Y;
}
...
point a(2,3);
Constructeur vide :
obj::obj() {
...
}
...
obj a;
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() {
...
}
Constructeur de copie :
obj::obj(const obj& o){
...
}
Utilis par :
- obj b(a);
- obj b=a;
// mieux que obj b;b=a;
- paramtres des fonctions
- valeur de retour
Affectation :
obj& obj::operator=(
const obj&o){
...
return
*
this;
}
Objets avec allocation dyna-
mique automatique : cf section
10.11
Accesseurs :
class mat {
double
*
x;
public:
double& operator()
(int i,int j){
assert(i>=0 ...);
return x[i+M
*
j];
}
double operator()
(int i,int j)const{
assert(i>=0 ...);
return x[i+M
*
j];
}
...
Compilation spare
#include "vect.h", y
compris dans vect.cpp
Fonctions : dclarations dans
le .h, dnitions dans le .cpp
Types : dnitions dans le .h
Ne dclarer dans le .h que les
fonctions utiles.
#pragma once au dbut du
chier.
Ne pas trop dcouper...
STL
min,max,...
complex<double> z;
pair<int,string> p;
p.first=2;
p.second="hop";
#include<list>
using namespace std;
...
list<int> l;
l.push_front(1);
...
if (l.find(3)!=l.end())
...
list<int>::const_iterator
it;
for (it=l.begin();
it!=l.end();it++)
s+=
*
it;
list<int>::iterator it
for (it=l.begin();
it!=l.end();it++)
if (
*
it==2)
*
it=4;
stack, queue, heap, map,
set, vector, ...
184
12. En vrac (suite) ... 12.7. Fiche de rfrence
Fiche de rfrence (4/6)
Template
Fonctions :
// A mettre dans LE
// fichier qui lutilise
// ou dans un .h
template <typename T>
T maxi(T a,T b) {
...
}
...
// Le type est trouv
// tout seul!
maxi(1,2); //int
maxi(.2,.3); //double
maxi("a","c");//string
Objets :
template <typename T>
class paire {
T x[2];
public:
paire() {}
paire(T a,T b) {
x[0]=a;x[1]=b;
}
T somme()const;
};
...
template <typename T>
T paire<T>::somme()const{
return x[0]+x[1];
}
...
// Le type doit tre
// prcis!
paire<int> a(1,2);
int s=a.somme();
paire<double> b;
...
Multiples :
template <typename T,
typename S>
class hop {
...
};
...
hop<int,string> A;
...
Entiers :
template <int N>
class hop {
..
};
...
hop<3> A;
hop<5> B;
...
Entres/Sorties
#include <iostream>
using namespace std;
...
cout <<"I=" <<i <<endl;
cin >> i >> j;
#include <fstream>
using namespace std;
ofstream f("hop.txt");
f << 1 << << 2.3;
f.close();
ifstream g("hop.txt");
if (!g.is_open()) {
return 1;
}
int i;
double x;
g >> i >> x;
g.close();
do {
...
} while (!(g.eof());
ofstream f;
f.open("hop.txt");
double x[10],y;
ofstream f("hop.bin",
ios::binary);
f.write((const char
*
)x,
10
*
sizeof(double));
f.write((const char
*
)&y,
sizeof(double));
f.close();
ifstream g("hop.bin",
ios::binary);
g.read((char
*
)x,
10
*
sizeof(double));
g.read((const char
*
)&y,
sizeof(double));
g.close();
string nom;
ifstream f(nom.c_str());
#include <sstream>
using namespace std;
stringstream f;
// Chane vers entier
f << s;
f >> i;
// Entier vers chane
f.clear();
f << i;
f >> s;
ostream& operator<<(
ostream& f,
const point&p) {
f<<p.x<< << p.y;
return f;
}
istream& operator>>(
istream& f,point& p){
f>>p.x>>p.y;
return f;
}
Conseils
Travailler en local
Imagine++ Project
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 chiers spars.
Le .h doit sufre lutilisa-
teur (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 ob-
jets !
Penser interface / implmen-
tation / utilisation.
185
12.7. Fiche de rfrence 12. En vrac (suite) ...
Fiche de rfrence (5/6)
Divers
i++;
i--;
i-=2;
j+=3;
j=i%n; // Modulo
#include <cstdlib>
...
i=rand()%n;
x=rand()/
double(RAND_MAX);
#include <ctime>
...
srand((unsigned int)
time(0));
#include <cmath>
double sqrt(double x);
double cos(double x);
double sin(double x);
double acos(double x);
#include <string>
using namespace std;
string s="hop";
char c=s[0];
int l=s.size();
if (s1==s1) ...
if (s1!=s2) ...
if (s1<s2) ...
size_t i=s.find(h),
j=s.find(h,3);
k=s.find("hop");
l=s.find("hop",3);
a="comment";
b="a va?";
txt=a+" "+b;
s1="un deux trois";
s2=string(s1,3,4);
getline(cin,s);
getline(cin,s,:);
const char
*
t=s.c_str();
#include <ctime>
s=double(clock())
/CLOCKS_PER_SEC;
#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 :
set(i,1) : i|=(1<<n)
reset(i,1) : i&=~(1<<n)
test(i,1) : if (i&(1<<n))
ip(i,1) : i^=(1<<n)
Erreurs frquentes
Pas de dnition de fonction
dans une fonction!
int q=r=4; // NON!
if (i=2) // NON!
if i==2 // NON!
if (i==2) then // NON!
for (int i=0,i<100,i++)
// NON!
int f() {...}
...
int i=f; // NON!
double x=1/3; // NON!
int i,j;
double x;
x=i/j; // NON!
x=double(i/j); //NON!
double x[10],y[10];
for (int i=1;i<=10;i++)
y[i]=2
*
x[i]; //NON
int n=5;
int t[n]; // NON
int f()[4] { // NON!
int t[4];
...
return t; // NON!
}
int t[4]; t=f();
int s[3]={1,2,3},t[3];
t=s; // NON!
int t[2];
t={1,2}; // NON!
struct Point {
double x,y;
} // NON!
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!
class point {
int x,y;
public:
...
};
...
point a={2,3}; // NON
Oublier de rednir le
constructeur vide.
point p=point(1,2);//NON
point p(1,2); // OUI
obj
*
t=new obj[n];
...
delete t; // manque []
//NON!
void f(int a=2,int b);
void f(int a,int b=0);
void f(int a);// NON!
Ne pas tout mettre inline!
int f() {
...
}
...
f()=3; // HORREUR!
int& f() {
int i;
return i;
}
...
f()=3; // NON!
if (i>0 & i<n) // NON
if (i<0 | i>n) // NON
if (...) {
...
if (...)
break; // Non,
// boucles seulement
}
for (i ...)
for (j ...) {
...
if (...)
break;//NON, quitte
// juste la boucle j
int i;
double x;
...
j=max(i,0);//OK
y=max(x,0);//NON!
// 0.0 et non 0: max
// est un template STL
Imagine++
Voir documentation...
186
12. En vrac (suite) ... 12.8. Devoir nal
Fiche de rfrence (6/6)
Tableaux
Dnition :
double x[10],y[10];
for(int i=0;i<10;i++)
y[i]=2
*
x[i];
const int n=5;
int i[n],j[2
*
n]; //OK
Initialisation :
int t[4]={1,2,3,4};
string s[2]={"ab","c"};
Affectation :
int s[3]={1,2,3},t[3];
for (int i=0;i<3;i++)
t[i]=s[i];
En paramtre :
void init(int t[4]) {
for(int i=0;i<4;i++)
t[i]=0;
}
void init(int t[],
int n) {
for(int i=0;i<n;i++)
t[i]=0;
}
Taille variable :
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];
A[i+2
*
j]=...;
Taille variable (suite) :
int
*
t,
*
s,n;
En paramtre (n) :
void f(const int
*
t,
int n) {
...
s+=t[i]; // OK
...
t[i]=...; // NON!
}
12.8 Devoir nal
Vous pouvez enn vous confronter aux devoirs complets proposs en annexe, par
exemple B.22 et B.21.
187
13. Structure de donnes
Chapitre 13
Structure de donnes
Les ordinateurs sont l pour nous affranchir des tches fastidieuses ou rptitives. Mais les
rptitions se retrouvent mme dans nos programmes. Ainsi, nous sommes souvent amens
rassembler une collection de donnes dans un tableau. Nous savons dj utiliser les tableaux
C++, mais notre usage en est primitif : ce ne sont pas des classes, donc nous devons tout faire
la main. En fait, il ny a pas une structure canonique satisfaisant tout le monde. Suivant
lusage quon en fait, une manire de grer la collection peut tre prfrable une autre. Ce
problme se rencontre si frquemment que le C++ propose par dfaut des classes. Pour la plus
grande gnricit possible, ces classes sont template et font partie de la STL, dont nous avons
dj rapidement parl. Ce chapitre na pas pour but de documenter les dtails de ces classes,
mais plutt dexpliquer les forces et faiblesses de chacune et la faon dont elles ont pu tre
implmentes.

13.1 Rappels sur les tableaux


Nous connaissons maintenant bien les tableaux, mais rappelons quelques caract-
ristiques importantes :
les tableaux sont de taille xe 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 lexcu-
tion, 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 sufsante. On ne peut pas
non plus demander sa taille un tableau... Mme si cest sufsant 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 pour-
quoi 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 efcace que les autres.
13.2. La complexit 13. Structure de donnes
13.2 La complexit
Avant de regarder de plus prs les diffrentes classes, il convient de prciser ce
quon entend par complexit.
13.2.1 Mais quest-ce donc que la complexit ?
Cest le nombre doprations quil faut effectuer pour rsoudre un problme et la
taille de la mmoire ncessaire. On parle de complexit en temps dans le premier cas et
de complexit en espace dans le second. On est souvent amen trouver un quilibre
entre les deux, quand on ne peut pas rduire lun et lautre. Dans ce cas le principe g-
nral est le suivant : la mmoire est bon march, le temps est prcieux. Plus concrte-
ment, ajouter de la mmoire une machine est facile, par contre la puissance de calcul
est limite (et ajouter des processeurs nest pas une option viable en gnral, car il
faut rcrire le programme pour lexploiter et beaucoup de problmes ne sont pas ou
difcilement paralllisables). Prenons un exemple simple : tracer lhistogramme dune
image. Voici une faon de faire :
i nt hi s t o [ 2 5 6 ] ;
f or ( i nt i =0; i < 256; i ++)
hi s t o [ i ] = 0;
f or ( i nt i =0; i < image . hei ght ( ) ; i ++)
f or ( i nt j =0; j < image . width ( ) ; j ++)
++ hi s t o [ image ( j , i ) ] ;
f or ( i nt i =0; i < 256; i ++)
drawRect ( i , 0 , 1 , hi s t o [ i ] ) ;
On voit quen plus de parcourir deux fois le tableau histo (ce qui ne dpend pas de la
taille de limage), on ne lit quune fois chaque pixel de limage. Sa complexit est donc
de lordre de N, le nombre de pixels. Considrons maintenant une variante :
f or ( i nt c =0; c < 256; c ++) {
i nt h=0;
f or ( i nt i =0; i < image . hei ght ( ) ; i ++)
f or ( i nt j =0; j < image . width ( ) ; j ++)
i f ( image ( j , i ) == c )
++h ;
drawRect ( i , 0 , 1 , h ) ;
}
La diffrence est quon a conomis en mmoire un tableau de 256 entiers. Du coup,
on lit chaque pixel de limage 256 fois. On a une complexit en temps de 256N, soit un
facteur 256 par rapport limplmentation prcdente. On prfrera sans ambigut la
premire solution.
Nous considrerons ici surtout la complexit en temps, car la mmoire nest pas un
facteur dterminant dans les problmes que nous traiterons.
190
13. Structure de donnes 13.2. La complexit
13.2.2 Comment la mesure-t-on?
13.2.3 La notation O
La question de la complexit se pose quand on a des problmes de grande dimen-
sion. 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 linni. On utilise dans ce cas la notation commode O(f(N)) o f est une
fonction croissante. Cela signie 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 algo-
rithmes 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) signie 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 don-
nes. 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 =
10
7
.
O(N
2
) est un algorithme quadratique. Il peut rester acceptable pour des N pas
trop grands.
O(2
N
) est une complexit exponentielle (f(N) = e
N log 2
). Cest un algorithme qui
nest pratique que pour des problmes de petite dimension.
13.2.4 P et NP
Nous nentrerons pas dans des considrations dinformatique thorique, mais beau-
coup ont entendu parler de la question P = NP? 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 NP 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 NP ne sont pas solubles
exactement en temps raisonnable
3
lorsque N est grand. Mais cela ne veut pas dire
quon ne peut pas trouver une solution approche en temps polynmial.
1. Il sagit souvent du logarithme en base 2, mais celui-ci ne diffrant du logarithme nprien que
par un facteur constant, nous navons pas besoin de prciser dans la notation O
2. Fast Fourier Transform, notez le Fast
3. ou plus exactement sils le sont, on ne sait pas comment !
191
13.3. Le vecteur : un tableau encapsul dans une classe 13. Structure de donnes
13.2.5 Pourquoi est-ce important ?
Un algorithme exponentiel est inutilisable pour de grandes donnes. Mais parmi
les algorithmes polynmiaux, on privilgiera celui de complexit minimale, car il nous
fera gagner du temps
4
. De nombreuses architectures ajustent leur frquence en fonc-
tion de lutilisation du CPU, donc consomment moins dnergie quand le processeur a
peu faire, comme par exemple la technologie SpeedStep chez Intel. Cela a des cons-
quences sur la batterie de votre ordinateur portable, sur la quantit dnergie consom-
me, sur le dgagement de chaleur du processeur, sur le bruit produit suivant que le
ventilateur se met en marche... Utilisons donc des algorithmes efcaces, que le proces-
seur puisse se remettre rapidement en mode basse frquence, cela participe la lutte
contre le rchauffement climatique !
Attention, il faut quand mme se garder de la tentation de vouloir optimiser trop
tt. Il faut dabord identier la partie coteuse en temps, et elle nest pas toujours o
lon croit, et chercher optimiser celle-ci. Car malheureusement les algorithmes ef-
caces en temps sont souvent plus complexes du point de vue de la programmation et
le temps gagn en calcul est perdu en temps de programmation, traque des bugs et
maintenance.
13.3 Le vecteur : un tableau encapsul dans une classe
13.3.1 Usage
Il sagit de la classe std :: vector (ou vector aprs using namespace std;) et il sagit
dun tableau encapsul tout simplement dans une classe, une diffrence de taille : il se
charge de grer sa mmoire et lajuste dynamiquement la demande. On lobtient par
un #include <vector>. Un vecteur de double se construit ainsi et ajouter les lments
se fait par la mthode push_back :
vect or <double> vect ;
vect . push_back ( 3 . 1 4 1 6 ) ;
vect . push_back ( 2 . 7 1 8 ) ;
cout <<vect . s i ze ( ) <<" el ements : " <<vect [ 0 ] <<" " <<vect [ 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 dnition de loprateur [] pour la classe vector.
Il y a cependant des erreurs viter :
st d : : vect or <double> vect ;
vect [ 0 ] = 3. 14 16; / / NON, v e c t [ 0 ] n e x i s t e pas
vect . push_back ( 2 . 0 ) ; / / OK, v e c t [ 0] ==2
vect [ 0 ] = 3. 14 16; / / C e s t bon mai nt enant , v e c t [ 0 ] e x i s t e
Loprateur = et le constructeur par recopie sont dnis comme il faut, mais justement,
il faut viter de lappeler inutilement :
4. et notre temps sur Terre est limit...
192
13. Structure de donnes 13.4. La pile, Last In First Out (LIFO)
void pr i nt ( st d : : vect or <double> vect ) { / / Ev i t e r : c o ns t r . par c o p i e
f or ( i nt i =0; i < vect . s i ze ( ) ; i ++)
st d : : cout << vect [ i ] << " " ;
st d : : cout << st d : : endl ;
}
Le paramtre vect de la fonction print est pass par valeur, ce qui appelle donc un
constructeur par recopie. Celui-ci alloue la mmoire ncessaire et recopie le paramtre
pass en entre de print, ce qui est inutile. Il vaut bien mieux passer par rfrence,
mais constante pour bien indiquer au compilateur quon ne souhaite pas modier le
tableau :
void pr i nt ( const st d : : vect or <double>& vect ) { / / Tr s b i e n
. . .
13.3.2 Complexit
Intressons-nous la complexit des oprations lmentaires sur le vecteur. Lop-
ration lmentaire est ici le nombre de lectures ou dcritures dans une case du tableau.
Lajout dun lment la n 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 Ni, donc jusqu O(N). Le retrait
dun lment (erase) galement. Par contre la lecture ou lcriture dans une case quel-
conque du tableau se fait en O(1). On voit que le vecteur est particulirement efcace
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 pour-
tant crucial pour son efcacit. 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 log
2
N
allocations mmoires. Par contre, si la mmoire est pleine et quon na besoin de najou-
ter quun lment, on aura gach N 1 emplacements mmoire, mais cest un choix
assum.
13.4 La pile, Last In First Out (LIFO)
Rappelons le principe de la pile : le dernier lment ajout est le premier enlev. On
voit que la pile simplmente tout fait naturellement avec un vecteur. Le haut de la
pile est le dernier lment, celui dindice size()1, quon peut aussi consulter avec la
mthode back(). Lajout dun lment se fait donc par push_back, et le retrait du haut
5. mais il est toujours possible de trier le vecteur aprs lavoir rempli, voir le chapitre suivant
193
13.5. La le, First In First Out (FIFO) 13. Structure de donnes
1 2 3 4 5
push(5) pop()
1 2 3 4 1 2 3 4 5
debut fin fin debut fin debut
FIGURE 13.1 Fonctionnement dune le. Gauche : le contenu de la le 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 spcique
et standard de la pile, la STL propose quand mme std :: stack :
st d : : st ack <double> s ;
s . push ( 3 . 1 4 1 6 ) ;
s . push ( 2 . 7 1 8 ) ;
cout << s . top ( ) << st d : : endl ; / / Af f i c h e 2. 718
s . pop ( ) ;
cout << s . top ( ) << st d : : endl ; / / Af f i c h e 3. 1416
s . pop ( ) ;
as s e r t ( s . s i ze ( ) ==0 ) ; / / Ou b i e n a s s e r t ( s . empty ( ) ) ;
Pour raison defcacit
6
, il y a une lgre diffrence avec linterface de pile que
nous avons dj rencontre : la mthode pop ne renvoie pas llment en haut de la pile,
en fait elle ne renvoie rien (void). Cet lment est donc perdu aprs lappel pop,
mais on pouvait toujours la rcuprer en appelant pop juste avant.
13.5 La le, First In First Out (FIFO)
Contrairement la pile, la le correspond la le dattente habituelle : premier
arriv, premier servi ! Nous pouvons stocker les lments dans un vecteur, mais si
le push_back est en O(1), le pop_front (cette mthode nexiste pas en fait) serait
en O(N). En dautres termes, le push de la le serait en O(1) et le pop en O(N). Ce
nest donc pas une bonne solution. Par contre, aprs avoir retir le premier lment, il
suft de se souvenir que le nouveau premier se trouve en fait tre le deuxime etc. Ce
principe est illustr Fig. 13.1. Voici une possibilit dimplmentation simplie :
c l a s s Fi l e {
st d : : vect or <double> v ;
i nt debut , f i n ;
i nt modulo( i nt i ) const ;
publ i c :
Fi l e ( ) ;
bool empty ( ) const ;
double f r ont ( ) const ;
void push( double d ) ;
double pop ( ) ;
} ;
6. En effet cela obligerait crer une variable locale dans la mthode pour y recopier la valeur en haut
de pile et retourner cette valeur lutilisateur, donc une deuxime recopie. Cela nest gure troublant
pour un type de base, mais devient plus gnant pour un type complexe, par exemple une image.
194
13. Structure de donnes 13.5. La le, First In First Out (FIFO)
/ / Co ns t r uc t e ur
Fi l e : : Fi l e ( ) {
debut=f i n =0;
}
/ / I ndi q ue s i l a f i l e e s t vi de
bool Fi l e : : empty ( ) const {
ret urn ( debut==f i n ) ;
}
/ / T t e de l a f i l e , pr o c ha i n c l i e n t
double Fi l e : : f r ont ( ) const {
ret urn v[ debut ] ;
}
/ / Ar i t hm t hi que modul o v . s i z e ( )
i nt Fi l e : : modulo( i nt i ) const {
i f ( v . capaci t y ( ) == 0)
ret urn 0;
ret urn ( i%v . capaci t y ( ) ) ;
}
/ / Aj out d un l me nt
void Fi l e : : push( double d) {
i nt f i n2 = modulo( f i n +1) ;
i f ( f i n2==debut ) { / / Le s e r p e nt s e mord l a queue !
st d : : vect or <double> v2 ; / / Re c o pi e du t a b l e a u
f or ( i nt i =debut ; i ! = f i n ; i =modulo( i +1) )
v2 . push_back ( v[ i ] ) ;
v = v2 ;
v . r eser ve ( v . capaci t y ( ) +2 ) ;
debut =0; / / On r e me t l a t t e l i n d i c e 0
f i n2 = v . s i ze ( ) +1 ;
}
i f ( f i n == v . s i ze ( ) )
v . push_back ( d ) ;
e l s e
v[ f i n ] = d;
f i n = f i n2 ;
}
/ / Re t r a i t de l l me nt de t t e
double Fi l e : : pop ( ) {
double d = f r ont ( ) ;
debut = modulo( debut +1) ;
ret urn d;
}
Notons que :
195
13.6. La liste chane 13. Structure de donnes
On utilise les mthodes reserve et capacity qui concernent le nombre dlments
dans le tableau allou (qui peut tre plus grand que le nombre dlments rels).
On fait toutes les oprations modulo v.capacity(), ce qui permet de rutiliser les
cases libres par un pop() prcdent et conomise de la mmoire. Ceci est fait
via la mthode modulo qui na pas besoin dtre publique puisque cest un dtail
dimplmentation.
La mthode push est complique par le fait quelle doit rinitialiser un tableau
plus grand si v est plein. Si cest le cas, on rinitialise la tte lindice 0 du tableau.
Nous avons prsent cette implmentation pour un tableau de double. Bien en-
tendu, une implmentation gnrique serait template, mais alors moins lisible.
La variable n indique lindice suivant la queue. Cet indice doit tre infrieur
v.capacity(), do lutilisation de reserve.
Limplmentation est un peu technique, mais montre quil est possible de pro-
grammer la le efcacement en une cinquantaine de lignes de code : push et pop
sont en O(1).
Il existe une classe template std :: deque (pour double-ended queue) qui implmente
compltement une le, bien que la gestion de la mmoire soit diffrente de celle pro-
pose ici.
13.6 La liste chane
Les structures que nous avons vues jusquici ntaient efcaces que pour les ajouts
et retraits dlments au dbut et/ou n 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 uc t chainon {
i nt prev , next ;
double val ;
} ;
Pour insrer llement d quon stocke lindice j juste aprs llment stock lin-
dice i, il suft 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 suft de reconnecter ensemble les suivant et prc-
dent :
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 n de liste. Cette faon de faire laisse
des trous dans le tableau t. supposant que lindice du premier lment est i0, pour
trouver llment list[i] on doit faire
196
13. Structure de donnes 13.7. Rcapitulatif des complexits
i nt i i = i 0 ;
f or ( i nt j =0; j < i ; j ++)
i i = t [ i i ] . next ;
double d = t [ i i ] . val ;
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 suft
de savoir quun pointeur indique o trouver un lment en mmoire. La STL fournit
une classe template std :: list qui implmente une liste.
13.7 Rcapitulatif des complexits
vecteur pile le liste
push_back O(1) O(1)
1
O(1)
1
O(1)
pop_back O(1) O(1)
2
n/a O(1)
push_front O(N)
3
n/a n/a O(1)
pop_front O(N)
3
n/a O(1)
2
O(1)
tab[ i ] O(1) n/a
4
n/a
4
O(N)
insert O(N) n/a n/a O(1)
erase O(N) n/a n/a O(1)
1
push_back sappelle en fait push pour pile et le
2
pop_back/pop_front sappellent en fait pop pour pile et le
3
Ces mthodes ne font pas partie de linterface du vecteur mais la fonctionnalit qui-
valente peut tre obtenue par insert ou erase.
4
Ces mthodes ne font pas partie de linterface de la le ou de la pile, mais peuvent
tre ajoutes avec complexit O(1) dans limplmentation que nous avons vue.
13.8 Les itrateurs
En ralit, pour indiquer un emplacement dans le tableau, comme par exemple
pour linsertion ou le retrait dun lment, les classes de la STL utilisent des itra-
teurs. Ceux-ci permettent dnumrer les lments ou de dsigner un emplacement.
Ils sont assez similaires des pointeurs. Lintrt de les utiliser cest quil nest pas n-
cessaire de dnir tous les algorithmes sur toutes les classes. Il suft de faire une fois
lalgorithme et daccder aux lments du tableau uniquement par lintermdiaire des
itrateurs, et non par un indice. Les itrateurs servent donc surtout de lien entre les
structures et les algorithmes quon peut leur appliquer. Voici un exemple dutilisation
de ces itrateurs :
st d : : vect or <double >: : c ons t _ i t e r a t or i t =vect . begi n ( ) ;
f or ( ; i t ! = vect . end ( ) ; ++ i t )
st d : : cout << i t << " " ;
st d : : cout << st d : : endl ;
La mthode begin renvoie un itrateur sur le premier lment du tableau et end pointe
une case plus loin que le dernier lment (donc attention de ne pas essayer daccder
llment point par cet itrateur). Loprateur ++ fait passer llment suivant et
loprateur * retourne llment point par litrateur (ici un double).
197
13.9. Autres structures 13. Structure de donnes
Si on veut modier les lments, il faut utiliser iterator au lieu de const_iterator,
mais cela nest possible que si vect nest pas const.
13.9 Autres structures
Dautres structures trs classiques car souvent utiles ont t passes sous silence
dans ce chapitre. Elles ne sont pas toutes proposes par dfaut dans la STL, mais cer-
taines 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 efcacement la valeur
associe une cl. La STL en propose une implmentation avec std :: map.
La table de hachache, qui permet une recherche efcace pourvu quon ait une
mthode pour grouper les objets en un petit nombre de classes ordonnes, typi-
quement une fonction associant un nombre entier chaque lment.
La le de priorit, qui maintient un ensemble ordonn mais autorise linsertion
ou le retrait efcaces dlments. Nous en parlerons dans le prochain chapitre.
Les graphes, forms de sommets et dartes (paires de sommets). Beaucoup de
problmes se formulent facilement avec des graphes.
Les arbres, qui sont des graphes particuliers.
198
14. Algorithmes de tri
Chapitre 14
Algorithmes de tri
Maintenant que nous savons ranger intelligemment nos donnes (voir le chapitre prc-
dent), 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 en-
tre 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 ta-
bleaux de N/2 lments. Nous verrons que cest ce qui se passe en gnral avec Quick-
Sort, et que la complexit rsultante est O(N log N).
En fait, cela ne signie 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 pos-
sibles, on peut trs bien faire un histogramme en O(N). En supposant que les valeurs
possibles sont les entiers entre 0 et 255 :
i nt hi s t o [ 2 5 6 ] ;
f or ( i nt i =0; i < 256; i ++)
hi s t o [ i ] = 0;
f or ( i nt i =0; i < N; i ++)
++ hi s t o [ t ab [ i ] ] ;
i nt k=0;
f or ( i nt i =0; i < 256; i ++)
f or ( i nt j =0; j < hi s t o [ i ] ; j ++)
t ab [ k++] = i ;
14.2 Algorithmes quadratiques
Il est extrment simple dimaginer un algorithme quadratique de tri. Par exemple,
on peut chercher la valeur minimale v
0
dans le tableau en O(N) (et on compte son
14.3. QuickSort 14. Algorithmes de tri
nombre n
0
doccurences). Puis on cherche la valeur minimale v
1
parmi les lments
qui restent et ainsi de suite. On parcourt donc N fois le tableau et la complexit est
donc en O(N
2
). Notons la ressemblance avec le tri par histogramme propos ci-dessus.
La seule diffrence est que les valeurs v
0
, v
1
, etc. ne sont pas connues lavance et
doivent donc tre trouves.
En fait, un algorithme encore plus simple programmer et bien connu est le tri
bulles. Chaque valeur reprsente le poids dune bulle et on voit les bulles les plus
lgres remonter (ou plutt aller vers le dbut du tableau). On parcourt le tableau et
on permute un lment avec son suivant sils ne sont pas dans lordre. On itre N fois
cette boucle et en n de compte le tableau est tri :
f or ( i nt i =0; i < N; i ++)
f or ( i nt j =0; j +1 < N; j ++)
i f ( t ab [ j ] > t ab [ j +1] )
swap( t ab [ j ] , t ab [ j +1 ] ) ;
Cet algorithme fait clairement N(N 1) soit O(N
2
) comparaisons. En fait on peut faire
mieux en observant que dans la boucle dindice j, ds quon arrive sur llment le
plus grand, on le fait descendre jusquau bout du tableau. Donc aprs un premier par-
cours, llment le plus grand est en bout et donc sa position nale. Il est donc inutile
par la suite de comparer tab[N2] et tab[N1], et ainsi de suite. Nous pouvons donc
remplacer la boucle en j par
f or ( i nt j =0; j +i +1 < N; j ++)
. . .
On fait ainsi (N 1) + (N 2) + ... + 1 comparaisons, soit N(N 1)/2. Avec cette
modication modeste, nous avons divis par 2 la complexit, mais cela reste du O(N
2
).
De nombreux autres algorithmes simples, tels que le tri par insertion ou le tri par
slection, sont simplement des variantes et restent en O(N
2
).
14.3 QuickSort
Nous allons tudier la complexit de QuickSort et constater quelle est optimale.
Rappelons brivement le principe :
1. Nous slectionnons un lment, appel pivot, et cherchons sa place dnitive
dans le tableau nal.
2. Ceci spare le tableau en deux parties, la gauche et la droite du pivot, et nous
itrons sur chacun de ces sous-tableaux.
Ltape cruciale est de trouver la position du pivot, ce qui peut se faire en N oprations
de la faon suivante :
Nous maintienons un compteur i partant du dbut du tableau et lincrmentons
tant que t[i] est infrieur au pivot.
Nous avons un deuxime compteur j qui part de la n et que nous dcrmentons
tant que t[j] est suprieur au pivot.
Si i < j, nous permutons t[i] et t[j] et continuons la progression des indices.
Lorsque i == j, la place du pivot est i.
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.
200
14. Algorithmes de tri 14.3. QuickSort
Nous voyons que cette tape du pivot ncessite un parcours du tableau, donc N1
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 cal-
culer. Ce sont ces dernires quil faut chercher minimiser. Si nous notons C
N
la com-
plexit et que nous nous retrouvons avec un pivot en position i, nous avons la relation
de rcurrence :
C
N
= (N 1) + C
i
+ C
Ni1
Lindice i dpend des donnes du tableau, mais nous pouvons supposer quen moyenne
il sagit du milieu du tableau. Nous crivons donc
C
N
= N + 2C
N/2
.
En utilisant la relation de rcurrence pour C
N/2
, nous obtenons
C
N
= N + 2(N/2 + 2C
N/4
) = 2N + 4C
N/4
.
En allant un cran plus loin, il vient
C
N
= 3N + 8C
N/8
.
Le processus sarrte lorsque lindice de C droite de la complexit devient 1, et alors
C
N
= (log N)N + NC
1
.
Le rsultat est donc C
N
= O(N log N). Pour le vrier rigoureusement, il faut en fait
montrer par rcurrence que pour N une puissance de 2, la suite C
N
= N log
2
N satisfait
la relation de rcurrence
C
2N
= 2N + 2C
N
,
ce qui nest pas difcile.
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
C
N
= (N 1) + C
N1
.
Si cela se reproduit chaque tape, le rsultat devient
C
N
= (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 in-
verse. Nous voyons que ce cas nest pas meilleur quun tri bulles. Malheureusement,
ce cas le pire nest pas si rare en pratique. Deux techniques courantes sont utilises
pour viter ce cas dfavorable :
1. Choisir comme pivot un lment tir au hasard dans le tableau plutt que le
premier ou dernier.
2. Choisir comme pivot llment du milieu (mdian) entre le permier, le dernier et
llment la moiti du tableau.
201
14.4. File de priorit et HeapSort 14. Algorithmes de tri
10
7 8
5 3 7
9 10
7 8
5 3 7 9
9
10
7
5 3 7
8
FIGURE 14.1 Fonction push dans la le de priorit
Ce choix de pivot se fait en temps constant, donc a un cot ngligeable dans le cas
normal o il serait inutile. Cela nest vrai que lorsquon peut accder un lment
quelconque en O(1), ce qui est valable pour un tableau ordinaire ou un vecteur, mais
faux pour une liste, comme nous lavons vu au chapitre prcdant.
Il est possible de se demander ce quil en est entre ces deux extrmes : pivot au
milieu ou pivot une extrmit. En fait il est possible de montrer que la complexit
moyenne est O(N log N), mme si le cas le pire reste O(N
2
). QuickSort est implment
par la fonction std :: sort de la STL (utiliser #include <algorithm>).
Nous allons voir quen fait il existe un algorithme de tri, HeapSort, qui reste optimal
dans le pire cas O(N log N).
14.4 File de priorit et HeapSort
Nous avons vu la le FIFO au chapitre prcdant. En fait, nous allons modier cette
le 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 le trie par ordre de priorit. En fait, ce nest pas pratique :
1. Si on peut accder un lment arbitraire de la le 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 suft 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 vriant le principe suivant est bien adapte : tout parent a une priorit
au moins aussi grande que ses deux enfants. Il est alors facile de voir que la racine est
llment de plus grande priorit. Voyons dabord comment insrer un lment.
14.4.1 Insertion dans la le de priorit (push)
Nous commenons par ajouter le nouvel lment comme feuille de larbre pro-
fondeur 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 log
2
N et donc O(log N) comparaisons sont ncessaires.
202
14. Algorithmes de tri 14.4. File de priorit et HeapSort
10
7
5
6
4
3
6
5
6
4
7
3
6
7
5
6
4 6
3
5
6
4 6
7
3
FIGURE 14.2 Fonction pop dans la le de priorit
14.4.2 Retrait de la le de priorit (pop)
Llment en tte est la racine de larbre. Nous retirons llment la dernire feuille
de larbre pour la mettre la racine et rparons larbre : si la racine a plus faible priorit
que le plus grand des (un ou deux) enfants, nous permutons les deux et comparons
aux deux nouveaux enfants et ainsi de suite (Fig. 14.2). Le processus sarrte lorsque
nous atteignons une feuille ou bien lorsque llment a plus grande priorit que ses en-
fants. Larbre tant quilibr, nous descendons llment au plus jusqu la profondeur
maximale, soit O(log N).
14.4.3 Stockage de la le de priorit
Pour stocker cet arbre binaire quilibr, il est trs efcace dutiliser un simple ta-
bleau. Pour une fois, nous considrerons que le premier lment du tableau est din-
dice 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 Pr i o r i t e : : push( double d) {
i nt i =s i ze ( ) ;
v . push_back ( d ) ;
whi le ( i >1 && v[ i ] > v[ i /2] ) {
st d : : swap( v[ i ] , v[ i /2 ] ) ;
i /= 2;
}
}
La fonction pop est lgrement plus complique :
double F i l e Pr i o r i t e : : pop ( ) {
double d = v [ 1 ] ;
v [ 1 ] = v . back ( ) ;
v . pop_back ( ) ;
i nt i = 1;
whi le ( 2 i < v . s i ze ( ) ) {
i nt j = i ; / / On c h e r c h e l e max de s e nf a nt s
i f ( v[ 2 i ] > v[ j ] ) / / Gauche
j = 2 i ;
i f ( 2 i +1 < v . s i ze ( ) && v[ 2 i +1] > v[ j ] ) / / Dr o i t e
j = 2 i +1;
i f ( i ==j ) break ;
203
14.5. Conclusion 14. Algorithmes de tri
st d : : swap( v[ i ] , v[ j ] ) ;
i = j ;
}
ret urn d;
}
14.4.4 HeapSort
Le principe de HeapSort est juste dutiliser la le de priorit, appele heap en an-
glais : les lments sont ajouts dans la le puis retirs les uns aprs les autres.
double HeapSort ( st d : : vect or <double>& v) {
F i l e Pr i o r i t e f ;
f or ( i nt i =0; i < v . s i ze ( ) ; i ++)
f . push( v[ i ] ) ;
f or ( i nt j =v . s i ze ( ) 1; j >= 0; j )
v[ j ] = f . pop ( ) ;
}
Mettre les N lments dans la le ncessite O(N log N) comparaisons et les retirer ga-
lement 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 supplmen-
taire pour la le de priorit et le cas le pire de QuickSort peut tre rendu trs impro-
bable 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. Rpertoires Windows :
Identier (sous "Poste de travail") les deux disques locaux (C: et D:) et les disques
rseau (H: et Z:). On travaillera en local sur le bureau qui se trouve en ralit
dans D:\Documents and Settings\login\Bureau.
Tout essai de travail directement dans H : ou Z : est cause de lenteur, de
saturation du rseau et peut entraner une perte de chier si vos quotas
disque sont atteints.
On vitera par contre de saturer le bureau en dplaant ses programmes dans Z:
en n de sance. Le principe est donc le suivant :
(a) Connexion.
(b) Copie ventuelle dun travail prcdent de Z: sur le bureau.
(c) Travail sur le bureau.
(d) Recopie du travail vers Z: (la DSI a la responsabilit de la sauvegarde de
Z:, mais pas de H:)
(e) Dconnexion.
3. Projet :
Tlcharger larchive Tp1_Initial.zip sur la page du cours, la dcompresser
sur le bureau. Observer le chier CMakeLists.txt qui dcrit le projet. Jusqu
la ligne FIND_PACKAGE il sagit dinstructions standard pour utiliser Imagine++,
et nous les retrouverons systmatiquement. La ligne suivante est celle qui nous
intresse vraiment :
ImagineAddExecutable ( Tp1 Tp1 . cpp)
A.1. Lenvironnement de programmation A. Travaux Pratiques
Elle indique que le programme sappellera Tp1 (Tp1.exe sous Windows) et que
le code source pour le construire est dans le chier Tp1.cpp. Crer un projet Tp1
sur le bureau.
Visual C++ voudra travailler dans "Mes Documents". Pour ne pas sur-
charger les transferts, on travaillera plutt par aller-retour entre Z : et
le bureau.
(a) Visual (donc sous Windows) nest pas capable dutiliser un tel format, 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 chiers gn-
rs des sources : slectionner dans la ligne suivante un nouveau rpertoire
"Build". Cliquer "Congure", slectionner le bon gnerateur (Visual Studio 9,
qui correspond 2008) et clicker le bouton "Finish". Ceci tant fait, le bouton
"Generate" doit tre activable, le clicker. Si tout sest bien pass, on peut fer-
mer Cmake et ouvrir le chier "solution" (extension .sln) dans le rpertoire
Build, ce qui lance "Microsoft Visual C++ Express 2008"
(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.
4. Programmation :
(a) Rajouter cout << "Hello" << endl; sur la ligne avant return 0;
5. Gnration :
(a) Dans la fentre "Solution explorer" de Visual Studio, rechercher et afcher le
chier Tp1.cpp.
(b) "Build/Build solution", ou "F7" ou bouton correspondant.
(c) Vrier lexistence dun chier Tp1 (en ralit Tp1.exe) dans Build/Tp1/Debug
(sans Debug sous Linux).
6. Excution :
(a) Sous Kdevelop, il faut commencer par aller dans le menu "Congure launches",
ajouter avec le bouton "+" et slectionner lexcutable. On peut alors le lancer
avec le bouton "Execute".
(b) Lancer le programme (sous Visual) avec "Debug/Start Without Debugging"
(ou
"Ctrl+F5" ou bouton correspondant). Il faut lui dabord lui prciser quel
programme lancer (clic droit sur le projet Tp1 dans lexplorateur de solu-
tion, "Slectionner comme projet de dmarrage") Une fentre fonds noir
"console" souvre, dans laquelle le programme sexcute, et la fentre console
reste ouverte jusqu lappui dune touche du clavier.
(c) Vrier quon a en fait cr un programme indpendant quon peut lancer
dans une fentre de commande :
Essayer de le lancer depuis le gestionnaire de chiers standard : le pro-
gramme se referme tout de suite !
Dans les menus Windows : "Dmarrer/Excuter"
"Ouvrir: cmd"
206
A. Travaux Pratiques A.1. Lenvironnement de programmation
Taper "D:",
1
puis
"cd \Documents and Settings\login\Bureau\Tp1\Tp1\Debug"
(apprenez proter de la compltion automatique avec la touche TAB).
Vrier la prsence de Tp1.exe avec la commande "dir".
Taper "Tp1".
Touche utile (Visual) : Ctrl+F5 = = Start without debugging
7. Fichiers :
On a dj suivi la cration des chiers 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 chiers de travail.
Quelle est la taille du rpertoire de Build de Tp1 (clic droit + proprits) ?
8. Nettoyage :
Supprimer les chiers de travail et les rsultats de la gnration avec "Build /
Clean solution" puis fermer Visual Studio. Quelle est la nouvelle taille du
rpertoire ?
9. 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
chiers trop importants peuvent subsister.
La politique de quotas fait quil est indispensable de nettoyer ses solu-
tions quand on quitte. Cest essentiel aussi pour pouvoir envoyer ses
exercices son responsable. De plus, il faut quitter Visual Studio avant
de comprimer.
10. Envoi :
Envoyer le chier par mail son responsable de PC et indiquant bien le nom de
son binme.
11. Sauvegarde :
Recopier aussi larchive comprime sur son compte (disque Z :)
A.1.2 Premires erreurs
1. Modier le programme :
Modier le programme en changeant par exemple la phrase afche.
(a) Tester une nouvelle gnration/excution. Vrier que Visual Studio sauve
le chier automatiquement avant de gnrer.
(b) Modier nouveau le programme. Tester directement une excution. Visual
Studio demande automatiquement une gnration!
Lancer directement une excution sauve et gnre automatique-
ment. Attention toutefois de ne pas conrmer lexcution si la g-
nration sest mal passe.
1. Sur certaines machines, il faut en fait aller sur C :, vriez en regardant o est install votre projet
207
A.1. Lenvironnement de programmation A. Travaux Pratiques
(c) Faire une nouvelle modication. Tester une simple compilation
(Build/Compile ou Ctrl+F7). Cette tape cre juste Tp1.obj partir
de Tp1.cpp sans aller jusqu Tp1.exe. Vrier dans la fentre de com-
mande avec dir que pour le chier Tp1.exe la date est antrieure celle
de Tp1.cpp et Tp1.obj. Vrier quune gnration ne lance plus ensuite
que ldition des liens sans avoir besoin de recompiler.
Touche utile : Ctrl+F7 = = Compile current le (without linking)
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 ler-
reur.
3. Erreur de linker
Il est un peu tt pour russir mettre le linker en erreur. Il est pourtant indis-
pensable 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 chier 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 lins-
tant une erreur de compilation.
(b) Corriger lerreur de compilation en rajoutant une ligne (pour linstant "ma-
gique")
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.
(c) Revenir un programme qui marche et taper mai au lieu de main. En d-
duire le nomde la fonction qui appelle main() sans quon nen soit conscient !
4. Indentations :
Avec toutes ces modications, le programme ne doit plus tre correctement "in-
dent". Cest pourtant essentiel pour une bonne comprhension et reprer dven-
tuelle 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.
208
A. Travaux Pratiques A.1. Lenvironnement de programmation
5. Warnings du compilateur
En modiant 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 fonc-
tion exit () quitte le programme en urgence !)
Il est trs formellement dconseill de laisser passer des warnings ! Il
faut les corriger au fur et mesure. Une option du compilateur propose
mme de les considrer comme des erreurs !
A.1.3 Debugger
Savoir utiliser le debuggeur est essentiel. Il doit sagir du premier rexe en
prsence dun programme incorrect. Cest un vritable moyen dinvestiga-
tion, plus simple et plus puissant que de truffer son programme dinstruc-
tions supplmentaires destines espionner son droulement.
1. Taper le main suivant.
i nt main ( )
{
i nt i , j , k ;
i =2;
j =3 i ;
i f ( j ==5)
k=3;
e l s e
k=2;
ret urn 0;
}
2. Lancer le programme "sous le debuggeur" avec Build/Start ou F5 ou le bou-
ton correspondant. Que se passe-til ?
2. Certains de ces warnings ne se manifestent quen niveau dexigence le plus lev : pour le mettre
en place, clic droit sur le projet dans la fentre de gauche, menu Proprits, onglet C++, slectionner
le warning level 4 (3, moins exigent, est le dfaut).
209
A.1. Lenvironnement de programmation A. Travaux Pratiques
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 va-
leurs 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 n 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 jus-
quau 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. Enn, 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 chier source. Cet afchage est en fait trs utile pour vrier ce que
fait le compilateur et voir sil optimise bien.
Touches utiles :
F5 = = Debug
Maj+F5 = = Stop
F10 = = Step over
F11 = = Step inside
A.1.4 Sil reste du temps
Tlcharger le programme supplmentaire Tp1_Final.zip sur la page du cours
(http://imagine.enpc.fr/~monasse/Info), jouer avec... et le complter !
A.1.5 Installer Imagine++ chez soi
Allez voir sur http://imagine.enpc.fr/~monasse/Imagine++. Si vous uti-
lisez MacOS, apprenez faire lquivalent de ce TP sous XCode. Vous devrez utiliser
CMake pour crer un projet XCode.
210
A. Travaux Pratiques A.2. Variables, boucles, conditions, fonctions
A.2 Variables, boucles, conditions, fonctions
A.2.1 Premier programme avec fonctions
1. Rcuprer le programme exemple :
Tlcharger larchive Tp2.zip sur la page du cours, la dcompresser sur le bu-
reau et ouvrir la solution dans Visual. Etudier le projet Hop dont voici les sources :
1 # i ncl ude <i ostream>
2 usi ng namespace st d ;
3
4 i nt pl us ( i nt a , i nt b)
5 {
6 i nt c ;
7 c=a+b ;
8 ret urn c ;
9 }
10
11 void t r i pl e 1 ( i nt a )
12 {
13 a=a 3;
14 }
15
16 void t r i pl e 2 ( i nt& a )
17 {
18 a=a 3;
19 }
20
21 i nt main ( )
22 {
23 i nt i , j =2 , k ;
24 i =3;
25 k=pl us ( i , j ) ;
26 t r i pl e 1 ( i ) ;
27 t r i pl e 2 ( i ) ;
28 ret urn 0 ;
29 }
2. Debugger :
Excuter le programme pas pas et tudier la faon dont les variables changent.
A.2.2 Premier programme graphique avec Imagine++
Dans ce TP et les suivants, nous utiliserons la librairie graphique dImagine++ (cf
annexe du polycopi). Elle permet de grer trs simplement le fentrage, le dessin, et
les entres-sorties clavier et souris.
1. Programme de dpart :
Etudier le programme du projet Tennis dont voici le source :
1 # i ncl ude <Imagine/Graphi cs . h>
211
A.2. Variables, boucles, conditions, fonctions A. Travaux Pratiques
2 usi ng namespace Imagine ;
3 . . .
4
5 / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
6 / / Fo nc t i o n p r i n c i p a l e
7 i nt main ( )
8 {
9 / / Ouvert ure de l a f e n e t r e
10 openWindow( 2 5 6 , 2 5 6 ) ;
11 / / Po s i t i o n e t v i t e s s e de l a b a l l e
12 i nt xb=128 ,
13 yb=20 ,
14 ub=2 ,
15 vb=3;
16 / / Bouc l e p r i n c i p a l e
17 while ( t r ue ) {
18 / / Af f i c h a g e de l a b a l l e
19 f i l l Re c t ( xb3, yb3 , 7 , 7 , Red ) ;
20 / / Te mpo r i s a t i o n
21 mi l l i Sl ee p ( 2 0 ) ;
22 / / Ef f a c e me nt de l a b a l l e
23 f i l l Re c t ( xb3, yb3 , 7 , 7 , White ) ;
24 / / Rebond
25 i f ( xb+ub>253)
26 ub=ub ;
27 / / Mise a j o ur de l a p o s i t i o n de l a b a l l e
28 xb+=ub ;
29 yb+=vb ;
30 }
31 endGraphics ( ) ;
32 ret urn 0;
33 }
Ne pas sintresser la fonction Clavier()
Gnrer puis excuter la solution. Que se passe-t-il ?
2. Aide de Visual Studio Atout moment, la touche F1 permet daccder la documen-
tation 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 :
Identier la boucle principale du programme. Elle se dcompose ainsi :
(a) Afchage de la balle
(b) Temporisation de quelques millisecondes pour que la balle ne se dplace pas
trop vite
(c) Effacement de la balle
(d) Gestion des rebonds
212
A. Travaux Pratiques A.2. Variables, boucles, conditions, fonctions
(e) Mise jour des coordonnes de la balle
Pourquoi la ligne comportant linstruction while suscite-t-elle un warning ? A
quoi sert la condition formule par linstruction if ?
4. Gestion de tous les rebonds :
Complter le programme an de grer tous les rebonds. Par exemple, il faut in-
verser la vitesse horizontale ub quand la balle va toucher les bords gauche ou
droit de la fentre.
5. Variables globales :
Doubler la hauteur de la fentre. Modier la taille de la balle. Cela ncessite de
modier le code plusieurs endroits. Aussi, la place de valeurs numriques
en dur, il vaut mieux dnir des variables. An de simplier et bien que a ne
soit pas toujours conseill, utiliser des variables globales constantes. Pour cela,
insrer tout de suite aprs les deux lignes dinclude le code suivant
const i nt width = 256; / / Lar ge ur de l a f e n e t r e
const i nt hei ght = 256; / / Hauteur de l a f e n e t r e
const i nt ba l l _ s i z e = 3; / / Rayon de l a b a l l e
et reformuler les valeurs numriques du programmes laide de ces variables.
Le mot cl const indique que ces variables ne peuvent tre modies aprs leur
initialisation. Essayer de rajouter la ligne width=300; au dbut de la fonction main
et constater que cela provoque une erreur de compilation.
6. Utilisation de fonctions :
La balle est dessine deux fois au cours de la boucle, la premire fois en rouge
et la deuxime fois en blanc pour leffacer. Ici le dessin de la balle ne ncessite
quune ligne mais cela pourrait tre beaucoup plus si sa forme tait plus com-
plexe. Aussi, pour que le programme soit mieux structur et plus lisible, et que
le code comporte le moins de duplications possible, regrouper lafchage de la
balle et son effacement dans une fonction DessineBalle dnie avant la fonction
main :
void Dessi neBal l e ( i nt x , i nt y , Color col ) {
. . .
}
De mme, dnir une fonction
void BougeBalle(int &x,int &y,int &u,int &v)
pour grer les rebonds et le dplacement de la balle.
A.2.3 Jeu de Tennis
Nous allons rendre ce programme plus ludique en y ajoutant deux raquettes se
dplaant horizontalement en haut et en bas de lcran, et commandes par les touches
du clavier.
1. Afchage des raquettes :
Ajouter dans la fonction main des variables xr1,yr1,xr2,yr2 ddies la position
des deux raquettes. Puis dnir une fonction DessineRaquette en prenant modle
sur DessineBalle. Placer les appels de ces fonctions aux endroits appropris dans
la boucle principale.
213
A.2. Variables, boucles, conditions, fonctions A. Travaux Pratiques
FIGURE A.1 Mini tennis...
2. Gestion du clavier :
La gestion du clavier est ralise pour vous par la fonction Clavier dont nous
ignorerons le contenu pour linstant. Cette fonction nous permet de savoir direc-
tement si une des touches qui nous intressent (q et s pour le dplacement de la
premire raquette, k et l pour la deuxime) sont enfonces ou non. Cette fonc-
tion, Clavier(int& sens1, int& sens2), retourne dans sens1 et sens2, les valeurs 0,
-1 ou 1 (0 : pas de dplacement, -1 : vers la gauche, 1 : vers la droite).
3. Dplacement des raquettes :
Coder le dplacement dune raquette dans une fonction
void BougeRaquette(int &x, int sens)
puis appeler cette fonction dans la boucle principale pour chacune des deux ra-
quettes. Evidemment, faire en sorte que les raquettes ne puissent sortir de la fe-
ntre.
4. Rebonds sur les raquettes :
Sinspirer de la gestion des rebonds de la balle. Ici il faut non seulement vrier si
la balle va atteindre le bas ou le haut de lcran mais aussi si elle est assez proche
en abscisse de la raquette correspondante.
5. Comptage et afchage du score :
Modier la fonction BougeBalle an de comptabiliser le score des deux joueurs
et lafcher dans la console.
6. Pour ceux qui ont ni :
Lors dun rebond sur la raquette, modier linclinaison de la trajectoire de la balle
en fonction de la vitesse de la raquette ou de lendroit de frappe.
Vous devriez avoir obtenu un programme ressemblant celui de la gure A.1.
214
A. Travaux Pratiques A.3. Tableaux
FIGURE A.2 Master mind la console...
A.3 Tableaux
Dans ce TP, nous allons programmer un jeu de Mastermind, o lutilisateur doit
deviner une combinaison gnre alatoirement par lordinateur. Le joueur dispose
dun nombre dtermin dessais. A chaque essai dune combinaison, lordinateur four-
nit deux indices : le nombre de pions correctement placs et le nombre de pions de la
bonne couleur mais incorrectement positionns.
A.3.1 Mastermind Texte
1. Rcuprer la solution de dpart :
Tlcharger larchive Tp3_Initial.zip sur la page du cours, la dcompres-
ser dans un rpertoire faisant apparatre les noms des deux lves et ouvrir la
solution MasterMind dans Visual Studio. Etudier le projet Mastermind.
2. Reprsenter une combinaison :
Nous prendrons ici une combinaison de 5 pions de 4 couleurs diffrentes. La
couleur dun pion sera reprsente par un entier compris entre 0 et 3. Pour une
combinaison de 5 pions, nous allons donc utiliser un tableau de 5 entiers.
i nt combin [ 5 ] ; / / t a b l e a u de 5 e n t i e r s
3. Afcher une combinaison :
Programmer une fonction permettant dafcher une combinaison donne lcran.
La manire la plus simple de faire consistera faire afcher les diffrents chiffres
de la combinaison sur une mme ligne les uns la suite des autres.
4. Gnrer une combinaison alatoirement :
En dbut de partie, lordinateur doit gnrer alatoirement une combinaison
faire deviner lutilisateur. Nous allons pour cela utiliser les fonctions dclares
dans le chier cstdlib, notamment la fonction rand() permettant de gnrer un
nombre au hasard entre 0 et RAND_MAX. An dobtenir un nombre entre 0 et n,
on procdera de la manire suivante :
x = rand()%n ;
215
A.3. Tableaux A. Travaux Pratiques
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 fonc-
tion time() dclare dans le chier ctime permet de lobtenir.
En n de compte, la fonction suivante nous permet donc de gnrer une combi-
naison :
# i ncl ude <c s t dl i b >
# i ncl ude <ctime >
usi ng namespace st d ;
void genereCombinaison ( i nt combin [ 5 ] )
{
srand ( ( unsigned i nt ) time ( 0 ) ) ; / / i n i t i a l i s a t i o n
/ / du g e ne r a t e ur
f or ( i nt i =0; i <5; ++i )
combin [ i ] = rand ( ) %4; / / a p p e l s au g e ne r a t e ur
}
5. Changer la complexit du jeu :
Rapidement, vous allez devenir des experts en Mastermind. Vous allez alors vou-
loir augmenter la difcult. Il sufra alors dallonger la longueur de la combinai-
son, ou daugmenter le nombre de couleurs possibles. Cela vous est dores et dj
trs facile si vous avez pens dnir une constante globale pour chacune de ces
deux grandeurs. Si ce nest pas le cas, il est grand temps de le faire. Dnissez
par exemple :
const i nt nbcases = 5; / / l ongue ur de l a c ombi nai s on
const i nt nbcoul = 4; / / nombre de c o ul e ur 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 modier.
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 nt combi [ nbcases ] )
{
cout << " Votre e s s ai : " ;
s t r i ng s ;
ci n >> s ;
f or ( i nt i =0; i <nbcases ; i ++)
combi [ i ]=s [ i ] 0 ;
}
Dans le cadre de notre Mastermind, il sagit de modier 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 re-
216
A. Travaux Pratiques A.3. Tableaux
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 va-
leurs : 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 n-
cessaires
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 convi-
vial. Nous allons y remdier en y ajoutant une interface graphique.
1. Etude du projet de dpart :
Passer dans le projet MastermindGraphique. Penser le dnir comme pro-
jet 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 dnies. Elles fonctionnent selon un principe
de division de la fentre graphique en lignes. La fonction :
void af f i cheCombi nai son ( i nt combi [ nbcases ] , i nt n ) ;
permet dafcher 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 afcher le droulement du jeu. On afche 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 reprogram-
mer la boucle principale du jeu en utilisant lafchage graphique.
3. mme si la fonction de traitement dune combinaison est pour linstant incomplte
217
A.3. Tableaux A. Travaux Pratiques
FIGURE A.3 Master mind graphique...
3. Ultime amlioration :
On souhaite pouvoir effacer une couleur aprs lavoir tape, au cas o lon se
serait tromp. Etudier les fonctions
i nt Cl avi er ( ) ;
et
void getCombinaison ( i nt [ ] , i nt ) ;
La premire prend dj en compte la touche Retour arrire, mais pas la seconde
qui la considre comme une erreur de frappe. Modier cette dernire en cons-
quence.
218
A. Travaux Pratiques A.4. Structures
A.4 Structures
Avertissement : Dans ce TP, nous allons faire voluer des corps soumis la gravita-
tion, 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 justication physique.
A.4.1 Etapes
Mouvement de translation
1. Pour commencer, tudier le projet :
Tlcharger le chier TP4.zip sur la page habituelle, le dcompresser et lan-
cer Visual C++. Parcourir le projet, en sattardant sur les variables globales et la
fonction main (inutile de regarder le contenu des fonctions dj dnies mais
non utilises). Le programme fait voluer un point (x, y) selon un mouvement
de translation constante (vx, vy), et afche rgulirement un disque centr en
ce point. Pour ce faire, an de leffacer, on retient la position du disque au der-
nier afchage (dans ox et oy) ; par ailleurs, deux instructions commenant par
NoRefresh sont places autour des instructions graphiques an dacclrer laf-
chage.
2. Utiliser une structure :
Modier le programme de faon utiliser une structure Balle renfermant toute
linformation sur le disque (position, vitesse, rayon, couleur).
3. Fonctions dafchage :
Crer (et utiliser) une fonction void AfcheBalle(Balle D) afchant 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 correspon-
dantes dj prsentes dans main par un appel une fonction qui modie les
coordonnes dune Balle, en leur ajoutant la vitesse de la Balle multiplie par
un certain pas de temps dni en variable globale (dt = 1 pour linstant).
Gravitation
5. volution par acclration :
Crer (et utiliser) une fonction qui modie la vitesse dune Balle de faon lui
faire subir une attraction constante a
x
= 0 et a
y
= 0.0005. Indice : procder comme
prcdemment, cest--dire ajouter 0.0005 dt v
y
...
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, xe (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). Lafcher.
7. Acclration gravitationnelle :
Crer (et utiliser la place de la gravitation uniforme) une fonction qui prend en
219
A.4. Structures A. Travaux Pratiques
argument la plante et le soleil, et qui fait voluer la position de la plante. Rappel
de physique : lacclration prendre en compte est Gm
S
/r
3
r , avec ici G = 1
(Vous aurez sans doute besoin de la fonction double sqrt(double x), qui retourne
la racine carre de x). Ne pas oublier le facteur dt... Faire tourner et observer.
Essayez diverses initialisations de la plante (par exemple x = largeur/2, y =
hauteur/3, v
x
= 1 , v
y
= 0). Notez que lexpression de lacclration devient
trs grande lorsque r sapproche de 0 ; on prendra donc garde ne pas utiliser ce
terme lorsque r devient trop petit.
8. Initialisation alatoire :
Crer (et utiliser la place des conditions initiales donnes pour le soleil) une
fonction initialisant une Balle, sa position tant dans la fentre, sa vitesse nulle,
son rayon entre 5 et 15, et sa masse valant le rayon divis par 20. Vous aurez
probablement besoin de la fonction Random...
9. Des soleils par milliers...
Placer 10 soleils alatoirement (et en tenir compte lafchage, dans le calcul du
dplacement de lastrode...).
10. Diminuer le pas de temps de calcul :
An dviter les erreurs dues la discrtisation du temps, diminuer le pas de
temps dt, pour le xer 0.01 (voire 0.001 si la machine est assez puissante).
Rgler la frquence dafchage en consquence (inversement proportionnelle
dt). Lancer plusieurs fois le programme.
Chocs lastiques simples
11. Faire rebondir lastrode :
Faire subir des chocs lastiques lastrode chaque fois quil sapproche trop
dun soleil, de faon ce quil ne rentre plus dedans (fonction ChocSimple), et
rtablir dt une valeur plus leve, par exemple 0.1 (modier la frquence daf-
chage en consquent). Pour savoir si deux corps sont sur le point dentrer en
collision, utiliser la fonction Collision.
Jeu de tir
(gure A.4 droite)
12. Ouvrir un nouveau projet :
An de partir dans deux voies diffrentes et travailler proprement, ajouter un
nouveau projet (Imagine++), appel Duel, dans cette mme solution, et recopier
(par exemple par copier/coller) intgralement le contenu du chier main.cpp
du projet Gravitation.
13. vous de jouer !
Transformer le projet Duel, laide des fonctions qui y sont dj prsentes, en un
jeu de tir, deux joueurs. Chacun des deux joueurs a une position xe, 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
dafchage. Le gagnant est le premier qui russit atteindre lautre... Conseils
pratiques : positionner symtriquement les joueurs par rapport au centre, de pr-
frence mi-hauteur en laissant une marge dun huitime de la largeur sur le
220
A. Travaux Pratiques A.4. Structures
FIGURE A.4 Corps clestes et jeu de tir...
ct ; utiliser la fonction GetMouse pour connatre la position de la souris ; en d-
duire la vitesse dsire par le joueur en retranchant ces coordonnes celles du
centre de la boule lancer, et en multipliant par un facteur 0.00025.
14. Amliorations :
Faire en sorte quil y ait systmatiquement un gros soleil au centre de lcran (de
masse non ncessairement consquente) an dempcher les tirs directs.
15. Initialisation correcte :
Modier la fonction de placement des soleils de faon ce que les soleils ne sin-
tersectent pas initialement, et quils soient une distance minimale de 100 pixels
des emplacements des joueurs.
Chocs lastiques
(gure A.4 gauche)
16. Tout faire voluer, tout faire rebondir :
On retourne dans le projet Gravitation. Tout faire bouger, y compris les soleils.
Utiliser, pour les chocs lastiques, la fonction Chocs (qui fait rebondir les deux
corps). Faire en sorte que lors de linitialisation les soleils ne sintersectent pas.
A.4.2 Aide
Fonctions fournies :
void InitRandom ( ) ;
est excuter une fois avant le premier appel Random.
double Random( double a , double b ) ;
renvoie un double alatoirement entre a et b (compris). Excuter une fois InitRandom();
avant la premire utilisation de cette fonction.
void ChocSimple ( double x , double y , double &vx , double &vy , double m,
double x2 , double y2 , double vx2 , double vy2 ) ;
221
A.4. Structures A. Travaux Pratiques
fait rebondir la premire particule, de coordonnes (x, y), de vitesse (vx, vy) et de
masse m, sur la deuxime, de coordonnes (x2, y2) et de vitesse (vx2, vy2), sans d-
placer la deuxime.
void Choc ( double x , double y , double &vx , double &vy , double m,
double x2 , double y2 , double &vx2 , double &vy2 , double m2) ;
fait rebondir les deux particules lune contre lautre.
bool Col l i s i on ( double x1 , double y1 , double vx1 , double vy1 , double r1 ,
double x2 , double y2 , double vx2 , double vy2 , double r2 ) ;
renvoie true si le corps de coordonnes (x1, y1), de vitesse (vx1, vy1) et de rayon r1
est sur le point dentrer en collision avec le corps de coordonnes (x2, y2), de vitesse
(vx2, vy2) et de rayon r2, et false sinon.
A.4.3 Thorie physique
NB : Cette section nest donne que pour expliquer le contenu des fonctions pr-
programmes 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.

i

F
i/A
= m
A

a
G(A)
Gravitation universelle
Soient deux corps A et B. Alors A subit une force dattraction

F
B/A
= Gm
A
m
B
1
d
2
A,B

u
BA
.
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 par-
ticules 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
A

v
A
+ m
B

v
B
2. le moment cintique M = m
A

r
A


v
A
+ m
B

r
B


v
B
(qui est un rel dans le
cas dun mouvement plan).
3. lnergie cintique E
c
=
1
2
m
A
v
2
A
+
1
2
m
B
v
2
B
.
Ce qui fait 4 quations pour 4 inconnues.
222
A. Travaux Pratiques A.4. Structures
Rsolution du choc
On se place dans le rfrentiel du centre de masse. On a alors, tout instant :
1.

P = 0 (par dnition de ce rfrentiel), do m
A

v
A
= m
B

v
B
.
2. M = (

r
A


r
B
) m
A

v
A
, do, en notant

r =

r
A


r
B
, M =

r m
A

v
A
.
3. 2E
c
= m
A
(1 +
m
A
m
B
)v
2
A
.
La constance de E
c
nous informe que dans ce repre, la norme des vitesses est
conserve, et la constance du moment cintique que les vitesses varient paralllement


r . Si lon veut que les vitesses varient effectivement, il ne nous reste plus quune
possibilit : mutliplier par 1 la composante des

v
i
selon

r . Ce qui fournit un algo-
rithme simple de rebond.
Dcider de limminence dun choc
On ne peut pas se contenter, lors de lvolution pas pas des coordonnes des
disques, de dcider quun choc aura lieu entre t et t +dt rien quen estimant la distance
entre les deux disques candidats la collision linstant t, ni mme en prenant en plus
en considration cette distance linstant t + dt, car, si la vitesse est trop leve, un
disque peut dj avoir travers lautre et en tre ressorti en t +dt... La solution consiste
expliciter le minimum de la distance entre les disques en fonction du temps, variant
entre t et t + dt.
Soit N(u) = (

r
A
(u)

r
B
(u))
2
le carr de la distance en question. On a :
N(u) = (

r
A
(t)

r
B
(t) + (u t)(

v
A
(t)

v
B
(t)))
2
Ce qui donne, avec des notations supplmentaires :
N(u) =

r (t)
2
+ 2(u t)

r (t)

v (t) + (u t)
2


v (t)
2
La norme, toujours positive, est minimale au point u tel que
u
N(u) = 0, soit :
(t
m
t) =


r (t)

v (t)


v (t)
2
Donc :
1. si t
m
< t, le minimum est atteint en t,
2. si t < t
m
< t + dt, le minimum est atteint en t
m
;
3. sinon, t + dt < t
m
, 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.
223
A.5. Fichiers spars A. Travaux Pratiques
A.5 Fichiers spars
Nous allons poursuivre dans ce TP les simulations de gravitation et de chocs las-
tiques entames la semaine dernire, en sparant dans diffrents chiers les diffrentes
fonctions et structures utilises.
1. De bonnes bases :
Tlcharger le chier 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 rutili-
ser votre propre solution, vous pouvez quitter Visual C++, remplacer le chier
Gravitation.cpp par celui que vous aurez rcupr dans votre TP4, et relan-
cer Visual C++.
A.5.1 Fonctions outils
2. Un chier de dnitions...
Ajouter un nouveau chier 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 sui-
vantes, que lon pourra retirer de Gravitation :
# i ncl ude <c s t dl i b >
# i ncl ude <ctime >
usi ng namespace st d ;
3. ... et un chier de dclarations
Ajouter un nouveau chier 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 dnition 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 chier 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 dnir) les oprateurs et
fonction suivants :
Vect or operat or +( Vect or a , Vect or b ) ; / / Somme de deux v e c t e ur s
Vect or operat or ( Vect or a , Vect or b ) ; / / Di f f r e n c e de deux v e c t e ur s
double norme2 ( Vect or a ) ; / / Norme e u c l i d i e n n e d un v e c t e ur
Vect or operat or ( Vect or a , double lambda ) ; / / Mul t i p l i c a t i o n par un s c a l a i r e
Vect or operat or ( double lambda , Vect or a ) ; / / Mul t i p l i c a t i o n par un s c a l a i r e
5. Fonctions et oprateurs sur les Vector :
Crer un nouveau chier Vector.cpp. Mettre un #include du chier den-tte
correspondant et dnir les oprateurs qui y sont dclars (Rappel : sqrt est
dni dans le chier den-tte systme <cmath>; ne pas oublier non plus le
224
A. Travaux Pratiques A.5. Fichiers spars
using namespace std; qui permet dutiliser cette fonction). Astuce : une fois quune
version de operator est dnie, la deuxime version peut utiliser la premire
dans sa dnition...
6. Vecteur vitesse et vecteur position :
Systmatiquement remplacer dans Gravitation.cpp les vitesses et positions
par des objets de type Vector (y compris dans la dnition de la structure Balle).
Utiliser autant que possible les oprateurs et fonction dnis dans Vector.cpp.
A.5.3 Balle part
7. Structure Balle :
Dplacer la structure Balle dans un nouveau chier den-tte Balle.h. Puisque
Balle utilise les types Vector et Color, il faut aussi ajouter ces lignes :
# i ncl ude <Imagine/Graphi cs . h>
usi ng namespace Imagine ;
# i ncl ude " Vect or . h"
8. Fonctions associes :
Dplacer toutes les fonctions annexes prenant des Balle en paramtres dans un
nouveau chier Balle.cpp. Il ne devrait plus rester dans Gravitation.cpp
dautre fonction que main. Dclarer dans Balle.h les fonctions dnies dans
Balle.cpp. Ajouter les #include ncessaires dans ce dernier chier et dans Gravitation.cpp
et faire les adaptations ncessaires (par exemple, si des fonctions utilisent largeur
ou hauteur, comme ces constantes ne sont dnies que dans Gravitation.cpp,
il faut les passer en argument...)
A.5.4 Retour la physique
9. Des soleils par milliers... :
Placer 10 soleils alatoirement (et en tenir compte lafchage, dans le calcul du
dplacement de lastrode...).
10. Diminuer le pas de temps de calcul :
An dviter les erreurs dues la discrtisation du temps, diminuer le pas de
temps dt, pour le xer 0.01 (voire 0.001 si la machine est assez puissante).
Rgler la frquence dafchage en consquence (inversement proportionnelle
dt). Lancer plusieurs fois le programme.
Chocs lastiques simples
11. Faire rebondir lastrode :
Faire subir des chocs lastiques lastrode chaque fois quil sapproche trop
dun soleil, de faon ce quil ne rentre plus dedans (fonction ChocSimple),
et rtablir dt une valeur plus leve, par exemple 0.1 (modier la frquence
dafchage en consquent). Pour savoir si deux corps sont sur le point dentrer
en collision, utiliser la fonction Collision.
225
A.5. Fichiers spars A. Travaux Pratiques
Jeu de tir
12. Ouvrir un nouveau projet :
An de partir dans deux voies diffrentes et travailler proprement, ajouter un
nouveau projet appel Duel, dans cette mme solution.
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 chiers Vector.h, Vector.cpp, Balle.h, Balle.cpp, Tools.h,
Tools.cpp. Les chiers sont les mmes que dans le projet Gravitation, ils ne sont
pas recopis. Mettre au dbut du Duel.cpp les #include correspondants. Essayer
de compiler Duel.cpp. Comme le compilateur narrive pas trouver les chiers
inclus, qui ne sont pas dans le mme rpertoire, il faut lui indiquer o les trouver.
(#include " ../Gravitation/Tools.h" par exemple).
14. vous de jouer !
Transformer le projet Duel, laide des fonctions dnies auparavant, en un jeu
de tir, deux joueurs. Chacun des deux joueurs a une position xe, 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
dafchage. Le gagnant est le premier qui russit atteindre lautre... Conseils
pratiques : positionner symtriquement les joueurs par rapport au centre, de pr-
frence 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) an dempcher les tirs directs.
16. Initialisation correcte :
Modier la fonction de placement des soleils de faon ce que les soleils ne sin-
tersectent pas initialement, et quils soient une distance minimale de 100 pixels
des emplacements des joueurs.
Chocs lastiques
17. Tout faire voluer, tout faire rebondir :
On retourne dans le projet Gravitation. Tout faire bouger, y compris les soleils.
Utiliser, pour les chocs lastiques, la fonction Chocs (qui fait rebondir les deux
corps). Faire en sorte que lors de linitialisation les soleils ne sintersectent pas.
226
A. Travaux Pratiques A.6. Les tris
FIGURE A.5 Corps clestes et jeu de tir...
FIGURE A.6 Deux tris en cours dexcution : tri bulle et Quicksort...
A.6 Les tris
Dans ce TP, nous allons programmer quelques algorithmes de tri dlments dans
un tableau. Pour que cela soit interactif, et que lon puisse voir comment se passent
chacun des tris, une interface graphique a t programme, qui afche le tableau et
permet de visualiser les oprations quon ralise dessus (gure A.6).
A.6.1 Mlanger un tableau
1. Pour commencer, tudier le projet :
Tlcharger le chier Tp6_Initial.zip sur la page habituelle, le dcompresser
et lancer Visual C++. Le projet est spar entre le chier main.cpp, dans lequel
on programmera les algorithmes de tri, et le couple (tools.cpp, tools.h), qui
gre linterface graphique et quelques fonctions utiles la comparaison des dif-
frents tris.
Ouvrir tools.h pour dcouvrir les fonctions de cette interface graphique, puis
main.cpp pour voir comment elles sont utilises (les lignes commentes sont
l pour montrer comment vous utiliserez les fonctions melange_tableau et
tri_selection que vous allez programmer).
227
A.6. Les tris A. Travaux Pratiques
Excuter le projet. Pour linstant, il ne fait quinitialiser un tableau et lafcher.
2. Accs un tableau, change de 2 valeurs
Pour que les oprations que vous effectuerez sur les tableaux soient afches
automatiquement, il faudra que vous utilisiez uniquement les fonctions valeur
et echange dclares dans tools.h (ne pas accder directement au tableau avec
T[i]).
Entranez-vous les utiliser dans la fonction main(), en accdant une valeur du
tableau T0, et en permutant 2 de ses lments.
3. Scuriser les accs tableau
Pour sassrer quon ne sort pas des bornes du tableaux, ajouter des appels
la fonction assert (vue en cours) dans valeur et echange. Constater ce qui se
passe lexcution lorsquon appelle par exemple
val eur ( 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 mel ange_tabl eau ( double T[ ] , i nt t a i l l e )
qui, comme son nomlindique, mlange un tableau. Ajoutez-la (dans tools.cpp
bien sr)
Ide : le mlange le plus rapide (et le plus efcace) consiste parcourir le tableau
une fois, et permuter chacun des lments avec un autre choisi au hasard (uti-
liser la fonction int random(int a) dnie 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(n
2
), 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 pou-
vez 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 ches...
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
228
A. Travaux Pratiques A.6. Les tris
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.
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
n
2
:
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 pi-
vot. 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 vrier quelle marche bien!
Ide :
on utilise 2 index qui parcourent le tableau, le premier partir du 2me l-
ment (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 ren-
contre, 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 program-
mer cette fonction peuvent la faire chouer dans des cas particuliers : demandez-
vous 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 ta-
bleau. En pratique, on va utiliser 2 arguments debut et fin qui indiquent quon
ne ralise le tri quentre les indices debut et fin.
229
A.6. Les tris A. Travaux Pratiques
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 initiale-
ment lindice debut)
10. Quicksort
On peut maintenant crire une fonction
void quicksort_recursif (double T[], int taille , int debut, int n),
qui contient lalgorithme, ainsi que la fonction
void quicksort(double T[], int taille ),
qui ne fait quappeler cette dernire, mais qui sera celle quon utilisera dans
main() (car plus simple).
A.6.4 Gros tableaux
Maintenant quon a vu graphiquement comment marchent ces algorithmes, il est
intressant de les faire fonctionner et de les comparer sur des tableaux de grande
taille.
11. Tableaux de taille variable
Si on veut tester nos algorithmes sur des tableaux de grande taille, il faut utiliser
des tableaux de taille variable. Remplacer toutes les lignes de type double t[ taille ];
par double t = new double[taille], et la n des fonctions o se trouvent ces d-
clarations, ajouter la ligne delete[] t ; .
Dautre part il faut dsactiver lafchage :
init_tools (512, false ).
12. Nombre de lectures et dcriture
Pour comparer plus rigoureusement 2 algorithmes, on peut comparer le nombre
de lectures du tableau et le nombre dcritures dans le tableau.
Crer 2 variables globales dans tools.cpp et modier les fonctions init_tri,
valeur, echange et fin_tri pour initialiser, compter et afcher le nombre de
lectures et dcritures. Au fait, en combien doprations seffectue en moyenne
Quicksort ?
13. Temps de calcul
Il est intressant galement davoir les temps de calcul exacts. Pour cela, on peut
enregistrer dans une nouvelle variable globale timer0 le temps quil est avant le
tri :
timer0 = double(clock())/CLOCKS_PER_SEC;
et la retrancher au temps quil est aprs le tri (modier les fonctions init_tri
et fin_tri pour faire ce calcul et lafcher).
14. Mode Release
On peut changer le mode de compilation en le passant de Debug Release. Vri-
er que lexcution des algorithmes est effectivement bien plus rapide en mode
Release !
230
A. Travaux Pratiques A.7. Images
A.7 Images
FIGURE A.7 Deux images et diffrents traitements de la deuxime (ngatif, ou, 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 (gure A.7).
A.7.1 Allocation
1. Rcuprer le projet :
Tlcharger le chier 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 innie, des allocations de 1000000 entiers sans dsallouer et re-
garder la taille du process grandir. (Utiliser Ctrl+Shift+Echap pour accder
au gestionnaire de tches). Compiler en mode Release pour utiliser la "vraie" ges-
tion du tas (Le mode Debug utilise une gestion spcique qui aide trouver les
bugs et se comporte diffremment...)
A.7.2 Tableaux statiques
3. Niveaux de gris :
Une image noir et blanc est reprsente par un tableau de pixels de dimensions
constantes W=300 et H=200. Chaque pixel (i,j) est un byte (entier de 0 255)
allant de 0 pour le noir 255 pour le blanc. Lorigine est en haut gauche, i est
lhorizontale et j la verticale. Dans un tableau de byte mono-dimensionnel t de
taille W
*
H mmorisant le pixel (i,j) en t[i+W
*
j] :
231
A.7. Images A. Travaux Pratiques
Stocker une image noire et lafcher avec putGreyImage(0,0,t,W,H).
Idem avec une image blanche.
Idem avec un dgrad du noir au blanc (attention aux conversions entre byte
et double).
Idem avec t(i, j) = 128 + 128 sin(4i/W) sin(4j/H) (cf gure A.7). Utiliser
# def i ne _USE_MATH_DEFINES
# i ncl ude <cmath>
pour avoir les fonctions et les constantes mathmatiques : M_PI vaut .
4. Couleurs :
Afcher, avec putColorImage(0,0,r,g,b,W,H), une image en couleur sto-
cke dans trois tableaux r, g et b (rouge, vert, bleu). Utiliser la fonction click()
pour attendre que lutilisateur clique avec la souris entre lafchage prcdent et
ce nouvel afchage.
A.7.3 Tableaux dynamiques
5. Dimensions au clavier :
Modier 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 chier
6. Image couleur :
La fonction loadColorImage("ppd.jpg",r,g,b,W,H); charge le chier "ppd.jpg"
qui est dans le rpertoire du projet, alloue elle-mme les tableaux r,g,b, les rem-
plit 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 lafcher. Ne pas oublier les dsallocations.
7. Image noir et blanc :
La fonction loadGreyImage("ppd.jpg",t,W,H) fait la mme chose mais conver-
tit limage en noir et blanc. Afcher 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, afcher et charger les images :
byt e AlloueImage ( i nt W, i nt H) ;
void Detrui tImage ( byte I ) ;
void Affi cheImage ( byte I , i nt W, i nt H) ;
byt e ChargeImage ( char name , i nt &W, i nt &H) ;
9. Fichiers :
Crer un image.cpp et un image.h en consquence...
232
A. Travaux Pratiques A.7. Images
A.7.6 Structure
10. Principe :
Modier le programme prcdent pour utiliser une structure :
s t r uc t Image {
byt e t ;
i nt w, h ;
} ;
AlloueImage() et ChargeImage() pourront retourner des Image.
11. Indpendance :
Pour ne plus avoir savoir comment les pixels sont stocks, rajouter :
byt e Get ( Image I , i nt i , i nt j ) ;
void Set ( Image I , i nt i , i nt j , byt e g ) ;
12. Traitements :
Ajouter dans main.cpp diffrentes fonctions de modication des images
Image Negat i f ( Image I ) ;
Image Flou ( Image I ) ;
Image Re l i e f ( Image I ) ;
Image Contours ( Image I , double s e ui l ) ;
Image Deforme ( Image I ) ;
et les utiliser :
(a) Negatif : changer le noir en blanc et vise-versa par une transformation
afne.
(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 proter 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 nie : 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 nies la drive horizontale d
x
= (I(i +
1, j) I(i 1, j))/2 et la drive verticale d
y
, puis la norme du gradient
|I| =
_
d
2
x
+ d
2
y
et afcher 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 n
13. Sil reste du temps, samuser :
Rtrcir une image.
Au lieu du ngatif, on peut par exemple changer le contraste. Comment ?
233
A.8. Premiers objets et dessins de fractales A. Travaux Pratiques
A.8 Premiers objets et dessins de fractales
FIGURE A.8 Fractales...
Dans ce TP, nous allons nous essayer la programmation objet. Nous allons trans-
former une structure vecteur en une classe et lutiliser pour dessiner des courbes frac-
tales (gure A.8).
A.8.1 Le triangle de Sierpinski
1. Rcuprer le projet :
Tlcharger le chier Tp8_Initial.zip sur la page habituelle, le dcompres-
ser et lancer Visual C++. Etudier la structure Vector dnie dans les chiers
Vector.cpp et Vector.h.
2. Interfaage avec Imagine++ :
La structure Vector ne comporte pas de fonction dafchage graphique. Ajouter
dans main.cpp des fonctions drawLine et drawTriangle prenant des Vector
en paramtres. Il suft de rebondir sur la fonction
void drawLine ( i nt x1 , i nt y1 , i nt x2 , i nt y2 , const Color& c , i nt pen_w) }
dImagine++. Le dernier paramtre contrle lpaisseur du trait.
3. Triangle de Sierpinski :
Cest la gure fractale choisie par lENPC pour son logo. La gure ci-dessous
illustre sa construction.
Ecrire une fonction rcursive pour dessiner le triangle de Sierpinski. Cette fonc-
FIGURE A.9 Construction du triangle de Sierpinski.
tion prendra en paramtres les trois points du triangle en cours et lepaisseur du
234
A. Travaux Pratiques A.8. Premiers objets et dessins de fractales
trait. Les trois sous-triangles seront dessins avec un trait plus n. Ne pas oublier
la condition darrt de la rcursion!
Utiliser cette fonction dans le main en lui fournissant un triangle initial dpais-
seur 6.
A.8.2 Une classe plutt quune structure
4. Classe vecteur :
Transformer la structure Vector en une classe. Y incorporer toutes les fonctions
et les oprateurs. Passer en public le strict ncessaire. Faire les modications n-
cessaires 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 utilisa-
teurs 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 gure ci-
dessous illustre le rsultat obtenu pour diffrentes profondeurs de rcursion.
Ecrire une fonction rcursive pour dessiner une telle courbe. Vous aurez besoin
FIGURE A.10 Construction de larbre.
de la fonction Rotate de la classe Vector.
A.8.3 Changer dimplmentation
7. Deuxime implmentation :
Modier limplmentation de la classe Vector en remplaant les membres double x,y;
par un tableau double coord[2];. Quelles sont les modications 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 dimen-
sion.
NB : la fonction Rotate et les accesseurs que nous avons crits ne se gnra-
lisent pas directement aux dimensions suprieures. Les laisser tels quels pour
linstant...
235
A.8. Premiers objets et dessins de fractales A. Travaux Pratiques
A.8.4 Le ocon 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 qui-
latral.
Ecrire une fonction rcursive pour dessiner une courbe de Koch.
FIGURE A.11 Construction de la courbe de Koch.
10. Flocon de neige :
Il sobtient en construisant une courbe de Koch partir de chacun des cts dun
triangle quilatral.
236
A. Travaux Pratiques A.9. Tron
FIGURE A.12 Jeu de Tron.
A.9 Tron
Dans ce TP, nous allons programmer le jeu TRON. Il sagit dun jeu 2 joueurs,
dans lequel chaque joueur pilote un mobile qui se dplace vitesse constante et laisse
derrire lui une trace infranchissable. Le premier joueur qui percute sa propre trace ou
celle de son adversaire a perdu.
A.9.1 Serpent
Nous allons procder en deux temps. Dabord programmer un jeu de Serpent
un joueur. Le programme serpent.exe vous donne une ide du rsultat recherch.
Dans ce jeu, le joueur pilote un Serpent qui sallonge petit petit (dun lment tous
les x tours, avec la convention que la longueur totale est borne nmax lments). Il
sagit de ne pas se rentrer dedans ni de percuter les murs.
La solution de dpart comporte deux chiers, utils.h et utils.cpp, qui contiennent
une structure point (quil faudra ventuellement toffer de mthodes utiles) et une
fonction destine rcuprer les touches clavier pour linteraction avec les joueurs.
Il sagit ici de concevoir un objet Serpent dot des mthodes adquates, plus une
fonction jeu_1p exploitant les capacits du Serpent pour reproduire le comportement
dsir. On pourra dans un premier temps ne pas grer les collisions (avec le bord et
avec lui-mme), et ne les rajouter que dans un second temps. Votre travail se dcom-
pose en 6 tapes :
1. (sur papier) Dnir linterface de la classe Serpent (cest--dire lister toutes les
fonctionnalits ncessaires).
2. (sur papier) Rchir limplmentation de la classe Serpent : comment stocker les
donnes ? comment programmer les diffrentes mthodes ? (lire en prliminaire
les remarques du paragraphe suivant).
3. Dans un chier serpent.h, crire la dclaration de votre classe Serpent : ses membres,
ses mthodes, ce qui est public, ce qui ne lest pas.
4. Soumettre le rsultat de vos rexions votre enseignant pour valider avec lui les choix
retenus.
5. Implmenter la classe Serpent (cest--dire programmer les mthodes que vous
avez dclares).
237
A.9. Tron A. Travaux Pratiques
6. Programmer la fonction jeu_1p utilisant un Serpent.
Remarque : Dans le chier utils.h sont dnis :
1. 4 entiers gauche, bas, haut, droite de telle manire que :
(a) la fonction x (x +1)%4 transforme gauche en bas, bas en droite, droite en
haut et haut en gauche ; cette fonction correspond donc un quart de tour
dans le sens trigonomtrique.
(b) la fonction x (x 1)%4 transforme gauche en haut, haut en droite, droite
en bas et bas en gauche ; cette fonction correspond donc un quart de tour
dans le sens des aiguilles dune montre.
2. un tableau de 4 points dir de telle manire que, moyennant la dnition dune
fonction permettant de faire la somme de deux points, la fonction p p + dir[d]
renvoie :
(a) pour d=gauche le point correspondant au dcalage de p de 1 vers la gauche.
(b) pour d=haut le point correspondant au dcalage de p de 1 vers la haut.
(c) pour d=droite le point correspondant au dcalage de p de 1 vers la droite.
(d) pour d=bas le point correspondant au dcalage de p de 1 vers la bas.
A.9.2 Tron
A partir du jeu de Serpent ralis prcdemment, nous allons facilement pouvoir
implmenter le jeu Tron. Le programme tron.exe vous donne une ide du rsultat
recherch. Le principe de ce jeu est que chaque joueur pilote une moto qui laisse der-
rire elle une trace infranchissable. Le but est de survivre plus longtemps que le joueur
adverse.
1. Passage deux joueurs.
A partir de la fonction jeu_1p, crer une fonction jeu_2p implmentant un
jeu de serpent 2 joueurs. On utilisera pour ce joueur les touches S, X, D et F.
La fonction Clavier() renverra donc les entiers int ( S ), int ( X), int ( D) et
int ( F ). Remarque : on ne grera quune touche par tour, soit un seul appel la
fonction Clavier() par tour.
2. Ultimes rglages
(a) Grer la collision entre les deux serpents.
(b) Le principe de Tron est que la trace des mobiles reste. Pour implmenter
cela, il suft dallonger nos serpents chaque tour.
A.9.3 Graphismes
Petit bonus pour les rapides : nous allons voir comment grer des graphismes un
peu plus sympas que les rectangles uniformes que nous avons utiliss jusquici. Lob-
jectif 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
afchage rapide. Pour charger une image dans une NativeBitmap on procde ainsi :
238
A. Travaux Pratiques A.9. Tron
/ / Ent i e r s p a s s s par r f r e n c e l o r s du char ge me nt de l i mage pour
/ / qu y s o i e n t s t o c k e s l a l a r g e ur e t l a haut e ur de l i mage
i nt w, h ;
/ / Chargement de l i mage
byt e rgb ;
loadColorImage ( " nom_f i chi er . bmp" , rgb , w, h ) ;
/ / D c l a r a t i o n de l a Nat i ve Bi t map
NativeBitmap ma_native_bitmap (w, h ) ;
/ / On p l a c e l i mage dans l a Nat i ve Bi t map
ma_native_bitmap . setCol orImage ( 0 , 0 , rgb , w, h ) ;
Lafchage dune NativeBitmap lcran se fait alors avec la mthode :
void putNativeBitmap ( i nt x , i nt y , NativeBitmap nb)
1. Remplacer dans le serpent lafchage de la tte par lafchage 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. Examens
Annexe B
Examens
Note : les corrigs sont disponibles sur la page Web du cours.
B.1 Examen sur machine 2011 : nonc
B.1.1 Automate cellulaire
Un automate cellulaire est une rgle de transformation dune squence de bits (0 ou
1), appels cellules, en une autre squence par une rgle de transformation : chaque bit
est transform en fonction de sa valeur et de ses deux voisins immdiats. Par exemple,
voici une telle rgle :
voisinage 111 110 101 100 011 010 001 000
nouvelle valeur centrale 0 1 1 0 1 1 1 0
La squence de bits de la deuxime ligne se lit en binaire comme le nombre 2
1
+ 2
2
+
2
3
+ 2
5
+ 2
6
= 110 et est la rgle 110. Voici un exemple de transformation suivant cette
rgle :
automate initial 1 1 0 1
automate transform 0 1 1 1
en considrant la squence comme circulaire, cest--dire que le voisin de gauche de la
premire cellule est la dernire cellule et que le voisin de droite de la dernire cellule
est la premire.
Pour calculer la valeur centrale, il suft dinterprter le voisinage comme un bi-
naire, par exemple 101 = 2
2
+ 2
0
= 5 et de lire le bit numro 5 en partant de la droite
(et commenant compter par 0) de la dcomposition binaire de la rgle : 01101110.
Parmi les 256 rgles possibles, certaines sont plus intressantes que dautres : elles
exhibent une certaine rgularit sans tre rellement priodiques. On reprsente un
automate en laissant en blanc ou en noir un pixel dune ligne en fonction de la valeur
de la cellule. Dans le diagramme (voir gure), chaque ligne est lautomate transform
de la ligne prcdente.
Il existe en C++ des oprateurs agissant directement sur les bits. Comme nous ne
les avons pas encore vus en cours, nous nallons pas les utiliser mais reprsenter la
valeur dune cellule par un bool et grer nous-mmes les oprations.
B.1. Examen sur machine 2011 : nonc B. Examens
FIGURE B.1 Une ralisation de lautomate cellulaire
B.1.2 Travail demand
Il est plus important de livrer un code clair et qui compile sans warning,
mme sil ne rpond pas toutes les questions. Pour cela, vriez chaque
tape que votre programme compile et se lance correctement.
1. Crez un nouveau projet Imagine++ nomm Automate et crivez le main, qui
ouvre une fentre de taille xe.
2. Dans un chier automate.h, crivez une structure Automate, comprenant un
tableau de bool et sa taille, de type int.
Automate
3. Dans automate.cpp, implmentez des fonctions :
init prenant un automate et une taille et allouant le tableau;
detruis librant le tableau de lautomate pass en argument ;
random mettant les cellules de lautomate alatoirement true ou false.
Noubliez pas de dclarer ces fonctions dans automate.h.
4. Implmentez une fonction affiche prenant un automate et un numro de ligne
j et mettant noir ou blanc le pixel (i, j) suivant la valeur de la cellule numro i
de lautomate.
5. Dans le chier diagramme.h, crivez la structure Diagramme avec 3 champs : n
(le nombre dautomates, un entier), nmax (taille maximum du diagramme) et un
tableau dautomates.
6. Une fonction init cre un nombre nmax dautomates, le premier tant initialis
par random.
7. Ecrivez une fonction affiche dessinant les n cases dun diagramme.
Binaires
8. Dans un chier binaire.h, crivez une structure Binaire contenant un ta-
bleau de 8 boolens.
242
B. Examens B.1. Examen sur machine 2011 : nonc
9. Ecrivez dans binaire.cpp une fonction decompose dcomposant un entier
pass en paramtre (suppos entre 0 et 255) en sa dcomposition binaire, avec la
convention 1=true, 0=false. Procder ainsi :
Si le nombre est impair (utiliser lopration modulo % sur les entiers), mettre
la premire valeur du tableau true (sinon false).
Diviser le nombre par 2 (quotient de division euclidienne).
Tester la parit du nombre, et affecter la deuxime valeur du tableau.
Diviser nouveau par 2.
Ritrer pour remplir tout le tableau.
10. Ecrivez une fonction compose prenant un tableau de 3 boolens et retournant
le nombre binaire correpondant : le premier boolen vaut 1, le deuxime 2 et le
troisime 4. On obtient donc un entier entre 0 et 7.
Rgle de transformation
11. Ecrivez une fonction transforme prenant deux 2 automates en paramtre et
remplissant le deuxime par la rgle de transformation. Pour cela, on passe aussi
la dcomposition binaire de la rgle et il suft daller chercher le bon bit dans ce
tableau en fonction du voisinage.
12. Ecrivez une fonction evolue ajoutant une ligne au diagramme par transforma-
tion de la dernire.
13. Faites remplir progressivement toute la fentre avec le diagramme : la premire
ligne est un automate alatoire et les suivantes sont le rsultat de la transforma-
tion de lautomate de la ligne prcdente.
14. Quant on arrive au bout de la fentre, pour ajouter une ligne on remonte tout
le diagramme actuel dune ligne pour pouvoir insrer la nouvelle. Modiez la
fonction evolue pour mettre a jour le diagramme quand n atteint nmax.
15. Faire dler linni lautomate dans la fentre.
Important : Quand vous avez termin, nettoyez la solution et crez une archive du
projet votre nom. Ne vous dconnectez pas avant que le surveillant ne soit pass vous
voir pour copier cette archive sur cl USB.
243
B.2. Examen sur machine 2010 : nonc B. Examens
FIGURE B.2 Des ttradres et octadres en rotation dans lespace. Aucun traitement
particulier nest fait pour les solides qui sintersectent.
B.2 Examen sur machine 2010 : nonc
B.2.1 Visualisation 3D par z-buffer
Nous allons visualiser des volumes 3D agencs dans lespace sur lcran et allons
les animer. Nous nous restreignons des polydres rguliers, mais le principe serait
similaire pour dautres volumes.
La principale difcult rside dans la gestion des parties caches (nos volumes sont
opaques), ou occlusions. Nous utiliserons pour cela la mthode classique du z-buffer,
qui est galement utilise dans les interfaces graphiques 3D openGL et DirectX par
exemple, et donc dans la plupart des jeux vido.
Lide de base est simple : pour chaque pixel on enregistre la hauteur (coordonne z)
du point 3D projet le plus haut. Cela forme une carte des hauteurs appele le z-buffer.
On reprsente la surface par une srie de triangles ; chacun de ceux-ci est projet dans
un ordre quelconque en un ensemble de pixels. On ne met le pixel dans limage la
couleur du triangle que si la hauteur en ce point est suprieure celle enregistre dans
le z-buffer jusqu prsent. Si cest le cas, on enregistre en plus ce nouveau z dans le
z-buffer.
De cette faon, on peut projeter les triangles dans nimporte quel ordre et un pixel
est rcrit chaque fois quon trouve un nouveau point plus lev aux mmes coordon-
nes x et y.
Prcisons enn que nous utilisons le principe de projection linni, cest--dire
quun point
_
x y z
_
se projette en
_
x y
_
.
B.2.2 Travail demand
Il est plus important de livrer un code clair et qui compile sans warning,
mme sil ne rpond pas toutes les questions. Pour cela, vriez chaque
tape que votre programme compile et se lance correctement.
1. Crez un nouveau projet Imagine++ nomm Polyedres et crivez le main, qui
ouvre une fentre de taille xe.
2. Preparez des chiers poly.h, poly.cpp, buffer.h et buffer.cpp. Les deux
premiers serviront aux structures Point, TriangleFace et Polyedre ainsi
244
B. Examens B.2. Examen sur machine 2010: nonc
quaux fonctions sy rapportant, alors que les deux derniers seront spciques
la visualisation par z-buffer.
Polydres
3. Crez une structure Point correspondant un point ou un vecteur 3D de les-
pace (coordonnes relles, non entires).
4. Ecrivez les oprateurs daddition et de soustraction de deux Points et lopra-
teur de multiplication par une constante.
5. Ecrire une structure Polyedre qui contient :
(a) un tableau de Points (les sommets), quon appellera sommets, et son nombre
dlments. On fera attention au fait que le nombre de sommets nest pas une
constante.
(b) un tableau de TriangleFaces
1
et son nombre dlments.
(c) un point reprsentant le barycentre.
La structure TriangleFace (que lon crira) reprsente un triangle (face ou mor-
ceau de face du polydre). Il est constitu dun tableau de 3 index indiquant les in-
dices des sommets du triangle dans le tableau sommets, et dune couleur Color
(type dImagine++).
6. Ecrire une fonction set qui prend en argument un TriangleFace, 3 index (des
entiers) et une couleur pour les crire dans le TriangleFace.
7. Ecrire une fonction tetraedre(int w, int h, int rmax) qui renvoie un
Polyedre construit de la manire suivante :
4 sommets de coordonnes
_
0 0 1
_
,
_
1 0 0
_
et
_
1/2

3/2 0
_
.
4 triangles, joignant tout sous-groupe de 3 parmi ces 4 sommets.
Compltez cette fonction dans les trois items suivants :
8. Crez une fonction randCol qui renvoie une couleur tire au hasard et lutiliser
pour affecter une couleur chaque triangle. On rappelle que lon peut crer une
couleur avec la syntaxe suivante : Color col(r,v,b), o r, v et b prennent
des valeurs entre 0 et 255. Ne pas oublier dinitialiser le gnrateur alatoire dans
le main.
9. Multiplier les points par un facteur dchelle alatoire ne dpassant pas rmax.
10. Placez le centre (champ de Polyedre, cf 5c) en une position alatoire (abscisse
entre 0 et w, ordonne entre 0 et h, z = 0) et translater le polydre de ce vecteur
laide dune fonction translate que vous crivez.
11. Ecrivez une fonction detruit qui prend en paramtre un Polyedre et qui li-
bre la mmoire alloue.
Visualisation
12. Ecrivez une structure Buffer, qui a comme champs un tableau de hauteurs z et
une image couleur im de type Image<Color> (type Imagine++) qui reprsente
limage qui sera afche.
1. ne pas lappeler Triangle pour viter la confusion avec la structure du mme nom dans
Imagine++
245
B.2. Examen sur machine 2010 : nonc B. Examens
13. Ecrivez la fonction Buffer creeBuffer(int w,int h) : elle alloue le ta-
bleau des z et initialise les dimensions de limage :
buf.im = Image<Color>(w,h);
Par la suite, pour retrouver ces dimensions on pourra utiliser buf.im.width()
et buf.im.height().
14. Ecrivez la fonction reinit qui va mettre le z-buffer une grande valeur ngative
et limage tout en blanc. On rappelle que lon peut accder au pixel (i,j) dune
image im de la manire suivante : im(i,j).
15. Ecrivez une fonction affiche qui se contente de dessiner limage du buffer dans
la fentre dafchage. On pourra utiliser par exemple la fonction display(im)
qui afche limage im dans la fentre.
16. Ecrivez une fonction projette(Polyedre p, Buffer& buf) qui dessine la
projection de p dans buf. Celle-ci appelle simplement une fonction
projette(TriangleFace tr, Point
*
sommets, Buffer& buf)
sur chacun des triangles et que nous allons implmenter.
17. Cette mthode fonctionne en trois temps :
Elle cherche le rectangle (de coordonnes entires) englobant le triangle.
Pour chaque pixel de ce rectangle (attention sil dborde de limage !) il calcule
ses coordonnes barycentriques (voir quation B.1 en annexe) et vrie sil est
bien lintrieur du triangle (coordonnes toutes positives). Si cest le cas, son
z est la moyenne pondre par les coordonnes barycentriques des z des som-
mets du triangle.
Si le z est au-dessus de la valeur enregistre dans le z-buffer, on met jour le
Buffer comme indiqu dans lintroduction.
18. Ecrivez ces fonctions rectangle et barycentre et servez-vous en pour projette.
Animation
19. A ce stade, vous pouvez dj afcher un ttradre (dont on ne peut jamais voir
plus de 3 faces la fois).
20. Pour varier le point de vue, nous allons faire tourner le polydre autour dun axe
arbitraire :
(a) Ecrivez une fonction randomVect() renvoyant un vecteur 3D alatoire de
la sphre :
_
cos cos sin cos sin
_
,
avec [0, 2] et [0, ], reprsentant la direction dun axe de rotation.
(b) Pour effectuer la rotation du polydre, il suft de :
translater le polyhdre pour le centrer lorigine (fonction translate_inv
crire.
appliquer la rotation chacun des sommets (voir les quations B.2 et B.3
en annexe)
utiliser translate pour ramener le centre sa position initiale.
(c) Avant dafcher votre ttradre, faites-le tourner de faon arbitraire suivant
un vecteur donn par randomVect().
246
B. Examens B.2. Examen sur machine 2010 : nonc
21. Animer le ttradre en rotation, en le faisant tourner autour dun axe arbitraire
mais x une fois pour toutes. Multiplier le rsultat de randomVect par 0.1 pour
avoir un angle pas trop grand (environ 6

).
22. Faites tourner ainsi une bonne dizaine de ttradres, chacun ayant son propre
axe de rotation.
23. Question bonus : ajouter une fonction octaedre sur le modle de tetraedre
dont les 6 sommets sont les points
_
0 0 1
_
(points 0 et 5)
_
1 0 0
_
(points 1 et 3)
_
0 1 0
_
(points 2 et 4)
et les 8 faces donnes par les points dindices
_
0 1 2
_ _
0 2 3
_ _
0 3 4
_ _
0 4 1
_
_
5 1 2
_ _
5 2 3
_ _
5 3 4
_ _
5 4 1
_
Ajoutez dans votre scne des octadres en rotation.
Important : Quand vous avez termin, nettoyez la solution et crez une archive du
projet votre nom. Ne vous dconnectez pas avant que le surveillant ne soit pass vous
voir pour copier cette archive sur cl USB.
B.2.3 Annexe : les formules
Coordonnes barycentriques
Le point
_
x y
_
scrit A + B + C avec
d = (x
B
x
A
)(y
C
y
A
) (y
B
y
A
)(x
C
x
A
)
= ((x
B
x)(y
C
y) (y
B
y)(x
C
x))/d
= ((x
C
x)(y
A
y) (y
C
y)(x
A
x))/d
= ((x
A
x)(y
B
y) (y
A
y)(x
B
x))/d
(B.1)
d est laire (ventuellement ngative) du paralllogramme dont 3 sommets sont A, B et
C. Si cette aire est trop petite |d| < 1, renvoyer des coordonnes ngatives de manire
dire que le triangle est vide et donc que tout point est en-dehors.
Justication (pour les curieux) : on peut imposer + + = 1 et on se retrouve
alors avec le systme :
_
_
x
A
x
B
x
C
y
A
y
B
y
C
1 1 1
_
_
_
_

_
_
=
_
_
x
y
1
_
_
.
Par les formules de Cramer, on obtient
=

x x
B
x
C
y y
B
y
C
1 1 1

x
A
x
B
x
C
y
A
y
B
y
C
1 1 1

x
A
x x
C
y
A
y y
C
1 1 1

x
A
x
B
x
C
y
A
y
B
y
C
1 1 1

x
A
x
B
x
y
A
y
B
y
1 1 1

x
A
x
B
x
C
y
A
y
B
y
C
1 1 1

.
247
B.2. Examen sur machine 2010 : nonc B. Examens
Pour calculer de tels dterminants, on soustrait une colonne chacune des deux autres
et on dveloppe suivant la troisime ligne. Par exemple :

x x
B
x
C
y y
B
y
C
1 1 1

x x
B
x x
C
x
y y
B
y y
C
y
1 0 0

x
B
x x
C
x
y
B
y y
C
y

= (x
B
x)(y
C
y)(y
B
y)(x
C
x).
Rotation 3D autour dun axe
On code la rotation par un vecteur v dont la norme reprsente langle de rotation et la
direction laxe de rotation. On extrait dabord ceci de v :
=
_
x
2
v
+ y
2
v
+ z
2
v
v v/
c = cos
s = sin
(B.2)
(remarquez quon renormalise v pour quil soit de norme 1, si est trop petit, on se
contente de ne rien faire, car cest une rotation faible) puis
d = x
v
x + y
v
y + z
v
z
x

= d(1 c)x
v
+ cx sz
v
y + sy
v
z
y

= d(1 c)y
v
+ cy + sz
v
x sx
v
z
z

= d(1 c)z
v
+ cz sy
v
x + sx
v
y
(B.3)
248
B. Examens B.3. Examen sur machine 2009 : nonc
21 22 23
20 7 8 9 10
19 6 1 2 11
18 5 4 3 12
17 16 15 14 13
FIGURE B.3 Ordre des cases parcourues par la spirale dUlam et rsultat sur une
fentre 512 512.
B.3 Examen sur machine 2009 : nonc
B.3.1 Spirale dUlam
Le mathmaticien Stanislav Ulam, dans une priode dennui lors dune confrence,
prit son stylo et machinalement crivit les nombres entiers en spirale sur une feuille de
papier, puis noircit les cases contenant un nombre premier. Des diagonales comportant
un nombre remarquable de cases noires apparurent, que jusqu prsent personne na
pu expliquer. Le but est de reproduire la dcouverte dUlam sur machine.
On rappelle quun nombre premier na par dnition que 1 et lui-mme comme
diviseurs, lexception tant le nombre 1 qui nest pas premier.
B.3.2 Travail demand
Le travail se compose de deux parties. Dans la premire, on va chercher les nombres
premiers par un crible dErastosthne. Dans la seconde, on va dessiner la spirale par
un robot qui se deplace et remplit sa case si celle-ci correspond un nombre premier.
Consignes importantes : Il est plus important de livrer un code propre mais qui com-
pile sans warning, mme sil ne rpond pas toutes les questions. Pour cela, vriez
chaque tape que votre programme compile et se lance correctement.
1. Crez un nouveau projet Imagine++ nomm Ulam et crivez le main, qui de-
mande lutilisateur une taille de fentre carre et cre un tableau de variables
249
B.3. Examen sur machine 2009 : nonc B. Examens
boolennes premier contenant autant dlments que le nombre de cases. Ll-
ment dindice i du tableau sera true si le nombre i est premier.
2. Dans des chiers spars, crez des fonctions crible et spirale, qui pour lins-
tant ne font rien (nous les remplirons plus tard). Appelez ces fonctions depuis le
main. A ce stade, veriez dj que votre programme compile et se lance correc-
tement, mme sil ne fait pas beaucoup...
Crible
3. Crez une fonction init qui initialise le tableau premier en mettant a priori
tous les indices true sauf 1.
4. Crez une fonction barre_multiples qui met false les multiples (autres
que lui-mme) dun nombre i dans premier.
5. Ecrivez la fonction crible, qui appelle barre_multiples pour tous les nombres
premiers.
Robot
6. Dans la fonction spirale, crez un tableau pour mmoriser le fait quune case
a t visite ou non. Linitialiser.
7. Pour dessiner la spirale, on va crire un robot qui part du centre de la fentre,
a une direction initiale, et cherche tourner droite ds que la case na pas t
visite. Crez une structure Robot qui a une position et une direction code sur
un entier, qui prendra une valeur entre 0 et 3.
8. Crez la fonction avance qui fait bouger le robot dune case dans sa direction.
9. Crez la fonction tourne qui fait tourner le robot sur sa droite si la case na pas
t visite. Cette fonction indique en retour si le dplacement a pu seffectuer.
10. Dans la fonction spirale, faire partir le robot du centre de la fentre et se dpla-
cer en appelant tourne, ou avance si le quart de tour sest sold par un chec.
Le robot noircit sa position si elle correspond un nombre premier.
Important : Quand vous avez termin, crez une archive du projet votre nom. Ne
vous dconnectez pas avant que le surveillant ne soit pass vous voir pour copier cette
archive sur cl USB.
250
B. Examens B.4. Examen sur machine 2008 : nonc
FIGURE B.4 Ensemble de Mandelbrot. A droite : dessin progressif
B.4 Examen sur machine 2008 : nonc
B.4.1 Ensemble de Mandelbrot
Le but de lexercice demand est de dessiner lensemble de Mandelbrot Mde deux
faons diffrentes.
Dnition. Soit c un nombre complexe. On considre la suite dnie par z
0
= 0 et
z
n+1
= z
2
n
+ c. Lensemble M est constitu des points c pour lesquels cette suite ne
diverge pas en module.
Proprit. On peut montrer que ds que |z
n
| > 2 pour un certain n alors la suite
diverge.
B.4.2 Travail demand
Notes importantes :
Crer sur le bureau un projet portant son nom.
Ne pas se dconnecter, an que lenseignant rcupre le travail la n de lexa-
men.
Etapes :
1. Dnir une structure reprsentant les complexes et les fonctions associes stric-
tement ncessaires au programme.
2. Programmer une fonction dcidant, de faon approche aprs un nombre maxi-
mum ditrations, si un complexe appartient M.
3. Dans une fentre de taille n n correspondant la rgion {z = a +ib | 2 a
1, 1.5 b +1.5}, dessiner M. On dessinera en blanc les points de M, en noir
les autres.
4. La dcision c / M est prise aprs un certain nombre ditrations. Dessiner ces
points dune couleur intermdiaire entre noir et blanc, fonction de ce nombre
ditrations (cf. gure)
5. Raliser maintenant dans une nouvelle fonction un dessin progressif de M. Il
sagit de voir safcher un carr occupant toute la fentre, puis quatre, puis seize,
etc. an dobserver M se prciser au fur et mesure (cf gure). La couleur de
chaque carr doit tre celle du complexe correspondant au coin en haut a gauche
251
B.4. Examen sur machine 2008 : nonc B. Examens
du carr. On sappliquera en outre ne pas recalculer ni afcher deux fois la
mme valeur.
252
FIGURE B.5 Chemin le plus court...
B.5 Examen sur machine 2007 : nonc
B.5.1 Chemins entre deux points
Le but de lexercice demand est de gnrer et de dessiner tous les chemins pos-
sibles entre deux points en utilisant une fonction rcursive.
B.5.2 Travail demand
Notes importantes :
travailler sur le bureau Windows et dans un projet portant son nom.
ne pas utiliser de variable globale non constante.
1. Modliser un terrain par un tableau de boolens de dimensions NxN, N tant un
entier constant (prendre une petite valeur, par exemple N=12). Chaque case est
soit vide, soit occupe par un mur.
2. Gnrer un terrain alatoirement. Les bords du terrain sont toujours occups par
un mur. Les autres cases seront occupes par un mur 3 fois sur 10 en moyenne.
3. Dessiner le terrain (en le grossissant).
4. Dsigner la souris un point de dpart et un point darrive (qui ne devront pas
tre occups par un mur)
5. Programmer une fonction rcursive explorant tous les chemins entre ces deux
points. Un chemin ne peut pas traverser un mur et ne repasse pas deux fois au
mme endroit. Les chemins seront dessins et effacs au fur et mesure.
6. Rajouter de quoi afcher la longueur du chemin chaque fois que larrive est
atteinte.
7. Rajouter de quoi mmoriser et afcher la longueur du chemin le plus court, sil
existe.
8. Rajouter de quoi mmoriser et afcher le plus court chemin une la recherche
termine.
9. (Rpondre cette question dans une remarque la n de votre source) La recherche est
trs lente puisquelle est exhaustive. Si le seul but est de trouver un des chemins
les plus courts, il existe des meilleures solutions. Proposer, sans le programmer,
un moyen de limiter (et donc dacclrer) la recherche.
B.6 Examen sur machine 2006 : nonc
B.6.1 Voyageur de commerce par recuit simul
Soient n villes situes aux points M
1
, ..., M
n
, le problme du voyageur de commerce
consiste trouver un circuit ferm de longueur minimale passant par toutes les villes.
Il sagit donc de permuter les villes entre elles pour minimiser
l(M
1
, ..., M
n
) =
n1
i=1
d(M
i
, M
i+1
) + d(M
n
, M
1
)
o d(A, B) est la distance entre A et B. Une mthode classique pour trouver une solu-
tion approche ce problme est de procder un "recuit simul" :
1. Partir dun circuit quelconque C
2. Pour un "grand" nombre de fois
(a) Modier alatoirement C en un circuit D
(b) Si l(D) < l(C) alors remplacer C par D
(c) Sinon, remplacer C par D avec une probabilit e
(l(D)l(C))/T
, o T est une
constante choisir.
B.6.2 Travail demand
1. Travailler en local dans D:\nom_prenom ;
2. Y crer une solution Examen et lui ajouter un projet "Winlib" de nomvoyageur;
3. Un circuit sera mmoris comme un tableau de Pixel
2
de taille constante n
(valeur raisonnable : n = 20) ;
4. Faire et utiliser une fonction qui gnre un circuit correspondant n villes situes
en des positions alatoires dans la fentre ;
5. Faire et utiliser une fonction qui afche ce circuit
3
;
6. Faire et utiliser une fonction qui calcule la longueur de ce circuit ;
7. Faire et utiliser une fonction qui transforme un circuit en un autre par change
de deux villes choisies au hasard;
8. Implmenter le recuit simul sans ltape (c). Afcher le circuit et sa longueur
chaque fois quil change. Lalgorithme devrait rester coinc dans un minimum
local ;
9. Rajouter ltape (c). On rappelle que double(rand())/RAND_MAX est un nombre
alatoire entre 0 et 1. Ajuster T pour ne pas toujours remplacer C par D!
10. Choisir maintenant une valeur de T qui dcrot en 1/

t, o t est le nombre des-


sais, de faon accepter de plus en plus rarement un D plus long que C ;
2. On rappelle que Pixel est une structure de la Winlib, contenant les champs double x et double
y
3. Les fonctions de la Winlib telles que OpenWindow ou DrawLine (confre Annexe C du poly)
seront trs utiles.
B. Examens B.6. Examen sur machine 2006 : nonc
11. Pour vraiment faire marcher lalgorithme, il faut programmer une nouvelle faon
de transformer un circuit en un autre. La voici : choisir deux villes au hasard et
retourner le chemin entre les deux villes, cest--dire
transformer (M
1
, ..., M
n
) en (M
1
, ..., M
i1
, M
j
, M
j1
, ..., M
i+1
, M
i
, M
j+1
, ..., M
n
)
Programmer cette nouvelle faon de faire ;
12. A la n de lexamen, ne pas vous dconnecter et attendre que le surveillant
passe recuprer votre travail.
255
B.7. Examen sur machine 2005: nonc B. Examens
B.7 Examen sur machine 2005 : nonc
Le but de ce problme est de crer et danimer un modle 3D et dafcher un mou-
vement de cet objet vu par une camra. Pour cela, nous procdons par tapes.
B.7.1 Construction du Modle 3D
1. Travailler en local sous D:\Info\ : crer un dossier nom_prenom dans D:\Info\
(remplacer nom_prenom par son nom et son prnom!).
2. Crer une solution Exam dans D:\Info\nom_prenom\ et ajouter un projet "Win-
lib" (Winlib5 Projet) de nom logo la solution Exam.
3. Ajouter au projet deux chiers vect.cpp et vect.h dans lesquels vous dclare-
rez et dnirez lendroit appropri :
(a) une structure vect reprsentant un vecteur de R
3
(de composantes double x,y,z
dans la base canonique (O,i,j,k) de R
3
),
(b) un oprateur + entre deux vect.
4. On souhaite construire une structure reprsentant un ttradre. Ajouter au projet
deux chiers tetra.cpp et tetra.h et :
(a) dnir une structure tetra contenant 4 sommets (vect M[4]) et une cou-
leur Color c,
(b) dclarer et dnir une fonction regulier ne prenant aucun argument et
retournant un ttradre rgulier de couleur rouge. Pour information, les
quatre points suivant forment un ttradre rgulier :
{1, 0, 0}, {
1
2
,

3
2
, 0}, {
1
2
,

3
2
, 0}, {0, 0,

2}.
On rappelle que la fonction sqrt est dnie dans #include <cmath>.
B.7.2 Projection : du 3D au 2D
Nous nous proccupons maintenant de la projection 2D de ces reprsentations 3D.
Ajouter au projet deux chiers camera.cpp et camera.h dans lesquels vous dclare-
rez et dnirez :
1. une structure camera contenant les variables suivantes : int u0,v0 (le centre
de limage), double x0 (lloignement), et double f (la focale). La gure ci-
aprs schmatise la reprsentation dune camra.
256
B. Examens B.7. Examen sur machine 2005 : nonc
2. une fonction projette qui projette un point 3D (vect M) par lintermdiaire
dune camra (camera c). La fonction projette retourne un point 2D de type
Pixel. Nous rappelons que le type Pixel est dni dans la WinLib :
struct Pixel{
int x,y;
};
Par consquent, ne pas oublier dajouter les lignes #include <win> et using namespace Win
dans le chier camera.h. Les coordonnes (u,v) du projet dun point 3D de
coordonnes (x,y,z) sont :
u = c.u0 +
c.f * y
c.x0-x
v = c.v0
c.f * z
c.x0-x
.
B.7.3 Afchage lcran
Nous sommes maintenant prts pour afcher notre objet ttradre lcran.
1. Dans les chiers tetra.cpp et tetra.h, dclarer et dnir une fonction affiche
prenant en argument un camera c, un tetra T. Cette fonction dessine un t-
tradre tetra T dans sa couleur, vu par la camera camera c. Pour cela, on uti-
lisera la fonction projette (dclare et dnie dans camera.h et camera.cpp),
ainsi que la fonction DrawLine (dnie dans la WinLib) pour tracer un segment
entre deux Pixel. La dnition de la fonction DrawLine est la suivante :
void DrawLine(const Pixel& p1,const Pixel& p2,const Color& col).
Le projet dun ttradre doit ressembler la gure ci-dessous (gure-a) :
2. Dans le chier main.cpp, crer un ttradre rgulier et lafcher lcran dans
une image de taille 512 512. Pour cela, crer une camra camera C de para-
mtres :
u0=256,v0=256, double x0=10, et double f=500.
B.7.4 Animation du ttradre
Nous cherchons maintenant animer notre modle 3D.
257
B.7. Examen sur machine 2005 : nonc B. Examens
1. Rotation dun vect : dans vect.h et vect.cpp, ajouter une fonction rotate
qui applique une rotation dangle double alpha autour de laxe Oz sur un vec-
teur vect a et qui renvoie un vect b. Les fonctions cos et sin sont dnies
dans #include <cmath>.
b.x = cos() a.x sin() a.y
b.y = sin() a.x + cos() a.y
b.z = a.z
2. Rotation dun tetra : dans tetra.h et tetra.cpp, dclarer et dnir une fonc-
tion rotate qui applique une rotation dangle double alpha autour de laxe
Oz sur un tetra et qui retourne un tetra.
3. Premire animation : dans main.cpp, animer le ttradre dun mouvement de
rotation sur place (pour linstant sans ce soucier de leffacer) : dans une boucle
for (int i=0;i<10000;i++), appliquer une rotation dangle
i
15
chaque
pas de temps. On pourra utiliser la fonction MilliSleep(10).
4. Deuxime animation : maintenant, nous souhaitons afcher et effacer notre ttra-
dre en mouvement :
(a) dans les chiers tetra.cpp et tetra.h, dclarer et dnir une fonction
changeColor prenant en argument un tetra T et un Color c et retour-
nant une structure tetra (de couleur Color c et dont les sommets pos-
sdent les mmes coordonnes que celles de tetra T),
(b) ajouter une fonction pour effacer un ttradre (utiliser les fonctions dnies
prcdemment !).
(c) dans main.cpp, animer le ttradre dun mouvement de rotation en effa-
ant proprement chaque pas de temps.
B.7.5 Un modle plus labor
1. Dans le chier main.cpp, crer un tableau tetra tetras[4] de 4 structures
tetra. Initialiser les 4 ttradres du tableau de manire produire la gure re-
presente ci-dessus (gure-b). Pour cela, gnrer 4 ttradres rguliers et ap-
pliquer lune des 4 translations suivantes sur chacun dentre eux :
{1, 0, 0}, {
1
2
,

3
2
, 0}, {
1
2
,

3
2
, 0}, {0, 0,

2}.
Dnir pour cela une fonction translate pour gnrer le translat dun ttra-
dre tetra T par un vecteur vect t (utiliser loprateur + de vect).
2. Finalement, nous allons animer notre objet tetra tetras[4] dun mouve-
ment complexe et afcher ce dernier dans la camra camera C. A chaque pas
de temps dans la boucle for (int i=0;i<10000;i++), appliquer une rota-
tion dangle
i
15
suivie par une translation de vecteur :
vect t={-12+8
*
cos(i/150.),8
*
sin(i/150.0),-3.0}.
258
B. Examens B.8. Examen sur machine 2004 : nonc
B.8 Examen sur machine 2004 : nonc
B.8.1 Calcul de lexponentielle dun nombre complexe
Le but de cet exercice est de calculer lexponentielle dun nombre complexe et de
sen servir pour calculer le sinus et le cosinus dun angle.
1. Partir dun projet "console" Win 32 Basic Console
2. Ajouter au projet deux chiers complexe.cpp et complexe.h dans lesquels
vous dclarerez et dnirez lendroit appropri :
(a) une structure complexe (et pas complex qui existe dj) reprsentant un
nombre complexe sous forme cartsienne (partie relle, partie imaginaire)
(b) les oprateurs +,
*
entre deux complexe
(c) loprateur / dnissant la division dun complexe par un double
3. On souhaite approximer la fonction exponentielle en utilisant son dveloppe-
ment en srie entire :
e
z
=
+

i=0
z
i
i!
crire une fonction exponentielle, prenant en argument un complexe z et
un int n, et qui retourne la somme :
n

i=0
z
i
i!
4. crire une fonction cos_sin qui renvoie le cosinus et le sinus dun angle en
utilisant le dveloppement limit de e
i
lordre n (on passera donc, en plus de
langle theta, lentier n en argument). On rappelle que :
e
i
= cos + i sin
5. Tester la fonction cos_sin pour diffrentes valeurs de theta et n. Vrier quavec
n = 15 et =

6
, on obtient une bonne approximation des valeurs du cosinus
(

3
2
0.866025404) et du sinus (
1
2
).
B.8.2 Compression RLE
Dans cet exercice nous allons implmenter lune des plus anciennes mthodes de
compression : le codage RLE (Run Length Encoding). Le principe consiste dtecter
une donne ayant un nombre dapparitions conscutives qui dpasse un seuil xe, puis
remplacer cette squence par deux informations : un chiffre indiquant le nombre de
rptitions et linformation rpter. Aussi, cette mthode remplace une squence par
une autre beaucoup plus courte moyennant le respect du seuil (que nous xerons, par
simplicit, 0). Elle ncessite la prsence de rptitions relativement frquentes dans
linformation source compresser.
Cette mthode prsente peu davantages pour la compression de chier texte. Par
contre, sur une image, on rencontre rgulirement une succession de donnes de mme
259
B.8. Examen sur machine 2004 : nonc B. Examens
valeur : des pixels de mme couleur.
Sur une image monochrome (un fax par exemple), lensemble compresser est une
succession de symboles dans un ensemble deux lments. Les lments peuvent tre
soit des 255 (pixel allum=blanc), soit des 0 (pixel teint=noir) ; il est relativement facile
de compter alternativement une succession de 0 et de 255, et de la sauvegarder telle
quelle.
source : 0 0 0 0 0 0 0 0 0 0 255 255 255 255 255 255 255 255 0 0 0 0 0 255 255 255 255
compression : 10 8 5 4
Cependant, une convention reste prendre : savoir par quel pixel commencera la suc-
cession de chiffres. Nous considrerons que le premier nombre reprsente une succes-
sion de 0. Si la source ne commence pas par des pixels noirs (un zro) il faut alors
commencer la chaine codante par un 0 pour indiquer labsence de pixel noir.
Dans ce qui suit nous allons encoder une image binaire (constitue de 0 et de 255
stocks dans un tableau de byte) dans un tableau de int.
1. Partir dun projet "Winlib" (Winlib5 Project).
2. Dans la fonction main,
(a) Dclarer les variables de type const int w et h representant la largeur et
la hauteur de limage encoder. Ces variables pourront tre initialises la
valeur 256.
(b) Dclarer le tableau de byte image_source de taille w h (rappel : le pixel
(i, j) est llment i + j w du tableau).
(c) Dclarer le tableau de byte image_decodee de taille w h (rappel : le pixel
(i, j) est llment i + j w du tableau).
(d) Dclarer le tableau de int image_encodee de taille w h + 1
3. Crer une fonction affiche_image de dclaration :
void affiche_image( byte image[], int w, int h );
Cette fonction afche limage stocke dans image de largeur w et de hauteur h
laide de la fonction PutGreyImage de la Winlib.
4. Dans la fonction main, tester la fonction affiche_image avec image_source, sans
lavoir initialise.
5. Crer une fonction remplir_rectangle de dclaration :
void remplir_rectangle(
byte image[], int w, int h,
int xul, int yul, int height, int width );
Cette fonction dessine un rectangle blanc dans une image en mettant 255 tous
les pixels de limage contenus dans le rectangle. Limage dans laquelle est dessin
le rectangle plein est donne par le tableau image et les dimensions w et h de
limage. Le rectangle remplir est donn par les coordonnes xul et yul de son
coin suprieur gauche ainsi que par ses dimensions height et width.
6. Nous allons maintenant tester la fonction remplir_rectangle. Pour ce faire :
(a) Remplir limage avec des 0.
260
B. Examens B.8. Examen sur machine 2004 : nonc
(b) Utiliser la fonction remplir_rectangle sur cette image pour y placer un rec-
tangle de votre choix.
(c) Afcher cette image laide de affiche_image
7. Crer une fonction RLE_encode de dclaration :
void RLE_encode(
byte source_image[], int w, int h,
int compression[], int &comp_size );
Limage compresser est donne par sa largeur w, sa hauteur h et par un ta-
bleau source_image. Le rsultat de la compression est stock dans le tableau
compression et le nombre dlments utiliss dans ce tableau comp_size. (pour
rappel, le tableau prvu pour recevoir limage comprime est dclare de ma-
nire statique dans main et w h + 1 nest quun majorant de la taille de limage
comprime.)
8. Crer une fonction RLE_decode de dclaration :
void RLE_decode( int compression[], int comp_size,
byte decomp_image[] );
Limage dcompresser est donne par un tableau compression et la taille des
donnes dans ce tableau comp_size. Le resultat de la dcompression est stock
dans le tableau decomp_image.
9. Dans la fonction main et laide des fonctions prcdemment dnies :
(a) Remplir dans le tableau image_source deux rectangles de votre choix.
(b) Afcher cette image
(c) Encoder image_source dans image_encodee
(d) Decoder image_encodee dans image_decodee
(e) Alaide dune boucle, vrier que les deux images image_source et image_decodee
sont identiques.
(f) Afcher limage dcode et valider la vrication prcdente de manire
visuelle.
10. Crer une fonction remplir_diagonale de dclaration :
void remplir_diagonale( byte image[], int w, int h );
Cette fonction met tous les pixels (i, j) tels que i == j de image 255.
11. Tester la fonction remplir_diagonale de la mme faon que la fonction remplir_rectangle
a t teste.
12. On xe w = 256 et h = 128
(a) Remplir limage source de zros.
(b) Remplir dans limage source un rectangle dont le coin suprieur gauche est
en (20, 20) de dimensions (80, 80)
(c) Remplir dans limage source un second rectangle dont le coin suprieur
gauche est en (120, 10) de dimensions (100, 100)
(d) Compresser limage source et afcher la taille de limage compresse comp_size
(e) Remplir nouveau limage source de zros.
261
B.8. Examen sur machine 2004 : nonc B. Examens
(f) Remplir dans limage source la diagonale.
(g) Compresser limage source et afcher la taille de limage compresse comp_size
13. On xe w = 1024 et h = 1024
(a) Un plantage se produit.
(b) Identier le problme et modier le programme pour quil fonctionne avec
les valeurs de w et h donnes.
262
B. Examens B.9. Examen sur machine 2003: nonc
B.9 Examen sur machine 2003 : nonc
B.9.1 Crible dratosthne
Rappel : un entier naturel est dit premier sil nest divisible que par 1 et lui-mme, et quil
est diffrent de 1.
Le but de cet exercice est de dresser par ordre croissant la liste des nombres pre-
miers. On utilisera pour cela le crible dratosthne, qui repose essentiellement sur le
fait que les diviseurs ventuels dun entier sont plus petits que lui. La mthode consiste
parcourir dans lordre croissant la liste des nombres entiers candidats (par exemple
initialement tous les nombres de 2 999, si lon se restreint aux entiers infrieurs
1000), et, chaque nombre rencontr, retirer de la liste des nombres candidats tous
les multiples de celui-ci. Une fois la liste parcourue, il ne restera que les nombres pre-
miers.
On recherche les nombres premiers infrieurs ou gaux n, n tant un nombre
entier suprieur 2, intialement x 100. Plutt que de grer une liste dentiers et den
enlever des nombres, on travaillera avec un tableau de n boolens avec la convention
que la i
e
case du tableau contiendra true si i est premier, et false sinon.
1. Partir dun projet "console" (Win 32 Basic Console).
2. Dans la fonction main, crer le tableau de boolens et le remplir avec des true.
3. Crer une fonction multiples qui prend en argument le tableau, sa taille et un
entier a, et qui tourne false les lments du tableau correspondants des
multiples de a (except a, bien sr).
4. Utiliser la fonction multiples de faon approprie dans la fonction main, dans
une boucle parcourant le tableau, an dexcuter le crible dratosthne. Il est
inutile de considrer les multiples des nombres qui ne sont pas premiers ( ce
propos ne pas oublier que 1 consitue un cas particulier).
5. Afcher le nombre de nombres premiers infrieurs ou gaux 211, ainsi que les
nombres premiers en question.
6. Vrier que lon peut sarrter

n.
B.9.2 Calcul de par la mthode de Monte Carlo
On dsigne par mthode de Monte Carlo une mthode de rsolution dun problme
mathmatique laide de suites de nombres alatoires convergeant vers le rsultat,
en rfrence aux nombreux casinos mongasques. Le mthode de Monte Carlo clas-
sique pour calculer une valeur approche de consiste tirer alatoirement un grand
nombre n de fois des points (x, y) du plan avec 1 x 1, 1 y 1 puis de d-
terminer quelle est la proportion p
n
de ces points qui sont situs dans le disque unit.
Comme la surface de ce dernier vaut , on peut montrer que lim
n+
p
n
=

4
. Le but
de cet exercice est de programmer ce calcul et une visualisation graphique de celui-ci.
Fonctions utiles
Nombres pseudo-alatoires
#include <cstdlib>
using namespace std;
void srand(unsigned int seed); // Initialise le gestionnaire de nombres
263
B.9. Examen sur machine 2003 : nonc B. Examens
// pseudo-alatoires
int rand(); // Retourne un nombre pseudo-alatoire entre 0 et RAND_MAX
// inclus
Temps
#include <ctime>
using namespace std;
long time(...); // time(0) renvoie le nombre de secondes coules
// depuis le 1er janvier 1970
WinLib
#include <win>
using namespace Win;
void DrawPoint(int x,int y,const Color& col); // Affiche un pixel
// lcran
void DrawCircle(int xc,int yc,int r,const Color& col); // Affiche un cercle
// centr en (xc,yc),
// de rayon r
// et de couleur col
1. Ajouter un nouveau projet WinLib nomm MonteCarlo la solution et le dnir
cmme projet de dmarrage.
2. Dnir dans main.cpp une constante globale taille_fenetre et lutiliser
dans lappel OpenWindow. Dans tout ce qui suit, la fentre sera carre, repr-
sentant lensemble des points dont les coordonnes sont entre -1 et 1.
3. Ajouter au projet deux chiers Point.h et Point.cpp dans lesquels vous d-
clarerez et dnirez lendroit appropri :
(a) une structure PointPlan reprsentant des points du plan (il faut en particu-
lier pouvoir avoir des coordonnes entre 1 et 1)
(b) une fonction GenerePoint retournant un PointPlan dont les coordonnes
sont tires alatoirement entre -1 et 1. Ne pas oublier quen C++, la division
de deux entiers est la division entire !
(c) une fonction AfchePoint, prenant en argument la taille de la fentre daf-
chage, une couleur et un PointPlan, et qui afche ce dernier lcran dans la
couleur prcise (penser renormaliser !).
4. Appeler la procdure dinitialisation du gestionnaire de nombre pseudo-alatoires
au dbut de main avec srand(unsigned int(time(0)));. Penser aux #include
et using namespace correspondants.
5. Dessiner lcran le cercle unitaire au dbut de main.cpp.
6. Dnir une constante globale nb_iterations dans main.cpp reprsentant le
nombre ditrations effectues. Construire une boucle effectuant nb_iterations
fois les oprations suivantes :
(a) Gnrer un point pseudo-alatoire
264
B. Examens B.9. Examen sur machine 2003 : nonc
(b) Incrmenter une variable compteur si ce point est dans le disque unitaire
(cest--dire si x
2
+y
2
1). On rajoutera une fonction retournant le carr de
la norme dun point.
(c) Afcher ce point lcran, en bleu sil est dans le disque, en rouge sinon.
7. Afcher la n du programme la valeur de dduite.
8. Varier nb_iterations an davoir un bon compromis temps dafchage / pr-
cision de la valeur calcule. Que constate-t-on? Pourquoi ? (Rpondre en com-
mentaire du programme)
B.9.3 Serpent
FIGURE B.6 A gauche : calcul de . A droite : un serpent.
Le but de cet exercice est de programmer un serpent qui se dplace sur un terrain
rectangulaire en vitant de se mordre lui-mme. Pour cela structures et tableaux seront
utiles. Ne pas oublier de compiler et tester chaque tape... NB : certaines questions
sont indpendantes.
1. Structure : Partir dun projet WinLib. Crer une structure point mmorisant
deux coordonnes entires x et y.
2. Dessin : On va dessiner un serpent de dplaant sur un terrain de dimensions
(w,h). Pour bien le visualiser, on lafchera dans un fentre de dimensions (w
*
z,h
*
z)
(Prendre par exemple (w,h,z)=(40,30,10)).
(a) Programmer une fonction void dessine(point p,int zoom,Color c)
utilisant la fonction
FillRect(x,y,w,h,c) pour dessiner un carr plein de coin suprieur
gauche (p.x
*
zoom,p.y
*
zoom), de ct zoom-1 et de couleur c.
(b) Ecrire une fonction void dessine(point s[],int n,int zoom) des-
sinant un serpent constitu des points s[0] s[n-1], s[0] tant la tte.
On pourra tracer le corps du serpent en rouge et sa tte en bleu (gure B.6).
(c) Initialiser un serpent dans un tableau de dimension constante n=10 et laf-
cher.
3. Divers : programmer quatre fonctions utiles pour la suite :
(a) Une fonction bool operator==(point a,point b) retournant vrai si
les points a et b sont gaux, ce qui permettra dcrire un test comme if (p1==p2).
265
B.9. Examen sur machine 2003 : nonc B. Examens
(b) Une fonction bool cherche(point s[],int n,point p) retournant
vrai si le point p se trouve dans le tableau s de taille n.
(c) Une fonction void decale(point s[],int n,point p) qui dcale le
tableau s vers le haut (i.e. s[i]=s[i-1]) et range p dans s[0]. Cette fonc-
tion sera utile pour la suite !
(d) Une fonction point operator+(point a,point b) calculant la somme
de deux points (considrs comme des vecteurs).
4. Avancer dune case : Le serpent peut avancer dans une des quatre directions. On
mmorise la direction de son dplacement par un entier de 0 3, avec la conven-
tion (0,1,2,3)=(est,sud,ouest,nord).
(a) Crer et initialiser une variable globale const point dir[4] telle que
dir[d] correspond un dplacement dans la direction d. Ainsi, d[0]={1,0},
d[1]={0,1}, etc.
(b) Ecrire et tester une fonction void avance(point s[],int n,int d,int zoom)
utilisant ce qui prcde pour :
Avancer le serpent s,n dans la direction d.
Afcher sa nouvelle position sans tout re-dessiner (effacer la queue et des-
siner la tte).
5. Avancer : Ecrire les quatre fonctions suivantes :
bool sort(point a,int w,int h) qui retourne vrai si le point a est en
dehors du terrain.
void init_rand() qui initialise le gnrateur alatoire.
void change_dir(int& d) qui change alatoirement d en d-1 une fois sur
20, en d+1 une fois sur 20 et ne modie pas d les 18 autres fois, ce qui, compte
tenu de la convention adopte pour les directions, revient tourner droite ou
gauche de temps en temps. Attention bien conserver d entre 0 et 3.
bool ok_dir(point s[],int n,int d,int w,int h) qui teste si le d-
placement du serpent dans la direction d ne va pas le faire sortir.
puis les utiliser pour faire avancer le serpent dans une boucle de 500 pas de
temps, en vriant quil ne sort pas du terrain. Penser rajouter un MilliSleep()
si le dplacement est trop rapide (sur certains PC de lENPC, la WinLib ne sera
pas jour et lafchage sera lent et saccad... Ignorer.)
6. Ne pas se manger : modier ok_dir() pour que le serpent ne se rentre pas dans
lui-mme.
7. Escargot : le serpent peut se retrouver coinc sil senroule sur lui-mme. Program-
mer une fonction bool coince(point s[],int n,int w,int h) qui teste
si aucune direction nest possible et modier la boucle principale pour terminer
le programme dans ce cas.
8. Grandir (question nale difcile) :
Changer le programme pour que le tableau s soit allou dans le tas (n pourra
alors tre variable).
Un pas de temps sur 20, appeler, la place de avance(), une fonction void allonge()
qui avance dans le direction d sans supprimer la queue. Du coup, s doit tre
rallou et n augmente de 1. Ne pas oublier de dsallouer lancienne valeur de
s. Bien grer lafchage, etc. Cette fonction sera dclare void allonge(point
*
& s,int& n,
int d,int zoom).
266
B. Examens B.10. Devoir maison 2007: nonc
B.10 Devoir maison 2007 : nonc
Enonc Erreurs et Quafche ce programme ?
Dans cet exercice, on vous demande deux choses :
1. Corriger les 10 erreurs contenues dans le code du programme suivant, directe-
ment sur la feuille dnonc ;
2. Donner, en le justiant, lafchage que produit ce programme sur la sortie stan-
dard (cest--dire lcran, via la commande cout << ... ;). Procdez avec
mthode en faisant attention aux retours de fonction, et lquilibre ncessaire
entre appels aux constructeurs/destructeurs.
1 include<iostream>
2 namespace std;
3
4 class A {
5 int a;
6 A();
7 A(int b);
8 A(const A& b);
9 void operator=(const A& b);
10 void add(int b);
11 ~A();
12 }
13
14 void A() {
15 cout << "C1" << endl;
16 a=0;
17 }
18
19 void A(int b) {
20 cout << "C2 " << b << endl;
21 a=b;
22 }
23
24 void A(const B& b){
25 cout << "C3 " << b.a << endl;
26 a=b.a;
27 }
28
29 void A::operator=(const A& b){
30 cout << "E " << a << " " << b.a << endl;
31 a.a=b.a;
32 }
33
34 void A::add(int b) {
35 cout << "A " << a << " " << b << endl;
36 a+=b;
37 }
267
B.10. Devoir maison 2007 : nonc B. Examens
38
39 void F(A d,A& e) {
40 d.add(3);
41 e.add(4);
42 f=d;
43 return f;
44 }
45
46 void ~A() {
47 cout << "D " << a << endl;
48 }
49
50 int main()
51 {
52 A a;
53 A b(1);
54 A c(b);
55 b.add(4);
56 c=F(b,a);
57 }
268
B. Examens B.11. Devoir maison 2006 : nonc
B.11 Devoir maison 2006 : nonc
B.11.1 Enonc Tours de Hanoi
Principe et rgles
Le jeu est constitu de 3 tiges, A, B et C, sur lesquelles sont empils des anneaux de
taille dcroissante, que lon numrotera de N 1.
FIGURE B.7 Conguration initiale
Au dbut du jeu, tous les anneaux sont empils sur la tige A (voir gure B.7). Lob-
jectif est de transfrer tous les anneaux sur la tige C. Il nest possible de dplacer les
anneaux que un par un, et il nest pas possible de poser un anneau sur un autre de
taille infrieure.
On dsignera par le terme configuration une disposition des anneaux sur les
tiges (voir gure B.8).
FIGURE B.8 Exemple de conguration
On dsignera par le terme dplacement de la tige X la tige Y le fait denlever
lanneau situ au sommet de la pile de la tige X pour le positionner au sommet de la
pile de la tige Y (voir gure B.9).
Objectif
Lobjectif ici est dcrire un programme qui afche la suite des dplacements ra-
liser pour rsoudre le problme avec n anneaux, sous la forme suivante :
numero_anneau : tige_origine > tige_destination
On souhaite de plus afcher ltat du jeu (les anneaux prsents sur chaque tige) aprs
chaque dplacement.
269
B.11. Devoir maison 2006 : nonc B. Examens
FIGURE B.9 Exemple de dplacement
Exemple pour 2 anneaux (la gure B.10 en donne une reprsentation graphique) :
A> 2 1
B> . .
C> . .
deplacement: 1 : A->B
A> 2 .
B> 1 .
C> . .
deplacement: 2 : A->C
A> . .
B> 1 .
C> 2 .
deplacement: 1 : B->C
A> . .
B> . .
C> 2 1
FIGURE B.10 Solution du problme pour N=2
270
B. Examens B.11. Devoir maison 2006 : nonc
Ce problme apparemment complexe se traite en fait simplement : pour transfrer
n anneaux dune tige X une tige Y, il "suft" deffectuer :
le transfert des n-1 anneaux suprieurs de la tige X sur la troisime tige Z
le dplacement de lanneau n de la tige X la tige destination Y
le transfert des n-1 anneaux de la tige Z la tige Y
Pour transfrer n-1 anneaux, on applique la mme mthode : transfert de n-2 anneaux
de la tige origine une tige temporaire, dplacement dun anneau de la tige origine
la tige destination, transfert des n-2 anneaux de la tige temporaire la tige destination
...
Questions
1. Dclarer une constante N, que lon xera 10, et qui reprsentera le nombre dan-
neaux du problme rsoudre.
2. Crer un objet capable de stocker une conguration. On pourra par exemple re-
prsenter chaque tige par un tableau de taille N, et chaque anneau par un entier
entre 1 et N. Vous utiliserez au choix une structure ou une classe, selon ce qui
vous semble le plus pertinent, ou dfaut ce avec quoi vous tes le plus laise.
3. Programmer de quoi initialiser une conguration, cest dire lui donner sa valeur
de dpart (tous les anneaux sur la premire tige).
4. Programmer une fonction permettant dafcher une conguration lcran, par
exemple sous la forme utilise ci-dessus pour lexemple pour N=2.
5. Programmer une fonction ralisant un dplacement : pour une tige origine et une
tige destination spcies, elle prlve le pion suprieur sur la tige origine et le
place sur la tige destination.
6. En vous basant sur la mthode propose, programmer une fonction ralisant un
transfert de k anneaux dune tige une autre, et afchant au fur et mesure les
dplacements effectus, et ltat du jeu aprs chaque dplacement.
7. En utilisant les lments prcdents, crire une fonction main() qui rsout le pro-
blme des tours de Hanoi de taille N et afche la solution complte.
Questions complmentaires
1. Si lon avait souhait nafcher que la liste des dplacements effectuer sans
afcher chaque fois les congurations intermdaires, le programme aurait t
beaucoup plus simple. Pourquoi ? Ecrire la fonction correspondante (5 lignes de
code environ).
2. Pour rsoudre le problme avec N anneaux, combien de dplacements sont n-
cessaires ?
271
B.12. Devoir maison 2004 : nonc B. Examens
B.12 Devoir maison 2004 : nonc
B.12.1 Tableau dexcution
Pour ce premier exercice, il sagit dexcuter pas pas le programme suivant :
1 #include <iostream>
2 using namespace std;
3
4 int hop(int x) {
5 x = x+2;
6 return x;
7 }
8
9 int hip(int& y) {
10 y = y
*
2;
11 return y+1;
12 }
13
14 int f(int& z) {
15 int t = z+3;
16 z=2
*
t;
17 if (z>20)
18 return z;
19 return f(t);
20 }
21
22 int main() {
23 int a;
24 a = hop(1);
25 int b;
26 b = hip(a);
27 a = f(a);
28 return 0;
29 }
Pour cela, remplissez le tableau ci-dessous, en crivant, si elles existent, les va-
leurs des variables du programme pour chaque excution de ligne (selon lexemple
du cours).
272
B. Examens B.12. Devoir maison 2004: nonc
Conventions :
mettre le numro de la ligne quon vient dexcuter (ventuellement, on peut
dcouper lexcution dune mme ligne en plusieurs tapes)
on peut utiliser le symbole pour rpter la valeur dune variable la ligne sui-
vante
une case vide signie que la variable nexiste plus ou pas encore
pour une variable qui nest autre quune rfrence sur une autre variable x, on
indiquera [x] ;
sil y a rcursivit, indicer par un entier plusieurs appels imbriqus une mme
fonction (f
1
, f
2
, ...)
Ligne a
m
b
m
x
hop
ret
hop
y
hip
ret
hip
z
f
1
t
f
1
ret
f
1
z
f
2
t
f
2
ret
f
2
B.12.2 Constructeurs
Dans ce deuxime exercice, on vous demande tout simplement de donner et jus-
tier lafchage que produit lexecution du programme suivant sur la sortie standard
(cest--dire lcran, via la commande cout<<...) :
1 #include<iostream>
2 using namespace std;
3
4 class Vecteur
5 {
6 int tab[2];
7 void affiche() const;
8 public:
9 Vecteur();
10 Vecteur(int n);
11 Vecteur(const Vecteur &right);
12 ~Vecteur();
13 void operator=(const Vecteur& right);
14 Vecteur operator+(const Vecteur& right) const;
15 int operator
*
(const Vecteur right) const;
16 int& getX(int i);
17 };
18
19 Vecteur::Vecteur()
20 {
21 for(int i=0;i<2;i++) tab[i]=0;
22 cout<<"Contructeur par defaut: ";
23 affiche();
273
B.12. Devoir maison 2004: nonc B. Examens
24 }
25 Vecteur::Vecteur(int x)
26 {
27 for(int i=0;i<2;i++) tab[i]=x;
28 cout<<"Constructeur de remplissage: ";
29 affiche();
30 }
31 Vecteur::Vecteur(const Vecteur &right)
32 {
33 for(int i=0;i<2;i++) tab[i]=right.tab[i];
34 cout<<"Constructeur par copie: ";
35 affiche();
36 }
37 Vecteur::~Vecteur()
38 {
39 cout<<"Destructeur: ";
40 affiche();
41 }
42 void Vecteur::operator=(const Vecteur& right) {
43 for(int i=0;i<2;i++) tab[i]=right.tab[i];
44 cout<<"Operateur de copie: ";
45 affiche();
46 }
47 Vecteur Vecteur::operator+(const Vecteur& right) const {
48 Vecteur Res;
49 for(int i=0;i<2;i++) Res.tab[i]=tab[i]+right.tab[i];
50 return Res;
51 }
52 int Vecteur::operator
*
(const Vecteur right) const {
53 int Res=0;
54 for(int i=0;i<2;i++) Res+=tab[i]
*
right.tab[i];
55 return Res;
56 }
57 int& Vecteur::getX(int i){
58 return tab[i];
59 }
60 void Vecteur::affiche() const{
61 for (int i=0;i<2;i++)
62 cout<<tab[i]<<" ";
63 cout<<endl;
64 }
65 void affiche(Vecteur vec){
66 cout<<"Affiche: ";
67 for (int i=0;i<2;i++)
68 cout<<vec.getX(i)<<" ";
69 cout<<endl;
70 }
71 int main()
72 {
274
B. Examens B.12. Devoir maison 2004 : nonc
73 Vecteur A;
74 cout<<endl;
75 Vecteur B(-2);
76 cout<<endl;
77 Vecteur C=B;
78 cout<<endl;
79 B.getX(1)=3;
80 affiche(C);
81 cout<<endl;
82 cout<<"Produit scalaire: "<<B
*
C<<endl;
83 cout<<endl;
84 A=B+C;
85 cout<<endl;
86 return 0;
87 }
B.12.3 Le compte est bon
Le but de cet exercice va tre dcrire un programme capable de rsoudre le pro-
blme du Compte est bon dont voici une description :
6 plaques sont tires au hasard parmi 14 plaques diffrentes dont chacune peut
tre choisi de 0 2 fois. Les 14 plaques comportent les nombres : 1, 2, 3, 4, 5, 6, 7,
8, 9, 10, 25, 50, 75, 100.
Un nombre entre 100 et 999 (inclus) est tir au hasard, on lappelera par la suite
le total.
Une des 4 oprations arithmtiques (addition, soustraction, division entire, mul-
tiplication) est utilise sur deux des 6 plaques pour obtenir un nouveau nombre
(attention : la division entire ne peut tre utilise que si le dividande est divi-
sible par le diviseur ; la soustraction ne peut tre utilise que si le premier terme
est plus grand que le second). Il nous reste alors 5 nombres (les 4 plaques non
choisies et le nouveau nombre) partir desquels recommencer.
Le but est dobtenir nalement le total, en utilisant tout ou partie des 6 plaques
ainsi que dcrit prcdemment.
Par exemple, on tire le total 987 et les plaques suivantes :
1 50 9 1 50 7
Une solution sera par exemple :
1 + 1 = 2
2 50 = 100
100 9 = 91
91 + 50 = 141
141 7 = 987
Il nexiste pas toujours de solution. Dans ce cas, le candidat doit trouver une solu-
tion approchant au plus prs le total. Pour simplier, le programme que vous crirez
cherchera uniquement des solutions exactes.
275
B.12. Devoir maison 2004 : nonc B. Examens
La manire dont trouver une solution est trs simple : le programme va simplement
munrer toutes les possibilits de combiner les nombres crits sur les plaques et les
oprateurs arithmtiques, jusqu tomber sur une bonne solution ou les avoir explores
toutes.
On utilisera dans le programme partout o cela est possible les constantes globales
suivantes :
const int total_min=100;
const int total_max=999;
const int nb_plaques=6;
const int nb_plaques_possibles=14;
const int plaques_possibles[nb_plaques_possibles]=
{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 25, 50, 75, 100};
const int nb_operateurs=4;
const char operateurs[nb_operateurs]={+,-,/,
*
};
1. Dnir une fonction de prototype int random(int a,int b) renvoyant un
nombre alatoire entre a et b (inclus). Prciser les #include et using namespace
ncessaires.
2. Dnir une fonction int genere_total() renvoyant un nombre alatoire entre
total_min et total_max
3. Dnir une fonction void affiche_plaques(int tab[nb_plaques]) que
lon utilisera ultrieurement pour afcher lcran les plaques tires au hasard.
Que faut-il rajouter comme #include?
4. Dnir une fonction void genere_plaques(int tab[nb_plaques]) effec-
tuant le tirage au hasard des plaques. An de se souvenir du nombre de fois o
on a tir chaque plaque et dimposer den tirer au plus 2, on pourra utiliser une
variable int plaque_deja_tiree[nb_plaques_possibles].
5. Dnir la fonction main de votre programme qui initialisera le gestionnaire de
nombre alatoires, effectuera le tirage du total et des plaques, et afchera le tout
lcran. Que faut-il rajouter comme #include?
6. Nous allons maintenant nous attaquer au cur du problme, la fonction
bool trouve_combinaison (int plaques[], int n, int total) qui,
tant donne un tableau de plaques plaques de taille n et un total total :
Renvoie true et afche lcran une solution si elle existe.
Renvoie false sinon.
trouve_combinaison sera programme rcursivement suivant le nombre de
plaques n :
Si n==1, le problme est trivial
Sinon, on effectue lopration plaques[i] op plaques[j] pour tous les
i et j distincts entre 0 et n et tous les oprateurs arithmtiques op, si elle est
possible. Ensuite :
Si on tombe sur le nombre total, on afche lopration qui nous a permis
dy accder et on renvoie true.
Sinon, on appelle rcursivement la fonction bool trouve_combinaison
sur un nouveau tableau de plaques. Si cet appel rcursif renvoie true, cest
276
B. Examens B.12. Devoir maison 2004 : nonc
quil est possible dobtenir une solution partir de cette tape intermdiaire.
On renvoie alors galement true et on afche lopration qui nous a permir
daccder cette tape intermdiaire. Sinon, on continue itrer.
7. Ajouter votre fonction main lutilisation de la fonction trouve_combinaison.
Quand aucune combinaison nest trouve, le signaler en afchant un message
lcran.
8. Le programme que vous avez crit ne sait pas trouver de solution approche, ne
retient quune seule solution (pas forcment la plus courte) et lafche en ordre
inverse. Que faudrait-il faire (en franais, sans essayer de le programmer) pour
rsoudre ces limitations ?
277
B.13. Devoir maison 2003 : nonc B. Examens
B.13 Devoir maison 2003 : nonc
B.13.1 Tableau dexcution
Pour ce premier exercice, il sagit dexcuter pas pas le programme suivant :
1 int hop(int x) {
2 x = x/2;
3 return x;
4 }
5
6 int hip(double& y) {
7 y = y/2;
8 return (int(y));
9 }
10
11 int test(double z) {
12 z = 2
*
hip(z) - z;
13 if (z>4)
14 z = test(z);
15 return (int(z));
16 }
17
18 int main() {
19
20 double a = 3.14;
21
22 int b = hop(int(a));
23 b = hip(a);
24
25 a = 18;
26 do {
27 a = test(a);
28 } while (a != 0);
29
30 return 0;
31 }
Pour cela, remplissez le tableau ci-dessous, en crivant, si elles existent, les va-
leurs des variables du programme pour chaque excution de ligne (selon lexemple
du cours).
278
B. Examens B.13. Devoir maison 2003 : nonc
Conventions :
mettre le numro de la ligne quon vient dexcuter (ventuellement, on peut
dcouper lexcution dune mme ligne en plusieurs tapes)
on peut utiliser le symbole pour rpter la valeur dune variable la ligne sui-
vante
pour une variable qui nest autre quune rfrence sur une autre variable x, on
indiquera [x] ;
Ligne a
main
b
main
ret
main
x
hop
ret
hop
x
hip
ret
hop
x
test(1)
ret
test(1)
x
test(2)
ret
test(2)
B.13.2 Grands entiers
Le but de cet exercice est de manipuler des entiers arbitrairement grands. Un entier
long sera un entier compris entre 0 et 10
4n
1, o n est une constante xe une fois
pour toutes, par exemple n = 30. On dcoupera les grands entiers comme si on les
considrait crits en base 10
4
, cest--dire sous la forme dun tableau de n int.
1. Crer une classe entier comprenant un tableau dentiers de taille n.
2. Le tableau dun entier contient la reprsentation de celui-ci en base 10
4
, la pre-
mire case contenant llment de poids le plus faible. Par exemple 15 439 573 458
se codera sous la forme 3458 3957 154. En consquent, on a besoin de connatre
lindice de llment de poids le plus fort an de savoir o arrter la lecture du
tableau. Ajouter la classe un champ de type int servant stocker lindice en
question.
3. Ajouter cette classe un constructeur vide.
4. Dnir un constructeur prenant en argument un int positif crant un entier
de mme valeur.
5. Ajouter la classe une mthode afchant lcran un entier, dans lordre stan-
dard pour la lecture.
6. Ajouter loprateur daddition (attention aux retenues !).
7. Ajouter loprateur de multiplication. On pourra procder en 3 temps : crer
une fonction multipliant un entier par un int (< 10
4
), une autre multipliant un
entier par 10
4
, et utiliser de faon adquate ces deux fonctions ainsi que laddi-
tion prcdemment dnie. Ou alors on pourra faire mieux (ce qui nest pas bien
difcile, il suft deffectuer une multiplication la main pour entrevoir diffrents
algorithmes).
8. Calculer 50! et lafcher.
B.13.3 Constructeurs
Examinez bien le contenu du programme suivant. La question est simple : quelles
sont les diffrentes lignes afches sur la sortie standard (cest--dire en console, via les
279
B.13. Devoir maison 2003: nonc B. Examens
instructions cout << ... ;) au cours de son excution? Justier vos rponses.
1 #include <iostream>
2 using namespace std;
3
4 class foo {
5 int x;
6
7 public:
8 // Constructeurs
9 foo();
10 foo(const foo &f);
11 foo(int i);
12
13 // Destructeur
14 ~foo();
15
16 // Oprateurs
17 foo operator+(const foo &f) const;
18 void operator=(const foo &f);
19
20 // Accesseur
21 int getx() const;
22 };
23
24 foo::foo()
25 {
26 x=0;
27 cout << "constr vide " << endl;
28 }
29
30 foo::foo(int i)
31 {
32 x=i;
33 cout << "constr int " << x << endl;
34 }
35
36 foo::foo(const foo &f)
37 {
38 x=f.x;
39 cout << "constr copie " << x << endl;
40 }
41
42 foo::~foo()
43 {
44 cout << "dest " << x << endl;
45 }
46
47 foo foo::operator+(const foo &f) const
280
B. Examens B.13. Devoir maison 2003 : nonc
48 {
49 cout << "somme " << x << " " << f.x << endl;
50 foo somme(x+f.x);
51 return somme;
52 }
53
54 void foo::operator=(const foo &f)
55 {
56 x=f.x;
57 cout << "affecte " << x << endl;
58 }
59
60 int foo::getx() const
61 {
62 return x;
63 }
64
65 void affiche1(foo f)
66 {
67 cout << f.getx() << endl;
68 }
69
70 void affiche2(const foo &f)
71 {
72 cout << f.getx() << endl;
73 }
74
75 int main()
76 {
77 foo a;
78 foo b=1;
79 foo c(2);
80 affiche1(b);
81 affiche2(c);
82 a=b+c;
83 return 0;
84 }
281
B.14. Devoir surveill 2011 : nonc B. Examens
B.14 Devoir surveill 2011 : nonc
B.14.1 Liste avec sauts
Les listes sont des structures de donnes simples permettant lajout et le retrait
dun lment en temps constant. Chaque noeud contient, outre une valeur, un pointeur
sur le noeud suivant. Les listes avec saut (en anglais, skip lists, voir Fig. B.11) ont t
proposes en 1990 par William Pugh, professeur lUniversit du Maryland, College
Park. Elles compensent des inconvnients des listes en ce qui concerne la recherche
dun lment dans une liste trie. Lide est de maintenir des raccourcis dans la liste
qui permettent de se dplacer plus vite. Cela se fait au prix dune certaine utilisation
mmoire supplmentaire, mais qui peut rester modre et qui est modulable.
1
3
4
8
10
15
16
T
e
t
e
Q
u
e
u
e
niveau 0
niveau 1
niveau 2
FIGURE B.11 Une liste avec sauts. Les valeurs sont tries. Des raccourcis existent aux
niveaux 1 et 2 pour une recherche plus rapide.
Le plus logique est dutiliser des pointeurs pour coder le lien dun noeud au sui-
vant. Comme nous avons vit le plus possible de manipuler des pointeurs dans le
cours, nous nous en passerons et coderons simplement un index (sous forme dentier)
dans un tableau.
Nous utiliserons des tableaux de taille variable. La structure de choix est la classe
template std :: vector, accessible par #include <vector>. Rappelons la partie de linter-
face des vecteurs qui nous sera utile :
t empl at e <typename T>
c l a s s vect or {
publ i c :
vect or ( ) ;
void push_back ( T t ) ;
T& operat or [ ] ( i nt i ) ;
i nt s i ze ( ) ;
/ / . . .
} ;
Un lment sajoute avec push_back. Par exemple :
st d : : vect or <i nt > vec ;
vec . push_back ( 0 ) ;
vec . push_back ( 1 ) ;
st d : : cout << vec [ 0 ] << << vec [ 1 ] << st d : : endl ;
vec [ 0 ] =2 ;
st d : : cout << vec [ 0 ] << << vec [ 1 ] << st d : : endl ;
/ / Af f i c h a g e :
/ / 0 1
/ / 2 1
282
B. Examens B.14. Devoir surveill 2011 : nonc
B.14.2 Travail demand
Certaines questions concernent lanalyse de la complexit, dautres la pro-
grammation et lalgorithmique.
Prliminaires
1. Rappeler la complexit de la recherche dichotomique dans un tableau tri.
2. Rappeler la complexit de la recherche dun lment dans une liste en fonction
de son nombre n dlments. Cette complexit change-t-elle lorsquon suppose la
liste trie ?
3. On implmente maintenant une liste trie 2 niveaux, avec des raccourcis comme
ci-dessous. Dcrire un algorithme permettant de vrier la prsence dun lment
de valeur v dans cette liste, utilisant au maximum les raccourcis de niveau 1 (la
liste complte sans raccourcis tant au niveau 0). On ne demande pas un code en
C++ mais une description dtaille en franais de lalgorithme.
1
3
4
8
10
15
16
T
e
t
e
Q
u
e
u
e
niveau 0
niveau 1
FIGURE B.12 Liste rgulire 2 niveaux
4. Quelle est la complexit en nombre de comparaisons de cet algorithme de re-
cherche ?
5. Dessiner la mme liste avec des raccourcis au niveau 2, sautant de 4 en 4 noeuds.
Adapter lalgorithme prcdent cette conguration, quelle est la complexit de
la recherche ?
6. Gnraliser en supposant quil y a log
2
(n) niveaux. Montrer que la recherche peut
alors se faire en log
2
(n) comparaisons.
7. Quelle est le nombre de liens (pointeurs) ncessaire pour une telle liste log
2
(n)
niveaux ?
8. Expliquer pourquoi une telle structure nest pas pratique modier.
Pour pallier cet inconvnient, lide est de faire des noeuds avec un nombre ala-
toire de niveaux : un noeud aura une probabilit p (0 < p < 1) davoir un lien
au niveau 1, p
2
davoir en plus un lien au niveau 2, p
3
davoir en plus un lien au
niveau 3, etc.
Implmentation
9. On dnit un noeud de la faon suivante :
c l a s s Noeud {
publ i c :
i nt v ;
st d : : vect or <i nt > l i e ns ;
/ / . . .
} ;
283
B.14. Devoir surveill 2011 : nonc B. Examens
Dans liens [0] il y aura lindex du noeud suivant de niveau 0, lindex du noeud
suivant au niveau 1 dans liens [1] (si le noeud a le niveau au moins 1), etc. Ainsi
le noeud est prsent liens . size () niveaux.
Implmenter un constructeur pour cette classe, prenant la valeur v en paramtre
et ne crant aucun lien.
10. Voici la dclaration de la classe :
c l a s s Li st eAvecSaut s {
st d : : vect or <Noeud> noeuds ; / / Ensembl e de s noeuds
void precedent s ( i nt prec [ maxNiveau] , i nt v ) ;
publ i c :
Li st eAvecSaut s ( ) ;
void aj out e ( i nt v ) ;
bool r e t i r e ( i nt v ) ;
bool cherche ( i nt v ) ;
} ;
Les noeuds sont stocks dans un std :: vector. On mettra lindice 0 de ce vecteur
un noeud qui servira la fois de dbut et de n : il aura une valeur plus grande
que toutes les valeurs admissibles (mettons 10000) et il pointe initialement vers
lui-mme tous les niveaux, de 0 maxNiveau1, qui est une constante du pro-
gramme :
const i nt maxNiveau=5;
Ecrire le constructeur de la classe.
11. Ecrire la mthode ListeAvecSauts::cherche qui procde ainsi : elle part du niveau
le plus haut de noeuds[0].liens et avance tant que la valeur du noeud suivant est
strictement infrieure v. La recherche continue alors au niveau immdiatement
infrieur et ainsi de suite jusquau niveau 0. Il ne reste plus qu comparer la
valeur du noeud suivant celui o lon sest arrt.
Modication de la liste
12. Ecrire une fonction qui retourne un niveau alatoire entre 0 et maxNiveau1 : 1
ou plus avec probabilit p, 2 ou plus avec probabilit p
2
, etc.
13. Pour ajouter ou enlever un lment, il faut remplir la mthode ListeAvecSauts::precedents
qui retourne les indices de noeuds o la recherche sarrte chaque niveau. Ainsi
dans la Fig. B.11, precedents(12) retournerait les indices des noeuds de valeur
10 (niveau 0), 8 (niveau 1) et 3 (niveau 2). Il sagit donc juste dune variante de
cherche. Ecrire cette fonction.
14. Ecrire la fonction retire . On ne cherchera pas retirer llment de noeuds, car
cela modierait les indices. On accepte simplement de perdre cette case noeuds[i]
et de ne plus lutiliser par la suite.
NB : cette fonction renvoie false si la valeur nest pas prsente dans la liste.
15. Ecrire de mme la mthode ListeAvecSauts::ajoute qui cre un nouveau noeud
et cherche le placer dans la liste avec un nombre de niveaux tir alatoire-
ment (comme dcrit ci-dessus). Il sagit dappeler ListeAvecSauts::precedents et
de faire les connexions ncessaires.
284
B. Examens B.14. Devoir surveill 2011 : nonc
Complexit
16. Montrer que la profondeur moyenne (nombre de niveaux) dun noeud est 1/(1
p). Rappel (de probabilit) : cette esprance se calcule par la formule :
1Proba(profondeur = 1)+2Proba(profondeur = 2)+3Proba(profondeur = 3)+. . .
On supposera que la profondeur nest pas limite par maxNiveau et donc laisser
la somme aller linni pour simplier.
17. Discuter loccupation mmoire de la liste avec sauts compare une liste ordi-
naire pour p = 1/2 et p = 1/4.
18. Montrer que la complexit de recherche C(n) sur la liste est en moyenne O(log n).
On procdera de la faon suivante :
(a) Montrer quen moyenne les noeuds ayant un niveau suprieur 0 sont
distance 1/p.
(b) Montrer quau niveau 1, il y a environ np noeuds.
(c) Etablir la relation de rcurrence :
C(n) 1/p + C(np)
(d) Conclure que C(n) = O(log n), le facteur constant dpendant de la valeur de
p.
19. Montrer un exemple dutilisation de la structure pour un tri en O(nlog n).
20. Comparer une autre structure de donnes adapte au stockage dlments avec
recherche rapide.
21. Proposer, sans limplmenter, une petite modication la structure permettant
daccder en temps O(log n) au k-ime lment.
285
B.15. Devoir surveill 2010 : nonc B. Examens
B.15 Devoir surveill 2010 : nonc
B.15.1 Programmation : machine registres
Nous allons implmenter en C++ une machine dinformatique thorique, dite ma-
chine registres, les registres correspondant aux variables dont les valeurs possibles
sont les entiers naturels (on ne soccupera pas des problmes de borne), et possdant
un nombre trs restreint dinstructions. Un programme est une suite dinstructions nu-
mrotes partir de 0. Les registres sont galement numrots partir de 0, le registre
R
0
tant spcial car il indique si le programme doit continuer (valeur 1) ou sarrter
(valeur 0). Les instructions sont les suivantes :
1. incrmenter le registre i : R
i
R
i
+ 1.
2. dcrmenter le registre i : R
i
R
i
1.
3. goto conditionnel qui, si le registre R
i
est nul (valeur 0), fait passer linstruction
numro p et sinon continue linstruction suivante :
if R
i
= 0 goto p
4. instruction darrt halt qui en fait met le registre R
0
0.
Il est noter que cette machine est sufsante pour faire de nombreux calculs, pourvu
que le nombre de registres soit sufsant. Pour le voir, considrons que chaque registre
est un bit (valeur 0 ou 1) et regardons par exemple le programme faisant un ip de
R
1
:
0 if R[1]=0 goto 3
1 dec R[1]
2 if R[2]=0 goto 4
3 inc R[1]
4 halt
La ligne 2 effectue un goto inconditionnel en supposant que le test R
2
= 0 est
toujours vrai (mais on aurait pu aussi bien dans ce cas remplacer par un halt). La
programmation en C++ de cette machine registres est en annexe 12.
Questions
1. Ecrire un programme mettant dans R
1
le rsultat de laddition de R
1
et R
2
. Pour
cela, dcrmenter R
2
tout en incrmentant R
1
tant que cest possible. On pourra
utiliser le registre R
3
pour un goto inconditionnel si ncessaire. Attention : il sagit
dcrire ce programme dans le langage de la machine registres.
2. Ecrire en C++ une classe Etat qui contient le tableau des registres un instant
donn et le numro de la prochaine instruction excuter (nomm next et quon
pourra mettre dans la partie publique), ainsi que ses constructeur et destructeur.
Par dfaut, seul le registre R
0
est stock, mais dautres registres pourront tre
ajouts la demande.
3. Ajouter des mthodes get et set qui respectivement renvoie la valeur du re-
gistre numro i et laffecte une valeur v. La mthode get est paresseuse, dans
le sens o elle renvoie 0 pour les registres nayant jamais t initialiss, mais sans
ajouter un nouveau registre.
286
B. Examens B.15. Devoir surveill 2010 : nonc
4. Ajouter une mthode qui afche les registres (autres que R
0
).
5. Ecrire une classe Instruction avec un constructeur par dfaut (correspondant
au halt) et un constructeur prenant un identiant, un numero de registre (utilis
par inc et dec) et un numro dinstruction suivante p, utilis uniquement pour
le goto. Lidentiant prendra lune des valeurs suivantes :
const int ID_HALT=0, ID_INC=1, ID_DEC=2, ID_GOTO=3;
6. Ajouter une mthode execute Instruction qui prend une variable de type
Etat en entre et le modie en sortie.
7. Ajouter une mthode afchant linstruction.
8. Ecrire une classe Programme qui contient une suite dinstructions, vide par d-
faut, et un Etat.
9. Ecrire une mthode pour ajouter une instruction la n du programme.
10. Ajouter une mthode print qui afche le programme, avec la prochaine instruc-
tion excuter prcde dun >, et ltat des registres (voir annexe 12).
11. Ajouter une mthode init qui met le registre i une valeur v.
12. Ajouter une mthode run qui excute le programme avec un appel print
aprs chaque instruction excute et qui sarrte aprs avoir excut linstruction
halt.
Annexe : exemple de programme ip
int main() {
Programme flip;
flip.ajoute( Instruction(ID_GOTO,1,3) );
flip.ajoute( Instruction(ID_DEC,1) );
flip.ajoute( Instruction(ID_GOTO,2,4) );
flip.ajoute( Instruction(ID_INC,1) );
flip.ajoute( Instruction(ID_HALT) );
std::cout << "flip 0 --> 1" << std::endl;
flip.init(1,0);
flip.run();
std::cout << "flip 1 --> 0" << std::endl;
flip.init(1,1);
flip.run();
return 0;
}
La sortie de ce programme est la suivante :
287
B.15. Devoir surveill 2010 : nonc B. Examens
flip 0 --> 1
[ 0 ]
> 0 if R[1]=0 goto 3
1 dec R[1]
2 if R[2]=0 goto 4
3 inc R[1]
4 halt
[ 0 ]
0 if R[1]=0 goto 3
1 dec R[1]
2 if R[2]=0 goto 4
> 3 inc R[1]
4 halt
[ 1 ]
0 if R[1]=0 goto 3
1 dec R[1]
2 if R[2]=0 goto 4
3 inc R[1]
> 4 halt
flip 1 --> 0
[ 1 ]
> 0 if R[1]=0 goto 3
1 dec R[1]
2 if R[2]=0 goto 4
3 inc R[1]
4 halt
[ 1 ]
0 if R[1]=0 goto 3
> 1 dec R[1]
2 if R[2]=0 goto 4
3 inc R[1]
4 halt
[ 0 ]
0 if R[1]=0 goto 3
1 dec R[1]
> 2 if R[2]=0 goto 4
3 inc R[1]
4 halt
[ 0 ]
0 if R[1]=0 goto 3
1 dec R[1]
2 if R[2]=0 goto 4
3 inc R[1]
> 4 halt
B.15.2 Algorithmique : ensemble sans doublon
On se propose de programmer un ensemble dlments au sens mathmatique,
cest--dire que les doublons (valeurs rptes) sont interdits. Les lments seront
du type Element dont la dnition exacte importe peu (cela pourrait tre un
argument template). Les lments sont stocks dans une classe Ensemble dont
voici une partie de linterface :
class Ensemble {
public:
Ensemble();
Ensemble(const Ensemble& e); // Constructeur de copie
int taille() const; // Nombre delements
Element opertor[](int i) const; // Element numero i (0<=i<=taille()-1)
void insere(int i, const Element& e);
void ajoute(const Element& e); // Raccourci pour insere(taille(),e)
...
};
13. Ecrire une fonction retournant lensemble union de deux Ensemble passs en
arguments.
14. Quelle est sa complexit en fonction des tailles m et n des ensembles ?
On suppose dans la suite quil existe une relation dordre stricte entre lments :
bool operator<(const Element& e1, const Element& e2);
et que les lments sont tris par ordre croissant au sein de lEnsemble.
15. Ecrire une fonction retournant lensemble union u de deux Ensemble passs en
arguments, e1 et e2. Pour cela, on peut :
dabord copier e2 dans u;
puis chercher le premier lment de u non infrieur strictement e1[0], appelons-
le u[j];
288
B. Examens B.15. Devoir surveill 2010 : nonc
insrer e1[0] dans u lindice j sil ne fait pas doublon;
faire de mme pour e1[1], sachant quil suft de parcourir u seulement
partir de lindice j+1;
continuer ainsi jusqu arriver au bout de u ou e1;
si on sest arrt par puisement de u, ajouter les lments restants de e1.
16. Quelle est sa complexit si tous les lments de e2 sont infrieurs ceux de e1?
17. Quelle est sa complexit si tous les lments de e1 sont infrieurs ceux de e2?
18. Quelle est sa pire complexit en fonction des tailles m et n des ensembles ? Pour
le voir, on pourra noter n
0
le nombre dlments de e2 infrieurs e1[0], n
i
entre e1[i-1] et e1[i] (i {1 . . . m 1}), n
m
ceux suprieurs e1[m-1]. On
regardera ce qui se passe suivant que n
m
est nul ou non.
289
B.16. Devoir surveill 2009: nonc B. Examens
B.16 Devoir surveill 2009 : nonc
B.16.1 Quafche ce programme ?
1. Examinez bien le contenu du programme suivant. La question est simple : quelles
sont les diffrentes lignes afches sur la sortie standard (cest--dire en console,
via les instructions cout << ... ;) au cours de son excution? Procdez
avec mthode en faisant attention aux retours de fonction par valeurs, et lqui-
libre ncessaire entre appels aux constructeurs/destructeurs. Justiez vos r-
ponses.
1 #include <iostream>
2 #include <algorithm> // Pour la fonction max
3 using namespace std;
4
5 const int maxDegre = 10;
6
7 class Polynome {
8 int deg;
9 float t[maxDegre+1];
10 void recalc_deg();
11 public:
12 Polynome();
13 Polynome(float a);
14 Polynome(float a, float b);
15 Polynome(int d, float
*
tab);
16 ~Polynome();
17
18 bool est_nul() const;
19 void set_coeff(int i, float a);
20 float evalue(float x) const;
21 void affiche() const;
22 Polynome operator+(const Polynome& P) const;
23 Polynome operator-(const Polynome& P) const;
24 };
25
26 void Polynome::recalc_deg() {
27 while(deg>=1 && t[deg]==0.0f)
28 --deg;
29 }
30
31 void Polynome::set_coeff(int i, float a) {
32 t[i] = a;
33 if(i > deg)
34 deg = i;
35 recalc_deg();
36 }
37
38 bool Polynome::est_nul() const {
290
B. Examens B.16. Devoir surveill 2009: nonc
39 return (deg==0 && t[0]==0.0f);
40 }
41
42 Polynome::Polynome() {
43 deg = 0;
44 t[0] = 0.0f;
45 cout << "C0: ";
46 affiche();
47 }
48
49 Polynome::Polynome(float a) {
50 deg = 0;
51 t[0] = a;
52 cout << "C1: ";
53 affiche();
54 }
55
56 Polynome::Polynome(float a, float b) {
57 deg = 1;
58 t[0] = b;
59 t[1] = a;
60 cout << "C2: ";
61 affiche();
62 }
63
64 Polynome::Polynome(int d, float
*
tab) {
65 deg = d;
66 for(int i=0; i<=d; i++) {
67 t[i] = tab[d-i];
68 }
69 cout << "C3: ";
70 affiche();
71 }
72
73 Polynome::~Polynome() {
74 cout << "D: ";
75 affiche();
76 }
77
78 float Polynome::evalue(float x) const {
79 float xn=1.0f, v=0.0f;
80 for(int i=0; i<=deg; i++) {
81 v += t[i]
*
xn;
82 xn
*
= x;
83 }
84 return v;
85 }
86
87 void Polynome::affiche() const {
291
B.16. Devoir surveill 2009: nonc B. Examens
88 for(int i=deg; i>=0; i--) {
89 if(t[i] >= 0)
90 cout << "+";
91 cout << t[i] << "X" << i << " ";
92 }
93 cout << endl;
94 }
95
96 Polynome Polynome::operator+(const Polynome& P) const {
97 Polynome Q;
98 Q.deg = max(deg,P.deg);
99 for(int i=0; i <=Q.deg; i++) {
100 float a=0.0f, b=0.0f;
101 if(i <= deg) a = t[i];
102 if(i <= P.deg) b = P.t[i];
103 Q.t[i] = a+b;
104 }
105 Q.recalc_deg();
106 cout << "op+" << endl;
107 return Q;
108 }
109
110 Polynome Polynome::operator-(const Polynome& P) const {
111 Polynome Q;
112 Q.deg = max(deg,P.deg);
113 for(int i=0; i <=Q.deg; i++) {
114 float a=0.0f, b=0.0f;
115 if(i <= deg) a = t[i];
116 if(i <= P.deg) b = P.t[i];
117 Q.t[i] = a-b;
118 }
119 Q.recalc_deg();
120 cout << "op-" << endl;
121 return Q;
122 }
123
124 int main() {
125 float p1[] = {1.0f, 3.0f, 3.0f};
126 Polynome P(2,p1);
127 cout << "P= ";
128 P.affiche();
129 cout << "P(-2)=" << P.evalue(-2.0f) << endl;
130 Polynome Q(2.0f,-1.0f);
131 cout << "Q= ";
132 Q.affiche();
133 Polynome R = P+Q;
134 cout << "R= ";
135 R.affiche();
136 R = R-P;
292
B. Examens B.16. Devoir surveill 2009 : nonc
137 cout << "R= ";
138 R.affiche();
139 return 0;
140 }
B.16.2 Programmation : enrichir la classe polynme
2. Ecrire une classe monme (aX
k
) avec un constructeur prenant comme paramtres
a et k.
3. Ecrire loprateur de multiplication dun polynme et dun monme.
4. Ajouter loprateur % : P%Q est le reste de la division euclidenne de P par Q.
Cest un polynme dont le degr est strictement infrieur celui de Q. Pour cela,
retirer de P le produit de Q par un monme bien choisi de manire annuler son
coefcient de plus haut degr. Recommencer cette opration tant que le degr de
P est au moins aussi grand que celui de Q.
5. Programmer le PGCD de deux polynmes par lalgorithme dEuclide : partant de
lobservation que le PGCD de P et Q est aussi celui de Q et Q%P, on itre ces restes
de divisions euclidiennes jusqu obtenir un polynme R et le polynme 0. R est
alors le PGCD recherch.
B.16.3 Algorithmie : problme de slection
On sintresse au problme de la recherche du kime plus petit lment parmi
un ensemble de n lments, t[0] . . . t[n 1]. Par exemple, pour k = 0 il sagit du
minimum, pour k = n 1 du maximum et pour k = n/2 du mdian.
6. Ecrire une fonction qui nen appelle aucune autre et qui calcule cette valeur. Pour
cela, chercher le minimum, puis le deuxime minimum, etc. On prendra alors
garde au cas o plusieurs lments de t ont mme valeur.
7. Quelle est la complexit de lalgorithme ci-dessus ?
8. Proposer sans lcrire une mthode de complexit moyenne O(nlog n).
9. Supposant la fonction pivot de QuickSort disponible, adapter QuickSort de ma-
nire ne trier que le sous-tableau contenant le kime.
10. Ecrire une relation de rcurrence concernant la complexit moyenne de cet algo-
rithme.
11. Montrer que cet algorithme a une complexit moyenne en O(n).
12. Montrer quune telle complexit est optimale.
293
B.17. Devoir surveill 2008: nonc B. Examens
B.17 Devoir surveill 2008 : nonc
B.17.1 Erreurs
Dans chacun des programmes ou passages de programmes suivants, notre pro-
grammeur utilise (ou devrait utiliser) un concept particulier du C++. Malheureuse-
ment, il commet chaque fois une ou plusieurs erreurs. A vous de comprendre ce quil
a voulu faire et de proposer une version exacte de son programme. Les ... repr-
sentent des passages inutiles la comprhension quil ne vous est pas demand de
complter.
1. Variables
double a=...;
double b=...;
if (a<b) {
int min=a;
} else {
min=b;
}
double c=3
*
min+15;
2. Fonctions
void solve(double a,double b,double c) {
delta(double a,b,c) {
return b
*
b-4
*
a
*
c;
}
double d=delta(a,b,c);
if (d>0) {
...
} else if (d==0)
...
else
...
}
3. Tableaux
int f() {
int t[10];
for (int i=1;i<=10;i++)
t(i)=...;
return t;
}
4. Rfrences
solve(double a,double b,double c) {
double d=delta(a,b,c);
int n;
double x1,x2;
294
B. Examens B.17. Devoir surveill 2008: nonc
if (d>0) {
n=2;
x1=-b+sqrt(d)/(2
*
a);
x2=-b-sqrt(d)/(2
*
a);
} else if (d==0) {
n=1;
x1=-b/(2
*
a);
} else
n=0;
return (n,x1,x2);
}
5. Rcursivit
double dichotomie(double a,double b) {
double c=(a+b)/2;
if (f(a)
*
f(c)>0)
dichotomie(c,b);
else
dichotomie(a,c);
}
6. Boucles
double dichotomie(double a,double b) {
until (b-a<1e-5) {
double c=(a+b)/2;
if (f(a)
*
f(c)>0)
b=c;
else
a=c;
}
return c;
}
7. Tests
...
int t[10];
do {
change(t);
} while ( for (int i=0;i<10;i++) mauvais(t[i]); )
...
8. Allocation dynamique
void f(int n) {
int t[n];
remplit(t,n);
295
B.17. Devoir surveill 2008 : nonc B. Examens
affiche(t,n);
}
9. Objets
class A {
int f(int x);
}
int f(int x) {
return 2
*
x;
}
void g() {
A a;
int b=a.f(2);
}
10. Constructeurs
class equipe {
int n;
string
*
noms;
public:
equipe();
equipe(int nb);
~equipe();
void print();
};
equipe::equipe() {
}
equipe::equipe(int nb) {
noms=new string[n];
}
equipe::~equipe() {
delete[] noms;
}
void equipe::print() {
for (int i=1;i<=n;i++)
cout << noms[i] << endl;
}
B.17.2 Problme
1. Etant donns :
deux entiers constants N et M
un tableau bi-dimensionnel de lettres char grille[N][N] reprsentant une
grille de jeu
un tableau mono-dimensionnel de lettres char mot[M] reprsentant un mot
296
B. Examens B.17. Devoir surveill 2008 : nonc
crire une fonction qui cherche partout dans la grille si le mot est crit horizonta-
lement de gauche droite et qui afche les coordonnes correspondantes.
2. Amliorer la fonction pour quelle cherche le mot horizontalement, verticalement
et en diagonale dans tous les sens, soit huit directions au total. (NB : il ne sagit
pas de multiplier par 8 la longueur de la rponse la question prcdente ! A titre
indicatif, une solution simple prend une trentaine de lignes).
3. Amliorer la fonction pour quelle accepte un mot de taille variable.
297
B.18. Devoir surveill 2007: nonc B. Examens
B.18 Devoir surveill 2007 : nonc
B.18.1 Groupes
Le programme suivant afche
====
Tous
====
Aarabi Sarah : 21 ans
Adamjy Inssia : 20 ans
Arlaud Jules : 22 ans
Aroles Jeremy : 22 ans
Bairi Iken : 23 ans
Baumhauer Oriane : 23 ans
Begon-Lours Clotilde : 22 ans
Bellakrid Amine : 24 ans
Benchimol Mike : 20 ans
Kharoub Amine : 25 ans
==========
Ont 22 ans
==========
Arlaud Jules : 22 ans
Aroles Jeremy : 22 ans
Begon-Lours Clotilde : 22 ans
==========
Se prenomment Amine
==========
Bellakrid Amine : 24 ans
Kharoub Amine : 25 ans
(NB : les ges ne sont pas exacts !).
Il manque les mthodes des objets. Dnissez-les !
1 #include <string>
2 #include<iostream>
3 using namespace std;
4
5 //====================================
6 class personne {
7 string N,P;
8 int A;
9 public:
10 personne();
11 personne(string n,string P,int a);
12 string nom() const;
13 string prnom() const;
14 int age() const;
15 };
298
B. Examens B.18. Devoir surveill 2007 : nonc
16
17 //========================================
18 class groupe {
19 personne
*
t;
20 int n;
21 public:
22 groupe();
23 groupe(const groupe& g);
24 void operator=(const groupe& g);
25 ~groupe();
26 void ajouter(personne p);
27 void affiche() const;
28 groupe agsDe(int a) const;
29 groupe prnomms(string p) const;
30 };
31
32 //=============================================================
33
34 ............ ici, les mthodes de personne et de groupe!
35
36 //=============================================
37 int main()
38 {
39 groupe g;
40
41 g.ajouter(personne("Aarabi","Sarah",21));
42 g.ajouter(personne("Adamjy","Inssia",20));
43 g.ajouter(personne("Arlaud","Jules",22));
44 g.ajouter(personne("Aroles","Jeremy",22));
45 g.ajouter(personne("Bairi","Iken",23));
46 g.ajouter(personne("Baumhauer","Oriane",23));
47 g.ajouter(personne("Begon-Lours","Clotilde",22));
48 g.ajouter(personne("Bellakrid","Amine",24));
49 g.ajouter(personne("Benchimol","Mike",20));
50 g.ajouter(personne("Kharoub","Amine",25));
51 cout << "====" << endl << "Tous" << endl << "====" << endl;
52 g.affiche();
53
54 groupe g2;
55 g2=g.agsDe(22);
56 cout << "==========" << endl << "Ont 22 ans" << endl << "==========" << endl;
57 g2.affiche();
58
59 cout << "==========" << endl << "Se prenomment Amine" << endl << "==========" << endl;
60 g.prnomms("Amine").affiche();
61
62 return 0;
63 }
299
B.18. Devoir surveill 2007 : nonc B. Examens
B.18.2 Anniversaires
Il est connu que sur une classe de 23 lves, il y a une chance sur 2 pour que deux
lves au moins soient ns le mme jour. crire un programme
qui tire au hasard le jour de naissance de N personnes,
qui vrie sil en existe au moins deux nes le mme jour,
et qui recommence un grand nombre de fois pour estimer la probabilit que ce
phnomne se produise.
Modier le programme pour estimer cette probabilit pour N allant de 2 30.
Voici, pour information, ce quon trouve :
2 eleves. Frequence: 0.00272
3 eleves. Frequence: 0.008289
4 eleves. Frequence: 0.016509
5 eleves. Frequence: 0.026964
6 eleves. Frequence: 0.040578
7 eleves. Frequence: 0.0559
8 eleves. Frequence: 0.074285
9 eleves. Frequence: 0.094771
10 eleves. Frequence: 0.117261
11 eleves. Frequence: 0.141013
12 eleves. Frequence: 0.167189
13 eleves. Frequence: 0.195052
14 eleves. Frequence: 0.222897
15 eleves. Frequence: 0.253055
16 eleves. Frequence: 0.28374
17 eleves. Frequence: 0.314569
18 eleves. Frequence: 0.346247
19 eleves. Frequence: 0.379274
20 eleves. Frequence: 0.411193
21 eleves. Frequence: 0.443089
22 eleves. Frequence: 0.476296
23 eleves. Frequence: 0.508387
24 eleves. Frequence: 0.536925
25 eleves. Frequence: 0.568921
26 eleves. Frequence: 0.598471
27 eleves. Frequence: 0.626731
28 eleves. Frequence: 0.653526
29 eleves. Frequence: 0.68141
30 eleves. Frequence: 0.705967
300
B. Examens B.19. Devoir surveill 2006: nonc
B.19 Devoir surveill 2006 : nonc
B.19.1 Erreurs corriger
Corrigez les erreurs dans le programme suivant. Cherchez bien : il y en a 23
1 int dmax=10;
2 class polynom {
3 double coef[dmax];
4 int deg;
5 void updateDegree() const;
6 public:
7 polynom() {
8 deg=0;
9 coef[0]=0;
10 }
11 void set(int d,double
*
c);
12 void print() const;
13 polynom derivate() const;
14 polynom integrate() const;
15 void op-(const polynom& Q) const;
16 }
17
18 void polynom::set(int d,double
*
c) {
19 deg=d;
20 for (int i=1;i<=d+1;i++)
21 coef[i]=c[i];
22 }
23
24 void polynom::print() const {
25 for (int i=deg;i>=0;i++) {
26 if (i<deg & coef[i]>=0)
27 cout<<+;
28 cout << coef[i] << "
*
x^"<<i;
29 }
30 }
31
32 polynom derivate(){
33 polynom D;
34 if (deg>0) {
35 D.deg=deg-1;
36 for (int i=0,i<deg,i++)
37 D.coef[i]=(i+1)
*
coef[i+1];
38 }
39 }
40
41 polynom polynom::integrate(){
42 polynom I;
43 I.deg=deg+1;
44 for (int i=1;i<=deg+1;i++)
301
B.19. Devoir surveill 2006: nonc B. Examens
45 I.coef[i]=coef[i-1]/i;
46 return I;
47 }
48
49 int polynom::updateDegree() {
50 for (int i=deg;i>=1;i--)
51 if (coef[i]==0)
52 deg=i-1;
53 }
54
55 polynom polynom::op-(const polynom& Q) const {
56 polynom P;
57 P.deg=max(deg,Q.deg);
58 for (int i=0;i<=P.deg;i++) {
59 double c1,c2;
60 if (i>deg) c1=0 else c1=coef[i];
61 if (i>Q.deg) c2=0 else c2=Q.coef[i];
62 P.coef[i]=c1-c2;
63 }
64 P.updateDegree;
65 return P;
66 }
67
68 int main() {
69 polynom P;
70 int t[4]={5,2,3,4};
71 P.set(3,t);
72 polynom Q;
73 Q=P.derivate();
74 polynom R;
75 R=Q.integrate();
76 S=P-R;
77 cout << "P=" ; P.print; cout << endl;
78 cout << "Q=" ; Q.print; cout << endl;
79 cout << "R=" ; R.print; cout << endl;
80 cout << "S=" ; S.print; cout << endl;
81 }
B.19.2 Quafche ce programme ?
Examinez bien le contenu du programme suivant. La question est simple : quelles
sont les diffrentes lignes afches sur la sortie standard (cest--dire en console, via les
instructions cout << ... ;) au cours de son excution? Procdez avec mthode
en faisant attention aux retours de fonction par valeurs, et lquilibre ncessaire entre
appels aux constructeurs/destructeurs. Justiez vos rponses.
1 #include <iostream>
2 #include <cmath>
3 using namespace std;
302
B. Examens B.19. Devoir surveill 2006: nonc
4
5 int number_of_points;
6
7 class Point3{
8 int which_point;
9 int X,Y,Z;
10
11 void init(int _x=0, int _y=0, int _z=0){
12 which_point = number_of_points++;
13 X=_x; Y=_y; Z=_z;
14 }
15 void print(){
16 cout << " Point #" << which_point ;
17 cout << " [ " << X << ", " << Y << ", " << Z << "]" << endl;
18 }
19
20 public:
21
22 Point3(){
23 cout << "Constructor #0: " ;
24 init();
25 print();
26 }
27 Point3(int _x, int _y, int _z){
28 cout << "Constructor #1: ";
29 init(_x,_y,_z);
30 print();
31 }
32 Point3(const Point3 &pt){
33 cout << "Constructor #2: ";
34 init(pt.X,pt.Y,pt.Z);
35 print();
36 }
37 ~Point3(){
38 cout << "Destructor: " ;
39 print();
40 number_of_points--;
41 }
42
43 Point3 operator+(Point3 pt){
44 cout << "Dans operator+ :" ; print();
45 Point3 tmp(X + pt.X , Y + pt.Y , Z + pt.Z);
46 cout << "Fin operator+ " <<endl;
47 return tmp;
48 }
49 const Point3& operator=(const Point3 &pt){
50 cout << "Dans operator= :" ; print();
51 X=pt.X; Y=pt.Y; Z=pt.Z;
52 cout << "Fin operator= :" ; print();
303
B.19. Devoir surveill 2006: nonc B. Examens
53 return pt;
54 }
55 };
56
57 int main()
58 {
59 number_of_points = 0 ;
60 cout << number_of_points << endl;
61 Point3 PTS[2];
62 cout << number_of_points << endl;
63 PTS[0] = Point3(1,2,3);
64 cout << number_of_points << endl;
65 Point3 PT(3,2,1);
66 PTS[1] = PTS[0] + PT;
67 cout << number_of_points << endl;
68 return 0;
69 }
B.19.3 Tableau dexcution
Remplir le tableau dexcution du programme suivant : le tableau dexcution est
fourni en annexe.
1 #include <iostream>
2 using namespace std;
3
4 void initClasse(int
*
classe , bool
*
bienHabilles, int nbEleves)
5 {
6 int identifiant_eleve;
7
8 for ( int i = 0 ; i < nbEleves ; i++ )
9 {
10 identifiant_eleve = i
*
2;
11 classe[i] = identifiant_eleve;
12 if( i%2 == 0 )
13 bienHabilles[i] = false;
14 else
15 bienHabilles[i] = true;
16 }
17 }
18
19 bool tousBienHabilles(bool
*
bienHabilles, int nbEleves)
20 {
21 bool ok = true;
22 for ( int i = 0 ; i < nbEleves ; i++ )
23 {
24 if( !bienHabilles[i] )
25 return false;
26 }
304
B. Examens B.19. Devoir surveill 2006 : nonc
27
28 nbEleves = 2;
29 return ok;
30 nbEleves = 1;
31 }
32
33 void PrendreLaPhoto(int & nbEleves)
34 {
35 nbEleves--;
36
37 int &n = nbEleves;
38 for (int i = 0 ; i < nbEleves ; i++ )
39 cout << "Eleve " << i << " dit : cheese" << endl;
40 cout << "Photographe dit : souriez ... Click Clack, merci ... " << endl;
41
42 n++ ;
43 }
44
45
46 int main()
47 {
48
49 int nbEleves = 2;
50 int
*
classe = new int[nbEleves];
51 int
*
copieDeClasse = classe;
52 bool
*
bienHabilles = new bool [nbEleves];
53
54 initClasse(classe,bienHabilles,nbEleves);
55
56 for (int i = 0 ; i < nbEleves ; i++ )
57 copieDeClasse[i] =classe[i] ;
58
59 bienHabilles[0] = true;
60
61 if( tousBienHabilles(bienHabilles,nbEleves ) )
62 PrendreLaPhoto(nbEleves);
63
64 delete[] bienHabilles;
65 delete[] classe;
66
67 return 0;
68 }
Il sagit dcrire, si elles existent, les valeurs des variables du programme pour
chaque excution de ligne.
Conventions :
mettre le numro de la ligne quon vient dexcuter (ventuellement, on peut
dcouper lexcution dune mme ligne en plusieurs tapes) ;
305
B.19. Devoir surveill 2006 : nonc B. Examens
laisser en blanc les variables non existantes ;
mettre un point dinterrogation pour les variables dclares non initialises ;
pour une variable qui nest autre quune rfrence sur une autre variable x, on
indiquera [x] ; mme chose si deux pointeurs adressent le mme espace m

moire
on peut utiliser le symbole pour rpter la valeur dune variable la ligne sui-
vante ;
pour gagner de la place dans le tableau, les noms des variables nont t indexs
que par la premire lettre du nom de la fonction laquelle elles sont rattaches,
suivie le cas chant du numro dappel la fonction. De mme, le retour dune
fonction f a pour nom ret
f
.
dans le cas dun tableau, on indiquera les valeurs prises dans tout le tableau dans
une seule cellule du tableau dexcution. Par exemple, si on a int tab[2] = {4,6} ;,
on indiquera le contenu de tab par : 4,6 dans la cellule correspondante du tableau
dexcution.
dans le cas des boucles for, il faudra mettre en vidence chaque passage. Ne pas
oublier les variables compteurs
B.19.4 Huit dames
FIGURE B.13 Une solution du problme des huit dames
Le but de cet exercice est dcrire un programme capable de gnrer toutes les so-
lutions du problme des 8 dames. Il sagit de poser huit dames dun jeu dchecs sur
un chiquier de 88 cases sans que les dames ne puissent se menacer mutuellement,
conformment aux rgles du jeu dchecs. Par consquent, deux dames ne doivent
jamais partager la mme range, colonne, ou diagonale. Une solution possible est re-
prsente gure B.13. On demande de fournir les programmes correspondant trois
solutions successives. Programmez bien de faon progressive, en dcoupant en fonc-
tions, etc. Mme sil vaut habituellement 8, mettez le nombre N de dames (et donc la
taille de lchiquier en paramtre constant au dbut du programme).
1. Solution 1 :
Une conguration, cest--dire les positions des huit dames, est mmorise
sous forme dun tableau de coordonnes.
crire une fonction qui afche une conguration.
crire une fonction rcursive qui gnre toutes les congurations possibles
(sans tenir compte des menaces !)
306
B. Examens B.19. Devoir surveill 2006 : nonc
crire une fonction qui vrie pour une conguration donne quil sagit dune
solution, cest--dire que les dames ne se menacent pas.
Complter la fonction de gnration pour afcher les solutions.
Combien explore ton de congurations ? Est-ce beaucoup pour un ordinateur
quand N = 8 ?
2. Solution 2 :
An de limiter le nombre de congurations explores, modier la solution pr-
cdente pour vrier, au moment de placer un dame, quelle nest pas en prise
avec celles qui sont dj poses.
3. Solution 3 :
Protant du fait quil y a exactement une dame par colonne, changer la faon
de reprsenter une solution.
Changer en consquence la faon de gnrer les congurations. Combien y-a-
til maintenant de congurations possibles ?
Pour acclrer les tests, il est astucieux de mmoriser pour chaque ligne et
chaque diagonale si celle-ci est dj "occupe" par une dame prcdemment
pose. Mettre en oeuvre cette solution.
4. NB : les meilleures solutions algorithmiques actuelles arrivent rsoudre le pro-
blme avec N = 1000000 en trs peu dtapes !
307
B.20. Devoir surveill 2005: nonc B. Examens
B.20 Devoir surveill 2005 : nonc
B.20.1 Erreurs corriger
Corriger le code fourni en annexe : effectuer les corrections directement sur le
script. Conseil : il y a une vingtaine derreurs.
B.20.2 Quafche ce programme ?
Examinez bien le contenu du programme suivant. La question est simple : quelles
sont les diffrentes lignes afches sur la sortie standard (cest--dire en console, via les
instructions cout << ... ;) au cours de son excution? Procdez avec mthode
en faisant attention aux retours de fonction par valeurs, et lquilibre ncessaire entre
appels aux constructeurs/destructeurs. Justiez vos rponses.
1 #include <iostream>
2 #include <cmath>
3 using namespace std;
4
5 class Complexe{
6 double m_Re, m_Im;
7 public:
8 Complexe();
9 Complexe(double re, double im);
10 Complexe(const Complexe & c);
11 ~Complexe();
12 void operator= (const Complexe & c);
13 const Complexe operator
*
(const Complexe & c) const;
14 void conjugue();
15 double norme() const;
16 void print() const;
17 };
18
19 Complexe::Complexe(){
20 cout << "Constructeur : vide" << endl;
21 }
22
23 Complexe::Complexe(double re, double im){
24 m_Re = re; m_Im = im;
25 cout << "Constructeur : ";
26 print();
27 }
28
29 Complexe::Complexe(const Complexe & c){
30 m_Re = c.m_Re; m_Im = c.m_Im;
31 cout << "Constructeur Copie : ";
32 print();
33 }
34
308
B. Examens B.20. Devoir surveill 2005: nonc
35 Complexe::~Complexe(){
36 cout << "Destructeur : ";
37 print();
38 }
39
40 void Complexe::operator= (const Complexe & c){
41 cout << "dans op= : " ;
42 m_Re = c.m_Re; m_Im = c.m_Im;
43 print();
44 }
45
46 const Complexe Complexe::operator
*
(const Complexe & c) const{
47 cout << "dans op
*
: " << endl;
48 Complexe result(c.m_Re
*
m_Re-c.m_Im
*
m_Im,c.m_Re
*
m_Im+c.m_Im
*
m_Re);
49 cout << "fin op
*
." << endl;
50 return( result );
51 }
52
53 void Complexe::conjugue (){
54 cout << "dans conjugue : ";
55 m_Im = -m_Im;
56 print();
57 }
58
59 double Complexe::norme() const{
60 cout << "dans norme : " << endl;
61 Complexe c(m_Re,m_Im);
62 c.conjugue();
63 c = operator
*
(c);
64 cout << "fin norme. " << endl;
65 return( sqrt(c.m_Re) );
66 }
67
68 void Complexe::print() const{
69 cout << m_Re << " + I
*
" << m_Im << endl;
70 }
71
72 int main(){
73 cout << "1" << endl;
74 Complexe c1(3,4);
75 Complexe c2(0,1);
76 cout << "2" << endl;
77 Complexe c3 = c1;
78 cout << "3" << endl;
79 c2 = (c1
*
c2);
80 cout << "4" << endl;
81 double d = c1.norme();
82 cout << "norme de c1 : " << d << endl;
83 cout << "5" << endl;
309
B.20. Devoir surveill 2005 : nonc B. Examens
84 return 0;
85 }
B.20.3 Tableau dexcution
Remplir le tableau dexcution du programme suivant : le tableau dexcution est
fourni en annexe.
1 int U(int &n)
2 {
3 n += 1;
4 return V(n)-5;
5 }
6
7 int V(int n)
8 {
9 if (n == 4 || n == 16)
10 return -1;
11
12 int tmp;
13 n = n+2;
14 tmp = U(n);
15 return tmp + n;
16 }
17
18 int setValueToTwelve(int a)
19 {
20 a = 12;
21 return a;
22 }
23
24 int main()
25 {
26 int a = 0 ;
27 int fin;
28
29 fin = setValueToTwelve(a);
30 fin = U(a);
31
32 if(fin-7)
33 a = 12;
34 else if(fin+7)
35 a = 0;
36 else
37 a = 4;
38
39 return 0;
40 }
310
B. Examens B.20. Devoir surveill 2005 : nonc
Il sagit dcrire, si elles existent, les valeurs des variables du programme pour
chaque excution de ligne.
Conventions :
mettre le numro de la ligne quon vient dexcuter (ventuellement, on peut
dcouper lexcution dune mme ligne en plusieurs tapes) ;
laisser en blanc les variables non existantes ;
mettre un point dinterrogation pour les variables dclares non initialises ;
pour une variable qui nest autre quune rfrence sur une autre variable x, on
indiquera [x] ;
on peut utiliser le symbole pour rpter la valeur dune variable la ligne sui-
vante ;
pour gagner de la place dans le tableau, les noms des variables nont t indexs
que par la premire lettre du nom de la fonction laquelle elles sont rattaches,
suivie le cas chant du numro dappel la fonction. De mme, le retour de la
fonction f a pour nom ret
f
.
B.20.4 Rsolveur de Sudoku
Le but de cet exercice est dcrire un programme capable de rsoudre le problme
du Sudoku dont on rappelle les rgles :
une grille 9 9 partiellement remplie est donne, chaque case de cette grille doit
contenir un entier de 1 9
il faut complter la grille en respectant les trois rgles suivantes :
dans une mme ligne, ne peuvent apparatre quune seule fois les entiers de 1
9,
dans une mme colonne, ne peuvent apparatre quune seule fois les entiers de
1 9,
dans une mme rgion, ne peuvent apparatre quune seule fois les entiers de
1 9 : une rgion est lun des neuf blocs 3 3 qui constituent la grille.
Dans la suite du problme, on considre la structure suivante :
1 struct Grille {
2 int valeur[9][9];
3 bool indice[9][9];
4 };
qui indique, pour chacune des 81 cases de la grille, sa valeur (un entier entre 1 et 9 ou
bien 0 si la case na pas de valeur) et si la case en question fait partie des indices (sa
valeur est xe).
1. Dnir une fonction de prototype void affiche_grille(const Grille& g)
qui afche une grille (avec cout comme droite de FIG. B.14 ) : si, en une case
de la grille, il ny a pas encore de valeur affecte, un caractre X est afch la
place.
2. crire une fonction Grille lit_grille(string l[9]) qui lit une grille en
entre (un caractre aprs lautre, ligne par ligne). Chaque string du tableau
pass en paramtre reprsente une ligne de la grille. Si s est lune de ces lignes,
alors :
311
B.20. Devoir surveill 2005 : nonc B. Examens
4 3
6 8
1
5 9
8 6
7 2
1 2 7
5 3 4
9

4 X X X 3 X X X X
X X X 6 X X 8 X X
X X X X X X X X 1
X X X 6 X X X 9 X
X 8 X X X X 6 X X
X 7 X 2 X X X X X
X X X 1 X 2 7 X X
5 X 3 X X X X 4 X
9 X X X X X X X X
FIGURE B.14 Exemple de grille
4 6 8 9 3 1 5 2 7
7 5 1 6 2 4 8 3 9
3 9 2 5 7 8 4 6 1
1 3 4 7 5 6 2 9 8
2 8 9 4 1 3 6 7 5
6 7 5 2 8 9 3 1 4
8 4 6 1 9 2 7 5 3
5 1 3 8 6 7 9 4 2
9 2 7 3 4 5 1 8 6
FIGURE B.15 Solution correspondante
312
B. Examens B.20. Devoir surveill 2005 : nonc
s[i] est le i-me caractre de la ligne,
si s[i] est gal X alors la case est complter,
sinon, il sagit dun indice de valeur s[i] - 0.
3. Programmer une fonction bool verifie_case(const Grille& g, int i, int j)
qui indique si la valeur actuellement attribue la case (i, j) entre en conit avec
les autres cases valides de la ligne i, de la colonne j et de la rgion dans laquelle
se trouve la case.
4. Dnir une fonction de prototype bool case_suivante(const Grille& g, int& i, int& j)
qui passe dune case (i, j) dans la grille la suivante dans lordre lexigraphique
(la nouvelle case sera renvoye par rfrence) : les cases dindice sont sautes, et,
si il ny a pas de case suivante ((i, j) est la dernire case de la grille), alors false
est renvoy.
5. Programmer une fonction rcursive bool resout_grille(Grille& g, int i, int j),
qui va :
donner successivement la case (i, j) toutes les valeurs possibles,
tester chaque fois si la valeur entre en conit ou non avec le reste de la grille
dj remplie,
sil ny a pas de conit, sappeler rcursivement sur la case suivante (unique-
ment si la n de la grille na pas dj t atteinte).
Cette fonction renvoie true si la grille est soluble partir de la conguration
donne, et false sinon. Quel est lintrt de passer la structure Grille par rf-
rence ?
6. Dnir une fonction de prototype bool resout_grille(Grille& g) qui ap-
pelle la fonction prcdente avec la bonne position de dpart pour la rsolution.
7. Programmer la fonction int main() qui lit une grille ( dnir dans le code),
lafche, puis essaie de la rsoudre en afchant la solution sil y en a une ou bien
un message.
8. Lalgorithme propos ralise beaucoup de calculs inutiles et nexploite pas suf-
samment les indices. Suggrer des amliorations (ne pas les programmer !).
313
B.21. Devoir surveill 2004: nonc B. Examens
B.21 Devoir surveill 2004 : nonc
B.21.1 Erreurs
Corriger le code suivant : (effectuer les corrections directement sur le script en
annexe )
1 // fichier main.cpp
2 #include <win>
3
4 const int w;
5 const int h;
6
7 struct point
8 {
9 int x,y;
10 }
11
12 point operator+(point p1, point p2)
13 {
14 point p
15 p.x = p1.x+p2.x
16 p.y = p1.y+p2.y
17 }
18
19 class quadrilatere
20 {
21 point t[4];
22 quadrilatere(point pts[4]);
23 affiche(Color col=Black) const;
24 void translate(point v) const;
25 bool dans_la_fenetre();
26 }
27
28 quadrilatere::quadrilatere(point pts[4])
29 {
30 t=pts;
31 }
32
33 quadrilatere::affiche(Color col) const
34 {
35 for(int i=0;i<=4;i++)
36 {
37 DrawLine(t[i].x,t[i].y,t[(i+1)%4].x,t[(i+1)%4].y,col);
38 }
39 }
40
41 void quadrilatere::translate(point v) const
42 {
314
B. Examens B.21. Devoir surveill 2004: nonc
43 for(int i=0;i<4;i=i+1)
44 t[i]=t[i]+v;
45 }
46
47 bool quadrilatere::dans_la_fenetre()
48 {
49 bool in=false;
50 for(int i=0;(i<4) && in;i++)
51 {
52 if ((t[i].x<0) or (t[i].x>=w) or (t[i].y<0) or (t[i].y>=h))
53 then in=true;
54 }
55 return in;
56 }
57
58 int main()
59 {
60 OpenWindow(w,h);
61
62 quadrilatere Q;
63 Q(pts);
64
65 point pts[4];
66 pts={{10,10},{10,100},{100,100},{100,10}};
67
68 point v={1,2};
69
70 while(Q.dans_la_fenetre())
71 {
72 Q.affiche();
73 MilliSleep(10);
74 Q.affiche(White);
75 Q.translate(v);
76 }
77
78 delete [] Q.t;
79 Terminate();
80 return 0;
81 }
B.21.2 Quafche ce programme ?
Examinez bien le contenu du programme suivant. La question est simple : quelles
sont les diffrentes lignes afches sur la sortie standard (cest--dire en console, via les
instructions cout << ... ;) au cours de son excution? Justiez vos rponses.
1 #include <iostream>
2 using namespace std;
3
315
B.21. Devoir surveill 2004: nonc B. Examens
4 int NbPt;
5
6 void check() {
7 cout<<endl;
8 cout<<"n_points= "<<NbPt<<endl;
9 cout<<endl;
10 }
11
12
13 struct Point {
14 double x,y;
15
16 Point()
17 {
18 NbPt++;
19 x=y=0;
20 cout<<"Point: Void Cons"<<endl;
21 };
22
23 Point( const Point &model)
24 {
25 NbPt++;
26 x=model.x;
27 y=model.y;
28 cout<<"Point: Copy Cons"<<endl;
29 }
30
31 Point( const double &_x, const double &_y)
32 {
33 NbPt++;
34 x=_x; y=_y;
35 cout<<"Point: (x,y) Cons"<<endl;
36 }
37
38 ~Point()
39 {
40 NbPt--;
41 cout<<"Point: Dest"<<endl;
42 }
43
44 void display() const
45 {
46 cout<<"x= "<<x<<" y= "<<y<<endl;
47 }
48 };
49
50 void displayPoint ( Point T)
51 {
52 T.display();
316
B. Examens B.21. Devoir surveill 2004: nonc
53 }
54
55 void displayTriangle2 ( Point
*
T) {
56 cout<<"A"<<endl; T[0].display();
57 cout<<"B"<<endl; T[1].display();
58 cout<<"C"<<endl; T[2].display();
59 }
60
61 void displayTriangle1 ( Point
*
&T) {
62 cout<<"A"<<endl; displayPoint(T[0]);
63 cout<<"B"<<endl; displayPoint(T[1]);
64 cout<<"C"<<endl; displayPoint(T[2]);
65 }
66
67 void buildTriangleStatic ( Point
*
T, const double
*
array) {
68 for(int i=0;i<3;i++) {
69 T[i]=Point(array[2
*
i],array[2
*
i+1]);
70 }
71 }
72
73 Point
*
buildTriangleDynamic( const double
*
array) {
74 Point
*
T=new Point[3];
75 for(int i=0;i<3;i++) {
76 T[i]=Point(array[2
*
i],array[2
*
i+1]);
77 }
78 return T;
79 }
80
81 void destroyTriangleDynamic (Point
*
T) {
82 delete[] T;
83 }
84
85 int main()
86 {
87 NbPt=0;
88 const double d6_1[6]={1.1,2.2,3.3,-0.1,-0.2,-0.3};
89 const double d6_2[6]={1,1,2,2,3,3};
90
91 check();
92 Point
*
t1=buildTriangleDynamic(d6_1);
93 check();
94 Point t2[3];
95 check();
96 buildTriangleStatic(t2,d6_2);
97 check();
98
99 displayTriangle1(t1);
100 check();
101 displayTriangle2(t2);
317
B.21. Devoir surveill 2004 : nonc B. Examens
102 check();
103
104 destroyTriangleDynamic(t1);
105 check();
106 t1=t2;
107 check();
108 displayTriangle1(t1);
109 check();
110
111 return 0;
112 }
B.21.3 Chemins dans un graphe
FIGURE B.16 Chemins minimaux
On se propose de calculer les plus courts chemins dans un graphe par lalgorithme
de Floyd. Soient n villes et, pour 0 i < n et 0 j < n, C
ij
le cot du trajet de la ville i
vers la ville j (avec C
ii
= 0, C
ij
non ncessairement gal C
ji
, et ventuellement C
ij
=
si ce trajet nexiste pas). Le cot dun chemin i
1
, i
2
, ..., i
p
est la somme
p1
k=1
C
i
k
i
k+1
.
Pour trouver les cots de tous les chemins minimaux entre toutes les villes, il suft de
construire, pour 0 k < n, les matrices D
(k)
dnies par :
_
D
(0)
= C
D
(k)
ij
= min(D
(k1)
ij
, D
(k1)
ik
+ D
(k1)
kj
)
Le cot du chemin minimum entre i et j est alors D
(n1)
ij
(ventuellement si on ne
peut relier i j).
Questions
1. Dnir une matrice int C[n][n] reprsentant le graphe gure B.16 dans la-
quelle chaque che de i vers j reprsente un C
ij
2. Ecrire une fonction void floyd(const int C[n][n],int D[n][n]) cal-
culant D
(n1)
partir de C
3. Appeler cette fonction et afcher les cots des chemins minimaux pour tous les
(i, j).
318
B. Examens B.21. Devoir surveill 2004 : nonc
Chemins
Pour mmoriser les chemins et non plus seulement leur cot, il suft de rajouter
une matrice P correspondant aux prdcesseurs dans les chemins minimaux. Cette
matrice est dnie comme suit :
Initialement, P
ij
= i si i = j et C
ij
< , P
ij
= sinon.
Lorsque D
(k1)
ik
+ D
(k1)
kj
est retenu comme valeur de D
(k)
ij
, alors faire P
ij
= P
kj
Aprs quoi, le chemin minimal de i vers j est celui de i vers P
ij
, suivi du trajet P
ij
j.
1. Modier la fonction floyd pour quelle remplisse cette matrice P.
2. Ecrire une fonction rcursive void chemin(const int P[n][n],int i, int j)
afchant le chemin minimal de i vers j pour P donne.
3. Utiliser les deux fonctions prcdentes pour calculer et afcher tous les chemins
minimaux dans le graphe prcdent.
B.21.4 Tours de Hano
Principe et rgles
Le jeu est constitu de 3 tiges, A, B et C, sur lesquelles sont empils des anneaux de
taille dcroissante, que lon numrotera de N 1.
FIGURE B.17 Conguration initiale
Au dbut du jeu, tous les anneaux sont empils sur la tige A (voir gure B.17). Lob-
jectif est de transfrer tous les anneaux sur la tige C. Il nest possible de dplacer les
anneaux que un par un, et il nest pas possible de poser un anneau sur un autre de
taille infrieure.
On dsignera par le terme configuration une disposition des anneaux sur les
tiges (voir gure B.18).
FIGURE B.18 Exemple de conguration
319
B.21. Devoir surveill 2004 : nonc B. Examens
On dsignera par le terme dplacement de la tige X la tige Y le fait denlever
lanneau situ au sommet de la pile de la tige X pour le positionner au sommet de la
pile de la tige Y (voir gure B.19).
FIGURE B.19 Exemple de dplacement
Objectif
Lobjectif ici est dcrire un programme qui afche la suite des dplacements ra-
liser pour rsoudre le problme avec n anneaux, sous la forme suivante :
numero_anneau : tige_origine > tige_destination
On souhaite de plus afcher ltat du jeu (les anneaux prsents sur chaque tige) aprs
chaque dplacement.
Exemple pour 2 anneaux (la gure B.20 en donne une reprsentation graphique) :
A> 2 1
B> . .
C> . .
deplacement: 1 : A->B
A> 2 .
B> 1 .
C> . .
deplacement: 2 : A->C
A> . .
B> 1 .
C> 2 .
deplacement: 1 : B->C
A> . .
B> . .
C> 2 1
Ce problme apparemment complexe se traite en fait simplement : pour transfrer
n anneaux dune tige X une tige Y, il "suft" deffectuer :
le transfert des n-1 anneaux suprieurs de la tige X sur la troisime tige Z
le dplacement de lanneau n de la tige X la tige destination Y
le transfert des n-1 anneaux de la tige Z la tige Y
320
B. Examens B.21. Devoir surveill 2004 : nonc
FIGURE B.20 Solution du problme pour N=2
Pour transfrer n-1 anneaux, on applique la mme mthode : transfert de n-2 anneaux
de la tige origine une tige temporaire, dplacement dun anneau de la tige origine
la tige destination, transfert des n-2 anneaux de la tige temporaire la tige destination
...
Questions
1. Dclarer une constante N, que lon xera 10, et qui reprsentera le nombre dan-
neaux du problme rsoudre.
2. Crer un objet capable de stocker une conguration. On pourra par exemple re-
prsenter chaque tige par un tableau de taille N, et chaque anneau par un entier
entre 1 et N. Vous utiliserez au choix une structure ou une classe, selon ce qui
vous semble le plus pertinent, ou dfaut ce avec quoi vous tes le plus laise.
3. Programmer de quoi initialiser une conguration, cest dire lui donner sa valeur
de dpart (tous les anneaux sur la premire tige).
4. Programmer une fonction permettant dafcher une conguration lcran, par
exemple sous la forme utilise ci-dessus pour lexemple pour N=2.
5. Programmer une fonction ralisant un dplacement : pour une tige origine et une
tige destination spcies, elle prlve le pion suprieur sur la tige origine et le
place sur la tige destination.
6. En vous basant sur la mthode propose, programmer une fonction ralisant un
transfert de k anneaux dune tige une autre, et afchant au fur et mesure les
dplacements effectus, et ltat du jeu aprs chaque dplacement.
7. En utilisant les lments prcdents, crire une fonction main() qui rsoud le pro-
blme des tours de Hanoi de taille N et afche la solution complte.
321
B.21. Devoir surveill 2004 : nonc B. Examens
Questions complmentaires
1. Si lon avait souhait nafcher que la liste des dplacements effectuer sans
afcher chaque fois les congurations intermdaires, le programme aurait t
beaucoup plus simple. Pourquoi ? Ecrire la fonction correspondante (5 lignes de
code environ).
2. Pour rsoudre le problme avec N anneaux, combien de dplacements sont n-
cessaires ?
B.21.5 Table de hachage
On a souvent besoin, en informatique, de manipuler des donnes sous forme de
tableaux associatifs, dans lesquels on associe une valeur chaque clef dun ensemble.
Cest le cas quand on veut reprsenter un annuaire ( un nom, on associe un numro
de tlphone) ou un dictionnaire monolingue ( un mot, on associe une dnition). On
se place dans le cas o clefs et valeurs sont reprsentes par des chanes de caractres
et o une unique valeur est associe chaque clef.
Une manire efcace de reprsenter cette notion abstraite de tableau associatif est
dutiliser une table de hachage. On se xe un entier naturel N et on alloue en mmoire
un tableau de N cases. On suppose que lon dispose dune fonction de hachage h qui
une clef (une chane de caractres, donc), associe un entier entre 0 et N1. On stockera
le couple clef/valeur (c, v) dans la case h(c) du tableau. Ainsi, pour rechercher par la
suite quelle valeur est associe une clef c, il sufra de regarder dans la case h(c), sans
avoir besoin de parcourir lintgralit du tableau.
Un problme qui se pose est que h nest pas une fonction injective. Non seulement
il y a potentiellement un nombre inni de clefs, mais on souhaite galement garder N
relativement petit pour ne pas avoir de trop gros besoins en mmoire. Il faut donc pr-
voir les collisions, cest--dire les cas de deux clefs distinctes c et c

telles que h(c) = h(c

).
Plusieurs stratgies de gestion des collisions existent, on en choisira une trs simple : si,
au moment dinsrer le couple (c

, v

), la case h(c

) est dj occupe par le couple (c, v),


on retente linsertion dans la case h(c

) + 1. Si celle-ci est nouveau occupe, on es-


saye la case suivante, etc., jusqu trouver une case vide ou avoir parcouru lintgralit
du tableau (on considre que la case 0 succde la case N 1), auquel cas linsertion
choue (le tableau associatif comportera donc au plus N lments). La recherche dune
clef dans la table de hachage se passe de faon similaire.
Une table de hachage est donc caractrise par :
sa taille N (on prendra N=1021)
la fonction de hachage h. Pour une clef c = (x
0
. . . x
l1
) de longueur l, on prendra :
h(c) =
_
l1

i=0
B
l1i
x
i
_
mod N
o B est une constante, que lon xera 256 et x mod y dsigne le reste de la
division euclidienne de x par y
la manire de grer les collisions, dcrite plus haut
1. Dnir les constantes globales N et B.
2. Dnir la fonction de hachage int hachage(const string &clef).
322
B. Examens B.21. Devoir surveill 2004 : nonc
3. Dnir une structure Entree, qui correspondra une case de la table de hachage.
Cette structure contient trois champs : la clef, la valeur et un boolen indiquant si
la case est occupe.
4. Dnir une classe TableHachage comprenant :
un tableau de N Entree
une fonction membre void inserer(const string &clef, const string &valeur);
une fonction membre string rechercher(const string &clef) const;
Un constructeur est-il ncessaire ? Pourquoi ? Le dnir le cas chant.
5. Dnir la fonction TableHachage::inserer. Dans le cas o la table est pleine,
on pourra simplement afcher un message lcran et ne rien faire dautre.
6. Dnir la fonction TableHachage::rechercher. Dans le cas o la clef nest
pas prsente dans la table, on pourra renvoyer la chane vide.
7. Dnir une fonction main utilisant un objet de type TableHachage. On pourra
stocker le mini-annuaire suivant, puis rechercher et afcher le numro de tl-
phone du SAMU.
SAMU 15
Police Secours 17
Pompiers 18
8. Sans rien programmer, expliquer ce quil faudrait faire pour rajouter la possibilit
denlever un lment la table de hachage.
323
B.22. Devoir surveill 2003 : nonc B. Examens
B.22 Devoir surveill 2003 : nonc
B.22.1 Tableau dexcution
Remplir le tableau dexcution du programme suivant :
1 int suite(int x){
2 if (x%2 == 0)
3 return x/2;
4 else
5 return 3
*
x + 1;
6 }
7
8 bool test(int y){
9 bool h;
10
11 if (y == 1)
12 h = true;
13 else
14 h = test(suite(y));
15
16 return h;
17 }
18
19 void initialise(int& z){
20 z = 4;
21 }
22
23 int fin(int& w){
24 w += 36;
25 return w+2;
26 w += 1;
27 }
28
29 int main(){
30 int a;
31 initialise(a);
32
33 if (test(a))
34 a = fin(a);
35
36 return 0;
37 }
Il sagit dcrire, si elles existent, les valeurs des variables du programme pour
chaque excution de ligne. Question bonus : ce code gnre un warning la compi-
lation; lequel, quelle ligne ?
Conventions :
324
B. Examens B.22. Devoir surveill 2003: nonc
mettre le numro de la ligne quon vient dexcuter (ventuellement, on peut
dcouper lexcution dune mme ligne en plusieurs tapes) ;
laisser en blanc les variables non existantes ;
mettre un point dinterrogation pour les variables dclares non initialises ;
pour une variable qui nest autre quune rfrence sur une autre variable x, on
indiquera [x] ;
on peut utiliser le symbole pour rpter la valeur dune variable la ligne sui-
vante ;
pour gagner de la place dans le tableau, les noms des variables nont t indexs
que par la premire lettre du nom de la fonction laquelle elles sont rattaches,
suivie le cas chant du numro dappel la fonction. De mme, le retour de la
fonction f a pour nom ret
f
.
B.22.2 Erreurs
Corriger le code suivant :
1 // fichier main.cpp
2
3 class paire {
4 string clef;
5 int valeur;
6 public:
7 paire(string key, int value);
8 void affecte_clef(string key) const;
9 void affecte_valeur(int value) const;
10 string donne_clef() const;
11 int donne_valeur() const;
12 }
13
14 paire::paire(string key, int value) {
15 clef=key;
16 valeur=value;
17 }
18
19 void paire::affecte_clef(string key) {
20 clef=key;
21 }
22
23 void paire::affecte_valeur(int value) {
24 valeur=value;
25 }
26
27 string paire::donne_clef() {
28 return clef;
29 }
30
31 int paire::donne_valeur() {
32 return valeur;
325
B.22. Devoir surveill 2003: nonc B. Examens
33 }
34
35 int cherche_valeur(paire repertoire[], int n, string key) {
36 for (i=1, i<=n, i++) {
37 if repertoire[i].donne_clef = key
38 return (repertoire[i].donne_valeur);
39 cout << "clef non trouvee" << endl;
40 }
41
42 int main()
43 {
44 paire a={"cheval",4};
45
46 paire b;
47 b.clef="poule";
48 b.valeur=2;
49
50 paire
*
pattes = new paire[5];
51 pattes = {a, b, paire("araignee",8), paire("baleine",0),
52 paire("martien",3)};
53
54 cout << "Un martien a "
55 << cherche_valeur(pattes[5],5,"martien")
56 << " pattes." << endl;
57
58 return 0;
59 }
B.22.3 Quafche ce programme ?
Examinez bien le contenu du programme suivant. La question est simple : quelles
sont les diffrentes lignes afches sur la sortie standard (cest--dire en console, via les
instructions cout << ... ;) au cours de son excution? Justiez vos rponses.
1 #include <iostream>
2 using namespace std;
3
4 class Paire {
5 double x,y;
6 public:
7 Paire();
8 Paire(double a,double b);
9 Paire operator+(const Paire &v) const;
10 Paire operator-(const Paire &v) const;
11 double getx() const;
12 double gety() const;
13 void setxy(double xx,double yy);
14 };
15
326
B. Examens B.22. Devoir surveill 2003 : nonc
16 Paire::Paire() {
17 cout << "Constructeur sans argument." << endl;
18 }
19
20 Paire::Paire(double a,double b) {
21 x=a;
22 y=b;
23 cout << "Constructeur avec arguments [" << x << "," << y << "]" << endl;
24 }
25
26 Paire Paire::operator+(const Paire& v) const {
27 cout << "Addition [" << x << "," << y << "] [" << v.x << ","
28 << v.y << "]" << endl;
29 Paire w;
30 w.x=x+v.x;
31 w.y=y+v.y;
32 return w;
33 }
34
35 Paire Paire::operator-(const Paire &v) const {
36 cout << "Soustraction [" << x << "," << y << "] [" << v.x << ","
37 << v.y << "]" << endl;
38 return Paire(x-v.x,y-v.y);
39 }
40
41 double Paire::getx() const {
42 return x;
43 }
44
45 double Paire::gety() const {
46 return y;
47 }
48
49 void Paire::setxy(double xx,double yy) {
50 x=xx;
51 y=yy;
52 }
53
54 int main(void) {
55 Paire tab[2];
56 tab[0].setxy(3,4);
57 tab[1]=Paire(4,6);
58
59 Paire a(1,1);
60 Paire b= ( ( a+Paire(1,2) ) + tab[0] ) - tab[1];
61 cout << "Resultat [" << b.getx() << "," << b.gety() << "]" << endl;
62 }
327
B.22. Devoir surveill 2003 : nonc B. Examens
B.22.4 Le jeu du Pendu
Tout le monde connat le jeu de pendu. Rappelons tout de mme les rgles pour
plus de sret ... Il sagit de trouver un mot cach en proposant des lettres une par
une. Si la lettre propose nest pas prsente dans le mot, une erreur est comptabilise.
Si le joueur dpasse 11 erreurs avant davoir trouv le mot, il a perdu. Inversement,
si le joueur parvient dcouvrir lintgralit du mot avant de totaliser 11 erreurs, il a
gagn.
Commencez par programmer au brouillon! ! !
On dispose dune fonction string dictionnaire(int n) qui renvoie un mot de
10 lettres diffrent pour chaque entier compris entre 0 et 999 pass en argument.
1. Utiliser cette fonction pour programmer une fonction mot_mystere qui renvoie
un mot mystre faire dcouvrir, en faisant en sorte que ce mot change alatoi-
rement chaque excution.
On utilise un tableau de boolens pour stocker le fait que le i
eme
caractre du mot
mystre ait t dcouvert ou non.
Exemple : pour "maquillage", si le joueur a dj entr i, a et s, le tableau de boolens est
{false, true, false, false, true, false, false, true, false, false}.
2. Programmer une fonction affiche qui partir du mot mystre et du tableau de
boolens associ afche lcran le rsultat courant (mot mystre dans lequel les
caractres non dcouverts sont remplacs par une toile *).
Exemple : pour "maquillage", si le joueur a dj entr i, a et s, le rsultat courant est
"*a**i**a**". La fonction afche donc "*a**i**a**".
3. Programmer une fonction essai qui demande lutilisateur de rentrer un carac-
tre au clavier et qui met jour le tableau de boolens en consquence.
4. Programmer une fonction reste qui renvoie le nombre de caractres restant
trouver dans le mot mystre (pour 2 caractres identiques, on comptera 2 pour
simplier).
Exemple : pour "maquillage", si le joueur a dj entr i, a et s, le rsultat courant est
"*a**i**a**". Il reste 7 caractres trouver (m,q,u,l,l,g,e).
On dispose maintenant de sufsants dlments pour construire une version simple
du jeu, sans notion de nombre limit dessais.
5. A partir des fonctions prcdentes, programmer une fonction jeu_mystere qui
demande lutilisateur dentrer un caractre puis afche le rsultat courant jus-
qu ce que le mot mystre soit entirement dcouvert, et signale ensuite luti-
lisateur quil a gagn.
Il sagit maintenant de rajouter cette premire version du jeu la notion de nombre
limit dessais.
6. Programmer une nouvelle version de la fonction essai pour quelle renvoie en
plus une variable signalant si le caractre entr par lutilisateur restait dcouvrir
(succs) ou non (chec) dans le mot mystre.
Exemple : pour "maquillage", si le joueur a dj entr i, a et s, le rsultat courant est
"*a**i**a**". Si lutilisateur rentre m, la fonction renvoie succs ; si lutilisateur rentre
z, la fonction renvoie chec ; si lutilisateur rentre a (dj rentr), la fonction renvoie
chec.
328
B. Examens B.22. Devoir surveill 2003: nonc
7. Assembler les fonction prcdentes lintrieur dune fonction jeu_pendu pour
crer un jeu de pendu. Grer la dfaite du joueur si il arrive 11 checs. Grer la
victoire du joueur si il dcouvre lintgralit du mot mystre avant de totaliser 11
checs.
B.22.5 Programme mystre
Le programme suivant est inspir dun clbre puzzle. Il sagit de deviner la logique
dune suite de chanes de caractres. On donne les quatre premiers lments de la suite
et on demande de trouver les deux suivants. Il y a un pige et certaines personnes
mettent plusieurs jours trouver...
Ici, nous ne vous donnons pas les premiers lments, mais un programme qui g-
nre la suite complte. Pour quelquun qui parle C++, ce programme est donc directe-
ment la solution du puzzle. A vous de jouer :
Que fait exactement ce programme ? Donnez le principe de la suite et dtaillez
un peu le fonctionnement de la fonction next().
Quafche t-il ?
Le programmeur a construit ligne 15 le caractre reprsentant le chiffre count
en utilisant char(0+count). Il a donc suppos que count restait plus petit
que 10. Pour se rassurer, il a ajout un assert() ligne 14. Etait-ce la peine ? Le
assert() reste-t-il vrai si on afche le reste de la suite ? La condition aurait-elle
pu tre encore plus restrictive ?
Question subsidiaire (hors examen!) : faites le test autour de vous et voyez com-
bien de personnes trouvent le 5
eme
lment partir des 4 premiers...
1 // Programme mystre...
2
3 #include <iostream>
4 #include <string>
5 #include <cassert>
6 using namespace std;
7
8 string next(string s) {
9 string t;
10 int count=1;
11 for (unsigned int i=0;i<s.size();i++) {
12 if (i==s.size()-1 || s[i]!=s[i+1]) {
13 string u="xx";
14 assert(count<10);
15 u[0]=char(0+count); // caractre reprsentant le chiffre count
16 u[1]=s[i];
17 t+=u;
18 count=1;
19 } else
20 count++;
21 }
22 return t;
23 }
24
329
B.22. Devoir surveill 2003 : nonc B. Examens
25 int main()
26 {
27 string s="1";
28 cout << s << endl;
29 for (int i=0;i<8;i++) {
30 s=next(s);
31 cout << s << endl;
32 }
33 return 0;
34 }
...
330
C. Imagine++
Annexe C
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 (gra-
phisme 2D et 3D) et Eigen (algbre linaire). Ceux-ci proposent une richesse de pos-
sibilits 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 modie vous de-
vez offrir selon les mmes termes les sources de vos modications. Une documentation
complte est disponible sur la page Web dImagine++, dtaillant linsallation et luti-
lisation.
Tout est englob dans un namespace Imagine, donc si vous voulez viter de pr-
xer les noms par Imagine:: vous devez utiliser dans votre code
usi ng namespace Imagine ;
C.1 Common
Imagine/Common.h dnit 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 noi r = Color ( 0 , 0 , 0 ) ;
Color bl anc = Color ( 255 , 255 , 255) ;
Color rouge = Color ( 2 5 5 , 0 , 0 ) ;
Un certain nombre de constantes de ce type sont dj dnies : BLACK, WHITE, RED,
GREEN, BLUE, CYAN, MAGENTA, YELLOW.
Un type byte (synonyme de unsigned char) est dni pour coder une valeur
entire entre 0 et 255.
C.2 Graphics
Imagine/Graphics.h propose du dessin en 2D et 3D. Les coordonnes 2D sont
en pixel, laxe des x est vers la droite et laxe des y vers le bas (attention, ce nest pas
le sens mathmatique usuel !). Le point (0,0) est donc le coin haut-gauche de la fentre
(pour les fonctions de trac) ou de lcran (pour openWindow).
C.3. Image C. Imagine++
openWindow( 5 0 0 , 5 0 0 ) ; / / T a i l l e de f e n t r e
drawRect ( 10 , 10 , 480 , 480 ,RED) ; / / Coi n haut gauche ( 10 , 10) ,
/ / l a r g e ur 480 , haut e ur 480
drawLine ( 10 , 10 , 490 , 490 , BLUE) ; / / Di agonal e
Window w = openWindow( 1 0 0 , 1 0 0 ) ; / / Aut re f e n t r e
setActiveWindow(w) ; / / S l e c t i o n pour l e s pr o c ha i ns t r a c s
drawStri ng ( 10 , 10 , "Du t e xt e " , MAGENTA) ; / / Me t t r e du t e x t e
endGraphics ( ) ; / / At t end un c l i c s o ur i s avant de f e r me r l e s f e n t r e s
Si on a beaucoup de dessins faire la suite et quon veut nafcher que le rsultat
nal (cest plus esthtique), on encadre le code de trac par :
noRefreshBegi n ( ) ;
. . .
noRefreshEnd ( ) ;
Pour faire une animation, il est utile de faire une petite pause entre les images pour
rguler la cadence :
mi l l i Sl e ep ( 5 0 ) ; / / Temps en mi l l i s e c o n d e s
On peut charger une image (loadGreyImage, loadColorImage) ou sauvegar-
der (saveGreyImage, saveColorImage) dans un chier. Attention, ces fonctions
allouent de la mmoire quil ne faut pas oublier de librer aprs usage.
byt e g ;
i nt l argeur , hauteur ;
loadGreyImage ( srcPat h ( " image . j pg " ) , g , l argeur , hauteur ) ;
/ / On a ur a i t d v r i f i e r l e r e t o ur de l oadGr e yI mage pour s a v o i r
/ / s i l e char ge me nt s e s t b i e n pa s s . . .
/ / De s s i ne ave c c o i n haut gauche en ( 0 , 0 ) :
putGreyImage ( 0 , 0 , g , l argeur , hauteur ) ;
del et e [ ] g ; / / Ne pas o u b l i e r !
A noter srcPath, dni dans Common, qui indique de chercher dans le dossier conte-
nant les chiers source.
En fait, pour viter de grer soi-mme la mmoire des images, il existe une classe
ddie cela :
C.3 Image
Imagine/Images.h gre le chargement, la manipulation et la sauvegarde des
images.
Image<byte > im; / / Image en ni ve aux de g r i s
l oad ( im, " f i chi er _i mage . j pg " ) ; / / I l f a u d r a i t v r i f i e r l e r e t o ur
di spl ay ( im ) ; / / De s s i ne dans l a f e n t r e a c t i v e
im( 0 , 0) =128; / / Met l e p i x e l en g r i s
save ( im, " f i chi er _i mage2 . png" ) ; / / Sauve gar de l i mage dans un f i c h i e r
Attention : la recopie et laffectation (oprateur =) sont des oprations peu co-
teuses qui en fait ne font quun lien entre les images, sans relle copie. Ce qui fait que
la modication de lune affecte lautre :
332
C. Imagine++ C.4. LinAlg
Image<Color > im1 ( 1 0 0 , 1 0 0 ) ;
Image<Color > im2 = im1 ; / / Co ns t r uc t e ur par r e c o p i e
im1 ( 10 , 10) = CYAN;
as s e r t ( im2 ( 10 , 10) == CYAN) ; / / im2 a t a f f e c t e
Pour faire une vraie copie plutt quun lien, on utilise :
im2 = im1 . cl one ( ) ;
im1 ( 10 , 10) = CYAN; / / N a f f e c t e pas im2
Ainsi, si on passe une image comme paramtre dune fonction, puisque cest le
constructeur par copie qui est appel, tout se passe comme si on avait pass par rf-
rence :
void f ( Image<Color > im) { / / Pas s age par va l e ur
im( 10 , 10) = CYAN;
}
f ( im1 ) ; / / Mo di f i e quand mme l e p i x e l ( 10 , 10)
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.
C.4 LinAlg
Imagine/LinAlg propose lalgbre linaire avec des classes matrice et vecteur.
Matrix<f l oat > I ( 2 , 2 ) ; / / T a i l l e 2x2
I . f i l l ( 0 . 0 f ) ; / / Mat r i c e nul l e
I ( 0 , 0) = I ( 1 , 1) =1. 0 f ; / / Mat r i c e i d e n t i t
cout << " det ( I )= " << det ( I ) << endl ; / / Dt ermi nant
Les oprateurs daddition (matrice+matrice, vecteur+vecteur), soustraction (matrice-
matrice, vecteur-vecteur) et multiplication (matrice*matrice, matrice*vecteur) sont bien
sr dnis.
Comme pour les images, attention de ne pas sortir des bornes en accdant aux
lments des matrices et vecteurs !
Une fonction trs utile est linSolve pour rsoudre un systme linaire.
333
D. Fiche de rfrence nale
Annexe D
Fiche de rfrence nale
Fiche de rfrence (1/5)
Variables
Dnition :
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=254;
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);
int i,j;
double x=double(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;
...
}
...
}
Tests
Comparaison :
== != < > <= >=
Ngation : !
Combinaisons : && ||
if (i==0)
j=1;
if (i==0)
j=1;
else
j=2;
if (i==0) {
j=1;
k=2;
}
bool t=(i==0);
if (t)
j=1;
switch (i) {
case 1:
...;
...;
break;
case 2:
case 3:
...;
break;
default:
...;
}
mx=(x>y)?x:y;
Boucles
do {
...
} while (!ok);
int i=1;
while (i<=100) {
...
i=i+1;
}
for (int i=1;i<=10;i++)
...
for (int i=1,j=10;j>i;
i=i+2,j=j-3)
...
for (int i=...)
for (int j=...) {
//saute le cas i==j
if (i==j)
continue;
...
}
for (int i=...) {
...
if (t[i]==s){
...
// quitte la boucle
break;
}
...
}
D. Fiche de rfrence nale
Fiche de rfrence (2/5)
Fonctions
Dnition :
int plus(int a,int b) {
int c=a+b;
return c;
}
void affiche(int a) {
cout << a << endl;
}
Dclaration :
int plus(int a,int b);
Retour :
int signe(double x) {
if (x<0)
return -1;
if (x>0)
return 1;
return 0;
}
void afficher(int x,
int y) {
if (x<0 || y<0)
return;
if (x>=w || y>=h)
return;
DrawPoint(x,y,RED);
}
Appel :
int f(int a) { ... }
int g() { ... }
...
int i=f(2),j=g();
Rfrences :
void swap(int& a,
int& b){
int tmp=a;
a=b;b=tmp;
}
...
int x=3,y=2;
swap(x,y);
Surcharge :
int hasard(int n);
int hasard(int a,
int b);
double hasard();
Oprateurs :
vect operator+(
vect A,vect B) {
...
}
...
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
}
Valeurs par dfaut :
void f(int a,int b=0);
void g() {
f(12); // f(12,0);
f(10,2);// f(10,2);
}
void f(int a,int b) {
// ...
}
Inline (appel rapide) :
inline double sqr(
double x) {
return x
*
x;
}
...
double y=sqr(z-3);
Rfrence en retour :
int i; // Var. globale
int& f() {
return i;
}
...
f()=3; // i=3!
Tableaux
Dnition :
double x[10],y[10];
for(int i=0;i<10;i++)
y[i]=2
*
x[i];
const int n=5;
int i[n],j[2
*
n]; //OK
Initialisation :
int t[4]={1,2,3,4};
string s[2]={"ab","c"};
Affectation :
int s[3]={1,2,3},t[3];
for (int i=0;i<3;i++)
t[i]=s[i];
En paramtre :
void init(int t[4]) {
for(int i=0;i<4;i++)
t[i]=0;
}
void init(int t[],
int n) {
for(int i=0;i<n;i++)
t[i]=0;
}
Taille variable :
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];
A[i+2
*
j]=...;
Taille variable (suite) :
int
*
t,
*
s,n;
En paramtre (n) :
void f(const int
*
t,
int n) {
...
s+=t[i]; // OK
...
t[i]=...; // NON!
}
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 en-
tirement public (cf objets !)
336
D. Fiche de rfrence nale
Fiche de rfrence (3/5)
Objets
struct obj {
int x; // champ
int f(); // mthode
int g(int y);
};
int obj::f() {
int i=g(3); // mon g
int j=x+i; // mon x
return j;
}
...
int main() {
obj a;
a.x=3;
int i=a.f();
class obj {
int x,y;
void a_moi();
public:
int z;
void pour_tous();
void un_autre(obj A);
};
void obj::a_moi() {
x=..; // OK
..=y; // OK
z=..; // OK
}
void obj::pour_tous() {
x=..; // OK
a_moi(); // OK
}
void une_autre(obj A) {
x=A.x; // OK
A.a_moi(); // OK
}
...
int main() {
obj A,B;
A.x=..; //NON
A.z=..; //OK
A.a_moi(); //NON
A.pour_tous(); //OK
A.une_autre(B); //OK
class obj {
obj operator+(obj B);
};
...
int main() {
obj A,B,C;
C=A+B;
// C=A.operator+(B)
Mthodes constantes :
void obj::f() const{
...
}
void g(const obj& x){
x.f(); // OK
}
Constructeur :
class point {
int x,y;
public:
point(int X,int Y);
};
point::point(int X,
int Y){
x=X;
y=Y;
}
...
point a(2,3);
Constructeur vide :
obj::obj() {
...
}
...
obj a;
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() {
...
}
Constructeur de copie :
obj::obj(const obj& o){
...
}
Utilis par :
- obj b(a);
- obj b=a;
// mieux que obj b;b=a;
- paramtres des fonctions
- valeur de retour
Affectation :
obj& obj::operator=(
const obj&o){
...
return
*
this;
}
Objets avec allocation dyna-
mique automatique : cf section
10.11
Accesseurs :
class mat {
double
*
x;
public:
double& operator()
(int i,int j){
assert(i>=0 ...);
return x[i+M
*
j];
}
double operator()
(int i,int j)const{
assert(i>=0 ...);
return x[i+M
*
j];
}
...
Compilation spare
#include "vect.h", y
compris dans vect.cpp
Fonctions : dclarations dans
le .h, dnitions dans le .cpp
Types : dnitions dans le .h
Ne dclarer dans le .h que les
fonctions utiles.
#pragma once au dbut du
chier.
Ne pas trop dcouper...
STL
min,max,...
complex<double> z;
pair<int,string> p;
p.first=2;
p.second="hop";
#include<list>
using namespace std;
...
list<int> l;
l.push_front(1);
...
if (l.find(3)!=l.end())
...
list<int>::const_iterator
it;
for (it=l.begin();
it!=l.end();it++)
s+=
*
it;
list<int>::iterator it
for (it=l.begin();
it!=l.end();it++)
if (
*
it==2)
*
it=4;
stack, queue, heap, map,
set, vector, ...
337
D. Fiche de rfrence nale
Fiche de rfrence (4/5)
Entres/Sorties
#include <iostream>
using namespace std;
...
cout <<"I=" <<i <<endl;
cin >> i >> j;
#include <fstream>
using namespace std;
ofstream f("hop.txt");
f << 1 << << 2.3;
f.close();
ifstream g("hop.txt");
if (!g.is_open()) {
return 1;
}
int i;
double x;
g >> i >> x;
g.close();
do {
...
} while (!(g.eof());
ofstream f;
f.open("hop.txt");
double x[10],y;
ofstream f("hop.bin",
ios::binary);
f.write((const char
*
)x,
10
*
sizeof(double));
f.write((const char
*
)&y,
sizeof(double));
f.close();
ifstream g("hop.bin",
ios::binary);
g.read((char
*
)x,
10
*
sizeof(double));
g.read((const char
*
)&y,
sizeof(double));
g.close();
string nom;
ifstream f(nom.c_str());
#include <sstream>
using namespace std;
stringstream f;
// Chane vers entier
f << s;
f >> i;
// Entier vers chane
f.clear();
f << i;
f >> s;
ostream& operator<<(
ostream& f,
const point&p) {
f<<p.x<< << p.y;
return f;
}
istream& operator>>(
istream& f,point& p){
f>>p.x>>p.y;
return f;
}
Template
Fonctions :
// A mettre dans LE
// fichier qui lutilise
// ou dans un .h
template <typename T>
T maxi(T a,T b) {
...
}
...
// Le type est trouv
// tout seul!
maxi(1,2); //int
maxi(.2,.3); //double
maxi("a","c");//string
Objets :
template <typename T>
class paire {
T x[2];
public:
paire() {}
paire(T a,T b) {
x[0]=a;x[1]=b;
}
T somme()const;
};
...
template <typename T>
T paire<T>::somme()const{
return x[0]+x[1];
}
...
// Le type doit tre
// prcis!
paire<int> a(1,2);
int s=a.somme();
paire<double> b;
...
Multiples :
template <typename T,
typename S>
class hop {
...
};
...
hop<int,string> A;
...
Entiers :
template <int N>
class hop {
..
};
...
hop<3> A;
hop<5> B;
...
Conseils
Travailler en local
Imagine++ Project
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 chiers spars.
Le .h doit sufre lutilisa-
teur (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 ob-
jets !
Penser interface / implmen-
tation / utilisation.
Clavier
Build : F7
Debug : F5
Step over : F10
Step inside : F11
Indent : Ctrl+K,Ctrl+F
Add New It. : Ctrl+Maj+A
Add Exist. It. : Alt+Maj+A
Step out : Maj+F11
Run to curs. : Click droit
Compltion : Alt+
Gest. tches : Ctrl+Maj+Ech
338
D. Fiche de rfrence nale
Fiche de rfrence (5/5)
Divers
i++;
i--;
i-=2;
j+=3;
j=i%n; // Modulo
#include <cstdlib>
...
i=rand()%n;
x=rand()/
double(RAND_MAX);
#include <ctime>
...
srand((unsigned int)
time(0));
#include <cmath>
double sqrt(double x);
double cos(double x);
double sin(double x);
double acos(double x);
#include <string>
using namespace std;
string s="hop";
char c=s[0];
int l=s.size();
if (s1==s1) ...
if (s1!=s2) ...
if (s1<s2) ...
size_t i=s.find(h),
j=s.find(h,3);
k=s.find("hop");
l=s.find("hop",3);
a="comment";
b="a va?";
txt=a+" "+b;
s1="un deux trois";
s2=string(s1,3,4);
getline(cin,s);
getline(cin,s,:);
const char
*
t=s.c_str();
#include <ctime>
s=double(clock())
/CLOCKS_PER_SEC;
#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 :
set(i,1) : i|=(1<<n)
reset(i,1) : i&=~(1<<n)
test(i,1) : if (i&(1<<n))
ip(i,1) : i^=(1<<n)
Erreurs frquentes
Pas de dnition de fonction
dans une fonction!
int q=r=4; // NON!
if (i=2) // NON!
if i==2 // NON!
if (i==2) then // NON!
for (int i=0,i<100,i++)
// NON!
int f() {...}
...
int i=f; // NON!
double x=1/3; // NON!
int i,j;
double x;
x=i/j; // NON!
x=double(i/j); //NON!
double x[10],y[10];
for (int i=1;i<=10;i++)
y[i]=2
*
x[i]; //NON
int n=5;
int t[n]; // NON
int f()[4] { // NON!
int t[4];
...
return t; // NON!
}
int t[4]; t=f();
int s[3]={1,2,3},t[3];
t=s; // NON!
int t[2];
t={1,2}; // NON!
struct Point {
double x,y;
} // NON!
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!
class point {
int x,y;
public:
...
};
...
point a={2,3}; // NON
Oublier de rednir le
constructeur vide.
point p=point(1,2);//NON
point p(1,2); // OUI
obj
*
t=new obj[n];
...
delete t; // manque []
//NON!
void f(int a=2,int b);
void f(int a,int b=0);
void f(int a);// NON!
Ne pas tout mettre inline!
int f() {
...
}
...
f()=3; // HORREUR!
int& f() {
int i;
return i;
}
...
f()=3; // NON!
if (i>0 & i<n) // NON
if (i<0 | i>n) // NON
if (...) {
...
if (...)
break; // Non,
// boucles seulement
}
for (i ...)
for (j ...) {
...
if (...)
break;//NON, quitte
// juste la boucle j
int i;
double x;
...
j=max(i,0);//OK
y=max(x,0);//NON!
// 0.0 et non 0: max
// est un template STL
Imagine++
Voir documentation...
339

You might also like