Didacticiel Bases de programmation en C++

Transcription

Didacticiel Bases de programmation en C++
1
Université Grenoble alpes.
Auteur :Frédéric Faure
Version : 10 septembre 2016.
Didacticiel
Bases de programmation en C++
Table des matières
1
Introduction
8
1.1
Choix du système d'exploitation . . . . . . . . . . . . . . . . . . . . . . . .
9
1.2
Environnement de programmation . . . . . . . . . . . . . . . . . . . . . . .
9
1.2.1
(conseillé) Programmer avec l'éditeur emacs et une fenêtre de commandes
1.2.2
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
(optionnel) Programmer avec l'environnement codeblocks
. . . . .
10
11
I Bases du langage C++
13
2
Le tout début
14
3
Lire le clavier et acher à l'écran
16
4
3.1
Achage dans une console . . . . . . . . . . . . . . . . . . . . . . . . . . .
16
3.2
Lire le clavier
17
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Déclaration et aectation des objets
4.1
4.2
Objets de base
19
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Les déclarations d'objets de base
4.1.2
Initialisation d'un objet de base . . . . . . . . . . . . . . . . . . . .
19
4.1.2.1
. . . . . . . . . . . . . . . . . . . . . . . . . .
20
4.1.3
Achage des nombres avec une précision donnée . . . . . . . . . . .
21
4.1.4
Attention à la division Euclidienne
4.1.5
Pour générer un nombre entier p aléatoire
4.1.6
Quelques opérations élémentaires
4.1.7
Quelques opérations sur les bits (base deux)
Remarques
Objets plus élaborés
4.2.1
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . .
19
21
22
22
. . . . . . . . . . . . .
23
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
24
Chaines de caractères (string)
4.2.1.1
. . . . . . . . . . . . . . . . . . .
19
4.1.1
. . . . . . . . . . . . . . . . . . . . .
Conversion de chires en chaine de caractère et inversement
24
25
4.2.2
Nombres complexes . . . . . . . . . . . . . . . . . . . . . . . . . . .
25
4.2.3
Liste d'objets : vector . . . . . . . . . . . . . . . . . . . . . . . . . .
26
4.2.4
Vecteurs et matrices pour l'algèbre linéaire . . . . . . . . . . . . . .
27
4.2.5
tuple : ensemble d'objets divers
28
2
. . . . . . . . . . . . . . . . . . . .
3
TABLE DES MATIÈRES
5
6
Les instructions de base
30
5.1
La boucle "for"
30
5.2
Ecrire une condition
31
5.2.0.2
Les opérateurs logiques
. . . . . . . . . . . . . . . . . . .
32
5.2.0.3
Remarque . . . . . . . . . . . . . . . . . . . . . . . . . . .
32
5.4
La boucle while (..) {..} ;
5.5
L'instruction if 5.6
break : pour sortir de la boucle. continue : pour sauter l'instruction. . . . .
34
. . . . . . . . . . . . . . . . . . . . . . . . .
32
. . . . . . . . . . . . . . . . . . . . . . . . . . .
32
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
33
Les fonctions
36
6.1
Exemple de fonction qui ne renvoit rien.
6.2
Exemple de fonction qui renvoit un objet . . . . . . . . . . . . . . . . . . .
37
6.3
Paramètres des fonctions par référence
. . . . . . . . . . . . . . . . . . . .
38
6.4
La surcharge des fonctions.
. . . . . . . . . . . . . . . . . . . . . . . . . .
39
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
39
Exemple :
. . . . . . . . . . . . . . . . . . .
Les chiers
7.2
36
41
Ecriture de données dans un chier
. . . . . . . . . . . . . . . . . . . . . .
41
7.1.0.1
Remarque :
. . . . . . . . . . . . . . . . . . . . . . . . . .
41
7.1.0.2
Exercice . . . . . . . . . . . . . . . . . . . . . . . . . . . .
42
Lecture de données depuis un chier
7.2.0.1
Remarques :
. . . . . . . . . . . . . . . . . . . . .
42
. . . . . . . . . . . . . . . . . . . . . . . . .
43
Les pointeurs
44
8.1
Déclaration et aectation d'un pointeur . . . . . . . . . . . . . . . . . . . .
44
8.2
Allocation dynamique de la mémoire
8.2.0.1
9
. . . . . . . . . . . . . .
31
Les opérateurs de comparaison :
La boucle do {..} while (..) ;
7.1
8
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.2.0.1
5.3
6.4.1
7
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Remarques
. . . . . . . . . . . . . . . . . . . . .
45
. . . . . . . . . . . . . . . . . . . . . . . . . .
46
Graphisme avec la librairie ROOT
48
9.1
Commande de compilation . . . . . . . . . . . . . . . . . . . . . . . . . . .
48
9.2
Exemple de départ qui dessine un cercle
49
9.3
Utilisation des pointeurs avec ROOT
9.3.1
9.3.2
9.4
. . . . . . . . . . . . . . . . . . . . .
51
. . . . . . . . . . . . . . . . . . . . . . .
52
. . . . . . . . . . . . . . . . . . . . . . . . .
53
Utiliser une liste d'ellipses
9.3.1.1
Remarques :
. . . . . . . . . . . . . . . . . . .
Utiliser un pointeur sur l'ellipse
. . . . . . . . . . . . . . . . . . . .
53
9.3.2.1
Remarques : . . . . . . . . . . . . . . . . . . . . . . . . . .
54
9.3.2.2
Informations succintes sur les pointeurs . . . . . . . . . . .
55
Représentation graphique des vecteurs et matrices de Armadillo
. . . . . .
55
4
TABLE DES MATIÈRES
10 Création d'une classe
61
10.1 Exemple . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
61
10.1.1 Commentaires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
62
10.2 Utilisation de pointeurs sur objets . . . . . . . . . . . . . . . . . . . . . . .
64
10.3 Surdénitions d'opérateurs . . . . . . . . . . . . . . . . . . . . . . . . . . .
65
10.3.1 Exemple d'opérateur binaire . . . . . . . . . . . . . . . . . . . . . .
65
10.3.2 Commentaire
65
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
10.3.3 Exemple d'un opérateur unaire
. . . . . . . . . . . . . . . . . . . .
66
10.3.4 Commentaires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
67
10.4 Fonctions amies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
67
10.4.1 Exemple . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
67
10.4.2 Commentaire
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
68
10.4.3 Exemple de cout << . . . . . . . . . . . . . . . . . . . . . . . . . .
68
10.5 Vecteurs de taille variable
. . . . . . . . . . . . . . . . . . . . . . . . . . .
69
10.5.1 Constructeur par recopie . . . . . . . . . . . . . . . . . . . . . . . .
69
10.5.2 Opérateur d'aectation =
70
. . . . . . . . . . . . . . . . . . . . . . .
11 Micro-projets
72
11.1 Suite de Syracuse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
72
11.2 Pendule simple
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
73
11.3 La fractale du dragon . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
74
11.4 Equation de la chaleur
. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
75
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
76
11.6 Algorithme de MonteCarlo pour le modèle d'Ising . . . . . . . . . . . . . .
76
11.5 Le jeu de la vie
11.6.1 Algorithme de Monte-Carlo
. . . . . . . . . . . . . . . . . . . . . .
77
II Projet
78
III Compléments sur le langage C++
80
12 Librairie Root : graphisme et autre.. (compléments)
82
12.1 Graphisme de base 2D
. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
82
12.1.1 Objets graphiques . . . . . . . . . . . . . . . . . . . . . . . . . . . .
82
12.1.2 Plusieurs fenêtres . . . . . . . . . . . . . . . . . . . . . . . . . . . .
83
12.1.3 Un cadre avec des axes gradués : DrawFrame . . . . . . . . . . . . .
84
12.1.4 Un seul axe gradué : TGaxis . . . . . . . . . . . . . . . . . . . . . .
85
12.2 Histogrammes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
86
12.2.1 Courbe (x,y) avec axes à partir de tableaux de valeurs x[] y[] : TGraph 86
12.2.2 Histograme 1Dim à partir de tableau y[] : TH1F . . . . . . . . . . .
86
12.2.3 Courbe a partir de l'expression d'une formule y=f(x) : TF1 . . . . .
87
12.2.4 Surface 2Dim : z=f(x,y) à partir d'un tableau z[i,j] : TH2F . . . . .
88
5
TABLE DES MATIÈRES
12.2.5 Surface 2D a partir de l'expression d'une formule z=f(x,y) : TF2 . .
88
12.2.6 Crée histogramme 1D par tirage aléatoire et ajustement par une
formule
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
12.2.7 Champ de vecteur
12.3 Utilisation de la souris
12.3.0.1
89
. . . . . . . . . . . . . . . . . . . . . . . . . . .
90
. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
92
Pour gérer la souris lorsque le programme calcule
. . . . .
94
12.4 Comment créer un chier gif animé. . . . . . . . . . . . . . . . . . . . . . .
94
12.5 Boutons de commandes (Widgets) . . . . . . . . . . . . . . . . . . . . . . .
95
12.6 Autres trucs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
103
12.6.0.1
Nombre aléatoire avec Root . . . . . . . . . . . . . . . . .
103
12.6.0.2
Options générales de Root . . . . . . . . . . . . . . . . . .
103
13 Mesure du temps
105
13.1 Mesure de la date et du temps écoulé . . . . . . . . . . . . . . . . . . . . .
13.1.0.1
Mesure très précise du temps :
13.1.0.2
Achage de la date
105
. . . . . . . . . . . . . . .
105
. . . . . . . . . . . . . . . . . . . . .
106
13.2 Attendre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
13.2.1 Attendre une certaine durée
13.2.2 Attendre jusqu'à une certaine date
106
. . . . . . . . . . . . . . . . . .
14 Ecrire et lire des données en binaires sur un chier
14.1 Fichiers en format texte
106
. . . . . . . . . . . . . . . . . . . . . .
106
108
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
14.2 Fichiers en format binaire
14.2.0.1
Exemple
14.2.0.2
Remarques
108
. . . . . . . . . . . . . . . . . . . . . . . . . . .
109
. . . . . . . . . . . . . . . . . . . . . . . . . . .
109
. . . . . . . . . . . . . . . . . . . . . . . . . .
110
15 Quelques commandes utiles
111
15.0.1 La commande system . . . . . . . . . . . . . . . . . . . . . . . . . .
111
16 Intégrer des équations diérentielles ordinaires (EDO) avec la librairie
ODEINT
112
17 Lecture et écriture d'un chier son de format WAV
117
17.0.0.1
Introduction
. . . . . . . . . . . . . . . . . . . . . . . . .
117
17.0.0.2
Travail préalable pour le projet C++ . . . . . . . . . . . .
117
17.0.1 Programme pour écrire un chier WAV :
17.0.2 Programme pour lire un chier WAV :
. . . . . . . . . . . . . . .
117
. . . . . . . . . . . . . . . .
120
18 Gestion de la carte son (audio) avec Jack
122
19 Gestion de la carte son (audio) avec la librairie sndio
123
19.1 Installation (à faire la première fois) . . . . . . . . . . . . . . . . . . . . . .
19.1.0.1
123
Travail préalable pour le projet C++ . . . . . . . . . . . .
124
19.2 Utilisation du micro (entrée) et du haut-parleur (sortie) . . . . . . . . . . .
124
6
TABLE DES MATIÈRES
19.2.1 Le micro . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
19.2.1.1
Exemple : détection de note de musique en temps réel
124
. .
126
19.2.2 Le haut-parleur . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
129
20 Plusieurs programmes en parallèle qui communiquent, avec thread
20.1 Lancement de threads ou processus
130
. . . . . . . . . . . . . . . . . . . . . .
131
20.1.0.1
Commande de compilation : . . . . . . . . . . . . . . . . .
131
20.1.0.2
Commentaires
132
20.1.0.3
Lancer un thread d'une fonction membre de classe
20.1.0.4
Lancer une liste de thread (tableau)
20.1.0.5
Lancer une liste de thread (vector)
. . . . . . . . . . . . . . . . . . . . . . . .
. . . .
132
. . . . . . . . . . . .
133
. . . . . . . . . . . . .
133
. . . . . . . . . . . . . . . . . . . . . .
134
. . . . . . . . . . . . . . . . . . . . . . . . .
134
20.2 Mutex pour annoncer occupé/libre
20.2.0.1
Introduction
20.2.0.2
Remarques
. . . . . . . . . . . . . . . . . . . . . . . . . .
134
20.2.0.3
Exemple . . . . . . . . . . . . . . . . . . . . . . . . . . . .
135
20.2.0.4
Résultat : . . . . . . . . . . . . . . . . . . . . . . . . . . .
135
20.3 Communications : variables conditionnelles pour réveiller un programme en
attente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
20.3.0.1
Exemple de base où un thread endormi est reveillé par un
autre.
20.3.0.2
136
. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Remarques
. . . . . . . . . . . . . . . . . . . . . . . . . .
21 Messages MIDI (Musical) avec la librairie sndio
136
137
143
21.1 Installation (à faire la première fois) . . . . . . . . . . . . . . . . . . . . . .
143
21.2 Exemple élémentaire sur l'envoi et reception de messages MIDI . . . . . . .
143
21.3 Exemple d'envoie de message MIDI à un synthétiseur . . . . . . . . . . . .
147
21.4 Reception de notes midi depuis un piano électrique
151
. . . . . . . . . . . . .
22 Convertir un programme C++ en Javascript avec emscripten
154
23 Autres librairies conseillées (?)
155
23.1 algèbre linéaire
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
155
23.2 Images, vidéos, graphisme et boites de dialogues . . . . . . . . . . . . . . .
155
24 Le traitement d'images et de vidéos avec la librairie OpenCV
156
24.1 Commande de compilation . . . . . . . . . . . . . . . . . . . . . . . . . . .
156
24.2 Lecture et ecriture de chiers images
156
24.2.0.1
. . . . . . . . . . . . . . . . . . . . .
Fonctions utiles sur les images
. . . . . . . . . . . . . . .
157
. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
158
24.4 Lecture et écriture d'un chier video ou de la webcam . . . . . . . . . . . .
161
24.5 Détection d'objet par leur couleur . . . . . . . . . . . . . . . . . . . . . . .
162
24.6 Détection et suivit d'objet de couleur avec l'algorithme CamShift
164
24.3 Utilisation de la souris
. . . . .
7
TABLE DES MATIÈRES
25 Gestion de projets avec chiers .h, Makele, Git et GitHub
25.1 Projet avec plusieurs chiers, chier .h
. . . . . . . . . . . . . . . . . . . .
172
172
25.1.0.1
Résultat du programme :
. . . . . . . . . . . . . . . . . .
172
25.1.0.2
Code c++ . . . . . . . . . . . . . . . . . . . . . . . . . . .
172
25.1.0.3
Commandes de compilation
. . . . . . . . . . . . . . . . .
174
25.2 Un programme Makele pour gérer la compilation . . . . . . . . . . . . . .
175
25.2.1 Un programme Makele plus pratique . . . . . . . . . . . . . . . . .
176
25.3 Gestion de versions d'un projet avec Git
25.3.0.1
. . . . . . . . . . . . . . . . . . .
Utilisation de Git sur son ordinateur
176
. . . . . . . . . . . .
177
25.4 Collaboration et partage de projets sur internet avec GitHub . . . . . . . .
177
Chapitre 1
Introduction
Ce didacticiel vous permettra d'appréhender le langage de programmation C++
(version C++11) et ses avantages. Dans ce didacticiel, les exercices marqués par (*)
seront vériés par l'enseignant pendant les séances. Il y a de nombreux liens html
qui renvoit vers plus d'informations.
Le grand avantage du langage C++ par rapport au langage C est
la programma-
tion objet. On peut utiliser des objets appartenant à des classes déjà existantes
ou à des classes que l'on a fabriqué soi-même. Par exemple, il existe la classe des
nombres complexes. Les objets appartenant à cette classe sont des nombres complexes. Il nous sura de déclarer trois objets
a, b
et
c
comme appartenant à la
classe des nombres complexes, et l'on pourra directement écrire
a=b+c ou n'importe
quelle autre expression mathématique. Comme autre exemple, il y a la classe de matrice complexe : on peut déclarer trois objets
et écrire
A, B
et
C
appartenant à cette classe
A=B+C ou n'importe quelle autre expression mathématique matricielle. Vers
la n de ce cours, nous aborderons l'aspect fabrication de classe.
Pourquoi enseigner le C++ à l'université, dans la lière physique ? d'une
part c'est un langage qui est très bien adapté pour la simulation ou l'analyse de
problèmes physiques et de façon plus générale pour le travail scientique. Dans les
laboratoires il a remplacé peu à peu le langage fortran. Le langage python est un
autre langage qui utilise aussi des classes et est aussi très utilisé dans le domaine
scientique. Un programme en python est peut être plus simple à mettre en oeuvre
qu'un programme en C++,
c'est une histoire de goût et d'habitude, mais
il est aussi beaucoup plus lent à l'exécution. Un avantage du langage python est
qu'il est interprété : on peut exécuter un programme ligne après ligne alors que
le langage C++ est compilé : un programme appelé compilateur le transforme
en langage machine (chier executable) avant qu'il ne soit exécuté et cela le rend
rapide. La dernière version C++11 fait du C++ un langage aussi conviviale et
simple d'utilisation que le langage python (et plus rapide !).
Nouvelles version du C++:les récentes versions C++11 puis C++14 ont pour vocation de rendre le C++ plus facile à apprendre et toujours performant.
Références :
8
CHAPITRE 1.
9
INTRODUCTION
Sur le web :
voici un site très utile : http://www.cplusplus.com/. Il contient un tutorial de
très bonne qualité, où les exemples peuvent être essayé en ligne (avec la petite
icône edit & run à droite de example, essayez par exemple sur setprecision) !.
Il contient aussi une documentation complète au langage C++.
Un autre site en français :cppreference.com ou en anglais : cppreference.com.
Une façon pratique de se documenter sur une fonction précise est d'utiliser le moteur de recherche de google. Par exemple pour acher un nombre
avec une certaine précision numérique, si vous ne savez pas comment faire,
tapez quelques mots clef comme achage precision c++ dans la fenêtre
de recherche de google, qui vous amènera sur une page de forum avec un
exemple. Vous y verrez que la fonction à utiliser est
setprecision.
Ensuite
pour avoir une bonne documentation sur cette fonction et des exemples que
l'on peut copier/coller, tapez toujours dans google cplusplus setprecision.
Signalons le cours en ligne en francais sur le C++ de Coursera sur : ce sont
des vidéos courtes, ou Cours de C++ de OpenClassRoom en français.
La page web de Bjarne Stroustrup, auteur du langage C++, et en particulier
la FAQ sur le C++11
Livre pour le langage C++ :
Programmer en C++ de Claude Delannoy, éditions Eyrolles
The C++ programming langage de Bjarne Stroustroup, 4eme édition.
Livre pour le langage C : Le langage C de B.W. Kernighan et D.M. Ritchie
edition Masson.
1.1
Choix du système d'exploitation
Ce didacticiel est indépendant du système d'exploitation. Cependant en séance on travaille avec
Linux et plus particulièrement avec la distribution Xubuntu ou Ubuntu. On
conseille cette distribution gratuite qui peut être installée sur votre ordinateur personnel
en suivant ce lien :
installer Ubuntu.
Il y a cependant possibilité d'utiliser d'autres systèmes comme windows, mac-os, etc...
Dans ce didacticiel, nous utiliserons les logiciels et librairies suivantes qui sont en général
téléchargeables depuis votre distribution :
Codeblocks.
Root.
Armadillo.
1.2
Environnement de programmation
Avec un petit exemple nous allons voir comment :
1.
écrire le code d'un programme en C++
CHAPITRE 1.
2. le
10
INTRODUCTION
compiler, c'est à dire que l'ordinateur traduit le programme C++ en un pro-
gramme executable (en langage machine) qu'il pourra exécuter ensuite.
3.
éxécuter le programme
4. éventuellement de le
débugger (cela signie recherche d'erreurs) : si le programme
ne fonctionne pas comme on le désire, on l'éxécute instruction après instruction an
de suivre ce qu'il fait pas à pas, dans le but de trouver un bug (= une erreur).
Dans la Section suivante nous eectuons ces taches avec l'éditeur emacs et une fenêtre de
commandes (terminal).
1.2.1 (conseillé) Programmer avec l'éditeur emacs et une fenêtre
de commandes
Lancer le
1. Ouvrir une fenêtre de
commandes ( ou terminal). Se placer dans le bon répertoire
pwd (qui dit dans quel dossier on est situé), ls (qui
les chiers et dossiers) , cd dossier ( : qui passe au dossier indiqué), cd ..
passe au dossier parent), cd . (qui reste dans le même dossier, c'est inutile),
de travail avec les commandes
liste
(qui
cd
(qui revient au dossier de départ),
2. Ecrire la commande
emacs &
qui lance l'éditeur. Le signe & signie que le ter-
minal reste utilisable. Dans emacs, une fois pour toutes, cocher dans le menu
Options/UseCUAKeys
et
Options/SaveOptions
qui vous permettra d'utiliser les
raccourcis clavier C-c C-v pour copier/coller.
3. Dans emacs, ouvrir un nouveau chier
projet1.cc
qui sera vide la première fois.
Le suxe .cc signie qu'il contiendra du C++ (ou peut aussi choisir le suxe .cpp).
Copier /coller le code suivant :
#include <iostream>
using namespace std;
int main()
{
cout << "Bonjour!" << endl;
return 0;
}
et sauvegarder ce chier qui contient le
code C++ de votre pro-
gramme. (Observer les raccourcis clavier proposés).
4. Dans emacs ouvir un nouveau chier
Makefile
qui sera vide la première fois. Co-
pier/coller le code de compilation suivant :
all:
g++ projet1.cc -o projet1 -std=c++11 -lm
et sauvegarder ce chier qui contient les instructions pour compiler votre
programme.
Attention : au début de la deuxieme ligne rajouter un espace
CHAPITRE 1.
11
INTRODUCTION
de tabulation avant g++. Remarque : on peut basculer d'un chier à l'autre
dans emacs par le menu Buers ou par le clavier avec
5.
C-x C-→.
Compiler le programme par la commande du menu : Tools/Compile.. qui fait
apparaitre en bas de page :
make -k (puis faire entrée).Remarque : cela a pour eet
d'éxécuter la commande de compilation qui se trouve dans le chier Makele : elle
dit qu'il faut utiliser le compilateur g++ et qu'à partir du chier
créer un chier éxecutable appelé
projet1.
projet1.cc il faut
Si tout se passe bien le dernier message est compilation nished.
6. Dans le terminal, placez vous dans le répertoire qui contient votre projet, vériez
qu'il contient le chier executable
./projet1
projet1
, et
éxécutez ce chier en écrivant :
Remarque 1.2.1.
1. Dans le programme
projet1.cc
ajoutez une erreur, par exemple en enlevant un
signe ; en n de ligne. Recompiler le programme et observer que le compilateur
détecte l'erreur et en cliquant sur le message d'erreur, emacs vous place à la ligne
où est l'erreur à corriger.
2. Dans les instructions ci-dessus nous vous avons proposé d'utiliser un chier Makele
qui sera surtout utile pour les projets de programation en C++. Vous pouvez plus
directement compiler votre programme en écrivant dans un terminal la commande
de compilation :
g++ projet1.cc -o projet1 -std=c++11 -lm
le désavantage est que pour corriger l'erreur il faut soi même se placer sur la
ligne indiquée.
Ou encore ecrire la commande
d'éxecuter le chier
3.
Makefile.
make
dans le terminal qui a pour eet
emacs peut mettre votre programme en forme
: pour cela sélectionnez tout le texte
et appuyez sur la touche tabulation. Il apparait des décalages de lignes convenables,
mais non obligatoires.
4. Pour écrire le programme, à la place de emacs vous pouvez utiliser tout autre éditeur
de texte.
Remarque 1.2.2. Voici une documentation sur les commandes possibles bash pour la fenêtre
terminal.
1.2.2 (optionnel) Programmer avec l'environnement codeblocks
Au lieu d'utiliser emacs comme ci-dessus vous pouvez utiliser un environnement de programmation plus convivial ou évolué comme codeblocks. Le désavantage est qu'il necessite
un certain apprentissage, qu'il eectue automatique certaines taches à votre place et vous
perdez un peu le controle de votre programmation.
Remarque 1.2.3. Il existe aussi d'autres environnements de développement sophistiqués
comme eclipse ou emacs.
CHAPITRE 1.
12
INTRODUCTION
Exercice 1.2.4.
1. Lancer le programme codeblocks.
2. Choisir "create a new project
3. Faire les choix : projet de type "Console application, C++ ; lui donner un nom,
comme "projet1 et choisir le répertoire où il sera hébergé.
Dans le répertoire Sources, il apparait un chier appelé
main.cpp contenant le
code C++ suivant :
#include <iostream>
using namespace std;
int main()
{
}
cout << "Hello world!" << endl;
return 0;
4. Une fois pour toute on va choisir la version 11 du C++. Pour cela, dans le menu
Settings/Compiler/Compiler Flags, cocher l'option de compilation
-std=c++11
5. La commande du menu Plugins/SourceCodeFormatter vous permet de mettre votre
programme en forme standard. Faites le souvent.
6. Pour
compiler et exécuter ce programme, taper F9 (ou build and run dans le
menu). Il apparait alors une fenêtre (console) avec le message "Hello
world !.
Dans la suite, vous pouvez modier le code de ce programme pour l'adapter aux
exemples donnés ci-dessous. Vous pouvez aussi créer d'autres projets de la même
manière.
Pour
copier un exemple, vous pouvez "copier le code donné en exemple dans ce
tutoriel et le "coller dans votre éditeur, sans le retaper.
Première partie
Bases du langage C++
13
Chapitre 2
Le tout début
Un petit programme pour commencer :
Voici un petit programme C++ qui additionne deux nombres entiers a et b et aecte
le résultat dans c. La classe
int
permet justement de stocker des nombres entiers. (Cela
vient de integer en anglais).
/*!
-----------------------ceci est
votre premier programme
-------------------------*/
#include <iostream>
using namespace std;
int main() //! entête du programme principal
{
// début
cout << "debut"<<endl;
int a,b; // déclaration des objets a et b de la classe
int.
a=1; // affectation: a prend la valeur 1
b=a+1; // affectation b prend la valeur 2
int c; // déclaration de l'objet c de la classe int.
c=a+b; // affectation: c prend la valeur a+b c'est à dire
3
cout << "fin"<<endl;
// fin
}
Exercice 2.0.1. Recopiez ce programme dans un chier prog.cc , modier la commande
de compilation de Makele pour le compiler et exécutez le.
14
CHAPITRE 2.
15
LE TOUT DÉBUT
Résultat du programme :
debut
fin
Remarque 2.0.2.
1. Le
point virgule sépare les instructions.
2. Les
commentaires doivent débuter par //. Le reste de la ligne est alors considéré
comme un commentaire. Un commentaire n'a aucune inuence sur le programme, il
sert en général à expliquer ce que fait le programme pour le rendre compréhensible
au lecteur. Une autre possibilité est de commencer le commentaire par /* et de nir
quelques lignes après par */.
3. Le début et la n d'un bloc d'instructions sont respectivement { et }
4. "main " veut dire "principal " en anglais. C'est le début du
programme principal
ou de la fonction principale. Les parenthèses () signient que le programme principal
n'utilise aucun paramètre dans cet exemple. Lorsque vous lancez votre programme,
son exécution commence toujours par cette fonction principale.
Chapitre 3
Lire le clavier et acher à l'écran
3.1
Achage dans une console
Dans le programme précédent, nous avons aecté la somme de deux entiers a et b
dans un autre entier c. Mais nous n'avons pas aché le résultat à l'écran. Pour cela on
cout. Cet objet représente l'écran. C'est un objet de la classe ostream (classe
standard du C++) qui est dénie dans le chier : iostream. Il ne faut donc pas oublier
d'inclure ce chier avec la commande #include en début du programme pour que, lors de
la compilation, l'ordinateur puisse savoir ce que veut dire cout.
utilise l'objet
#include <iostream>
using namespace std;
int main()
{
int a,b; // déclaration des objets a et b de la classe
int
a=1; // affectation: a prend la valeur 1
b=a+1; // affectation b prend la valeur 2
int c; // déclaration de l objet c de classe int
c=a+b; // affectation: c prend la valeur a+b c'est à dire
3
// affichage à l'écran:
cout<<"la somme de "<< a <<" et "<< b <<" vaut "<< c <<endl;
}
Résultat du programme :
la somme de 1 et 2 vaut 3
Exercice 3.1.1. Recopiez ce programme et exécutez le.
Remarque 3.1.2.
16
CHAPITRE 3.
17
LIRE LE CLAVIER ET AFFICHER À L'ÉCRAN
1. Les caractères entre guillements " " sont considérés comme une chaîne de caractères
et écrits tels quels à l'écran . Par contre les caractères
a, b,c
ne sont pas entre ".
Cela signie que ce sont des noms d'objets, et qu'il faut acher le contenu de ces
objets (et non pas leur nom).
2. Le terme
cout
symbolise l'écran. Les signes << sont évocateurs : on envoit ce qui
suit vers l'écran.
3. Ce symbole
endl
cout
signie que l'on va à la ligne (end of line = n de ligne).
appartient à la bibliothèque
iostream
qui est chargée grâce
#include <iostream> . En principe pour l'achage il faustd::cout car cout appartient à l'espace de nom appelé std comme
standard ; mais la deuxième ligne using namespace std; signie que l'on pourra
écrire plus simplement cout. (Il peut être utile de ne pas faire cette simplication
si le symbôle cout est utilisé par une autre bibliothèque dans un autre espace de
nom comme std2 . On pourra alors les distinguer : std::cout et std2::cout).
à la première ligne
drait écrire
4.
iostream est un chier déjà présent sur l'ordinateur. Ce chier contient des informations sur des commandes C++ d'achage à l'écran et de saisie de touches appuyées
au clavier. Iostream vient de l'anglais : Input (=entrée) , Output (=sortie) , Stream
(=ux d'information).
5.
cout est un objet C++ de la classe ostream. Cet objet est associé à l'écran comme
expliqué ci-dessus. Le signe << est un
opérateur de cette classe à qui on a donné
le sens précis d'acher à l'écran. Tout cela est contenu dans le chier iostream
qui est lui même un programme C++. L'existence de cette classe simplie donc
la programmation pour les utilisateurs. C'est l'esprit du C++. Vous apprendrez à
écrire vous même des classes et des opérateurs par la suite.
Exercice 3.1.3. (*)
"\t" permet d'acher une
tabulation (i.e. sauter un espace jusqu'à la colonne sui-
vante). Modier le programme précédent pour acher :
objets:
valeurs:
3.2
a
1
b
2
c=a+b
3
Lire le clavier
Il peut être intéressant que l'utilisateur puisse lui-même entrer les valeurs de
a
et
b.
Pour cela l'ordinateur doit attendre que l'utilisateur entre les données au clavier et les
valide par la touche entrée.
#include <iostream>
using namespace std;
int main()
{
CHAPITRE 3.
LIRE LE CLAVIER ET AFFICHER À L'ÉCRAN
18
int a,b; // déclaration des objets a et b de la classe
int
cout <<"Quelle est la valeur de a ? "<<flush;
cin >> a; // lire a au clavier, attendre return
cout <<"Quelle est la valeur de b? "<<flush;
cin >> b; // entrer b au clavier puis return
int c; // déclaration de la objet c de la classe int
c = a + b; // affectation: c prend la valeur a+b
// affichage à l'écran:
cout <<"la somme de "<<a<<" et "<<b<<" vaut "<<c<<endl ;
}
Exercice 3.2.1. Recopier ce programme et exécuter le.
Remarque 3.2.2.
1. L'objet
cin appartient à la classe istream et représente le clavier. Le signe >>
est
un opérateur associé à cet objet et qui a pour eet de transférer les données tapées
au clavier dans l'objet qui suit (une fois la touche entrée enfoncée).
flush à la n de la phrase d'achage à pour eet d'acher la phrase
sans faire de retour à la ligne. Si on ne met rien (ni flush, ni endl) la
2. L'instruction
à l'écran
phrase n'apparait pas forcément tout de suite à l'écran.
Chapitre 4
Déclaration et aectation des objets
4.1
Objets de base
4.1.1 Les déclarations d'objets de base
Pour stocker une information dans un programme, on utilise un objet qui est symbolisé
par une lettre ou un mot. (Comme
a,b,c précédement). On choisit la classe de cet objet,
selon la nature de l'information que l'on veut stocker (nombre entier, nombre à virgule,
nombre complexe, ou série de lettres, ou matrice, ...).
Voici les diérentes classes de base qui existent en C++ (il y en a d'autres, voir variables) :
classe objet;
int a;
double d;
char e;
bool v;
char * t;
Déclaration :
signication
Limites
nombre entier
-2147483648 à 2147483647
±10±308 à 10−9 près
nombre réel double précision
caractère
booléen : vrai (true) ou faux (false)
chaîne de caractères
Remarque 4.1.1. Les classes ci-dessus sont les classes de base standard du langage C++
qui existent aussi en langage C. Dans le vocabulaire du C, on dirait plutot
de
classe, et variable à la place de objet. Par exemple en écrivant
que
d
est une variable du type
double.
type à la place
double d;
on dirait
4.1.2 Initialisation d'un objet de base
On peut déclarer un objet et l'initialiser en même temps de diérentes manières :
#include <iostream>
19
CHAPITRE 4.
20
DÉCLARATION ET AFFECTATION DES OBJETS
using namespace std;
int main ()
{
int i = 0;
double x = 1.23;
double x1(3.4),x2(6); // equivalent a x1=3.4
double y(x1), y2 = x2;
char mon_caractere_prefere = 'X';
const char * texte = "que dire de plus?";
char c = texte[0]; // premier caractere
auto z = x + 2;
{
double x=5;
}
}
4.1.2.1
Remarques
1. i est un
int qui vaut 0, x un oat qui vaut 1.23, x1 est un double qui vaut 3.4, etc..
auto
double.
2. Le terme
est un
signie que le compilateur devine lui même la classe de l'objet, ici
z
3. Dans le nom des objets, le langage C++ fait la diérence entre les majuscules et
les minuscules. On peut choisir ce que l'on veut et utiliser même des chires et le
caractère _.
4. On peut initialiser un objet avec des paranthèses comme
x1(3.4).
C'est équivalent
x1=3.4. On peut initialiser un objet avec un objet déjà existant comme
y2 = x2.
que d'écrire
y(x1)
ou
5. Un caractère est entre le signe ', comme
'X'.
6. Un texte (chaîne de caractère) est entre le signe ". Ici l'objet
const char *
texte
est du type
qui signie que c'est une chaine de caractères (ou tableau de carac-
tères) constante. Les indices du tableau commencent à
0.
C'est pourquoi
texte[0]
extrait le premier caractère qui est q.
7. A la dernière ligne on déclare
x = 5 dans un
bloc d'accolades ou bloc d'instruc-
tions {..}. Cela est possible et signie que dans ce bloc x est une nouvelle variable
locale (qui masque la valeur de x précédente).
8. On pourra bien sûr modier ces valeurs dans la suite du programme.
Exercice 4.1.2. (*) Recopiez le programme, modiez le pour qu'il ache la valeur de
chaque objet, et exécuter le. En particulier acher la valeur de x dans le bloc d'accolades
et après le bloc d'accolades. Essayer de ne pas mettre d'accolades. (si auto signale une
erreur, vériez si l'option c++11 est cochée, voir remarque de l'exercice 1.2.4(4).)
CHAPITRE 4.
DÉCLARATION ET AFFECTATION DES OBJETS
21
4.1.3 Achage des nombres avec une précision donnée
(il faut inclure
<iomanip>
et
<math.h>)
#include <iostream>
using namespace std;
#include <math.h>
#include <iomanip>
//..................
int main ()
{
double c = M_PI;
cout<<setprecision(3)<<c<<endl;
}
Résultat :
3.14
Référence : voir setprecision.
4.1.4 Attention à la division Euclidienne
Voici un exemple :
#include <iostream>
using namespace std;
//......
int main ()
{
int a=7, b=2;
double c=7.;
cout<<"7/2 = "<<a/b<<" = "<<7/2<<endl;
cout<<"7./2 = "<<c/b<<" = "<<7./2<<" = "<<(double)a/b<<endl;
cout<<"Le quotient de 7/2 = "<<7/2<<" Le reste de 7/2 est r= "<<7%2<<endl;
}
Résultat :
7/2 = 3 = 3
7./2 = 3.5 = 3.5 = 3.5
Le quotient de 7/2 = 3 Le reste de 7/2 est r= 1
CHAPITRE 4.
22
DÉCLARATION ET AFFECTATION DES OBJETS
Remarques :
L'exemple précédent montre que l'écriture
euclidenne qui est bien 3 car
7/2
signie le quotient de la division
7=2*3+1. Le reste 1 euclidien est obtenu par l'opération
7%2.
Pour eectuer la division parmi les nombres réels (à virgule) il faut utiliser le type
double.
L'instruction
une variable
(double)a permet de convertir la variable a qui est de classe int
de classe double. Ce changement de classe est appelé cast.
en
Attention, cette convention que 3/2 est la division euclidienne en langage C et C++
est souvent source d'erreur.
4.1.5 Pour générer un nombre entier p aléatoire
#include <iostream>
using namespace std;
#include <time.h>
#include <stdlib.h>
int main ()
{
srand(time(NULL)); // initialise le hasard.(1 seule fois)
int N = 100;
int p=rand()%(N+1); // entier aléatoire entre 0 et N compris.
cout<<" p="<<p<<endl;
}
Puis au moment de choisir un nombre au hasard :dans le programme
p=rand()%(N+1);
Remarque 4.1.3.
signie modulo,
rand()
a%b est
génère un nombre entier aléatoire entre 0 et 32768. Ensuite
le reste de la division de
reste de la division du nombre aléatoire par
N+1.
a
par
b.
Ainsi
rand()%(N+1)
%
est le
C'est donc un entier compris entre 0 et
N (inclus). En c++11 il y a une classe random plus élaborée pour générer des nombres
aléatoires selon diverses lois.
4.1.6 Quelques opérations élémentaires
#include <iostream>
using namespace std;
int main ()
CHAPITRE 4.
DÉCLARATION ET AFFECTATION DES OBJETS
{
int b=1;
cout<<" b="<<b<<endl;
b++; // equivalent a b=b+1
cout<<" b="<<b<<endl;
b-- ; // equivalent a b=b-1
cout<<" b="<<b<<endl;
}
4.1.7 Quelques opérations sur les bits (base deux)
#include <iostream>
using namespace std;
#include <bitset>
int main ()
{
//... Declaration et Affichage en base deux:
int A=0b1001; // declaration en base 2 (prefixe 0b)
cout<<" A="<<A<<" en binaire = "<<bitset<8>(A)<<endl;
//.. decalages de bits
int b= A<<4; // equivalent a b= A * (2^4)
cout<<" b ="<<b<<" = "<< bitset<8>(b)<<endl;
int c = b>>3; // equivalent a c= b / (2^3)
cout<<" c ="<<c<<" = "<< bitset<8>(c)<<endl;
//..
int
int
int
int
operations
B=0b1;
d = A | B;
e = A ^ B;
f = A & B;
cout<<"
cout<<"
cout<<"
cout<<"
}
bits a bits
// ou inclusif bit a bit cad 1|1 donne 1
// ou exclusif bit a bit cad 1^1 donne 0
// et bit a bit
B="<<B<<" = "<<bitset<4>(B)<<endl;
A|B = "<<d<<" = "<<bitset<4>(d)<<endl;
A^B = "<<e<<" = "<<bitset<4>(e)<<endl;
A&B = "<<f<<" = "<<bitset<4>(f)<<endl;
Resultat :
A=9 en binaire = 00001001
b =144 = 10010000
23
CHAPITRE 4.
c =18
B=1 =
A|B =
A^B =
A&B =
DÉCLARATION ET AFFECTATION DES OBJETS
24
= 00010010
0001
9 = 1001
8 = 1000
1 = 0001
Remarque 4.1.4. De même on peut utiliser la base 16 (hexadécimale) avec le préxe 0x.
Par exemple :
a=0xFA;
cout<<a<<endl;
cout<<hex<<a<<endl;
4.2
// pour acher en base 16.
Objets plus élaborés
Nous présentons ici quelques classes plus élaborées et très utiles. Ce ne sont plus des
"classes de base. Pour une liste complète des classes d'objets possibles en C++, voir par
exemple la référence.
4.2.1 Chaines de caractères (string)
Référence : string. Voici un exemple de programme que vous pouvez essayer.
#include <iostream>
#include <string>
using namespace std;
int main ()
{
string texte1 = "J'aime le café";
cout << texte1<<endl;
string texte2 = texte1 + " sans sucre";
cout << texte2<<endl;
cout<<"Le premier caractère est du texte précédent est:
"<<texte2[0]<<endl;
}
Résultat :
J'aime le café
J'aime le café sans sucre
Le premier caractère du texte précédent est:J
Remarque 4.2.1. Voici plus d'informations et d'exemples sur la classe string.
Exercice 4.2.2. (*) Modier le programme précédent pour qu'il ache :
J'aime le café sans sucre
Le troisième caractère du texte précédent est:a
CHAPITRE 4.
4.2.1.1
25
DÉCLARATION ET AFFECTATION DES OBJETS
Conversion de chires en chaine de caractère et inversement
(On peut sauter cette section en premiere lecture et revenir si besoin.)
On souhaite par exemple construire une chaine de caractère s qui contient le resultat
d'une opération numerique. Pour cela il faut utiliser la classe
ostringstream
qui fonc-
tionne comme la classe. Inversement pour extraire des chires d'une chaine de caractère il
faut utiliser la classe
istringstream .
#include <iostream>
using namespace std;
#include <string>
#include <sstream>
//.....
int main ()
{
//..... Conversion chiffre -> string
int c=7; // chiffre
ostringstream os;
os<<"resultat = "<<c; // on concatene du texte et le chiffre
string s = os.str(); // convertit os en string
cout<<s<<endl<<endl; // affichage pour verifier
//..... Conversion string -> chiffres
string s2 = "3 4"; // chaine contenant des chiffres
cout<<s2<<endl;
istringstream is;
is.str(s2); // convertit chaine s2 en is
int a,b;
is>>a; // extrait le premier chiffre trouve dans is
is>>b; // extrait chiffre de is
cout<<a<<" "<<b<<endl; // affichage pour verifier
}
Résultat :
resultat = 7
3 4
3 4
4.2.2 Nombres complexes
Référence : complex.
CHAPITRE 4.
26
DÉCLARATION ET AFFECTATION DES OBJETS
#include <iostream>
using namespace std;
#include <complex>
typedef complex<double> Complex;
int main ()
{
Complex I(0,1); // on définit le nombre I
cout<<"I.I="<<I*I<<endl;
Complex a=2.+I*4., b=3.-2.*I;
Complex c=a+b, d=a*b;
cout<<" a="<<a<<" b="<<b<<endl;
cout<<" a+b="<<c<<" a*b="<<d<<endl;
cout<<" real(a)="<<a.real()<<" norme(a)="<<abs(a)<<endl;
}
Résultat :
I.I=(-1,0)
a=(2,4) b=(3,-2)
a+b=(5,2) a*b=(14,8)
real(a)=2 norme(a)=4.47214
Remarque 4.2.3.
L'écriture
2.
est équivalente à
2.0
ou à
2.
Ainsi
a=2.+I*4.
est équivalente à
a=2+4*I
typedef complex<double> Complex; signie que l'écriture Complex est un raccourci pour le type complex<double> qui est un nombre complexe avec double en
partie réelle et imaginaire.
4.2.3 Liste d'objets : vector
La classe
string ci-dessus est une liste de caractères. Plus généralement la classe vector
permet de manipuler des listes d'objets quelconques (des nombres ou autres). Par exemple
pour une liste de nombres entiers avec
#include <iostream>
using namespace std;
#include <vector>
#include <algorithm>
vector<int>
:
CHAPITRE 4.
DÉCLARATION ET AFFECTATION DES OBJETS
27
int main ()
{
vector<int> L={5,7,3,12,34,6}; // on initialise une liste
d'entiers
cout<<"Le premier élément de la liste est "<<L[0]<<endl;
auto i = max_element(L.begin(),L.end()); //renvoit la position
du maximum
int p=i-L.begin(); // position depuis le debut de la liste
(0,1,2..)
cout<<"L'élement le plus grand est "<<L[p]<<" ou "<<*i<<endl;
cout<<"Il est à la position "<<p<<endl;
}
Résultat :
Le premier élément de la liste est 5
L'élement le plus grand est 34 ou 34
Il est à la position 4
i qui représente une position sur la liste
utilise ici le terme auto qui détecte cette
Remarque 4.2.4. Dans l'exemple ci-dessus, l'objet
est de la classe
vector<int>::iterator.
On
classe et est plus simple à écrire. Voici plus d'informations et d'exemples sur la classe
vector. Pour plus d'informations et d'exemples sur les manipulations de listes (appelées
containers)
voir algorithms.
4.2.4 Vecteurs et matrices pour l'algèbre linéaire
Pour cela on utilise la librairie armadillo dont voici la documentation. Cette librairie
ne fait pas partie de la "librairie standard STL, il faut donc l'installer au préalable sur
l'ordinateur.
Il faut aussi
rajouter
-larmadillo dans les options de compilation. (Si vous utili-lm, Si vous utilisez codeblocks, c'est à rajouter dans
sez un Makele c'est à la suite de
Menu/Settings/Compiler/LinkerSettings/OtherLinkerOptions).
#include <iostream>
#include <armadillo>
using namespace std;
using namespace arma;
int main ()
{
Col<int> V("1 2 3"); // vecteur colonne d'entiers
cout<<"V="<<endl<<V<<endl;
CHAPITRE 4.
DÉCLARATION ET AFFECTATION DES OBJETS
28
Mat<int> M("3 2 1; 4 5 0");// matrice d'entiers
cout<<"M="<<endl<<M<<endl;
Col<int> W = M * V; // calcule le produit
cout<<"M*V="<<endl<<W<<endl;
cout<<"element de matrice M(0,0)="<<M(0,0)<<endl;
}
Résultat :
V= 1
2
3
M= 3 2 1
4 5 0
M*V= 10
14
element de matrice M(0,0)=3
Remarque 4.2.5. Vous pourrez de la même façon utiliser d'autres classes selon vos besoins
à condition cette fois ci d'inclure le chier d'entête
#include <...>
approprié au début
du programme et d'associer les librairies correspondantes à la compilation.
Exercice 4.2.6. Recopiez ce programme et exécuter le.
Exercice 4.2.7. (*) Modiez le programme précédent pour qu'il calcule et ache :
A = 1 1
0 1
B = 1 0
1 1
A*B = 2 1
1 1
4.2.5 tuple : ensemble d'objets divers
Un objet de la classe tuple est un ensemble d'objets divers. Par exemple :
CHAPITRE 4.
29
DÉCLARATION ET AFFECTATION DES OBJETS
Résultat du programme suivant :
a=10
b=10 c=x
d=14
o1[0]=100
o3[5]=y
#i n c l u d e <i o s t r e a m >
using
namespace
std ;
#i n c l u d e <t u p l e >
int
main
()
{
//...
construction
t u p l e <i n t , c h a r >
o1 ( 1 0 , ' x ' ) ;
auto
o2 = make_tuple
//...
lecture
int
des
//
(" t e s t " ,
creation
3.1 ,
14 ,
d ' un
objet
'y ' ) ;
//
constitué
d ' un
en
construction
sans
pr
elements
a = g e t <0>(o1 ) ;
//
on
extrait
le
premier
element
( position
0)
c o u t <<"a="<<a<<e n d l ;
int
b;
tie
( b , c ) = o1 ;
char
c;
//
extraits
les
elements
de
o1
c o u t <<"b="<<b<<" c="<<c<<e n d l ;
int
d;
tie
( ignore ,
ignore ,
d,
i g n o r e ) = o2 ;
//
extrait
certains
c o u t <<"d="<<d<<e n d l ;
//.....
ecriture
g e t <0>(o1 ) = 1 0 0 ;
d ' elements
//
on
remplace
c o u t <<"o1 [0]="<< g e t <0>(o1)<< e n d l ;
//..
concatenation
auto
de
tuples
o 3 = t u p l e _ c a t ( o1 , o2 ) ;
c o u t <<"o3 [5]="<< g e t <5>(o3)<< e n d l ;
return
}
0;
l ' element
0
de
o1
elements
d
Chapitre 5
Les instructions de base
5.1
La boucle "for"
#include <iostream>
using namespace std;
int main()
{
for(int i=2; i<=8; i=i+2)
{
int j=i*i;
cout <<i << "\t "<<j <<endl;
}
}
Ce programme produira :
2
4
6
8
4
16
36
64
Remarque 5.1.1. La syntaxe de la boucle for dans l'exemple ci-dessous signie : Fait les
instructions en partant de i=2, repête l'instruction i=i+2 et les instructions
qui suivent dans le bloc
{...}
tant que i<=8 (inférieur ou égal).
Une autre possibilité d'utilisation de la boucle
#include <iostream>
using namespace std;
#include <vector>
30
for
en C++11 est :
CHAPITRE 5.
31
LES INSTRUCTIONS DE BASE
int main()
{
vector<int> L={5,7,3,12}; // une liste
for(auto x:L) // la variable x parcourt la liste L
cout<<x+1<<",";
}
Ce programme produira :
6,8,4,13,
Exercice 5.1.2. (*) :
dans l'exemple ci-dessus, remplacer le corps du programme par
string chaine("Salut!");
for (auto a : chaine)
{
cout << a << ",";
}
cout<<endl;
Qu'est ce que cela produit ? quel est le type de la variable a ?
Aide : pour connaitre le type de la variable
a on peut utiliser typeid(c).name()
et écrire
#include <typeinfo>
//....
cout << typeid(a).name() <<endl;
5.2
Ecrire une condition
On a utilisé ci-dessus la condition
i<=30.
Voici la syntaxe pour écrire une condition
plus générale :
5.2.0.1
Les opérateurs de comparaison :
Signication
supérieur à
inférieur à
supérieur ou égal à
inférieur ou égal à
égal à
diérent de
>
<
>=
<=
==
!=
a == 2 sert à comparer l'objet a avec 2 (et ne change
a = 2 met la valeur 2 dans l'objet a.
Attention à la confusion possible :
pas la valeur de a). Par contre
symbole
CHAPITRE 5.
5.2.0.2
32
LES INSTRUCTIONS DE BASE
Les opérateurs logiques
Signication
symbole
ou
&&
||
non
!
et
5.2.0.3
Remarque
Lorsqu'une condition est évaluée comme
i<=30,
la valeur rendue est de classe
boolean
(vrai ou faux).
5.3
La boucle do {..} while (..) ;
signie
fait le bloc d'instructions {..} tant que la condition (..) est vraie.
#include<iostream>
using namespace std;
int main( )
{
int MAX(11);
int i(1),j;
do
{
j=i*i;
cout<<i<<"\t "<<j<<endl;
i=i+1; // Ne pas oublier l'incrémentation
}
while(i<MAX); // i < MAX est la condition
}
produira :
1 1
2 4
etc...
10 100
5.4
La boucle while (..) {..} ;
signie
tant que la condition (..) est vraie, fait le bloc d'instructions {..} .
CHAPITRE 5.
LES INSTRUCTIONS DE BASE
#include <iostream>
using namespace std;
int main ( )
{
int max=11;
int i=1, j;
while(i<max) // condition
{
j=i*i;
cout<<i<<"\t"<<j<<endl;
i=i+1; // ne pas oublier l'incrémentation
} ; // ne pas oublier le point virgule
}
Produira :
1
1
2
4
etc...
10
5.5
100
L'instruction if #include<iostream>
using namespace std;
int main()
{
for(int i=1; i<=10; i=i+1)
{
if (i==4)
cout<<"i= "<<i<<endl;
}
}
Ache :
4
On peut compléter avec if.. else if.... else.. comme ceci :
#include<iostream>
using namespace std;
int main()
{
33
CHAPITRE 5.
LES INSTRUCTIONS DE BASE
34
for(int i=1; i<=10; i=i+1)
{
if (i == 4)
cout<<"i="<<i<<endl;
else if(i==5)
cout<<"2i= "<<2*i<<endl;
else
{
cout<<"."<<endl;
cout<<":-)"<<endl;
}
}
}
Remarque 5.5.1. Dans le dernier cas on a mis les deux instructions à executer dans un
bloc d'accolades. En eet, il faut toujours mettre un ensemble d'instructions dans un bloc
d'accolades sauf si il y en a une seule à executer.
Exercice 5.5.2. On souhaite acher la suite 12345. Trouver l(es) erreur(s) et corriger le
programme suivant :
#include<iostream>
using namespace std;
int main()
{
int i=1 ;
for(i=1; i<=5; i=i+1);
cout<<i;
}
5.6
break : pour sortir de la boucle. continue : pour
sauter l'instruction.
#include <iostream>
using namespace std;
int main()
{
int i;
int j;
for(i=2;i<=10;i=i+2)
{
if (i==4)
CHAPITRE 5.
LES INSTRUCTIONS DE BASE
35
continue; // on passe au suivant.
if (i==8)
break; // on sort de la boucle
j=i*i;
cout <<i << "\t "<<j <<endl;
}
}
Ce programme produira :
2
6
4
36
Exercice 5.6.1. (*)
Faire un programme qui au départ choisit un nombre au hasard entre 0 et 1000 (Utiliser
la Section 4.1.5), puis demande à l'utilisateur de le trouver, en répondant "trop grand "
ou "trop petit " à chaque essai. L'utilisateur a le droit à 10 essais.
Chapitre 6
Les fonctions
Une fonction est un petit sous programme qui eectue des instructions et utilise éventuellement certains paramètres donnés en entrée.
Une fonction peut renvoyer un objet ou ne rien renvoyer du tout.
6.1
Exemple de fonction qui ne renvoit rien.
#include <iostream>
using namespace std;
//!========declaration de la fonction carre=============
void carre(int i)
{
int j=i*i; // declaration d'un objet local
cout<<"le carré de "<<i<<" est "<<j<<endl;
}
//!====declaration de la fonction principale =====================
int main()
{
int x;
cout<<" entrer x "<<endl;
cin>>x;
carre(x); // appel de la fonction carre
}
Ce programme renvoit :
36
CHAPITRE 6.
37
LES FONCTIONS
entrer x
2
le carré de 2 est 4
Remarque 6.1.1.
1. La déclaration de la fonction
carre
montre que cette fonction prend un paramètre
(l'objet i de la classe int) et ne renvoit rien (à cause du préxe
vide). La fonction
main
appelle la fonction
carre
void
qui signie
et lui passe un parametre qui
est l'objet x choisi par l'utilisateur.
{..} de la fonction carre on a déclaré l'objet j.Par conséquent, cet
j n'est connu que dans ce bloc et pas ailleurs. On dit que c'est un objet
local. On ne peut pas l'utiliser dans la fonction main. De même l'objet x déclaré
2. Dans le bloc
objet
dans le bloc de la fonction n'est pas connu ailleurs : On ne peut pas l'utiliser dans
la fonction
6.2
carre.
Exemple de fonction qui renvoit un objet
#include<iostream>
using namespace std;
//======declaration de la fonction carre ===================================
int carre(int i)
{
int j=i*i;
return j; //on renvoie le résultat au programme principal
}
//===declaration
int main()
{
int x;
cout<<"entrer x
cin>>x;
int y=carre(x);
cout<<"Le carré
}
de la fonction principale =====
"<<endl;
// appel de la fonction carre
de "<<x<<" est "<<y<<endl;
CHAPITRE 6.
LES FONCTIONS
38
Ce programme renvoit :
entrer x
2
le carré de 2 est 4
Remarque 6.2.1. La seule diérence avec l'exemple précédent est que la fonction
carre
renvoit son résultat qui est un objet de la classe int. Pour cela on utilise l'instruction
return. Pour appeler cette fonction, on utilise la syntaxe y=carre(x) si bien que le résultat
renvoyé est tout de suite stocké dans l'objet y.
6.3
Paramètres des fonctions par référence
Quand le programme principal fournit un objet à une fonction, celle-ci peut éventuellement vouloir modier le contenu de l'objet. Dans l'exemple précédent la procédure ou la
fonction
carre ne modie pas la valeur de x. Dans l'exemple ci-dessous, on veut échanger
le contenu de deux objets a et b. On doit alors dire à la fonction qu'elle a le droit de changer
les objets qu'on lui donne en entrée. Pour cela, dans la déclaration des paramètres, on met
le signe & devant les paramètres pouvant être modié par la fonction :
#include<iostream>
using namespace std;
//====== fonction permute ==================
void permute(int& i,int& j)
{
int t=i; // stockage temporaire
i=j;
j=t;
}
//======== fonction main ===================
int main()
{
int a=10, b=5;
cout<<a<<" "<<b<<endl;
permute(a,b);
cout<<a<<" "<<b<<endl;
}
CHAPITRE 6.
LES FONCTIONS
39
Ce programme renvoit :
10 5
5 10
Exercice 6.3.1. Executer ce programme, puis enlever les signes & (de référence) dans
le passage des paramètres, et ré-essayer. Conclusion ?
6.4
La surcharge des fonctions.
Un aspect interessant du C++ est que l'on peut "surcharger " les fonctions : c'est à
dire que l'on peut donner le même nom à des fonctions qui font des choses diérentes. Ce
sont les paramètres demandés lors de l'appel de la fonction qui permet à l'ordinateur de
distinguer qu'elle est la fonction à appeler.
6.4.1 Exemple :
#include<iostream>
using namespace std;
//------------------------------------------void func(int i)
{
cout<<"fonction 1 appelée"<<endl;
cout<<" paramètre = "<<i<<endl;
}
//------------------------------------------void func(char *s,int i)
{
cout<<"fonction 2 appelée"<<endl;
cout<<" paramètre = "<< s <<endl;
cout<<" paramètre = "<< i <<endl;
}
//--------------------------------------------int main()
{
func(10);
func("Chaîne ",4);
}
CHAPITRE 6.
40
LES FONCTIONS
Renvoit
fonction 1 appelée
paramètre = 10
fonction 2 appelée
paramètre = Chaîne
paramètre = 4
Exercice 6.4.1. (*)
Ecrire une fonction qui en entrée prend un objet
sa factorielle x ! de la classe
long.
x de la classe long, et en sortie renvoit
Dans la fonction principale on utilisera cette fonction
avec le code suivant :
long a=5;
long b = factorielle(a);
cout<<" a="<<a<<" a!="<<b<<endl;
an qu'il ache :
a= 5 a!=120
Remarque 6.4.2. En pratique il n'est pas nécessaire de programmer la fonction factorielle
ou d'autres
fonctions spéciales que l'on trouve dans la librairie boost.
Chapitre 7
Les chiers
Jusqu'à présent, nous avons lu des données au clavier et nous les avons aché à l'écran.
On peut aussi écrire des données dans un chier (sur le disque dur de l'ordinateur ou sur
un disque dur ou clef USB externe). Pour cela il faut utiliser des fonctions qui manipulent
les chiers. Ces fonctions sont regroupées dans le chier
ofstream
pour écrire dans un chier ou
ifstream
fstream
et font partie des classes
pour lire dans un chier.
Traduction : Output (=sortie) File (=chier) Stream (= ux de données), ou Input(=entrée),etc..
7.1
Ecriture de données dans un chier
Par exemple pour écrire un chire dans un nouveau chier que l'on appellera
hector.txt,
il sut de faire 3 étapes :
#include <iostream>
using namespace std;
#include <fstream> // utilisation des fichiers
int main()
{
ofstream f("hector.txt"); // ouvre le nouveau fichier en ecriture.
On lui associe l'objet: f
f<<3.1514<<endl; // permet d'ecrire dans le fichier.
f.close(); // fermeture du fichier f
}
7.1.0.1
Remarque :
hector.txt par le suxe .txt. Ce n'est
pas une obligation, mais une convention : txt signie texte, et précise que ce chier
1. On a choisit de terminer le nom du chier
peut se lire comme un texte (même si il y a des chires). De la même façon le chier
41
CHAPITRE 7.
42
LES FICHIERS
qui contient votre programme se termine par
.cc
pour signier que c'est le texte
d'un programme C++.
2. Pour écrire dans le chier, on a utilisé un objet intermédiaire de la classe
ofstream
(abréviation de output-le-stream).Cet objet a été initialisé avec le nom du chier,
et pour écrire dans le chier on utilise
l'écran (avec
f
à la place de
cout).
la même syntaxe que pour écrire à
3. L'ouverture du chier se fait par l'initialisation de l'objet f avec le nom du chier
donné sous la forme d'une chaine de caractères. On peut donc passer un objet
intermédiaire (chaine de caractères) de la façon suivante :
string nom=hector.txt
ofstream f(nom);
4. Lorsque l'on a eectué l'opération
et
close()
f.close(),
f est un objet de la classe
est une fonction de cette classe. On parle de
ofstream
fonction membre. Re-
marquez la syntaxe : l'objet et la fonction membre sont reliés par un point.
5. Pour rajouter des données à un chier existant (ou non existant) il faut l'ouvrir
avec l'instruction
ofstream f("hector.txt",ios::app);
(app signie appen-
d, ajouter)
7.1.0.2
Exercice
Ecrire le programme précédent et vérier l'existence du chier. Ouvrir le chier dans
codeblocks ou avec un autre éditeur et vérier son contenu.
7.2
Lecture de données depuis un chier
Le programme suivant permet de lire le chire écrit précédement dans le chier
#include<iostream>
using namespace std;
#include<fstream> // utilisation des fichiers
int main()
{
ifstream g("hector.txt");// ouvre un nouveau fichier en
lecture. On lui associe l'objet: g
double x;
g>>x; //on lit ce qu'il y a au début du fichier, et on
le copie dans l'objet x
cout<<x<<endl; // on écrit à l'écran le contenu de x
g.close(); // fermeture du fichier g
}
hector.txt.
CHAPITRE 7.
7.2.0.1
43
LES FICHIERS
Remarques :
1. Si le début du chier n'avait pas contenu de chire (mais des lettres ou rien du tout)
le programme n'aurait rien mit dans l'objet
x.
2. On peut de la même façon écrire ou lire plusieurs données à la suite dans un chier :
il est important de savoir que le programme est au départ positionné au début du
chier, puis après avoir lu (ou écrit) une donnée, il se trouve positionné juste après
cette donnée, prêt à lire la donnée suivante.
3. La lecture
g>>x
depuis le chier est similaire à la syntaxe de lecture
cin>>x depuis
le clavier.
4. A la place de
cout<<x<<endl;
on peut écrire les instructions :
if(g.good())
cout<<x<<endl;
En eet
g.good()
renvoit true si la lecture précédente s'est bien faite.
Exercice 7.2.1. (*) Reprendre et modier le programme de l'exercice 5.6.1 pour que les
numéros proposés par l'utilisateur soient ecrit dans un chier.
Chapitre 8
Les pointeurs
8.1
Déclaration et aectation d'un pointeur
La notion de pointeur est importante dans le langage C et C++. Elle est réputée comme
étant dicile et technique ; nous espérons que vous aurez néanmoins les idées claires après
la lecture de cette section.
Nous introduisons rapidement la notion de pointeur, et montrons comme exemple, son
intérêt pour créer des
tableaux de taille variable au cours du programme.
Rappellons déjà ce qu'est un objet. Par exemple :
int i;
Cette instruction a pour eet de réserver une case en mémoire de l'ordinateur, permettant
de stocker un nombre entier. Cette case s'appelle 'i'.
Bien sûr, cette case se trouve quelque part dans la mémoire de l'ordinateur. Elle a un
certain emplacement, caractérisée par son adresse, appellée son
Il faut donc retenir que
pointeur.
pointeur d'un objet signie adresse d'un objet dans la
mémoire de l'ordinateur.
On peut avoir accès à l'adresse de la case i en faisant :
int i=2; // déclare l'objet i et affecte la valeur 2
int *p; // déclaration du pointeur p
p=&i; // p devient l'adresse de i
Grâce au signe *, la deuxième ligne déclare p comme étant un pointeur (désignant une case
mémoire qui est sensée contenir un entier).
Le signe
&i
signie l'adresse de l'objet i. Donc la troisième ligne met dans
l'adresse de l'objet
i.
44
p
CHAPITRE 8.
45
LES POINTEURS
Pour acher le contenu de l'objet i on a maintenant deux possibilités :
cout<<i;
ou :
cout<<(*p);
qui sont équivalentes car :
(*p) signie le contenu de la case où pointe p.
Pour modier le contenu de l'objet i on a maintenant deux possibilités :
i=5;
ou :
(*p)=5 ;
qui sont équivalentes.
Attention : avant d'eectuer l'opération d'écriture :
(*p)=5 ; il faut être sur que l'adresse
du pointeur correspond à une case existante.
Vous avez donc compris que avant d'écrire, il faut prendre le soin de réserver de la place
mémoire.
8.2
Allocation dynamique de la mémoire
On peut réserver une suite de n cases mémoires, par l'instruction :
int n;
cout <<"entrez n ?"<< flush;
cin >> n;
double *p; //on déclare le pointeur p
p=new double[n]; // on réserve n cases mémoire de la classe double
Cela s'appelle une
allocation dynamique de la mémoire, car elle se fait au cours de
l'éxécution du programme.
Après cela,
p pointe sur la première case réservée : on peut légitimement écrire
dans cette première case par l'instruction :
(*p)=1;
ou (ce qui est équivalent)
p[0]=1;
On peut de même écrire dans la case suivante par :
*(p+1)=2;
ou
p[1]=2;
etc... jusqu'à
p[n-1].
CHAPITRE 8.
46
LES POINTEURS
Remarquer qu'il y a n cases, numérotées de 0 à n-1.
A la n de l'utilisation, n'oubliez pas de
libérer l'emplacement mémoire par l'ins-
truction :
delete [ ] p; // on libere les cases mémoire
8.2.0.1
Remarques
1. On peut réserver un nombre xe de cases en mémoire par l'instruction :
double p[6]; // creation de 6 cases mémoires de type double, numérotées
de 0 à 5.
p[1] = 3.14; // on écrit dans la case 1.
Il s'agit d'une allocation de mémoire dite statique (non dynamique) car
le nombre de case est xé à la compilation, et ne peut être variable.
2. Si l'on veut ne réserver qu'une seule case mémoire au lieu d'un tableau, il sut de
faire :
int *p;
p=new int; // on réserve une case mémoire de classe int.
(*p)=1;
delete p; // pour libérer la place mémoire
3. Un tableau de caractères est aussi appelé une
chaîne de caractères. On utilise
souvent la déclaration :
char *chaine = toto;
qui déclare
chaine comme étant un pointeur sur caractère, pointant sur les
4 caractères toto.
CHAPITRE 8.
LES POINTEURS
47
Exercice 8.2.1. (*) Ecrire un petit programme qui demande à l'utilisateur un entier n
compris entre 1 et 5, puis alloue de façon dynamique un tableau p de taille n de la classe
string. Ensuite dans une bloucle on demande à l'utilisateur le contenu de chaque case du
tableau. A la n, on ache le contenu du tableau. L'execution du programme doit donner
cela (par exemple) :
entrez n? 3
entrez p[0] ? un
entrez p[1] ? petit
entrez p[2] ? programme
p[0] =un
p[1] =petit
p[2] =programme
Chapitre 9
Graphisme avec la librairie ROOT
Comme il est très utile et agréable de représenter des données et résultats de façon
graphique, nous présentons tout de suite l'utilisation d'une librairie graphique. La librairie
root est développée au Cern (Laboratoire international de physique des particules). C'est
une librairie en C++ gratuite et très performante, qui permet :
de faire du
graphisme évolué (dessiner des axes, des courbes, des surfaces, des
objets en 3D,..), mais aussi du
de traiter des
graphisme simple (lignes, points, ronds,..)
données pour faire des statistiques ; cela est très utile au CERN
pour étudier les milliards de résultats issus d'une expérience de collisions entre
particules.
de faire des
interfaces graphiques pour un programme (gestion de la souris, menus
déroulants, boites de dialogues avec boutons, ...)
et beaucoup d'autres choses. Voir la page web de presentation.
Sur le réseau il y a une documentation complète. On peut aussi accéder à la liste
des classes. Il y a une mailling list d'utilisateurs qui s'entraident. Quand on a un
problème, il sut d'envoyer un mail, la réponse nous revient quelques minutes ou
heures plus tard. Le plus simple est souvent d'écrire dans le moteur de recherche de
google des mots clefs comme root cern ellipse pour trouver comment dessiner une
ellipse avec root.
Remarque 9.0.1. Root peut s'utiliser aussi en mode interprété C++ : lancer la commande
root dans une fenêtre xterm, et suivre les instructions...
9.1
Commande de compilation
Avant d'écrire un programme utilisant les librairies Root, il faut, une fois pour toutes,
Si vous utilisez l'éditeur codeblocks
Dans Settings/Compiler/Compiler_Settings/Other_Options, rajouter :
-I/usr/include/root
Dans Settings/Compiler/Linker_Settings/Other_linker_Options, rajouter :
48
CHAPITRE 9.
` r o o t −c o n f i g
49
GRAPHISME AVEC LA LIBRAIRIE ROOT
−− c f l a g s
`
` r o o t −c o n f i g
−− l i b s
`
` r o o t −c o n f i g
−− g l i b s
`
Si vous n'utilisez pas codeblocks, mais un Makele par exemple, la commande de
compilation du programme
g++ main . cpp
9.2
−o
main
main.cpp
pour donner l'executable
main
−g − s t d=c++11 − I / u s r / i n c l u d e / r o o t
doit être :
` r o o t −c o n f i g
−− c f l a g s
Exemple de départ qui dessine un cercle
#include <TApplication.h> // (A)
#include <TCanvas.h> //(B)
#include <TEllipse.h> // (C)
//------ fonction main (A) -------------------int main(int argc, char **argv)
{
TApplication theApp("App", &argc, argv); // (A)
TCanvas c("c","fenetre",400,400); // (B) Fenetre graphique. Taille
en pixels
double xmin(0),ymin(0),xmax(5),ymax(5);
c.Range(xmin,ymin,xmax,ymax); // coordonnees de la fenetre (optionnel,
par defaut: 0-1)
//------ dessin d'une ellipse dans la fenetre c (C) ------TEllipse e(2,3,1); // on precise le centre (x=2,y=3) et le rayon=1
e.Draw(); // dessine l'ellipse
c.Update(); //(B) Montre le dessin
theApp.Run(); //(A) donne la main a l'utilisateur
return 0;
}
Résultat du programme :
`
CHAPITRE 9.
50
GRAPHISME AVEC LA LIBRAIRIE ROOT
Remarque 9.2.1. Le programme précédent est découpé en trois types de blocs :
(A) : dans tout programme utilisant Root, ces parties doivent toujours être
réécrites. Il y a l'objet
theApp
de la classe
TApplication.
(B) : c'est le code qu'il faut pour créer la fenetre graphique (objet c de la Classe TCanvas). Si votre programme fait du graphisme, il faut toujours créer une fenêtre graphique
qui contiendra votre dessin.
(C) : c'est le code qu'il faut pour créer le cercle (objet e de la Classe TEllipse).
Exercice 9.2.2. Recopier le programme ci-dessus, le compiler et l'exécuter. Une fois que le
dessin apparait, vous pouvez choisir dans le menu :
View/Event Status Bar qui ache
la position de la souris.
Modier le programme an d'acher un cercle rouge au centre de la fenetre. Pour
cela, rajouter la commande
table des couleurs :
e.SetLineColor(kRed);
à la bonne place. (voici la
table des couleurs).
Remarque 9.2.3. la commande
e.Draw()
appelle la fonction membre
Draw()
de la classe
TEllipse qui dessine l'objet e. Pour connaitre toutes les opérations possibles que l'on peut
eectuer sur l'ellipse, il faut regarder la liste des fonctions membres de cette classe TEllipse.
On remarquera dans l'entête, que la classe TEllipse hérite d'autres classes comme la classe
TAttLine qui concerne les propriétés des traits. Ainsi la classe TEllipse peut utiliser les
fonctions de la classe TAttLine comme la fonction
la couler des traits.
SetLineColor()
qui permet de changer
CHAPITRE 9.
GRAPHISME AVEC LA LIBRAIRIE ROOT
51
Exercice 9.2.4. Modier le programme ci-dessus pour dessiner une ellipse remplie d'un
motif bleu comme ci-dessous. Aide : d'après la documentation, TEllipse hérite de la classe
TAttFill. On utilisera les fonctions membres :
e.SetFillColor(kBlue);
e.SetFillStyle(3025); // motif en carreaux
Exercice 9.2.5. (*) Modier le programme ci-dessus pour faire apparaitre un cercle de
rayon 1 qui tourne autour de l'origine, à la distance
liser les fonctions membre
SetX1(x1), SetY1(y1)
R = 2. Aide : une possibilité est d'utiTEllipse pour positionner
de la classe
son centre et utiliser une boucle for.
9.3
Utilisation des pointeurs avec ROOT
Dans l'exercice précédent, vous avez remarqué que lorsque vous redessinez l'ellipse
e,
l'ancien dessin disparait. Cela est pratique, pour faire de l'animation (faire déplacer un
cercle par exemple), mais comment dessiner plusieurs cercles simultanéments ?
Nous vous proposons deux possibilités :
CHAPITRE 9.
GRAPHISME AVEC LA LIBRAIRIE ROOT
52
9.3.1 Utiliser une liste d'ellipses
Ecrire le programme :
#include <TApplication.h>
#include <TEllipse.h>
#include <TCanvas.h>
#include <vector>
using namespace std;
int main(int argc, char **argv)
{
TApplication theApp("App", &argc, argv);
TCanvas c( "c","fenetre",400,400); // on precise la taille en pixels
CHAPITRE 9.
53
GRAPHISME AVEC LA LIBRAIRIE ROOT
c.Range(-2,-2,8,2); // coordonnees de la fenetre
//------------vector<TEllipse> tab_e(3); // liste de 3 ellipses
for (int i=0; i<3; i++)
{
tab_e[i].SetX1(3*i); // coord x du centre
tab_e[i].SetY1(0); //coord y du centre
tab_e[i].SetR1(1); // rayon selon x
tab_e[i].SetR2(1); // rayon selon y
tab_e[i].SetFillColor(kWhite);
tab_e[i].Draw(); // dessin
}
//------------c.Update();
theApp.Run();
return 0;
}
Ainsi l'ellipse numéro i+1 n'eace pas l'ellipse numéro i.
9.3.1.1
Remarques :
Rappel : dans l'instruction for, l'instruction
même
i--
est équivalente à
i = i-1
i++
est équivalente à
Pour enlever l'ellipse numéro 1 de la liste, il faut utiliser la fonction
classe
vector
i = i+1.
erase()
:
tab_e.erase(tab_e.begin()+1);
Rajouter cette ligne au bon endroit, dans le programme précédent et observer
le résultat.
9.3.2 Utiliser un pointeur sur l'ellipse
Ecrire le programme :
#include <TApplication.h>
#include <TEllipse.h>
#include <TCanvas.h>
#include <vector>
using namespace std;
int main(int argc, char **argv)
De
de la
CHAPITRE 9.
54
GRAPHISME AVEC LA LIBRAIRIE ROOT
{
TApplication theApp("App", &argc, argv);
TCanvas c( "c","fenetre",400,400); // on precise la taille en pixels
c.Range(-2,-2,8,2); // coordonnees de la fenetre
//------------TEllipse *pe; // crée un pointeur sur TEllipse
for (int i=0;i<3;i++)
{
pe= new TEllipse(3*i,0,1); // pe pointe sur une ellipse
crée en position x=3*i, y=0, rayon 1
pe->Draw(); // dessin
}
//------------c.Update();
theApp.Run();
return 0;
}
9.3.2.1
Remarques :
Cette dernière méthode est simple, et voici ce qu'il se passe :
un objet de la classe
TEllipse()
TEllipse
pe
est un pointeur sur
autrement dit l'adresse de l'objet. L'instruction
crée un objet ellipse et aecte
pe
pe= new
à l'adresse de cet objet. La deuxième fois,
cette même instruction crée un nouvel objet, sans détruire l'ancien, et
pe devient l'adresse
de ce nouvel objet. A la n on a la situation suivante où les trois ellipses existent (numéros
0,1,2), et
pe
est l'adresse du dernier objet :
L'avantage de cette dernière méthode est que l'on peut créer un nombre indéni d'ellipses. Pour modier une ellipse déjà existante il faudrait stocker au fur et à mesure les
adresses
pe
dans un tableau (ou utiliser une fonction de ROOT qui permet de récupérer
les adresses des objets existants).
CHAPITRE 9.
9.3.2.2
GRAPHISME AVEC LA LIBRAIRIE ROOT
55
Informations succintes sur les pointeurs
(plus de détails dans la Section 8)
pe est un pointeur (une adresse), alors *pe est le contenu (l'objet) à cette adresse.
e est un objet alors &e est le pointeur (adresse) de l'objet.
Si pe est un pointeur (une adresse), l'instruction pe->Draw(); est équivalente à
(*pe).Draw();
On comprends l'écriture TEllipse *pe; comme la déclaration que *pe est un objet,
donc pe est un pointeur.
Si
Réciproquement si
Exercice 9.3.1. (*) Ecrire un programme qui dessine un tableau de 10*8 cercles colorés
au hasard comme cela :
9.4
Représentation graphique des vecteurs et matrices
de Armadillo
Description :
Ces fonctions combinent les classes de vecteur et matrice de armadillo et
les classes graphiques de Root, an de représenter graphiquement le contenu de vecteurs
et de matrices.
Cela peut être pratique en diverses circonstances.
CHAPITRE 9.
GRAPHISME AVEC LA LIBRAIRIE ROOT
56
Travail préalable :
1. Fichiers à télécharger et sauver dans son répertoire : bib_fred.h et bib_fred.cc.
2. Si vous utilisez codeblocks, il faut ajouter ces chiers au projet en faisant dans le
menu : Projet/add_les ...
#include "bib_fred.cc"
#include "bib_fred.h". Ou bien utiliser la
3. Si vous utilisez simplement emacs, il faut rajouter la ligne
dans le code suivant à la place de
gestion de projet avec Makele comme expliqué dans la Section 25.2.
4. Il faut aussi
rajouter
-larmadillo
dans les options de compilation (pour code-
blocks c'est dans Setting/Compiler/LinkerSettings/OtherLinkerOptions)
Exemples
#include <iostream>
using namespace std;
#include <armadillo>
using namespace arma;
#include
#include
#include
#include
<TApplication.h>
<TEllipse.h>
<TCanvas.h>
"bib_fred.h"
int main(int argc, char **argv)
{
TApplication theApp("App", &argc, argv);
//!--------Dessin de vecteur réel -----------------------------vec v1=randu<vec>(100); // vecteur réel avec elements au hasard
dans (0,1)
Dessin(v1,"V1"); // Dessin du vecteur. On lui donne le titre V1
//!... plus d'options:
int opt_log=1;// echelle log en y
TH1D *h=Dessin(v1,"x","y",0,7,"V2",0,"L",0.01,100,kRed,opt_log);
h->SetYTitle("z"); // change le titre de l'axe Y à partir du pointeur
h récupéré.
h->Draw("L"); // redessine
//!....Dessin sur un cercle............
Dessin_phases(v1,"V3");
CHAPITRE 9.
GRAPHISME AVEC LA LIBRAIRIE ROOT
57
//!------Dessin de matrice réelle---------------------------mat m1=randu<mat>(10,10); // vecteur avec elements au hasard dans
(0,1)
Dessin(m1,"M1"); // Dessin de la matrice avec le titre "M1"
//!... plus d'options:
Dessin(m1,"x",0,3,"y",0,7,"M2","lego2");
//!---- Dessin d'un vecteur de nombres complexes
cx_vec w1=randu<cx_vec>(20)-complex<double>(0.5,0.5); //distribution
dans [-0.5,05]*[-0.5,0.5]
Dessin(w1,"W1"); // dessin des points dans le plan complexe
//!------- Affichage de vector -----------vector<double> V1({1.2,2.1,3.4});
cout<<V1<<endl;
vector<int> V2({1,2,3});
cout<<V2<<endl;
theApp.Run();
return 0;
}
Resultats :
CHAPITRE 9.
GRAPHISME AVEC LA LIBRAIRIE ROOT
58
CHAPITRE 9.
GRAPHISME AVEC LA LIBRAIRIE ROOT
59
CHAPITRE 9.
GRAPHISME AVEC LA LIBRAIRIE ROOT
60
[1.2,2.1,3.4]
[1,2,3]
Exercice 9.4.1. (*)
vec et mat de armadillo (vecteur et matrice de
nombres à virgule) et les fonctions Dessin() ci-dessus, dessiner la fonction Gaussienne à
2
2
2
une variable et deux variables : f (x) = exp (−x ) et f (x, y) = exp (− (x + y )) pour un
intervalle de x, y convenable.
En utilisant respectivement les classes
Chapitre 10
Création d'une classe
Programmer
une classe consiste à dénir un nouveau type d'objets avec des opérations
précises que l'on pourra faire avec. Vous avez utilisé par exemple la classe des
Complexes
déjà existante (écrite par d'autres informaticiens avant vous). Avec cette classe, on peut
déclarer des variables complexes et eectuer directement des opérations sur les nombres
complexes comme z3=z1*z2*exp(z4),...sans devoir décomposer l'opération sur les parties
réelles et imaginaires.
Dans cette section, on va apprendre à écrire soi-même une classe, et l'on prendra
l'exemple des vecteurs et des matrices. Il existe déjà des classes de vecteurs et de matrices
prêtes à l'emploi et très performantes, comme armadillo, voir Section 4.2.4. Considérez
donc cette Section comme étant à but pédagogique.
10.1
Exemple
Un vecteur de
R3
est dénit par ses trois composantes
(x, y, z). Voici un programme de
départ pour dénir un vecteur en C++. Essayez ce code. Lisez ensuite les commentaires.
Résultat :
vecteur construit...
Norme = 3.74166
vecteur détruit....
#i n c l u d e <i o s t r e a m >
using
namespace
std ;
#i n c l u d e <math . h>
// ===============d é c l a r a t i o n
class
de
la
Vect
{
61
c l a s s e ===============
CHAPITRE 10.
62
CRÉATION D'UNE CLASSE
public :
//−−−−
variables
double
//−−
le
membres
x,y,z;
c o n s t r u c t e u r −−−
Vect ( )
{
x =0;
y =0;
z =0;
c o u t << " v e c t e u r
construit . . .
c o u t << " v e c t e u r
détruit . . . .
" << e n d l ;
}
//−−
le
d e s t r u c t e u r −−−
~Vect ( )
{
" << e n d l ;
}
//−− l a
Norme−−−
fonction
double
Norme ( )
{
double
n =0;
n=x * x+y * y+z * z ;
return
sqrt (n ) ;
}
};
//====== Programme
p r i n c i p a l =======================
main ( )
{
Vect
v;
v . x =1;
//
declaration
v . y =2;
v . z =3;
c o u t << "Norme = " << v . Norme ( ) << e n d l ;
}
10.1.1 Commentaires
Commençons par la première ligne du programme principal : on a déclaré un objet
v
de la classe
Vect.
classe, appelée
Cela a pour eet d'appeler la fonction
Vect()
déclarée dans la
constructeur. Dans la partie déclaration, il est écrit qu'un objet
de la classe Vect possède trois variables
x,y,z
de type double appelées variables
CHAPITRE 10.
63
CRÉATION D'UNE CLASSE
membres. Dans notre exemple la fonction constructeur a pour eet de mettre les
variables x,y,z à zéro et d'acher le texte vecteur construit....
Ainsi à la deuxième ligne du programme principal, le code
dans la variable membre
x
de l'objet
v.
De façon générale
v.x=1; met la valeur 1
objet.variable est la
façon de désigner une variable associée à un objet.
A la troisième ligne du programme principal, le code
Norme()
qui est déclarée dans la classe.
p
√
2
2
x + y + z 2 = 1 + 4 + 9 = 3.74.. De
v.Norme()
appèle la fonction
Cela a pour eet de calculer et acher
façon générale
objet.fonction()
est la
façon d'appeler une fonction membre associée à un objet.
a la n du programme principal, l'objet v va être détruit et le programme appelle
la fonction ~Vect() appelée destructeur, même si cela n'est pas explicitement écrit.
Dans notre exemple cela a pour eet d'acher le texte "vecteur détruit.... ".
Remarque 10.1.1.
Dans la déclaration d'une fonction membre comme
x,y,z
Norme()
les variables membre
s'accèdent directement (sans la syntaxe v.x) car ce sont sans ambiguité les
v.
Vect() est appelée chaque fois qu'un objet Vect
variables membre de l'objet en cours pour qui la fonction a été appelée, ici
La fonction membre
constructeur
est déclaré au cours du programme. Comme son nom l'indique, le constructeur sert
à initialiser le nouvel objet créé. Le constructeur doit toujours porter le nom de la
classe en question (ici Vect).
la fonction membre
destructeur
∼Vect() est appelée chaque fois qu'un objet Vect
est détruit au cours du programme. Le destructeur doit toujours porter le nom de
la classe en question (ici
L'annonce
public:
Vect)
avec
~
devant .
en haut des déclarations permet à ce qui suit d'être connu et
accessible hors de la déclaration de la classe. Au contraire on rend les déclarations
non accessibles (si besoin est) par l'annonce
private:
Remarquez dans le programme la présence ou non de points virgule ; en particulier
à la n de la déclaration de la classe.
Exercice 10.1.2. (*)
1. Ecrire le programme ci dessus dans un chier
vecteur.cc (dans un nouveau réper-
toire /classes/), éxécutez le, et vériez le comportement attendu.
2. Rajouter une fonction membre de la classe
Vect
que l'on appelera
Affiche(),
et
qui ache à l'écran le contenu du vecteur sous la forme :
Vect: x=1 | y=2 | z=3
Appelez cette fonction dans le programme principal et testez le bon fonctionnement.
3. Rajouter une fonction membre de la classe
v2),
Vect que l'on appelera double Produit(Vect
et qui permet de calculer le produit scalaire entre deux vecteurs. Le résultat
double. Dans le programme
Vect v,w;
double d=v.Produit(w);
est un
principal, l'appel doit se faire sous la forme :
CHAPITRE 10.
64
CRÉATION D'UNE CLASSE
4. Appelez cette fonction dans le programme principal et testez le bon fonctionnement.
Remarque : essayez de comprendre la cause des messages construit et détruit. Il
manque un construit. La raison de cela sera expliquée ultérieurement au paragraphe
Constructeurs par recopie, et aectations.
5. Rajoutez un constructeur qui permet d'initialiser directement les variables membres
x,y,z
Vect(double xi,double yi,double zi).
Vect v(1,2,3);
constructeur précédent Vect() qui ne prend pas d'argu-
du vecteur. Il aura la syntaxe
Dans le programme principal, on pourra alors déclarer un objet par
Remarque : vous gardez le
ment. N'oubliez pas en eet qu'en C++ des fonctions diérentes peuvent avoir le
même nom et ne diérent que par leurs arguments.
10.2
Utilisation de pointeurs sur objets
Nous avons expliqué les pointeurs dans la section 8. Dans l'exemple 10.1 ci-dessus,
remplacer les quelques lignes du programme principal par
//====== Programme
p r i n c i p a l =======================
main ( )
{
Vect
*w
= new
Vect ( ) ;
//
declaration
w−>x =0; w−>y =3; w−>z =4;
c o u t << "Norme
d e w = " << w−>Norme ( ) << e n d l ;
}
Résultat :
vecteur construit...
Norme de w = 5
Commentaires
La déclaration Vect *w signie que w est un pointeur (c'est à dire une adresse) sur
un objet de la classe Vect. le code w = new Vect() ; a pour eet de créer un objet
à cette adresse.
A la deuxième ligne le code
w->x=0;
met la valeur 0 dans la variable membre
de l'objet pointé par w. De façon générale
pointeur->variable
x
est la façon de
désigner une variable associée à un objet qui est pointé par le pointeur.
w->Norme() appèle la fonction
générale pointeur->fonction()
A la troisième ligne du programme principal, le code
Norme()
qui est déclarée dans la classe. De façon
CHAPITRE 10.
65
CRÉATION D'UNE CLASSE
est la façon d'appeler une fonction membre associée à un objet pointé par le pointeur
pointeur.
Remarque 10.2.1.
Il n'y a pas d'appel automatique de la fonction destructeur à la n du programme.
En fait seule la variable pointeur est détruite. Si l'on souhaite appeler le destructeur
de l'objet pointé il faut le faire explicitement par
w->~Vect();
ou
delete(w);
qui
a le même eet.
10.3
Surdénitions d'opérateurs
10.3.1 Exemple d'opérateur binaire
Pour eectuer le produit scalaire entre deux vecteurs, plutôt que d'utiliser la fonction
Produit
ci dessus, on aimerait utiliser la syntaxe plus agréable :
d=v*w;
cela est possible
en redénissant l'opérateur * de la façon suivante.
Code à rajouter dans le code des fonctions membres :
double operator*(Vect v2)
{
double p=0;
p=x*v2.x+y*v2.y+z*v2.z;
return p;
}
Utilisation dans le programme principal :
main()
{
double d;
Vect v(1,2,3);
Vect w(2,3,4);
d=v*w;
cout <<d= << d << endl;
}
10.3.2 Commentaire
La syntaxe de l'opération * est semblable à celle de la fonction membre
Comme pour
Produit ,
Il est important de remarquer
Vect v2
Produit .
est un paramètre
de l'opération *. Lorsque l'on écrit d=v*w, l'opération * est appelée pour l'objet v
qui se trouve à gauche de * (et non w) ; à l'appel de cette opération, le vecteur v2
prend la valeur de w qui se trouve à droite de *.
CHAPITRE 10.
66
CRÉATION D'UNE CLASSE
On dit que * est
un opérateur binaire car lors de l'appel
d=v*w
il y a deux entrées
pour *. Il y a à sa gauche l'objet de la classe, et à sa droite un autre objet (à priori
quelconque, ici
Les
Vect v2 ).
symboles possibles pour un opérateur binaire sont :
(),[ ], -> , *,/,%,+,-, << ,>>,<,<=,>,>=,==,!=,&,^, ||,&&,|,=,
+=,-=,*=,/=,%=,&=,^=,|=, <<=,>>=, et la virgule , .
Exercice 10.3.1. (*)
1. Ecrivez le code pour eectuer la somme (termes à termes) et la diérence de deux
vecteurs par la syntaxe :
w=u+v; w=u-v;
et testez.
2. Ecrivez le code pour eectuer le produit et la division externe d'un réel
vecteur v (produit de chaque élément) par la syntaxe
w=v*a;
ou
w=v/a;
a
avec un
et testez.
v.x,v.y,v.z respectivement, par la
v[1],v[2],v[3] . Cela est possible en dénissant l'opérateur [ ] avec la
déclaration double & operator [ ](int i); A l'utilisation, on écrit par exemple
d=v[1]; . Ecrivez le code de cet opérateur et testez.
Rem : le signe & permet d'écrire aussi une aectation comme v[1]=3; . Cela sera
3. On aimerait accéder aux variables membres
syntaxe
mieux expliqué au paragraphe aectations.
10.3.3 Exemple d'un opérateur unaire
Comme en mathématiques, on aimerait donner un sens à
vecteur
v.
Pour cela il faut écrire :
Code à rajouter dans le code des fonctions membres :
Vect operator-()
{
return (*this)*(-1);
}
Utilisation dans le programme principal :
main()
{
Vect v(1,2,3);
Vect w;
w=-v;
w.Affiche();
}
(−v)
qui est l'opposé du
CHAPITRE 10.
67
CRÉATION D'UNE CLASSE
10.3.4 Commentaires
On dit que - est
un opérateur unaire car lors de l'appel
w=-v
car la seule entré
de - est l'objet v de la classe qui se trouve à droite. (il n'y a pas d'entrée à gauche).
Les
symbôles possibles pour un opérateur unaire sont :
+,-,++,--,!,~,*,&,new,new[],delete,(cast)
(*this) dans la fonction membre signie l'objet en cours. Ici il s'agit du vecteur v.
Remarquez la façon économique de calculer le résultat : on utilise la multiplication
externe déjà programmée.
Exercice 10.3.2.
Simpliez le code de l'opérateur binaire w=u-v déjà écrit en utilisant l'opérateur
binaire + et l'opérateur unaire - seulement.
Simpliez le code de l'opérateur binaire w=v/a déjà écrit en utilisant l'opérateur
binaire * .
10.4
Fonctions amies
10.4.1 Exemple
La syntaxe que permet les fonctions membres ci-dessus a une petite limitation. Dans
la classe Vect, on aimerait par exemple dénir le produit externe * d'un double
Vect
v,
et écrire :
a
avec un
w=a*v;
Mais d'après la disposition des objets, il faudrait que * soit un opérateur binaire de
l'objet
a
qui se trouve à gauche . On peut cependant associer cette opération à la classe
Vect de l'objet
v
en écrivant :
Code à rajouter dans le code des fonctions membres :
friend Vect operator*(double a,Vect v2)
{
return (v2*a);
}
Utilisation dans le programme principal :
main()
{
double d(2);
Vect v(1,2,3);
Vect w;
w=d*v;
}
CHAPITRE 10.
68
CRÉATION D'UNE CLASSE
10.4.2 Commentaire
Le préxe
friend
signie que cet opérateur n'est pas exactement une fonction
membre de la classe Vect, mais pas non plus indépendante puisque elle est déclarée
Vect:: .
Les paramètres de l'opération * dans la déclaration sont (double a,Vect v2). Ils
se trouvent de part et d'autre de * lors de l'utilisation : w=a*v;
avec la classe. Remarquez l'ommission du préxe de rattachement
Comme en mathématiques, on a dénit a*v=v*a. Mais on pourrait écrire ce que
l'on veut et ne pas respecter la commutativité.
double d(2) est équivalente à double d=2.
constructeur double(int) est appelé.)
la déclaration
le
(La signication est que
10.4.3 Exemple de cout <<
On aimerait pouvoir acher le contenu du vecteur v au moyen de la commande :
<<v;
Remarquons que l'objet à gauche de l'opérateur
<<
cout
est cout appartenant à la
classe ostream (ici o signie output donc achage, et cette classe standard du C++ est
obtenue par
#include <iostream> ). Si l'on veut dénir cette opération dans notre classe
Vect, il faudra donc la déclarer comme une fonction amie. Exactement comme l'exemple
précédent, cela se fait en écrivant :
Code à rajouter dans le code des fonctions membres :
friend ostream & operator <<(ostream & sortie, Vect v2)
{
sortie <<Vecteur: <<v2.x <<| <<v2.y <<| <<v2.z
<<|;
return sortie;
}
Utilisation dans le programme principal :
main()
{
Vect v(1,2,3);
cout <<v <<endl;
}
Exercice 10.4.1. (*)
Ecrivez le code nécessaire an de pouvoir demander le contenu d'un vecteur à l'utili-
cin >>v; L'utilisateur doit entrer les composantes à la suite des
Vect x=?,y=?,z=?. La syntaxe est cette fois-ci :
istream & operator >>(istream & entree,Vect & v2);
Le symbole & s'appelle passage par références, et est nécessaire puisque le vecteur v
sateur par la syntaxe
questions
est modié à la sortie de la fonction.
CHAPITRE 10.
10.5
69
CRÉATION D'UNE CLASSE
Vecteurs de taille variable
3
On va améliorer la classe de vecteurs de R précédente, en permettant d'avoir des
N
vecteurs de R
avec N quelconque, au choix de l'utilisateur.
Important : avant de commencer, nous conseillons vivement de (re)lire le paragraphe
sur les pointeurs.
Il apparaît que les variables membres de la nouvelle classe, doivent être :
(Code à écrire dans la déclaration de la classe :)
//---- variables membres
int N; //taille
double *element; //pointeur
Important : la valeur de N nous renseigne à tout moment sur la taille de l'espace mémoire
réservé.
Cette modication nous obligera à réécrire les fonctions membres de la classe Vect,
mais aussi d'introduire deux nouvelles fonctions membres : le constructeur par recopie, et
l'opérateur d'aectation =.
Exercice 10.5.1. Créer une nouvelle classe de vecteurs, qui contient les variables membres
ci-dessus, avec les fonctions membres suivantes :
Une fonction membre
Une fonction membre
Libere() qui libère l'espace mémoire de taille N.
Init(Ni) qui fait dans l'ordre les opérations suivantes
:
(1) Libère l'emplacement mémoire déjà existante (si N>0)
(2) :Réserve l'emplacement mémoire de taille Ni , (si Ni>0).
(3) :met N=Ni.
(4) met les composantes du vecteur à zéro.
Un
constructeur
Vect(),
qui met seulement
N = 0,
et ne réserve pas de place
mémoire. (il appelle la fonction : Init(0) ; )
Un
constructeur
Vect(int Ni),
pour créer un vecteur de taille Ni (il appelle la
fonction : Init(Ni) ; ).
Un
destructeur
~Vect()
qui libère l'espace mémoire. (il appelle la fonction Li-
bere())
10.5.1 Constructeur par recopie
double Produit
v.Produit(w) dans le pro-
Dans un exercice précédent, nous avons écrit une fonction membre
(Vect v2) .
Lors de l'appel de cette fonction par la syntaxe
gramme principal, un objet v2 est créé, et l'objet w est
copié dedans. Mais il était apparu
qu'aucun Constructeur n'était explicitement appelé pour cette recopie. La raison est que le
compilateur C++ a appelé un
constructeur de recopie par défaut, qu'il a créé lui-même.
Dans ce constructeur par défaut, il est eectuée une recopie simple des variables membres
(il s'agissait de x,y,z).
CHAPITRE 10.
70
CRÉATION D'UNE CLASSE
Dans le cas actuel, une recopie simple des variables membres ne sut pas. A la création
du nouvel objet Vect v2, il faut en eet réserver l'emplacement mémoire, et donc écrire
nous même
le constructeur par recopie. Le code est le suivant :
Code à rajouter dans le code des fonctions membres :
Vect (const Vect & v2)
{
// ... code à écrire (exercice)...
}
10.5.2 Opérateur d'aectation =
Dans la classe Vect de
R3 ,
si v,w sont deux vecteurs, et que l'on écrit l'opération
d'aectation :
v=w;
alors les composantes de w sont copiées dans celles de v. Cette
opération d'aectation
a été faite automatiquement par le compilateur. Il y a recopie des variables membres x,y,z.
Dans notre cas actuel de vecteur à taille variable, on ne peut laisser faire le compilateur,
puisqu'il faut libérer l'ancien emplacement mémoire de v, puis lui réserver un nouvel emplacement mémoire, et ensuite copier les composantes de w dans celles de v.
On va écrire nous même le code de
l'opérateur d'aectation =
Code à rajouter dans le code des fonctions membres :
Vect & operator=(const Vect & v2)
{
// ... code à écrire (exercice)...
}
Exercice 10.5.2.
1. Ecrivez le code du
constructeur par recopie et de l'opérateur d'aectation
= ci dessus, et testez. (en achant des messages).
2. Reprenez les
fonctions membres de la classe Vect de
versions Vect de
R
N
R3
précédente, et écrivez les
correspondantes.
Concernant spécialement
l'opérateur [ ], avec
N
éléments, on décide d'y accéder
de la façon suivante :
Vect v(N);
v[0],v[1],...,v[N-1],
Rem : Dans le code de l'opérateur [ ](int i), il est bien de tester si l'indice i est dans
l'intervalle autorisé, et sinon d'acher un message d'erreur (i.e. si i<0, ou i>N-1).
CHAPITRE 10.
71
CRÉATION D'UNE CLASSE
3. Dans un deuxième temps, on aimerait que
le premier indice i1 soit quelconque
(et pas forcément i1 = 0). Par exemple, que les trois composantes d'un vecteur de
R3 soit v[2],v[3],v[4], i.e. i1 = 2, N = 3;
Précisement, on voudrait qu'un vecteur soit caractérisé par
sa taille
N,
et par
son
v[i]
i[i1 , i1 + 1, ..., i1 + N ].
Il faut donc rajouter i1 comme variable membre de la classe, et modier les construcpremier indice i1 , an d'accéder aux éléments du vecteur par la commande
avec
teurs et destructeurs, et fonctions membres si besoin est. A la n, on aura une classe
de vecteur assez sophistiquée, et prête à l'emploi.
4. (Long) Sur le même modèle que la classe vecteur, créer une
classe matrice, avec
les opérations usuelles.
Rem : on pourra dénir une matrice N*M comme étant un tableau de vecteurs, et
prendre comme variables membres:
int N,M; // taille
Vect * ligne;
Rem : attention aux fonctions
Init() et Libere()
!
Chapitre 11
Micro-projets
En guide de conclusion de cette première partie, nous vous proposons de réaliser un
micro-projet parmi la liste suivante. Ce sont des programmes rapides à réaliser (quelques
lignes de code), an de vous montrer l'aspect ludique et créatif de la programmation, et
aussi pour résumer diérentes notions vues dans ce didacticiel.
Choisissez et réalisez un de ces micro-projet, selon votre motivation et l'aisance que
vous ressentez en informatique.
Pour le graphisme, utiliser la Section 12.
11.1
Suite de Syracuse
Diculté : facile.
u0 ≥ 1. Par récurrence, on dénit un+1
(
1
un
si un est pair
un+1 = 21
(3un + 1) si un est impair
2
Soit un nombre entier
à partir de
un
par :
u0 = 1 donne la suite innie périodique 1, 2, 1, 2, 1, . . .
La valeur de départ u0 = 7 donne la suite 7, 11, 17, 26, 13, 20, 10, 5, 16, 8, 4, 2, 1, 2, 1, 2, 1, . . .
Observer que la valeur de départ
La fameuse
valeur
conjecture de Syracuse non démontrée à ce jour est que partant de toute
u0 la suite arrive forcément au bout d'un moment T
à la suite périodique
1, 2, 1, 2, . . .
Exercice 11.1.1.
1. Ecrire un programme qui demande
ce que la valeur
2. Pour
u0
un = 1
à l'utilisateur et ache la suite
(un )n
jusqu'à
soit atteinte.
T (u0 ) la plus
u0 ∈ {1, 2, . . . 1000}.
donné, on note
en fonction de
u0
petite valeur de
72
n
telle que
un = 1.
Tracer
T
CHAPITRE 11.
11.2
73
MICRO-PROJETS
Pendule simple
Diculté : moyenne.
Dessiner (en animation) dans l'espace réel et dans l'espace des phases, le mouvement
d'oscillation d'un pendule simple de masse m, longeur l , soumis à la force de pesanteur
P = mg avec g = 9.8m/s2 et à une force de frottement de coef γ .
Equations physiques :
temps
t
si
a (t)
est la position angulaire et
b (t)
la vitesse angulaire au
on a
da
= Fa := b
dt
db
g
γ
= Fb := − sin (a) − b
dt
l
m
Position du pendule :
x = l sin (a) ,
Méthode de résolution numérique
petit intervalle de temps
dt ;
y = −l cos (a)
Avec la
au premier ordre en
méthode de Euler, on considère un
dt
on a
a (t + dt) = a (t) + dt · Fa
b (t + dt) = b (t) + dt · Fb
Structure du programme
Ecrire une fonction :
/* Formule du champ de vecteur sur le plan (a,b)
entree: a,b
CHAPITRE 11.
74
MICRO-PROJETS
sortie: Fa,Fb
*/
void F(double a, double b, double & Fa,double & Fb)
{
//... code ici
}
Dans la fonction main, partir de valeurs
a, b initiales, calculer a, b à chaque pas de temps
et dessiner le pendule.
11.3
La fractale du dragon
Diculté : moyenne.
Si on prend une feuille de papier que l'on plie
N =1
fois et que l'on impose au pli de
faire un angle droit (90°) on obtient la forme suivante. Si on plie
N = 3 fois
ou ...N = 16 fois on obtient les formes suivantes assez surprenantes. La forme pour N 1
s'appelle la fractale du dragon.
N =2
fois ou
CHAPITRE 11.
75
MICRO-PROJETS
Ecrire un programme qui demande une valeur
plie
N
N ≥ 1 et dessine la forme obtenue si l'on
fois. Qu'observez vous de surprenant ?
Aide :
on peut coder la forme comme une suite
−1)
SN
de
2N − 1
valeurs
±1,
où
+1
(respect.
signie que le pli est à droite (respect. à gauche).
Observer que la suite
SN
se déduit facilement de la suite
SN −1 .
Dans le programme
il faut faire une fonction qui s'appelle elle même (récursivité).
11.4
Equation de la chaleur
Diculté : moyenne.
Notons
x = (x1 , . . . xn )
les variables d'espace et
t
la variable de temps. Joseph Fourier
a découvert en 1822 que la propagation de la température dans un matériau est régit par
l'équation d'évolution
∂T (x, t)
= (∆T ) (x, t)
∂t
appelée équation de la chaleur, avec l'opérateur Laplacien :
∆T :=
∂ 2T
∂ 2T
+
.
.
.
+
∂x21
∂x2n
n = 2. On discrétise l'espace avec un
x1 = ih, x2 = jh avec i, j = 0 . . . N − 1. Le champ
l'instant t est modélisé par une matrice T (i, j) de taille N × N . On
0
avec un pas 1 très petit et on note T le champ de température à
On va modéliser cette évolution en dimension
pas
h1
très petit. On note donc
de températures à
discrétise le temps
l'instant
t + .
On remplaçant les dérivées par des diérences nies montrer que l'équation de la chaleur
ci-dessus devient :
CHAPITRE 11.
76
MICRO-PROJETS
T 0 (i, j) = T (i, j) +
(T (i + 1, j) + T (i − 1, j) + T (i, j + 1) + T (i, j − 1) − 4T (i, j)) .
h2
Partant d'un champ de températures quelconque, en utilisant cette modélisation, écrire
un programme qui fait évoluer et dessine le champ de température. Discuter le choix des
paramètres
11.5
, h.
Le jeu de la vie
Diculté : moyenne.
En utilisant une matrice d'entiers de taille 10*10 avec des conditions périodiques, faire
évoluer et dessiner les coecients de matrice selon les règles du jeu de la vie.
11.6
Algorithme de MonteCarlo pour le modèle d'Ising
Diculté : moyenne à dicile.
Figure 11.1 Resultat de l'algorithme de Monte-Carlo pour le modèle d'aimantation de
Ising, aux températures
β = 1/(kb T ) = 0.3
(désordre),
0.4
(transition de phase) et
0.5
(ordre magnétique).
(Explication physique : voir Cours de Master 1: système dynamiques, chapitre Dynamique probabiliste sur les graphes).
On considère un réseau
les variables sont
fX = ±1
N ×N
X = (x, y)
et dont
et modélisent des spins (up/down). Si deux sites sont voisins
(chaque site a 4 voisins) on note
f = (fX )X .
périodique dont les sites sont notés
X ∼ Y . Une conguration des spins est un champ donné :
Son énergie ferromagnétique est :
E (f ) :=
X X
X Y,X∼Y
(−fX .fY )
CHAPITRE 11.
77
MICRO-PROJETS
Remarque 11.6.1. Il y a
N2
sites et
2N
2
congurations possibles.
(−fX .fY ) minimal soit fX .fY =
1 pour tous X, Y . Il y a donc deux congurations d'énergie minimale qui sont : fup ={fX = 1
pour tous site X } et fdown ={fX = −1 pour tous site X }.
Les congurations d'énergie minimale sont celles donnant (−fX .fY ) maximal soit fX .fY =
−1 pour tous X, Y . Cela est possible si N est pair avec des spins dont les valeurs sont alLes congurations d'énergie minimale sont celles donnant
ternées (il y a deux congurations).
11.6.1 Algorithme de Monte-Carlo
L'algorithme d'évolution suivant est appelé
algorithme de montecarlo.
paramètre xé qui est l'inverse de la température :
1. A l'instant
n = 0,
2. On choisit un site
site
X
β = 1/ (kb T ).
on part d'une conguration
X
β ≥ 0 est un
f
choisie au hasard.
0
au hasard, et on note f la conguration identique à
fX0 = −fX (spin opposé).
f
sauf au
où le spin est opposé :
3. Choix de la conguration à l'instant
n + 1,
E (f 0 ) < E (f ) on choisit la conguration f 0 .
0
0
Si E (f ) ≥ E (f ) on choisit la conguration f
exp (−β. (E (f 0 ) − E (f ))) (et on choisit f avec la
(a) Si
(b)
avec la probabilité
Pf →f 0 =
probabilité complémentaire).
4. On revient en (2) pour poursuivre l'évolution du champ de spins.
Remarque 11.6.2. Cet algorithme correspond à une matrice stochastique réversible pour la
mesure de Boltzmann :
u (f ) =
1
exp (−βE (f ))
Z
Exercice 11.6.3.
1. Ecrire un programme qui fait évoluer et représente la conguration du champ de
spins
fX
selon l'algorithme de Monte-Carlo. (Utiliser une matrice d'entiers avec
armadillo et la fonction Dessin()).
de
sls
sls d
β.
f
est
M (f ) :=
P
X fX . Représenter l'aimantation
au cours du temps. Quand l'équilibre statistique est atteind, calculer la moyenne
hM i et la variance V = (M − hM i)2 . Représenter hM i (β) et V (β) en fonction
2. L'aimantation de la conguration
Deuxième partie
Projet
78
79
On propose maintenant de réaliser un projet d' expérimentation numérique.
Voir la page
projets.
Troisième partie
Compléments sur le langage C++
80
81
Dans les chapitres suivants nous présentons quelques librairies C++ utiles.
Nous conseillons de consulter la page de Boost qui présente d'autres librairies.
Chapitre 12
Librairie Root : graphisme et autre..
(compléments)
12.1
Graphisme de base 2D
12.1.1 Objets graphiques
Voici quelques exemples de graphisme 2D. Attention à ne pas oublier la ligne d'entête
#include<..> correspondant à l'objet que vous dessinez.
Figure 12.1 Graphisme de base 2D
#include
#include
#include
#include
#include
<TApplication.h>
<TCanvas.h>
<TLine.h>
<TEllipse.h>
<TMarker.h>
82
CHAPITRE 12. LIBRAIRIE ROOT : GRAPHISME ET AUTRE.. (COMPLÉMENTS)83
r
#include <TBox.h>
#include <TText.h>
int main(int argc, char **argv)
{
TApplication theApp("App", &argc, argv);
//.. La fenetre
TCanvas *c=new TCanvas("titre","titre",400,400);
c->Range(0,0,1,1);
//.. une ligne bleue
TLine *l=new TLine(0.1,0.5,0.3,0.9); // x1,y1,x2,y2.
l->SetLineColor(kBlue); // bleu
l->Draw();
//.. Une Ellipse (ou cercle)
TEllipse *e= new TEllipse(0.2,0.3,0.1); // (centre (x,y) et rayon
e->Draw();
//.. Un point
TMarker *m=new TMarker(0.3,0.4,8); // (x,y) et 6 ou 8: forme du
Marker
m->SetMarkerColor(kPink);
m->Draw();
//.. Un rectangle vert
TBox *p=new TBox(0.15,0.6,0.25,0.7); // on precise les coins bas-gauche
(x1,y1) et haut droit (x2,y2) :
p->SetFillColor(kGreen);
p->Draw();
//.. Du texte
TText *texte=new TText(0.2,0.2,"un petit texte"); // position x,y
texte->Draw();
c->Update(); // Montre le dessin
theApp.Run();
return 0;
}
12.1.2 Plusieurs fenêtres
Avec la classe TCanvas qui hérite de TPad.
c2
TCanvas c1("c1","fenetre",400,400); // creation de la fenetre c1
TCanvas c2( "c2","fenetre",400,400); // creation de la fenetre
TMarker *m1=new TMarker(0.3,0.4,8); // un point
CHAPITRE 12. LIBRAIRIE ROOT : GRAPHISME ET AUTRE.. (COMPLÉMENTS)84
TMarker *m2=new TMarker(0.3,0.4,8); // un point
m2->SetMarkerColor(kRed);
c1.cd(); // choix de la fenetre c1 pour le prochain dessin
m1->Draw(); // dessin du point 1
c2.cd(); // choix de la fenetre c2 pour le prochain dessin
m2->Draw(); // dessin du point 2
12.1.3 Un cadre avec des axes gradués : DrawFrame
Avec la classe TPad.
TCanvas *c=new TCanvas("titre","titre",400,400);
double x1(0),y1(0),x2(1),y2(1); // position du cadre
c->DrawFrame(x1,y1,x2,y2);
Figure 12.2 Un cadre seul avec axes gradués.
On peut compléter le dessin du cadre de la façon suivante. On récupère un pointeur
sur un histogramme de la classe TH1, et on a accès aux fonctions membres pour modier
le cadre, par exemple :
#include
#include
#include
#include
<TApplication.h>
<TCanvas.h>
<TEllipse.h>
<TH1F.h>
int main(int argc, char **argv)
{
TApplication theApp("App", &argc, argv);
TCanvas *c=new TCanvas("titre","titre",400,400);
CHAPITRE 12. LIBRAIRIE ROOT : GRAPHISME ET AUTRE.. (COMPLÉMENTS)85
double xmin(0),ymin(0),xmax(5),ymax(5);
double x1(0),y1(0),x2(10),y2(10); // position du cadre
TH1F *h =c->DrawFrame(x1,y1,x2,y2);
TEllipse e(2,3,1);
e.Draw();
h->SetXTitle("x");
h->SetYTitle("y");
h->SetMinimum(-2); // change les bornes en y
h->SetMaximum(8);
h->SetBins(1000,1,5); // change les bornes en x: 1->5
c->Update(); // Montre le dessin
theApp.Run();
return 0;
}
Figure 12.3 Un cadre seul avec axes gradués. On a rajouté un titre aux axes et changé
les bornes.
12.1.4 Un seul axe gradué : TGaxis
Avec la classe TGaxis.
#include <TGaxis.h>
double x1(0),y1(0),x2(1),y2(0); // position de l'axe
double v1(0),v2(1); // graduations extremes
TGaxis axe(x1,y1,x2,y2,v1,v2,510,"");
axe.SetTitle("x"); // met le titre sur l'axe
axe.Draw();
CHAPITRE 12. LIBRAIRIE ROOT : GRAPHISME ET AUTRE.. (COMPLÉMENTS)86
Figure 12.4 un axe seul gradué.
12.2
Histogrammes
Voici quelques exemples d'utilisation des Histogrammes. Pour utiliser ces exemples,
il faut remplacer les parties (C) de l'exemple de la Section 9.2, par le code ci-dessous.
Attention : la ligne
#include <..>
mentionnée chaque fois est à mettre en haut de votre
programme.
12.2.1 Courbe (x,y) avec axes à partir de tableaux de valeurs x[]
y[] : TGraph
Avec la classe TGraph.
#include <TGraph.h>
#include <math.h> // pour utiliser sin(.)
//---- creation de tableaux
int n = 20;
float x[20], y[20];
for (int i=0;i<n;i++) // remplissage
{ x[i] = i*0.1; y[i] = 10*sin(10*x[i]); }
//--- dessin
TGraph gr(n,x,y);
gr.SetMarkerColor(kBlack); gr.SetMarkerStyle(6); // 6:petit cercle
gr.Draw("AC"); // trace un segment entre les points
12.2.2 Histograme 1Dim à partir de tableau y[] : TH1F
Avec la classe TH1F.
#include <TH1.h>
int nx(10); // nombre de points
double xmin(0.0),xmax(1.0); // graduation de l'axe x
TH1F *h1=new TH1F("nom","titre",nx,xmin,xmax);
h1->SetXTitle("x");
CHAPITRE 12. LIBRAIRIE ROOT : GRAPHISME ET AUTRE.. (COMPLÉMENTS)87
Figure 12.5 Courbe (x,y) avec axes à partir de tableaux x[] y[]
h1->SetYTitle("y");
for(int i=1;i<=nx;i++) // remplissage
{
double z=i*i;
h1->SetBinContent(i,z);
}
h1->Draw("L"); // option L: ligne ou P: marker
Figure 12.6 Histograme 1Dim à partir de tableau y[].
12.2.3 Courbe a partir de l'expression d'une formule y=f(x) : TF1
Avec la classe TF1.
#include <TF1.h>
TF1 *f1 = new TF1("f1","sin(x)/x",0,10);
f1->Draw();
CHAPITRE 12. LIBRAIRIE ROOT : GRAPHISME ET AUTRE.. (COMPLÉMENTS)88
12.2.4 Surface 2Dim : z=f(x,y) à partir d'un tableau z[i,j] : TH2F
Avec la classe TH2F.
#include <TH2.h>
int nx(10),ny(10);
double xmin(0),xmax(1),ymin(0),ymax(2);
TH2F *h= new TH2F("h1","titre",nx,xmin,xmax,ny,ymin,ymax);
h->SetXTitle("x");
h->SetYTitle("y");
h->SetStats(kFALSE); // pas de panneau stat
for(int i=1;i<=nx;i++) // remplissage
for(int j=1;j<=ny;j++)
{
double z=i*j;
h->SetCellContent(i,j,z);
}
h->Draw("surf2"); // essayer options: surf1, surf3,surf4,lego
Figure 12.7 Surface 2Dim : z=f(x,y)a partir d'un tableau z[i,j]
12.2.5 Surface 2D a partir de l'expression d'une formule z=f(x,y) :
TF2
Avec la classe TF2.
#include <TF2.h>
TF2 *f2 = new TF2("f2","sin(x)*sin(y)/(x*y)",0,5,0,5);
f2->Draw("surf4");
CHAPITRE 12. LIBRAIRIE ROOT : GRAPHISME ET AUTRE.. (COMPLÉMENTS)89
12.2.6 Crée histogramme 1D par tirage aléatoire et ajustement
par une formule
Remarque 12.2.1. voir http ://root.cern.ch/root/HowtoFit.html
#i n c l u d e <TRandom . h>
#i n c l u d e <T A p p l i c a t i o n . h>
#i n c l u d e <TCanvas . h>
#i n c l u d e <TF1 . h>
#i n c l u d e <TH1 . h>
#i n c l u d e <i o s t r e a m >
using
int
namespace
main ( i n t
std ;
argc ,
char
** a r g v )
{
TApplication
TCanvas
//−−−−−−−
cree
theApp ( " App " , &a r g c ,
* c=new
histo
argv ) ;
TCanvas ( " t i t r e " , " t i t r e " , 4 0 0 , 4 0 0 ) ;
avec
tirage
aleatoire
avec
loi
gaussienne
−−−−−−−−−−−−
d o u b l e xmin=−3,xmax=3;
* h=new TH1D( "TW" , "TW" , n b i n , xmin , xmax ) ;
gRandom−>S e t S e e d ( ) ; / / i n i t i a l i s e s u r l ' h o r l o g e
int
nbin =100;
TH1D
for ( int
i =1; i <=1e 5 ; i ++) // t i r a g e s
{
double
x= gRandom−>Gaus ( 0 , 1 ) ;
//
c ' est
P( x)= e x p ( − 0 . 5 * ( x / s i g m a
CHAPITRE 12. LIBRAIRIE ROOT : GRAPHISME ET AUTRE.. (COMPLÉMENTS)90
h−> F i l l ( x ) ;
}
h−>Draw ( ) ;
//−−−−
calcule
integrale
et
normalise
l ' histogramme
−−−−−−−−−−−
I= h−>I n t e g r a l ( ) ;
double
h−>S c a l e ( 1 / I ) ;
h−>Draw ( ) ;
//−−−
Fit
TF1
l ' histogramme
* f1
avec
une
Gaussienne
−−−−−−−−−−−−−−
= new TF1 ( " f 1 " , " g a u s " ) ;
h−>F i t ( " f 1 " ) ;
c o u t <<"E c a r t
//−−− m o n t r e
t y p e ="<<f 1 −>G e t P a r a m e t e r (2)<< e n d l ;
fenetres
de
commandes
−−−
h−>DrawPanel ( ) ;
h−>F i t P a n e l ( ) ;
//−−−−−−
c−>Update ( ) ;
//−−−−−−
theApp . Run ( ) ;
return
0;
}
12.2.7 Champ de vecteur
/// Ce code dessine un champ de vecteur spécié à deux dimensions.
#include <TApplication.h>
#include <TCanvas.h>
#include <TArrow.h>
CHAPITRE 12. LIBRAIRIE ROOT : GRAPHISME ET AUTRE.. (COMPLÉMENTS)91
Figure 12.8 Champ de vecteur
#include <TLine.h>
#include <math.h>
#include <iostream>
using namespace std;
///--- definition du champ de vecteur
void vect(double x, double y, double &vx, double &vy)
{
vx = x ;
vy= -y;
}
//---------------------------------int main(int argc, char **argv)
{
TApplication theApp("App", &argc, argv);
//-----------double xmin=-1, xmax=1, ymin=-1,ymax=1; // coordonnées
TCanvas *c=new TCanvas("c","c",500,500); // fenetre
c->DrawFrame(xmin,ymin,xmax,ymax);
//-----------------double N=20; // nbre d'echantillons
double dx=(xmax-xmin)/N;
double dy=(ymax-ymin)/N;
for (int i=0; i<N; i++)
for (int j=0; j<N; j++)
{
double x=xmin + dx*i;
double y=ymin + dy*j;
double vx,vy;
vect(x,y,vx,vy);// calcul les coordonnées du champ de vecteur
vx *= dx *1; // normalise
CHAPITRE 12. LIBRAIRIE ROOT : GRAPHISME ET AUTRE.. (COMPLÉMENTS)92
vy *= dy *1;
double n=sqrt(vx*vx+vy*vy); // norme
TArrow *t= new TArrow(x,y,x+vx,y+vy,dx*n);
// TLine *t= new TLine(x,y,x+vx,y+vy);
t->Draw();
} // for i,j
//-donne la main a l'utilisateur (A)
theApp.Run();
return 0;
}
12.3
Utilisation de la souris
Fenetre (héritée de la
Voici un code complet qui denit une classe que l'on appelle
classe TCanvas). La nouveauté est la fonction
ExecuteEvent()
qui est appelée chaque
fois que la souris bouge devant la fenetre. Lorsque le programme execute l'instruction
theApp.Run();
il est en attente d'évènements provenant du clavier ou de la souris. Si un
tel évènement se produit, il appelle la fonction void ExecuteEvent(Int_t event, Int_t
px, Int_t py), en passant les paramètres de l'évènement. C'est donc dans cette fonction
que vous allez rajouter du code ou des appels à d'autres fonctions.
#i n c l u d e <i o s t r e a m >
using
namespace
std ;
#i n c l u d e <TCanvas . h>
#i n c l u d e <TMarker . h>
#i n c l u d e <T A p p l i c a t i o n . h>
#i n c l u d e < T E l l i p s e . h>
//====Ma C l a s s e
class
Fenetre :
F e n e t r e================================
public
TCanvas
{
public :
//
constructeur
. . . . . .
F e n e t r e ( Text_t * name ,
{
//−
void
Cette
Text_t *
title ,
I n t _ t ww,
I n t _ t wh )
:
TCanvas ( nam
}
fonction
est
ExecuteEvent ( Int_t
appelle
event ,
si
la
Int_t
souris
px ,
s ' agitte .
Int_t
py )
{
//
affiche
c o u t <<"
//
l ' etat
position :
convertit
la
de
la
souris
px="<<px<<" py="<<py<<" c o d e
position
en
pixel
px , py
en
du
b o u t o n="<<e
coordonnees
(x , y)
CHAPITRE 12. LIBRAIRIE ROOT : GRAPHISME ET AUTRE.. (COMPLÉMENTS)93
double
x = gPad−>A b s P i x e l t o X ( px ) ;
double
y = gPad−>A b s P i x e l t o Y ( py ) ;
//
si
bouton
gauche
appuye
dessine
un
point
i f ( e v e n t ==1)
{
*m=new
TMarker
TMarker ( x , y , 8 ) ;
m−>Draw ( ) ;
Update ( ) ;
}
//
Ev e n t
if
( e v e n t==k K e y P r e s s )
clavier
//
touche
appuyée
{
c o u t <<"t o u c h e
switch
px="<<px<<e n d l ;
( px )
{
//
change
case
de
mode
( t e m p é r é <−>
juste )
'm' :
c o u t <<"Touche m a p p u y é e"<< e n d l ;
break ;
};
}
//
if
//
switch
px
event
}
};
///−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
int
main ( i n t
argc ,
char
** a r g v )
{
TApplication
Fenetre
double
*c
theApp ( " App " , &a r g c ,
= new
Fenetre (" c " ,
theApp . Run ( ) ;
return
400 ,400);
//
crée
une
fenet
xmin ( 0 ) , ymin ( 0 ) , xmax ( 5 ) , ymax ( 5 ) ;
c−>Range ( xmin , ymin , xmax , ymax ) ;
}
argv ) ;
" fenetre1 " ,
0;
//
coordonnees
de
la
fenetre
( optionne
CHAPITRE 12. LIBRAIRIE ROOT : GRAPHISME ET AUTRE.. (COMPLÉMENTS)94
12.3.0.1
Pour gérer la souris lorsque le programme calcule
Si votre programme est lancé dans un calcul interminable, et que vous vouliez cependant
modier des paramètres par l'intermédiaire de la souris ou d'un commande clavier, il faut
dans votre boucle mettre la commande suivante :
gSystem->ProcessEvents(); // avec #include <TSystem>
Ainsi si la souris est actionnée, dans l'exemple ci-dessus, la fonction
Fenetre::ExecuteEvent
sera appelée.
12.4
Comment créer un chier gif animé.
Voici un exemple complet qui permet de créer un chier lm.gif animé à partir
d'images créées par root.
Travail préalable :
1. Fichiers à télécharger et sauver dans son répertoire : bib_fred.h et bib_fred.cc.
2. Il faut avoir installé le logiciel
gifsicle,
#include <TApplication.h>
#include <TCanvas.h>
#include <TEllipse.h>
#include "bib_fred.h"
///-----------------------------------int main(int argc, char **argv)
{
TApplication theApp("App", &argc, argv);
TCanvas *c= new TCanvas("c","fenetre",400,400);
double xmin(-1),ymin(-1),xmax(2),ymax(2);
c->Range(xmin,ymin,xmax,ymax);
int periode = 10; // temps entre chaque image en 1/100e sec.
int imin=1, imax=30;
for(int i =imin; i<=imax ; i++)
{
double x = i/(double)imax;
TEllipse e(x,0.5,1);
e.Draw(); // dessine l'ellipse
c->Update();
Animation(c,i,imin,imax,"film",periode); // -> crée fichier film.gif
}
CHAPITRE 12. LIBRAIRIE ROOT : GRAPHISME ET AUTRE.. (COMPLÉMENTS)95
theApp.Run();
return 0;
}
Ensuite pour visualiser le lm vous pouvez utiliser la commande
12.5
firefox film.gif.
Boutons de commandes (Widgets)
Documentation
Exemple très complet qui montre toutes les possibilités et comment le faire : guitest.cc à copier/coller. (attention, il y a une erreur : remplacer (char **) par (const
char **)).
Voici un exemple plus simple, à copier/coller, donnant :
/ * ! ===========================
* Exemple s i m p l e d ' une f e n e t r e de commande .
* I l y a deux c l a s s e s : l a c l a s s e Com q u i e s t l a f e n e t r e
* e t q u i p e r m e t d e commander une a u t r e c l a s s e A
* Chaque c l a s s e a un l i e n v e r s l ' a u t r e a f i n d e p o u v o i r
*/
#i n c l u d e <TCanvas . h>
#i n c l u d e <TGFrame . h>
de
commande
communiquer .
CHAPITRE 12. LIBRAIRIE ROOT : GRAPHISME ET AUTRE.. (COMPLÉMENTS)96
#i n c l u d e <TGButton . h>
#i n c l u d e <TGComboBox . h>
#i n c l u d e <TGTextEntry . h>
#i n c l u d e <TGLabel . h>
#i n c l u d e <T G P r o g r e s s B a r . h>
#i n c l u d e <TPad . h>
#i n c l u d e <TFrame . h>
#i n c l u d e <TH1F . h>
#i n c l u d e <TSystem . h>
#i n c l u d e <TGNumberEntry . h>
#i n c l u d e <T G S l i d e r . h>
#i n c l u d e <T G D o u b l e S l i d e r . h>
#i n c l u d e <T G T r i p l e S l i d e r . h>
#i n c l u d e <TGMenu . h>
#i n c l u d e <T A p p l i c a t i o n . h>
#i n c l u d e <TRootEmbeddedCanvas . h>
#i n c l u d e <t h r e a d >
#i n c l u d e <c h r o n o >
#i n c l u d e <i o s t r e a m >
using
namespace
class
A;
//
std ;
classe
definie
/*!==========f e n e t r e
* Description
*/
class
Com :
de
public
la
de
après
commandes ========================
classe
Com
TGMainFrame
//
( derivee
d
une
fenetre
la
fenetre
{
public
:
*fMB ; // b a r r e de menu
* fM ; // menu
TGTextButton
* fB ; // b o u t o n
TGTextEntry
* fT ; // z o n e t e x t e
TGLabel
* f L ; // t e x t e f i x e e c r i t
TGComboBox * fC ;
//
l i s t e deroulante
TGCheckButton * fCB ;
TGMenuBar
TGPopupMenu
TGHProgressBar
TGNumberEntry
TGHSlider
* fS ;
* fH ;
* fNE ;
//
barre
de
progres
sur
GUI )
CHAPITRE 12. LIBRAIRIE ROOT : GRAPHISME ET AUTRE.. (COMPLÉMENTS)97
* fS2 ;
TGTripleHSlider * fS3 ;
TRootEmbeddedCanvas * fEC ;
TGDoubleVSlider
//
fenetre
//−−−−−−−−−−−−−−−−−−−
* a ; / / l i e n v e r s un o b j e t a
//−−−−−−−−−−−−−−−−−−−−−
Com(A * a_i ) ;
// c o n s t r u c t e u r
A
~Com ( ) ;
Bool_t
//
d ' une
classe
A
destructeur
P r o c e s s M e s s a g e ( Long_t msg ,
Long_t
parm1 ,
Long_t ) ;
//
fonction
appe
//=========================================
//
met
void
a
jour
les
valeurs
Met_a_jour ( ) ;
//
de
la
fonction
fenetre
qui
met à jour
les
valeurs
de
la
fenetre
this_thread : : s l e e p _ f o r ( chrono : : m i l l i s e c o n d s
{100});
//
attend
};
/*!===============
*
Une
classe
pour
effectuer
un
calcul
...
*/
class
A
{
public :
double
x;
//−−−−−−−−−−−−−
void
Calcul ()
{
c o u t <<" c a l c u l : " ;
f o r ( x =1;
x <=50; x++)
{
0.5
ms//
attente ,
com−>Met_a_jour ( ) ;
c o u t <<x<<","<< f l u s h ;
}
c o u t <<e n d l ;
}
//−−−−−−−−−−−−
Com
* com ;
//
pointeur
vers
l ' objet
fenetre
de
control
CHAPITRE 12. LIBRAIRIE ROOT : GRAPHISME ET AUTRE.. (COMPLÉMENTS)98
void
Lance_commandes ( )
{
TApplication
com=
l ' objet
theApp ( " App " , NULL, NULL ) ;
new Com( t h i s ) ;
//
cree
fenetre
de
control
et
l ' associe
a
present
theApp . Run ( ) ;
//−−−−donne
la
main
a
l ' utilisateur
−−−
}
};
/*!=====================
Constructeur
*/
Com : : Com(A
* a_i )
:
TGMainFrame ( g C l i e n t −>GetRoot ( ) , 4 0 0 , 3 5 0 )
//
taille
de
la
f
{
a=a_i ;
//
initialise
le
lien
vers
l ' objet
à controller
( sera
utile
pour
a−>com= t h i s ;
//−−−−−−−−−−−−−−−−
SetWindowName ( " F e n e t r e
//−−−−−−−−− Menu
de
commandes " ) ;
// nom d e
la
fenetre
−−−−−−−−−−
fMB = new TGMenuBar ( t h i s ,
AddFrame ( fMB ,
new
35 ,
50 ,
kHorizontalFrame ) ;
TGLayoutHints ( kLHintsTop
|
kLHintsExpandX ,
2,
2,
2,
fM=fMB−>AddPopup("&Menu " ) ;
fM−>AddEntry ("& O p t i o n
1" ,3);
fM−>A d d S e p a r a t o r ( ) ;
fM−>AddEntry ( "O&p t i o n
2" ,4);
fM−>A s s o c i a t e ( t h i s ) ;
//−−−−− b o u t o n s
−−−−−−−−−−−−−−−−−−−−−−−−
fB= new TGTextButton ( t h i s , " S&t a r t " , 2 ) ;
code
message
//
cree
bouton ,
lettre
2
fB−>Move ( 1 0 0 , 1 0 ) ;
//
position
fB−>S e t T o o l T i p T e x t ( " Demarre
un
x,y
calcul
...");
//
texte
d ' aide
fB−>A s s o c i a t e ( t h i s ) ;
fB−>R e s i z e ( 8 0 ,
fB−>G e t D e f a u l t H e i g h t ( ) ) ;
/ / . . Label = t e x t e
fixe . . . . . . . . . . . . . . . . .
//
taille
(x , y)
t ,
et
5))
CHAPITRE 12. LIBRAIRIE ROOT : GRAPHISME ET AUTRE.. (COMPLÉMENTS)99
f L= new
TGLabel ( t h i s ,
f L −>Move ( 1 0 , 4 0 ) ;
/ / . . . Zone
string
de
//
texte
new
TGString ( " L a b e l = Texte
position
x,y
. . . . . . . . . . . . . . . .
t e x t =" t o t o " ;
fT = new TGTextEntry ( t h i s , t e x t . c _ s t r ( ) , 1 ) ;
fT−>Move ( 1 0 , 7 0 ) ;
//
position
// c o d e
1
x,y
fT−>R e s i z e ( 4 0 , fT−>G e t D e f a u l t H e i g h t ( ) ) ;
char
fixe "));
//
taille
(x , y)
buf [100]=" t e x t e " ;
fT−>A s s o c i a t e ( t h i s ) ;
//−−−−−−−
Liste
−−−−
Combo
fC = new TGComboBox ( t h i s ,
88);
fC−>AddEntry ( " e n t r e e
1" ,1);
fC−>AddEntry ( " e n t r e e
2" ,2);
fC−>R e s i z e ( 1 5 0 ,
fC−>S e l e c t ( 2 ) ;
AddFrame ( fC ,
20);//
//
new
fC−>Move ( 1 0 , 1 0 0 ) ;
//−−−−− P r o g r e s s
fH = new
taille
selection
a
(x , y)
priori
TGLayoutHints ( kLHintsTop
//
|
kLHintsLeft , 5 , 5 , 5 , 5 ) ) ;
//
mis
position
B a r s −−−−−−−−−−−−−−
TGHProgressBar ( t h i s , T G P r o g r e s s B a r : : kFancy , 3 0 0 ) ;
fH−>S h o w P o s i t i o n ( ) ;
fH−>S e t B a r C o l o r ( " g r e e n " ) ;
fH−>Move ( 1 0 , 1 3 0 ) ;
//
position
fH−>S e t R a n g e ( 0 . 0 , 5 0 . ) ;
fH−>S e t P o s i t i o n ( 1 0 . ) ;
//
//
min ,
max
position
fH−>S h o w P o s i t i o n (kTRUE, kFALSE , " v a l e u r :
//−−−−−−−−−−−
f S = new
Sliders
%.0 f " ) ;
−−−−−−−−−−−
TGHSlider ( t h i s , 1 5 0 , k S l i d e r 1 | k S c a l e D o w n R i g h t , 2 ) ;
//
simple ,
f S −>S e t R a n g e ( 0 , 5 0 ) ;
f S −>S e t P o s i t i o n ( 3 9 ) ;
f S −>Move ( 1 0 , 2 0 0 ) ;
f S 2 = new
TGDoubleVSlider ( t h i s , 1 0 0 , kDoubleScaleNo , 3 ) ;
f S 2 −>S e t R a n g e (
−10 ,10);
f S 2 −>Move ( 2 0 0 , 2 0 0 ) ;
// d o u b l e
I d =2
CHAPITRE 12. LIBRAIRIE ROOT : GRAPHISME ET AUTRE.. (COMPLÉMENTS)100
f S 3 = new
TGTripleHSlider ( t h i s , 1 0 0 , kDoubleScaleBoth , 4 ,
// t r i p l e
kHorizontalFrame ) ;
f S 3 −>S e t C o n s t r a i n e d (kTRUE ) ;
f S 3 −>S e t R a n g e ( 0 , 1 0 ) ;
f S 3 −>S e t P o s i t i o n ( 2 , 3 ) ;
−>S e t P o i n t e r P o s i t i o n
f S 3 −>Move ( 2 5 0 , 2 0 0 ) ;
fS3
//−−−−−−−− e n t r e e
double
numerique
(2.5);
−−−−−−
val =3.1415;
fNE = new TGNumberEntry ( t h i s ,
val ,
5,
4 0 0 , ( TGNumberFormat : : E S t y l e )
2);
//
fNE−>A s s o c i a t e ( t h i s ) ;
fNE−>Move ( 1 0 , 2 5 0 ) ;
//−−−−− F e n e t r e
Canvas
//
position
−−−−−−−−−−−−−−
fEC = new TRootEmbeddedCanvas ( " e c 1 " ,
AddFrame ( fEC ,
new
this ,
TGLayoutHints ( kLHintsTop
200 ,
|
100);
kLHintsLeft
kLHintsExpandY ,
5,
5,
5,
|
5));
fEC−>Move ( 2 0 0 , 1 0 ) ;
TCanvas
*c
= fEC−>GetCanvas ( ) ;
c−>DrawFrame ( 0 , 0 , 1 , 1 ) ;
c−>Update ( ) ;
//
c−>M o d i f i e d ( ) ;
/ / . . . . Check
Button
fCB= new TGCheckButton ( t h i s , " : b o u t o n
fCB−>S e t T o o l T i p T e x t ( " t e x t e
d ' aide
check " , 4 2 0 ) ;
pour
bouton
check " ) ;
fCB−>A s s o c i a t e ( t h i s ) ;
fCB−>R e s i z e ( 1 2 0 ,
fCB−>G e t D e f a u l t H e i g h t ( ) ) ;
fCB−>Move ( 1 0 , 2 8 0 ) ;
//−−−−−−−−−−−−
MapSubwindows ( ) ;
MapWindow ( ) ;
}
//
//
dessin
dessin
de
des
la
objets
fenetre
( bouttons
,...)
kLHintsExpandX
//
les
opti
CHAPITRE 12. LIBRAIRIE ROOT : GRAPHISME ET AUTRE.. (COMPLÉMENTS)101
/*!====================================
*
*/
Destructeur
Com : : ~ Com ( )
{
delete
fB ;
delete
fT ;
delete
fH ;
delete
fC ;
}
/*!====================================
* Fonction appelee l o r s q u e l '
* −> I c i on g à ¨ r e l e s a c t i o n s
*/
utilisateur
Bool_t Com : : P r o c e s s M e s s a g e ( Long_t msg ,
agit
Long_t
p1 ,
sur
la
Long_t
fenetre
de
p2 )
{
i n t M = GET_MSG( msg ) ,
c o u t <<" M = "<<M<<"
switch
S=GET_SUBMSG( msg ) ;
S = "<<S<<"
p1="<<p1<<"
(M)
{
case
1:
switch
(S)
{
case
3:
s w i t c h ( p1 )
{
case
2:
c o u t <<"b o u t o n 2"<< e n d l ;
a−>C a l c u l ( ) ;
p2="<<p2<<e n d l ;
commandes
CHAPITRE 12. LIBRAIRIE ROOT : GRAPHISME ET AUTRE.. (COMPLÉMENTS)102
break ;
}
break ;
case
4:
i f ( p1 ==420)
{
c o u t <<"b o u t o n
check
..
"<< e n d l ;
i f ( fCB−>IsOn ( ) )
c o u t <<"
on."<< e n d l ;
c o u t <<"
o f f ."<< e n d l ;
else
}
break ;
case
7:
c o u t <<"combo
choisi :
"<<fC−>G e t S e l e c t e d ()<< e n d l ;
break ;
}
case
4:
switch
(S)
{
case
1:
c o u t <<" t e x t e
switch
c h a n g e"<< e n d l ;
( p1 )
{
case
1:
c o u t <<"Le
nouveau
texte
est :
nouveau
chiffre
"<<((fT−>G e t B u f f e r ()) − > G e t S t r i n g
break ;
case
400:
c o u t <<"Le
break ;
}
break ;
}
break ;
default :
break ;
}
est :
"<<((fNE−>GetNumber ())) < < e n d l ;
CHAPITRE 12. LIBRAIRIE ROOT : GRAPHISME ET AUTRE.. (COMPLÉMENTS)103
Met_a_jour ( ) ;
r e t u r n kTRUE ;
}
/ * ! =========================================
* F o n c t i o n a p p e l à © e p a r une f o n c t i o n e x t e r n e
* p o u r m e t t r e a j o u r l e s v a l e u r s de l a f e n e t r e
*/
void
,
Com : : Met_a_jour ( )
{
fH−>S e t P o s i t i o n ( a−>x ) ;
//
//
progress
Bar
g C l i e n t −>NeedRedraw ( fH ) ;
gSystem −>P r o c e s s E v e n t s ( ) ;
//
handle
GUI
events
}
/*!===Main================================================
*/
int
main ( i n t
argc ,
char
** a r g v )
{
* a=new A ;
a−>Lance_commandes ( ) ;
A
return
0;
}
12.6
Autres trucs
12.6.0.1
Nombre aléatoire avec Root
Voir TRandom.
#include <TRandom.h>
gRandom->SetSeed(0); // initialise le hasard sur l'horloge
double x= gRandom->Uniform(2); // nombre aléatoire dans l'intervalle
(0,2)
12.6.0.2
Options générales de Root
avec
#include <TROOT.h>
gROOT->SetBatch(kTRUE); // mode Batch cad sans sortie d'ecran (+ rapide)
gROOT->SetStyle("Plain"); // style standard
gPad->SetEditable(0); // rend les dessins non modifiables
CHAPITRE 12. LIBRAIRIE ROOT : GRAPHISME ET AUTRE.. (COMPLÉMENTS)104
gPad->SetEditable(1); // rend les dessins modifiables
TCanvas *c=h->GetCanvas(); //Pour obtenir la fenetre TCanvas
histogramme TH1 *h
gStyle->SetPadGridX(kTRUE); // grilles en X,Y
gStyle->SetPadGridY(kTRUE);
à partir d'un
Chapitre 13
Mesure du temps
On présente ici la classe chrono.
Mais on peut aussi conseiller la classe Boost.Date_Time.
13.1
13.1.0.1
Mesure de la date et du temps écoulé
Mesure très précise du temps :
#include <chrono>
using namespace std::chrono;
#include <iostream>
using namespace std;
int main()
{
auto t1=high_resolution_clock::now(); // mesure du "time point 1"
//....... instruction qui prend du temps .....
for(int i=0;i<1e5;i++)
cout<<" \t"<<i<<flush;
//.............
auto t2=high_resolution_clock::now(); // mesure du "time point 2"
auto dt1=duration_cast<nanoseconds>(t2-t1).count(); // calcul de
la durée en ns
auto dt2=duration_cast<milliseconds>(t2-t1).count(); // calcul de
la durée en ms
105
CHAPITRE 13.
MESURE DU TEMPS
106
auto dt3=duration_cast<duration<double>>(t2-t1).count(); // calcul
de la durée en s
cout<<"durée: "<<dt1<< "ns"<<" = "<<dt2<< "ms"<<" = "<<dt3<<" s."<<endl;
}
Ache :
0 1 2 3 ...99996 99997 99998 99999
durée: 491264417ns = 491ms = 0.491264 s.
13.1.0.2
Achage de la date
Au programme précédent, si on rajoute :
auto tt=system_clock::to_time_t(t1); // convertit au type time_t
cout << "Maintenant c'est: " << ctime(&tt);
On obtient :
Maintenant c'est: Mon Aug 25 19:37:30 2014
13.2
Attendre
13.2.1 Attendre une certaine durée
#include <thread>
#include <chrono>
using namespace std::chrono;
using namespace std::this_thread;
sleep_for(seconds{2}); // attend 2 secondes
sleep_for(milliseconds{3}); // attend 3 millisecondes
13.2.2 Attendre jusqu'à une certaine date
#include <iostream>
#include <thread>
#include <chrono>
using namespace std::chrono;
using namespace std::this_thread;
using namespace std;
CHAPITRE 13.
MESURE DU TEMPS
107
int main ()
{
auto t1 = high_resolution_clock::now(); // date actuelle
cout<<" J'attends 3 secondes"<<endl;
auto t2 = t1 + seconds {3}; // date future
sleep_until(t2); // attente
auto t3 = high_resolution_clock::now(); // date mesurée
auto dt = duration_cast<duration<double>>(t3-t1).count(); // mesure
dt = t3-t1
cout<<"La durée a été dt = "<<dt<<" s."<<endl;
return 0;
}
On obtient :
J'attends 3 secondes
La durée a été dt = 3.00016 s.
Chapitre 14
Ecrire et lire des données en binaires
sur un chier
14.1
Fichiers en format texte
Dans les leçons précédentes, nous avons vu comment écrire des données numériques
dans un chier. Ces données étaient écrites sous forme de chires (ex : 17) et sont lisibles
depuis un éditeur. On dit qu'il s'agit d'une écriture en
format texte ou format ASCII ;
car un objet numérique est représentée en base dix par un ou plusieurs chires dans le
chier.
Or dans la mémoire de l'ordinateur une objet numérique (ex :
short int i=17
stockée en binaire (i.e. base 2) sous forme d'une suite de 0 et 1 (appelés des
séquence de 8 bits est appellée
Par exemple la objet
) est
bits. Une
un octet).
short int i
contenant 17 est stockée en mémoire en base 2 par
une suite de 16 bits, soit 2 octets :
0000 0000 0001 0001
Lorsque l'on écrit dans un chier en format texte (par la commande
fichier <<i <<endl;
) l'ordinateur convertit le nombre en base dix et écrit la suite des chires, ce qui donne
17. (Il faut savoir que chaque caractère est codé par un octet en mémoire, selon le codage
ASCII).
L'avantage du format texte est que l'on peut visualiser le contenu du chier à l'aide
d'un éditeur comme emacs.
Rappels sur le code ASCII :
cout <<(int)'A' << endl; // --affiche 65 qui est le code ASCII
de A
cout <<(char)(65) <<endl; //--affiche A qui a pour code ASCII:
65
108
CHAPITRE 14. ECRIRE ET LIRE DES DONNÉES EN BINAIRES SUR UN FICHIER
14.2
109
Fichiers en format binaire
Il y a cependant la possibilité d'écrire les données dans un chier de façon brute et
compacte, en format binaire.
L'avantage est la rapidité, et l'économie de place mémoire. C'est aussi utile (mais non
nécéssaire) si l'on veut accéder en lecture ou écriture à un endroit quelconque du chier.
14.2.0.1
Exemple
Le programme suivant montre comment écrire et lire en format binaire, et comment se
positionner dans un chier.
#include <stdlib> // -----fichiers d'entetes ---#include <iostream>
using namespace std;
#include <fstream>
#include <iomanip>
main()
{
double x=3.14;
int i=17;
int nx=sizeof(double); //nombre d'octets occupés par une objet de
classe double
int ni=sizeof(int); //nombre d'octets occupés par une objet de classe
int
cout << "nx= " <<nx << " ni= " <<ni <<endl;
//===============================
ofstream sortie( "donnees.dat "); //ouverture d un fichier en ecriture
-sortie.write(&x, nx); //--- ecriture de x --sortie.write(&i, ni); //--- ecriture de i --sortie.close(); //- fermeture du fichier -//===============================
ifstream entree( "donnees.dat "); //-- ouverture d un fichier en
lecture -entree.read(&x, nx); //--- lecture de x --entree.read(&i, ni); //--- lecture de i --cout << " x= " <<x << " i= " <<i<<endl;
entree.seekg(nx); // on se positionne après nx octets en partant
du début, soit après la valeur de x
entree.read(&i, ni); //--- lecture de i --cout << " i= " <<i <<endl;
CHAPITRE 14. ECRIRE ET LIRE DES DONNÉES EN BINAIRES SUR UN FICHIER
110
int pos=entree.tellg(); // donne la position du curseur dans le
fichier (en octets)
cout << "la position du curseur est maintenant: " <<pos << endl;
}
14.2.0.2
Remarques
1. La fonction
sizeof(double)
permet de connaitre la taille en octet occupée par le
classe double en mémoire. Voici le taille des principales classes de base :
char short int int long float double Complex
Classe
Taille (octets)
1
2
4
4
4
1. Pour se positionner en lecture, il faut utiliser la fonction
en écriture, il faut utiliser la fonction
seekp.
8
16
seekg. Pour se positionner
Le premier argument de ces fonctions
indique le déplacement relatif du pointeur (en octets). Le deuxième argument (facultatif ) indique l'origine du déplacement. Ce peut-être :
le début du chier :
ios::beg
la position actuelle du curseur :
ios::end
entree.seekg(nx,ios::beg);
la n du chier
exemple :
ios::cur
Chapitre 15
Quelques commandes utiles
15.0.1 La commande system
La commande system() permet de lancer d'autres commandes ou programmes sur l'ordinateur. Par exemple pour lancer la commande ls qui ache la liste des chiers :
#include <iostream> // pour cout
using namespace std;
#include <stdlib.h> // pour system
//----Fonction principale ---------------int main()
{
system("ls"); // execute la commande ls. (affiche les fichiers)
return 0;
}
111
Chapitre 16
Intégrer des équations diérentielles
ordinaires (EDO) avec la librairie
ODEINT
On propose d'utiliser la librairie
Introduction :
Une équation diérentielle ordinaire (E.D.O.) est une équation de la
forme :
dX
= F (X, t)
dt
t ∈ R, et F : Rn × R → Rn est
une fonction connue. On connait X (0) et F l'on cherche X (t) pour d'autres valeurs de
t ∈ R. Le théorème de Cauchy Lipchitz garantit l'existence et l'unicité de X (t) mais on
où
X (t) ∈ Rn
odeint de boost. Voir aussi la documentation ODEINT.
est un vecteur qui dépend du temps
a rarement une expression explicite de la solution. L'ordinateur est utile pour trouver des
valeurs approchées de
X (t).
Exemple
Pour intégrer avec la methode runge_kutta_dopri5 with standard error bounds
3
10-6 for the steps, le champ de vecteur dans X (t) = (x0 (t) , x1 (t) , x2 (t)) ∈ R du système
de Lorenz déni par :
dx0
= σ · (x1 − x0 )
dt
dx1
= Rx0 − x1 − x0 x2
dt
dx2
= −bx2 + x0 x1
dt
σ = 10, R = 28, b = 8./3.,
#include <iostream>
#include <boost/array.hpp>
#include <boost/numeric/odeint.hpp>
avec les paramètres :
112
on écrit :
CHAPITRE 16. INTÉGRER DES ÉQUATIONS DIFFÉRENTIELLES ORDINAIRES (EDO) AVEC LA L
using namespace std;
using namespace boost::numeric::odeint;
const double sigma = 10.0;
const double R = 28.0;
const double b = 8.0 / 3.0;
typedef boost::array< double , 3 > state_type; // le chiffre 3 car il y a
3 variables
///- definition de la fonction à intégrer
void lorenz( const state_type &x , state_type &dxdt , double t )
{
dxdt[0] = sigma * ( x[1] - x[0] );
dxdt[1] = R * x[0] - x[1] - x[0] * x[2];
dxdt[2] = -b * x[2] + x[0] * x[1];
}
///
int main(int argc, char **argv)
{
state_type x = { 10.0 , 1.0 , 1.0 }; // conditions initiales
double t1 =0 ,t2=25; // temps initial et final
double dt =0.1; //pas de temps
for(double t=t1; t<=t2; t=t+dt)
{
integrate( lorenz , x , t, t+dt, dt); // intégre entre t et t+dt
cout << t << '\t' << x[0] << '\t' << x[1] << '\t' << x[2] << endl;
}
}
Remarque 16.0.1. Pour plus d'options ou autres méthodes d'intégration voir la documentation.
Exercice 16.0.2. Modier le code ci-dessus pour dessiner la trajectoire dans le plan
avec un point tous les pas de temps
dt = 0.02
et
t = 0 → 100
x1 , x2
et obtenir l'image suivante :
CHAPITRE 16. INTÉGRER DES ÉQUATIONS DIFFÉRENTIELLES ORDINAIRES (EDO) AVEC LA L
Solution :
#i n c l u d e <i o s t r e a m >
#i n c l u d e <b o o s t / a r r a y . hpp>
#i n c l u d e <b o o s t / n u m e r i c / o d e i n t . hpp>
#i n c l u d e <T A p p l i c a t i o n . h>
#i n c l u d e <TCanvas . h>
#i n c l u d e <TMarker . h>
#i n c l u d e <TH1F . h>
CHAPITRE 16. INTÉGRER DES ÉQUATIONS DIFFÉRENTIELLES ORDINAIRES (EDO) AVEC LA L
using
namespace
std ;
using
namespace
boost : : numeric : : o d e i n t ;
const
double
const
double R =
const
double
typedef
sigma =
10.0;
28.0;
b = 8.0
/
3.0;
boost : : array< double
/// −
definition
void
lorenz (
de
la
const
,
3 > state_type ;
fonction
à
//
le
chiffre
3
car
double
t
)
il
y
a
3
v
intégrer
s t a t e _ t y p e &x
,
s t a t e _ t y p e &d x d t
,
{
dxdt [ 0 ]
= sigma
dxdt [ 1 ]
= R
dxdt [ 2 ]
=
*
*
(
x[0]
− x[0] );
− x[0] *
x[0] * x [1];
x[1]
−
x[1]
−b *
x[2]
+
argc ,
char
** a r g v )
x[2];
}
///
int
main ( i n t
{
TApplication
TCanvas
double
TH1F
*c
theApp ( " App " , &a r g c ,
= new TCanvas (
argv ) ;
"c " ," f e n e t r e " ,500 ,500);
//
on
precise
* h=c−>DrawFrame ( xmin , ymin , xmax , ymax ) ;
//
coordonnees
de
h−>S e t Y T i t l e ( " x2 " ) ;
c p t =0;
state_type
double
taille
xmin ( − 3 0 ) , ymin ( 0 ) , xmax ( 3 0 ) , ymax ( 5 0 ) ;
h−>S e t X T i t l e ( " x1 " ) ;
int
la
t1
x = {
=0
10.0
, t 2 =100;
,
//
1.0
,
temps
1.0
};
//
initial
conditions
et
final
initiales
la
fenetre
(
CHAPITRE 16. INTÉGRER DES ÉQUATIONS DIFFÉRENTIELLES ORDINAIRES (EDO) AVEC LA L
double
dt
=0.002;
f o r ( double
t=t 1 ;
// p a s
t<=t 2 ;
de
temps
t=t+d t )
{
integrate (
//
lorenz
c o u t << t <<
,
x
,
t ,
t+dt ,
' \ t ' << x [ 0 ] <<
dt ) ;
TMarker
* p=new
TMarker
TMarker ( x [ 1 ] , x [ 2 ] , 6 ) ;
p2 ( x [ 1 ] , x [ 2 ] , 8 ) ;
p2 . S e t M a r k e r C o l o r ( kRed ) ;
p2 . S e t M a r k e r S i z e ( 2 ) ;
p2 . Draw ( ) ;
i f ( c p t %10==0)
c−>Update ( ) ;
}
c−>Update ( ) ;
theApp . Run ( ) ;
return
}
0;
intégre
' \ t ' << x [ 1 ] <<
c p t ++;
p−>Draw ( ) ;
//
entre
t
et
t+d t
' \ t ' << x [ 2 ] << e n d l
Chapitre 17
Lecture et écriture d'un chier son de
format WAV
17.0.0.1
Un
Introduction
signal audio est une fonction
de pression sonores en fonction du
Un
t ∈ R → s (t) ∈ R
temps t.
qui représente les variations
chier audio au format WAV représente le signal audio pour des valeurs dis-
crètes du temps :
tj = j.δt,
j = 1, 2, . . .
−1
avec un pas de temps δt
appelée fréquence d'échantillonage. Les valeurs standard
−1
−1
sont : δt
= 8000Hz, δt = 44100Hz, δt−1 = 48000Hz, etc. Chaque valeur s (tj )
est codée sur 8 bits ou 16 bits. Le nombre de caneaux 1 ou 2 indique si le chier peut
contient un (mode mono) ou deux signaux (mode stéréo). Le chier WAV contient
les valeurs
s (tj ) à la suite, mais au début du chier il y a les paramètres du codage.
Regarder le code source pour plus d'informations.
17.0.0.2
Travail préalable pour le projet C++
1. Fichiers à télécharger et sauver dans son répertoire : bib_fred.h et bib_fred.cc.
2. Dans codeblocks, il faut ajouter ces chiers au projet en faisant dans le menu :
Projet/add_les ...
3. Il faut aussi
rajouter
-larmadillo
dans les options de compilation (pour code-
blocks c'est dans Setting/Compiler/LinkerSettings/OtherLinkerOptions)
17.0.1 Programme pour écrire un chier WAV :
Pour tester, télécharger ce chier Voyelles_Par_Malik.wav et sauvegarder le dans le
répertoire de votre projet.
Le programme suivant permet
117
CHAPITRE 17. LECTURE ET ÉCRITURE D'UN FICHIER SON DE FORMAT WAV118
de lire et acher les paramètres du codage audio de ce chier sonore
s (tj ) ∈ R
(nombre de canaux, taux d'echantillonage etc..),
de mettre les amplitudes
s (tj )
dans un vecteur de nombres
de dessiner ce vecteur avec la librairie ROOT.
De plus il lance le logiciel
vlc qui permet d'entendre le chier (il envoie le chier
sur la carte son).
#include <iostream>
using namespace std;
#include <TCanvas.h>
#include <TMarker.h>
#include <TApplication.h>
#include "bib_fred.h"
///-------------------------------------------int main(int argc, char **argv)
{
TApplication theApp("App", &argc, argv);
//---- lecture audio du fichier wav avec le logiciel vlc
system("vlc Voyelles_Par_Malik.wav");
//--- lecture de tout le fichier
vec signal;
double f=Lecture_fichier_wav("Voyelles_Par_Malik.wav", signal);
// transfert les données du fichier dans le vecteur
double dt=1./f; // pas en temps
//Dessin(signal, "signal");
Dessin(signal,"s","s",0,signal.size()*dt,"signal",0,"L",-1e4,1e4,kBlue,0);
//--- lecture d'une partie
vec signal2;
double t1=4, t2=4.03; // intervalle de temps en secondes
f=Lecture_fichier_wav("Voyelles_Par_Malik.wav", signal2, 1,t1,t2);
dt=1./f;
//Dessin(signal2, "s2");
Dessin(signal2,"s","s",t1,t1+signal2.size()*dt,"signal2",0,"L",-1e4,1e4,kRed,0);
//--------theApp.Run();
return 0;
}
Sortie du programme :
CHAPITRE 17. LECTURE ET ÉCRITURE D'UN FICHIER SON DE FORMAT WAV119
Remarquer que vous pouvez zoomer le dessin etc...
Sortie du programme sur le terminal :
=====Crée tableau à partir du fichier: Voyelles_Par_Malik.wav
ChunkID=RIFF
format=WAVE
Nombre de canaux=1
Frequence d'echantillonage=48000 Hz
Bits par echantillon=16
Nombre de données =834402
Nombre d'octets par echantillon=2
Pas de temps dt =2.08333e-05 s.
Durée T =8.69169 s.
Lecture fichier finie.
--------------------
CHAPITRE 17. LECTURE ET ÉCRITURE D'UN FICHIER SON DE FORMAT WAV120
17.0.2 Programme pour lire un chier WAV :
Le programme suivant crée un signal échantilloné dans un vecteur de double (de type
vec) et ensuite écrit ce vecteur dans un chier au format WAV. De plus il lance le logiciel
vlc qui permet de jouer le chier (envoie sur la carte son).
#include <iostream>
using namespace std;
#include <TCanvas.h>
#include <TMarker.h>
#include <TApplication.h>
#include "bib_fred.h"
///-------------------------------------------int main(int argc, char **argv)
{
TApplication theApp("App", &argc, argv);
double dt=1./48000; // pas de temps en s.
double T=5;// durée en s.
int N=T/dt;
vec signal(N);
double f0=440; // frequence de la note que l'on veut en Hz
for(int i=0; i<N; i++)
{
double t=i*dt;
double f= f0 *(1+0.01*cos(2.*M_PI*t/1.)); // modulation de frequences
double s= sin( 2*M_PI * f * t ); // note pure de frequence f
s = s + 0.5 * sin( 2*M_PI * 2*f * t ); // rajoute harmonique 2
s = s + 0.3 * sin( 2*M_PI * 3*f * t ); // rajoute harmonique 3
signal(i) = s;
}
Dessin(signal,"s","s",0,signal.size()*dt,"signal",0,"L",-4,4,kBlue,0);
// ecrit le vecteur signal dans le fichier son.wav. On indique dt
Ecriture_fichier_wav(signal, dt,"son.wav");
//---- lecture audio du fichier wav avec le logiciel vlc
system("vlc son.wav");
//--------theApp.Run();
return 0;
}
CHAPITRE 17. LECTURE ET ÉCRITURE D'UN FICHIER SON DE FORMAT WAV121
Sortie du programme : ce chier son.wav, l'image du signal et la sortie sur terminal :
=====Crée un fichier wav à partir du tableau ====fichier:son.wav
Nombre de canaux=1
Frequence d'echantillonage=48000 Hz
Bits par echantillon=16
Nombre d'octets par echantillon=2
Nombre d' echantillons =240000
Pas de temps dt =2.08333e-05 s.
Durée T =5 s.
Chapitre 18
Gestion de la carte son (audio) avec
Jack
On va expliquer l'utilisation de base de jack.
reference
gcc -o myprog `pkg-cong --cags --libs jack` myprog.c
Tutorial sur le lancement de Jack
Tutorial
122
Chapitre 19
Gestion de la carte son (audio) avec la
librairie sndio
On utilisera la librairie sndio développée pour OpenBSD mais utilisable aussi avec
linux. Cette librairie permet de gérer le son audio et aussi les évènements midi en temps
réel (entrée/sortie).
Remarques
Pour avoir la liste des cartes sons faire
cela donne par exemple
cat /proc/asound/cards
ou
aplay -l,
0 [HDMI ] : HDA-Intel - HDA Intel HDMI
HDA Intel HDMI at 0xf7a1c000 irq 38
1 [PCH ] : HDA-Intel - HDA Intel PCH
HDA Intel PCH at 0xf7a18000 irq 37
19.1
Installation (à faire la première fois)
Si vous êtes administrateur, suivre cette documentation. Puis tester :
test.wav sur la carte
aucat -i ~/test.wav.
Pour envoyer (écouter) le chier
par exemple il faut écrire :
0 (carte son par default)
Si il n'y a pas de son et le message snd0: rsnd/0: failed to open audio device c'est
que la carte son 0 n'existe pas. Vérier la liste des cartes sons de votre ordi avec la
commande
aplay -l
et :
test.wav
aucat -f rsnd/1 -i ~/test.wav.
Pour envoyer (écouter) le chier
écrire :
sur la carte 1 par exemple il faut
Si la carte 1 est la carte son par defaut (au lieu de la carte 0) il faut éditer
/etc/default/sndiod
et ajouter l'option
DAEMON_OPTS="-f rsnd/1"
Sinon si vous n'êtes pas administrateur et que cette librairie n'est pas déjà installée,
il vous faut l'installer sur votre compte. Pour cela :
123
CHAPITRE 19. GESTION DE LA CARTE SON (AUDIO) AVEC LA LIBRAIRIE SNDIO124
Télécharger le source et décompresser l'archive.
Dans le répertoire de sndio ainsi créé, taper :
configure --prefix=$HOME
make
make install
Remarque : cela a pour eet de compiler la bibliothèque et d'installer les librairies
dans HOME/lib et HOME/include.
Dans les options de compilation, il faudra rajouter :
-I/$HOME/include
( : dans codeblocks, c'est à rajouter dans Settings/Compi-
ler/CompilerSetting/OtherOptions),
-L/$HOME/lib
et
-lsndio
( : dans codeblocks, c'est à rajouter dans Settings/-
Compiler/LinkerSettings/OtherOptions)
Dans le chier
.bashrc
il faut rajouter
export LD_LIBRARY_PATH=$HOME/lib
. .bashrc
et
executer dans une fenêtre de commande :
19.1.0.1
Travail préalable pour le projet C++
1. Fichiers à télécharger et sauver dans son répertoire : bib_fred.h et bib_fred.cc,
audio.h et audio.cc.
2. Dans codeblocks, il faut ajouter ces chiers au projet en faisant dans le menu :
Projet/add_les ...
3. Il faut aussi
rajouter
-larmadillo
dans les options de compilation (pour code-
blocks c'est dans Setting/Compiler/LinkerSettings/OtherLinkerOptions)
4. Démarrage du serveur sndiod, si vous êtes administrateur, écrire dans un terminal :
sudo sndiod -dd -z480 -f rsnd/0
et le laisser ouvert,
sinon, si sndio a été installé sur votre compte, écrire dans un terminal :
-z480 -f rsnd/0
19.2
sndiod -dd
et le laisser ouvert.
Utilisation du micro (entrée) et du haut-parleur
(sortie)
19.2.1 Le micro
Le micro échantillonne le son avec un pas de temps
Dt = 1/48000sec.
et envoie les
données à la carte son qui les convertit sous forme numérique. Cet exemple lit les données du
micro depuis la carte son par bloc de
et les dessine au fur et à mesure.
N = 1000 donnée, soit de durée T = N.Dt = 0.02sec.,
CHAPITRE 19. GESTION DE LA CARTE SON (AUDIO) AVEC LA LIBRAIRIE SNDIO125
#include "bib_fred.h"
#include "audio.h"
#include <iostream>
using namespace std;
#include <TROOT.h>
#include <TApplication.h>
//===Main================================================
int main(int argc, char **argv)
{
TApplication theApp("App", &argc, argv);
Audio s;
s.Initialise_audio(1); // initialise la carte son en entree (1)
et af-
che ses informations
int N=1000; // on donne la taille que l'on veut (nombre d'echantillons)
vec V(N);
while(1) // boucle infinie
{
s.Lit_donnees_audio(V); // remplit V depuis le micro
CHAPITRE 19. GESTION DE LA CARTE SON (AUDIO) AVEC LA LIBRAIRIE SNDIO126
Dessin(V,"V");
// Dessin(V,"t","s",0,N*s.Dt,"V",0,"L",-1e4,1e4,kRed,0); //
dessin normalisé en rouge
}
}
19.2.1.1
/// ----Donne la main a l'utilisateur --theApp.Run();
return 0;
Exemple : détection de note de musique en temps réel
Une note de musique est un signal périodique de période
dans l'intervalle audible
30Hz ≤ f ≤ 3000Hz .
T,
et fréquence
f = 1/T
Le programme suivant utilise un al-
gorithme de détection de période dans un signal, et ache le signal sur une (ou plusieurs périodes) ainsi que le nom de la note jouée. (Pour information, la fonction utilisée
Signal::Detecte_Periode()
se trouve dans le chier
signal.cc.)
Résultat :
Periode : T=0.0034036s. <-> Frequence f=1/T=293.806 Hz
Note =Re, Ecart = 0.00833494 demitons, Octave=0.
CHAPITRE 19. GESTION DE LA CARTE SON (AUDIO) AVEC LA LIBRAIRIE SNDIO127
#i n c l u d e
" b i b _ f r e d . h"
#i n c l u d e
" a u d i o . h"
#i n c l u d e <i o s t r e a m >
using
namespace
std ;
#i n c l u d e <TROOT. h>
#i n c l u d e <T A p p l i c a t i o n . h>
string
Notes [ 1 2 ]
= {"Do " , " Do#" ,
"Re " , " Re#" ,
"Mi " ,
"Fa " ,
"Fa#" ,
" Sol " ,
" S o l #" ,
//===Main================================================
int
main ( i n t
argc ,
char
** a r g v )
{
TApplication
Audio
theApp ( " App " , &a r g c ,
argv ) ;
s;
s . Initialise_audio (1);
//
initialise
la
carte
son
en
entree
(1)
et
affich
CHAPITRE 19. GESTION DE LA CARTE SON (AUDIO) AVEC LA LIBRAIRIE SNDIO128
int
N= 2 0 0 0 ;
//
on
donne
la
taille
que
l ' on
veut
( determine
la
fenetre
tem
v e c V(N ) ;
// p a r a m e t r e s
pour
la
int
1 ,2:
nbre
NP=2;
double
//
f_A = 4 4 0 ;
detection
//
de
de
la
periodes
periode
à
afficher
diapason
s . s e u i l _ n o r m e _ p a r _ I=1e 4 ;
s . seuil_C
=0.1;
s . verbose
=0;
//1
affiche
messages
erreur
//−−−−−−−−−−−−−−−−−−−−−
while (1)
//
boucle
infinie
{
s . L i t _ d o n n e e s _ a u d i o (V ) ;
double
int
t0
=0;
//
date
opt_dessin_C =0;
//
de
//
remplit V
debut
0
ou
depuis
le
micro
d ' analyse
1
d o u b l e T=s . D e t e c t e _ P e r i o d e (V,
t0 ,
opt_dessin_C ) ;
//
−>
P e r i o d e T ou
0
i f (T>0)
{
double
f =1/T ;
c o u t <<" P e r i o d e
//−−−
conversion
T="<<T<<"s . <−>
en
note
de
nu_i =
Frequence
+ 69;
f l o o r ( nu + 0 . 5 ) ;
c o u t <<" Note ="<<N o t e s [ nu_i%12]<<"
//−−−−
vec
affiche
NP p e r i o d e s
E x t r a i t = V. rows (
uword
i
f =1/T="<<f <<" Hz"<< e
musique
nu = 1 2 * l o g ( f /f_A ) / l o g ( 2 . )
double
int
:
E c a r t = "<<nu−nu_i<<" d e m i t o n s
extraites
t 0 / s . Dt ,
( t 0+NP*T) / s . Dt ) ;
//
on
extrait
NP
= E x t r a i t . index_max ( ) ;
E x t r a i t =s h i f t ( Extrait ,
−i
);
//
place
le
max de
l ' extraitau
debu
D e s s i n ( E x t r a i t , " t " , " s " , 0 ,NP*T, " E x t r a i t " , 0 , " L" , − 1 1 1 1 , − 1 1 1 1 , kRed , 0 )
}
}
///
−−−−Donne
la
theApp . Run ( ) ;
main
a
l ' utilisateur
−−−
CHAPITRE 19. GESTION DE LA CARTE SON (AUDIO) AVEC LA LIBRAIRIE SNDIO129
return
0;
}
19.2.2 Le haut-parleur
Voici un exemple qui lit les données sur le micro et les envoie sur le haut-parleur après
un délai de 200ms (la latence). Mettre un casque.
#i n c l u d e
" b i b _ f r e d . h"
#i n c l u d e
" a u d i o . h"
#i n c l u d e <i o s t r e a m >
using
namespace
std ;
#i n c l u d e <TROOT. h>
#i n c l u d e <T A p p l i c a t i o n . h>
int
main ( i n t
argc ,
char
** a r g v )
{
TApplication
Audio
theApp ( " App " , &a r g c ,
argv ) ;
s;
s . Initialise_audio (3);
//
initialise
la
carte
son
en
entree
et
sortie
(3
vec V;
while (1)
//
boucle
infinie
{
s . L i t _ d o n n e e s _ a u d i o (V ) ;
//
micro
−>
V
D e s s i n (V, " t " , " s " , 0 ,V . s i z e ( ) * s . Dt , "V" , 0 , " L" , − 1 e4 , 1 e4 , kRed , 0 ) ;
de V
s . E c r i t _ d o n n e e s _ a u d i o (V ) ;
// V
−>
}
///
−−−−Donne
la
theApp . Run ( ) ;
return
}
0;
main
a
l ' utilisateur
−−−
Haut
Parleur
//
dessin
Chapitre 20
Plusieurs programmes en parallèle qui
communiquent, avec thread
Il peut être utile dans un projet de répartir le travail entre plusieurs programmes qui fonctionnent ensemble et communiquent. Un ordinateur est capable de gérer plusieurs programmes qui fonctionnent. Si l'ordinateur ne possède qu'un processeur, il va faire avancer chaque programme tour à tour en
alternant sur des intervalles de temps très court. Cela donne l'impression que
plusieurs programmes fonctionnent en parallèle. On va s'interesser à la communication entre ces programmes.
On peut imaginer cela comme des travailleurs qui fabriquent une maison.
Chacun travaille sur sa partie mais il y a des moments où ils doivent échanger
des informations, parfois un travailleur doit attendre qu'un autre ait ni son
travail. Alors c'est le chef de projet (le processeur) qui se charge de superviser
tout cela, de mettre en attente certains et réactiver d'autres.
Il y a donc trois concepts importants dont nous allons voir la mise en oeuvre :
1. Les threads : au niveau de la terminologie, chaque programme s'appelle un thread
ou process ou context. On va tout d'abord apprendre à
démarrer des threads.
2. Les mutex : lorsque ces programmes doivent communiquer c'est à dire échanger
des données (et c'est souvent le cas) il y a des précautions à prendre an que
tout ce passe bien : par exemple il faut éviter qu'une variable soit manipulée par
deux threads en même temps. Ce sera le rôle des
mutex qui sont simplement des
indicateurs booleen (vrai/faux) que l'on interprète comme (occupé/libre).
3. Les variables conditionnelles, cond_var : c'est comme une sonnette. Cela permet
au processeur de mettre en attente certains thread et d'en réactiver d'autres sous
certaines conditions.
références : article sur C++11 threads, locks and condition variables.
130
CHAPITRE 20. PLUSIEURS PROGRAMMES EN PARALLÈLE QUI COMMUNIQUENT, AVEC
20.1
THRE
Lancement de threads ou processus
#include <iostream> // std::cout
using namespace std;
#include <thread>
//---- Une petite fonction qui affiche des a---------------void f(int n)
{
for(int i=0;i<n;i++)
cout<<"a"<<flush;
cout<<endl;
}
//----Fonction principale ---------------int main()
{ // ici la fonction main est lancée
int n=10;
thread t(f,n); // lance la fonction f(n) dans un thread
cout << "Les fonctions main et f sont maintenant en concurrence.\n";
for(int i=0;i<n;i++) // affiche des b
cout<<"b"<<flush;
cout<<endl;
t.join(); // attend ici que la fonction f soit finie
cout << "f est finie.\n";
return 0;
}
20.1.0.1
Commande de compilation :
Si on utilise codeblocks, il faut rajouter
-pthread -std=c++11
dans Settings/Com-
piler/Compiler_Settings/Other_Options et dans Settings/Compiler/Linker_Settings/Other_linker
Pour une compilation en ligne de commande :
g++ main.cpp -o main.out -pthread -std=c++11
Résultat :
Les fonctions main et f sont maintenant en concurrence.
bbbbbaaaaaaaaaabbbbb
f est finie.
CHAPITRE 20. PLUSIEURS PROGRAMMES EN PARALLÈLE QUI COMMUNIQUENT, AVEC
20.1.0.2
Commentaires
1. Les fonctions
f
et
main
fonctionnent en parallèle et leur achages 'a' ou 'b' sur
l'écran se mélangent de façon incontrollée. Un deuxième lancement du programme
donnerait éventuellement autre chose.
2. On peut passer (ou pas) des variables à la fonction lancée en parallèle, ici on passe
la variable n.
f en écrivant
par exemple, void f(int * pn) {..} pour la déclaration et thread t(f,&n);
pour le lancement.. Cela permet de transmettre un résultat de la fonction f vers
la fonction main.
(a) On peut
passer un pointeur (adresse d'une variable) à la fonction
(b) Mais pour
passer une variable par référence, il faut écrire par exemple,
void f(int & n) {..}
pour la déclaration et
thread t(f,ref(n));
pour le
lancement.
3. Il peut être utile de créer une certaine attente dans un programme. Utiliser Section
13.2.
4. Si vous oubliez
t.join() à la n, il y aura une erreur à l'execution du programme.
5. Documentation complète sur thread.
20.1.0.3
Lancer un thread d'une fonction membre de classe
#include <iostream>
using namespace std;
#include <thread>
class A
{
public:
void f(int n ) { cout << n << endl; }
};
int main()
{
A a;
thread t1(&A::f, &a, 100);
t1.join();
return 0;
}
THRE
CHAPITRE 20. PLUSIEURS PROGRAMMES EN PARALLÈLE QUI COMMUNIQUENT, AVEC
Commentaires
Si on lance le thread depuis une autre fonction membre, il faut bien sûr remplacer
le pointeur
20.1.0.4
&a
par
this.
Lancer une liste de thread (tableau)
#include <iostream> // std::cout
using namespace std;
#include <thread>
//---- Une petite fonction qui affiche n fois n ---------------void f(int n)
{
for(int i=0; i<n; i++)
cout<<n<<flush;
}
//----Fonction principale ---------------int main()
{
int n=7;
thread L_t[n]; // tableau de threads
for (int i=0; i<n; i++)
L_t[i] = thread(f,i); // lance de thread i qui affiche i fois i
for(int i=0; i<n; i++)
L_t[i].join(); // attend ici que le thread i soit fini
return 0;
}
Résultat :
20.1.0.5
315524563656666254443
Lancer une liste de thread (vector)
Même chose que précédement mais avec un
vector.
#include <iostream> // std::cout
using namespace std;
#include <thread>
//---- Une petite fonction qui affiche n fois n ---------------void f(int n)
{
for(int i=0; i<n; i++)
cout<<n<<flush;
}
//----Fonction principale ---------------int main()
THRE
CHAPITRE 20. PLUSIEURS PROGRAMMES EN PARALLÈLE QUI COMMUNIQUENT, AVEC
{
int n=7;
vector<thread> L_t; // tableau de threads
for (int i=0; i<n; i++)
L_t.push_back(thread(f,i)); // lance de thread i qui affiche i fois i
for(int i=0; i<n; i++)
L_t[i].join(); // attend ici que le thread i soit fini
return 0;
}
Résultat :
20.2
213332444456666665555
Mutex pour annoncer occupé/libre
20.2.0.1
Introduction
Un mutex est simplement une variable booléenne dont on pense que la valeur est occupé/libre. Une analogie possible est qu'un mutex est comme le panneau devant une salle de
bain (sans serrure) qui annonce une valeur occupé/libre. Par exemple, on peut utiliser un
mutex avant de modier une variable commune à plusieurs threads. On déclare un mutex
par
mutex mtx;
Ensuite il y a deux commandes de base :
1. La commande
mtx.lock();
qui signie : si
mtx
est libre on le met à occupé et on continue, sinon, si il est
occupé alors on attend jusqu'à ce qu'il soit libre.
2. La commande
mtx.unlock();
qui signie : on met
20.2.0.2
mtx
à libre
Remarques
mtx ordisi mtx est libre on le met à
occupé et on continue, sinon on attend ?
Quel intéret d'utiliser un mutex
mtx
plutot qu'une variable boolenne
naire pour eectuer la suite d'instructions
réponse : en utilisant une variable ordinaire, il y a le risque qu'un autre processus
viennent modier la valeur de la variable pendant cette suite d'instructions, ce que
l'on ne souhaite absolument pas.En utilisant un mutex il est garantit que cette suite
d'instruction est faite tout à la suite par l'ordinateur, sans être interrompu (on dit
de façon atomique).
THRE
CHAPITRE 20. PLUSIEURS PROGRAMMES EN PARALLÈLE QUI COMMUNIQUENT, AVEC
20.2.0.3
Exemple
#include <iostream> // std::cout
using namespace std;
#include <thread>
#include <mutex>
mutex mtx;
//-------------------void f(int n,char c)
{
mtx.lock(); // si mtx est libre on le bloque, sinon on attend
for(int i=0;i<n;i++)
cout<<c<<flush;
cout<<endl;
mtx.unlock(); // débloque mtx
}
//-------------------int main()
{
int n=10;
thread t1(f,n,'a');
thread t2(f,n,'b');
t1.join();
t2.join();
return 0;
}
20.2.0.4
Résultat :
aaaaaaaaaa
bbbbbbbbbb
Dans cet exemple le thread t1 est arrivé le premier et a bloqué le mutex. Le thread t2 a
donc attendu qu'il soit débloqué pour le bloquer à son tour.
THRE
CHAPITRE 20. PLUSIEURS PROGRAMMES EN PARALLÈLE QUI COMMUNIQUENT, AVEC
20.3
Communications : variables conditionnelles pour
réveiller un programme en attente
Introduction
Dans la communication entre programmes (process), il peut arriver qu'un
programme 2 attende le resultat d'un autre(s) programme(s) 1.
Voici une solution naive et maladroite pour programmer cette solution :
Il y a une variable qui est a=0 au départ et que le programme met à la valeur a=1
dès qu'il a obtenu sont résultat. (Cette variable est protégée par un mutex). Le
programme 2 eectue une boucle innie dans laquelle il lit la variable a et si a==1
alors il sort de la boucle et récupère le résultat. Cette solution est maladroite car le
programme 2 consomme du CPU (processeur de la machine) pour rien.
Une meilleure solution est que le programme 2 soit mis en attente (en veille, il ne consomme
pas de CPU) et réveillé à l'arrivée du résultat. Pour cela on utilise une
variable condi-
tionelle.
Instructions de base :
Une variable conditionnelle se déclare par
condition_variable cv;
Il y a la commande
cv.wait(lck);
qui met le thread en attente (endormi), et
cv.notify_one();
qui reveille un parmi les autres thread qui se sont mis en attente, (ou
cv.notify_all();
qui reveille tous les autres thread qui se sont mis en attente.)
20.3.0.1
Exemple de base où un thread endormi est reveillé par un autre.
#include <iostream>
using namespace std;
#include <thread>
#include <chrono>
#include <mutex>
#include <condition_variable>
mutex mtx;
condition_variable cv;
//-------------------void f()
{
unique_lock<mutex> lck(mtx); // bloque le mutex mtx. Il sera debloque
automatiquement par wait(lck), ou a la destruction de lck.
THRE
CHAPITRE 20. PLUSIEURS PROGRAMMES EN PARALLÈLE QUI COMMUNIQUENT, AVEC
cv.wait(lck); // met le thread en attente (endormi). Il sera réveillé
par un signal extérieur.
cout<<"FIN"<<endl;
}
//-------------------int main()
{
thread t(f);
cout<<"On attend 2sec."<<endl;
this_thread::sleep_for(chrono::seconds{2});
cout<<"voilà."<<endl;
// ... reveille cv
cv.notify_one(); // reveille le thread endormi
//.........
t.join();
return 0;
}
Résultat :
On attend 2sec.
voilà.
FIN
20.3.0.2
Remarques
L'instruction wait() sauvegarde tous les registres du processus (tout son environnement mémoire) et notify_one() les restore.
dans cet exemple l'utilisation du mutex ne sert à rien, mais le langage C++
oblige à l'utiliser dans l'instruction wait(lck). Voir ci-dessous l'exemple 20.3.4 où
l'utilisation du mutex est utile et obligatoire. Pourquoi faut il utiliser le mutex
mtx ?
L'utilisation d'un mutex est obligatoire, car les thread se partagent une
variable
cv
qui spécie l'état sommeil/reveillé et donner l'instruction de réveil.
La fonction wait() utilise cette variable en faisant en fait de façon séquentielle :
lecture de cv, si signal je dois me réveiller.
Exemple 20.3.1. où un thread endormi attend des évènements provenant de
dierents thread extérieurs.
THRE
CHAPITRE 20. PLUSIEURS PROGRAMMES EN PARALLÈLE QUI COMMUNIQUENT, AVEC
#include <iostream>
using namespace std;
#include <thread>
#include <chrono>
#include <mutex>
#include <condition_variable>
mutex mtx;
condition_variable cv;
//
void f1()
{
while(1)
{
this_thread::sleep_for(chrono::milliseconds {1000});
cout<<"1"<<flush;
cv.notify_one(); // reveille le thread main() endormi
}
}
//
void f2()
{
while(1)
{
this_thread::sleep_for(chrono::milliseconds {1502});
cout<<"2"<<flush;
cv.notify_one(); // reveille les thread main() endormi
}
}
//
int main()
{
thread t1(f1);
thread t2(f2);
unique_lock<mutex> lck(mtx); // bloque le mutex mtx. Il sera debloque
automatiquement par wait(lck), ou a la destruction de lck.
while(1)
{
cv.wait(lck); // met le thread main() en sommeil
cout<<","<<flush;
}
//.........
t1.join();
THRE
CHAPITRE 20. PLUSIEURS PROGRAMMES EN PARALLÈLE QUI COMMUNIQUENT, AVEC
t2.join();
return 0;
}
Résultat :
1,2,1,1,2,1,2,1,1,2,1,2,1,1,2,1,2,1,1,2,...
Remarque 20.3.2.
Pour expliquer le résultat, il faut remarquer que la fonction f1() ache 1 aux
dates suivantes (en ms) : 1000, 2000, 3000, 4000, 5000,.... Après chaque achage,
la fonction f1() reveille la fonction main() qui ache ,. De même, la fonction f2()
ache
2 aux dates : 1502, 3004, 4506, 6008, ... et de même à chaque achage
elle reveille la fonction main() qui ache ,. La suite chronologique de ces dates est :
1000,1502,2000,3000,3004,4000,4506,5000,6000,6008, ... ce qui explique le résultat
1,2,1,1,2,1,2,1,1,2,..
Plusieurs threads peuvent être mis en attente par
cv.wait(lck);
et la commande
cv.notify_one(); va en reveillé un seul (on ne sait pas lequel). Par contre
commande cv.notify_all(); reveillerait tous les threads mis en attente.
la
Exemple 20.3.3. où deux thread endormi sont reveillés par une notication
venant d'un 3eme thread.
#include <iostream>
using namespace std;
#include <thread>
#include <chrono>
#include <mutex>
#include <condition_variable>
mutex mtx;
condition_variable cv;
//
void f1()
{
unique_lock<mutex> lck(mtx); // bloque le mutex
automatiquement par wait(lck), ou a la destruction
cout<<"a"<<endl;
cv.wait(lck);
cout<<"b"<<endl;
}
//
void f2()
{
unique_lock<mutex> lck(mtx); // bloque le mutex
automatiquement par wait(lck), ou a la destruction
cout<<"A"<<endl;
mtx. Il sera debloque
de lck.
mtx. Il sera debloque
de lck.
THRE
CHAPITRE 20. PLUSIEURS PROGRAMMES EN PARALLÈLE QUI COMMUNIQUENT, AVEC
THRE
cv.wait(lck);
cout<<"B"<<endl;
}
//
int main()
{
thread t1(f1);
thread t2(f2);
for(int t=3; t>=0; t--) // compte à rebour
{
this_thread::sleep_for(chrono::seconds {1});
cout<<t<<","<<flush; // \r permet de re-ecrire au debut de la ligne
}
cout<<"partez!"<<endl;
cv.notify_all();
//.........
t1.join();
t2.join();
return 0;
}
Exemple 20.3.4. Un thread remplit une liste. Le deuxieme thread vide la liste.
#i n c l u d e <i o s t r e a m >
using
namespace
std ;
#i n c l u d e <t h r e a d >
#i n c l u d e <c h r o n o >
#i n c l u d e <mutex>
#i n c l u d e <c o n d i t i o n _ v a r i a b l e >
#i n c l u d e < l i s t >
mutex mtx ;
condition_variable
cv ;
l i s t <i n t > L ;
int
N= 1 0 ;
//−−−−−−−−−−−−−−−−−−−−
void
f ()
{
while (1)
//
boucle
infinie
{
{
u n i q u e _ l o c k <mutex> l c k ( mtx ) ;
i f ( L . s i z e () < N)
//
cv . w a i t ( l c k ) ;
si
//
liste
//
bloque
non
pleine
debloque
le
mutex
le
lck
mutex mtx .
et
met
le
Il
sera
thread
debl
en
at
CHAPITRE 20. PLUSIEURS PROGRAMMES EN PARALLÈLE QUI COMMUNIQUENT, AVEC
}
//
ici
lck
est
detruit
donc
mtx
est
debloque
mtx . l o c k ( ) ;
c o u t <<" L i s t e
pleine .
On
vide
la
liste
par
le
d e b u t :"<< f l u s h ;
w h i l e (L . s i z e () >0)
{
c o u t <<L . f r o n t ()<<","<< f l u s h ;
this_thread : : s l e e p _ f o r ( chrono : : m i l l i s e c o n d s
L . pop_front ( ) ;
//
enleve
le
premier
reveille
le
thread
{100});
element
}
c o u t <<e n d l ;
mtx . u n l o c k ( ) ;
cv . n o t i f y _ o n e ( ) ;
//
endormi
}
}
//−−−−−−−−−−−−−−−−−−−−
int
main ( )
{
thread
t( f );
while (1)
//
//
lance
boucle
la
fonction
f
dans
un
thread
infinie
{
mtx . l o c k ( ) ;
//
bloque
i f ( L . s i z e ()==0)
//
si
le
mutex
liste
vide
{
c o u t <<" L i s t e
Vide .
Remplit
for ( int
j< N;
j ++)
j =0;
la
l i s t e :"<< f l u s h ;
{
c o u t <<j <<","<< f l u s h ;
L . push_back ( j ) ;
this_thread : : s l e e p _ f o r ( chrono : : m i l l i s e c o n d s
}
c o u t <<e n d l ;
}
mtx . u n l o c k ( ) ;
//
cv . n o t i f y _ o n e ( ) ;
//
se
met
en
debloque
//
reveille
attente
u n i q u e _ l o c k <mutex> l c k ( mtx ) ;
cv . w a i t ( l c k ) ;
}
//.........
t . join ();
return
}
0;
le
thread
endormi
{100});
THRE
CHAPITRE 20. PLUSIEURS PROGRAMMES EN PARALLÈLE QUI COMMUNIQUENT, AVEC
Résultat :
Liste
Liste
Liste
Liste
...
Vide. Remplit la liste:0,1,2,3,4,5,6,7,8,9,
pleine. On vide la liste par le debut:0,1,2,3,4,5,6,7,8,9,
Vide. Remplit la liste:0,1,2,3,4,5,6,7,8,9,
pleine. On vide la liste par le debut:0,1,2,3,4,5,6,7,8,9,
THRE
Chapitre 21
Messages MIDI (Musical) avec la
librairie sndio
La convention sur les messages MIDI (Musical Instrument Digital Interface) entre ordinateurs sont apparus dans les années 80 pour la
musique électronique, l'informatique
musicale et les synthétiseurs. Référence sur les messages MIDI.
Dans ce chapitre nous voyons comment envoyer et/ou recevoir des messages midi depuis
un programme en utilisant la librairie sndio. On verra aussi comment utiliser ces messages
midi en musique.
Conguration préalable
21.1
Installation (à faire la première fois)
Suivre les instructions de la Section 19.1.
De plus il faut télécharger les chiers suivants et les sauver dans son répertoire :
midi.h et midi.cc.
1. Si vous utilisez codeblocks, il faut ajouter ces chiers au projet en faisant dans
le menu : Projet/add_les ...
2. Si vous utilisez un Makele, rajouter les chiers au projet selon les explications
de la Section 25.
3. Il faut aussi
rajouter
-lsndio dans les options de compilation (pour codeblocks
c'est dans Setting/Compiler/LinkerSettings/OtherLinkerOptions)
21.2
Exemple élémentaire sur l'envoi et reception de
messages MIDI
Ce premier exemple montre comment envoyer des octets (chires) entre deux programmes via un port midi virtuel de l'ordinateur. En pratique les messages MIDI ont
143
CHAPITRE 21.
MESSAGES MIDI (MUSICAL) AVEC LA LIBRAIRIE SNDIO
144
un contenu très particulier qui ont une signication musicale comme début de telle note, changement de volume,.., et dans la Section suivante on précisera comment utiliser
ces messages pour l'informatique musicale (reception des notes d'un clavier numérique et
envoie de notes à un synthétiseur).
Instructions
Copier et compiler les programmes
test_midi_in.cc
et
test_midi_out.cc
avec
le programme Makele donnés ci-dessous.
Lancer le programme
test_midi_in
dans un terminal puis le programme
test_midi_out
dans un autre terminal.
Résultat :
Le programme
(provenant du
test_midi_in se met en attente et lorsqu'il recoit
programme test_midi_out) il l'ache à l'écran :
un message midi
port ouvert.
Attente de message ..
1 messages recus.
Message recu sur le port=0: 144, 60, 50
Midi::On ferme les ports midi..
Le programme
test_midi_out
ache :
port ouvert.
J'ai envoye le message 144, 60, 50 sur le port 0
Midi::On ferme les ports midi..
Code C++ des programmes :
Voici le code C++ ainsi que le Makele permet de
compiler les deux programmes par l'instruction
//===== Programme
make all.
t e s t _ m i d i _ i n . c c ==========
#i n c l u d e <i o s t r e a m >
using
namespace
#i n c l u d e
int
std ;
" midi . h"
main ( )
{
Midi
midi ;
//−−− o u v r e
un
port
int
//
numero
p o r t =0;
midi
de
en
entree
port
CHAPITRE 21.
145
MESSAGES MIDI (MUSICAL) AVEC LA LIBRAIRIE SNDIO
int
r e s = midi . I n i t i a l i s e _ p o r t _ m i d i _ i n ( port ) ;
i f ( r e s ==0)
c o u t <<"p o r t
est
encore
f e r m e ."<< e n d l ;
c o u t <<"p o r t
o u v e r t ."<< e n d l ;
else
//−−−
attente
de
c o u t <<"A t t e n t e
messages
de
message
.." < < e n d l ;
midi . Attente_Message ( ) ;
c o u t <<"M e s s a g e s
r e c u s ."<< e n d l ;
//−−− on
affiche
les
f o r ( auto
message
:
messages
recus
m i d i . L_message )
dans
//
v e c t o r <v e c t o r <u n s i g n e d
analyse
chacun
des
messages
{
int
p o r t _ i n =( i n t ) m e s s a g e [ 0 ] ;
//
message . e r a s e ( message . begin ( ) ) ;
octet .
Il
reste
le
message
midi
c o u t <<"M e s s a g e
for ( int
numero
//
de
enleve
port
le
sur
le
i =0; i <m e s s a g e . s i z e ( ) ; i ++)
c o u t <<e n d l ;
}
port
midi
m i d i . Ferme_port_midi_in ( p o r t ) ;
return
0;
}
//===== Programme
t e s t _ m i d i _ o u t . c c ==========
#i n c l u d e <i o s t r e a m >
using
namespace
#i n c l u d e
int
std ;
" midi . h"
main ( )
1 er
p o r t="<<p o r t _ i n <<": "<< f l u s h ;
c o u t <<( i n t ) m e s s a g e [ i ] < <" , "<< f l u s h ;
//−−− f e r m e
entrant
standard
recu
c h a r >>
mid
CHAPITRE 21.
MESSAGES MIDI (MUSICAL) AVEC LA LIBRAIRIE SNDIO
146
{
Midi
midi ;
//−−− o u v r e
port
int
port = 0;
int
res =
midi
out
midi . I n i t i a l i s e _ p o r t _ m i d i _ o u t ( port ) ;
i f ( r e s ==0)
{
c o u t <<"p o r t
est
encore
f e r m e ."<< e n d l ;
exit (1);
}
else
c o u t <<"p o r t
//−−−−
envoie
o u v e r t ."<< e n d l ;
message :
v e c t o r <u n s i g n e d
144 ,
60 ,
50
char> message ;
m e s s a g e . push_back ( 1 4 4 ) ;
m e s s a g e . push_back ( 6 0 ) ;
m e s s a g e . push_back ( 5 0 ) ;
m i d i . Envoi_Message ( p o r t ,
c o u t <<"J ' a i
//−−
ferme
envoye
les
le
ports
message ) ;
message
midi
144 ,60 ,50
ouverts
m i d i . Ferme_port_midi_out ( p o r t ) ;
return
0;
}
#=== F i c h i e r
LIBR=
−lm
CC =g++
Makefile
−l p t h r e a d − l s n d i o
− s t d=c++11
− s t d=c++11
#==========================================
OBJETS_MIDI_O=
test_midi_out . o
midi . o
sur
le
port
"<<p o r t <<e n d l ;
CHAPITRE 21.
midi . o
:
MESSAGES MIDI (MUSICAL) AVEC LA LIBRAIRIE SNDIO
midi . cc
$ (CC)
$ (CC)
:
−c
$ * . cc
test_midi_out . cc
$ (CFLAGS)
test_midi_out :
$ (CC)
midi . h
$ (CFLAGS)
test_midi_out . o
147
−c
$ * . cc
−o
$@
bib_fred . h
−o
$@
$ (OBJETS_MIDI_O)
−o
test_midi_out
$ (OBJETS_MIDI_O)
$ ( LIBR )
#==========================================
OBJETS_MIDI_I= t e s t _ m i d i _ i n . o
midi . o
:
midi . cc
$ (CC)
test_midi_in . o
$ (CC)
test_midi_in :
$ (CC)
midi . h
$ (CFLAGS)
:
midi . o
−c
$ * . cc
test_midi_in . cc
$ (CFLAGS)
−c
−o
$@
bib_fred . h
$ * . cc
−o
$@
$ ( OBJETS_MIDI_I )
−o
test_midi_in
$ ( OBJETS_MIDI_I )
$ ( LIBR )
#==========================
all
:
21.3
test_midi_out
test_midi_in
Exemple d'envoie de message MIDI à un synthétiseur
Comme application de l'exemple précédent, le programme
gamme.cc suivant envoie des
notes à un synthetiseur ou à un instrument électronique et cela joue la gamme chromatique.
Pour cela il envoit un premiere message MIDI qui sélectionne le son de l'instrument sur le
canal 0, puis envoie une série d'instruction correspondant à jouer une note/ eteindre une
note, avec un espace de 0.2 seconde.
Le numéro de la note do est 60, ensuite do# = 61, etc... Voir messages midi.
CHAPITRE 21.
MESSAGES MIDI (MUSICAL) AVEC LA LIBRAIRIE SNDIO
Travail préalable
Installer un synthétiseur MIDI comme
midithru/0
l'écoute du port
0
qui est le port
midisyn
148
et le lancer. Il sera alors à
dans notre programme. Par exemple
on lance midisyn dans une fenetre de commande à part, par l'instruction :
midisyn /home/faure/alex_ratchov/patches/midisyn.conf
Brancher un piano électrique ou autre instrument midi sur l'ordinateur, et
observer le numéro de port par l'instruction
amidi -l dans un terminal. Cela donne
le numéro de port à mettre dans le programme suivant d'après la liste dans le chier
midi.h.
Par exemple
rmidi/2
(port 2 de l'instruction amidi -l) est sur le port 4.
Lancer le programme suivant qui va jouer la gamme chromatique.
//===== Programme gamme . c c ==========
#i n c l u d e <i o s t r e a m >
using
namespace
#i n c l u d e
int
std ;
" midi . h"
main ( )
{
Midi
midi ;
//−−− o u v r e
port
int
port = 0;
int
res =
midi
out
midi . I n i t i a l i s e _ p o r t _ m i d i _ o u t ( port ) ;
i f ( r e s ==0)
{
c o u t <<"p o r t
est
encore
f e r m e ."<< e n d l ;
exit (1);
}
else
c o u t <<"p o r t
o u v e r t ."<< e n d l ;
//.....
int
ch = 0 ;
int
inst
//
= 0;
canal
//
instrument ,
m i d i . Program_Change ( p o r t ,
for ( int
{
key =60;
key <=72;
ch ,
0:
piano
inst );//
associe
k e y++) // montee
de
la
l ' instrument
gamme
inst
chromatique
au
c
CHAPITRE 21.
149
MESSAGES MIDI (MUSICAL) AVEC LA LIBRAIRIE SNDIO
//.......
int
vol
joue
une
note
midi
. . . . . . . . . .
=100;
c o u t <<" j o u e
n o t e "<<key<<e n d l ;
midi . Joue_note ( p o r t ,
midi . S l e e p ( 2 0 0 ) ;
//....
eteind
//
une
ch ,
key ,
attente
note
ch ,
//
message
pour
jouer
une
en ms
midi
midi . Eteind_note ( port ,
vol ) ;
. . . . . . . . .
key ) ;
//
message
pour
eteindre
la
}
//−−
ferme
les
ports
midi
ouverts
m i d i . Ferme_port_midi_out ( p o r t ) ;
return
0;
}
#====== F i c h i e r
M a k e f i l e====================================
OBJETS_MIDI_G=
gamme . o
midi . o
:
midi . cc
$ (CC)
gamme . o
:
midi . h
$ (CFLAGS)
−c
$ * . cc
−o
$@
−c
$ * . cc
−o
$@
gamme . c c
$ (CC)
gamme :
midi . o
$ (CFLAGS)
$ (OBJETS_MIDI_G)
$ (CC)
−o
gamme
$ (OBJETS_MIDI_G)
$ ( LIBR )
Remarque 21.3.1. La liste des codes des instruments est donnée d'après la convention
general midi. Le canal 9 est réservé aux percussions. Mais si vous utilisez le synthetiseur
midisyn, pour le moment il n'y a que les possibilités suivantes :
code program_change
0
1
2
3
4
33
73
74
75
76
instrument
piano
guitare
guitare
guitare
guitare
bass
ute
ute
ute
ute
et spécialement sur le canal 9 des percussions :
instrument :
kick
stick
snare1
snare2
hihatc
hihatf
hihato
crash1
ride1_bell
ride1
key :
36
37
38
40
42
44
46
49
51
53
Le détail des sons est congurable dans les chiers
sp
patches/midisyn.conf patches/piano/piano.conf
CHAPITRE 21.
etc...
MESSAGES MIDI (MUSICAL) AVEC LA LIBRAIRIE SNDIO
150
On peut par exemple rajouter de nouveaux sons en ajoutant les chiers raw cor-
respondant et un chier de conguration associé.
//===== Programme gamme . c c ==========
#i n c l u d e <i o s t r e a m >
using
namespace
#i n c l u d e
int
std ;
" midi . h"
main ( )
{
Midi
midi ;
//−−− o u v r e
port
int
port = 0;
int
res =
midi
out
midi . I n i t i a l i s e _ p o r t _ m i d i _ o u t ( port ) ;
i f ( r e s ==0)
{
c o u t <<"p o r t
est
encore
f e r m e ."<< e n d l ;
exit (1);
}
else
c o u t <<"p o r t
o u v e r t ."<< e n d l ;
//.....
int
ch = 0 ;
int
inst
//
= 0;
canal
//
instrument ,
m i d i . Program_Change ( p o r t ,
for ( int
key =60;
key <=72;
0:
ch ,
piano
inst );//
associe
k e y++) // montee
de
la
l ' instrument
gamme
{
//.......
int
vol
joue
une
midi
. . . . . . . . . .
=100;
c o u t <<" j o u e
n o t e "<<key<<e n d l ;
midi . Joue_note ( p o r t ,
//...
note
ch ,
key ,
vol ) ;
ride
midi . Joue_note ( p o r t ,
9,
53 ,
vol ) ;
inst
chromatique
au
c
CHAPITRE 21.
MESSAGES MIDI (MUSICAL) AVEC LA LIBRAIRIE SNDIO
midi . S l e e p ( 2 0 0 ) ;
//...
//
attente
151
en ms
stick
midi . Joue_note ( p o r t ,
midi . S l e e p ( 2 0 0 ) ;
//....
eteind
une
//
9,
37 ,
vol ) ;
attente
en ms
note
midi . Eteind_note ( port ,
midi
ch ,
. . . . . . . . .
key ) ;
}
//...
splash
midi . Joue_note ( p o r t ,
//−−
ferme
les
ports
midi
9,
55 ,
100);
ouverts
m i d i . Ferme_port_midi_out ( p o r t ) ;
return
0;
}
21.4
Reception de notes midi depuis un piano électrique
Comme application de l'exemple précédent, le programme reception_notes.cc suivant
ecoute les messages midi et les analyse. Si il reconnait Note on et Note o , il ache les
notes identiées.
Travail préalable
Brancher un piano électrique ou autre instrument midi sur l'ordinateur, et
observer le numéro de port par l'instruction
amidi -l dans un terminal. Cela donne
le numéro de port à mettre dans le programme suivant d'après la liste dans le chier
midi.h.
Par exemple
//===== Programme
namespace
(port 2 de l'instruction amidi -l) est sur le port 4.
r e c e p t i o n _ n o t e s . c c ==========
#i n c l u d e <i o s t r e a m >
using
rmidi/2
std ;
CHAPITRE 21.
#i n c l u d e
int
152
MESSAGES MIDI (MUSICAL) AVEC LA LIBRAIRIE SNDIO
" midi . h"
main ( )
{
Midi
midi ;
//−−− o u v r e
un
port
int
//
numero
p o r t =0;
int
midi
en
de
entree
port
r e s = midi . I n i t i a l i s e _ p o r t _ m i d i _ i n ( port ) ;
i f ( r e s ==0)
c o u t <<"p o r t
est
encore
f e r m e ."<< e n d l ;
c o u t <<"p o r t
o u v e r t ."<< e n d l ;
else
//−−−
attente
de
messages
midi . Attente_Message ( ) ;
//−−− a n a l y s e
f o r ( auto
des
message
messages
:
recus
m i d i . L_message )
{
int
p o r t _ i n =( i n t ) m e s s a g e [ 0 ] ;
//
numero
message . e r a s e ( message . begin ( ) ) ;
octet .
Il
reste
le
message
midi
//
de
enleve
port
le
entrant
1 er
standard
i f ( ( ( i n t ) m e s s a g e [ 0 ] ) / 1 6 == 0 x9 && ( ( i n t ) m e s s a g e [ 2 ] )
//
note
on
avec
> 0
)
v e l >0
{
int
ch =( i n t ) m e s s a g e [ 0 ] − 0 x 9 0 ;
int
k e y =( i n t ) m e s s a g e [ 1 ] ;
int
v e l =( i n t ) m e s s a g e [ 2 ] ;
c o u t <<"Note
on
recue
:
p o r t="<<p o r t _ i n <<"
c a n a l="<<ch
}
else
//
note
off
i f ( ( ( i n t ) m e s s a g e [ 0 ] ) / 1 6 = = 0 x8
||
( ( ( i n t ) message [ 0 ] ) / 1
CHAPITRE 21.
MESSAGES MIDI (MUSICAL) AVEC LA LIBRAIRIE SNDIO
153
{
int
ch =( i n t ) m e s s a g e [ 0 ] ;
i f ( c h /16==0x8 )
ch−= 0 x 8 0 ;
else
i f ( ch /16==0x9 )
ch−= 0 x 9 0 ;
int
k e y =( i n t ) m e s s a g e [ 1 ] ;
int
v e l =( i n t ) m e s s a g e [ 2 ] ;
c o u t <<"Note
off
recue
:
p o r t="<<p o r t _ i n <<"
}
}
//−−− f e r m e
port
midi
m i d i . Ferme_port_midi_in ( p o r t ) ;
return
0;
}
#==========================================
OBJETS_MIDI_R=
midi . o
:
reception_notes . o
midi . cc
$ (CC)
$ (CC)
:
−o
−c
−c
:
−o
$@
$ * . cc
−o
$@
$ (OBJETS_MIDI_R)
reception_notes
#==========================
all
$ * . cc
reception_notes . cc
$ (CFLAGS)
reception_notes :
$ (CC)
midi . h
$ (CFLAGS)
reception_notes . o
midi . o
reception_notes
$ (OBJETS_MIDI_R)
$ ( LIBR )
c a n a l="<<c
Chapitre 22
Convertir un programme C++ en
Javascript avec emscripten
Le langage javascript est un langage qui peut tourner sur toutes les plateformes (Windows, Mac, Android etc..) et qui est bien adapté au interfaces du web. Voici un tutorial.
Il est possible de convertir automatiquement un programme C++ en javascript à l'aide
du programme emscripten.
On convertit un programme c++
test.cc
avec
emcc test.cc -o test.html
On lance le programme avec
firefox test.html
154
en javascript encapsulé dans du html
Chapter 23
Autres librairies conseillées (?)
Trinilos
Math Kernel Library
23.1
algèbre linéaire
SimuNova
Eigen pour l'algèbre linéaire.
23.2
Images, vidéos, graphisme et boites de dialogues
FLTK pour les boites de dialogues et le graphisme. Bibliothèque légère et performante.
QT bibliothèque très générale, performante, multiplateformes mais assez complexe.
OpenCV pour le traitement d'image et de vidéos (CV signie Computer-Vision).
Un Tutoriel. Livre conseillé : OpenCV by example de P. Joshi, D.M. Escriva et V.
Godoy.
155
Chapitre 24
Le traitement d'images et de vidéos
avec la librairie OpenCV
24.1
Commande de compilation
Avant d'écrire un programme utilisant les librairies de OpenCV, il faut, une fois pour
toutes,
Dans l'éditeur codeblocks
Dans Settings/Compiler/Linker_Settings/Other_linker_Options, rajouter :
` pkg− c o n f i g
−− c f l a g s −− l i b s
opencv `
Remarque 24.1.1. Si vous n'utilisez pas codeblocks, mais Makele par exemple, la compilation en ligne de commande du programme
main.cpp
pour donner l'executable
main
est :
g++ main . cpp
24.2
−o
main
−g − s t d=c++11
` pkg− c o n f i g
−− c f l a g s −− l i b s
opencv `
Lecture et ecriture de chiers images
Les commentaires expliquent les fonctions. Il faut avoir le chier Image.jpg dans le
répertoire du programme.
#i n c l u d e <i o s t r e a m >
using
namespace
std ;
#i n c l u d e < s t r i n g >
#i n c l u d e <o p e n c v 2 / c o r e / c o r e . hpp>
156
CHAPITRE 24. LE TRAITEMENT D'IMAGES ET DE VIDÉOS AVEC LA LIBRAIRIE OPENCV157
#i n c l u d e <o p e n c v 2 / h i g h g u i / h i g h g u i . hpp>
using
namespace
cv ;
//=========================
int
main ( i n t
argc ,
char
** a r g v )
{
//.....
Lecture
des
images
−>
img
Mat img= i m r e a d ( " Image . j p g " ) ;
//......
Ecriture
d ' une
i m w r i t e ( " Image2 . j p g " ,
//.....
Lecture
d ' un
int
x=img . c o l s / 2 ;
int
y=img . r o w s / 2 ;
Vec3b
img
−>
fichier
Image2 . j p g
pixel
//
selectionne
point
(x , y)
au
centre
de
l ' image
p= img . a t<Vec3b >(x , y ) ;
c o u t << " La
//.....
valeur
dessin
de
imshow ( " Image " ,
//.....
int
image :
img ) ;
Attend
du
en
x="<<x<<", y="<<y<<"
est
( B , G, R ) :
( " << ( i n t ) p
l ' image
img ) ;
qu ' une
c = waitKey ( 0 ) ;
return
pixel
//
touche
soit
rem :
montre
appuyee .
le
dessin .
Renvoit
le
code
de
la
0;
}
Resultat :
Ache deux images : une en couleur et l'autre en noir et blanc.
Pixel value (B,G,R): (3,7,12)
24.2.0.1
Fonctions utiles sur les images
Copie d'une image :
Mat image2 = image1; ne fait pas une copie des images mais seuleimage1 et image2 vont désigner la même image).
dupliquer l'image il faut faire image1.copyTo(image2);
La commande
ment de l'adresse (c'est à dire que
Pour
touche .
O
CHAPITRE 24. LE TRAITEMENT D'IMAGES ET DE VIDÉOS AVEC LA LIBRAIRIE OPENCV158
Recadrage d'une image :
resize(frame, frame, Size(), scalingFactor, scalingFactor, INTER_AREA) ;
Mat img= imread("Image.jpg", 0) ; // 0 : convertit en gris
24.3
Utilisation de la souris
Le programme suivant montre comment utiliser la souris. Dans cet exemple on selectionne une partie de l'image avec clic gauche (down/up) et on revient à l'image de départ
avec clic droit (down). Le codesuivant utilise le chier image M2.png à télécharger dans le
même répertoire.
#i n c l u d e <i o s t r e a m >
using
namespace
std ;
#i n c l u d e < s t r i n g >
CHAPITRE 24. LE TRAITEMENT D'IMAGES ET DE VIDÉOS AVEC LA LIBRAIRIE OPENCV159
#i n c l u d e <o p e n c v 2 / c o r e / c o r e . hpp>
#i n c l u d e <o p e n c v 2 / h i g h g u i / h i g h g u i . hpp>
using
namespace
cv ;
//−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
Mat img ,
int
image ,
roi ;
etat_select = 0;//
Rect
selection ;
Point
//−−−−
static
origin ;
//
//
fonction
void
0:
selection
taille
point
de
appelee
onMouse (
de
int
la
la
de
souris
event ,
faite ,
1:
selection
en
cours ,
2:
sele
selection
depart
si
pas
int
la
selection
est
x,
actionnee
int
y,
int ,
void *
)
{
//
c o u t <<"e v e n t="<<e v e n t <<" x="<<x<<" y="<<y<<e n d l ;
//−−−
switch (
si
action
event
du
clic
souris
)
{
case
1:
//
clic
gauche
down
i f ( e t a t _ s e l e c t ==0)
{
origin
= Point ( x , y ) ;
selection
= Rect ( x , y , 0 , 0 ) ;
etat_select = 1;
}
break ;
case
4:
if (
//
clic
gauche
up
e t a t _ s e l e c t ==1 && s e l e c t i o n . w i d t h > 0 && s e l e c t i o n . h e i g h t > 0
)
{
etat_select
=2;
bitwise_not ( roi ,
imshow ( " Image " ,
}
break ;
//
selection
roi );
roi );
//
finie
inversion
de
roi
( dans
imag
CHAPITRE 24. LE TRAITEMENT D'IMAGES ET DE VIDÉOS AVEC LA LIBRAIRIE OPENCV160
case
2:
// c l i c
droit
down
i f ( e t a t _ s e l e c t ==2)
{
etat_select = 0;
imshow ( " Image " ,
img ) ;
}
}
//−−−
if (
si
deplacement
e t a t _ s e l e c t == 1
souris ,
traitement
de
l ' image
)
{
s e l e c t i o n . x = MIN( x ,
origin .x );
s e l e c t i o n . y = MIN( y ,
origin .y );
− origin .x );
: : abs ( y − o r i g i n . y ) ;
s e l e c t i o n . width = s t d : : abs ( x
s e l e c t i o n . height = std
s e l e c t i o n &= R e c t ( 0 ,
0,
img . c o l s ,
img . r o w s ) ;
//
pour
ne
i f ( s e l e c t i o n . w i d t h > 0 && s e l e c t i o n . h e i g h t > 0
pas
depasser
)
{
img . copyTo ( i m a g e ) ;
roi
= Mat ( image ,
bitwise_not ( roi ,
imshow ( " Image " ,
}
}
}
//=========================
int
main ( i n t
argc ,
char
** a r g v )
{
//.....
Lecture
des
images
img= i m r e a d ( "M2 . png " ) ;
selection );
roi );
image ) ;
//
//
//
la
matrice
inversion
montre
de
image
roi
roi
est
( dans
avec
sa
un
imag
selec
CHAPITRE 24. LE TRAITEMENT D'IMAGES ET DE VIDÉOS AVEC LA LIBRAIRIE OPENCV161
//.....
Affiche
namedWindow (
l ' image
" Image " ,
setMouseCallback (
imshow ( " Image " ,
1
a
l ' ecran
);
" Image " ,
onMouse ,
0
);
//
associe
la
fonction
de
img ) ;
waitKey ( 0 ) ;
return
0;
}
24.4
Lecture et écriture d'un chier video ou de la webcam
#i n c l u d e <i o s t r e a m >
#i n c l u d e < s t r i n g >
#i n c l u d e <s s t r e a m >
using
namespace
std ;
#i n c l u d e
" o p e n c v 2 / c o r e . hpp "
#i n c l u d e
" o p e n c v 2 / h i g h g u i . hpp "
using
namespace
cv ;
//−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
int
main (
int
argc ,
const
c h a r **
argv
)
{
//....
ouverture
String
nom = " v i d e o _ p e n d u l e . mp4 " ;
VideoCapture
du
fichier
video
ou webcam
//
fichier
cap ;
c a p . o p e n ( nom ) ;
// c a p . o p e n ( 0 ) ; / /
ouvre
i f ( ! cap . isOpened ( ) )
return
/ / . . . ouverture
//
la
webcam .
si
erreur
d ' ouverture
− 1;
d ' un
fichier
video
en
ecriture
video
lecture
de
CHAPITRE 24. LE TRAITEMENT D'IMAGES ET DE VIDÉOS AVEC LA LIBRAIRIE OPENCV162
String
Size
nom2= " v i d e o _ p e n d u l e 2 . a v i " ;
(
out
video
( nom2 , CV_FOURCC( ' P ' , ' I ' , 'M' , ' 1 ' ) ,
! out . isOpened ( )
boucle
20 ,
frameSize ,
static_
true ) ;
)
− 1;
return
//....
fichier
f r a m e S i z e ( s t a t i c _ c a s t <i n t >( c a p . g e t (CV_CAP_PROP_FRAME_WIDTH) ) ,
VideoWriter
if
//
infinie
while ( true )
{
Mat img ;
c a p >> img ;
//
extrait
imshow ( " V i d e o " ,
img ) ;
o u t . w r i t e ( img ) ;
//
la
//
ecrit
i f ( w a i t K e y ( 3 0 ) >= 0 )
prochaine
dessin
de
l ' image
break ;
//
image
img
dans
la
dans
fichier
Montre
dessin .
fenetre
Attente
Video
de
30 ms
}
cap . r e l e a s e ( ) ;
return
// l i b e r e
la
memoire
allouee
0;
}
Remarque 24.4.1. La commande waitKey(0) permettrait de faire avancer la video image
par image.
24.5
Détection d'objet par leur couleur
Voici un exemple de programme qui lit une image et selectionne les objets bleus. Pour
acher les images intermediares, il faut décommenter les lignes correspondantes.
Voici une documentation sur les formats de couleurs. On utilise le format HSV (voir
H ∈ [0, 1] est la
H ∈ [0.5, 0.7] est une palette de bleus. S ∈ [0, 1]
est la saturation entre S = 0 (blanc) et S = 1 (couleur vive). V ∈ [0, 1] est la
luminosité entre V = 0 (noir) et V = 1
aussi HSV sur wikipedia) qui est proche de la perception humaine :
teinte de la couleur, par exemple
Sauver l'image objet_bleu.jpg ci-dessous dans le répertoire du programme.
et
arr
CHAPITRE 24. LE TRAITEMENT D'IMAGES ET DE VIDÉOS AVEC LA LIBRAIRIE OPENCV163
Résultat du programme :
CHAPITRE 24. LE TRAITEMENT D'IMAGES ET DE VIDÉOS AVEC LA LIBRAIRIE OPENCV164
#include <iostream>using namespace std;#include <string> #include <opencv2/core/core.hpp
24.6
Détection et suivit d'objet de couleur avec l'algorithme CamShift
Voici des informations, sur les algorithmes MeanShift et CamShift , ou sur wikipedia.
CHAPITRE 24. LE TRAITEMENT D'IMAGES ET DE VIDÉOS AVEC LA LIBRAIRIE OPENCV165
Le code ci-dessous est tiré du site web de opencv.
#i n c l u d e
" o p e n c v 2 / v i d e o / t r a c k i n g . hpp "
#i n c l u d e
" o p e n c v 2 / i m g p r o c / i m g p r o c . hpp "
#i n c l u d e
" o p e n c v 2 / h i g h g u i / h i g h g u i . hpp "
#i n c l u d e <i o s t r e a m >
#i n c l u d e <c t y p e . h>
using
namespace
cv ;
using
namespace
std ;
CHAPITRE 24. LE TRAITEMENT D'IMAGES ET DE VIDÉOS AVEC LA LIBRAIRIE OPENCV166
//−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
Mat
image ;
bool
backprojMode =
bool
showHist =
bool
selectObject =
int
false ;
false ;
selection ;
Point
origin ;
//−−−−
static
Si
//
//
//0:
taille
point
de
l ' utilisateur
void
onMouse (
//
true :
true :
false ;
trackObject = 0;
Rect
//
//
de
la
si
on
montre
l ' image
seuillee
l ' histogramme
est
selection
depart
event ,
on
en
train
faite .
− 1:
de
selectionner
selection
faite
m a i s à selection
de
selection
int
si
montre
true :
pas
de
on
un
int
la
selection
rectangle
x,
int
y,
−>
int ,
selection
void *
et
le
flag
trackO
)
{
//
c o u t <<"e v e n t="<<e v e n t <<" x="<<x<<" y="<<y<<e n d l ;
if (
selectObject
)
{
s e l e c t i o n . x = MIN( x ,
origin .x );
s e l e c t i o n . y = MIN( y ,
origin .y );
− origin .x );
: : abs ( y − o r i g i n . y ) ;
s e l e c t i o n . width = s t d : : abs ( x
s e l e c t i o n . height = std
s e l e c t i o n &= R e c t ( 0 ,
0,
image . c o l s ,
image . rows ) ;
//
}
switch (
event
)
{
c o u t <<"e v e n t="<<e v e n t <<e n d l ;
c a s e CV_EVENT_LBUTTONDOWN:
origin
//
coin
de
depart
= Point ( x , y ) ;
selection
= Rect ( x , y , 0 , 0 ) ;
selectObject = true ;
break ;
c a s e CV_EVENT_LBUTTONUP:
selectObject =
if (
false ;
s e l e c t i o n . w i d t h > 0 && s e l e c t i o n . h e i g h t > 0
trackObject =
break ;
}
− 1;
)
pour
ne
pas
depas
CHAPITRE 24. LE TRAITEMENT D'IMAGES ET DE VIDÉOS AVEC LA LIBRAIRIE OPENCV167
}
//−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
static
void
help ()
{
c o u t << "\ n T h i s
"You
select
" Usage :
"
is
a demo
a
color
c o u t << "\ n \ nHot
"\ t c
"\ t b
"\ t h
"\ t p
"To
shows
objects
mean− s h i f t
such
as
your
based
face
t r a c k i n g \n"
and
it
−
−
−
−
−
keys :
quit
stop
the
p rog ram \ n "
t r a c k i n g \n"
t o / from
show / h i d e
pause
\n"
the
switch
\n " ;
backprojection
object
view \n"
h i s t o g r a m \n"
v i d e o \n"
initialize
tracking ,
select
the
object
with
mouse \ n " ;
}
///−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
int
main (
int
argc ,
const
c h a r **
argv
)
{
help ( ) ;
VideoCapture
Rect
int
cap ;
trackWindow ;
hsize = 16;
float
hranges [ ]
const
float
String
//
*
//
pour
if (
histogramme
= {0 ,180};
phranges = hranges ;
v i d e o F i l e= " p e n d u l e _ j a u n e . mp4 " ;
cap . open ( v i d e o F i l e ) ;
cap . open ( 0 ) ;
//
ouvre
! cap . isOpened ( )
//
ouvre
le
//
fichier
fichier
video
video
camera
)
{
c o u t << " *** Could
return
}
tracks
\n"
. / camshiftdemo
"\ tESC
that
− 1;
not
initialize
capturing . . . * * * \ n ";
i t . \ n"
CHAPITRE 24. LE TRAITEMENT D'IMAGES ET DE VIDÉOS AVEC LA LIBRAIRIE OPENCV168
//...
initialise
les
fenetres
namedWindow (
" Histogram " ,
namedWindow (
" C a m S h i f t Demo " ,
setMouseCallback (
//....
int
pose
les
vmin = 1 0 ,
0
);
0
);
" C a m S h i f t Demo " ,
onMouse ,
0
);
//
associe
vmax = 2 5 6 ,
createTrackbar (
"Vmax" ,
" C a m S h i f t Demo " , &vmax ,
256 ,
0
);
createTrackbar (
" Smin " ,
" C a m S h i f t Demo " , &smin ,
256 ,
0
);
Mat
hue ,
hsv ,
paused =
d
smin = 3 0 ;
"Vmin " ,
frame ,
fonction
sliders
createTrackbar (
bool
la
" C a m S h i f t Demo " , &vmin ,
mask ,
hist ,
256 ,
0
);
h i s t i m g = Mat : : z e r o s ( 2 0 0 ,
3 2 0 , CV_8UC3 ) ,
false ;
//−−−−−− b o u c l e
infinie
−−−−−−−−−−
for ( ; ; )
{
if (
! paused
)
{
c a p >> f r a m e ;
if (
f r a m e . empty ( )
)
break ;
}
f r a m e . copyTo ( i m a g e ) ;
if (
! paused
//
−>
image
)
{
c v t C o l o r ( image ,
hsv , COLOR_BGR2HSV ) ;
if (
)
trackObject
//
convertit
−>
hsv
{
int
//
_vmin = vmin ,
seuillage
i n R a n g e ( hsv ,
// imshow (
−>
_vmax = vmax ;
mask
( fenetre
Scalar (0 ,
" mask " ,
smin ,
mask ) ;
de
0
et
1)
MIN( _vmin , _vmax ) ) ,
Scalar (180 ,
2
CHAPITRE 24. LE TRAITEMENT D'IMAGES ET DE VIDÉOS AVEC LA LIBRAIRIE OPENCV169
//
Mix
int
the
ch [ ]
specified
= {0 ,
channels
0};
hue . c r e a t e ( h s v . s i z e ( ) ,
m i x C h a n n e l s (& hsv ,
//
on
if (
traite
la
hsv . depth ( ) ) ;
1 , &hue ,
selection
trackObject < 0
1,
−>
ch ,
//
1);
−>
//
hue :
−>
nouvelle
copie
hsv
fene
dans
hu
interest
(r
histogramme
)
{
c o u t <<" i c i "<< e n d l ;
//
Create
Mat
//
imshow (
// imshow (
" hue " ,
" mask " ,
images
r o i ( hue ,
based
on
selection ) ,
selected
regions
of
m a s k r o i ( mask ,
selection );
and
it
hue ) ;
mask ) ;
// w a i t K e y ( 0 ) ;
//
Compute
the
histogram
c a l c H i s t (& r o i ,
1,
normalize ( hist ,
trackWindow =
0,
normalize
maskroi ,
hist ,
0,
hist ,
−>
tableau
1 , &h s i z e ,
hist
&p h r a n g e s )
2 5 5 , CV_MINMAX) ;
selection ;
trackObject = 1;
histimg = Scalar : : a l l ( 0 ) ;
int
binW = h i s t i m g . c o l s
Mat
buf ( 1 ,
for (
int
i
hsize ,
= 0;
/
hsize ;
CV_8UC3 ) ;
i <
hsize ;
i++ )
b u f . a t<Vec3b >( i ) = Vec3b ( s a t u r a t e _ c a s t <u c h a r >( i
c v t C o l o r ( buf ,
buf ,
//
rectangles
dessin
for (
int
des
i
= 0;
*180./
CV_HSV2BGR ) ;
i <
de
hsize ;
l ' histogramme
sur
la
fenetre
i++ )
{
int
v a l = s a t u r a t e _ c a s t <i n t >( h i s t . a t<f l o a t >( i ) * h i s t i m
rectangle (
histimg ,
Point ( i
* binW , h i s t i m g . r o w s ) ,
Point
}
}
//
Compute
the
histogram
c a l c B a c k P r o j e c t (&hue ,
1,
backprojection
0,
hist ,
backproj ,
&p h r a n g e s ) ;
//
−>
CHAPITRE 24. LE TRAITEMENT D'IMAGES ET DE VIDÉOS AVEC LA LIBRAIRIE OPENCV170
b a c k p r o j &= mask ;
//...
trackwindow ,
RotatedRect
Check
if (
ameliore
backproj
backproj
−>
trackBox
trackBox = CamShift ( b a c k p r o j ,
T e r m C r i t e r i a ( CV_TERMCRIT_EPS |
//
//
if
CV_TERMCRIT_ITER,
the
area
of
10 ,
1
));
is
too
small
sure
the
trackingRect
trackingRect
trackWindow . a r e a ( ) <= 1
trackWindow ,
)
{
//
Use
int
an
cols
offset
value
to
= backproj . cols ,
make
r o w s = b a c k p r o j . rows ,
trackWindow = R e c t ( trackWindow . x
−
r ,
trackWindow . x + r ,
Rect ( 0 ,
0,
cols ,
has
r = (MIN(
trackWindow . y
−
r ,
trackWindow . y + r ) &
rows ) ;
}
if (
backprojMode
cvtColor (
//
draw
backproj ,
rotated
Point2f
)
image , COLOR_GRAY2BGR ) ;
rectangle
trackBox
rect_points [ 4 ] ;
trackBox . p o i n t s (
rect_points
);
RNG r n g ( 1 2 3 4 5 ) ;
Scalar
for (
color = Scalar (
int
j = 0;
line (
//
Draw
ellipse (
image ,
the
j <
}
if (
trackObject < 0
paused =
false ;
top
trackBox ,
}
else
on
)
255) ,
rng . uniform ( 0 , 2 5 5
j++ )
rect_points [ j ] ,
ellipse
image ,
4;
rng . uniform ( 0 ,
of
the
r e c t _ p o i n t s [ ( j +1)%4] ,
image
Scalar (0 ,0 ,255) ,
3 , CV_AA ) ;
color ,
CHAPITRE 24. LE TRAITEMENT D'IMAGES ET DE VIDÉOS AVEC LA LIBRAIRIE OPENCV171
//
Apply
if (
the
' negative '
effect
on
the
Mat
r o i ( image ,
selection );
bitwise_not ( roi ,
roi );
}
imshow (
" C a m S h i f t Demo " ,
imshow (
" Histogram " ,
char
if (
image
histimg
);
);
c = ( c h a r ) waitKey ( 1 0 ) ;
c == 27
)
//
esc .
break ;
switch ( c )
{
case
'b ' :
backprojMode =
! backprojMode ;
break ;
case
'c ' :
trackObject = 0;
histimg = Scalar : : a l l ( 0 ) ;
break ;
case
'h ' :
showHist =
if (
! showHist ;
! showHist
)
destroyWindow (
" Histogram "
);
else
namedWindow (
break ;
case
'p ' :
paused =
break ;
default :
;
}
}
}
region
of
interest
s e l e c t O b j e c t && s e l e c t i o n . w i d t h > 0 && s e l e c t i o n . h e i g h t > 0
{
return
selected
0;
! paused ;
" Histogram " ,
1
);
)
Chapitre 25
Gestion de projets avec chiers .h,
Makele, Git et GitHub
25.1
Projet avec plusieurs chiers, chier .h
Si on developpe un projet il est parfois utile d'écrire le code c++ dans diérents chiers.
D'une part pour l'organisation, pour la réutilisation ultérieure, etc..
Voici un projet informatique en C++, très simple (et sans trop de sens) qui est fait de
3 chiers :
un chier main.cc qui contient la fonction main() par laquelle commence le programme.
un chier autres.cc qui contient une classe Vect et une fonction f utilisées dans la
fonction main.
un chier main.h qui contient les déclarations de la classe Vect et de la fonction f.
Le suxe .h signie header (comme entêtes).
25.1.0.1
Résultat du programme :
Norme de v=1
3*3=9
25.1.0.2
//==
Code c++
fichier
main . c c =============
#i n c l u d e <i o s t r e a m >
using
namespace
#i n c l u d e
int
std ;
" main . h "
main ( )
172
CHAPITRE 25. GESTION DE PROJETS AVEC FICHIERS .H, MAKEFILE, GIT ET GITHUB173
{
Vect
v;
v . x =1;
c o u t <<"Norme
de
v="<<v . Norme()<< e n d l ;
c o u t <<"3*3="<< f (3)<< e n d l ;
return
0;
}
Remarque 25.1.1. Au début du chier main.cc, l'instruction #include "main.h" a pour
eet d'inclure le contenu du chier main.h à cet endroit. Il y a les déclarations (mais pas
le code) de la classe Vect et de la fonction f qui sont utilisées dans la fonction main().
//==
fichier
#i n c l u d e
a u t r e s . c c =============
" main . h "
#i n c l u d e <math . h>
//−−− c o n t e n u
du
constructeur
Vect : : Vect ( )
{
x =0;
y =0;
z =0;
}
//−−−−− c o n t e n u
double
de
la
fonction
Norme
V e c t : : Norme ( )
{
s q r t ( x * x+y * y+z * z ) ;
return
}
//−−− c o d e
double
de
la
fonction
f ( double
x)
return
x*x ;
f
{
}
Remarque 25.1.2. Le chier autres.cc contient le code des fonctions membres de la classe
Vect et le code de la fonction f(). Noter la syntaxe particulière Vect : :Norme() qui signie
fonction Norme de la classe Vect.
//==
#i f
fichier
main . h =============
! d e f i n e d (__MAIN_H)
#d e f i n e __MAIN_H
//−−−−−− d ' une
classe
CHAPITRE 25. GESTION DE PROJETS AVEC FICHIERS .H, MAKEFILE, GIT ET GITHUB174
class
Vect
{
public :
double
x,y,z;
Vect ( ) ;
double
//
constructeur
Norme ( ) ;
};
//
−−−−−
double
declaration
f ( double
d ' une
fonction
x);
#e n d i f
Remarque 25.1.3. Le chier main.h contient les déclarations (et non pas le code) de la
classe Vect et de la fonction f(). Les lignes particulières :
#if !dened(__MAIN_H)
#dene __MAIN_H
...
#endif
ne sont pas nécessaires ici mais sont souvent utiles : ce sont des instructions
du préprocesseur c'est à dire que au moment de la compilation, si le compilateur voit le code intermédiaire ... la première fois, il le considère (et met la
variable __MAIN_H à 1). Les fois suivantes, il ne considère par cette partie
de code. L'intérêt de cela est que si le chier main.h se trouve répété à cause
d'inclusions multiples (par exemple le chier A.cc inclut main.h et B.h, et le
chier B.h inclut main.h), alors le code n'est écrit qu'une seule fois, ce qui évite
une erreur de code déjà déclaré.
25.1.0.3
Commandes de compilation
g++ -c main.cc -o main.o
g++ -c autres.cc -o autres.o
g++ main.o autres.o -o main
Remarque 25.1.4. Le première ligne signie on compile le chier c++ main.cc et on fabrique un chier en langage machine main.o. De même pour la deuxièmre ligne. La troisième ligne signie à partir des hciers main.o et autres.o on fabrique le chier exécutable
nal main.
CHAPITRE 25. GESTION DE PROJETS AVEC FICHIERS .H, MAKEFILE, GIT ET GITHUB175
25.2
Un programme Makele pour gérer la compilation
Dans l'exemple de la section 25.1.0.3, si les chiers sont importants et si on eectue
main.cc, on souhaiterait de pas refaire la
-c autres.cc -o autres.o. Le programme Makefile
une modication du code que dans le chier
commande de compilation g++
permet de gérer cela automatiquement (il regarde les dates de modication des chiers).
#
−−−
fichier
main . o :
main . c c
g++
autres . o :
:
−c
main . h
main . c c
autres . cc
g++
final
Makefile
−c
main . o
−o
main . o
main . h
autres . cc
−o
autres . o
autres . o
g++ main . o
autres . o
−o
main
Remarque 25.2.1. Attention le décalage en début de ligne est une tabulation.
Commande de compilation
on écrit :
make final
résultat :
g++ -c main.cc -o main.o
g++ -c autres.cc -o autres.o
g++ main.o autres.o -o main
Commentaires :
Dans le chier Makele, les deux lignes
main.o: main.cc main.h
g++ -c main.cc -o main.o
signient que si on souhaite main.o, cela dépend des chiers
main.cc et main.h,
g++ -c main.cc
et si un de ces derniers a été modié il faut éxécuter la commande
-o main.o
la commande make nal signie que l'on souhaite final. Cela a pour eet d'enclencher les commandes de compilation nécessaires. Essayer par exemple de modier
main.cc et refaites make final.
g++ -c main.cc -o main.o
g++ main.o autres.o -o main
seulement le chier
Le résultat est :
CHAPITRE 25. GESTION DE PROJETS AVEC FICHIERS .H, MAKEFILE, GIT ET GITHUB176
25.2.1 Un programme Makele plus pratique
Le programme suivant est totalement équivalent au précédent mais il est plus pratique
car on a placé des instructions de compilation dans des variables (en lettres majuscules).
Ainsi si vous rajoutez des librairies, il sut de la faire à un seul endroit. De plus grâce au
symbols $@ et $* on évite des réécritures.
#
#
−−−
fichier
....
librairies
−lm
LIBR =
#
...
Makefile
instructions
CC =g++
autres . o :
main . h
−c
$ * . cc
autres . cc
$ (CC)
:
autres . o
main . c c
$ (CC)
final
compilation
− s t d=c++11
OBJETS= main . o
main . o :
de
−c
main . o
$ (CC)
−o
$@
main . h
$ * . cc
−o
$@
−o
main
autres . o
$ (OBJETS)
$ ( LIBR )
clean :
rm
*. o
Remarque 25.2.2.
Par exemple dans ce tutorial, on utilise souvent beaucoup de librairies et au total
on écrira :
LIBR=$(shell root-config --libs) $(shell root-config --glibs) -lm -lpthread
-lsndio -larmadillo -std=c++11 -lboost_system
CC =g++ -std=c++11 $(shell root-config --cflags) # compilateur
On a rajouté l'entrée clean qui permet d'eacer tous les chiers en langage machine
.o par la commande
25.3
make clean
Gestion de versions d'un projet avec Git
Le logiciel Git permet de gérer diérentes versions d'un projet. Il se souvient de l'historique. On lui indique une liste de chiers (le dépot) et régulièrement on lui demande
de faire une sauvegarde (par l'instruction commit)
CHAPITRE 25. GESTION DE PROJETS AVEC FICHIERS .H, MAKEFILE, GIT ET GITHUB177
25.3.0.1
Utilisation de Git sur son ordinateur
]La premiere fois :
Dans le répertoire de travail, taper
git init
Cela initialise un répertoire .git/
Ecrire (en mettant votre nom et adresse) :
git config --global user.name "Frederic Faure"
git config --global user.email [email protected]
Cela modie le chier ~/.gitconfig
A faire au cours du projet :
git status
git add chier.txt ou git add '*.cc' ou git add * pour ajouter un (des) chier(s)
:montre l'état des chiers par rapport au dépot.
au dépot
git commit -a ou git commit -a -m 'titre' fait une sauvegarde des chiers du
dépot avec le titre indiqué
git log pour voir l'historique des opérations
git rm chier :enleve un chier du dépot
git mv le1 le2 : deplace ou change le chier.
git di pour lire les dierences
Dans le chier
25.4
.gitignore il y a les extensions et repertoires à ignorer.
Collaboration et partage de projets sur internet
avec GitHub
Si vous développez un projet avec des collaborateurs, vous pouvez déposer (gratuitement) votre projet sur le site github (par exemple).
Au départ il faut vous créer un compte sur github qui va héberger votre projet.
]Pour poser/ retirer l'archive sur le site web de github :
Si votre projet s'appelle
projet,
et votre compte s'appelle
fdrfaure
Commandes :
git remote add projet https://github.com/fdrfaure/projet.git
Cela associe votre projet local au dépot sur le site web
git push -u expanding_map master
Cela dépose les dernières modications de votre projet sur le site web
git pull expanding_map master
Cela permet de récupérer les dernières modications (posées par les collaborateurs)
qui sont sur le site web.
CHAPITRE 25. GESTION DE PROJETS AVEC FICHIERS .H, MAKEFILE, GIT ET GITHUB178
git clone https://github.com/fdrfaure/projet.git repertoire_local
Cela copie le dépot du site web sur votre répertoire local. (a faire une premiere fois
pour copier un projet)
Bibliography
179