Vous êtes sur la page 1sur 269

Initiation

la programmation
http://www.free-livres.com/

avec Python et C++


Apprenez
simplement les
bases de la
programmation

Yves Bailly

Program Livre Page I Vendredi, 16. mai 2008 8:13 08

L E P R O G R A M M E U R

Initiation
la programmation
avec Python et C++

Yves Bailly

Program Livre Page II Vendredi, 16. mai 2008 8:13 08

Pearson Education France a apport le plus grand soin la ralisation de ce livre afin de vous fournir une information complte et fiable. Cependant, Pearson Education France nassume de responsabilits, ni pour son utilisation, ni pour les contrefaons de brevets ou atteintes aux droits de tierces
personnes qui pourraient rsulter de cette utilisation.
Les exemples ou les programmes prsents dans cet ouvrage sont fournis pour illustrer les descriptions
thoriques. Ils ne sont en aucun cas destins une utilisation commerciale ou professionnelle.
Pearson Education France ne pourra en aucun cas tre tenu pour responsable des prjudices
ou dommages de quelque nature que ce soit pouvant rsulter de lutilisation de ces exemples ou
programmes.
Tous les noms de produits ou marques cits dans ce livre sont des marques dposes par leurs
propritaires respectifs.
Publi par Pearson Education France
47 bis, rue des Vinaigriers
75010 PARIS
Tl. : 01 72 74 90 00
Mise en pages : TyPAO
ISBN : 978-2-7440-4086-3
Copyright 2009 Pearson Education France
Tous droits rservs

Aucune reprsentation ou reproduction, mme partielle, autre que celles prvues larticle L. 122-5 2 et 3 a) du code de la
proprit intellectuelle ne peut tre faite sans lautorisation expresse de Pearson Education France ou, le cas chant, sans le
respect des modalits prvues larticle L. 122-10 dudit code.
No part of this book shall be reproduced, stored in a retrieval system, or transmitted by any means, electronic, mechanical,
photocopying, recording, or otherwise, without written permission from the publisher.

Program Livre Page III Vendredi, 16. mai 2008 8:13 08

Sommaire

Introduction ...........................................

VII

10. Mise en abme : la rcursivit ........

129

1. Mise en place .....................................

11. Spcificits propres au C++ ............

139

2. Stockage de linformation :
les variables .......................................

15

12. Ouverture des fentres :


programmation graphique ..............

153

3. Des programmes dans un programme :


les fonctions .......................................
29

13. Stockage de masse : manipuler


les fichiers ..........................................

185

4. Linteractivit : changer
avec lutilisateur ...............................

14. Le temps qui passe ..........................

193

15. Rassembler et diffuser :


les bibliothques ...............................

207

16. Introduction OpenGL ..................

215

39

5. Ronds-points
et embranchements : boucles
et alternatives ....................................

47

6. Des variables composes ...................

59

17. Aperu de la programmation


parallle .............................................

229

7. Les Tours de Hano ...........................

79

18. Aperu dautres langages ...............

237

8. Le monde modlis : la programmation


oriente objet (POO) ........................ 103

19. Le mot de la fin ................................

247

Glossaire .................................................

251

9. Hano en objets ..................................

Index .......................................................

253

115

Program Livre Page IV Vendredi, 16. mai 2008 8:13 08

Program Livre Page V Vendredi, 16. mai 2008 8:13 08

Table des matires

Introduction ................................................ IX

3.2. Les paramtres ................................. 33

Un peu dhistoire ..................................... X


Le choix des langages ............................. XI
Et maintenant ...................................... XII
Convention dcriture .................................. XIII

3.3. Les blocs dinstructions ................... 35

Partie 1 - Dcouverte

CHAPITRE 1. Mise en place .........................

1.1. Windows ...........................................


1.2. Mac OS X ........................................
1.3. Linux ................................................
1.4. Votre premier programme ................

5
8
8
9

3.4. Retourner une valeur ........................ 36


3.5. Quand crer une fonction ? .............. 38
CHAPITRE 4. Linteractivit : changer
avec lutilisateur .................................... 39
4.1. Salutations ! ..................................... 40
4.2. Obtenir un nombre entier ................. 42

CHAPITRE 2. Stockage de linformation :


les variables ............................................ 15
2.1. Des types des donnes .....................
2.2. Les entiers ........................................
2.3. Les dcimaux et la notation
scientifique ..............................................
2.4. Les chanes de caractres .................
2.5. Erreurs typiques lies aux variables .

19
20

4.3. Obtenir une valeur dcimale ............ 44


4.4. Saisir plusieurs valeurs ..................... 44
CHAPITRE 5. Ronds-points
et embranchements : boucles
et alternatives ......................................... 47
5.1. Lalternative ..................................... 49
5.2. La boucle .......................................... 54
5.3. Algbre de Boole et algorithmique .. 57
CHAPITRE 6. Des variables composes ...... 59

21
23
25

6.1. Plusieurs variables en une :


les tableaux .............................................. 60
6.2. Plusieurs types en un : structures ..... 67
6.3. Variables composes et paramtres .. 72

CHAPITRE 3. Des programmes


dans un programme : les fonctions ...... 29

CHAPITRE 7. Les Tours de Hano ............... 79

3.1. Dfinir une fonction ......................... 31

7.1. Modlisation du problme ............... 82

Program Livre Page VI Vendredi, 16. mai 2008 8:13 08

VI

Initiation la programmation avec Python et C++

7.2. Conception du jeu .................................. 86


7.3. Implmentation ...................................... 90

CHAPITRE 13. Stockage de masse :


manipuler les fichiers ............................ 185
13.1. criture .................................................187

Partie 2 - Dveloppement logiciel

101

13.2. Lecture ..................................................189


13.3. Apart : la ligne de commande .............190

CHAPITRE 8. Le monde modlis :


la programmation oriente objet (POO) ... 103

CHAPITRE 14. Le temps qui passe ................... 193


14.1. Ralisation dun chronomtre ...............195

8.1. Les vertus de lencapsulation ................. 105


8.2. La rutilisation par lhritage ................. 109
8.3. Hritage et paramtres :
le polymorphisme ......................................... 112

Partie 3 - Pour aller plus loin

205

CHAPITRE 9. Hano en objets ........................... 115

CHAPITRE 15. Rassembler et diffuser :


les bibliothques .......................................... 207

9.1. Des tours compltes ............................... 116

15.1. Hirarchie dun logiciel ........................209

9.2. Un jeu autonome .................................... 119

15.2. Dcoupage de Hano .............................210

9.3. Linterface du jeu ................................... 122

CHAPITRE 16. Introduction OpenGL ........... 215

9.4. Le programme principal ......................... 126


CHAPITRE 10. Mise en abme :
la rcursivit ................................................ 129
10.1. La solution du problme ...................... 130
10.2. Une fonction rcursive ......................... 132
10.3. Intgration au grand programme .......... 135

16.1. Installation ............................................217


16.2. Triangle et cube de couleur ...................217
16.3. La GLU et autres bibliothques ............226
CHAPITRE 17. Aperu de la programmation
parallle ....................................................... 229
17.1. Solution simple pour Hano ..................231

CHAPITRE 11. Spcificits propres au C++ ..... 139

17.2. Solution paralllise .............................232

11.1. Les pointeurs ........................................ 140

17.3. Prcautions et contraintes .....................236

11.2. Mmoire dynamique ............................ 142

CHAPITRE 18. Aperu dautres langages ........ 237

11.3. Compilation spare ............................ 144


11.4. Fichiers de construction ....................... 149

18.1. Ada ........................................................238


18.2. Basic .....................................................240

CHAPITRE 12. Ouverture des fentres :


programmation graphique ......................... 153

18.3. C# ..........................................................241

12.1. La bibliothque Qt .............................. 154

18.5. Pascal ....................................................243

12.2. Installation des bibliothques Qt


et PyQt .......................................................... 155

18.6. Ruby ......................................................244

12.3. Le retour du bonjour ........................... 159

18.4. OCaml ...................................................242

CHAPITRE 19. Le mot de la fin ......................... 247

12.4. Un widget personnalis et boutonn .... 162

Glossaire ........................................................... 251

12.5. Hano graphique ................................... 169

Index ................................................................. 253

Program Livre Page IX Vendredi, 16. mai 2008 8:13 08

Introduction

Depuis que vous utilisez un ordinateur, vous avez trs certainement entendu parler de
cette activit mystrieuse quest "programmer", exerce par ces tres tranges que sont
les "programmeurs". Tout ce que vous voyez sur votre cran est lexpression de
programmes. Nous vous proposons, dans cet ouvrage, de passer de lautre ct de lcran,
de dcouvrir la programmation.
Un ordinateur est un outil, comme un tournevis. Mais la diffrence du tournevis,
lordinateur nest pas conu pour excuter une tche prcise et unique : il lest, au
contraire, pour tre fortement modulable et adaptable pratiquement tous les contextes possibles. Pour cela, il a la capacit dexcuter un certain nombre dordres, plus
communment appels instructions, chacune correspondant une tche simple et
lmentaire. Programmer consiste dfinir une succession ordonne dinstructions
lmentaires devant tre excutes par la machine, pour obtenir laccomplissement
dune tche complexe. Un aspect essentiel de la programmation est donc la mise en
pratique de la mthode cartsienne : dcomposer un problme complexe en sousproblmes simples.
Mais peut-tre vous demandez-vous : "Pourquoi programmer ?"
Simplement par jeu ! crire un programme et le voir sexcuter, chercher pourquoi il
ne fait pas ce que vous attendiez, est une activit ludique en soi.
Plus srieusement, aussi bien en milieu professionnel que familial, on prouve
frquemment le besoin dautomatiser certaines choses rptitives et laborieuses,
dobtenir certains rsultats, de traiter certaines donnes pour lesquelles on ne dispose

Program Livre Page X Vendredi, 16. mai 2008 8:13 08

Initiation la programmation avec Python et C++

pas de logiciel tout prt ou alors, ceux qui existent sont mal adapts ou trop onreux
acqurir. Dans ce genre de situations, avoir la capacit dcrire un petit programme
peut faire gagner beaucoup de temps et de prcision, de limiter les efforts. Voici un
autre aspect important de la programmation : elle est bien souvent la meilleure
rponse la paresse !

Un peu dhistoire
Les premiers ordinateurs taient programms laide de cartes perfores, les instructions tant donnes sous la forme de longues suites de 0 et de 1 quil fallait entrer
manuellement. Les programmeurs devaient se confronter directement au langage
machine, cest--dire au langage brut que comprend la machine qui na rien
dhumain. Au milieu des annes cinquante, apparurent les premiers langages de
programmation, dont lobjectif premier tait de dtacher le programmeur du langage
machine. Ds lors, les programmes sont crits dans un dialecte relativement comprhensible, respectant une syntaxe et une grammaire strictes et prcises. Comme ces
langages furent dvelopps par nos amis anglo-saxons, on pourrait les qualifier de
langue anglaise trs simplifie.
Tandis que le langage machine est, par nature, trs li llectronique de lordinateur, le dveloppement des langages de programmation a ouvert la voie de labstraction, principe fondamental qui cherche loigner autant que possible le
programmeur des contingences matrielles, pour le rapprocher des concepts, de la
"chose" concrte ou abstraite quil doit traiter. Lorsquon veut modliser informatiquement un moteur de voiture ou la courbe subtile dune fonction mathmatique,
autant ne pas trop sencombrer lesprit avec ltat molculaire des transistors du
microprocesseur
Cest ainsi quau fil du temps diffrents paradigmes (ensembles de rgles et de principes de conception) de programmation se sont dvelopps. Programmation structure,
fonctionnelle, oriente objet chaque volution, lobjectif est toujours le mme :
permettre la meilleure reprsentation informatique possible, la plus intuitive possible,
de lide sur laquelle opre le programme.
On peut esquisser une sorte darbre gnalogique des langages de programmation. En
donner une liste exhaustive est pratiquement impossible : des milliers sont ou ont
t utiliss. La Figure I en prsente tout de mme quelques-uns, les flches indiquant
les sources dinspiration utilises pour un langage donn.

Program Livre Page XI Vendredi, 16. mai 2008 8:13 08

Introduction

1960

1954

1970

1980

Fortran

1990

2000

Ada

Pascal
Modula
Algol

XI

Python

C
Ruby

C++

Simula

C#

Eiffel

Lisp
Smalltalk

Perl
Basic

Visual Basic

Java
VB.NET

Cobol

Figure I
Gnalogie simplifie de quelques langages de programmation.

Il ne sagit l que dune vue vraiment trs simplifie, presque caricaturale. Mais vous
pouvez constater que les langages sinspirent fortement les uns des autres. Par ailleurs,
la plupart de ceux prsents sur ce schma sont toujours utiliss et continuent
dvoluer cest, par exemple, le cas du vnrable anctre Fortran, notamment dans
les applications trs gourmandes en calculs scientifiques.

Le choix des langages


Il en va de la programmation comme de bien dautres disciplines : cest en forgeant
que lon devient forgeron. Cette initiation sappuiera donc sur deux langages de
programmation, appartenant deux grandes familles diffrentes : celle des langages
interprts et celle des langages compils.
Python

C++

Le langage Python a t conu en 1991,


principalement par le Nerlandais Guido
Van Rossum, comme successeur du langage
ABC, langage interactif lui-mme destin
remplacer le langage Basic.

Le langage C++ a t conu en 1983 par


Bjarne Stroustrup, comme une extension du
langage C, langage proche de llectronique
et encore trs largement utilis, pour lui
apporter les principes de la POO (programmation oriente objet).

Program Livre Page XII Vendredi, 16. mai 2008 8:13 08

XII

Initiation la programmation avec Python et C++

Python

C++

Le langage Python est un langage orient


objet interprt.

Le langage C++ est un langage orient


objet compil.

Les programmes crits dans un langage


interprt ne sont pas directement excuts
par lordinateur, mais plutt par un interprteur. Grossirement, linterprteur lit les
instructions du programme et les transforme en instructions destines la
machine. Linterprteur est donc toujours
ncessaire pour excuter un programme en
langage interprt.

Les programmes crits dans un langage


compil doivent subir une phase intermdiaire, la compilation, lissue de laquelle
on obtient un fichier directement excutable
par la machine. La compilation est effectue par un compilateur, qui nest alors plus
ncessaire ds que le fichier excutable a
t produit.

Il figure aujourdhui en bonne place dans de


nombreux domaines, de lanalyse scientifique la programmation de jeux, en passant
par la ralisation rapide de petits outils
spcialiss ou de sites web dynamiques.

Le C++ est aujourdhui lun des langages


les plus utiliss dans le monde, dans toutes
sortes de contextes, notamment ceux o la
rapidit dexcution est primordiale.

Bien quappartenant deux familles diffrentes, ces deux langages partagent un point
commun essentiel : ce sont des langages objet, cest--dire des langages utilisant les
techniques de la programmation oriente objet. Nous verrons plus loin ce que ce terme
sibyllin reprsente. Pour lheure, disons simplement quil sagit, de trs loin, du paradigme de programmation le plus rpandu dans le monde.

Et maintenant
Que les impatients se rassurent, nous allons trs bientt passer la pratique. Une
premire partie prsentera quelques-uns des concepts fondamentaux de la programmation au moyen de nombreux exemples, partir desquels nous construirons progressivement un vritable programme permettant de jouer au problme des Tours de Hano
(aussi appel problme des Tours de Brahma). Cela aprs avoir install sur votre
ordinateur les logiciels ncessaires, essentiellement linterprteur Python et le compilateur C++.
Une deuxime partie nous emmnera plus loin dans lexploration des techniques
permettant la ralisation dun logiciel, notamment en dcrivant ce quest exactement
cette mystrieuse programmation objet. Nous aboutirons alors un programme presque
complet, disposant dune interface graphique attrayante (du moins, esprons-le !).

Program Livre Page XIII Vendredi, 16. mai 2008 8:13 08

Introduction

XIII

Enfin, cet ouvrage se terminera par quelques pistes destines vous ouvrir de larges
horizons, de laffichage en trois dimensions la programmation parallle, sans oublier
une petite prsentation dautres langages courants.
Nous naurons pas la prtention de vous faire devenir en quelques pages un ingnieur
en dveloppement logiciel : cest un mtier part entire, qui ncessite des annes
dtudes et dexpriences. Ce livre nest en aucun cas un manuel pour apprendre les
langages Python ou C++ : ceux-ci ne sont utiliss que comme support des concepts
gnraux de la programmation, tels quils se retrouvent dans la plupart des langages de
programmation. Les approches qui seront proposes ici ne seront pas toujours les plus
efficaces en termes de performances, mais nous recherchons avant tout la comprhension et lacquisition des concepts. lissue de cette lecture, vous serez en mesure de
crer vos programmes pour vos propres besoins et daller chercher les informations
qui vous manqueront invitablement. Ce sera alors pleinement vous de jouer.

Convention dcriture
La flche indique la continuit de la ligne de code.

Program Livre Page XIV Vendredi, 16. mai 2008 8:13 08

Program Livre Page 1 Vendredi, 16. mai 2008 8:13 08

Partie

Dcouverte
CHAPITRE 1. Mise en place
CHAPITRE 2. Stockage de linformation : les variables
CHAPITRE 3. Des programmes dans un programme : les fonctions
CHAPITRE 4. Linteractivit : changer avec lutilisateur
CHAPITRE 5. Ronds-points et embranchements : boucles et alternatives
CHAPITRE 6. Des variables composes
CHAPITRE 7. Les Tours de Hano

Program Livre Page 2 Vendredi, 16. mai 2008 8:13 08

Program Livre Page 3 Vendredi, 16. mai 2008 8:13 08

1
Mise en place
Au sommaire de ce chapitre :

Windows

Mac OS X

Linux

Votre premier programme

Program Livre Page 4 Vendredi, 16. mai 2008 8:13 08

Initiation la programmation avec Python et C++

Pour forger, encore faut-il disposer dune forge et de quelques outils indispensables.
Nous allons donc commencer par mettre en place un environnement de dveloppement, cest--dire installer sur votre ordinateur les outils ncessaires la pratique de
la programmation.
Si vous cherchez un peu, vous trouverez sans doute de nombreux environnements
sophistiqus, chatoyants, parfois onreux. Mais noubliez pas que mme les plus
grands pilotes de Formule 1 ont appris conduire, probablement sur de petites
voitures Aussi allons-nous nous contenter dun environnement minimal, simple
matriser. Au moins dans un premier temps, nous naurons besoin que de trois
outils :

un interprteur du langage Python, pour excuter nos programmes crits en


Python ;

un compilateur du langage C++, pour compiler nos programmes crits en C++ afin
dobtenir un fichier excutable ;

un diteur de texte adapt la programmation.

Dtaillons ce dernier point. Un diteur de texte, raccourci pour dire en ralit


"programme permettant de modifier un fichier ne contenant que du texte brut", nest
en aucun cas un logiciel de traitement de texte. En effet, un fichier issu dun traitement
de texte ne contient pas que des caractres : il contient galement de nombreuses
informations de mise en forme, comme le gras, litalique, la taille de certaines
lettres Toutes ces informations sont parfaitement indigestes pour un interprteur ou
un compilateur.
Ce quil nous faut, cest un logiciel ne stockant rellement que les lettres que nous
allons taper au clavier pour crire nos programmes. Sous Windows, un exemple
typique est Notepad ; sous GNU/Linux, KEdit ou GEdit ; sous Mac OS X, TextEdit.
Sils pourraient convenir, ces (petits) logiciels sont pourtant trop simples pour notre
tche de programmation : il faut des outils possdant certaines fonctionnalits adaptes.
On pourrait citer les clbres Vim ou Emacs, sur tous les systmes, mais leur prise
en main peut tre pour le moins dlicate. Les sections suivantes proposeront des
outils plus simples manipuler.

Program Livre Page 5 Vendredi, 16. mai 2008 8:13 08

Chapitre 1

Mise en place

1.1. Windows
Pour commencer, nous vous recommandons dinstaller lditeur de texte Notepad++,
que vous trouverez sur le site http://notepad-plus.sourceforge.net/fr/
site.htm. Il sera plus que suffisant pour nos besoins. partir de la page indique,
suivez le lien Tlcharger, puis Tlcharger les binaires de Notepad++, enfin choisissez le fichier npp.4.6.Installer.exe. Excutez ce programme une fois tlcharg
afin dinstaller Notepad++.
Ensuite, linterprteur Python. Rcuprez le fichier dinstallation correspondant votre
systme depuis le site http://www.python.org/download/releases/2.5.1/, le plus
souvent sous la forme dun fichier dinstallation MSI. Par exemple, si vous tes sur un
systme 64 bits bas sur un processeur AMD, choisissez le fichier python2.5.1.amd64.msi. Si vous tes sur un systme 32 bits, probablement le cas le plus
rpandu, choisissez le fichier python-2.5.1.msi. Ouvrez ce fichier, acceptez les options
par dfaut proposes. Et voil, linterprteur Python est install !
Enfin, nous allons installer un compilateur C++, nommment celui faisant partie de la
suite GCC. Celle-ci est un ensemble de compilateurs trs largement utiliss dans la
vaste communaut du logiciel libre. Pour en obtenir une version sous Windows, nous
allons installer lenvironnement MinGW, dont vous pouvez tlcharger le programme
dinstallation partir du site http://sourceforge.net/project/showfiles.php?group_id=2435&package_id=240780. Choisissez la version la plus
rcente, celle contenue dans le fichier MinGW-5.1.3.exe au moment o ces lignes
sont crites. Une fois ce fichier tlcharg, excutez-le. Linstallation tant lgrement
plus complique que celle de linterprteur Python, nous allons la dtailler.
Le tlchargement des composants puis linstallation devraient se drouler sans
problme. Terminez tout cela en choisissant Next autant de fois que ncessaire.
Nous en avons presque termin. Pour finir, crez un rpertoire Programmes dans
lequel seront stocks les programmes que vous allez crire (par exemple la racine
du disque C:), puis crez un petit fichier de commande pour obtenir une ligne de
commande dans laquelle nous pourrons effectuer les compilations et excutions.
Pour cela, crez sur le bureau un fichier nomm prog.bat et contenant les lignes
suivantes :
set PATH=C:\Python25;C:\MinGW\bin;C:\MinGW\libexec\gcc\mingw32\3.4.5;%PATH%
C:
cd \Programmes
cmd.exe

Program Livre Page 6 Vendredi, 16. mai 2008 8:13 08

Initiation la programmation avec Python et C++

Cette installation ncessite une


connexion Internet, pour tlcharger
les composants ncessaires. Dans le
premier cran, choisissez Download
and Install puis cliquez sur Next.
Acceptez ensuite lcran de licence en
cliquant sur I Agree.

Ensuite vient le choix du type de


paquetage installer, parmi une
ancienne version, la version stable
actuelle ou une version exprimentale.
Choisissez Current, pour installer la
version stable actuelle.

Vous devez maintenant choisir les


lments installer. En plus du composant de base (le premier de la liste),
cochez les cases devant g++ compiler
et MinGW Make. Si vous le souhaitez,
rien ne vous interdit dinstaller galement les autres compilateurs (pour les
langages Fortran, Ada, Java et Objective-C), mais nous ne les aborderons
pas ici !

Program Livre Page 7 Vendredi, 16. mai 2008 8:13 08

Chapitre 1

Mise en place

Nous vous recommandons vivement


daccepter le choix par dfaut du rpertoire de destination, savoir C:\MinGW.
Vous pouvez naturellement choisir un
autre rpertoire, mais alors il vous
faudra adapter les indications qui vous
seront donnes par la suite.

Si vous avez install Python ou MinGW des endroits autres que ceux proposs par
dfaut, modifiez la ligne commenant par set PATH en consquence : elle est destine
faciliter laccs linterprteur et au compilateur. De mme, si vous avez cr le
rpertoire Programmes un autre endroit que la racine du disque C:, adaptez les deux
lignes suivantes.
Lorsque vous lancez ce fichier de commande, vous devriez obtenir quelque chose
comme ce qui est illustr par la Figure 1.1 (exemple sous Windows Vista).
Figure 1.1
Ligne de commande pour
la programmation sous
Windows.

Vous tes maintenant prt commencer programmer !

Program Livre Page 8 Vendredi, 16. mai 2008 8:13 08

Initiation la programmation avec Python et C++

1.2. Mac OS X
Un excellent diteur de texte adapt la programmation est Smultron, disponible
partir de la page http://smultron.sourceforge.net. Slectionnez la version adapte
votre version de Mac OS.
Ensuite, le plus simple pour disposer dun compilateur C++ est dinstaller le logiciel
Xcode, distribu gratuitement par Apple. Il sagit en fait dun environnement de dveloppement complet, le compilateur fourni tant celui issu de la suite GCC, le mme
que nous allons utiliser sous Windows et GNU/Linux. Vous trouverez lenvironnement
Xcode partir de la page http://developer.apple.com/tools/xcode/, le tlchargement ncessitant toutefois de senregistrer auprs dApple. Cela reprsente
environ 1 Go tlcharger, linstallation occupant environ 2,5 Go : prenez donc garde
ne pas remplir votre disque !
Pour linterprteur Python, rcuprez le fichier dinstallation python-2.5.1macosx.dmg depuis le site http://www.python.org/download/releases/2.5.1/.
Python ncessite au minimum une version 10.3.9 de Mac OS X et fonctionne aussi
bien sur les systmes base de processeurs PowerPC ou Intel.
Enfin, partir de Finder, crez un rpertoire Programmes dans votre dossier personnel : cest l que vous stockerez les diffrents fichiers que vous allez crer par la
suite.

1.3. Linux
Normalement, tout est dj install si vous utilisez une distribution gnraliste,
comme Mandriva, Debian, Ubuntu, RedHat, SuSE cette liste ntant pas exhaustive. Si tel ntait pas le cas, il suffit dinstaller quelques paquetages supplmentaires. Leurs noms peuvent varier dune distribution lautre, mais ils devraient se
ressembler.
Pour le compilateur C++, recherchez les paquetages nomms g++ et libstdc++.
Pour linterprteur Python, recherchez les paquetages nomms python2.5 et
python2.5-dev.

Program Livre Page 9 Vendredi, 16. mai 2008 8:13 08

Chapitre 1

Mise en place

Concernant lditeur de texte, vous disposez dun choix assez considrable, pour ne
pas dire droutant. Si vous utilisez principalement lenvironnement Gnome,
lditeur GEdit convient parfaitement. Si vous utilisez plutt lenvironnement KDE,
lditeur Kate est probablement le mieux adapt. Sachez que, quel que soit votre
environnement de bureau, vous pouvez utiliser nimporte lequel de ces deux
diteurs. Dautres sont naturellement disponibles, comme les clbres Emacs ou
Vim, mais sils sont extrmement puissants, ils peuvent tre dlicats prendre en
main.
Enfin, crez un rpertoire Programmes dans votre rpertoire personnel pour contenir
vos programmes.

1.4. Votre premier programme


Nous allons sacrifier une vieille tradition : lorsquil sagit de prsenter un langage ou
une technique de programmation, le premier programme consiste habituellement
tout simplement afficher le message "Bonjour, Monde !" Cette tradition remonte (au
moins) louvrage de Brian Kernighan, The C Programming Language, prsentant le
langage C en 1974, qui a eu une grande influence sur plus dune gnration de
programmeurs. Voici donc les programmes, en Python et en C++ :
Python

C++

Fichier bonjour.py :

Fichier bonjour.cpp :

1. # -*- coding: utf8 -*2. print "Bonjour, Monde!"

1. #include <iostream>
2. int main(int argc, char* argv[])
3. {
4.
std::cout << "Bonjour, Monde!"
<< std::endl;
5.
return 0;
6. }

Si vous tes surpris de voir que le programme C++ est beaucoup plus long que le
programme Python, sachez que cest une manifestation du fait que le langage Python
est un langage dit de haut niveau, tandis que le langage C++ est de plus bas niveau.
Dit autrement, en Python, linterprteur masque beaucoup daspects que le compilateur C++ laisse au grand jour. Chacune des deux approches prsente des avantages et
des inconvnients : tout dpend du contexte et de lobjectif de votre programme.

Program Livre Page 10 Vendredi, 16. mai 2008 8:13 08

10

Initiation la programmation avec Python et C++

Gnralement, on peut dire que masquer les dtails permet de programmer plus rapidement, tandis quavoir la possibilit dagir sur ces dtails permet damliorer les
performances au moment dexcuter le programme.
Mais revenons justement nos programmes. Les deux textes que vous voyez, et que
vous tes invit recopier dans votre diteur de texte, constituent ce que lon appelle
le code source dun programme. Le code source est ainsi le texte que le programmeur
tape dans un diteur. Cest ce texte que nous allons bientt soumettre, pour lun,
linterprteur Python, pour lautre, au compilateur C++.
Une prcision avant cela : vous aurez remarqu que les lignes des programmes sont
numrotes, de 1 2 en Python, de 1 6 en C++. Cette numrotation na pas dautre
objectif que de nous permettre de faire rfrence aisment telle ou telle partie du
programme, dans le cadre de cet ouvrage. En aucun cas, vous ne devez recopier ces
numros dans votre diteur de texte. Sil existe bien des langages de programmation
qui exigeaient que les lignes soient numrotes, comme les premiers Basic ou Cobol,
ce procd a t abandonn depuis fort longtemps car inefficace. Les Figures 1.2 1.4
montrent quoi pourrait ressembler votre diteur sur cran.
Windows

Figure 1.2
diteur de texte sous Windows (Notepad++).

Program Livre Page 11 Vendredi, 16. mai 2008 8:13 08

Chapitre 1

Mise en place

11

Mac OS X

Figure 1.3
diteur de texte sous Mac OS X (Smultron).

Linux

Figure 1.4
diteur de texte sous Linux (Kate).

Remarquez que les lignes sont automatiquement numrotes par lditeur lui-mme :
vous navez donc pas vous en soucier. Vous voyez galement que, ds que vous avez
sauvegard votre fichier, son aspect change : certains mots se voient attribuer une
couleur. Cest la mise en vidence syntaxique (ou syntax highlight en anglais), une aide
visuelle prcieuse lorsquon programme. chaque couleur correspond une certaine
nature de mot dans le programme, ce que nous allons dcouvrir au fil des pages qui
suivent.
Enfin, la norme du langage C++ exige que tout programme se termine par une ligne
vide : cest pourquoi vous voyez sept lignes sur les captures dcran au lieu de six.
Cette ligne vide supplmentaire sera implicite dans tous les exemples qui seront
donns par la suite, donc ne loubliez pas.

Program Livre Page 12 Vendredi, 16. mai 2008 8:13 08

12

Initiation la programmation avec Python et C++

1.4.1. Excution pour Python


tant un langage interprt, le programme Python peut tre excut directement par
linterprteur, comme ceci :
python bonjour.py

Par exemple, la Figure 1.5 vous montre le rsultat dans une ligne de commande Linux.
Figure 1.5
Excution du programme
sous Linux.

Cest un des principaux avantages des langages interprts : leur mise en uvre est
trs simple.
Si on regarde ce trs court programme, la premire ligne indique selon quel encodage
le texte du programme a t enregistr. Cela est particulirement important pour les
messages que vous voudrez afficher, notamment sils contiennent des lettres accentues. Laffichage proprement dit du message a lieu la ligne suivante. la fin de cet
affichage, un saut de ligne est automatiquement effectu.
Remarquez labsence de toute ponctuation la fin de la ligne 2, celle contenant
linstruction daffichage : cest l une diffrence fondamentale avec le programme en
langage C++, sur lequel nous allons nous pencher immdiatement.

1.4.2. Compilation et excution pour le C++


Nous lavons dit, le langage C++ est un langage compil, cest--dire que le code
source doit tre trait par le compilateur pour obtenir un fichier excutable.
Allez la fentre de ligne de commande, dans le rpertoire dans lequel vous avez
enregistr votre fichier bonjour.cpp. Excutez linstruction :
g++ -o bonjour bonjour.cpp

Cela va crer un fichier excutable nomm bonjour (nanti de lextension .exe sous
Windows) dans le rpertoire courant. Vous pouvez excuter ce programme immdiatement

Program Livre Page 13 Vendredi, 16. mai 2008 8:13 08

Chapitre 1

Mise en place

13

en appelant simplement bonjour, ou ./bonjour si vous tes sous GNU/Linux.


La Figure 1.6 montre un exemple sous Windows.
Figure 1.6
Compilation et excution
sous Windows.

Flicitations, vous venez de saisir, de compiler et dexcuter votre premier programme


en langage C++ ! Ce ntait pas si difficile, nest-ce pas ?
Mais examinons un peu ce code. La premire ligne, dont le sens profond sclaircira
plus tard, est ncessaire pour nous permettre dutiliser les capacits daffichage du
C++. Laffichage en lui-mme est effectu ligne 4, en "envoyant" un texte dlimit par
des guillemets dans un "objet daffichage". Cet objet est std::cout, lenvoi en luimme est symbolis par <<. Le prfixe std:: signale quil sagit dun objet de la
bibliothque standard, ce qui signifie quil est normalement disponible sur nimporte
quel autre compilateur C++. Nous verrons plus loin la signification exacte de ce
prfixe. Le morceau cout pourrait se traduire par console output, cest--dire sortie
vers lcran.
la suite du texte, on envoie (toujours avec <<) lobjet std::endl, qui est une
faon de reprsenter un saut de ligne. Encore une fois, cet objet fait partie de la
bibliothque standard, endl signifiant end of line (fin de ligne en franais). Sans
cela, nous naurions pas de saut de ligne aprs laffichage du texte, ce qui pourrait
tre disgracieux.
Les autres lignes font partie de ce quon pourrait appeler "lemballage" minimal dun
programme C++. Inutile de rentrer pour linstant dans les dtails (mais cela viendra).
Pour lheure, il suffit de savoir que, sans elles, le programme serait incorrect
et donc ne pourrait pas tre transform en fichier excutable par le compilateur.

Program Livre Page 14 Vendredi, 16. mai 2008 8:13 08

14

Initiation la programmation avec Python et C++

Peut-tre encore plus que linterprteur Python, un compilateur C++ peut se rvler particulirement tatillon sur lexactitude de ce quon crit.
Enfin, dernire remarque, la prsence du point-virgule est obligatoire pour sparer
deux instructions du programme. Si vous oubliez un seul point-virgule dans votre
programme, le compilateur vous couvrira dinsultes diverses et refusera de crer un
fichier excutable.

Program Livre Page 15 Vendredi, 16. mai 2008 8:13 08

2
Stockage
de linformation :
les variables
Au sommaire de ce chapitre :

Des types des donnes

Les entiers

Les dcimaux et la notation scientifique

Les chanes de caractres

Erreurs typiques lies aux variables

Program Livre Page 16 Vendredi, 16. mai 2008 8:13 08

16

Initiation la programmation avec Python et C++

Nous navons manipul dans les exemples prcdents que des valeurs littrales, cest-dire des donnes quon crit littralement, en toutes lettres. Mais lessence mme
dun programme est de travailler sur des donnes (plus ou moins) quelconques, pour
les combiner, les modifier, afin de produire un certain rsultat. Il est donc ncessaire
de les stocker afin de pouvoir les rutiliser plus tard. Ce stockage seffectue dans la
mmoire de lordinateur et chaque donne, pour tre ultrieurement identifie, reoit
un nom : cest ce quon dsigne usuellement par le terme de variable. Au sens strict,
une variable est une zone mmoire contenant une donne susceptible dtre modifie,
le nom ntant quun moyen dy accder. Dans la pratique, on identifie le nom la
variable, par abus de langage.
Considrez les deux programmes suivants :
Python

C++

1. # -*- coding: utf8 -*2. x = 5


3. print 10*x

1.
2.
3.
4.
5.
6.
7.
8.

La ligne 2 a deux effets : dabord, elle


dclare la variable x, ensuite la valeur 5 y
est stocke. Tout cela au moyen du simple
signe =.

La ligne 4 a pour effet de dclarer la


variable x. La valeur 5 y est stocke en
ligne 5, par le signe =.

En Python, la dclaration dune variable se


fait simplement en lui affectant une valeur.

#include <iostream>
int main(int argc, char* argv[])
{
int x;
x = 5;
std::cout << 10*x << std::endl;
return 0;
}

En C++, les variables doivent tre dclares


avant toute utilisation. Mais une dclaration
peut combiner une affectation.

Excutez ces deux programmes, comme nous lavons vu prcdemment. Les deux
devraient simplement afficher la valeur 50, alors que celle-ci napparat nulle
part Elle est en fait calcule partir de la valeur contenue dans une variable, ici
nomme x.
Dans pratiquement tous les langages de programmation, une variable doit tre
dclare avant dtre utilise. La dclaration dune variable consiste (au minimum)
lui donner un nom, afin que linterprteur ou le compilateur reconnaisse ce nom
par la suite et "sache" quil correspond une variable dont on veut utiliser le
contenu.

Program Livre Page 17 Vendredi, 16. mai 2008 8:13 08

Chapitre 2

Stockage de linformation : les variables

17

Techniquement, la dclaration dune variable consiste rserver une zone dune


certaine taille dans la mmoire de lordinateur et identifier cette zone avec une tiquette
(voir Figure 2.1). Lopration consistant placer une certaine valeur dans cette zone
est laffectation. Ltiquette, cest--dire le nom de la variable, est un moyen daccder
la valeur contenue dans cette zone.
Figure 2.1
Une variable permet
de stocker une valeur dans
la mmoire de lordinateur
et daccder celle-ci.

programme

mmoire

Regardez les instructions daffichage des deux programmes prcdents, ligne 3 en


Python et ligne 6 en C++. Le symbole toile (ou astrisque) * est celui de la multiplication. Lcriture 10*x peut donc se traduire par :
multiplier par 10 le contenu de la zone mmoire identifie par x

Ou, plus simplement et par un abus trs commun de langage :


multiplier par 10 la valeur de x

On demande ensuite simplement laffichage du rsultat de cette opration.


Il est naturellement possible daffecter une autre valeur la variable x, par exemple le
rsultat dune opration limpliquant elle-mme :
Python

C++

1. x = 5
2. x = 10*x
3. print x

1. x = 5;
2. x = 10*x;
3. std::cout << x << std::endl;

La deuxime ligne des extraits prcdents peut surprendre : quel sens cela a-t-il de
dire que x est gal dix fois sa propre valeur ? Il faut bien comprendre que, dans ces
contextes, les symboles = ne reprsentent pas une situation dgalit, mais quils sont
une opration dans laquelle on change le contenu dans la variable figurant gauche de
loprateur pour y placer la valeur figurant droite.
Au cours de vos exprimentations, vous constaterez probablement une certaine difficult trouver un nom vos variables. La tentation est alors grande de les nommer
simplement par a, b, c Surtout ne cdez pas cette tentation. Rappelez-vous quun

Program Livre Page 18 Vendredi, 16. mai 2008 8:13 08

18

Initiation la programmation avec Python et C++

programme nest crit quune seule fois mais gnralement relu de nombreu ses
fois. Comparez ces trois extraits de code, qui font exactement la mme chose :
d = 100;
t = 2;
v = d/t;

distance = 100;
temps = 2;
vitesse = distance/temps;

distance_km = 100;
temps_h = 2;
vitesse_km_h = distance_km/temps_h;

Vous laurez devin, le symbole barre incline (ou slash) / est celui de la division.
Lextrait de gauche ne prsente aucune signification. Lextrait au milieu est, par
contre, assez limpide dans ses intentions, bien que subsiste une incertitude sur les
units utilises. Lextrait de droite est parfaitement clair, les noms donns aux variables
indiquent exactement lopration effectue.
Pour nommer vos variables, vous disposez des 26 lettres de lalphabet (sans accent),
des 10 chiffres et du caractre de soulignement (_). De plus, les langages C++ et
Python sont sensibles la casse, cest--dire quils diffrencient minuscules et majuscules : var, VAR et Var pourraient tre les noms de trois variables diffrentes1. Cela
vous laisse donc au total 63 caractres disponibles combiner entre eux, la seule
contrainte est que le nom doit commencer par une lettre (ou le caractre de soulignement, mais ce nest pas conseill, car cela peut avoir une signification particulire dans
certains contextes). Usez et abusez des possibilits offertes. Si une paresse lgitime
fait que les noms des variables vous paraissent bien longs taper, pensez au temps
considrable que vous gagnerez quand vous voudrez reprendre et modifier votre
programme dans plusieurs mois, voire plusieurs annes. Une paresse efficace doit
avoir une vision long terme !

1. Dautres langages, comme Basic, Pascal ou Ada, ne font pas cette distinction entre minuscules
et majuscules. En revanche, certains comme Ada autorisent lutilisation de pratiquement
nimporte quel caractre, mme ceux issus dalphabets non latins.

Program Livre Page 19 Vendredi, 16. mai 2008 8:13 08

Chapitre 2

Stockage de linformation : les variables

19

2.1. Des types des donnes


Nous navons manipul dans les exemples prcdents que des nombres, plus prcisment des nombres entiers. Heureusement, les possibilits ne se limitent pas cela.
Voyez par exemple :
Python

C++

1.
2.
3.
4.

1.
2.
3.
4.
5.
6.
7.

# -*- coding: utf8 -*pi = 3.14159


texte = "Pi vaut "
print texte, pi

#include <iostream>
#include <string>
int main(int argc, char* argv[])
{
double pi = 3.14159;
std::string texte = "Pi vaut ";
std::cout << texte << pi
<< std::endl;
8.
return 0;
9. }

Remarquez dans le programme C++, en lignes 5 et 6, la combinaison dune dclaration


dune variable et dune affectation.
Dans ces programmes, la variable pi est une valeur dcimale, un nombre en virgule
flottante. La variable texte contient du texte, ce quon dsigne par chane de caractres
(imaginez des caractres individuels aligns le long dune chane, afin de former un
texte). lvidence, ces variables ne sont pas de la mme nature que la variable x
utilise prcdemment.
On ne parle pas ici de nature dune variable, mais plutt de son type. Le type dune
variable dfinit la nature de ce quon peut y stocker, ce qui dtermine directement la
faon dont cette variable est code lintrieur de lordinateur (qui, rappelons-le, ne sait
manipuler que des groupes de 0 et de 1) ainsi que lespace dont elle a besoin.
Formellement, un type est dfini par lensemble des valeurs quon peut stocker dans
les variables de ce type, ainsi que les oprations disponibles entre ces valeurs. Plus
simplement, disons que le type dune variable permet dattacher celle-ci une smantique rudimentaire.
La plupart des langages interprts, comme Python, sont capables de dterminer
par eux-mmes le type dune variable selon la valeur qui lui est affecte et selon le
contexte, ce type pouvant ventuellement changer par la suite. Par contre, les
langages compils exigent en gnral que le type soit donn explicitement et dfinitivement ds la dclaration. Dans les programmes C++ que nous avons vus jusquici, les

Program Livre Page 20 Vendredi, 16. mai 2008 8:13 08

20

Initiation la programmation avec Python et C++

lments int, double et std::string sont des noms de types, dsignant respectivement
des nombres entiers relatifs, des nombres virgule flottante et des chanes de caractres. int et double sont normalement toujours disponibles, tandis que, pour
pouvoir utiliser std::string, il est ncessaire dajouter une ligne #include
<string> au dbut du programme. Mais nous reviendrons plus tard sur cet aspect.
mesure que nous avancerons dans nos exprimentations, nous dcouvrirons de
nouveaux types et diverses oprations disponibles sur chacun deux. Nous verrons
galement comment crer nos propres types. Mais dtaillons un peu les trois grands
types prcdents.

2.2. Les entiers


Les types entiers permettent de reprsenter de manire exacte des valeurs entires,
comme les dnombrements. Par exemple, vous pouvez calculer ainsi le nombre de combinaisons possibles au Loto national :
Python

C++

1. # -*- coding: utf8 -*2. comb_loto = (49*48*47*46*45*44) / \


3.
(6*5*4*3*2)
4. print comb_loto

1. #include <iostream>
2. int main(int argc, char* argv[])
3. {
4.
int comb_loto = (49*48*47*46*45*44) /
5.
(6*5*4*3*2);
6.
std::cout << comb_loto <<
std::endl;
7.
return 0;
8. }

Le programme en Python affiche le rsultat 13 983 816, tandis que le programme C++
affiche 2 053 351. Lequel a raison, pourquoi lautre a-t-il tort ?
Cet exemple a deux objectifs. Dabord montrer comment vous pouvez crire des
oprations relativement complexes, ventuellement en utilisant des parenthses
comme vous le feriez sur une feuille de papier. Remarquez, de plus, que lopration
est crite sur deux lignes, afin de lui donner un aspect plus harmonieux. La prsentation du code nest pas quun souci esthtique, cela facilite galement la relecture. En
Python, il est ncessaire dajouter un caractre barre inverse \ (ou backslash) afin
dindiquer que linstruction se poursuit sur la ligne suivante. Ce nest pas ncessaire
en C++.
Ensuite, ayez toujours un regard critique sur les rsultats que vous obtenez de vos
programmes : jaugez-en la cohrence et la crdibilit. Dans cet exemple, sans pour

Program Livre Page 21 Vendredi, 16. mai 2008 8:13 08

Chapitre 2

Stockage de linformation : les variables

21

autant douter un instant de laltruisme de la Franaise des Jeux, on peut considrer que
la valeur la plus probable est la plus leve, cest--dire celle donne par le
programme Python. Mais alors, pourquoi une valeur errone en C++ ?
Parce que les ordinateurs en gnral, et les programmes en particulier, prsentent une
limite la quantit de chiffres quils peuvent manipuler. Le langage Python, qui est un
langage de haut niveau1, est organis de telle sorte que cette limite est trs leve, en
fait pratiquement celle autorise par la mmoire dont vous disposez cela au prix de
performances amoindries. En C++, par contre, cette limite est dfinie par la capacit
du processeur, la taille de lunit de donne quil peut accepter. Elle est donc bien infrieure, mais, en contrepartie, les calculs se font beaucoup plus rapidement.
Le produit intermdiaire 49*48*47*46*45*44, qui vaut 10 068 347 520, dpasse de
loin la limite dun processeur ou dun systme 32 bits (encore les plus courants
aujourdhui). Ce calcul provoque un dbordement, cest--dire qu une certaine tape
le processeur ne peut plus contenir la valeur et "oublie" tout simplement une partie du
nombre. Il en rsulte une erreur de calcul.

2.3. Les dcimaux et la notation scientifique


Les nombres entiers ne suffisent naturellement pas pour reprsenter le monde qui nous
entoure. Par exemple, la surface dun disque na que peu de chances dtre un entier.
Aussi utilise-t-on frquemment des nombres dcimaux pour reprsenter des dimensions
physiques :
Python

C++

1. # -*- coding: utf8 -*2. aire_disque = 3.141592654 * \


3.
2.0 * 2.0
4. print aire_disque

1.
2.
3.
4.
5.
6.
7.
8.
9.

#include <iostream>
int main(int argc, char* argv[])
{
double aire_disque = 3.141592654 *
2.0 * 2.0;
std::cout << aire_disque
<< std::endl;
return 0;
}

1. Un langage est dit de "haut niveau" sil permet (ou impose) une certaine distance vis--vis du
matriel par lutilisation de concepts plus ou moins sophistiqus. Au contraire, un langage est
dit de "bas niveau" sil est en prise assez directe avec llectronique de lordinateur. Le langage
C++ est dans une position intermdiaire, autorisant les deux approches.

Program Livre Page 22 Vendredi, 16. mai 2008 8:13 08

22

Initiation la programmation avec Python et C++

Remarquez lutilisation du point anglo-saxon en lieu et place de notre virgule.


Le programme Python affiche 12.5663706144, tandis que le programme C++ affiche
12.5664. Ces deux valeurs sont proches, mais elles ne sont pas gales Encore un
problme de prcision ?
Tel est bien le cas, mais pas uniquement. Pour la mme raison que les entiers,
lordinateur ne peut manipuler quun nombre limit de dcimales (nombre total de
chiffres), environ une douzaine. Les calculs sont donc ncessairement entachs
dune certaine erreur, mme minime. cette approximation "interne" sajoute
lapproximation de laffichage : si Python affiche normalement toutes les dcimales disponibles, C++ se contente en gnral de 6 mme si davantage sont disponibles dans la mmoire. Une fois de plus, ne croyez pas aveuglment ce que vous
voyez !
Les nombres dcimaux permettent en outre de reprsenter des valeurs plus grandes
que les nombres entiers. Essayez, par exemple, de refaire ainsi le calcul du nombre de
possibilits au Loto en C++ :
double comb_loto = (49.0*48.0*47.0*46.0*45.0*44.0) /
(6.0*5.0*4.0*3.0*2.0);

Ce qui donne laffichage 1.39838E+07 (ou 1.39838e+07, avec un "e" minuscule, ce


qui est identique).
Cette forme dcriture, un nombre dcimal suivi dun E, dun signe puis dun
entier, est appele notation scientifique. Elle permet de reprsenter de grandes
valeurs avec peu de chiffres. Lentier relatif qui suit le E est la puissance de 10 par
laquelle il faut multiplier le premier nombre pour avoir le rsultat. Par exemple,
lcriture 1.234E+24 signifie littralement "1.234 multipli par 10 24", soit
1 234 000 000 000 000 000 000 000. Mais encore une fois, tous ces chiffres ne sont
pas rellement stocks dans lordinateur Essayez dafficher le rsultat du calcul
suivant, en Python comme en C++ :
deux = 1.0E+50 + 2.0 1.0E+50

Il suffit de regarder cette instruction une seconde pour sapercevoir que les deux
grands nombres sannulent : mathmatiquement, ce calcul vaut donc exactement 2.

Program Livre Page 23 Vendredi, 16. mai 2008 8:13 08

Chapitre 2

Stockage de linformation : les variables

23

Mais lordinateur effectue les calculs oprateur par oprateur : il va donc commencer
par calculer 1.0E+50+2. Du fait des limites de prcision, la valeur 2 est ngligeable par rapport la valeur 1050 : cette somme nest en ralit mme pas effectue.
Ce qui donne 1050, auquel on soustrait 1050 ce qui donne 0. Mathmatiquement
faux, mais informatiquement invitable.
Ces virgules qui flottent
Peut-tre vous demandez-vous pourquoi on dsigne les nombres dcimaux par le terme
de nombres virgule flottante. Cela est d au fait que, dans la reprsentation de ces
nombres, la virgule (qui est reprsente par un point, selon la convention anglo-saxonne)
na pas une position fixe, cest--dire que le nombre de chiffres gauche et droite de la
virgule peut varier selon la situation.
Il est important de comprendre que le nombre que nous voyons lcran nest quune reprsentation du contenu dune zone mmoire de lordinateur, qui finalement ne sait manipuler
que des suites de 0 et de 1, le fameux codage binaire. Nous avons vu que dans le cas du type
dcimal de Python, environ 16 chiffres sont disponibles. La virgule flottante nous permet
donc de contenir en mmoire les nombres 1.123456789012345 et 123456789012345.1 avec la
mme prcision, sans arrondi (ou presque).
Cette reprsentation soppose celle dite virgule fixe. Celle-ci impose un nombre fixe de
chiffres de part et dautre de la virgule. Selon ce principe, pour pouvoir reprsenter avec la
mme prcision les deux nombres prcdents, il faudrait pouvoir disposer de 32 chiffres, ce
qui impliquerait que la zone mmoire soit deux fois plus vaste.
De nos jours, les processeurs des ordinateurs utilisent (presque) tous la reprsentation en
virgule flottante, car cest celle qui offre le meilleur compromis entre prcision,
complexit de traitement et occupation mmoire.

2.4. Les chanes de caractres


Aussi tonnant que cela puisse paratre, notre lectronique actuelle moderne et
surpuissante na toujours aucune ide de ce quest une simple lettre. Encore moins
la notion de phrase ou de texte ! Du point de vue de lordinateur, ce que nous percevons comme lettres nest en fait que codes numriques et nombres entiers. La
correspondance entre une valeur numrique et une lettre prcise se fait au travers
dun processus relativement complexe, que nous passerons sous silence pour
linstant.

Program Livre Page 24 Vendredi, 16. mai 2008 8:13 08

24

Initiation la programmation avec Python et C++

Une chane de caractres nest ainsi quune suite ordonne de codes numriques, que
nous dcidons de considrer comme un tout ayant un sens. Considrons les programmes
suivants :
Python

C++

1. # -*- coding: utf8 -*2. chaine = "Bonjour!"


3. print chaine

1.
2.
3.
4.
5.
6.
7.
8.
9.

#include <iostream>
#include <string>
int main(int argc, char* argv[])
{
std::string chaine;
chaine = "Bonjour!";
std::cout << chaine << std::endl;
return 0;
}

La variable chaine (sans accent circonflexe sur le "i", car les noms de variables
ne doivent pas contenir de lettre accentue) est alors un point daccs une suite
de caractres (reprsents par des codes numriques), un peu comme si on
avait dclar autant de variables de type caractre quil y en a dans la chane.
Seulement, cette suite est vue comme un tout, ce que lon pourrait reprsenter selon la
Figure 2.2.
Figure 2.2
Reprsentation dune
chane de caractres.

chaine
B o n

u r

Toutefois, cette reprsentation est approximative : le programme a besoin de "connatre"


la longueur de cette chane, le nombre de caractres quelle contient. Chaque langage
de programmation applique sa propre technique pour cela, qui ne prsente pas beaucoup dintrt pratique pour nous. Disons simplement que dans le schma prcdent, il
manque un petit bloc dinformations utilis pour les besoins propres du programme,
afin quil puisse manipuler cette chane.
Comme pour les types numriques, de nombreuses oprations sont disponibles sur les
chanes de caractres.

Program Livre Page 25 Vendredi, 16. mai 2008 8:13 08

Chapitre 2

Stockage de linformation : les variables

25

Par exemple :
Python

C++

# -*- coding: utf8 -*bonjour = "Bonjour, "


monde = "Monde!"
print bonjour + monde
tiret = "-"
print 16*tiret

#include <iostream>
#include <string>
int main(int argc, char* argv[])
{
std::string bonjour = "Bonjour, ";
std::string monde = "Monde!";
std::cout << bonjour+monde << std::endl;
return 0;
}

Loprateur daddition + permet de "coller" deux chanes, lune au bout de lautre,


pour en crer une nouvelle plus longue. Dans le jargon informatique, on appelle cela la
concatnation de deux chanes de caractres.
Plus curieux, loprateur de multiplication * permet de dupliquer une chane pour en
produire une plus longue. Cet oprateur nest toutefois disponible quen Python : cest
pourquoi, ici, le programme C++ nest pas quivalent. Nous verrons plus loin
comment obtenir un rsultat similaire.

2.5. Erreurs typiques lies aux variables


Invitablement, vous commettrez des erreurs lors de la saisie de vos programmes.
Ce nest pas grave, cest parfaitement naturel, surtout au dbut. Une erreur
commune est dutiliser une variable avant de lavoir dclare :
Python

C++

1. # -*- coding: utf8 -*2. print une_variable

1.
2.
3.
4.
5.

#include <iostream>
int main(int argc, char* argv[])
{
std::cout << une_variable;
}

Le retour la ligne par std::endl a ici t


omis, car ne prsentant aucun intrt pour
la dmonstration.

Program Livre Page 26 Vendredi, 16. mai 2008 8:13 08

26

Initiation la programmation avec Python et C++

Cela sera impitoyablement sanctionn par linterprteur et par le compilateur par une
borde dinsultes diverses, dans ce style :
Python

C++

Traceback (most recent call last):


File "test.py", line 2, in <module>
print une_variable
NameError: name une_variable is not defined

test.cpp: In function int main(int, char**):


test.cpp:4: error: une_variable was
not declared in this scope

Python nous dit "une_variable is not defined", C++ plutt "une_variable


was not declared". Mais la signification est la mme : vous avez tent dutiliser une
variable sans lavoir dclare auparavant. Le message derreur contient dautres informations utiles, comme le fichier dans lequel elle apparat et la ligne concerne.
Il peut arriver galement que lon tente de combiner deux variables de faon incorrecte, le plus souvent quand elles sont de types diffrents. Par exemple :
Python
1.
2.
3.
4.

# -*var_1
var_2
print

C++
coding: utf8 -*= "chane"
= 5
var_1 + var_2

1.
2.
3.
4.
5.
6.
7.

#include <iostream>
int main(int argc, char* argv[])
{
std::string var_1 = "chane";
int var_2 = 5;
std::cout << var_1 + var_2;
}

Ces programmes tentent une trange opration : additionner une chane et un entier
moins quil ne sagisse de concatnation ? Autant additionner des chaussures
avec des tomates. Cette opration na pas de sens, elle est donc immdiatement
sanctionne :
Python

C++

Traceback (most recent call last):


File "test.py", line 4, in <module>
print var_1 + var_2
TypeError: cannot concatenate str
and int objects

test.cpp: In function int main(int, char**):


test.cpp:6: error: no match for
operator+ in var_1 + var_2

Program Livre Page 27 Vendredi, 16. mai 2008 8:13 08

Chapitre 2

Stockage de linformation : les variables

27

nouveau, interprteur et compilateur nous donnent la localisation prcise de lerreur.


Python nous dit quil est impossible de concatner (cannot concatenate) des objets de
types chane et entier, tandis que C++ nous informe quil nexiste pas doprateur +
valide (no match for operator+) pour lopration demande.
Aussi agaants quils puissent tre, ces messages derreurs ne sont pas l pour vous
ennuyer mais pour vous aider construire un programme correct. Ils ne font que
signaler une incohrence, manifestation probable dun problme dans la conception
du programme. Corriger ces erreurs permet non seulement dobtenir un programme
qui fonctionne mais, de plus, augmente les chances que ce programme fonctionne
correctement.

Program Livre Page 28 Vendredi, 16. mai 2008 8:13 08

Program Livre Page 29 Vendredi, 16. mai 2008 8:13 08

3
Des programmes dans un
programme : les fonctions
Au sommaire de ce chapitre :

Dfinir une fonction

Les paramtres

Les blocs dinstructions

Retourner une valeur

Quand crer une fonction ?

Program Livre Page 30 Vendredi, 16. mai 2008 8:13 08

30

Initiation la programmation avec Python et C++

Jusqu prsent, nous avons essentiellement excut des instructions la suite les unes
des autres. Mais imaginez une suite dinstructions qui revient sans cesse dans un
programme : la retaper chaque fois peut rapidement devenir fastidieux. Prenons un
exemple : vous voulez afficher une suite de nombres, un par ligne, chacun tant spar
des autres par une ligne de caractres de soulignement (underscore en anglais) et une
ligne de tirets. Essayez dcrire vous-mme le programme, par exemple pour afficher
les nombres 10, 20, 30 et 40. Le programme pourrait ressembler ceci :
Python
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.

# -*print
print
print
print
print
print
print
print
print
print

C++
coding: utf8 -*10
"_"*10
"-"*10
20
"_"*10
"-"*10
30
"_"*10
"-"*10
40

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

#include <iostream>
#include <string>
int main(int argc, char* argv[])
{
std::string soulign = "_____";
std::string tirets = "-----";
std::cout << 10 << std::endl;
std::cout << soulign << std::endl;
std::cout << tirets << std::endl;
std::cout << 20 << std::endl;
std::cout << soulign << std::endl;
std::cout << tirets << std::endl;
std::cout << 30 << std::endl;
std::cout << soulign << std::endl;
std::cout << tirets << std::endl;
std::cout << 40 << std::endl;
return 0;
}

Pour commencer, essayez dimaginer ce que fait ce programme. Ensuite, saisissez-le


dans votre diteur de texte et excutez-le. Vous voyez quune paire de lignes revient
trois fois. Ce ne sont que deux lignes et que trois fois, mais imaginez si cela reprsentait
cinquante lignes devant tre rptes vingt fois !
Rptons-le, les informaticiens sont paresseux, donc une solution a t cherche pour
viter de rpter ainsi une squence dinstructions. Dans les "temps anciens", on
parlait de sous-programmes, cest--dire quune suite dinstructions taient regroupes
dans une sorte de miniprogramme, lequel tait excut la demande par ce qui devenait alors le programme principal. Ceux dentre vous qui ont pratiqu des langages
comme le Basic se rappellent peut-tre des instructions comme GOTO ou GOSUB, qui
taient les prmices de la programmation structure.

Program Livre Page 31 Vendredi, 16. mai 2008 8:13 08

Chapitre 3

Des programmes dans un programme : les fonctions

31

Aujourdhui, on parle plutt de fonctions, parfois de procdures. Une fonction est


donc une suite dinstructions, qui peut tre excute la demande par un programme :
tout se passe alors comme si on avait insr les instructions de la fonction lendroit
o on lappelle.

3.1. Dfinir une fonction


Crer une fonction est trs simple, autant en Python quen C++. Voyez les deux
programmes prcdents modifis :
Python

C++

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

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

# -*- coding: utf8 -*def Separateur():


print "_"*10
print "-"*10
print 10
Separateur()
print 20
Separateur()
print 30
Separateur()

12. print 40

La fonction est ici dfinie aux lignes 2 4.


Cette dfinition est introduite par le mot def
(pour define, dfinir en anglais) suivi du
nom de la fonction, suivi dune paire de
parenthses et enfin du caractre deuxpoints. Les deux lignes suivantes constituent
le corps de la fonction.

#include <iostream>
#include <string>
void Separateur()
{
std::cout << "__________" << std::endl;
std::cout << "----------" << std::endl;
}
int main(int argc, char* argv[])
{
std::cout << 10 << std::endl;
Separateur();
std::cout << 20 << std::endl;
Separateur();
std::cout << 30 << std::endl;
Separateur();
std::cout << 40 << std::endl;
return 0;
}

La fonction est ici dfinie aux lignes 3 7.


Cette dfinition commence par donner le type
des valeurs ventuellement fournies par la
fonction (ici, void signifie "rien"), suivi de
son nom, suivi dune paire de parenthses. Le
corps de la fonction est encadr dune paire
daccolades.

Program Livre Page 32 Vendredi, 16. mai 2008 8:13 08

32

Initiation la programmation avec Python et C++

Nous dfinissons ici une fonction nomme Separateur(). Cette fonction a pour rle
dafficher la double ligne de sparation voulue. On dsigne par corps de la fonction la
suite dinstructions quelle doit excuter. Remarquez la prsentation de ces instructions : elles sont dcales vers la droite, simplement par lajout dun certain nombre
despaces en dbut de chaque ligne. Ce dcalage vers la droite est appel indentation.
Cette indentation est purement dcorative en C++ ; elle ne vise qu amliorer la
prsentation du code et donc faciliter sa relecture. Par contre, elle est absolument
essentielle en Python : cest prcisment cette indentation des lignes qui suivent
celle commenant par def qui indique quelles sont les instructions "appartenant"
la fonction.
Dans les deux cas, la ligne vide qui suit la fonction (ligne 5 en Python et ligne 8 en
C++) na quun but esthtique, afin de bien sparer visuellement la fonction du reste
du programme.
La suite, justement, vous montre comment utiliser cette fonction : lignes 7, 9 et 11 en
Python, lignes 12, 14 et 16 en C++. Il suffit de donner son nom, suivi dune paire de
parenthses. Tout se passe alors comme si les instructions contenues dans la fonction
taient places lendroit o son nom apparat. On peut schmatiser le droulement
des oprations (en se basant sur le code Python, mais cest exactement la mme chose
en C++) comme illustr la Figure 3.1.
Figure 3.1
Chemin des instructions
lors de lappel
dune fonction.

2
3

print 10*_

print 10
1
Sparateur ( )

print 10*_
print 20
4

Les oprations se droulent ainsi :


1. Linstruction print 10 est excute normalement.
2. On rencontre linstruction Separateur() : Python (ou C++) reconnat quil sagit
dune fonction prcdemment dfinie ; le programme est alors drout vers les
instructions de la fonction.

Program Livre Page 33 Vendredi, 16. mai 2008 8:13 08

Chapitre 3

Des programmes dans un programme : les fonctions

33

3. Les instructions contenues dans la fonction sont excutes.


4. lissue de lexcution de la fonction, on retourne "dans" le programme qui
reprend son cours normal.
La deuxime tape est dsigne par le terme dappel de fonction. Lensemble de ce
mcanisme, de la dfinition lutilisation de fonctions, est la base de ce que lon
appelle la programmation structure.
Mais regardez dun peu plus prs le programme en C++, en particulier la ligne 9 :
int main(int argc, char* argv[])

On retrouve les mmes lments que pour la dclaration de la fonction Separateur() : dabord un type (ici le type entier int), puis un nom (ici main, que lon
peut traduire par principale), puis une paire de parenthses sauf quici des choses
figurent lintrieur. Il sagit bel et bien dune fonction : tout programme en C++
(ou en C) doit ainsi dclarer une fonction nomme main(), qui est en fait la fonction
excute au moment o le programme est lanc. Mais que sont ces lments entre
les parenthses ?

3.2. Les paramtres


On ne le rptera jamais assez, un programme volue au cours du temps, au fur et
mesure que les besoins changent. Par exemple, on pourrait estimer quune largeur de
10 caractres pour les lignes sparatrices est insuffisante ; 20 serait prfrable. Dans le
premier programme, cela ncessite de changer six valeurs, alors quil suffit den changer deux dans le second. Nous avons donc gagn en facilit de maintenance et
dvolution. Toutefois, ce nest pas encore satisfaisant.
Il est prvisible que, plus tard, nous voudrons encore changer la largeur de nos lignes
de tirets. Mieux : sans doute voudrons-nous un jour pouvoir afficher des lignes de
diffrentes longueurs ! Par exemple, une ligne de 10 tirets, puis une ligne de 20, puis
une ligne de 30 On pourrait imaginer crer une fonction pour chaque largeur de
ligne, mais on retomberait alors dans les mmes problmes que ceux du premier
programme. Ce quil nous faut, cest une fonction plus gnrique, cest--dire qui
puisse adapter son action selon la situation. Pour raliser cela, nous allons utiliser des
paramtres.

Program Livre Page 34 Vendredi, 16. mai 2008 8:13 08

34

Initiation la programmation avec Python et C++

Modifions lgrement les programmes prcdents :


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

# -*- coding: utf8 -*def Separateur(largeur):


print largeur*_
print largeur*-
print 10
Separateur(10)
print 20
Separateur(20)
print 30
Separateur(30)
print 40

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

#include <iostream>
#include <string>
void Separateur(int largeur)
{
std::cout << std::string
(largeur, _)
<< std::endl;
std::cout << std::string
(largeur, -)
<< std::endl;
}
int main(int argc, char* argv[])
{
std::cout << 10 << std::endl;
Separateur(10);
std::cout << 20 << std::endl;
Separateur(20);
std::cout << 30 << std::endl;
Separateur(30);
std::cout << 40 << std::endl;
return 0;
}

Nous avons ajout un lment, nomm largeur, entre les parenthses de la dfinition
de la fonction Separateur(). Il sagit dun paramtre donn la fonction, au sein de
laquelle il se comporte comme une variable, dont la valeur nest pas connue a priori.
Ce paramtre dtermine le nombre de caractres qui seront affichs sur chaque ligne.
ce sujet, notez quon place habituellement un caractre unique entre apostrophes
plutt quentre guillemets. Notez galement que lon construit une chane de caractres en C++ comme la rptition dun unique caractre (lignes 5 et 7) : on retrouve le
nom du type std::string, puis on donne entre parenthses le nombre de rptitions
et le caractre rpter.
Lorsque la fonction est utilise (lignes 7, 9 et 11 en Python, lignes 13, 15 et 17 en
C++), on place dsormais une valeur entre les parenthses de lappel de fonction.
Cette valeur est transmise la fonction par lintermdiaire du paramtre largeur :
tout se passe alors comme si largeur tait une variable contenant la valeur indique
au moment de lappel de fonction. Au premier appel, largeur vaut 10, au deuxime,
elle vaut 20, et 30 au troisime appel.

Program Livre Page 35 Vendredi, 16. mai 2008 8:13 08

Chapitre 3

Des programmes dans un programme : les fonctions

35

3.3. Les blocs dinstructions


Les instructions contenues dans une fonction forment un tout : elles vont ensemble.
On dit quelles constituent un bloc dinstructions. Plus gnralement, un bloc est un
ensemble dinstructions qui se distinguent du programme. Cette notion est fondamentale car en dcoule (en partie) la notion de porte dune variable : toute variable
nexiste qu lintrieur du bloc dans lequel elle a t dclare.
Python

C++

Un bloc est dfini par lindentation des


lignes qui le composent. Une suite de lignes
indentes dun mme nombre de caractres
constitue donc un bloc.

Un bloc est dlimit par une paire daccolades.


Par exemple :

1. # -*- coding: utf8 -*2. print "Dbut"


3.
entier = 1
4.
print entier
5. print entier

1.
2.
3.
4.
5.
6.
7.
8.
9.
10.

Les lignes 3 et 4 constituent un bloc distinct


du reste du programme.

Les lignes 6 et 7 constituent un bloc distinct


du reste du programme.

Par exemple :

#include <iostream>
int main(int argc, char* argv[])
{
std::cout << "Dbut";
{
int entier = 1;
std::cout << entier;
}
std::cout << entier;
}

Dans les deux programmes prcdents, une variable nomme entier est dclare
lintrieur dun bloc. Cette variable na alors dexistence qu lintrieur de ce bloc :
en dehors, elle nexiste pas. Aussi, la ligne 5 du programme Python et la ligne 9 du
programme C++ provoqueront-elles une erreur, car cet endroit on se situe en dehors
du bloc dinstructions prcdent : la variable entier est donc inconnue ce point
prcis.
Les blocs sont essentiellement utiliss pour rassembler des instructions au sein dune
fonction. Une utilisation comme celle montre prcdemment est plus rare, mais
prsente parfois un intrt quand on souhaite faire "disparatre" des variables dont on
na plus besoin. Incidemment, cela montre que vous pouvez parfaitement dclarer des
variables lintrieur dune fonction.

Program Livre Page 36 Vendredi, 16. mai 2008 8:13 08

36

Initiation la programmation avec Python et C++

3.4. Retourner une valeur


Il nous reste un dernier pas faire sur le chemin de la gnricit (enfin, pour linstant).
Notre fonction effectue certains calculs, dans la mesure o on considre que la
construction dune chane de caractres est un calcul, puis affiche le rsultat de ces
calculs. Cest justement l le dernier problme qui reste rsoudre : cette fonction ne
devrait pas afficher quoi que ce soit.
La possibilit de dfinir des fonctions, ou dautres types dentits que nous rencontrerons plus tard, participe du grand principe de la programmation structure. Cest-dire quon sefforce dorganiser les programmes selon une mthode cartsienne,
consistant diviser les gros problmes en plus petits. On constitue ainsi des lments
de programme, qui communiquent et travaillent ensemble, tout en vitant dans la
mesure du possible que ces lments dpendent trop les uns des autres. Tous les langages de programmation modernes offrent des techniques pour atteindre cet objectif, les
fonctions en tant une parmi dautres. Le but est dviter le fouillis que prsentaient
danciens programmes crits, par exemple, en langage Basic.
De lexprience accumule depuis les origines de la programmation, deux grands
ensembles se sont dgags, chacun tant prsent dans chaque programme :

Linterface. Partie du programme responsable de linteraction avec lutilisateur,


cest--dire de ce que doit afficher le programme et les moyens par lesquels lutilisateur peut donner des informations au programme ; on utilise parfois le terme
savant dIHM (interface homme-machine) ou langlicisme look and feel (apparence et manire dtre).

Le noyau. Cur du programme, contenant ce quon pourrait appeler (avec les


prcautions dusage) son intelligence : la partie du programme qui effectue des
calculs pour produire des rsultats certains devant tre affichs, dautres pas.

Lexprience a montr quil tait vivement recommand de sparer nettement ces deux
grandes parties. La structure du programme nen est que plus claire et aise apprhender ; ainsi, la modification dune partie risque moins dentraner des modifications
importantes dans lautre partie. Ne pas respecter ce principe lmentaire dbouche
invitablement, tt ou tard, sur un effort considrable si le programme doit voluer : si
tout est trop mlang, un changement dun ct ncessitera un changement de lautre,
qui son tour risquera dentraner un changement dans la premire partie Cela
devient rapidement infernal.
Notre exemple, si trivial soit-il, nchappe pas cette rgle. Nous avons une partie
de programme qui effectue un calcul, savoir construire une chane de caractres.

Program Livre Page 37 Vendredi, 16. mai 2008 8:13 08

Chapitre 3

Des programmes dans un programme : les fonctions

37

strictement parler, la fonction qui effectue ce calcul na pas "savoir" ce que lon va
faire de cette chane de caractres. Pour linstant, la fonction provoque un affichage.
Mais comment faire si, demain, au lieu dafficher cette chane nous voulons lcrire
dans un fichier ou la combiner de manire complexe avec dautres chanes ? En ltat
actuel, une telle volution du programme ncessiterait des modifications profondes
stendant sur tout le code.
Lide consiste donc dfinir des fonctions qui font ce pour quoi elles ont t cres,
mais pas plus. Notre fonction calcule une chane de caractres, daccord. Mais ce nest
pas son rle de lafficher. Ce rle est dvolu au programme principal. Il faut donc un
moyen pour "rcuprer" ce que la fonction aura produit. On dit alors que la fonction va
retourner un rsultat, ce rsultat tant la valeur de retour de la fonction. Ce qui sera
fait de ce rsultat est de la responsabilit de celui qui appelle la fonction, ici, le
programme principal. Voyez la nouvelle et dernire version de notre programme :
Python

C++

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

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

# -*- coding: utf8 -*def Separateur(largeur):


chaine = largeur*_ + "\n" + \
largeur*- + "\n"
return chaine
print
print
print
print
print
print
print

10
Separateur(10),
20
Separateur(20),
30
Separateur(30),
40

La virgule la fin des instructions print


des lignes 8, 10 et 12 demande Python de
ne pas sauter de ligne aprs laffichage. En
effet, le saut de ligne est dj contenu dans
la chane produite par Separateur().

#include <iostream>
#include <string>
std::string Separateur(int largeur)
{
std::string chaine =
std::string(largeur, _) +
"\n" +
std::string(largeur, -) +
"\n";
return chaine;
}
int main(int argc, char* argv[])
{
std::cout << 10 << std::endl
<< Separateur(10)
<< 20 << std::endl
<< Separateur(20)
<< 30 << std::endl
<< Separateur(30)
<< 40 << std::endl;
return 0;
}

Program Livre Page 38 Vendredi, 16. mai 2008 8:13 08

38

Initiation la programmation avec Python et C++

Dsormais, la fonction Separateur() construit une chane de caractres et la


"retourne". Dans les deux langages, le retour dune valeur est effectu par linstruction
return (ligne 5 en Python, ligne 10 en C++). Invoquer return provoque la sortie
immdiate de la fonction, mme si des instructions se trouvent aprs. Par ailleurs,
notez comment la dclaration de la fonction a d tre change dans le programme
C++ : void est devenu std::string, signalant ainsi littralement que la fonction ne
retourne pas rien, mais une valeur de type std::string. Une fonction dfinie ainsi
peut tre rapproche de la notion mathmatique ponyme.
Enfin, le lecteur attentif aura probablement remarqu au sein de la fonction Separateur() la prsence de la chane "\n", concatne aux suites de tirets. Il sagit en
ralit dune chane dun unique caractre, reprsent par le symbole \n : celui-ci
correspond tout simplement au saut de ligne. Lorsque ce caractre est affich, les
affichages suivants commencent au dbut de la ligne suivante.

3.5. Quand crer une fonction ?


Cest sans doute lune des premires questions que vous vous poserez lors de vos
exprimentations personnelles. Il ny a pas vraiment de rponse absolue. mesure
que vous gagnerez en exprience, lintrt des fonctions vous apparatra plus clairement. Pour vous donner une ide, quelquun a dit un jour : "Si a fait plus de trois
lignes et que je men serve plus de trois fois, alors je fais une fonction." Cest un peu
simpliste, mais vous voyez lide. Dune manire gnrale, on fait une fonction
lorsquune squence dinstructions a toutes les chances dtre utilise de nombreuses
fois, quitte remplacer quelques valeurs par des paramtres.

Program Livre Page 39 Vendredi, 16. mai 2008 8:13 08

4
Linteractivit : changer
avec lutilisateur
Au sommaire de ce chapitre :

Salutations !

Obtenir un nombre entier

Obtenir une valeur dcimale

Saisir plusieurs valeurs

Program Livre Page 40 Vendredi, 16. mai 2008 8:13 08

40

Initiation la programmation avec Python et C++

Afficher des rsultats est une fonction importante de limmense majorit des programmes. Encore faut-il les prsenter correctement, dune manire aisment comprhensible par lutilisateur Lequel utilisateur doit gnralement entrer des informations
pour que le programme puisse calculer ces rsultats. Cet change dinformations entre
un logiciel et celui qui lutilise est gnralement dsign par le terme dinteractivit :
le programme et lutilisateur agissent lun sur lautre, par lintermdiaire de lcran
(ou tout autre dispositif daffichage, comme une imprimante) et le clavier (ou tout
autre dispositif dentre, comme la souris). Voyons ensemble comment mettre en
uvre cette interactivit avec quelques exemples simples.

4.1. Salutations !
Notre premier programme saluait le monde. Voici comment crire un programme qui
demande son nom lutilisateur, avant de le saluer :
Python
1.
2.
3.
4.

# -*print
nom =
print

C++
coding: utf8 -*"Quel est votre nom?",
raw_input()
"Bonjour,", nom, "!"

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

#include <iostream>
#include <string>
int main(int argc, char* argv[])
{
std::cout << "Quel est votre nom? ";
std::string nom;
std::cin >> nom;
std::cout << "Bonjour, " << nom
<< "!" << std::endl;
return 0;
}

La virgule dans linstruction print insre


un espace laffichage et empche le retour
la ligne : cest cette fin quelle est utilise
ligne 2.

Essayez de deviner par vous-mme ce qui se passe dans ces programmes, en usant de
votre imagination pour dterminer ce que font les lments encore inconnus.

Program Livre Page 41 Vendredi, 16. mai 2008 8:13 08

Chapitre 4

Linteractivit : changer avec lutilisateur

41

Le principe est extrmement simple : on cre une variable, nom, destine recevoir
une chane de caractres contenant justement le nom de lutilisateur. Ce nom est
demand, rcupr partir du clavier, puis stock dans la variable. Laquelle est enfin
utilise pour saluer lutilisateur.
La partie "rcupration partir du clavier" est effectue ligne 3 en Python, ligne 7 en
C++.
Python

C++

On utilise linstruction raw_input(), qui


est, en fait, une fonction retournant une
chane de caractres. Lorsque cette fonction
est invoque, le programme est comme
"bloqu" : il attend que lutilisateur appuie
sur la touche Entre. Ds que cette action
survient, la fonction retourne les caractres
qui ont t saisis auparavant.

On utilise lobjet std::cin, qui est le


miroir de lobjet std::cout que vous
connaissez dj. Remarquez dailleurs que
les chevrons >> sont en sens inverse,
comme si des caractres "sortaient" de
std::cin pour aller dans la variable. cet
endroit, le programme est en attente de
lappui de la touche Entre, aprs quoi les
caractres saisis auparavant sont transmis
par std::cin.

Comme vous le voyez, il ny a l rien de bien compliqu (pour linstant). Comme


toujours, la diffrence essentielle entre les deux programmes est quen C++, la
variable nom doit tre dclare avant de pouvoir y placer quoi que ce soit.
Sans doute serez-vous amen frquemment parler dentres-sorties dun
programme. Par convention, le point de rfrence est le programme lui-mme :
des informations entrent dans le programme ou sortent du programme. La distinction est particulirement visible en C++ : si vous considrez les objets std::cin
et std::cout comme des canaux de communication avec le monde extrieur au
programme, les chevrons indiquent dans quel sens les informations transitent.

Program Livre Page 42 Vendredi, 16. mai 2008 8:13 08

42

Initiation la programmation avec Python et C++

4.2. Obtenir un nombre entier


Nous venons de voir comment obtenir une chane de caractres de lutilisateur. Mais
quen est-il lorsque nous voulons obtenir un nombre entier ? Par exemple, tant donn
un jeu de cartes et un nombre de joueurs, comment savoir combien de cartes aura
chaque joueur, et ventuellement combien il en restera ? Une solution possible est la
suivante :
Python

C++

1.
2.
3.
4.
5.
6.
7.
8.
9.

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

# -*- coding: utf8 -*print "Combien de cartes?",


nb_cartes = int(raw_input())
print "Combien de joueurs?",
nb_joueurs = int(raw_input())
print "Chaque joueur aura", \
nb_cartes / nb_joueurs, \
"cartes, il en restera", \
nb_cartes % nb_joueurs

#include <iostream>
int main(int argc, char* argv[])
{
int nb_cartes = 0;
int nb_joueurs = 1;
std::cout << "Combien de cartes? ";
std::cin >> nb_cartes;
std::cout << "Combien de joueurs? ";
std::cin >> nb_joueurs;
std::cout << "Chaque joueur aura "
<< nb_cartes / nb_joueurs
<< " cartes, il en restera "
<< nb_cartes % nb_joueurs
<< std::endl;
return 0;
}

Cet exemple met en lumire une nouvelle diffrence entre Python et C++. Comparons
deux extraits de code faisant exactement la mme chose, savoir obtenir un entier de
lutilisateur :
Python

C++

nb_cartes = int(raw_input())

int nb_cartes;
std::cin >> nb_cartes;

Program Livre Page 43 Vendredi, 16. mai 2008 8:13 08

Chapitre 4

Linteractivit : changer avec lutilisateur

43

Le code en C++ est trs proche de celui que nous avons vu pour la chane de caractres.
Par contre, le code Python "enferme" la fonction raw_input() dans ce qui pourrait
tre une fonction nomme int().
Rappelez-vous, nous avons dit que, dune part, Python dtermine le type dune variable partir du contexte et de sa valeur, et dautre part, que raw_input() retourne une
chane de caractres. Or, ici, nous voulons des nombres entiers afin deffectuer
certains calculs. Contrairement au C++, o toute variable doit tre dclare avec un
type clairement spcifi, Python na aucun moyen de "deviner" que nous voulons un
nombre entier. Le rle de cette pseudo-fonction int() est prcisment de prciser le
type recherch : partir de la chane de caractres renvoye par raw_input(), on
"fabrique" une valeur numrique, laquelle sera stocke dans la variable qui sera
donc, par consquent, de type numrique et non pas de type chane de caractres.
Du ct du C++, la fabrication de la valeur numrique partir des caractres donns
par lutilisateur est effectue automatiquement par lobjet std::cin lui-mme, celuici ayant reconnu que la variable dans laquelle il doit placer ses informations est de
type numrique.
Nous sommes donc en prsence dune conversion de type : partir dune valeur dun
type donn (une chane de caractres), on fabrique une valeur dun autre type (une
valeur numrique entire). Dans le verbiage de la programmation, on parle de transtypage (type cast, ou simplement cast, en anglais).
Dernier mot, remarquez lutilisation de loprateur % : celui-ci donne le reste dune
division entre nombres entiers, en Python comme en C++.

Program Livre Page 44 Vendredi, 16. mai 2008 8:13 08

44

Initiation la programmation avec Python et C++

4.3. Obtenir une valeur dcimale


Pour tre complet, voyons maintenant un exemple utilisant des nombres virgule
flottante, par exemple pour calculer la vitesse moyenne dun trajet :
Python

C++

1.
2.
3.
4.
5.
6.

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

# -*- coding: utf8 -*print "Distance (en km)?",


distance = float(raw_input())
print "Dure (en h)?",
duree = float(raw_input())
print "Vitesse:", distance/duree

#include <iostream>
int main(int argc, char* argv[])
{
double distance = 0.0;
double duree = 1.0;
std::cout << "Distance (en km)? ";
std::cin >> distance;
std::cout << "Dure (en h)? ";
std::cin >> duree;
std::cout << "Vitesse: "
<< distance / duree
<< std::endl;
return 0;
}

Nous retrouvons un transtypage en Python, cette fois-ci en utilisant la pseudo-fonction


float(). En dehors de cela, ces exemples sont parfaitement quivalents aux prcdents !

4.4. Saisir plusieurs valeurs


Si vous utilisez la version C++ du programme de salutation vu prcdemment en lui
donnant un nom complet (prnom suivi du nom), vous remarquerez sans doute un
problme qui napparat pas en Python. Comparez les Figures 4.1 et 4.2.
Python

C++

Figure 4.1
Salutation en Python, avec nom et prnom.

Figure 4.2
Salutation en C++, avec nom et prnom.

Program Livre Page 45 Vendredi, 16. mai 2008 8:13 08

Chapitre 4

Linteractivit : changer avec lutilisateur

45

Le programme C++ ne "rcupre" quune partie de ce qui a t saisi, en fait jusqu


lespace sparant les deux mots. Dans certaines situations, cela peut tre ennuyeux. Au
contraire, le programme Python rcupre bien les deux mots en une seule chane de
caractres Mais alors, comment faire si vous voulez effectivement les sparer pour
les stocker dans deux variables ?

4.4.1. Le cas du C++


Pour rcuprer une ligne complte sous la forme dune chane de caractres, il est
ncessaire dutiliser une fonction spcialement ddie cela. Il suffit de modifier ainsi
la ligne 7 du programme :
std::getline(std::cin, nom);

La fonction standard std::getline() reoit deux paramtres :

dabord un flux de caractres, cest--dire un objet partir duquel obtenir des


caractres, ce quest prcisment std::cin ;

ensuite, une variable de type chane de caractres, dans laquelle placer les caractres
obtenus partir du flux prcdent.

Tous les caractres du flux sont placs dans la chane, jusqu ce que std::getline()
rencontre un saut de ligne : il sagit dun caractre spcial "cr" par lappui de la
touche Entre. Autrement dit, std::getline() place dans une chane tous les caractres se trouvant sur une ligne. Ainsi modifi, le programme C++ se comporte comme
le programme Python.
Si, au contraire, vous voulez effectivement obtenir les mots de la ligne dans diffrentes
variables, vous devez dclarer autant de variables et les donner comme "cible"
std::cin. Par exemple, pour lire en une seule fois un prnom, un nom et un ge (donc
une valeur numrique) :
std::string prenom;
std::string nom;
int age;
std::cin >> prenom >> nom >> age;

Lutilisateur peut alors saisir un texte comme "Yves Bailly34" (sans les guillemets !),
les trois lments seront bien rpartis dans les trois variables.

Program Livre Page 46 Vendredi, 16. mai 2008 8:13 08

46

Initiation la programmation avec Python et C++

4.4.2. Le cas de Python


La fonction Python raw_input() est lquivalent de la fonction C++
std::getline() que nous venons de voir. Pour obtenir les diffrents lments dune
chane entre par lutilisateur, il est ncessaire de dcouper (split en anglais) celle-ci,
puis daffecter chaque morceau aux variables concernes. Le code qui suit anticipe un
peu sur lavenir, nommment les Chapitres 6 et 9, mais vous devriez pouvoir lapprhender sans difficult :
chaine = raw_input()
morceaux = chaine.split()
prenom = morceaux[0]
nom = morceaux[1]
age = int(morceaux[2])

Le dcoupage de la chane est ralis en lui appliquant une opration nomme


split(). Sans entrer pour linstant dans les dtails, une opration est un peu comme
une fonction qui sapplique sur un objet dont le nom la prcde, spar par un point
(cela sera expliqu au Chapitre 9). Ici, on applique donc lopration split() lobjet
chaine. Le rsultat est le morcellement de la chane, les morceaux tant stocks dans
une variable nomme morceaux (qui est, en fait, un tableau, mais nous reverrons cela
au Chapitre 6). Chaque morceau de la chane, lui-mme une chane de caractres, est
finalement obtenu en donnant le nom du morcellement (ici morceaux) suivi du
numro du morceau entre crochets, le premier ayant le numro 0 (zro), le deuxime
le numro 1, et ainsi de suite. On retrouve dailleurs le transtypage ncessaire pour
obtenir une valeur de type numrique. Cet extrait de code Python est ainsi quivalent
lextrait de code C++ que nous venons de voir.

Program Livre Page 47 Vendredi, 16. mai 2008 8:13 08

5
Ronds-points
et embranchements :
boucles et alternatives
Au sommaire de ce chapitre :

Lalternative

La boucle

Algbre de Boole et algorithmique

Program Livre Page 48 Vendredi, 16. mai 2008 8:13 08

48

Initiation la programmation avec Python et C++

Les programmes que nous avons vus jusquici sont essentiellement linaires, cest-dire que les instructions sont excutes lune aprs lautre, dans lordre dans lequel
elles apparaissent. Cela prsente le mrite de la simplicit mais, galement, linconvnient dtre trop sommaire.
Dans votre vie quotidienne, vous tes sans cesse amen faire des choix guids par
votre environnement. Par exemple, avant de partir en balade, sans doute vous posezvous la question en regardant le ciel : "Pleut-il ?" Si oui, vous mettez des bottes, si
non, vous mettez des chaussures. De mme, il arrive que lon doive effectuer des
tches caractrises par la rptition dune ou de plusieurs actions, de la laborieuse
vrification dun relev de compte bancaire lpluchage dun kilo de pommes de
terre. Pour triviales quelles puissent paratre, ces situations se retrouvent galement
en programmation. Afin dillustrer cela, nous allons raliser le petit jeu suivant : le
programme va choisir un nombre au hasard entre 1 et 100, que lutilisateur devra deviner en faisant des propositions. chaque proposition, le programme affichera "trop
grand" ou "trop petit", selon le cas.
Le dbut de ce programme pourrait ressembler ceci :
Python

C++

1.
2.
3.
4.
5.
6.

1.
2.
3.
4.
5.
6.
7.
8.
9.
10.

# -*- coding: utf8 -*from random import randint


nombre_a_trouver = randint(1, 100)
print "Donner un nombre:"
chaine = raw_input()
nombre_joueur = int(chaine)

La fonction randint() (ligne 3) donne un


nombre entier au hasard compris entre les
deux valeurs quelle reoit en paramtre.
Cette fonction est rendue disponible par la
ligne 2, dont la signification sera explicite
au chapitre consacr aux bibliothques.

#include <iostream>
#include <cstdlib>
int main(int argc, char* argv[])
{
srandom((long)argv);
int nombre_a_trouver =
(random()%100) + 1;
int nombre_joueur;
std::cout << "Donnez un nombre: ";
std::cin >> nombre_joueur;

La fonction random() (ligne 7) donne un


nombre entier au hasard compris entre 0 et
une valeur maximale nomme RAND_MAX.
Cette fonction est rendue disponible par la
ligne 2.

Nous anticipons un peu sur lavenir pour bnficier de la fonction randint() en


Python et de la fonction random() en C++. Remarquez que ces fonctions ne sont pas

Program Livre Page 49 Vendredi, 16. mai 2008 8:13 08

Chapitre 5

Ronds-points et embranchements : boucles et alternatives

49

exactement quivalentes : si la premire permet de spcifier lintervalle dans lequel on


souhaite obtenir un nombre au hasard, la seconde donne toujours un nombre entre 0 et
RAND_MAX dont la valeur dpend du compilateur utilis et du systme. On doit donc
ajuster ce que nous donne random().
Pour cela, on commence par demander le reste de la division du nombre donn par
random() par le nombre le plus grand quon souhaite obtenir, ici 100. Le reste dune
division entre nombres entiers est donn par loprateur % (en C++ comme en Python).
Ce reste est forcment compris entre 0 et 99. On ajoute donc 1 pour obtenir effectivement
un nombre entre 1 et 100. Ainsi sexplique la formule ligne 7.
Il convient de prciser ici que le hasard nexiste pas en programmation. Si vous appelez plusieurs fois random(), vous obtiendrez une suite de nombres qui correspond
lapplication dune formule complexe, conue de faon simuler le hasard. Cette
formule utilise ce que lon appelle une graine, partir de laquelle la suite de nombres
est calcule. Sans autre prcision, cette graine est toujours 1, ce qui signifie que la
suite de nombres gnre par random() est toujours la mme. La ligne 5 du
programme C++ a pour effet de modifier cette graine, au moyen de la fonction srandom(). Sans entrer dans les dtails, le procd utilis ici exploite la localisation en
mmoire du programme lors de son excution, laquelle localisation est trs certainement diffrente chaque excution. On obtient ainsi une nouvelle suite de valeurs au
hasard chaque excution. Remarquez que ce nest pas ncessaire en Python, la
graine tant automatiquement modifie chaque excution.
Le hasard en programmation, que lon peut rapprocher de la notion de variable alatoire en probabilits, est un sujet bien plus complexe quil ny parat. Ce que nous
venons de voir devrait toutefois rpondre la plupart de vos besoins. Mais revenons au
sujet principal de ce chapitre.

5.1. Lalternative
Une alternative est une situation o lon a le choix entre deux possibilits, deux
chemins. Chacune des possibilits est parfois dsigne par le terme de branche de
lalternative. Plaons-nous dans la situation o lutilisateur a saisi un nombre : il nous
faut dterminer le message afficher. Suivez bien, le procd voqu maintenant est
lun des plus fondamentaux pour concevoir une partie de programme, voire un
programme complet.

Program Livre Page 50 Vendredi, 16. mai 2008 8:13 08

50

Initiation la programmation avec Python et C++

Mettez-vous la place du programme, l o nous en sommes, et agissez mentalement


sa place. Vous disposez dun nombre de rfrence, celui tir au hasard, ainsi que
dun nombre donn par lutilisateur : ce sont les donnes dentre, celles que vous
devez traiter. Par ce traitement, vous devez produire un rsultat qui est un message
adapt la comparaison de ces deux nombres. Dcomposez autant que possible votre
processus de pense, en oubliant pour linstant le cas particulier o les deux nombres
sont gaux. Cela devrait ressembler ceci :
si le nombre donn est plus grand que le nombre trouver, alors le message est
"Trop grand", sinon le message est "Trop petit".

Cette simple phrase, parfois dsigne par le terme de principe, est une description
synthtique et concise que ce que lon veut faire. Maintenant, rapprochons-nous
de la programmation, en remplaant certains termes par les variables qui les reprsentent :
si nombre_joueur est plus grand que nombre_a_trouver, alors etc.

La notion de message, dans notre cas, correspond un affichage lcran. Par ailleurs,
la comparaison de deux nombres se fait en utilisant les oprateurs mathmatiques
usuels que sont les symboles < et >. Poursuivons le "codage" de notre principe, par
exemple en utilisant le langage Python :
si nombre_joueur > nombre_a_trouver, alors print "Trop grand", sinon print "Trop
petit".

Voil qui commence ressembler un programme ! Il nous reste traduire les mots
"si", "alors" et "sinon". Le mot "si" se traduit en anglais, ainsi que dans la plupart des
langages de programmation, par if. Le mot "alors" se traduit par then, mais de
nombreux langages, dont Python et C++, omettent simplement ce mot. Enfin, "sinon"
se traduit par else. Ce qui nous donne les instructions suivantes :
Python
1. if ( nombre_joueur > \
2.
nombre_a_trouver ):
3.
print "Trop grand!"
4. else:
5.
print "Trop petit!"

C++
1.
2.
3.
4.
5.
6.

if ( nombre_joueur >
nombre_a_trouver )
{
std::cout << "Trop grand!"
<< std::endl;
}

Program Livre Page 51 Vendredi, 16. mai 2008 8:13 08

Chapitre 5

Ronds-points et embranchements : boucles et alternatives

Python

Remarquez la fin des lignes 2 et 4, vous


retrouvez les deux-points que nous avions
rencontrs pour la dfinition dune fonction. Les lignes 3 et 5 sont indentes : de
mme que les instructions dune fonction
sont rassembles dans un bloc dinstructions, les instructions correspondant
chacune des branches de lalternative sont
rassembles dans un bloc. Ici chacun des
deux blocs nest constitu que dune seule
ligne, mais naturellement vous tes libre
den rajouter.

51

C++
7.
8.
9.
10.

else
{
std::cout << "Trop petit!"
<< std::endl;

11.

On retrouve ici les accolades que nous


avions rencontres lors de la dfinition
dune fonction. En effet, les instructions qui
doivent tre excutes dans chacune des
branches de lalternative sont rassembles
dans un bloc, dlimit par ces accolades.
Ici, nous navons quune seule instruction
pour chacun de ces blocs : dans ce cas particulier, les accolades sont facultatives. Mais
il est vivement recommand de toujours les
utiliser, afin dviter toute ambigut.

Rappelez-vous quen Python lindentation


est trs importante : cest ce dcalage vers
la droite, obtenu en insrant des espaces en
dbut de ligne, qui dtermine ce qui fait
partie de tel ou tel bloc.

Lalternative est introduite par la premire ligne de ces deux extraits :


if ( nombre_joueur > nombre_a_trouver ) ...

Ce qui se trouve entre les parenthses constitue la condition, ou le test, qui permet de
dterminer quelle branche de lalternative sera excute. Si le test russit, cest--dire
si la condition est vraie, alors on excute la premire branche, celle qui se trouve juste
aprs le if. Sinon, on excute lautre branche. Une fois retraduit en franais, tout cela
est en fait trs naturel ! Ne ngligez pas limportance de la formulation et de la smantique en langage naturel, elle est bien souvent un guide prcieux pour lcriture du
code informatique.
Dans certaines situations, vous naurez pas besoin de deuxime branche lalternative. Vous voudrez simplement excuter quelque chose si une condition est remplie,
et ne rien faire sinon. Dans ce cas, vous avez essentiellement deux possibilits.

Program Livre Page 52 Vendredi, 16. mai 2008 8:13 08

52

Initiation la programmation avec Python et C++

La premire consiste explicitement indiquer que la seconde branche ne fait rien, ce


qui scrit ainsi :
Python

C++

if ( une_condition ):
des_instructions
else:
pass
autres_instructions

if ( une_condition )
{
des instructions;
}
else
{
}
autres_instructions;

Linstruction pass signifie exactement


"passer la suite sans rien faire".

Si vous vous demandez quel est lintrt dutiliser une instruction comme pass en
Python ou de crer un bloc vide en C++, disons simplement que de nombreux
programmeurs estiment que, dune part, cela amliore la lisibilit du programme,
dautre part, cela prpare en quelque sorte la place pour de futures volutions.
Comparez avec lautre possibilit, qui consiste tout simplement "oublier" la partie
else :
Python

C++

if ( une_condition ):
des_instructions
autres_instructions

if ( une_condition )
{
des_instructions;
}
autres_instructions;

Cette forme plus concise est probablement la plus rpandue. vous de choisir celle
qui vous convient le mieux, bien quil soit recommand dutiliser la forme longue
(avec blocs vides), au moins dans un premier temps.
Comme exercice, essayez de modifier un peu le principe prcdent pour traiter le
cas o les deux nombres sont gaux et de modifier le code en consquence, sachant
que, pour tester lgalit entre deux nombres, il faut utiliser loprateur == (et non
pas le simple signe =, cest une source trs courante derreurs).

Program Livre Page 53 Vendredi, 16. mai 2008 8:13 08

Chapitre 5

Ronds-points et embranchements : boucles et alternatives

53

Voici une possibilit :


Python

C++

1. if ( nombre_joueur == \
2.
nombre_a_trouver ):
3.
print "Vous avez gagn!"
4. else:
5.
if ( nombre_joueur > \
6.
nombre_a_trouver ):
7.
print "Trop grand!"
8.
else:
9.
print "Trop petit!"

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

if ( nombre_joueur ==
nombre_a_trouver )
{
std::cout << "Vous avez gagn!";
}
else
{
if ( nombre_joueur >
nombre_a_trouver )
{
std::cout << "Trop grand!"
<< std::endl;
}
else
{
std::cout << "Trop petit!"
<< std::endl;
}
}

Respectez bien lindentation, elle est trs


importante en Python, surtout dans des
situations comme celle-ci.

Cette solution est un exemple typique dune imbrication de blocs, que lon peut schmatiser comme la Figure 5.1, o un cadre plus fonc reprsente un bloc plus "interne" :
Figure 5.1
Blocs dinstructions
imbriqus.

if ( nombre_ joueur == nombre_a_trouver ) :


print Vous avez gagn !
else :
if ( nombre_ joueur > nombre_a_trouver ) :
print Trop grand !
else :
print Trop petit !

Program Livre Page 54 Vendredi, 16. mai 2008 8:13 08

54

Initiation la programmation avec Python et C++

Les blocs dinstructions peuvent ainsi tre imbriqus les uns dans les autres. En fait,
vous constaterez quil sagit dune pratique trs courante, laquelle il est pratiquement
impossible dchapper !
Votre attention est attire sur la matrialisation de ces blocs dans le code source : sils
sont relativement aisment reprables en C++, grce aux accolades, la seule faon de
les reconnatre en Python se fonde, par contre, sur lindentation. Rptons-le, cette
indentation nest pas seulement esthtique en Python : cest elle qui dfinit rellement
les blocs dinstructions, linterprteur va sappuyer dessus pour identifier les blocs.
Alors quen C++, elle nest rien dautre quune aide visuelle, le compilateur nen
faisant aucun cas.

5.2. La boucle
Nous avons progress, mais notre programme nest pas termin : actuellement, lutilisateur ne peut proposer quun seul nombre, puis le programme se termine. Comment
faire pour que lutilisateur puisse proposer plusieurs nombres ? Une solution naturelle
consisterait dupliquer les lignes de code. Mais cela pose au moins deux problmes
majeurs.
Si les lignes dupliquer sont trs nombreuses, on augmenterait ainsi considrablement
la masse totale du code source, cest--dire le nombre dinstructions. Or il a t
dmontr que les risques derreurs et la difficult de maintenance dun programme
augmentent mcaniquement et rapidement avec sa taille : cette solution conduirait
donc un programme quil serait extrmement pnible de faire voluer et de corriger
en cas de problme.
Dans bien des situations, dont la ntre, on ne sait pas lavance combien de fois il sera
ncessaire de rpter ces mmes instructions. Ici, si on peut estimer quune centaine
de rptitions serait largement suffisante et supportable, cela ne serait vraiment pas
raisonnable dans les cas o les instructions doivent tre rptes des milliers ou des
millions de fois.
Le premier point pourrait tre rgl par lutilisation dune fonction. Mais cela ne changerait rien au second. Nous avons besoin de quelque chose de nouveau.
Encore une fois, cherchons un principe, formul en langage clair. Considrons le
suivant :
tant que les nombres ne sont pas gaux, afficher un message adapt puis demander
un nouveau nombre.

Program Livre Page 55 Vendredi, 16. mai 2008 8:13 08

Chapitre 5

Ronds-points et embranchements : boucles et alternatives

55

La partie "afficher un message" a dj t traite, nous pouvons donc remplacer ce


membre par notre principe prcdent (que nous savons dj traduire) :
tant que les nombres ne sont pas gaux, si le nombre donn est plus grand que le
nombre de rfrence, alors afficher "Trop grand", sinon afficher "Trop petit", puis
demander un nouveau nombre.

Ce qui nous intresse ici est le premier membre de cette phrase. Les mots "tant que"
sous-entendent que nous allons effectuer les oprations qui suivent jusqu ce quune
certaine condition soit vrifie ou, plus prcisment ici, tant quune condition nest
pas vrifie. Premier problme, comment traduire lexpression "ne sont pas gaux" ?
Il suffit dexprimer la ngation de la condition "sont gaux". En langage Python
comme en C++, la ngation dune condition scrit simplement not. En effectuant les
remplacements par les variables que nous avons, et en utilisant les oprateurs de
comparaison, nous pouvons rcrire la phrase ainsi :
tant que not ( nombre_joueur == nombre_a_trouver ), si ( nombre_joueur >
nombre_a_trouver ) alors print "Trop grand", sinon print "Trop petit",
puis demander un nouveau nombre.

Maintenant, si vous saviez que les mots "tant que" se traduisent en anglais par while,
avec un peu dimagination vous devriez pouvoir crire le code correspondant cette
phrase. Prcisons simplement que while est galement un mot cl en Python et en
C++ pour reprsenter une boucle Voici ce que cela devrait donner finalement
comme programmes complets :
Python

C++

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

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

# -*- coding: utf8 -*from random import randint


nombre_a_trouver = randint(1, 100)
print "Donnez un nombre:",
chaine = raw_input()
nombre_joueur = int(chaine)
while ( not ( nombre_joueur == \
nombre_a_trouver ) ):
if ( nombre_joueur > \
nombre_a_trouver ):
print "Trop grand!"
else:
print "Trop petit!"

#include <iostream>
#include <cstdlib>
int main(int argc, char* argv[])
{
srandom((long)argv);
int nombre_a_trouver =
(random()%100) + 1;
int nombre_joueur;
std::cout << "Donnez un nombre: ";
std::cin >> nombre_joueur;
while ( not ( nombre_joueur ==
nombre_a_trouver ) )

Program Livre Page 56 Vendredi, 16. mai 2008 8:13 08

56

Initiation la programmation avec Python et C++

Python

C++

14.
print "Donnez un nombre:",
15.
chaine = raw_input()
16.
nombre_joueur = int(chaine)
17. print "Vous avez trouv!"

13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28. }

Nous ne le rpterons jamais suffisamment : prenez garde lindentation. Si vous


constatez que ce programme ne fonctionne
pas correctement, vrifiez votre indentation.
Les instructions dun mme bloc doivent
tre dcales vers la droite dun mme
nombre despaces !

{
if ( nombre_joueur >
nombre_a_trouver )
{
std::cout << "Trop grand!\n";
}
else
{
std::cout << "Trop petit!\n";
}
std::cout << "Donnez un nombre: ";
std::cin >> nombre_joueur;
}
std::cout << "Vous avez trouv!\n";
return 0;

Remarquez que les sauts de ligne rpondent


au caractre \n plutt qu std::endl.

Cela nest valable quen langage Python,


lindentation na quun but esthtique
dans limmense majorit des autres
langages.

Si vous avez essay de raliser vous-mme ce programme et que vous aboutissiez


une solution un peu diffrente, cela ne signifie pas forcment que vous tes dans
lerreur : pour une mme tche, ds que la quantit de code augmente, diffrents
programmeurs "produiront" diffrentes solutions, chacun sappuyant sur son exprience, ses connaissances et mme sa sensibilit. Cest ainsi que le style fait son apparition dans une activit pourtant apparemment si stricte.
Ce que nous venons de raliser est parfois dsign par le terme de rptitive, et plus
communment par le terme de boucle. Une boucle consiste rpter une suite
dinstructions (rassembles dans un bloc, comme vous pouvez le constater ici), chaque
rptition (ou tour de boucle) tant prcde dune condition. Si cette condition est

Program Livre Page 57 Vendredi, 16. mai 2008 8:13 08

Chapitre 5

Ronds-points et embranchements : boucles et alternatives

57

vrifie, le bloc de la boucle est excut, sinon il est ignor et le programme se poursuit
linstruction suivant immdiatement la boucle.
Dans notre exemple, la condition est la ngation (not) de lgalit de deux nombres.
Cela signifie que la boucle sarrtera ds que les deux nombres seront gaux. Formul
diffremment et plus prcisment, la boucle sarrtera ds quil sera faux de dire que
les deux nombres ne sont pas gaux : comme en langage naturel, une double ngation
quivaut une affirmation. Ou encore : la boucle se poursuivra tant quil sera vrai de
constater quil est faux de dire que les deux nombres sont gaux

5.3. Algbre de Boole et algorithmique


Nous venons de voir trois notions trs importantes en programmation, dont une de
faon implicite.
Dabord la notion dalternative, qui permet dorienter le flux dun programme, la
manire dun aiguillage sur une voie ferre. Grce cela, certaines parties dun
programme ne seront excutes que selon certaines conditions donnes.
Puis la notion de boucle, qui permet dexcuter plusieurs fois une mme suite
dinstructions, comme si la voie ferre formait un anneau, revenant sur elle-mme.
La sortie de lanneau, cest--dire la fin de la boucle, dpend dune condition donne
(dun aiguillage).
La troisime notion vient juste dtre mentionne deux fois, car elle est implicite dans
les deux prcdentes : la notion de condition. La valeur dune condition est donne par
le rsultat dun calcul particulier, qui est le test correspondant la condition. strictement parler, le type de cette valeur nest ni un entier, ni une chane de caractres.
Il sagit dun type dit boolen (prononcez "bou-l-un"), par rfrence au modle dvelopp par le mathmaticien et logicien britannique George Boole (1815-1864, dates
mettre en regard du terme si commun "nouvelles technologies").
Une variable entire (de type entier) peut prendre les valeurs 1, 2, 3, 197635, et bien
dautres. Une variable boolenne (de type boolen) ne peut prendre que deux valeurs :
soit vrai (true), soit faux (false). Par commodit, on note souvent chacune de ces
valeurs respectivement 1 et 0, mais ce nest quune reprsentation. Les rgles qui
rgissent les calculs quon peut effectuer sur de telles variables (et bien dautres
choses encore) sont rassembles dans ce quon appelle lalgbre de Boole, conu
justement par M. Boole. Nous avons rencontr lune de ces oprations : la ngation.

Program Livre Page 58 Vendredi, 16. mai 2008 8:13 08

58

Initiation la programmation avec Python et C++

Cette opration donne une valeur oppose la valeur de la variable sur laquelle elle est
applique : si une variable a la valeur vrai, sa ngation est la valeur faux. Si une variable a la valeur faux, sa ngation a la valeur vrai. Cela a t utilis dans lcriture de la
condition de la boucle dans les programmes prcdents.
Ces trois notions font aujourdhui partie des fondements de ce quon appelle lalgorithmique, la science qui tudie les algorithmes. Ltymologie de ce mot remonte au
Moyen ge, au moment de la traduction dun ouvrage publi en lan 825 par lastronome et mathmaticien perse Muhammad ibn Musa al-Khuwarizmi, ouvrage dont le
titre (Al-jabr wal-muqbalah) a donn le mot algbre. Un autre ouvrage du mme
auteur a t le premier traiter du systme des nombres indiens que nous utilisons
aujourdhui, premier systme de lhistoire inclure le nombre zro.
Un algorithme est la description dune suite dactions ou doprations effectuer dans
un certain ordre. Au cours de cette suite, peuvent intervenir des boucles ou des alternatives. La notion mme dalgorithme est loin dtre nouvelle. On doit Euclide un
procd systmatique pour trouver le plus grand diviseur commun (PGDC) de deux
nombres, vers le IIIe sicle avant lan 1. peine plus jeune, ratosthne a imagin une
mthode, appele crible dratosthne, qui permet dobtenir les nombres premi ers.
La notion dalgorithme a t formalise par lady Ada Lovelace Byron, fille du pote
ponyme au XIXe sicle, en hommage de laquelle fut nomm le rigoureux langage de
programmation Ada.
Nous venons de pratiquer un peu lalgorithmique, en imaginant les successions
dactions ncessaires pour obtenir le rsultat dsir. Laction de traduire cet algorithme en langage Python ou C++ est dsigne par le terme dimplmentation de
lalgorithme. Tout travail de programmation est, en fait, compos au minimum de ces
deux tapes : conception puis implmentation de lalgorithme. Avec lexprience,
dans bien des situations la premire phase devient presque inconsciente et on a
tendance se jeter sur le clavier. Il faut se mfier de cette tendance, qui conduit parfois
omettre des cas particuliers importants.

Program Livre Page 59 Vendredi, 16. mai 2008 8:13 08

6
Des variables composes
Au sommaire de ce chapitre :

Plusieurs variables en une : les tableaux

Plusieurs types en un : structures

Variables composes et paramtres

Program Livre Page 60 Vendredi, 16. mai 2008 8:13 08

60

Initiation la programmation avec Python et C++

Plaons-nous un instant dans la peau dun enseignant distribuant des notes ses lves
et voulant obtenir quelques informations, comme la note moyenne lissue dune
preuve, ceux qui sont au-dessus, ceux qui sont en dessous Bref, un certain nombre
de statistiques simples mais laborieuses obtenir "manuellement". Ajoutons que notre
enseignant ignore que son tablissement scolaire dispose de trs bons logiciels pour
faire cela.
Avec ce que nous venons de voir, vous tes dj mme de concevoir un programme
capable de calculer une moyenne : il suffit de demander des notes les unes aprs les
autres, de les additionner au fur et mesure, pour enfin afficher le total divis par le
nombre de notes. Seulement, cela ne nous permet pas de dterminer quelles sont les
notes au-dessus ou en dessous de cette moyenne : il faudrait pour cela se "souvenir" de
chaque note, pour pouvoir la comparer la moyenne aprs que celle-ci a t calcule.
Le problme essentiel tant quon ne sait pas forcment lavance combien il y aura
de notes dont il faudra se souvenir
Peut-tre pensez-vous crer un grand nombre de variables, comme note_01, note_02,
etc., pour mmoriser toutes ces valeurs. Mais jusquo devrez-vous aller ? Si lenseignant est dans un collge, une quarantaine de variables devraient suffire, ce qui est
dj assez pnible saisir dans un programme. Mais sil est dans une universit, alors
il en faudra peut-tre des centaines Mais il est hors de question de crer " la main"
des centaines de variables !
Heureusement, des outils existent pour ce type de situations.

6.1. Plusieurs variables en une : les tableaux


Plutt que de dclarer des centaines de variables, nous allons en dclarer une
unique, ayant la capacit de contenir des centaines de valeurs. Ce sera par la suite
possible de manipuler chacune de ces valeurs, comme sil sagissait dune variable
individuelle.
Selon les situations et les besoins, il existe diffrentes techniques pour raliser un tel
stockage. Nous ne verrons ici que la plus simple : lutilisation de tableaux (array en
anglais). Le principe est extrmement simple. Imaginez quune valeur que vous voulez
mmoriser soit un uf. Si vous navez besoin que dun uf (que dune seule valeur),
vous pouvez le saisir directement. Mais si vous avez besoin dune douzaine dufs,
vous aurez naturellement recours une bote ufs : vous ne manipulez plus les ufs
un par un, mais plutt la bote dans son ensemble. Un tableau nest rien dautre quune

Program Livre Page 61 Vendredi, 16. mai 2008 8:13 08

Chapitre 6

Des variables composes

61

bote ufs, o les valeurs sont ranges les unes ct des autres. De mme que vous
pouvez dsigner chaque uf dans la bote en les dnombrant (le premier uf, le
deuxime, etc.), les valeurs dans un tableau sont numrotes : elles sont donc implicitement ordonnes, telle valeur se trouvant avant ou aprs telle autre valeur.
Un tableau est un exemple de conteneur, cest--dire dun type de variable destin
contenir plusieurs valeurs. Pour poursuivre votre omelette, vous pouvez utiliser un
panier plutt quune bote : dans ce cas, les ufs sont dsordonns. Il existe des types
de conteneurs dans lesquels les valeurs sont mmorises "en vrac".
Revenons notre enseignant. Pour mmoriser ses notes, il va donc utiliser un tableau,
par exemple nomm notes. La Figure 6.1 montre comment on peut reprsenter la
variable notes.
Figure 6.1
Un tableau de notes.

programme

notes

mmoire

12 8.5 13.5

11

...

Peut-tre cette reprsentation vous rappelle-t-elle quelque chose : celle dune chane
de caractres, que nous avons vue au Chapitre 2. Ce nest pas un hasard : fondamentalement, une chane de caractres est un tableau de caractres. Vous manipulez donc
des tableaux depuis le dbut !
Voyons enfin comment dclarer et utiliser un tableau dans un programme. Voici le
dbut du programme moyenne que pourrait crer lenseignant :
Python

C++

# -*- coding: utf8 -*print "Combien de notes?",


nb_notes = int(raw_input())
notes = nb_notes * [0.0]

#include <iostream>
#include <vector>
int main(int argc, char* argv[])
{
int nb_notes = 0;
std::cout << "Combien de notes? ";
std::cin >> nb_notes;
std::vector<double> notes(nb_notes, 0.0);

Le programme commence par demander combien il devra mmoriser de notes (nous


verrons un peu plus loin comment faire si cette information nest pas connue), ce

Program Livre Page 62 Vendredi, 16. mai 2008 8:13 08

62

Initiation la programmation avec Python et C++

nombre tant stock dans la variable nb_notes. Puis le tableau notes lui-mme est
cr.
La syntaxe en Python nest pas sans rappeler celle permettant de dupliquer une chane
de caractres : on utilise loprateur de multiplication *, en donnant dun ct le
nombre dlments dans le tableau, de lautre la valeur initiale de chacun de ces
lments entre crochets. Ce sont les crochets qui indiquent que nous construisons un
tableau, plutt que de simplement multiplier deux nombres.
Comme on pouvait sy attendre, les oprations sont un peu plus complexes en C++.
Notez dabord la deuxime ligne de lextrait de code prcdent : #include <vector>.
Elle est ncessaire pour pouvoir utiliser les tableaux, du moins ceux reprsents par le
type std::vector<>. Remarquez bien les chevrons < et > la fin du nom du type.
Entre eux, vous devez inscrire le type des valeurs qui seront stockes dans le tableau,
ici le type double. Cela pourrait tre (presque) nimporte quel autre type. Dans notre
cas, std::vector<double> est donc un type "tableau de valeurs de type double". Si
vous crivez std::vector<std::string>, il sagit dun type "tableau de valeurs de
type chane de caractres". Vous pouvez mme dclarer une variable contenant un
tableau de tableaux, par exemple :
std::vector<std::vector<double> > variable;

On dit alors que variable est de type "tableau de valeurs de type tableau de valeurs
de type double", ou plus simplement, "tableau de tableaux de doubles". Il sagit dun
tableau deux dimensions. Un tableau "simple", dont les valeurs ne sont pas ellesmmes des tableaux, est une dimension. Vous pouvez crer ainsi des tableaux aussi
complexes que vous le souhaitez, mais prenez garde ne pas vous perdre dans des
mandres spatio-temporels
la suite du type std::vector<double> se trouve, comme toujours, le nom de la
variable, ici accompagne du nombre dlments dans le tableau (nb_notes) et la valeur
de chacun de ces lments (0.0). Cette criture est parfaitement analogue celle que
nous avons rencontre prcdemment pour crer des lignes de tirets, la section 3.2.
Une preuve supplmentaire quune chane de caractres est un type particulier de
tableau dont les valeurs sont des caractres.
En Python comme en C++, la suite de ces instructions nous disposons dune variable
notes permettant daccder un tableau de valeurs de type double, pour linstant
toutes initialises 0.0. Si lutilisateur a demand cinq notes, la situation en mmoire
est telle que montre par la Figure 6.2.

Program Livre Page 63 Vendredi, 16. mai 2008 8:13 08

Chapitre 6

Figure 6.2
Tableau de notes
initialises 0.

Des variables composes

63

notes
0.0 0.0 0.0 0.0

0.0

6.1.1. La boucle borne


Maintenant que nous avons notre tableau, il est temps dy placer les notes obtenues par
les lves. Nous allons avoir besoin dune boucle : pour chaque lve, demander la
note obtenue et la placer dans le tableau. Vous pourriez construire une boucle en utilisant le mot cl while vu au chapitre prcdent, mais nous nous trouvons ici dans une
situation un peu particulire : nous savons exactement combien de notes il y a
demander. Dans ce genre de cas, assez typique lorsquon utilise des tableaux, on se
sert plutt dun autre type de boucle, une boucle borne. Celle-ci sappuie sur une
variable utilise comme compteur pour dnombrer le nombre de fois que la boucle
doit tre excute. Ce type de boucle est introduit par le mot cl for, dont voici une
application :
Python
for indice_note in range(nb_notes):
print "Note", indice_note+1, "?",
notes[indice_note] = \
float(raw_input())

La fonction range() permet de dclarer un


intervalle de valeurs entires comprises
entre 0 et la valeur passe en paramtre,
moins 1. Par exemple, range(5) reprsente
un intervalle compris entre 0 et 4, soit les
valeurs 0, 1, 2, 3 et 4.

C++
for(int indice_note = 0;
indice_note < nb_notes;
++indice_note)
{
std::cout << "Note "
<< indice_note+1
<< "? ";
std::cin >> notes[indice_note];
}

Pour une variable v de type entier, ++v


augmente la valeur de v de 1 ; cette criture
est donc quivalente v=v+1.

La ligne qui commence par for (ou les trois lignes en C++, remarquez les parenthses)
pourrait se traduire par :
pour indice_note allant de 0 nb_notes1, faire

Program Livre Page 64 Vendredi, 16. mai 2008 8:13 08

64

Initiation la programmation avec Python et C++

Cette forme de boucle est quivalente une boucle introduite par while. On pourrait
crire la mme chose de la faon suivante :
Python
indice_note = 0
while ( indice_note < nb_notes ):
print "Note", indice_note+1, "?",
notes[indice_note] = \
float(raw_input())
indice_note += 1

C++
int indice_note = 0;
while ( indice_note < nb_notes )
{
std::cout << "Note "
<< indice_note+1
<< "? ";
std::cin >> notes[indice_note];
++indice_note;
}

Lcriture avec for() et celle avec while() se comportent de la mme faon1, mais la
premire est gnralement considre comme plus "compacte" car elle rassemble au
mme endroit la dclaration du compteur de boucle (ici, indice_note), la vrification
que ce compteur est dans lintervalle voulu et lincrmentation de la valeur de ce
compteur. lintrieur de la boucle, le compteur se comporte comme une variable.
Toutefois, ne modifiez jamais la valeur du compteur lintrieur de la boucle !
Bien que cela soit techniquement possible, lexprience montre quune modification
du compteur dune boucle for() lintrieur de la boucle (cest--dire dans les
instructions excutes par la boucle) conduit gnralement des catastrophes, pouvant
aller jusquau plantage du programme.
Mais sans doute vous demandez-vous pourquoi le compteur de boucle va de 0
nb_notes-1, plutt que de 1 nb_notes. La raison est simple : en Python comme en
C++, le premier lment dun tableau porte le numro 0 (zro). Plutt que de numro,
on parle dindice, do le nom donn au compteur. Vous pouvez voir cet indice comme
le nombre de cases quil faut sauter dans le tableau pour arriver llment : pour le
premier lment, il faut sauter zro (aucune) case, pour le deuxime, il faut en sauter
une, et ainsi de suite.

1. En ralit, ces deux formes ne sont pas exactement quivalentes et interchangeables. En Python,
range() cre en fait un tableau temporaire qui sera parcouru, ce qui narrive pas avec la boucle
while(). En C++, le compteur de boucle nexiste qu lintrieur de la boucle for(), alors
quil existe aprs la boucle while(). Toutefois ces subtilits ne devraient pas vous gner avant
davoir davantage dexprience.

Program Livre Page 65 Vendredi, 16. mai 2008 8:13 08

Chapitre 6

Des variables composes

65

Pour obtenir llment du tableau situ un indice donn, il suffit dinscrire le nom de
la variable contenant le tableau, suivi de lindice donn entre crochets. Dans lexemple, le premier lment du tableau notes est donc obtenu par notes[0], le deuxime
par notes[1], et ainsi de suite. Dans la boucle for() prcdente, on donne le compteur de boucle plutt quune valeur fixe : mesure que le compteur va prendre les
valeurs 0, 1, 2 On va ainsi parcourir le tableau en faisant rfrence chacun de
ses lments, lun aprs lautre.
Essayez maintenant dcrire le code qui va calculer la moyenne des notes. Il suffit
pour cela de dclarer une variable destine recevoir la somme des notes, puis de la
diviser par le nombre de notes. Voici une solution possible :
Python
somme = 0.0
for indice_note in range(nb_notes):
somme += notes[indice_note]
moyenne = somme / nb_notes
print "Moyenne =", moyenne

C++
double somme = 0.0;
for(int indice_note = 0;
indice_note < nb_notes;
++indice_note)
{
somme += notes[indice_note];
}
double moyenne = somme / nb_notes;
std::cout << "Moyenne = "
<< moyenne << std::endl;

Il est ici particulirement important de bien initialiser la variable somme 0.0, afin que
le calcul soit juste. Sil est obligatoire en Python de donner une valeur initiale une
variable au moment de sa dclaration, cela ne lest pas en C++, mais cest plus que
vivement recommand.

6.1.2. Si la taille est inconnue


Plus souvent que vous ne le souhaiteriez, vous ne connatrez pas lavance le nombre
dlments stocker dans un tableau. Par ailleurs, du point de vue de la convivialit
dun programme, on considre quil est prfrable de dcharger lutilisateur du soin de
compter le nombre de valeurs. Aprs tout, compter est encore ce que les ordinateurs
font le mieux, or les ordinateurs sont l pour nous simplifier la tche. Mais alors,
comment faire avec nos tableaux ?
La solution consiste dclarer un tableau vide, ne contenant aucun lment, puis lui
en ajouter au fur et mesure que les lments nous parviennent. Pour notre calcul de
moyenne, cela signifie que lon ne peut plus utiliser une boucle borne pour demander

Program Livre Page 66 Vendredi, 16. mai 2008 8:13 08

66

Initiation la programmation avec Python et C++

les notes : nous devons revenir une boucle while classique. Voici comment
procder :
Python

C++

notes = []
print "Note ", len(notes)+1, "?",
une_note = float(raw_input())
while ( une_note >= 0.0 ):
notes.append(une_note)
print "Note ", len(notes)+1, "?",
une_note = float(raw_input())
nb_notes = len(notes)
print nb_notes, "notes."

std::vector<double> notes;
double une_note = 0.0;
std::cout << "Note "
<< notes.size()+1 << "? ";
std::cin >> une_note;
while ( une_note >= 0.0 )
{
notes.push_back(une_note);
std::cout << "Note "
<< notes.size()+1 << "? ";
std::cin >> une_note;
}
int nb_notes = notes.size();
std::cout << nb_notes << " notes.\n";

La premire ligne de chacun des programmes dclare une variable notes de type
tableau, ce tableau tant vide, cest--dire quil ne contient aucun lment. Il va sans
dire que si vous tentez de manipuler, ne serait-ce que consulter, une valeur dans lun
de ces tableaux, votre programme sarrtera brutalement en vous couvrant dinjures.
Dune manire gnrale, si vous tentez daccder un lment dun tableau en
donnant un indice suprieur ou gal au nombre dlments dans le tableau, alors vous
allez au-devant de graves ennuis : dans le meilleur des cas, un plantage du programme,
dans le pire, une corruption de donnes.
la suite de cette dclaration, on amorce une boucle sur le mme modle que celle
que nous avions utilise dans le petit jeu du chapitre prcdent. Comme il faut bien un
moyen quelconque dinterrompre la boucle, on dcide arbitrairement quon cesse de
demander des valeurs ds quune note infrieure 0 est donne ou, plus prcisment,
on continue tant que la note donne est suprieure ou gale 0.
Si une note valide a t donne, on lajoute au tableau :
Python
notes.append(une_note)

C++
notes.push_back(une_note);

Program Livre Page 67 Vendredi, 16. mai 2008 8:13 08

Chapitre 6

Des variables composes

67

Cet ajout est effectu au moyen dune opration disponible pour les types tableau.
Nous avons dj rencontr la notion dopration, la section 4.4.2 qui concernait le
dcoupage dune chane de caractres en Python. Ici, lopration est append() pour
Python, push_back() pour C++. Leffet est le mme dans les deux langages : le
tableau auquel lopration est applique est agrandi dune case, dans laquelle la valeur
passe en paramtre (une_note dans lexemple) est stocke. Les mots anglais append
et push_back sont ici synonymes et signifient "ajouter la fin". La dernire valeur
ajoute est donc le dernier lment du tableau.
Lorsque la boucle sarrte, on peut obtenir le nombre dlments effectivement
contenu dans le tableau, au moyen de la fonction len() en Python et de lopration
size() en C++ :
Python

C++

nb_notes = len(notes)

int nb_notes = notes.size();

Ds lors, la suite du programme est inchange, il est nouveau possible dutiliser une
boucle borne pour les traitements suivants.

6.2. Plusieurs types en un : structures


Revenons notre enseignant. Calculer une moyenne, cest bien, mais il est encore plus
intressant de savoir quels sont les lves au-dessus ou au-dessous de cette moyenne.
Essayez dcrire la boucle qui permet, par exemple, dobtenir les notes infrieures la
moyenne. Elle pourrait ressembler ceci :
Python

C++

for indice_note in range(nb_notes):


if ( notes[indice_note] < moyenne ):
print indice_note,

for(int indice_note = 0;
indice_note < nb_notes;
++indice_note)
{
if ( notes[indice_note] < moyenne )
{
std::cout << indice_note << " ";
}
}

Program Livre Page 68 Vendredi, 16. mai 2008 8:13 08

68

Initiation la programmation avec Python et C++

Cela fonctionne, mais il y a un inconvnient : lutilisateur obtient une suite dindices,


lui ensuite de faire correspondre ces indices avec les noms des lves. Tche laborieuse, ingrate et source derreurs. Nous devons faire mieux.
Il faudrait pouvoir associer un nom une note. Cest--dire mmoriser non seulement
les notes mais galement les noms des lves les ayant obtenues. Comme si notre
tableau ne contenait pas seulement des valeurs numriques mais aussi des chanes de
caractres.
Une telle association est possible par lutilisation de structures, aussi appeles parfois
enregistrements. Une structure est un nouveau type, cr de toutes pices par le
programmeur, qui rassemble en une seule entit des informations de diffrents types.
Ces informations, appeles les membres de la structure, se comportent un peu comme
des "sous-variables".
Crons donc une structure nomme Eleve dans le programme de moyenne, modifi
ainsi :
Python

C++

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

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

# -*- coding: utf8 -*class Eleve: pass


eleves = []
un_eleve = Eleve()
print "Nom", len(eleves)+1, "?",
un_eleve.nom = raw_input();
while ( len(un_eleve.nom) > 0 ):
print "Note", len(eleves)+1, "?",
un_eleve.note = \
float(raw_input())
eleves.append(un_eleve)
un_eleve = Eleve()
print "Nom", len(notes)+1, "?",
un_eleve.nom = raw_input();
nb_eleves = len(eleves)

#include <iostream>
#include <vector>
#include <string>
struct Eleve
{
std::string nom;
double note;
};
int main(int argc, char* argv[])
{
std::vector<Eleve> eleves;
Eleve un_eleve;
std::cout << "Nom "
<< eleves.size()+1
<< "? ";
std::getline(std::cin,
un_eleve.nom);
while ( un_eleve.nom.size() > 0 )
{

Program Livre Page 69 Vendredi, 16. mai 2008 8:13 08

Chapitre 6

Python

Des variables composes

69

C++
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.

std::cout << "Note "


<< eleves.size()+1
<< "? ";
std::cin >> un_eleve.note;
eleves.push_back(un_eleve);
std::cout << "Nom "
<< eleves.size()+1
<< "? ";
std::cin.ignore();
std::getline(std::cin,
un_eleve.nom);
}
int nb_eleves = eleves.size();

Du ct Python, la dclaration dune structure est introduite par le mot cl class. Du


ct C++, on utilise le mot cl struct, suivi de la liste des membres de la structure
entre accolades, comme sil sagissait de variables. Noubliez pas le point-virgule la
fin de cette liste ! Remarquez que pour Python, les membres de la structure ne sont pas
donns sa dclaration, mais "ajouts" ds quon leur affecte une valeur, comme pour
les variables classiques.
Rptons-le, la dclaration dune structure quivaut crer un nouveau type. Il est
donc possible de crer des variables de ce type, comme ici un_eleve, et mme de
crer des tableaux de ce type, comme ici le tableau eleves. En Python, la dclaration
dune variable dun type structur se fait en utilisant le nom du type suivi de parenthses,
comme sil sagissait dune fonction (ligne 6).
Lorsque vous avez une variable dun tel type, vous pouvez accder aux membres en
utilisant la notation pointe : le nom de la variable, suivi dun point, suivi du nom du
membre. Par exemple, un_eleve.nom permet daccder au membre nom (en loccurrence, une chane de caractres) contenu dans la variable de type Eleve nomme
un_eleve.
Par convention, on parle plutt dinstance que de variable dans le cas des types structurs. Ainsi, on dit que un_eleve est une instance de la structure Eleve, ou plus
simplement que un_eleve est une instance dEleve. De mme, un_eleve.note dsigne
le membre note de linstance un_eleve dEleve.

Program Livre Page 70 Vendredi, 16. mai 2008 8:13 08

70

Initiation la programmation avec Python et C++

Naturellement, si vous crez deux instances dun mme type de structure, vous obtenez
deux groupes de membres parfaitement distincts :
Python

C++

un_eleve = Eleve()
autre_eleve = Eleve()
un_eleve.nom = "Yves"
autre_eleve.nom = "Lubin"

Eleve un_eleve;
Eleve autre_eleve;
un_eleve.nom = "Yves";
autre_eleve.nom = "Lubin";

lissue de ces instructions, on peut reprsenter la situation en mmoire comme la


Figure 6.3.
Figure 6.3
Deux instances
pour deux lves.

un_lve
nom
Y v e

note
??

autre_lve
nom
s

u b

note
??

Les instances (les variables) un_eleve et autre_eleve nont aucun lien entre elles, si
ce nest quelles partagent le mme type, savoir Eleve. Mais peut-tre vous demandez-vous quelle est la signification des points dinterrogation dans le cadre qui reprsente le membre note ? Cela dpend en fait du langage considr.

En Python, ce membre nexiste pas encore, car aucune valeur ne lui a t affecte.

En C++, comme pour toute variable, sa valeur nest pas connue tant quaucune ne
lui a t affecte. On ne sait donc pas ce que contient ce membre.

Ces deux points dinterrogation peuvent tre gnants dans un programme : la suite
de la cration dune instance dEleve, quel que soit le langage, nous sommes en
prsence dune forme dincertitude. Or, les programmeurs dtestent lincertitude : elle
est le chemin le plus sr vers un programme dfectueux. Heureusement, une solution
existe.

Program Livre Page 71 Vendredi, 16. mai 2008 8:13 08

Chapitre 6

Des variables composes

71

6.2.1. Initialiser le contenu


Lobjectif est ici de savoir exactement ce que contient une instance dune structure,
ds sa cration, sans doute possible. Il nous faut pour cela initialiser les membres de la
structure au moment mme de sa cration. Voici comme procder :
Python

C++

class Eleve:
def __init__(self):
self.nom = "
self.note = 0.0

struct Eleve
{
Eleve()
{
this->nom = ";
this->note = 0.0;
}
std::string nom;
double note;
};

Remarquez lindentation : les trois lignes


qui suivent la premire "appartiennent" la
structure Eleve, elles en font partie.

Il suffit dajouter la structure ce quon appelle un constructeur, cest--dire une fonction


spciale charge de construire toute instance de cette structure.
Python

C++

Cette fonction spciale porte un nom


spcial qui est __init__(). Elle doit prendre (au moins) un paramtre spcial,
portant le nom spcial self. Comme vous
le constatez, tout cela est trs spcialis.

Cette fonction spciale doit porter le mme


nom que la structure, Eleve() dans notre
exemple. Au sein de cette fonction, il existe
implicitement une variable nomme this
qui reprsente linstance elle-mme, cest-dire celle en cours de cration. Cette
variable implicite est un peu particulire :
pour accder aux membres de la structure,
il faut utiliser la flche -> plutt que
le point. Ainsi this->nom reprsente le
membre nom de linstance et celui daucune
autre.

Le paramtre self reprsente linstance


elle-mme, cest--dire celle en cours de
cration. self.nom reprsente donc le
membre nom de linstance et celui daucune
autre.
Ce paramtre self est lquivalent de la
variable implicite this du C++.

Cette variable implicite this est lquivalent du paramtre self en Python.

Program Livre Page 72 Vendredi, 16. mai 2008 8:13 08

72

Initiation la programmation avec Python et C++

partir de maintenant, ds que vous crez une instance dEleve, les instructions qui
ont t places dans le constructeur seront excutes. De cette faon, il ny a plus
aucune incertitude quant au contenu de linstance nouvellement cre.
Par exemple :
Python

C++

un_eleve = Eleve()
print un_eleve.note

Eleve un_eleve;
std::cout << un_eleve.note << std::endl;

Sans constructeur, cet extrait provoquerait


un plantage du programme, car alors le
membre note nexisterait pas encore dans
linstance un_eleve.

Sans constructeur, cet extrait afficherait


probablement une valeur parfaitement
fantaisiste, car alors le membre note
naurait pas t initialis.

Ces deux extraits affichent simplement 0.0, qui est la note attribue par dfaut un
lve. Cest une trs mauvaise note, mais au moins elle est parfaitement connue et
dtermine.

6.3. Variables composes et paramtres


Pour terminer ce long chapitre consacr aux variables composes, revenons un
instant au problme du passage de paramtres une fonction. Plus prcismen t,
au mcanisme par lequel une fonction reoit un paramtre. Le but tant ici de crer
une fonction pour calculer la moyenne des notes obtenues par les lves, laquelle
moyenne dpend naturellement de la liste des notes, donc du tableau dlves que
nous avons cr. Autrement dit, nous allons devoir passer ce tableau en paramtre
notre fonction.
Toutefois, on ne transmet pas un objet compos une fonction comme on transmet
une simple valeur numrique. Il convient ici de sinterroger sur le "chemin" suivi par
un paramtre.

Program Livre Page 73 Vendredi, 16. mai 2008 8:13 08

Chapitre 6

Des variables composes

73

6.3.1. Paramtre par valeur


Essayez les programmes suivants :
Python

C++

# -*- coding: utf8 -*def Fonction(entier):


entier = 2
print "entier =", entier
un_entier = 1
Fonction(un_entier)
print "un_entier =", un_entier

#include <iostream>
void Fonction(int entier)
{
entier = 2;
std::cout << "entier = "
<< entier << std::endl;
}
int main(int argc, char* argv[])
{
int un_entier = 1;
Fonction(un_entier);
std::cout << "un_entier = "
<< un_entier << std::endl;
return 0;
}

Les deux programmes afficheront :


entier = 2
un_entier = 1

Regardez la fonction dfinie dans ces programmes. Elle modifie le paramtre quelle
reoit en y plaant une valeur, puis affiche la valeur contenue dans le paramtre.
Comme on pouvait sy attendre, la valeur affiche est bien celle qui a t stocke la
ligne prcdente. Par contre, aprs lappel de cette fonction, la variable qui lui a t
passe en paramtre na pas t modifie, comme le prouve laffichage de sa valeur.
On parle dans ce cas dun passage de paramtre par valeur : le paramtre entier que
reoit la fonction est comme une "vraie" nouvelle variable, qui contient une copie de
la valeur contenue dans la variable un_entier passe la fonction. Le temps de
lexcution de la fonction, on a effectivement deux variables, la variable "temporaire"
(le paramtre) tant initialise avec une copie du contenu de la variable "permanente".
Celle-ci nest en aucun cas modifie par la fonction, quoi que celle-ci applique comme
opration sur son paramtre.

Program Livre Page 74 Vendredi, 16. mai 2008 8:13 08

74

Initiation la programmation avec Python et C++

6.3.2. Paramtre par rfrence


Mais voyons maintenant ce qui se passe dans le cas dune structure ne contenant, par
exemple, quun seul membre de type entier :
Python

C++

# -*- coding: utf8 -*def Fonction(structure):


structure.membre = 2
print "structure.membre =", \
structure.membre

#include <iostream>
struct Une_Structure
{
int membre;
Une_Structure()
{
this->membre = 1;
}
};
void Fonction(Une_Structure structure)
{
structure.membre = 2;
std::cout << "structure.membre = "
<< structure.membre
<< std::endl;
}
int main(int argc, char* argv[])
{
Une_Structure une_instance;
std::cout << "une_instance.membre = "
<< une_instance.membre
<< std::endl;
Fonction(une_instance);
std::cout << "une_instance.membre = "
<< une_instance.membre
<< std::endl;
return 0;
}

class Une_Structure:
def __init__(self):
self.membre = 1
une_instance = Une_Structure()
print "une_instance.membre =", \
une_instance.membre
Fonction(une_instance)
print "une_instance.membre =", \
une_instance.membre

Affichage :

Affichage :

une_instance.membre = 1
structure.membre = 2
une_instance.membre = 2

une_instance.membre = 1
structure.membre = 2
une_instance.membre = 1

Cette fois, les deux programmes se comportent diffremment. Si le programme C++


donne un rsultat analogue au programme prcdent, il en va tout autrement pour le

Program Livre Page 75 Vendredi, 16. mai 2008 8:13 08

Chapitre 6

Des variables composes

75

programme Python : il semble que la fonction ait effectivement modifi la valeur du


membre membre de linstance une_instance de la structure Une_Structure. Autrement dit, dans cette situation, en Python la fonction modifie linstance qui lui est
donne en paramtre.
On parle alors de passage de paramtre par rfrence : la fonction ne reoit plus une
copie de la variable, mais en fait un nouveau point daccs celle-ci. Lorsque vous
passez une variable compose (tableau ou structure) une fonction en Python, vous ne
transmettez quun nouveau point daccs cette variable, dont le contenu peut tre
modifi par la fonction.
En C++ au contraire, vous pouvez constater que lon reste dans le cas dun passage par
valeur : la fonction reoit bel et bien une copie de linstance, toute action de la
fonction sur son paramtre ne se retrouve pas dans la variable passe en paramtre.
Les deux langages se comportent trs diffremment dans ce genre de situations.
Il nest pas possible de reproduire en Python (du moins de manire simple) le comportement du C++ concernant le passage en paramtre de variables composes. Par
contre, il est possible en C++ de prciser quon souhaite un passage de paramtre
par rfrence. Il suffit pour cela de modifier lgrement la dclaration de la fonction
comme suit :
void Fonction(Une_Structure& structure)
{
/* des instructions */
}

Si vous ajoutez le symbole perluette (ou "et commercial") la suite du type du paramtre, alors le mcanisme du passage par rfrence sera utilis. Essayez de modifier
ainsi le programme C++ prcdent : il se comportera alors comme le programme
Python.

6.3.3. Mode de passage des paramtres


Ds que vous voulez passer des variables composes en paramtres des fonctions, il
est vivement recommand dutiliser le passage par rfrence. Cest automatique en
Python, par contre vous devez le prciser explicitement en C++ au moyen de lperluette. Ce mcanisme vite la duplication des donnes en mmoire, qui survient lors
du passage par valeur. Cette duplication nest pas gnante sil sagit dun simple entier
ou dun nombre en virgule flottante. Elle peut devenir assez coteuse, en temps
dexcution ainsi quen encombrement de la mmoire, dans le cas de variables composes.
Dupliquer un tableau contenant un million de valeurs nest pas gratuit !

Program Livre Page 76 Vendredi, 16. mai 2008 8:13 08

76

Initiation la programmation avec Python et C++

Incidemment, nous avons vu que les chanes de caractres ressemblaient fortement


des tableaux de caractres. La remarque prcdente sapplique galement pour ce type
de donnes : si vous devez passer une chane de caractres une fonction, utilisez un
passage par rfrence. Les Figures 6.4 et 6.5 schmatisent la diffrence de comportement
entre les deux modes de passage.
Figure 6.4
Passage de paramtre
par valeur.

valeur

valeur

une_variable
Une_Fonction (parametre)
{
parametre
}

Figure 6.5
Passage de paramtre
par rfrence.

valeur

une_variable
Une_Fonction (parametre)
{
parametre
}

Dernier petit raffinement, que vous verrez frquemment utilis dans du code C++ crit
par des programmeurs plus expriments. Pour diverses raisons, il peut tre souhaitable de sassurer quune fonction ne modifie pas le contenu du paramtre qui lui est
pass. Cest une faon efficace de contrler les modifications que subit une variable
(dun type compos) et ainsi de rduire la complexit dun programme. Dun point de
vue strictement thorique, on peut mme dire quune fonction qui retourne une valeur ne
devrait jamais modifier aucun de ses paramtres, bien quen pratique une telle rigueur
soit rarement applique.
Si vous crez une fonction qui ne devrait jamais modifier ses paramtres, il suffit
dajouter le mot cl const devant le nom du type du paramtre. Nous pouvons alors
enfin construire une fonction calculant une moyenne partir des notes des lves :
double Moyenne(const std::vector<Eleve>& eleves)
{
double somme = 0.0;
for(int indice_note = 0;
indice_note < eleves.size();
++indice_note)

Program Livre Page 77 Vendredi, 16. mai 2008 8:13 08

Chapitre 6

Des variables composes

77

{
somme += eleves[indice_note].note;
}
return somme / eleves.size();
}

La fonction Moyenne() va recevoir un paramtre "doublement structur", puisque


nous lui passerons un tableau de structures Eleve. On utilise donc le passage par rfrence avec &. De plus, il est logique de considrer quune fonction calculant une
moyenne ne doit en aucun cas modifier les valeurs qui lui sont donnes : on ajoute
donc const devant le type du paramtre, qui est galement le type du tableau contenant les lves. Si vous tentez de modifier lune des valeurs contenues dans le tableau,
le compilateur vous avertira dune erreur. Vous pouvez vrifier cela sur le petit
programme de la section prcdente qui montrait le passage par rfrence : ajoutez
const dans la dclaration de la fonction, le compilateur refusera le programme avec
un message dans le style de celui-ci :
$ g++ test_passage_reference.cpp
test_passage_reference.cpp: In function void Fonction(const Une_Structure&):
test_passage_reference.cpp:12: error: assignment of data-member
Une_Structure::membre in read-only structure

Ce qui signifie, en gros, "tentative daffectation dun membre dans une structure en
lecture seule".
Malgr tous ces raffinements, la fonction Moyenne() sutilise tout naturellement dans
le programme principal :
double moyenne = Moyenne(eleves);

Pour vous exercer, modifiez le programme en Python de faon calculer la moyenne


par une fonction. En sachant que le passage par rfrence sera automatique et quil
nexiste pas dquivalent au mot cl const en Python
Dune manire gnrale, en C++, nhsitez pas utiliser le mot cl const. Il donne
des informations prcieuses au compilateur concernant vos intentions, ainsi il est
mieux mme de vrifier que votre code est correct. Cela peut galement permettre
certaines optimisations aboutissant une excution plus rapide.

Program Livre Page 78 Vendredi, 16. mai 2008 8:13 08

Program Livre Page 79 Vendredi, 16. mai 2008 8:13 08

7
Les Tours de Hano
Au sommaire de ce chapitre :

Modlisation du problme

Conception du jeu

Implmentation

Rsum de la dmarche

Program Livre Page 80 Vendredi, 16. mai 2008 8:13 08

80

Initiation la programmation avec Python et C++

Le moment est venu de rassembler lensemble de ce que nous avons vu jusquici pour
construire un programme plus important. Lexemple choisi est celui du jeu des Tours
de Hano, ou Tours de Brahma, jeu imagin par le mathmaticien franais douard
Lucas et inspir dune trs ancienne lgende.
Dans le grand et fameux temple de Bnars, sous le dme marquant le centre du
monde, trne un socle de bronze sur lequel sont fixes trois aiguilles de diamant,
chacune delles haute dune coude et fine comme la taille dune gupe.
Sur une de ces aiguilles, la Cration, Dieu empila soixante-quatre disques dor
pur, du plus grand au plus petit, le plus large reposant sur le socle de bronze.
Il sagit de la tour de Brahma.
Jour et nuit, inlassablement, les moines dplacent les disques dune aiguille vers
une autre tout en respectant les immuables lois de Brahma qui les obligent ne
dplacer quun disque la fois et ne jamais le poser sur un disque plus petit.
Quand les soixante-quatre disques auront t dplacs de laiguille sur laquelle
Dieu les dposa la Cration vers une des autres aiguilles, la tour, le temple et les
brahmanes seront rduits en poussire, et le monde disparatra dans un grondement
de tonnerre.

Figure 7.1
Modle en bois du jeu des Tours de Hano.

Program Livre Page 81 Vendredi, 16. mai 2008 8:13 08

Chapitre 7

Les Tours de Hano

81

Lintrt de ce jeu, trs couramment utilis en programmation, est quil est suffisamment simple pour tre compris sans difficult, tout en tant suffisamment riche pour
mettre contribution toutes les techniques que nous avons rencontres et dautres
encore, comme nous le verrons dans la suite de cet ouvrage.
Premire tape : comprendre peu prs de quoi il retourne. La lgende parle de
soixante-quatre disques, ce qui est assez considrable (nous verrons dans un instant
pourquoi). La puissance des matriels disponibles ne cessant de crotre, il est frquent
denvisager des programmes destins manipuler de grandes quantits de donnes ou,
du moins, ncessitant des quantits astronomiques de calculs. Dans ces situations, on
commence par tudier le problme sur un ensemble plus petit. Pour notre exemple,
prenons simplement trois disques. Saurez-vous trouver le bon enchanement de
mouvements ? La Figure 7.2 donne la solution.
Figure 7.2
Solution au problme
avec trois disques.

T1

T3

T2

Nous avons d effectuer sept mouvements. Et cest la meilleure solution : il est


impossible de rsoudre le problme avec moins de mouvements. En fait, on peut
dmontrer (ce que nous ne ferons pas ici) que pour n disques, il faut effectuer au minimum 2n 1 mouvements. Question : combien les moines de la lgende devront-ils
effectuer de mouvements ? Rponse : 264 1 = 18 446 744 073 709 551 615, soit
environ dix-huit milliards de milliards, en supposant quil ny ait aucune erreur. Si les
moines parviennent dplacer un disque par seconde, le temps ncessaire au dplacement

Program Livre Page 82 Vendredi, 16. mai 2008 8:13 08

82

Initiation la programmation avec Python et C++

de toute la tour sera dun peu moins de 585 milliards dannes Ce qui est rassurant,
la fin du monde nest pas pour demain ! Mme simuls sur un ordinateur actuel, capable deffectuer des dizaines de millions de mouvements par seconde, tous ces dplacements ncessiteraient tout de mme des milliers dannes pour tre numrs. Ce petit
exemple montre que parfois, un problme apparemment simple demeure hors de
porte de nos machines, pourtant si puissantes.

7.1. Modlisation du problme


Voyons maintenant comment nous allons reprsenter les diffrents lments. A priori,
le jeu compte au moins deux types dobjets : le disque et la tour. Pour simplifier un
peu les choses, nous pouvons dcider quun disque est simplement reprsent par un
entier, dont la valeur est sa taille. Dans lexemple de trois disques prcdent, au tout
dbut du jeu, le petit disque au sommet sera donc reprsent par lentier 1, et le grand
tout en bas par lentier 3.
Une tour est un empilement vertical de disques. Si on la fait tomber, on a un alignement horizontal de disques, cest--dire, en fait, dentiers. Une tour sera donc constitue dun tableau dentiers, le premier lment dans le tableau contenant la taille du
disque le plus bas. Par convention, on peut dcider quune valeur de 0 indique quil
ny a pas de disque. Ce tableau devra pouvoir contenir autant dentiers quil y a de
disques dplacer. Toutefois, un instant donn, une tour ne porte que quelques
disques, voire aucun : il est donc galement ncessaire de connatre le nombre de
disques actuellement empils sur une tour. On doit associer un tableau dentiers et un
compteur : nous sommes en prsence dune structure, qui pourrait tre nomme Tour
et avoir lallure suivante :
Python

C++

class Tour:
def __init__(self, taille_max):
self.nb_disques = 0
self.disques = \
[0 for e in range(taille_max)]

struct Tour
{
int nb_disques;
std::vector<int> disques;
Tour(int taille_max):
nb_disques(0),
disques(taille_max, 0)
{
}
};

Program Livre Page 83 Vendredi, 16. mai 2008 8:13 08

Chapitre 7

Les Tours de Hano

83

Nous retrouvons ici les tableaux et les structures, dment initialiss, que nous avons
vus au chapitre prcdent. Remarquez toutefois la prsence dun paramtre supplmentaire dans chacun des constructeurs (en Python et en C++) : il est possible de
donner des informations la structure au moment o on en cre une instance. Ainsi,
lexemple suivant cre une instance de la structure Tour dimensionne pour recevoir
cinq disques :
Python

C++

une_tour = Tour(5)

Tour une_tour(5);

En Python, linformation (le paramtre) est passe entre parenthses aprs le nom de
la structure, tandis quelle est passe aprs le nom de linstance en C++. Finalement, le
constructeur se comporte un peu comme une fonction qui serait intimement attache
la structure.
De plus, nous avons utilis une notation particulire en C++ pour initialiser les
membres de la structure : ils sont donns les uns aprs les autres, spars par des
virgules, accompagns dune paire de parenthses contenant les informations ncessaires leur initialisation. Cette liste est introduite par le caractre deux-points (:).
Dans ce contexte, lcriture nb_disques(0) signifie exactement "initialiser le
membre nb_disques la valeur 0". Le membre disques, qui est un tableau, est
initialis partir de deux informations : la taille du tableau et la valeur placer dans
chaque case. Remarquez que cette criture ressemble beaucoup celle utilise lors de
la cration dune instance dune structure, lorsque le constructeur de cette structure
demande un paramtre. Cette similitude nest pas fortuite. Mais nous reviendrons ldessus.
Il existe un troisime type dobjet : le jeu lui-mme. Celui-ci est caractris par trois
tours, ainsi que par une hauteur maximale. En effet, il est souhaitable que lutilisateur
puisse choisir le nombre maximal de disques avec lesquels il veut jouer. Inutile de lui
imposer un nombre trop lev, ce qui le dcouragerait, ou trop faible, ce qui lui
paratrait trivial.

Program Livre Page 84 Vendredi, 16. mai 2008 8:13 08

84

Initiation la programmation avec Python et C++

Nous avons donc une autre structure, charge de contenir ltat du jeu tout instant :
Python

C++

1. class Hanoi:
2.
def __init__(self, hauteur_max):
3.
self.hauteur = hauteur_max
4.
self.tours = \
5.
[Tour(hauteur_max) \
6.
for t in range(3)]
7.
for etage in range(hauteur_max):
8.
self.tours[0].disques[etage]= \
9.
hauteur_max - etage
10.
self.tours[0].nb_disques = \
11.
hauteur_max

1. struct Hanoi
2. {
3.
int hauteur;
4.
std::vector<Tour> tours;
5.
Hanoi(int hauteur_max):
6.
hauteur(hauteur_max),
7.
tours(3, Tour(hauteur_max))
8.
{
9.
for(int etage = 0;
10.
etage < hauteur_max;
11.
++etage)
12.
{
13.
this->tours[0].disques[etage] =
14.
hauteur_max - etage;
15.
}
16.
this->tours[0].nb_disques =
17.
hauteur_max;
18.
}
19. };

Cette structure Hanoi contient donc, en plus dun entier, un tableau de structures
contenant elles-mmes un tableau. Voil qui commence devenir une imbrication
complexe. Cette complexit transparat dans linitialisation de la premire tour, qui est
simplement "remplie" avec les disques demands afin que le jeu soit prt dmarrer
ds sa cration (lignes 7 11 en Python, lignes 9 17 en C++). Dans nos structures,
tours est un tableau de structures Tour.
Notez quil est ncessaire de passer un paramtre au constructeur de Hanoi pour
pouvoir construire les instances de Tour quelle contient, le constructeur de Tour
attendant lui-mme un paramtre. Dans le cas de linitialisation du tableau en C++, ce
paramtre est donn la suite du type contenu dans le tableau : le membre tours sera
donc un tableau de trois instances de la structure Tour, le constructeur de chacune de
ces instances recevant le paramtre hauteur_max.
Ainsi, dans le cadre dune instance de cette structure Hanoi :

tours[0] est le premier lment de ce tableau, donc dans notre cas une variable
(structure) de type Tour.

Program Livre Page 85 Vendredi, 16. mai 2008 8:13 08

Chapitre 7

Les Tours de Hano

85

tours[0].nb_disques dsigne le membre nomm nb_disques dans la structure


de type Tour se trouvant en premire position dans le tableau tours.

tours[0].disques dsigne le membre nomm disques dans la structure de type


Tour se trouvant en premire position dans le tableau tours, ce membre tant luimme un tableau.

tours[0].disques[etage] dsigne, enfin, llment la position etage dans le


tableau disques contenu dans la structure de type Tour se trouvant en premire
position dans le tableau tours.

Prenez le temps de lire et de comprendre chacune de ces dsignations. Nous avons des
objets qui englobent dautres objets. Dans une dsignation, lobjet "le plus englobant"
se trouve gauche. mesure quon parcourt la dsignation vers la droite, on traverse
les niveaux dimbrication des objets, un peu comme on traverserait des strates gologiques ou les couches dun oignon. On passe dune couche lautre en utilisant soit
les crochets (lorsque la couche do on vient est un tableau), soit le point suivi dun
nom de membre (lorsque la couche do on vient est une structure).
Imaginons que nous dclarions un jeu pour trois disques :
Python

C++

le_jeu = Hanoi(3)

Hanoi le_jeu(3);

On peut alors reprsenter, la Figure 7.3, le contenu de la variable le_jeu que nous
venons de dclarer, dans les deux langages.
Figure 7.3
Instance du jeu
pour trois disques.

le_ jeu
tours
disques
3

2 1

nb_disques
3

disques

disques

q q q

q q q

nb_disques
0

nb_disques
0

hauteur
3

Si nous voulons, par exemple, la taille du disque situ au troisime tage de la


deuxime tour, nous devrons crire le_jeu.tours[1].disques[2]. Suivez le cheminement pour arriver cette valeur.

Program Livre Page 86 Vendredi, 16. mai 2008 8:13 08

86

Initiation la programmation avec Python et C++

7.2. Conception du jeu


Nous disposons maintenant de tout ce qui est ncessaire laffichage du jeu. Il est
temps de nous pencher sur sa logique interne, cest--dire sur la mise en uvre des
rgles du jeu telles qunonces dans la lgende, la plus importante tant quil est
interdit de poser un disque sur un autre plus petit.
Dans ce genre de situation, une mthode qui fonctionne assez bien (pour des programmes de taille modeste) consiste exprimer littralement une tape du jeu ou de
laction que doit effectuer le programme. Cette expression doit tre aussi gnrale que
possible, exempte de dtails, loigne de toute ide de programmation. Dans notre cas,
cela donnerait quelque chose comme ceci :
1. Afficher ltat du jeu.
2. Dterminer le piquet de dpart.
3. Dterminer le piquet darrive.
4. Dplacer le disque du piquet de dpart vers celui darrive.
5. Recommencer tant que le jeu nest pas termin.
Remarquez quon utilise principalement des verbes linfinitif. Ces verbes vont se
traduire en presque autant de fonctions, ce qui donne une premire bauche du
programme, comme vu de trs loin. Notez que la plupart de ces fonctions nexistent
pas encore, elles se dgagent naturellement de la liste prcdente. On peut ds maintenant crire cette bauche, en supposant disposer de toutes les variables ncessaires
(dans notre exemple, une instance de la structure Hanoi) :
Python

C++

while ( not Est_Fini(le_jeu) ):


Afficher_Jeu(le_jeu)
tour_depart = \
Demander_Tour_Depart(le_jeu)
tour_arrivee = \
Demander_Tour_Arrivee(le_jeu)
Deplacer_Disque(le_jeu, \
tour_depart, \
tour_arrivee)

while (! Est_Fini(le_jeu) )
{
Afficher_Jeu(le_jeu);
int tour_depart =
Demander_Tour_Depart(le_jeu);
int tour_arrivee =
Demander_Tour_Arrivee(le_jeu);
Deplacer_Disque(le_jeu,
tour_depart,
tour_arrivee);
}

Program Livre Page 87 Vendredi, 16. mai 2008 8:13 08

Chapitre 7

Les Tours de Hano

87

On passe systmatiquement linstance de Hanoi, qui contient ltat du jeu, aux fonctions qui ont t envisages : cela semble simplement logique que ces fonctions aient
"besoin" de connatre ltat du jeu pour oprer. Pour la mme raison, la fonction
Deplacer_Disque() doit logiquement "savoir" de quel piquet quel piquet elle doit
effectuer le dplacement dun disque.
Il est maintenant temps dintroduire une considration extrmement importante en
programmation, surtout dans un programme qui doit interagir avec un utilisateur (ce
qui est en fait le cas de la plupart). Elle sexprime sous la forme dune rgle simple :
Ne jamais faire confiance aux informations reues de lextrieur !
Autrement dit, toujours vrifier que ce que donne lutilisateur est cohrent. Dans notre
cas, cela signifie quil est ncessaire de vrifier :

Que le piquet de dpart demand est correct, cest--dire quil ne sagit pas dun
piquet vide ou dun numro de piquet aberrant (plus petit que 1 ou plus grand
que 3).

Que le piquet darrive est correct, cest--dire que le mouvement est autoris
selon les rgles du jeu et quil sagit dun numro valide (compris entre 1 et 3).

Il va donc falloir deux fonctions supplmentaires charges deffectuer ces vrifications.


Elles retourneront une valeur boolenne (vrai ou faux) selon le cas. Le droulement
dune tape du jeu devient alors :
1. Afficher ltat du jeu.
2. Demander le piquet de dpart.
3. Si ce piquet est valide, alors :
demander le piquet darrive ;
si ce piquet est valide, alors :
dplacer le disque ;
sinon, afficher un message derreur.
4. Sinon, afficher un message derreur.
5. Recommencer tant que le jeu nest pas termin.
Comme vous pouvez le constater, cette expression littrale laisse entrevoir la structure
gnrale du programme. Une faon assez commune de reprsenter graphiquement un

Program Livre Page 88 Vendredi, 16. mai 2008 8:13 08

88

Initiation la programmation avec Python et C++

tel droulement est dutiliser un ordinogramme (ou algorigramme, ou organigramme


de programmation), qui se prsente comme la Figure 7.4.
Figure 7.4
Ordinogramme du jeu.

Afficher
ltat du jeu

Demander le
piquet de dpart

Piquet de dpart
valide ?

Non

Afficher un
message derreur

Non

Afficher un
message derreur

Oui

Demander le
piquet darrive

Piquet darrive
valide ?
Oui
Dplacer le disque

Non

Le jeu
est fini ?

Oui

Program Livre Page 89 Vendredi, 16. mai 2008 8:13 08

Chapitre 7

Les Tours de Hano

89

Les flches indiquent le droulement des oprations. Les cadres aux bords inclins
sont utiliss pour reprsenter les interactions avec lutilisateur, tandis que les formes
en losange reprsentent les alternatives. Enfin, un cadre simple reprsente simplement
des calculs "internes" au programme.
Ce type de reprsentation a longtemps t trs en vogue et est toujours utilis pour
reprsenter des algorithmes simples ou une vision trs gnrale dun logiciel. Son
principal intrt est sa simplicit. Il prsente toutefois certaines limites ds quil sagit
de traiter un problme complexe ou de grande taille. Aujourdhui, on utilise plutt le
diagramme dtat, qui nest quune partie dune technique beaucoup plus vaste
nomme UML (Unified Modeling Language, langage de modlisation unifi), particulirement adapte la programmation oriente objet (que nous allons aborder trs
prochainement). Nous ne dtaillerons pas ici ce procd sophistiqu, qui ncessiterait
lui seul un ouvrage complet.
partir de cette reprsentation, nous pouvons toffer notre bauche de
programme :
Python
while ( not Est_Fini(le_jeu) ):
Afficher_Jeu(le_jeu)
tour_depart = Demander_Tour_ Depart(le_jeu)
if ( Tour_Depart_Valide(le_jeu,
tour_depart) ):
tour_arrivee = Demander_Tour_Arrivee(le_jeu)
if ( Tour_Arrivee_Valide(le_jeu,
tour_depart,
tour_arrivee) ):
Deplacer_Disque(le_jeu, \
tour_depart, \
tour_arrivee)
else:
print "Arrive invalide!"
else:
print "Dpart invalide!"

Program Livre Page 90 Vendredi, 16. mai 2008 8:13 08

90

Initiation la programmation avec Python et C++

C++
while (! Est_Fini(le_jeu) )
{
Afficher_Jeu(le_jeu);
int tour_depart =
Demander_Tour_Depart(le_jeu);
if ( Tour_Depart_Valide(le_jeu,
tour_depart) )
{
int tour_arrivee =
Demander_Tour_Arrivee(le_jeu);
if ( Tour_Arrivee_Valide(le_jeu,
tour_depart,
tour_arrivee) )
{
Deplacer_Disque(le_jeu,
tour_depart,
tour_arrivee);
}
else
{
std::cout << "Arrive invalide!\n";
}
}
else
{
std::cout << "Dpart invalide!\n";
}
}

Ds que vous vous attaquez un problme complexe, il est vivement recommand


den exprimer littralement les tapes et ventuellement de construire un ou plusieurs
ordinogrammes. Lide est de partir dune vision trs gnrale, de "haut niveau", puis
de "descendre" progressivement dans les dtails de chacun des lments identifis. On
retrouve l une mise en pratique de la mthode cartsienne, ce que nous allons faire
immdiatement.

7.3. Implmentation
Nous pouvons maintenant raliser limplmentation des diverses fonctions qui ont t
prvues la section prcdente.

Program Livre Page 91 Vendredi, 16. mai 2008 8:13 08

Chapitre 7

Les Tours de Hano

91

7.3.1. Afficher le jeu


Voyons comment nous allons coder la fonction daffichage Afficher_Jeu(). Tout naturellement, lutilisateur ne saurait se contenter dun simple affichage de valeurs numriques.
On pourrait en effet trs simplement afficher ceci (ce qui est laiss en exercice au lecteur) :
Tour 0: 3 2 1
Tour 1: 0 0 0
Tour 2: 0 0 0

Cest un reflet fidle de la ralit, mais pas trs pratique utiliser et encore moins ludique.
Il serait dj plus sympathique davoir quelque chose comme ceci :
<=!=>
<==!==>
<===!===>

!
!
!

!
!
!

Les colonnes de points dexclamation reprsentent chacune un piquet, les disques


tant figurs par une suite de caractres = entre chevrons. Chaque disque, ou plutt
chaque tage, devra ainsi tre reprsent individuellement en fonction du disque qui
sy trouve (ou non) et de la largeur maximale possible. Cette reprsentation tant une
chane de caractres, il semble logique denvisager la cration dune fonction
Chaine_Etage() charge de "produire" la chane en question. Voici quelle pourrait
tre cette fonction :
Python

C++

def Chaine_Etage(taille,
largeur):
espace = *(largeur-taille)
chaine = ""
if ( taille > 0 ):
disque = =*taille
chaine = espace + "<" + \
disque + "!" + \
disque + ">" + espace
else:
chaine = espace + "! " + espace
return chaine

std::string Chaine_Etage(int taille,


int largeur)
{
std::string
espace(largeur-taille, );
std::string chaine;
if ( taille > 0 )
{
std::string disque(taille, =);
chaine = espace + "<" +
disque + "!" +
disque + ">" + espace;
}
else
{
chaine = espace + "! " + espace;
}
return chaine;
}

Program Livre Page 92 Vendredi, 16. mai 2008 8:13 08

92

Initiation la programmation avec Python et C++

Cette fonction prend deux paramtres, le premier est la taille du disque se trouvant
ventuellement cet tage (ou 0, sil ny a pas de disque), le second la largeur totale
de ltage, ce qui correspond en fait la plus grande taille possible de disque, soit le
nombre de disques. partir de ces deux paramtres, on construit une chane reprsentant
un tage, ventuellement avec un disque.
Maintenant que nous savons reprsenter un tage, voyons comment reprsenter
lensemble du jeu. Le mode texte pose une contrainte particulire : on ne peut afficher
les caractres que ligne par ligne, la nouvelle ligne repoussant la prcdente vers le
haut. Cela pose deux problmes :

Si nous parcourons une tour "normalement", du premier tage au dernier, elle arrivera lcran la tte en bas : le premier tage sera la premire ligne affiche, qui
sera repousse vers le haut, la ligne du dernier tage tant la dernire affiche
donc la plus basse lcran.

Si nous affichons les tours lune aprs lautre, elles apparatront lune en dessous
de lautre lcran, ce qui nest gure gracieux ; il nous faudrait trouver un moyen
pour quelles apparaissent cte cte.

Vous le voyez, ce qui peut passer pour un problme simple afficher les disques de
chacune des tours peut en ralit cacher des difficults inattendues. Tentons de les
rsoudre.
Le premier point est assez simple : plutt que de parcourir les tages du premier (en
bas) au dernier (en haut), il suffit de les parcourir du dernier au premier. Plusieurs
solutions sont possibles pour cela, par exemple utiliser une boucle borne classique
dans laquelle on dterminerait le numro correct de ltage, comme ceci :
Python

C++

for etage in range(nb_etages):


etage_correct = nb_etages - etage 1
# afficher ltage

for(int etage = 0;
etage < nb_etages;
++etage)
{
etage_correct = nb_etages etage 1;
// afficher ltage
}

La "correction" du numro dtage tient compte du fait que, si nb_etages reprsente le nombre dtages afficher, alors les tages sont effectivement numrots

Program Livre Page 93 Vendredi, 16. mai 2008 8:13 08

Chapitre 7

Les Tours de Hano

93

de 0 nb_etages-1. Une autre solution consiste parcourir les numros en ordre


inverse :
Python

C++

for etage in range(nb_etages, 0, -1):


# afficher ltage

for(int etage = nb_etages-1;


etage >= 0;
--etage)
{
// afficher ltage
}

Cette criture de range() utilisant trois


paramtres permet de spcifier la valeur de
dpart, la valeur darrive (qui nest jamais
atteinte) et le dcalage appliquer chaque
tape pour passer dune valeur la suivante.

Le compteur est cette fois dcrment


plutt quincrment. Le symbole >= signifie "suprieur ou gale", qui est lexacte
ngation de <.

La solution la seconde difficult est presque aussi simple : plutt que dafficher ltat de
chaque tage de chaque tour, nous allons afficher ltat de chaque tour chaque tage.
Autrement dit, plutt que dexaminer les tours lune aprs lautre et, pour chacune,
dexaminer ses tages, nous allons examiner les tages et, pour chacun, examiner les tours
cet tage. Cest--dire que nous allons afficher ltat du jeu tage par tage. Implicitement, nous avons deux boucles imbriques lune dans lautre :
Python

C++

for etage in range(nb_etages, 0, -1):


for tour in range(3):
# afficher ltage de la tour

for(int etage = nb_etages;


etage >= 0;
--etage)
{
for(int tour = 0; tour < 3; ++tour)
{
# afficher ltage de la tour
}
}

Il semble que nous ayons l le principe qui permettra la fonction Afficher_Jeu() de


fonctionner. Pour pouvoir afficher ltat du jeu, celle-ci a naturellement besoin de connatre, justement, ltat du jeu, qui se trouve mmoris dans une structure de type Hanoi.

Program Livre Page 94 Vendredi, 16. mai 2008 8:13 08

94

Initiation la programmation avec Python et C++

Elle devra donc recevoir un paramtre de ce type, en appliquant les principes que nous
avons vus la fin du chapitre prcdent. Voici enfin cette fonction :
Python
def Afficher_Jeu(jeu):
for etage in range(jeu.hauteur, 0, -1):
for colonne in range(3):
taille = jeu.tours[colonne].disques[etage-1]
print " " + Chaine_Etage(taille, jeu.hauteur),
print

C++
void Afficher_Jeu(const Hanoi& jeu)
{
for(int etage = jeu.hauteur-1;
etage >= 0;
--etage)
{
for(int colonne = 0;
colonne < 3;
++colonne)
{
int taille = jeu.tours[colonne].disques[etage];
std::cout << " " << Chaine_Etage(taille, jeu.hauteur) << " ";
}
std::cout << std::endl;
}
}

Remarquez lutilisation dun passage par rfrence constante, avec lutilisation de const et
de &.

Voyez comme la fonction Chaine_Etage() dfinie prcdemment est mise contribution. Chaque chane est, de plus, encadre dun espace de chaque ct, afin que la
prsentation soit un peu are.

7.3.2. Fonctions utilitaires


On dsigne par ce terme des fonctions gnralement trs simples destines remplir
des tches assez lmentaires mais qui peuvent ncessiter plusieurs lignes de code.
Pour simples quelles soient, ces portions de code prsentes au milieu des instructions
dun algorithme compliqu sont comme une nuisance : en quelque sorte, elles
brouillent la lecture de lalgorithme. On parle alors de bruit.

Program Livre Page 95 Vendredi, 16. mai 2008 8:13 08

Chapitre 7

Les Tours de Hano

95

Ici, les fonctions permettant de demander les piquets de dpart et darrive sont typiquement des fonctions utilitaires. Les instructions quelles contiennent pourraient
parfaitement tre intgres directement dans lbauche que nous avons construite,
mais lalgorithme reprsent par cette bauche serait alors moins ais suivre. Voici
lune de ces deux fonctions :
Python

C++

def Demander_Tour_Depart(jeu):
print "Tour de dpart?",
tour_depart = int(raw_input())
return tour_depart - 1

int Demander_Tour_Depart(const Hanoi& jeu)


{
int tour_depart;
std::cout << "Tour de dpart? ";
std::cin >> tour_depart;
return tour_depart - 1;
}

Comme vous pouvez le constater, rien de bien terrible. La seule petite subtilit qui
naura pas chapp au lecteur attentif est la valeur retourne : pourquoi soustraire 1
la valeur donne par lutilisateur ?
Souvenez-vous que pour les langages Python et C++, le premier lment dun tableau
porte le numro 0. Les tours sont donc numrotes de 0 2, les tages de 0 au nombre
dtages diminu de 1. Mais une telle numrotation est gnralement perue comme
non naturelle : spontanment, on numroterait plutt les lments dun tableau en
partant de 1, pas de 0.
Dans un souci de convivialit avec lutilisateur, on lui permet ici dutiliser une numrotation naturelle, en partant de 1. On sattend donc ce que lutilisateur nous donne
un nombre entre 1 et 3, quil nous faut transformer pour nos besoins internes de
programmation en nombre entre 0 et 2. Do la soustraction de 1 la valeur donne
par lutilisateur.
La fonction pour demander la tour darrive est tout fait similaire. Enfin, celle
permettant de savoir si le jeu est termin ou non se contente de vrifier si le nombre de
disques sur le troisime piquet est gal au nombre total de disques, cest--dire la
hauteur de la tour. Elle tient en une seule ligne :
Python

C++

def Est_Fini(jeu):
return jeu.tours[2].nb_disques == \
jeu.hauteur

bool Est_Fini(const Hanoi& jeu)


{
return jeu.tours[2].nb_disques ==
jeu.hauteur;
}

Program Livre Page 96 Vendredi, 16. mai 2008 8:13 08

96

Initiation la programmation avec Python et C++

Rptons-le, le symbole pour tester lgalit de deux valeurs est le double gal ==, pas
le simple signe gal = utilis pour laffectation. La confusion des deux symboles est
une erreur trs courante, surtout lorsquon dbute en programmation, donc prenez
garde !

7.3.3. Vrification des entres


Commenons par vrifier le numro demand pour le piquet de dpart. Celui-ci nest
valide que sil est compris entre 0 et 2 et quil y ait bien un disque sur ce pique t.
La fonction est assez simple :
Python

C++

def Tour_Depart_Valide(jeu,
depart):
if ( depart < 0 ):
return False
if ( depart > 2 ):
return False
return \
jeu.tours[depart].nb_disques > 0

bool Tour_Depart_Valide(const Hanoi& jeu,


int depart)
{
if ( (depart < 0) or
(depart > 2) )
{
return false;
}
return
jeu.tours[depart].nb_disques > 0;
}

Les deux versions de la fonction font exactement la mme chose mais laide de deux
critures diffrentes. La version en Python utilise deux tests pour vrifier lintervalle
du numro de piquet donn, tandis que la version en C++ utilise un seul test compos.
Le mot anglais or se traduit par ou, donc le test (depart<0) or (depart>2)
vaut vrai (true) si lun des deux membres au moins est vrifi. Notez que ces fonctions
contiennent plusieurs instructions return : il est en effet possible de "sortir" dune
fonction en plusieurs endroits. Naturellement, ds quune instruction return est
rencontre et excute, les ventuelles instructions qui suivent ne sont pas excutes.
Ici, la fonction est assez simple, mais dans le cas dune fonction complexe avec beaucoup dembranchements (boucles ou alternatives), il est prfrable de sarranger pour
navoir quune seule instruction return, place la fin : cela facilite la relecture et
lvolution du code.

Program Livre Page 97 Vendredi, 16. mai 2008 8:13 08

Chapitre 7

Les Tours de Hano

97

Comme on pouvait sy attendre, la vrification du piquet darrive est un peu plus


complique, car sa validit dpend du disque prsent sur le piquet de dpart et de celui
ventuellement prsent sur celui darrive. Cest prcisment ce moment que nous
allons assurer le respect de la rgle qui dit quil est interdit de poser un disque sur un
autre plus petit. Pour cela, nous devons trouver la taille des disques au sommet de
chacune des deux tours. Sachant que la structure Tour contient un membre
nb_disques qui est justement le nombre de disques prsents sur un piquet, si cette
valeur est suprieure 0, alors le disque au sommet se trouve lindice nb_disques-1
dans le tableau disques (la soustraction de 1 tant due, encore une fois, la numrotation des lments dun tableau qui commence 0).
partir de tous ces lments, vous devriez tre mme dcrire la fonction vrifiant le
piquet darrive. La voici, mais ne regardez pas la solution propose avant davoir
essay !
Python

C++

def Tour_Arrivee_Valide(jeu,
tour_depart,
tour_arrivee):
if tour_arrivee < 0:
return False
if tour_arrivee > 2:
return False
nb_disques_arrivee = \
jeu.tours[tour_arrivee].nb_disques
if ( nb_disques_arrivee == 0 ):
return True
disque_arrivee = \
jeu.tours[tour_arrivee]. \
disques[nb_disques_arrivee-1]
nb_disques_depart = \
jeu.tours[tour_depart].nb_disques
disque_depart = \
jeu.tours[tour_depart]. \
disques[nb_disques_depart-1]
return \
disque_arrivee > disque_depart

bool Tour_Arrivee_Valide(const Hanoi&jeu,


int tour_depart,
int tour_arrivee)
{
if ( (tour_arrivee < 0) or
(tour_arrivee > 2) )
{
return false;
}
int nb_disques_arrivee =
jeu.tours[tour_arrivee].nb_disques;
if ( nb_disques_arrivee == 0 )
return true;
int disque_arrivee =
jeu.tours[tour_arrivee].
disques[nb_disques_arrivee-1];
int nb_disques_depart =
jeu.tours[tour_depart].nb_disques;
int disque_depart =
jeu.tours[tour_depart].
disques[nb_disques_depart-1];
return disque_arrivee > disque_depart;
}

Program Livre Page 98 Vendredi, 16. mai 2008 8:13 08

98

Initiation la programmation avec Python et C++

La mme chose aurait pu tre crite plus succinctement, sans utiliser les variables
intermdiaires que sont nb_disques_arrivee, disque_depart, etc. Ces variables ont
pour objet de clarifier lcriture de chaque instruction. Sans elles, nous aurions quelque
chose de relativement illisible, par exemple en Python :
return \
jeu.tours[tour_depart].disques[jeu.tours[tour_depart].nb_disques-1] > \
jeu.tours[tour_arrivee].disques[jeu.tours[tour_arrivee].nb_disques-1]

Pour finir, il serait pertinent de vrifier galement le nombre de disques demands par
lutilisateur au dbut du programme : on peut considrer quil faut au moins deux
disques, mais pas plus dune dizaine, le nombre optimal de dplacements pour dix
disques tant de 1 023, ce qui est dj beaucoup.

7.3.4. Dplacement dun disque


Nous en arrivons au cur du programme, la fonction qui va effectivement dplacer
un disque dun piquet vers un autre. Ou plutt, simuler ce dplacement, qui va en
ralit se traduire par des modifications opres dans les tableaux de disques des
tours.
Le principe est en fait assez comparable celui utilis dans la vrification du piquet
darrive. Sauf quau lieu de retourner une valeur boolenne nous allons modifier le
contenu des instances de la structure Tour : linstance qui correspond la tour de dpart
perdra un disque, que celle qui correspond la tour darrive gagnera. Cette fonction
ne devrait pas prsenter trop de difficults :
Python

C++

def Deplacer_Disque(jeu, \
depart, \
arrivee):
nb_disques_depart = \
jeu.tours[depart].nb_disques
nb_disques_arrivee = \
jeu.tours[arrivee].nb_disques
disque_depart = \
jeu.tours[depart]. \
disques[nb_disques_depart-1]

void Deplacer_Disque(Hanoi& jeu,


int depart,
int arrivee)
{
int nb_disques_depart =
jeu.tours[depart].nb_disques;
int nb_disques_arrivee =
jeu.tours[arrivee].nb_disques;
int disque_depart =
jeu.tours[depart].
disques[nb_disques_depart-1];

Program Livre Page 99 Vendredi, 16. mai 2008 8:13 08

Chapitre 7

Python

Les Tours de Hano

99

C++
jeu.tours[arrivee].
disques[nb_disques_arrivee] = \
disque_depart;
jeu.tours[depart].
disques[nb_disques_depart-1] = 0;
jeu.tours[depart].nb_disques -= 1;
jeu.tours[arrivee].nb_disques += 1;

jeu.tours[arrivee]. \
disques[nb_disques_arrivee] = \
disque_depart
jeu.tours[depart]. \
disques[nb_disques_depart-1] = 0
jeu.tours[depart].nb_disques -= 1
jeu.tours[arrivee].nb_disques += 1
}

Remarquez lantpnultime instruction, qui place la valeur 0 lendroit du tableau o


se trouvait le disque quon vient de retirer. Cela permet de conserver le contenu de nos
tableaux dans un tat cohrent : un tage o il ny a pas de disques est suppos contenir la valeur 0. Par ailleurs, la dclaration de la fonction dans la partie C++ nutilise
pas le qualificatif const devant le type Hanoi: en effet, le contenu de linstance de
Hanoi doit tre modifi par la fonction (cest son objet mme), le paramtre ne peut
donc pas tre considr comme invariable au sein de la fonction.

Le reste du programme est laiss en exercice au lecteur, tous les lments sont
dsormais disponibles. Vous trouverez le code complet sur le site web de Pearson
www.pearson.fr la page ddie cet ouvrage.

7.4. Rsum de la dmarche


Nous arrivons au terme de cette premire partie. Le chapitre que vous venez de lire
vous a donn un aperu de la dmarche usuelle lorsquon souhaite dvelopper un
programme qui nest pas trivial :

dabord, modliser les donnes du problme traiter, en usant de structures et de


tableaux pour rassembler les objets logiquement lis entre eux ;

ensuite, concevoir les principaux algorithmes, en les exprimant littralement, le cas


chant en dessinant des ordinogrammes pour une prsentation plus visuelle ;

poursuivre la conception tant quil reste des tapes qui ne paraissent pas triviales ;

enfin et seulement, crire le code correspondant aux algorithmes prcdemment


conus et aux fonctions prvues en cours danalyse.

Program Livre Page 100 Vendredi, 16. mai 2008 8:13 08

100

Initiation la programmation avec Python et C++

Naturellement, il nest pas ncessaire de suivre un cheminement aussi prcis lorsque


vous avez raliser un petit programme rapide. Tout aussi naturellement, cette dmarche est indispensable dans le cadre de tout projet important. Les grands logiciels que
vous avez sans doute lhabitude de manipuler ont t conus en suivant un paradigme
analogue, gnralement bien plus strict et dtaill.
Avec lexprience, vous prouverez de moins en moins le besoin de dtailler les tapes
dun programme mesure que vous analysez le problme et les algorithmes impliqus. Un programmeur ayant peine une ou deux annes de pratique serait capable de
produire le programme que nous venons de raliser en une heure, aboutissant un
rsultat sans doute diffrent, mais assez ressemblant. Noubliez pas, cest en forgeant
que lon devient forgeron !
Vous constaterez galement une forte tendance vous jeter sur le clavier, pour crire
des kilomtres dinstructions qui vous viennent presque naturellement lesprit.
Rsistez cette tentation : elle est souvent le plus court chemin vers linefficacit et la
perte de temps. Il est de loin prfrable de sasseoir quelques minutes, avec un crayon
et une feuille de papier, pour prvoir les tapes raliser ft-ce au minimum et
gnralement, la fin, ce sera un gain de temps.

Program Livre Page 101 Vendredi, 16. mai 2008 8:13 08

Partie

Dveloppement logiciel
CHAPITRE 8. Le monde modlis : la programmation oriente objet (POO)
CHAPITRE 9. Hano en objets
CHAPITRE 10. Mise en abme : la rcursivit
CHAPITRE 11. Spcificits propres au C++
CHAPITRE 12. Ouverture des fentres : programmation graphique
CHAPITRE 13. Stockage de masse : manipuler les fichiers
CHAPITRE 14. Le temps qui passe

Program Livre Page 102 Vendredi, 16. mai 2008 8:13 08

Program Livre Page 103 Vendredi, 16. mai 2008 8:13 08

8
Le monde modlis :
la programmation
oriente objet (POO)
Au sommaire de ce chapitre :

Les vertus de lencapsulation

La rutilisation par lhritage

Hritage et paramtres : le polymorphisme

Program Livre Page 104 Vendredi, 16. mai 2008 8:13 08

104

Initiation la programmation avec Python et C++

Les programmes que nous avons crs jusquici dclaraient des variables, dfinissaient des fonctions agissant sur ces variables, tout cela un peu ple-mle. Si cest
acceptable pour un petit programme, cela devient rapidement ingrable ds que lon
sattaque une application denvergure. Aussi, le besoin dorganiser les choses, de
regrouper ensemble les lments lis logiquement, sest-il rapidement impos aux
programmeurs.
Plusieurs techniques ont t labores pour permettre cette organisation. Par exemple,
regrouper les fonctions en modules cohrents, auxquels un programme principal peut
faire appel. Nous verrons plus tard cette notion de modules. Ici, nous allons tudier
une autre technique, qui prend ses racines en 1967 avec le langage Simula, puis un peu
plus tard avec SmallTalk : la programmation par objets.
Ce paradigme de programmation sest vritablement impos dans les annes 1980,
avec lavnement du langage C++, extension du langage C, pour introduire dans ce
dernier les principes de la programmation objet. Dautres langages ont galement t
tendus pour fournir les outils objet : Ada, Pascal, mme Basic sont des exemples. Des
langages objet ds leur cration ont galement t conus, comme Java, Ruby, ou
notre ami Python. Aujourdhui, la programmation par objets est le paradigme de
programmation le plus largement utilis dans le monde.
Mais finalement, quest-ce quun objet ? Il sagit avant tout dun mcanisme
dabstraction, qui repose sur un principe fondamental. La chose manipule par le
programme, quil sagisse de la modlisation dun objet physique, comme une voiture,
ou dun objet abstrait, comme une fonction mathmatique, est plus que la simple
collection de ses caractristiques. Pour modliser correctement cette chose, il faut
galement prendre en compte les mthodes, les rgles de son utilisation. Une voiture
ne se rduit pas une vitesse et une quantit dessence : cest galement un acclrateur
par lequel on agit sur ces deux quantits.
Lobjet vise donc fournir une reprsentation plus proche de la ralit de ce quon
manipule : contenir en une mme structure informatique, non seulement les donnes
associes la chose, mais aussi les moyens, les mthodes par lesquelles on peut agir
dessus.
Un autre objectif essentiel de la programmation oriente objet est la rutilisation
du code. Cest--dire, fournir des mcanismes facilitant la cration de nouvelles
structures partir de plus anciennes, lesquelles sont adaptes, ajustes, spcialises.

Program Livre Page 105 Vendredi, 16. mai 2008 8:13 08

Chapitre 8

Le monde modlis : la programmation oriente objet (POO)

105

la base de ces mcanismes se trouve la notion dhritage ou de drivation : un


nouveau type objet est construit partir dun autre, dont il hrite les caractristiques et les mthodes. On aboutit ainsi une vritable taxonomie des "choses" que le
programme doit manipuler, qui nest pas sans rappeler celle des espces animales.

8.1. Les vertus de lencapsulation


Mais avant dexaminer le principe dhritage, penchons-nous un instant sur celui de
lencapsulation. Ce terme trange, qui voque laction de mettre une capsule (un
bouchon) sur quelque chose, dsigne une rgle affirmant que, autant que possible, les
donnes dun objet ne doivent pas tre directement accessibles. Cest--dire que
lutilisateur dun objet, que cela soit vous-mme ou un autre programmeur, ne doit pas
pouvoir modifier (ni mme consulter) les valeurs des variables contenues dans lobjet
sans que cet accs soit contrl par lobjet lui-mme. Si cela peut sembler bien
contraignant au premier abord, ce principe sest impos de lui-mme comme une
ncessit absolue pour obtenir des programmes fiables, fonctionnant correctement et
donnant des rsultats cohrents.
Imaginons que vous vouliez modliser un vhicule motoris, comme une voiture, un
bateau ou un vaisseau spatial. Pour simplifier les choses lextrme, nous nallons
considrer que deux caractristiques de ce vhicule : sa vitesse et la quantit de carburant quil lui reste un instant donn. Au moment de sa "cration", notre vhicule est
statique (sa vitesse est nulle) et nemporte pas de carburant. Avec ce que nous avons vu
jusquici, nous pourrions modliser cela ainsi :
Python

C++

class Vehicule:
def __init__(self):
self.vitesse = 0.0
self.carburant = 0.0

struct Vehicule
{
Vehicule()
{
this->vitesse = 0.0;
this->carburant = 0.0;
}
double vitesse;
double carburant;
};

Program Livre Page 106 Vendredi, 16. mai 2008 8:13 08

106

Initiation la programmation avec Python et C++

Nous pouvons maintenant crer des vhicules, de nimporte quel type (croyons-nous).
Lutilisation de cette structure parat tout fait triviale. Pour faire le plein dun vhicule,
rien de plus simple :
Python

C++

moto = Vehicule()
moto.carburant = 100.0

Vehicule moto;
moto.carburant = 100.0;

Puis nous pouvons "acclrer", sans problme, simplement en modifiant la vitesse de


notre moto :
Python

C++

moto.vitesse = 90.0

moto.vitesse = 90.0;

Tout va bien. Nous pouvons continuer dacclrer ainsi ou ralentir. La magie de tout
cela, cest que la quantit de carburant ne change pas.
Et cest bien l le problme.
Nous venons de modliser le vhicule idal, capable de changer de vitesse
volont sans consommer une goutte de carburant. Vous admettrez que nous sommes
bien loin de la ralit : lorsque vous conduisez une voiture, vous ne pouvez acclrer que par lintermdiaire de la pdale dacclrateur, action qui se traduit forcment par une diminution de la quantit disponible de carburant. Jusqu la panne
sche.
Autrement dit : notre modle offre toutes les possibilits pour donner des rsultats
incohrents.
Le problme vient du fait que nous pouvons modifier directement les donnes de nos
objets. Et pour lheure, il ne saurait en tre autrement, nous navons gure le choix.
Lidal serait de disposer dune mthode, nomme Acceleration(), qui se chargerait
elle-mme de modifier les valeurs la fois de la vitesse et du carburant restant.

Program Livre Page 107 Vendredi, 16. mai 2008 8:13 08

Chapitre 8

Le monde modlis : la programmation oriente objet (POO)

107

Voici une solution possible :


Python

C++

class Vehicule:

class Vehicule

def __init__(self):

self.vitesse = 0.0

public:

self.carburant = 0.0

Vehicule():

def Remplir(self, quantite):

vitesse(0.0),

self.carburant += quantite

carburant(0.0)

def Accelerer(self, delta):

{ }

self.vitesse += delta

void Remplir(double quantite)

self.carburant -= delta/10.0

def Vitesse(self):

this->carburant += quantite;

return self.vitesse

def Carburant(self):

virtual

return self.carburant

void Accelerer(double delta)

# ...

moto = Vehicule()

this->vitesse += delta;

moto.Remplir(50.0)

this->carburant -= delta/10.0;

moto.Accelerer(90.0)

}
double Vitesse() const
{ return this->vitesse; }
double Carburant() const
{ return this->carburant; }
protected:
double vitesse;
double carburant;
};
/* ... */
Vehicule moto;
moto.Remplir(50.0);
moto.Accelerer(90.0);

La notion de donnes prives en Python


nexiste pas rellement, cest--dire quil
nest pas "naturel" de chercher limiter
laccs aux donnes dune classe.

En C++, on utilise de prfrence le mot cl


class plutt que struct. Tout ce qui se
trouve dans une classe C++ est normalement

Program Livre Page 108 Vendredi, 16. mai 2008 8:13 08

108

Initiation la programmation avec Python et C++

Python

C++

Une technique consistant faire prcder le


nom dune donne par un double caractre
de soulignement existe (par exemple,
self.__carburant), mais elle nest gure
pratique et prsente plus dinconvnients
que davantages.

priv (private), cest--dire que lutilisateur


de la classe ne peut y accder. Les mots cls
public et protected permettent dindiquer si ce qui les suit est, respectivement,
librement accessible ou dun accs restreint
lintrieur de la classe.

La protection des donnes en Python est


donc essentiellement une affaire de convention entre le concepteur dune classe et
lutilisateur de cette classe.

Le sens du mot virtual qui prcde la


mthode Accelerer() sera explicit
la dernire section de ce chapitre.

Notez les changements subtils (et moins subtils) par rapport aux dclarations prcdentes. Dsormais, lors de lutilisation de la classe Vehicule, les donnes qui lui sont
propres comme le carburant et la vitesse ne sont plus directement modifiables : il est
ncessaire de passer par lintermdiaire de mthodes, ici Remplir() et Accelerer(),
pour altrer leurs valeurs. De cette faon, il est possible dassurer la cohrence des
donnes de la classe et ainsi dviter dobtenir des rsultats fantaisistes.
Les mthodes Vitesse() et Carburant() sont ce quon appelle des accesseurs : leur
seule et unique vocation est de donner la valeur des donnes correspondantes, celles-ci
ntant pas accessibles puisque prives. Dans notre situation, cela se rsume retourner ce qui est contenu dans les donnes membres de linstance. Mais cela peut galement rsulter dun calcul plus ou moins complexe, comme nous allons le voir bientt.
Remarquez la prsence du mot cl const en C++ : il indique que ces mthodes ne
vont pas modifier le contenu de linstance (cest--dire modifier les valeurs des
donnes membres). Cest une indication donne au compilateur, qui va vrifier que
cette rgle est effectivement suivie. Si, par mgarde, vous tentez de modifier une
donne au sein dune mthode qualifie par le mot cl const, le compilateur vous le
signalera par un message derreur. Ce sera alors trs certainement le signe dun
problme de conception.
Ce principe consistant limiter laccs aux donnes dune classe est donc dsign par
le terme dencapsulation. Dune manire gnrale, il est communment admis que
toutes les donnes dune classe devraient tre prives, la classe fournissant des mthodes
spcialises, des accesseurs, la fois pour obtenir les valeurs des donnes et pour les
changer. De ce fait, les changements sont contrls, ce qui doit normalement induire
une meilleure cohrence de lensemble.

Program Livre Page 109 Vendredi, 16. mai 2008 8:13 08

Chapitre 8

Le monde modlis : la programmation oriente objet (POO)

109

Enfin, notre modle est ici extrmement simple. Dans la mthode Accelerer(), on ne
devrait effectivement modifier la vitesse que sil reste suffisamment de carburant pour
cela. Le code est toujours potentiellement incohrent, mais au moins la structure est
plus robuste. Le lecteur est invit modifier cette mthode pour la rendre plus raliste.

8.2. La rutilisation par lhritage


Notre vhicule nous a permis de reprsenter une moto. Et nous pourrions reprsenter de mme une voiture, un camion Mais considrez le cas dun avion, voire
dun sous-marin. Il serait intressant de disposer de donnes supplmentaires,
comme laltitude ou la profondeur, ainsi que de mthodes supplmentaires, comme
monter ou descendre. Ce sont des vhicules bien diffrents dune moto, mais cela
reste des vhicules.
Naturellement, nous pourrions simplement dupliquer le code de notre classe Vehicule, par un copier-coller trop facile, et ajouter ce quil nous manque. Mais, dune
part, lexprience montre que la duplication de code est source derreurs et de
dysfonctionnements (car on copie galement les bogues contenus dans le code),
dautre part, il pourrait tre intressant de conserver la smantique qui affirme quune
moto, un avion et un sous-marin sont tous des vhicules. Ils font tous partie dune
mme famille.
La solution consiste utiliser lhritage, cest--dire dfinir de nouvelles classes
partir de classes existantes. Voici quoi pourrait ressembler une classe Avion :
Python

C++

class Avion(Vehicule):
def __init__(self):
Vehicule.__init__(self)
self.altitude = 0.0
def Monter(self, delta):
self.altitude += delta
def Descendre(self, delta):
sefl.altitude -= delta
def Altitude(self):
return self.altitude
def Accelerer(self, delta):
print "Avion acclre..."
self.vitesse += delta

class Avion: public Vehicule


{
public:
Avion():
Vehicule(),
altitude(0.0)
{ }
void Monter(double delta)
{
this->altitude += delta;
}

Program Livre Page 110 Vendredi, 16. mai 2008 8:13 08

110

Initiation la programmation avec Python et C++

Python

C++

self.carburant -= \
delta * \
((self.Altitude()+1.0)/1000.0)

void Descendre(double delta)


{
this->altitude -= delta;
}
virtual
void Accelerer(double delta)
{
std::cout <<
"Avion acclre...\n";
this->vitesse += delta;
this->carburant -= \
delta *
((this->Altitude()+1.0)/1000.0);
}
double Altitude() const
{ return this->altitude; }
protected:
double altitude;
};

Voyez comment est dclare la nouvelle classe :

En Python, class Avion(Vehicule) indique que la classe Avion drive de la


classe Vehicule, ou encore quelle en hrite.

En C++, class Avion: public Vehicule indique que la classe Avion drive de
la classe Vehicule, ou encore quelle en hrite.

Tout cela signifie que la classe Avion "contient" tout ce que contient la classe Vehicule. Constatez la puissance du procd : nous avons une nouvelle classe, offrant au
total deux fois plus de mthodes que la prcdente, mais sans devoir crire deux fois
plus de code. Le lecteur est invit crire le code ncessaire une classe Sous_Marin,
tout fait similaire, mais qui aurait une profondeur plutt quune altitude. Ou alors,
imaginez une classe Poisson_Volant, ayant la fois une altitude et une profondeur
ce qui imposerait de savoir tout moment laquelle des deux valeurs est pertinente.
Le monde naturel est une source inpuisable de surprises.

Program Livre Page 111 Vendredi, 16. mai 2008 8:13 08

Chapitre 8

Le monde modlis : la programmation oriente objet (POO)

111

Remarquez tout de mme que nous avons surdfini la mthode Accelerer(). Cela
signifie que nous avons dfini dans la nouvelle classe une mthode de mme nom,
ayant le mme aspect (on dit plutt la mme signature), cest--dire prenant les
mmes paramtres dans le mme ordre. Si nous dclarons une instance dAvion et
que nous invoquions la mthode Accelerer(), cest bien celle surdfinie dans
Avion qui sera utilise, pas celle venant de la classe mre Vehicule.
Par exemple :
Python

C++

robin = Avion()
robin.Remplir(50.0)
print robin.Carburant()
robin.Accelerer(100.0)
print robin.Carburant()

int main(int argc, char* argv[])


{
Avion robin;
robin.Remplir(50.0);
std::cout << robin.Carburant()
<< std::endl;
robin.Accelerer(100.0);
std::cout << robin.Carburant()
<< std::endl;
return 0;
}

Les mthodes Remplir() et Carburant() dans les extraits prcdents viennent directement de la classe Vehicule, bien quelles soient appliques une instance de la
classe Avion. On dit que la mthode Remplir() est hrite de la classe Vehicule, ou
encore que la classe Avion hrite de la mthode Remplir() partir de la classe Vehicule. Par contre, Accelerer() est bien celle dfinie dans Avion.
Du ct du C++, il est prfrable que toute mthode pouvant tre ainsi surdfinie dans
une classe fille soit qualifie par le mot cl virtual, comme cest le cas ici. Nous
allons voir immdiatement pourquoi.

Program Livre Page 112 Vendredi, 16. mai 2008 8:13 08

112

Initiation la programmation avec Python et C++

8.3. Hritage et paramtres : le polymorphisme


En utilisant lhritage, nous pouvons crer toute une famille de type de vhicules.
Remarquez que nous navons dfini quune mthode Accelerer(), pas de mthode
permettant de dfinir exactement une vitesse. Ce nest pas trs grave : partir dune
vitesse donne, on peut atteindre une autre vitesse en acclrant ou en freinant (freiner
quivalant une acclration ngative). On peut alors imaginer une fonction ressemblant
ceci :
Python
def Atteindre_Vitesse(un_vehicule, une_vitesse):
delta = une_vitesse - \
un_vehicule.Vitesse()
un_vehicule.Accelerer(delta)

C++
void Atteindre_Vitesse(Vehicule& un_vehicule,
double une_vitesse)
{
double delta = une_vitesse un_vehicule.Vitesse();
un_vehicule.Accelerer(delta);
}

Remarquez la prsence de lperluette : on passe bien une rfrence sur une instance de
Vehicule.

La question que lon peut alors se poser est, si ce que nous venons dcrire fonctionne
pour une instance de Vehicule, comment faire pour une instance dAvion ou pour
toute autre classe drive de Vehicule ?
La bonne nouvelle est que le comportement est naturel, cest--dire quil correspond
ce quon attend effectivement.

Program Livre Page 113 Vendredi, 16. mai 2008 8:13 08

Chapitre 8

Le monde modlis : la programmation oriente objet (POO)

113

Par exemple :
Python
moto = Vehicule()
moto.Remplir(50.0)
robin = Avion()
robin.Remplir(50.0)
Atteindre_Vitesse(moto, 90.0)
Atteindre_Vitesse(robin, 100.0)

C++
Vehicule moto;
moto.Remplir(50.0);
Avion robin;
robin.Remplir(50.0);
Atteindre_Vitesse(moto, 90.0);
Atteindre_Vitesse(robin, 100.0);

Nous crons deux vhicules, lun "gnrique" (la moto), lautre un modle davion.
Puis nous cherchons pour chacun deux atteindre une vitesse donne, en utilisant la
fonction Atteindre_Vitesse().
En son sein, celle-ci va faire appel la mthode Accelerer() prsente dans chacune
des classes Vehicule et Avion. Ce qui se passe ensuite est trs naturel : si la fonction
reoit en paramtre une instance de Vehicule, alors cest la mthode Accelerer() de
Vehicule qui sera invoque ; si elle reoit en paramtre une instance dAvion, alors
cest la mthode Accelerer() dAvion qui sera invoque. Si vous excutez ce
programme, vous obtiendrez laffichage :
Vehicule acclre...
Avion acclre...

Ce qui est la preuve que cest la "bonne" mthode qui est utilise.
Cela peut sembler vident en Python, mais beaucoup moins en C++ : le type du paramtre pass la fonction Atteindre_Vitesse() est une rfrence une instance de
Vehicule et cest justement en raison de cette rfrence que cela fonctionne, associ
au fait que la classe Avion drive de la classe Vehicule.
La variable robin est une instance dAvion. Mais comme Avion drive de Vehicule,
robin est aussi une instance de Vehicule. On peut donc la passer la fonction
Atteindre_Vitesse(). Consquence de la rfrence, un phnomne un peu trange
survient : la fonction "croit" recevoir une instance de Vehicule, mais cest bel et bien
une instance dAvion quelle manipule, sans mme le "savoir". Les mthodes invoques dans la fonction ne peuvent qutre des mthodes prsentes dans la classe Vehicule, mais ce sont bien les mthodes de la classe Avion qui seront invoques. Sil a
t ncessaire de surdfinir une mthode, comme cest le cas de la mthode Accelerer(), la prsence du mot cl virtual garantit que, dans cette situation, cest la

Program Livre Page 114 Vendredi, 16. mai 2008 8:13 08

114

Initiation la programmation avec Python et C++

mthode surdfinie qui sera invoque. Cest pourquoi toute mthode dune classe
destine tre surdfinie dans une classe drive doit tre qualifie par le mot cl
virtual.
Faites les expriences suivantes. Dabord, retirez le mot cl virtual prcdant la
dclaration de la mthode Accelerer() dans la classe Vehicule, puis compilez et
excutez nouveau le programme. Ensuite, replacez le mot cl virtual, mais retirez
lperluette dans la dclaration de la fonction Atteindre_Vitesse(), puis compilez
et excutez nouveau le programme. Vous obtiendrez ce rsultat :
Vehicule acclre...
Vehicule acclre...

Seule la mthode Accelerer() de la classe Vehicule est invoque.


Avec la combinaison de virtual et de lperluette, on peut dire que le paramtre
un_vehicule pass la fonction change daspect selon la nature relle de la variable
qui lui est donne. Ce phnomne est dsign par le terme de polymorphisme, qui
signifie littralement "pouvant prendre plusieurs formes". Pour la fonction
Atteindre_Vitesse(), le paramtre un_vehicule est polymorphe : selon le type de
la variable qui lui est donne, il aura en ralit tantt la forme dun Vehicule, tantt la
forme dun Avion, ou toute autre forme dfinie par une classe drivant de Vehicule (ou
mme dAvion).
En langage Python, le polymorphisme est implicite et automatique. En langage C++, il
ncessite lemploi des rfrences pour les paramtres et du mot cl virtual pour les
mthodes susceptibles dtre surdfinies dans des classes drives. Il sagit l dun
mcanisme fondamental et extrmement puissant de la programmation oriente objet,
comme nous allons le constater bientt.

Program Livre Page 115 Vendredi, 16. mai 2008 8:13 08

9
Hano en objets
Au sommaire de ce chapitre :

Des tours compltes

Un jeu autonome

Linterface du jeu

Le programme principal

Program Livre Page 116 Vendredi, 16. mai 2008 8:13 08

116

Initiation la programmation avec Python et C++

Il est maintenant temps que transformer le programme du jeu des Tours de Hano, cr
au Chapitre 7, afin de lui appliquer ce que nous venons de voir. Toutes les techniques
ne seront pas utilises immdiatement, mais cela va vous donner une vue densemble
de ce quoi peut ressembler un programme objet complet.

9.1. Des tours compltes


Pour commencer, voyons la classe nous permettant de reprsenter une tour, ou plutt
laiguille portant la tour. La structure prsente au Chapitre 7 se limitait au seul constructeur. Nous avons maintenant la possibilit de faire bien mieux, en ajoutant de
nombreuses petites mthodes pratiques.
Python
class Tour:
def __init__(self, taille_max):
self.nb_disques = 0
self.disques = \
[0 for e in range(taille_max)]
def Est_Vide(self):
return self.nb_disques == 0
def Est_Pleine(self):
return \
self.nb_disques == \
len(self.disques)
def Sommet(self):
if self.Est_Vide():
return 0
else:
return self.disques [self.nb_disques-1]
def Disque_Etage(self, etage):
if ( self.Est_Vide() or \
(etage >= self.nb_disques) ):
return 0
else:
return self.disques[etage]
def Peut_Empiler(self, disque):
if self.Est_Vide():
return True
else:
return (self.Sommet() > disque)
def Empiler(self, disque):
self.disques[self.nb_disques] = disque
self.nb_disques += 1
def Depiler(self):
self.nb_disques -= 1
self.disques[self.nb_disques] = 0

Pour Python, il importe de bien


respecter lindentation : cest en
effet elle qui va assurer, dune part,
que la dfinition dune mthode
appartient une classe, dautre part,
que des instructions appartiennent
une mthode.
Par ailleurs, si vous comparez ce
code avec la version C++ qui suit,
vous constatez quil nexiste pas de
signes particuliers pour indiquer
quune mthode ne modifie pas
linstance ou quune donne ne doit
pas tre utilise directement.
Python ne possde pas, en effet, de
moyen simple et immdiat pour
mettre en uvre ces techniques
normalement destines amliorer
la robustesse du programme.
En contrepartie, le code est plus
simple et plus "compact".

Program Livre Page 117 Vendredi, 16. mai 2008 8:13 08

Chapitre 9

Hano en objets

117

C++
class Tour
{
public:
Tour(int taille_max):
nb_disques(0),
disques(taille_max, 0)
{ }
bool Est_Vide() const
{
return this->nb_disques == 0;
}
bool Est_Pleine() const
{
return
this->nb_disques ==
this->disques.size();
}
int Sommet() const
{
if ( this->Est_Vide() )
return 0;
else
return
this->disques [this->nb_disques-1];
}
int Disque_Etage(int etage) const
{
if ( this->Est_Vide() or
(etage >= this->nb_disques) )
return 0;
else
return this->disques[etage];
}
bool Peut_Empiler(int disque) const
{
if ( this->Est_Vide() )
return true;
else
{
if ( this->Est_Pleine() )
return false;
else
return (this->Sommet() > disque);
}
}
void Empiler(int disque)
{
this->disques[this->nb_disques] = disque;
this->nb_disques += 1;
}

Ct C++, remarquez comme les


mthodes
purement informatives,
comme Est_Vide(), sont toutes qualifies par le mot cl const.
Comme nous lavons vu au chapitre
prcdent, cela indique que ces mthodes ne doivent pas modifier linstance
sur laquelle elles sont appliques.
Grce quoi, des erreurs dinattention
peuvent tre dtectes, comme utiliser
un simple signe = (affectation) la
place du signe == (comparaison
dgalit).
Par ailleurs, les donnes de la classe
sont places dans une section private
(prive), contrairement la section
protected (protge) vue au chapitre
prcdent. La consquence est une
meilleure protection de ces donnes :
non seulement, elles ne sont pas directement utilisables lextrieur de la
classe mais, en plus, mme une classe
drivant de celle-ci ne pourrait pas les
manipuler. On aboutit ainsi une
encapsulation complte de ces
donnes.
Enfin, notez quil est fait une certaine
conomie sur les accolades, notamment dans les structures alternatives
(if). En labsence daccolades, seule
lunique instruction suivant immdiatement le test introduit par if est excute
si la condition est vrifie. Ainsi :
if ( une_condition )
une_instruction;
une_autre_instruction;

Program Livre Page 118 Vendredi, 16. mai 2008 8:13 08

118

Initiation la programmation avec Python et C++

C++
void Depiler()
{
this->nb_disques -= 1;
this->disques[this->nb_disques] = 0;
}
private:
int nb_disques;
std::vector<int> disques;
}; // class Tour

est quivalent :
if ( une_condition )
{
une_instruction;
}
une_autre_instruction;

Rappelez-vous que lindentation en


C++ est purement dcorative et na
aucune valeur pour le langage.

Les mthodes ajoutes dans cette classe devraient parler delles-mmes. Dcrivons-les
brivement :

Est_Vide() permet de savoir si une tour est vide ou non, cest--dire si elle
contient au moins un disque ou non.

Est_Pleine(), symtrique de la prcdente, permet de savoir si tous les tages


disponibles sont occups.

Sommet() donne la taille du disque prsent au sommet de la tour, Disque_Etage()


donne la taille du disque prsent un tage donn ; une taille nulle signifie
quaucun disque nest prsent lendroit demand.

Peut_Empiler() fait partie de la modlisation de la rgle du jeu : elle indique si la


tour peut recevoir la taille de disque quon lui donne en paramtre.

Empiler() et Depiler() permettent, respectivement, dajouter ou de retirer un


disque au sommet de la tour. Elles sont ici particulirement sommaires : dans un
vritable programme, il faudrait ajouter des tests pour vrifier que ces oprations
sont lgitimes (par exemple, on ne peut dpiler un disque dune tour vide). Ces
tests sont laisss en exercice au lecteur.

Peut-tre toutes ces mthodes assez lmentaires vous semblent-elles inutiles. Aprs
tout, elles contiennent si peu dinstructions quon pourrait tout aussi bien les rcrire
lorsque le besoin se prsente. Mais voyons comment, maintenant, nous allons modliser
le jeu lui-mme.

Program Livre Page 119 Vendredi, 16. mai 2008 8:13 08

Chapitre 9

Hano en objets

119

9.2. Un jeu autonome


Vous laurez compris, nous allons maintenant tendre la structure Hanoi du Chapitre 7, comme nous avons tendu la structure Tour. Mais ne perdez pas de vue la raison
dtre de cette structure : modliser le jeu des Tours de Hano et rien dautre. En
particulier, cela signifie que nous nallons y intgrer aucune mthode ralisant un affichage quelconque : nous allons bel et bien crer un moteur de jeu, parfaitement indpendant de la manire dont il sera prsent lutilisateur. Nous verrons plus loin
limportance que cela aura.
Vous retrouverez dans cette classe certaines des fonctions que nous avions cres dans
notre premier programme de jeu, naturellement adaptes au nouveau contexte. Un
changement important rside dans la cration dune mthode Initialiser(), dont le
but est tout simplement de prparer le jeu dans un tat initial en fonction dun certain
nombre de disques demands.
Voici donc notre nouvelle classe Tour :
Python
class Hanoi:
def __init__(self):
self.hauteur_max = 0
self.tours = []
def Initialiser(self, nb_disques):
self.tours = [Tour(nb_disques), \
Tour(nb_disques), \
Tour(nb_disques)]
for disque in range(nb_disques,0, -1):

Dans le constructeur, lcriture


self.tours=[] permet de
dclarer la donne membre
tours comme un tableau,
initialement vide (ne contenant aucun lment).
Au sein de la mthode Initialiser(), le tableau des tours
est tout simplement recr.

self.tours[0].Empiler(disque)
self.hauteur_max = nb_disques
def Hauteur(self):
return self.hauteur_max
def Une_Tour(self, num_tour):

Un nouveau tableau, dont on


donne explicitement la liste des
lments entre crochets, lui est
affect.

return self.tours[num_tour]

Program Livre Page 120 Vendredi, 16. mai 2008 8:13 08

120

Initiation la programmation avec Python et C++

Python
def Tour_Depart_Valide(self, depart):
if (depart < 0) or (depart > 2):
return False
return (not self.tours[depart].Est_Vide())
def Tour_Arrivee_Valide(self, depart,arrivee):
if (arrivee < 0) or (arrivee > 2):
return false;
disque_depart = self.tours[depart].Sommet()
return
self.tours[arrivee].Peut_Empiler(disque_depart)
def Deplacer_Disque(self, depart, arrivee):
disque = self.tours[depart].Sommet()
self.tours[depart].Depiler()
self.tours[arrivee].Empiler(disque)
def Jeu_Termine(self):
return self.tours[2].Est_Pleine()

C++
class Hanoi
{
public:
Hanoi():
hauteur_max(0),
tours()
{
}
void Initialiser(int nb_disques)
{
this->tours.clear();
this->tours.resize(3, Tour(nb_disques));
for(int disque = nb_disques;
disque > 0;
--disque)
{
this->tours[0].Empiler(disque);
}
this->hauteur_max = nb_disques;
}
int Hauteur() const
{
return this->hauteur_max;
}
const Tour& Une_Tour(int i) const

Au sein de la mthode Initialiser(), on utilise deux


mthodes propres au type
std::vector.
clear() a pour effet de vider le
tableau, cest--dire de dtruire
tous les lments quil contient :
tout se passe alors comme si on
avait un tableau vide aprs son
application.
resize(), au contraire, permet
de spcifier la taille du tableau,
cest--dire
le
nombre
dlments quil pourra contenir.

Program Livre Page 121 Vendredi, 16. mai 2008 8:13 08

Chapitre 9

Hano en objets

121

C++
{
return this->tours[i];
}
bool Tour_Depart_Valide(int depart)const
{
if ( (depart < 0) or (depart > 2) )
{
return false;
}
return (not this->tours[depart].Est_Vide());
}
bool Tour_Arrivee_Valide(int depart,
int arrivee)const
{
if ( (arrivee < 0) or (arrivee > 2) )
{
return false;
}
int disque_depart =
this->tours[depart].Sommet();
return
this->tours[arrivee].Peut_Empiler(disque_depart);
}
void Deplacer_Disque(int depart,
int arrivee)
{
int disque = this->tours[depart].Sommet();
this->tours[depart].Depiler();
this->tours[arrivee].Empiler(disque);
}
bool Jeu_Termine() const
{
return this->tours[2].Est_Pleine();
}
private:
int hauteur_max;
std::vector<Tour> tours;
}; // class Hanoi

Le deuxime paramtre de
resize() est une valeur initiale
recopier dans chacun des
nouveaux lments du tableau.
Vous pouvez constater que
resize() prsente un aspect
tout fait analogue celui que
nous avons rencontr jusquici
lors de la dclaration dun
tableau : une taille suivie dune
valeur initiale.

Comparez maintenant lcriture des mthodes telles que Tour_Arrivee_Valide(),


avec celle que nous avons vue dans les fonctions ponymes du Chapitre 7. Vous
pouvez constater que le code donn ici est plus lisible : lutilisation des mthodes

Program Livre Page 122 Vendredi, 16. mai 2008 8:13 08

122

Initiation la programmation avec Python et C++

fournies par la classe Tour permet dobtenir une criture plus compacte et plus expressive. Cest lun des avantages "annexes" de la programmation oriente objet : lobtention de programmes plus lisibles car plus expressifs, donc plus aiss maintenir dans
le temps et faire voluer.
La mthode Une_Tour() est un peu particulire : elle donne accs lune des tours du
jeu en cours aux utilisateurs de la classe Hanoi. Remarquer en C++ lutilisation du
mot cl const : la rfrence sur une tour renvoye est ainsi qualifie pour garantir que
lutilisateur ne pourra pas modifier cette tour. En effet, normalement seule linstance
de la classe Hanoi doit pouvoir modifier les tours, seule faon dapporter une garantie
(relative) la robustesse et la cohrence de lensemble. Incidemment, cela signifie
qu partir de cette instance constante de la classe Tour obtenue, lutilisateur ne pourra
utiliser que des mthodes de Tour elles-mmes qualifies par const : les seules garantissant lintgrit des donnes de la tour.

9.3. Linterface du jeu


Rptons-le, un principe essentiel de la programmation consiste bien sparer linterface dun programme, cest--dire la partie charge dinteragir avec lutilisateur, du
moteur de calcul, cest--dire la partie contenant effectivement la mise en uvre des
rgles et contraintes du problme rsoudre. Limportance de cette sparation sera
pleinement dmontre dans quelques chapitres. Pour lheure, nous allons utiliser la
programmation par objet pour traduire cette distinction, en crant une classe
Hanoi_Texte charge prcisment de (presque) tous les aspects de laffichage du
programme. Cette classe est ici donne dans son intgralit, afin que vous puissiez
comparer son contenu avec celui des fonctions cres au Chapitre 7.
Python
class Hanoi_Texte:
def __init__(self, jeu):
self.jeu_hanoi = jeu
self.nb_mouvements = 0
def Jeu(self):
return self.jeu_hanoi
def Demander_Tour_Depart(self):
print "Tour de dpart?",
tour_depart = int(raw_input())
return tour_depart-1

Program Livre Page 123 Vendredi, 16. mai 2008 8:13 08

Chapitre 9

Python
def Demander_Tour_Arrivee(self):
print "Tour darrive?",
tour_arrivee = int(raw_input())
return tour_arrivee-1
def Afficher_Jeu(self):
for etage in \
range(self.Jeu().Hauteur(), -1, -1):
for tour in range(3):
print " " + \
self.Chaine_Etage(tour, etage),
print
def Nb_Mouvements(self):
return self.nb_mouvements;
def Etape(self):
tour_depart = self.Demander_Tour_Depart()
if self.Jeu().Tour_Depart_Valide (tour_depart):
tour_arrivee =
self.Demander_Tour_Arrivee()
if self.Jeu().Tour_Arrivee_Valid (tour_depart, \
tour_arrivee):
self.Jeu().Deplacer_Disque(tour_depart, \
tour_arrivee)
self.nb_mouvements += 1
else:
print "Arrive invalide!"
else:
print "Dpart invalide!"
def Chaine_Etage(self, tour, etage):
largeur_max = self.Jeu().Hauteur()
disque =
self.Jeu().Une_Tour(tour).Disque_Etage(etage)
espace = *(largeur_max-disque)
chaine = ""
if ( disque > 0 ):
ligne = =*disque
chaine = espace + "<" + ligne + "!" +
ligne + ">" + espace
else:
chaine = espace + "! " + espace
return chaine

Hano en objets

123

Program Livre Page 124 Vendredi, 16. mai 2008 8:13 08

124

Initiation la programmation avec Python et C++

C++
class Hanoi_Texte
{
public:
Hanoi_Texte(Hanoi& jeu):
jeu_hanoi(jeu),
nb_mouvements(0)
{
}
Hanoi& Jeu()
{
return this->jeu_hanoi;
}
const Hanoi& Jeu() const
{
return this->jeu_hanoi;
}
int Demander_Tour_Depart()
{
int tour_depart;
std::cout << "Tour de dpart? ";
std::cin >> tour_depart;
return tour_depart - 1;
}
int Demander_Tour_Arrivee()
{
int tour_arrivee;
std::cout << "Tour darrive? ";
std::cin >> tour_arrivee;
return tour_arrivee - 1;
}
void Afficher_Jeu() const
{
for(int etage = this->Jeu().Hauteur();
etage >= 0;
--etage)
{
for(int tour = 0; tour < 3; ++tour)
{
std::cout << " "
<< this->Chaine_Etage(tour, etage)
<< " ";
}
std::cout << std::endl;
}
}

Program Livre Page 125 Vendredi, 16. mai 2008 8:13 08

Chapitre 9

Hano en objets

125

C++
int Nb_Mouvements() const
{
return this->nb_mouvements;
}
void Etape()
{
int tour_depart = this->Demander_Tour_Depart();
if ( this->Jeu().Tour_Depart_Valide(tour_depart) )
{
int tour_arrivee = \
this->Demander_Tour_Arrivee();
if (
this->Jeu().Tour_Arrivee_Valide(tour_depart,
tour_arrivee) )
{
this->Jeu().Deplacer_Disque(tour_depart,
tour_arrivee);
this->nb_mouvements += 1;
}
else
{
std::cout << "Arrive invalide!\n";
}
}
else
{
std::cout << "Dpart invalide!\n";
}
}
std::string Chaine_Etage(int tour,
int etage)const
{
int largeur_max = this->Jeu().Hauteur();
int disque =
this->Jeu().Une_Tour(tour).Disque_Etage(etage);
std::string espace(largeur_max-disque, );
std::string chaine;
if ( disque > 0 )
{
std::string ligne(disque, =);
chaine = espace + "<"+ ligne + "!" +
ligne + ">" + espace;
}

Program Livre Page 126 Vendredi, 16. mai 2008 8:13 08

126

Initiation la programmation avec Python et C++

C++
else
{
chaine = espace + "! " + espace;
}
return chaine;
}
protected:
Hanoi& jeu_hanoi;
int nb_mouvements;
}; // Hanoi_Texte

Quelques remarques simposent. Tout dabord, le constructeur de cette classe


Hanoi_Texte attend un paramtre, qui doit tre une instance de la classe Hanoi
cest--dire, une instance du jeu afficher. Il serait en effet absurde de vouloir afficher
un jeu qui nexiste pas. Cela signifie que linstance du jeu devrait tre cre "en
dehors" de linstance de la classe dinterface. On gagne ainsi une certaine souplesse,
en ayant la possibilit de faire afficher un mme jeu par diffrentes interfaces (en
supposant que cela prsente un intrt quelconque).
Remarquez quen C++ cette instance du jeu est passe par une rfrence et stocke en
tant que rfrence : cela signifie que la donne jeu_hanoi permettra effectivement
daccder au jeu dclar par ailleurs, cette donne nest pas une autre instance du jeu.
Une telle rfrence est implicite en Python : dans ce langage, tout paramtre reprsentant une variable structure est pass par rfrence.
Enfin, voyez comment lutilisation dune rfrence sur la classe Tour dans la mthode
Chaine_Etage(), en C++, se limite lutilisation de la mthode Disque_Etage(),
elle-mme qualifie par const. Si vous retirez ce qualificatif dans la dclaration de
cette mthode dans Tour, vous obtiendrez une erreur la compilation, car la rfrence
retourne par Une_Tour() (de la classe Hanoi) est qualifie par const : seules les
mthodes constantes sont autorises sur une rfrence constante. Encore une fois, il
sagit l dune mesure de protection pour viter des erreurs de conception. Un tel
mcanisme est toutefois absent en Python.

9.4. Le programme principal


Maintenant que lessentiel des fonctionnalits du programme est organis en classes et mthodes, le programme principal se rsume finalement sa plus simple
expression :

Program Livre Page 127 Vendredi, 16. mai 2008 8:13 08

Chapitre 9

Hano en objets

127

Python
print "Nombre de disques?",
hauteur_max = int(raw_input())
le_jeu = Hanoi()
le_jeu.Initialiser(hauteur_max);
interface = Hanoi_Texte(le_jeu)
while (not le_jeu.Jeu_Termine()):
interface.Afficher_Jeu()
interface.Etape()
interface.Afficher_Jeu()
print "Russit en", \
interface.Nb_Mouvements(), \
"mouvements!"

C++
int main(int argc, char* argv[])
{
int hauteur_max;
std::cout << "Nombre de disques? ";
std::cin >> hauteur_max;
Hanoi le_jeu;
le_jeu.Initialiser(hauteur_max);
Hanoi_Texte interface(le_jeu);
while ( not le_jeu.Jeu_Termine() )
{
interface.Afficher_Jeu();
interface.Etape();
}
interface.Afficher_Jeu();
std::cout << "Russit en "
<< interface.Nb_Mouvements()
<< " mouvements!\n";
return 0;
}

Sa structure fondamentale est la mme que celui du Chapitre 7. Toutefois, lalgorithmique en est infiniment plus simple, tout tant assur par les diverses classes que nous
avons cres. Il en devient de fait beaucoup plus facile lire.

Program Livre Page 128 Vendredi, 16. mai 2008 8:13 08

Program Livre Page 129 Vendredi, 16. mai 2008 8:13 08

10
Mise en abme :
la rcursivit
Au sommaire de ce chapitre :

La solution du problme

Intgration au grand programme

Program Livre Page 130 Vendredi, 16. mai 2008 8:13 08

130

Initiation la programmation avec Python et C++

Non, nous nallons pas tudier ici un procd cinmatographique. Maintenant que
nous avons un programme permettant un utilisateur de jouer aux Tours de Hano, il
est temps de proposer une fonctionnalit essentielle pour un jeu de rflexion : afficher
la solution pour rsoudre le problme.

10.1. La solution du problme


Avant de nous lancer dans une programmation effrne, il vaut mieux se demander
comment nous pouvons rsoudre le problme, dj en tant qutres humains. Et si
possible, trouver une solution optimale, en un minimum de mouvements. Et sachant
galement que nous ne prtendrons pas ne serait-ce queffleurer les principes conduisant
lintelligence artificielle.
Une approche possible consisterait examiner systmatiquement tous les mouvements
possibles, afin de dterminer la succession la plus rapide pour parvenir au rsultat. Toutefois, nous nous heurterions rapidement ce quon appelle parfois lexplosion combinatoire : le nombre de possibilits est tellement vaste que mme un ordinateur
puissant ne pourrait les examiner toutes en un temps raisonnable.
Nous allons donc devoir nous reposer sur notre intuition, tre intelligents et transmettre
notre intelligence au programme. Posons-nous la question : comment moi, tre humain,
puis-je rsoudre ce problme ?
Pour fixer les ides, prenons par exemple quatre disques. Parmi eux, un est remarquable : le plus grand. Il ne peut tre pos sur aucun autre, il est donc le plus contraignant
dplacer. Une solution idale ne devrait dplacer ce disque quune seule fois.
Nommons T1, T2 et T3 les trois piquets, la pile de disques tant au dpart sur T1.
Idalement, donc, le plus grand disque sera dplac de T1 T3 en une seule fois.
Comme il ne peut tre pos sur aucun autre disque car il est le plus grand , cela ne
pourra se faire que si aucun disque nest prsent sur T3. Autrement dit, on ne pourra
effectuer ce dplacement particulier que si le grand disque est seul sur T1, et que tous
les autres disques sont sur T2. Incidemment, nous venons de rduire le problme :
pour pouvoir dplacer nos quatre disques de T1 vers T3, il faut dabord dplacer
(seulement) trois disques de T1 vers T2. Puis dplacer le grand sur T3. Puis redplacer
les trois disques de T2 vers T3. Ces trois tapes sont illustres la Figure 10.1.
Le grand disque prsente une autre particularit : il est le seul sur lequel on peut poser
nimporte lequel des autres disques. Donc, dans le cadre dun dplacement des trois
disques suprieurs, il na aucune incidence : tout se passe comme sil ntait pas l.
Cette remarque nous amne un constat intressant : on peut traiter le problme du
dplacement des trois disques, exactement de la mme manire que nous avons trait

Program Livre Page 131 Vendredi, 16. mai 2008 8:13 08

Chapitre 10

Mise en abme : la rcursivit

Figure 10.1
tapes pour une rduction
du problme quatre
disques.

T1

131

T3

T2

celui des quatre. La Figure 10.2, dans sa partie droite, montre comment la premire des
trois tapes peut elle-mme tre dcompose en trois "sous-tapes" : dplacer les deux
disques suprieurs, puis le disque infrieur, puis redplacer les deux disques suprieurs.
La troisime tape de la partie gauche pourrait tre dcompose de la mme faon.
T1

T3

T2

1.1

1
1.2
2
1.3

Figure 10.2
tapes pour une rduction du problme quatre disques.

Program Livre Page 132 Vendredi, 16. mai 2008 8:13 08

132

Initiation la programmation avec Python et C++

Gnralisons. Nous avons comme objectif de dplacer n disques dun piquet T1 vers
un piquet T3, par lintermdiaire dun piquet T2 (appel le pivot). La marche suivre
est alors :
1. Dplacer n1 disques de T1 vers T2.
2. Dplacer un disque (le dernier restant) de T1 vers T3.
3. Dplacer n1 disques de T2 vers T3.
Les tapes 1 et 3 peuvent elles-mmes se dcomposer selon le mme principe, sauf que les
rles des piquets sont intervertis. Par exemple, dans la premire, on va de T1 vers T2, donc
forcment par lintermdiaire de T3. Voici la marche suivre, donne sur deux niveaux :
1. Dplacer n1 disques de T1 vers T2 :
dplacer n2 disques de T1 vers T3,
dplacer un disque de T1 vers T2,
dplacer n2 disques de T3 vers T2.
2. Dplacer un disque de T1 vers T3.
3. Dplacer n1 disques de T2 vers T3 :
dplacer n2 disques de T2 vers T1,
dplacer un disque de T2 vers T3,
dplacer n2 disques de T1 vers T3.
Et ainsi de suite. Nous tenons l une ide de solution. Maintenant, voyons comment
toutes ces ides se traduisent en programmation.

10.2. Une fonction rcursive


Nous allons commencer par un petit programme tout simple, qui affiche la solution au
problme sans soccuper de lesthtique.
Lide de la fonction rcursive est la suivante : tant donn un problme de taille N, on
suppose savoir le rsoudre si la taille est 1, ainsi que disposer dune fonction permettant de le rsoudre la taille N1. La solution pour la taille N sappuie donc sur la
solution la taille N1. Les lecteurs ayant un penchant pour les mathmatiques auront
reconnu l une expression du principe de rcurrence.
Un exemple concret dans la vie courante se rencontre dans le cas dun emprunt
bancaire ou dun placement financier. Si vous placez la somme de 100 au taux de 5 %
par an, quel sera votre capital au bout de dix ans ? Exactement 5 % de plus que ce quil
tait au bout de neuf ans. Et au bout de neuf ans ? Exactement 5 % de plus que ce

Program Livre Page 133 Vendredi, 16. mai 2008 8:13 08

Chapitre 10

Mise en abme : la rcursivit

133

quil tait au bout de huit ans. Et ainsi de suite. Si on suppose disposer dune fonction
Capital() donnant le capital au bout de n annes (passes en paramtre) partir
dune mise initiale de x (passe en paramtre) en fonction dun taux t (pass en paramtre), une criture naturelle reprenant le principe prcdent serait :
Python

C++

def Capital(annees,
mise,
taux):
return (1.0 + (taux/100.0)) * \
Capital(annees-1, mise, taux)

double Capital(int annees,


double mise,
double taux)
{
return (1.0 + (taux/100.0)) *
Capital(annees-1, mise, taux);
}

La fonction Capital() sutilise elle-mme pour calculer son rsultat. crite ainsi, elle
prsente toutefois un inconvnient majeur : elle va sappeler elle-mme linfini, sans
jamais sarrter ou du moins, en ne sarrtant quau moment o le programme aura
puis la mmoire disponible, tout appel de fonction consommant une petite quantit
de mmoire.
Il faut ajouter un test, pour retourner le rsultat lorsquon peut le calculer directement.
Ici, lorsquon demande le capital au bout dune seule anne. Ce test est ce que lon
appelle parfois la condition darrt de la rcursivit. Voici un programme complet :
Python

C++

def Capital(annees, mise, taux):


if ( annees == 1 ):
return (1.0 + (taux/100.0)) * mise
else:
return (1.0 + (taux/100.0)) * \
Capital(annees-1, mise, taux)

#include <iostream>
double Capital(int annees,
double mise,
double taux)
{
if ( annees == 1 )
return (1.0 + (taux/100.0)) * mise;
else
return (1.0 + (taux/100.0)) *
Capital(annees-1, mise, taux);
}
int main(int argc, char* argv[])
{
std::cout << Capital(10, 100.0, 5.0)
<< std::endl;
return 0;
}

print Capital(10, 100.0, 5.0)

Program Livre Page 134 Vendredi, 16. mai 2008 8:13 08

134

Initiation la programmation avec Python et C++

Encore une fois, les mathmaticiens auront reconnu une exponentielle. Mais limportant est ici de comprendre le mcanisme en uvre : une fonction sutilisant elle-mme.
Essayez maintenant dcrire un programme minimaliste affichant les dplacements ncessaires pour rsoudre le problme des Tours de Hano. Cela pourrait ressembler ceci :
Python

C++

def Hanoi_Solution(nb_disques,
depart,
pivot,
arrivee):
if ( nb_disques == 1 ):
print depart, "->", arrivee
else:
Hanoi_Solution(nb_disques-1, \
depart, \
arrivee, \
pivot)
Hanoi_Solution(1, \
depart, \
pivot, \
arrivee)
Hanoi_Solution(nb_disques-1, \
pivot, \
depart, \
arrivee)

#include <iostream>
void Hanoi_Solution(int nb_disques,
int depart,
int pivot,
int arrivee)
{
if ( nb_disques == 1 )
std::cout << depart << " -> "
<< arrivee << std::endl;
else
{
Hanoi_Solution(nb_disques-1,
depart,
arrivee,
pivot);
Hanoi_Solution(1,
depart,
pivot,
arrivee);
Hanoi_Solution(nb_disques-1,
pivot,
depart,
arrivee);
}
}
int main(int argc, char* argv[])
{
Hanoi_Solution(4, 1, 2, 3);
return 0;
}

Hanoi_Solution(3, 1, 2, 3)

La fonction Hanoi_Solution() attend en paramtre le nombre de disques dplacer,


la tour de dpart, la tour darrive et la tour intermdiaire utilise comme pivot. Celleci pourrait tre dtermine automatiquement, mais cela alourdirait inutile notre exemple (toutefois, le lecteur est invit crire les quelques tests ncessaires). Voyez
comme on retrouve exactement le principe de solution esquiss la premire section
de ce chapitre.

Program Livre Page 135 Vendredi, 16. mai 2008 8:13 08

Chapitre 10

Mise en abme : la rcursivit

135

10.3. Intgration au grand programme


Voyons maintenant comme intgrer tout cela notre grand programme. Le calcul
dune solution au jeu semble bien faire partie de la partie "interne" du programme,
aussi serait-on tent dajouter une mthode effectuant ce calcul dans la classe
Hanoi. Cependant, ce calcul est indpendant du jeu : il na pas besoin des donnes
contenues dans la classe. De plus, se pose le problme de laffichage du rsultat : il
nest videmment pas question dafficher quoi que ce soit en dehors de la classe
Hanoi_Texte prvue cet effet.
Pour ces raisons, naturellement discutables, le choix a t fait de placer le calcul dune
solution dans une fonction totalement indpendante. De plus, le rsultat de ce calcul
sera stock au fur et mesure dans un paramtre supplmentaire, un tableau dans
lequel sera place la suite de mouvements dtermine.
Python
def Hanoi_Solution(nb_disques, \
depart, \
pivot, \
arrivee, \
soluce):
if ( nb_disques == 1 ):
soluce.append( (depart, arrivee) )
else:
Hanoi_Solution(nb_disques-1,depart, arrivee, pivot, soluce)
Hanoi_Solution(1, depart, pivot,arrivee, soluce)
Hanoi_Solution(nb_disques-1,pivot, depart, arrivee, soluce)

Le paramtre soluce est attendu comme un tableau. mesure quon avance dans le calcul des
dplacements, on ajoute la fin de ce tableau (append) une paire de valeurs, ce que lon
appelle un tuple en Python.
Un tuple est crit comme une liste de valeurs, spares par des virgules, encadre de parenthses. Les parenthses en apparence surnumraires dans linstruction mise en vidence ici ne
sont donc pas une erreur, elles permettent effectivement dcrire un tuple compos de deux
lments.

Program Livre Page 136 Vendredi, 16. mai 2008 8:13 08

136

Initiation la programmation avec Python et C++

C++

De mme quen Python, nous allons stocker des paires de valeurs dans un tableau.
void Hanoi_Solution(int nb_disques,
int depart,
int pivot,
int arrivee,
std::vector<std::pair <int, int> >& soluce)
{
if ( nb_disques == 1 )
soluce.push_back(std::make_pair(depart, arrivee));
else
{
Hanoi_Solution(nb_disques-1, depart, arrivee, pivot, soluce);
Hanoi_Solution(1, depart, pivot,arrivee, soluce);
Hanoi_Solution(nb_disques-1, pivot,depart, arrivee, soluce);
}
}

En C++, une paire de valeurs peut tre stocke dans une instance du type gnrique
std::pair<>, en donnant dans les chevrons les types de chacune des valeurs (ici des entiers).
Une instance dun tel type est obtenue par la fonction std::make_ pair(), qui prend en paramtres les deux valeurs stocker. Enfin, lajout dun lment la fin dun tableau est obtenu par
la mthode push_ back() du type gnrique std::vector<> que nous connaissons depuis
longtemps.

Le contenu de ce tableau est ensuite exploit par la classe Hanoi_Texte pour afficher
effectivement la solution. Cette fois, nous allons ajouter une mthode dans cette
classe, nomme Solution(), ddie cette tche. Sa premire action sera de rinitialiser le jeu (par la mthode Initialiser() de la classe Hanoi), afin de pouvoir simuler
un droulement normal.

Program Livre Page 137 Vendredi, 16. mai 2008 8:13 08

Chapitre 10

Mise en abme : la rcursivit

137

Python
def Solution(self):
print "-"*40
print "SOLUTION"
print "-"*40
soluce = []
self.Jeu().Initialiser (self.Jeu().Hauteur())
self.Jeu().Solution(soluce)
self.Afficher_Jeu()
print "-"*40
for mouvement in soluce:
self.Jeu().Deplacer_Disque(mouvement[0],
mouvement[1])
self.nb_mouvements += 1
self.Afficher_Jeu()
print "-"*40
void Solution()
{
std::string ligne(40, -);
std::cout << ligne << std::endl;
std::cout << "SOLUTION\n";

Remarquez la forme particulire de la boucle for utilise ici : la variable de boucle mouvement va automatiquement et successivement prendre la valeur de chacun des lments du
tableau soluce, sans quil soit ncessaire de passer par un intervalle (range).
chaque "tour de boucle", mouvement prendra donc la valeur de lun des tuples qui auront
t stocks dans le tableau soluce. Les lments dun tuple sont obtenus comme ceux dun
tableau, en donnant leur indice (le premier tant 0) entre crochets

C++
std::cout << ligne << std::endl;
std::vector<std::pair<int, int> >soluce;
this->Jeu().Solution(soluce);
this->Jeu().Initialiser(this->Jeu().Hauteur());
this->Afficher_Jeu();
std::cout << ligne << std::endl;

Program Livre Page 138 Vendredi, 16. mai 2008 8:13 08

138

Initiation la programmation avec Python et C++

C++
for(int ind_mouvement = 0;
ind_mouvement < soluce.size();
++ind_mouvement)
{
this->Jeu().Deplacer_Disque(soluce [ind_mouvement].first,
soluce[ind_mouvement].second);
this->nb_mouvements += 1;
this->Afficher_Jeu();
std::cout << ligne << std::endl;
}
}

En C++, les deux valeurs contenues dans une instance de std::pair<> peuvent tre rcupres par les membres first (pour la premire) et second (pour la seconde). Cest lun des
trs rares exemples o des donnes membres sont directement accessibles.

Enfin, une mthode Solution() est galement ajoute dans la classe Hanoi, dont le rle
se limite invoquer la fonction globale Hanoi_Solution() en lui donnant notamment
le nombre total de disques :
Python
def Solution(self, soluce):
Hanoi_Solution(self.hauteur_max, 0, 1, 2, soluce)

C++
void Solution(std::vector<std::pair<int,int> >& soluce) const
{
Hanoi_Solution(this->hauteur_max, 0, 1, 2, soluce);
}

Nous avons maintenant un programme complet, permettant de jouer au jeu des Tours
de Hano et capable dafficher la solution du problme.

Program Livre Page 139 Vendredi, 16. mai 2008 8:13 08

11
Spcificits propres
au C++
Au sommaire de ce chapitre :

Les pointeurs

Mmoire dynamique

Compilation spare

Fichiers de construction

Program Livre Page 140 Vendredi, 16. mai 2008 8:13 08

140

Initiation la programmation avec Python et C++

Le chapitre suivant abordera les techniques propres la programmation graphique,


afin de crer des programmes permettant lutilisation de la souris pour agir sur une
interface plus attrayante. Il est toutefois ncessaire de nous arrter un instant pour
examiner quelques aspects propres aux langages de programmation compils, en
gnral, et au langage C++, en particulier. Ces aspects auront en effet un rle majeur
dans ce qui va suivre.

11.1. Les pointeurs


Imaginez que vous soyez lincarnation vivante dun programme. Vous avez connaissance dau moins une autre personne, qui serait lincarnation dune information dans
lordinateur et aussi llue de votre cur. Pour joindre cette personne, vous ne disposez que du tlphone : celui-ci est comme une variable dans un programme, il vous
permet davoir accs une information.
Mais vous ne savez pas o se trouve physiquement cette autre personne. Vous pouvez
la consulter et lui transmettre des donnes, mais il est par exemple impossible de
demander un fleuriste de lui livrer un bouquet : vous ne connaissez pas son adresse.
Ce qui peut parfois tre bien malheureux. Or, toute personne possde une adresse, une
localisation, mme si elle nest pas fixe.
Il en va de mme dans le cas dune variable dans un programme. Le nom que vous
utilisez est comme un tlphone, qui vous permet daccder une donne sans savoir
quel emplacement elle se trouve en mmoire. Dans le cadre du langage Python, cette
information vous est compltement masque : elle est inaccessible (du moins par des
moyens simples).
En C++, on dsigne par le terme de pointeur une variable qui contient la position
dune autre variable en mmoire. Cette position est dsigne par le terme dadresse,
que lon peut comparer une adresse postale pour une personne, ou plutt une borne
kilomtrique le long dune route : en pratique, une adresse est un nombre entier, gnralement assez grand. Notez que la valeur mme de ce nombre est le plus souvent sans
aucun intrt pour le programmeur, sauf dans certaines situations trs particulires que
nous nous garderons dvoquer.
Les pointeurs en C++ sont des variables comme les autres, donc ayant un type. Si un
pointeur contient ladresse dune variable de type int, alors on dit quil est de type
"pointeur sur int". On le note int*, cest--dire le nom du type dont on veut connatre les adresses des variables, suivie dune toile (astrisque). On dit alors que le type
int* pointe sur des variables de type int.

Program Livre Page 141 Vendredi, 16. mai 2008 8:13 08

Chapitre 11

Spcificits propres au C++

141

Lorsque vous disposez dune variable, vous pouvez obtenir son adresse en faisant
prcder son nom par le caractre perluette :
int une_variable = 0;
int* pointeur = &une_variable;

Lutilisation de ce caractre nest pas sans rappeler la notion de rfrence, que nous
avons vue la fin du Chapitre 6. Ce nest pas un hasard : ces deux notions sont assez
proches, mais elles sutilisent dans des contextes diffrents.
Naturellement, si vous tentez de prendre ladresse dune variable dun certain type
pour la stocker dans un pointeur dun autre type, le compilateur vous insultera copieusement. Par exemple, lextrait suivant sera refus par un compilateur C++ :
int une_variable = 0;
double* pointeur = &une_variable;

L o cela devient vraiment subtil, cest quune telle affectation est possible dans le
cadre de la programmation objet : vous pouvez affecter une variable dun type pointeur ladresse dune variable dun type driv du type point. Voyez cet extrait de
code :
class Mere
{
};
class Derivee: public Mere
{
};
/* ... */
Derivee une_instance;
Mere* pointeur = &une_instance;

Cela fonctionne parfaitement. Cest mme une technique trs largement utilise pour
la mise en uvre du polymorphisme, que nous avons explor au chapitre prcdent.
Nous verrons trs bientt des exemples concrets dutilisation.
Prcisons ds maintenant que lutilisation des pointeurs est une source infinie de
dysfonctionnements dans un programme. Au cours de vos expriences, vous remarquerez quil est bien trop ais de mettre un dsordre infernal au point de vous retrouver, dune part, avec des pointeurs qui pointent sur "rien" ou sur une donne errone,
et dautre part, dans lincapacit de comprendre ce qui se passe, votre propre code
devenant un labyrinthe digne de Ddale.

Program Livre Page 142 Vendredi, 16. mai 2008 8:13 08

142

Initiation la programmation avec Python et C++

11.2. Mmoire dynamique


Vous pouvez alors vous demander quel est lintrt de toute cette histoire de pointeurs,
puisque cela semble tre un nid problmes. Ce qui nest pas faux.
Rappelons que le langage C++ tire son hritage du langage C, qui est un langage dit de
"bas niveau", cest--dire relativement proche de llectronique de lordinateur. La
majorit des systmes dexploitation, que cela soit Windows, Linux ou Mac OS, sont
crits principalement en langage C. Dans ces contextes particuliers, lutilisation des
pointeurs est une obligation pour avoir la matrise ncessaire de lorganisation des
lments en mmoire. Il serait ainsi impossible dcrire un systme dexploitation en
Python, ce langage occultant compltement la notion de pointeurs.
Plus communment, les pointeurs permettent deffectuer ce quon appelle une allocation
dynamique de mmoire. Lorsque vous crez une variable dans un programme ou une
fonction, elle reoit un emplacement particulier dans la mmoire de lordinateur pour y
contenir les donnes quelle reprsente : on dit que de la mmoire est alloue la variable.
Cette allocation est automatique, ds la dclaration de la variable (on parle dailleurs de
variables automatiques). Dans le cas dune variable dclare dans une fonction, nous
lavons vu, elle "disparat" ds que la fonction se termine : techniquement, cela se traduit
par la libration de la mmoire alloue, qui devient ds lors utilisable par une autre variable, voire la mme variable mais lors dun autre appel de la fonction.
Dans bien des situations, toutefois, il est souhaitable de matriser plus finement la
manire dont la mmoire est alloue et libre. Par exemple, si on ne sait pas
lavance (lors de lcriture du programme) de quelle quantit on a besoin ou si on
souhaite crer une variable dont la dure de vie sera suprieure celle de la fonction
dans laquelle elle a t cre. Voyez lextrait suivant :
int* pointeur = 0;
pointeur = new int;

On commence par dclarer une variable pointeur, donc destine contenir ladresse
dune zone de mmoire. ce moment-l, aucun espace nest effectivement rserv en
mmoire : le pointeur est dit invalide, sa valeur nayant pas de sens car arbitraire. Afin
de clarifier les choses, on lui attribue ds la dclaration la valeur 0 (zro), valeur
universelle reprsentant une adresse mmoire invalide.
La seconde ligne rserve effectivement un emplacement dans la mmoire de lordinateur, ici destin contenir un nombre entier, par le mot cl new (techniquement, il
sagit en fait dun oprateur, comme le sont +, /, etc.). Lcriture new int "produit"

Program Livre Page 143 Vendredi, 16. mai 2008 8:13 08

Chapitre 11

Spcificits propres au C++

143

une adresse mmoire, que nous mmorisons soigneusement dans la variable pralablement dclare : la variable pointeur contient ds lors ladresse dune zone mmoire
valide, destine recevoir la valeur dun nombre entier.
Mais ce qui nous intresse rellement, cest justement de placer une valeur dans cette
zone mmoire. partir dun pointeur, on peut accder linformation qui se trouve
derrire en le faisant prcder dune toile, par exemple :
*pointeur = 1;
int entier = 2 * *pointeur;

Remarquez que dans la seconde ligne, les espaces ont une certaine importance pour la
lisibilit, bien quils ne soient pas rellement ncessaires. Ces deux lignes sont parfaitement quivalentes la prcdente :
int entier = 2**pointeur;
int entier = 2*(*pointeur);

vous de trouver la notation qui vous semble la plus lisible.


Par lutilisation dune allocation dynamique laide de new, on obtient une zone
mmoire qui va perdurer au-del de la fonction dans laquelle elle a eu lieu, jusqu la
fin du programme. moins, naturellement, quon dcide de librer cette zone
mmoire explicitement :
delete pointeur;

Le mot cl delete (qui est galement un oprateur, en ralit) a pour effet de librer
une zone mmoire pralablement rserve par new, afin quelle puisse tre subsquemment utilise par dautres variables ou dautres allocations dynamiques. Il convient de
prendre garde deux points importants :

La valeur de la variable pointeur (non pas le contenu de la zone mmoire pointe)


nest en rien modifie par delete : vous navez aucun moyen immdiat de savoir si
une zone rserve a t libre ou non.

Le contenu de la zone mmoire pointe, par contre, peut ds cet instant tre modifi : vous navez aucune garantie que ce contenu a encore un sens ds que loprateur
delete a t appliqu.

Lorsque vous utilisez delete, une bonne habitude est de systmatiquement placer une
valeur invalide dans la variable pointeur :
delete pointeur;
pointer = 0;

Program Livre Page 144 Vendredi, 16. mai 2008 8:13 08

144

Initiation la programmation avec Python et C++

Ainsi, vous pourrez tester par la suite si un pointeur a t libr ou non, et donc savoir
si les donnes sur lesquelles il pointe sont cohrentes ou non.
Vous voyez quil y a dans ces mcanismes de nombreuses sources de soucis. Un
problme assez rpandu, et gnralement difficile corriger, est ce quon appelle la
fuite de mmoire (memory leak en anglais). Cela se produit lorsquun programme
rserve frquemment de la mmoire, avec new, mais nglige de librer les zones rserves lorsquelles ne sont plus utiles. Au fil du temps, le programme consomme de plus
en plus de mmoire alors quil nen a pas besoin. Jusquau moment o le systme
nest plus capable de fournir davantage de mmoire, ce qui rsulte au mieux en un
plantage du programme, au pire en un plantage du systme. Prenez donc toujours bien
soin de vrifier que toute mmoire que vous rservez est symtriquement libre ds
quelle nest plus utile.

11.3. Compilation spare


mesure que vos programmes deviendront plus complexes, vous vous trouverez
confront au problme de la taille des fichiers de code source : vous aurez de plus en
plus de difficult vous y retrouver dans un fichier contenant plusieurs milliers de
lignes. Il deviendra bientt ncessaire de "ventiler" le code source du programme dans
plusieurs fichiers, le mieux tant un fichier par type objet (classe).
Si un tel morcellement du code source est assez naturel et simple mettre en uvre en
Python, il en va tout autrement en C++. Pour un langage compil, la distribution du
code source en plusieurs fichiers ncessite en gnral deffectuer une compilation
spare suivie dune dition des liens.
La compilation spare consiste compiler chaque fichier de code source indpendamment du reste. Au sens strict, la compilation ne produit pas de fichier excutable :
le code source est transform en un fichier dit "objet", dans lequel les instructions
contenues dans le fichier source ont t pour la plupart transformes en codes binaires
comprhensibles par le processeur. Ce fichier nest toutefois pas excutable en tant
que tel, car il ne contient ni les instructions prsentes dans les autres fichiers sources,
ni les informations "administratives" ncessaires son excution par le systme
dexploitation.
lissue de cette compilation, les diffrents fichiers objet sont assembls en une seule
entit cohrente, chacun tant li lensemble. Cette phase est nomme dition des
liens, car cest ce moment que les relations entre chacun des lments sont effectivement
tablies et vrifies.

Program Livre Page 145 Vendredi, 16. mai 2008 8:13 08

Chapitre 11

Spcificits propres au C++

145

En ralit, ces deux tapes sont prsentes depuis notre premier programme. Jusquici,
elles ont t traites ensemble, car les exemples taient suffisamment simples. Nous
allons maintenant nous attaquer des activits plus sophistiques, aussi la connaissance de ce mcanisme est-elle ncessaire.
Reprenons la salutation, le programme qui demande le nom de lutilisateur avant de lafficher. On pourrait sparer ce programme pourtant trivial en quatre fichiers diffrents :

un fichier contenant une fonction pour demander le nom ;

un fichier contenant une fonction pour saluer un nom reu en paramtre ;

un fichier contenant la fonction principale main(), ncessaire tout programme


C++, qui utiliserait les deux fonctions prcdentes ;

un fichier den-tte qui raliserait la liaison entre les trois prcdents.

Un fichier den-tte, en langages C/C++, est un fichier dextension .h (ou .hpp,


parfois .hxx) ne contenant en gnral que des dclarations, cest--dire ne faisant
quannoncer quun certain objet existe. Dans notre cas, on pourrait crer un fichier
salutation.h qui contiendrait ceci :
#ifndef SALUTATION_H__
#define SALUTATION_H__ 1
#include <string>
void Demander_Nom(std::string& nom);
void Saluer(const std::string& nom);
#endif // SALUTATION_H__

Ce fichier ne contient que des dclarations de fonctions, pas de vritable code. Remarquez au dbut et la fin du fichier les lignes commenant par #ifndef, #define et
#endif : il sagit de directives de compilation, destines viter que le fichier soit
inclus plus dune fois au sein dune implmentation. La raison de cela est explicite un
peu plus loin.
Limplmentation de la fonction Demander_Nom() pourrait se situer dans un fichier
demander_nom.cpp ressemblant ceci :
#include <iostream>
#include "salutation.h"
void Demander_Nom(std::string& nom)
{
std::cout << "Quel est votre nom? ";
std::cin >> nom;
}

Program Livre Page 146 Vendredi, 16. mai 2008 8:13 08

146

Initiation la programmation avec Python et C++

Remarquez la deuxime ligne : on fait rfrence notre fichier den-tte. En fait,


tout se passe comme si le fichier salutation.h tait intgralement recopi lintrieur du fichier demander_nom.cpp. Du point de vue du compilateur, il devient
alors ceci :
#include <iostream>
#ifndef SALUTATION_H__
#define SALUTATION_H__ 1
#include <string>
void Demander_Nom(std::string&
void Saluer(const std::string&
#endif // SALUTATION_H__
void Demander_Nom(std::string&
{
std::cout << "Quel est votre
std::cin >> nom;
}

nom);
nom);
nom)
nom? ";

Cest l le vritable sens de cette directive #include que nous utilisons depuis le
dbut : elle signifie, littralement, "recopier cet endroit le contenu du fichier
auquel il est fait rfrence". Vous laurez compris, ce qui est vrai pour notre fichier
den-tte salutation.h lest galement pour les fichiers iostream et string : ce
sont bel et bien, eux aussi, des fichiers den-tte, situs un emplacement prdfini
par le compilateur lui-mme. Les deux lignes #include restantes seront donc
remplaces par les contenus de ces fichiers. Et ainsi de suite jusqu ce quil nen
reste plus une seule.
Cette opration consistant remplacer les fichiers mentionns dans des directives
#include par leur contenu est ce que lon appelle ltape de prtraitement (preprocessing en anglais). Ce nest quaprs cette tape que la compilation va effectivement pouvoir avoir lieu. Les directives #ifndef, #define et #endif sont galement
interprtes lors de cette tape.
Ces trois directives, justement, permettent dviter une situation qui pourrait tre
dlicate : celle o un fichier den-tte est inclus (par #include) dans un autre, luimme inclus dans un programme avec le premier. On pourrait ainsi aboutir une
situation o le contenu dun mme fichier den-tte est inclus plusieurs fois avant la
compilation. Si dans certains cas, ce nest gure gnant, dans la plupart des situations cela peut aboutir des incohrences subtiles. Le fait "dencadrer" le contenu
dun fichier den-tte laide des directives #ifndef, #define et #endif, permet

Program Livre Page 147 Vendredi, 16. mai 2008 8:13 08

Chapitre 11

Spcificits propres au C++

147

de dtecter si un contenu a dj t inclus ou non et donc de ne pas linclure une


seconde fois. Cela est fait simplement en dfinissant une sorte didentifiant du
fichier, par convention son nom en majuscules suivi de deux caractres de soulignement,
comme cest fait ici.
Si vous tes curieux, vous pouvez consulter le rsultat intermdiaire de cette tape, qui
normalement ne vous est jamais prsent :
> g++ -E demander_nom.cpp -o demander_nom.e

Le rsultat est stock dans le fichier demander_nom.e, lequel est, vous le constatez,
considrablement plus volumineux que votre fichier de dpart. Vous avez en fait sous
les yeux ce qui sera effectivement compil.
La compilation, justement, est effectue en passant une option spciale au compilateur :
> g++ -c demander_nom.cpp -o demander_nom.o

Loption -c indique que lon souhaite effectuer seulement la compilation (et le prtraitement, naturellement) et rien dautre. Le nom du fichier objet rsultant utilise lextension
.o, par convention (parfois .obj si on utilise dautres compilateurs).
Nous crons et compilons de la mme manire limplmentation de la fonction
Saluer(), dans saluer.cpp :
#include <iostream>
#include "salutation.h"
void Saluer(const std::string& nom)
{
std::cout << "Bonjour, "
<< nom << std::endl;
}
> g++ -c saluer.cpp -o saluer.o

Et enfin la fonction principale, dans salutation.cpp :


#include "salutation.h"
int main(int argc, char* argv[])
{
std::string nom;
Demander_Nom(nom);

Program Livre Page 148 Vendredi, 16. mai 2008 8:13 08

148

Initiation la programmation avec Python et C++

Saluer(nom);
return 0;
}
> g++ -o salutation.cpp -o salutation.o

Cette fonction utilise les deux fonctions Demander_Nom() et Saluer() : cela nest
possible que parce quelles ont t dclares (annonces) dans le fichier den-tte
salutation.h, lequel est justement inclus ici.
Nous disposons donc maintenant de trois fichiers objet. Pour obtenir enfin un fichier
excutable, nous pouvons effectuer ldition des liens ainsi :
> g++ demander_nom.o saluer.o salutation.o -o salutation

Finalement, les tapes ncessaires la construction dun fichier excutable peuvent


tre schmatises comme la Figure 11.1.
source
demander_nom.cpp

source
saluer.cpp

source
salutation.cpp

prtraitement
g++ E
(le rsulat nest
conserv quen mmoire)

prtraitement
g++ E
(le rsulat nest
conserv quen mmoire)

prtraitement
g++ E
(le rsulat nest
conserv quen mmoire)

compilation
g++ c

compilation
g++ c

compilation
g++ c

fichier objet
demander_nom.o

fichier objet
saluer.o

fichier objet
salutation.o

dition des liens


g++

fichier excutable
salutation (.exe)

Figure 11.1
tapes pour la construction dun fichier excutable.

Program Livre Page 149 Vendredi, 16. mai 2008 8:13 08

Chapitre 11

Spcificits propres au C++

149

Il ne devrait chapper personne quil sagit l dun processus relativement laborieux,


pouvant tre entach derreurs chacune des tapes. Et encore ne sagit-il que dun
exemple extrmement modeste. Heureusement, des solutions existent pour nous
simplifier la vie, que nous allons examiner ds maintenant.

11.4. Fichiers de construction


Nous avons vu quun programme pouvait tre dcompos en plusieurs fichiers. Cest
mme presque une rgle dans le cas de logiciels vritablement complexes : le code
source de votre traitement de texte favori se rpartit en plusieurs milliers de fichiers !
Compiler chacun deux " la main", comme nous lavons fait prcdemment, nest
tout simplement pas envisageable.
Dans ces cas-l, on fait appel ce que lon appelle un programme de construction
(aussi appel moteur de production). Il sagit dun logiciel spcialement ddi la
tche consistant produire des logiciels. La structure du programme quon
souhaite construire est dcrite dans un fichier de construction, ainsi que les ventuelles rgles particulires devant tre appliques. partir de ce fichier de construction, le moteur de production va prendre en charge automatiquement la compilation
des diffrents fichiers source puis ldition des liens finale pour obtenir un logiciel
excutable.
Le plus rpandu des moteurs de production est aujourdhui lutilitaire GNU make. Il
est normalement presque toujours disponible sur tous les systmes de type Unix (ce
qui inclut GNU/Linux et Mac OS). Une version accompagne lensemble MinGW sous
Windows. Par convention, le fichier de construction quutilisera lutilitaire make se
nomme simplement Makefile (sans extension).
Si on reprend lexemple de la section prcdente, nous pourrions dcrire ainsi notre
programme de salutation dans un fichier de construction nomm Makefile :
CXX = g++
TARGET = salutation
OBJECTS = demander_nom.o saluer.o salutation.o
%.o: %.cpp
$(CXX) -c $^ -o $@
all: $(OBJECTS)
$(CXX) -o $(TARGET) $(OBJECTS)

Program Livre Page 150 Vendredi, 16. mai 2008 8:13 08

150

Initiation la programmation avec Python et C++

Nous nallons pas rentrer dans le dtail des rgles dcriture des fichiers de construction, qui peuvent tre assez complexes. Lexemple prcdent donne toutefois une ide
gnrale. Pour commencer, les flches que vous voyez reprsentent un caractre de
tabulation : cest absolument obligatoire, impossible dutiliser simplement des espaces. Concernant le contenu, on commence par renseigner quelques variables usuelles
(CXX pour le compilateur utiliser, TARGET pour le rsultat final que lon souhaite
obtenir, OBJECTS pour la liste des fichiers intermdiaires), puis on dfinit quelques
rgles sous la forme :
cible: dpendances
commande excuter

Par "dpendances" on entend ici des fichiers partir desquels la cible va tre gnre,
en excutant la commande indique. La cible nomme all est un peu particulire :
cest la cible par dfaut si aucune nest prcise lors de lutilisation de make.
Si vous placez ce fichier Makefile dans le mme rpertoire que celui contenant les
fichiers de notre programme de salutation, lutilisation de make est facile. Il suffit
dexcuter la commande :
$ make

Ou si vous tes sous Windows :


> mingw32-make

Vous devriez alors obtenir quelque chose comme ceci :


g++ -c demander_nom.cpp -o demander_nom.o
g++ -c saluer.cpp -o saluer.o
g++ -c salutation.cpp -o salutation.o
g++ -o salutation demander_nom.o saluer.o salutation.o

Vous pouvez constater que chacun des fichiers est compil indpendamment, puis
lexcutable final est cr par ldition des liens. On retrouve des commandes analogues celles que nous avions utilises la section prcdente. Faites lexprience
suivante : relancer immdiatement la commande make.
Normalement, presque rien ne devrait se passer. Cest l un autre intrt dutiliser un
outil de construction : seuls les fichiers devant tre recompils le sont effectivement.
Modifiez lun des fichiers source, simplement en ajoutant un espace ou une ligne vide,
puis relancez make. Seul le fichier modifi sera recompil.

Program Livre Page 151 Vendredi, 16. mai 2008 8:13 08

Chapitre 11

Spcificits propres au C++

151

Nous verrons bientt que nous naurons mme pas crire nous-mme de fichier de
construction. En effet, ils peuvent tre assez dlicats mettre au point. Ne vous laissez
pas tromper par lexemple donn ici, il est dune simplicit caricaturale (et, en fait, pas
tout fait correct, par souci de comprhension). Des outils existent pour crer ces
fichiers de construction, partir dautres fichiers encore plus simples.

Program Livre Page 152 Vendredi, 16. mai 2008 8:13 08

Program Livre Page 153 Vendredi, 16. mai 2008 8:13 08

12
Ouverture des fentres :
programmation graphique
Au sommaire de ce chapitre :

La bibliothque Qt

Installation des bibliothques Qt et PyQt

Le retour du bonjour

Un widget personnalis et boutonn

Hano graphique

Program Livre Page 154 Vendredi, 16. mai 2008 8:13 08

154

Initiation la programmation avec Python et C++

Tous nos programmes taient jusqu maintenant des programmes en mode texte,
cest--dire quils ne font quafficher et recevoir des caractres, rien dautre. Ce qui
na rien dinfamant. Linterface graphique, qui nous semble aujourdhui si commune,
na t imagine dans les laboratoires de lentreprise Xerox quaux alentours de 1973,
avant dtre largement dveloppe et diffuse par les entreprises Apple (ordinateurs
Macintosh, 1984), Sun (stations de travail graphiques, 1983), SGI (affichage 3D,
1984) et Microsoft (environnement Windows 1.0 en 1985 et 3.0 en 1990).
Aujourdhui, mme les systmes les plus rcents que sont Mac OS X, Windows Vista
ou les nombreux environnements graphiques disponibles pour systmes Linux (et
Unix), sinscrivent dans la ligne des concepts gs de plus de trente-cinq ans. Tout au
long de cette priode, de trs nombreux programmes de premier ordre, comme les
applications comptables, sont rests longtemps cantonns un affichage base de
caractres.
Mais aujourdhui, il parat naturel pour la majorit des utilisateurs de disposer du
confort dune interface graphique, manipulable laide dune souris. Il ny a dailleurs
gure dautre solution lorsque la nature mme des informations quon souhai te
afficher est fondamentalement graphique.
Aussi allons-nous dsormais dcouvrir quelques-unes des possibilits qui soffrent
nous. Vous pourrez constater que, si les programmes gagnent alors en convivialit et
attractivit, ils gagnent aussi en complexit. Il sagit l dune rgle qui semble saffirmer : plus un logiciel est agrable et ais dutilisation, plus il est complexe dans sa
conception et son criture.

12.1. La bibliothque Qt
Un langage de programmation, en lui-mme, noffre gnralement pas de possibilits
pour raliser un programme graphique ( quelques rares exceptions prs). Le
graphisme nest atteint le plus souvent quen utilisant des bibliothques doutils,
adaptes un langage donn et un environnement graphique donn. En effet, la
manire de programmer graphiquement diffre assez considrablement selon que
lon se trouve sous un systme Linux, Windows ou Mac OS, chacun deux pouvant
ventuellement proposer diffrents contextes (X-Window ou framebuffer sur Linux,
par exemple).
Aussi, allons-nous devoir utiliser une bibliothque spcialise. Le choix a t fait de la
bibliothque Qt (prononcez "kiouti"), cre et dveloppe depuis de nombreuses

Program Livre Page 155 Vendredi, 16. mai 2008 8:13 08

Chapitre 12

Ouverture des fentres : programmation graphique

155

annes par la socit norvgienne TrollTech (http://www.trolltech.com, rcemment


passe sous le contrle de Nokia). Plusieurs raisons ont guid ce choix :

Il sagit dune bibliothque en langage C++ disponible sur tous les systmes
dexploitation majeurs.

Elle propose une myriade doutils divers et puissants, tout en conservant une
certaine cohrence et une logique interne, qui facilite son apprentissage.

La documentation qui laccompagne est dune grande qualit (quoique uniquement


en anglais), aspect essentiel pour pouvoir raliser un projet sans passer des nuits
chercher une information.

Elle est employe quotidiennement par des millions dutilisateurs, ne serait-ce


quau travers de lenvironnement graphique KDE couramment utilis sur systmes
Linux, dont elle constitue les fondations.

Une version libre (au sens du logiciel libre) est disponible gratuitement, de mme
que plusieurs adaptations dans dautres langages que le C++. Il est ainsi possible
de lutiliser en langage Python, au travers de ladaptation PyQt, elle-mme de
grande qualit.

Au-del de toutes ces bonnes raisons, ne perdez pas de vue que le choix dune bibliothque, comme celui dun langage de programmation, obit galement des motivations subjectives. Mme si cela peut vous paratre encore un peu abstrait, vous
constaterez que lesthtique dune bibliothque a galement son importance.
Gardez lesprit que cet ouvrage nest en aucun cas un manuel de la bibliothque Qt,
dont la documentation complte reprsente plusieurs fois le volume que vous tenez
entre les mains. lissue de linstallation des bibliothques, vous trouverez cette
documentation dans un rpertoire nomm doc lendroit o Qt a t installe. Nous ne
dcrirons pas tous les outils utiliss dans le moindre dtail, aussi nhsitez pas aller
consulter cette documentation. La lecture est une part non ngligeable de la programmation !

12.2. Installation des bibliothques Qt et PyQt


Au moment o ces lignes sont crites, la version libre 4.3.3 est la dernire de Qt. Vous
trouverez son code source sur le site FTP ftp://ftp.trolltech.com/qt/source,
pour toutes les plates-formes.

Program Livre Page 156 Vendredi, 16. mai 2008 8:13 08

156

Initiation la programmation avec Python et C++

La bibliothque PyQt, ladaptation en Python de Qt, ncessite linstallation dun petit


utilitaire pour pouvoir tre compile. Nomm SIP, vous le trouverez partir de la page
http://www.riverbankcomputing.co.uk/sip/download.php. PyQt est, quant
elle, disponible partir de la page http://www.riverbankcomputing.co.uk/pyqt/
download.php.

12.2.1. Windows
Tlchargez le fichier qt-win-opensource-src-4.3.3.zip partir du site FTP indiqu prcdemment, puis dcompressez son contenu dans le rpertoire C:\Programmes.
Renommez le rpertoire nouvellement cr en qt433, afin dconomiser quelques
touches de clavier par la suite.
partir dune ligne de commande, placez-vous dans ce rpertoire, puis excutez la
commande suivante :
C:\Programmes\qt433> configure -platform win32-g++ -release -qt-sql-sqlite
-qt-zlib -qt-libpng -qt-libmng -qt-libtiff -qt-libjpeg -no-dsp -no-vcproj
-no-qt3support -no-openssl -no-qdbus

Cela va paramtrer Qt pour votre systme et prparer sa compilation, que vous lancerez
laide de la commande :
C:\Programmes\qt433> mingw32-make

Puis, allez prparer quelques cafs. En plus de la bibliothque elle-mme, dune taille
assez considrable, de nombreux outils annexes et des exemples seront galement
compils. Le processus complet peut demander plusieurs heures, selon la puissance de
votre machine.
Cela fait, modifiez comme suit le fichier de commande prog.bat que nous avions cr au
premier chapitre, qui permet dobtenir un environnement de dveloppement adquat
(attention, la ligne commenant par set PATH est bien une seule et unique ligne !) :
set QTDIR=C:\Programmes\qt433
set QMAKESPEC=win32-g++
set
PATH=%QTDIR%\bin;C:\Python25;C:\MinGW\bin;C:\MinGW\libexec\gcc\mingw32\3.4.5;%PATH%
C:
cd \Programmes
cmd.exe

Fermez la ligne de commande puis relancez-la, afin de bnficier du nouvel environnement. Essayez dexcuter la commande qtdemo afin de voir quelques dmonstrations
et exemples de Qt.

Program Livre Page 157 Vendredi, 16. mai 2008 8:13 08

Chapitre 12

Ouverture des fentres : programmation graphique

157

Rcuprez maintenant le fichier sip-4.7.4.zip partir de la page http://


www.riverbankcomputing.co.uk/sip/download.php. Dcompactez ce fichier,
nommez le rpertoire en sip474, partir duquel vous excutez les commandes :
C:\Programmes\sip474> python configure.py -p win32-g++
C:\Programmes\sip474> mingw32-make
C:\Programmes\sip474> mingw32-make install

Nous pouvons maintenant installer PyQt. Rcuprez le fichier PyQt-win-gpl4.3.3.zip sur la page http://www.riverbankcomputing.co.uk/pyqt/download.php. Comme prcdemment, dcompactez-le dans C:\Programmes, puis nommez
le rpertoire nouveau pyqt433. partir de ce rpertoire, excutez simplement :
C:\Programmes\pyqt433> python configure.py
C:\Programmes\pyqt433> mingw32-make
C:\Programmes\pyqt433> mingw32-make install

Vous tes maintenant prt dvelopper des logiciels graphiques aussi bien en C++
quen Python.

12.2.2. Linux
Si vous utilisez une distribution majeure rcente (datant de lanne 2006), il est certain
que les bibliothques Qt et PyQt sont disponibles parmi les paquetages quelle
propose. En particulier, cherchez les paquetages dont les noms ressemblent ceux-ci :

pour Qt, libqt4-dev, libqt4-core, libqt4-gui ;

pour PyQt, python-qt4, pyqt4-dev-tools, python-qt4-gl, python-qt4-doc.

Installez tous ces paquetages, ainsi que ceux desquels ils dpendent.

12.2.3. Mac OS X
Tlchargez le fichier qt-mac-opensource-src-4.3.3.tar.gz partir du site FTP
de TrollTech, puis placez-le dans votre rpertoire personnel. partir de la ligne de
commande, dcompressez-le puis configurez Qt comme suit :
$ tar zxf qt-mac-opensource-src-4.3.3.tar.gz
$ cd qt-mac-opensource-src-4.3.3
$ ./configure -prefix $HOME/opt/qt433 -qt-sql-sqlite -no-qt3support -qt-zlib
-qt-libtiff -qt-libmng -qt-libjpeg -no-framework -no-dwarf2

Program Livre Page 158 Vendredi, 16. mai 2008 8:13 08

158

Initiation la programmation avec Python et C++

Enfin, lancez la compilation de Qt, non sans avoir prvu quelques cafs ou un divertissement quelconque, car cette opration peut prendre un certain temps :
$ make -j2 && make install

Il est ensuite ncessaire de modifier votre environnement, afin que la bibliothque Qt


soit dment reconnue et localise. Crez pour cela un fichier nomm qt_env.sh dans
votre rpertoire Programmes, contenant les lignes suivantes :
$ export QTDIR=$HOME/opt/qt433
$ export LD_LIBRARY_PATH=$QTDIR/lib:$LD_LIBRARY_PATH
$ export PATH=$QTDIR/bin:$PATH

Dsormais, chaque fois que vous lancerez une ligne de commande, il vous suffira
dinvoquer ce fichier afin de mettre jour lenvironnement :
$ source Programmes/qt_env.sh

Cela fait, vous pouvez vrifier que votre installation fonctionne correctement en
excutant le programme de dmonstration de Qt :
$ qtdemo

Tlchargez maintenant le fichier sip-4.7.4.tar.gz sur le site http://www.riverbankcomputing.co.uk/sip/download.php. Dcompactez-le puis, partir du rpertoire nouvellement cr, excutez les commandes :
$ python configure.py
$ make
$ make install

Il ne nous reste maintenant plus qu installer PyQt. Rcuprez le fichier PyQt-macgpl-4.3.3.zip sur la page http://www.riverbankcomputing.co.uk/pyqt/
download.php. Comme prcdemment, dcompactez-le puis, partir du rpertoire
cr, excutez simplement :
$ python configure.py
$ make
$ make install

Vous tes maintenant prt dvelopper des logiciels graphiques aussi bien en C++
quen Python.

Program Livre Page 159 Vendredi, 16. mai 2008 8:13 08

Chapitre 12

Ouverture des fentres : programmation graphique

159

12.3. Le retour du bonjour


Il est toujours prfrable de commencer la dcouverte dune nouvelle technique ou
dun nouvel outil par des ralisations simples. Nous avons entam notre initiation la
programmation en saluant le monde, faisons de mme pour dcouvrir la programmation
graphique !
Voici le programme.
Python

C++

import sys
from PyQt4 import QtCore, QtGui
appli = QtGui.QApplication(sys.argv)
bijour = QtGui.QLabel("Bonjour, Monde!")
bijour.show()
sys.exit(appli.exec_())

#include <Qapplication>
#include <Qlabel>
int main(int argc, char* argv[])
{
QApplication appli(argc, argv);
QLabel bijour("Bonjour, Monde!");
bijour.show();
return appli.exec();
}

Vous pouvez constater que, si ces programmes demeurent extrmement courts, ils sont
nanmoins beaucoup plus longs que leurs quivalents purement textuels. Examinons
un instant leur contenu.
Pour commencer, tout programme graphique sappuyant sur Qt doit contenir une
instance et une seule de la classe QApplication (ici la variable appli). Celle-ci reprsente en quelque sorte le cadre gnral dans lequel le programme va sexcuter.
Elle maintient galement la boucle dvnements dont il a dj t question.
Du point de vue de Python, cette classe fait partie du module QtGui. Le terme module
pour Python est plus ou moins lquivalent du terme bibliothque : un regroupement
en une seule entit de fonctions et de classes utilisables par un autre programme .
La premire ligne du programme prcdent, import sys, signale que nous allons
utiliser le contenu du module nomm sys. La deuxime rend disponibles les contenus
des modules QtCore et QtGui, qui sont, en fait, des sous-modules du module gnral
PyQt4.
Pour le C++, il est ncessaire dajouter la ligne #include <QApplication> pour
pouvoir lutiliser. Notez bien quil est ncessaire et indispensable de crer une instance
de QApplication avant dutiliser nimporte laquelle des possibilits graphiques de

Program Livre Page 160 Vendredi, 16. mai 2008 8:13 08

160

Initiation la programmation avec Python et C++

Qt : si vous ne le faites pas, votre programme plantera irrmdiablement avant mme


dafficher la moindre fentre.
Ensuite, nous crons un label (variable bijour), cest--dire un lment graphique
dont le seul objet est dafficher un texte, sur lequel lutilisateur na pas de possibilit
daction directe. Tout lment textuel dune interface graphique doit ainsi tre contenu
dans un objet graphique, dune manire ou dune autre. Ce qui tranche radicalement
avec les programmes que nous avons raliss jusquici, o les chanes de caractres
taient simplement affiches. Ici, ce nest pas "vous" qui affichez, cest lobjet contenant
votre chane de caractres.
On demande ensuite cet lment graphique, que lon appelle communment un
widget (contraction des mots window gadget, en franais gadget de fentre) ou un
contrle, de justement safficher. leur cration, les widgets sont normalement
masqus, il faut donc demander explicitement leur apparition lcran : cest le rle
de la mthode show() (montrer en anglais).
Enfin, on dmarre la fameuse boucle dvnements au moyen de la mthode
exec() de la classe QApplication. Cest elle qui va recevoir tous les vnements
et les transmettre aux lments appropris du programme. Par exemple, si une fentre vient occulter celle de votre programme, celui-ci recevra un vnement linformant quil est masqu. Sil redevient visible, un nouvel vnement sera transmis,
lequel sera dirig vers les diffrents composants pour leur demander de se redessiner
lcran.
Nous en avons presque termin avec notre premier programme graphique. La version
en Python doit normalement sexcuter directement, simplement en invoquant la
commande :
$ python bonjour_gui.py

Par contre, la version C++ doit tre compile, ce qui peut devenir rapidement assez
complexe pour un programme graphique utilisant une bibliothque aussi sophistique
que Qt (cela est vrai pour la plupart des bibliothques graphiques). Juste pour satisfaire votre curiosit, voici quoi pourrait ressembler la compilation de ce programme
tout simple (exemple sur systme Windows) :
> g++ -c -O2 -frtti -fexceptions -mthreads -Wall -DUNICODE DQT_LARGEFILE_SUPPORT -DQT_DLL -DQT_NO_DEBUG -DQT_GUI_LIB -DQT_CORE_LIB DQT_THREAD_SUPPORT -DQT_NEEDS_QMAIN -I"qt433\include\QtCore" I"qt433\include\QtCore" -I"qt433\include\QtGui" -I"qt433\include\QtGui" -

Program Livre Page 161 Vendredi, 16. mai 2008 8:13 08

Chapitre 12

Ouverture des fentres : programmation graphique

161

I"qt433\include" -I"c:\Programmes\qt433\include\ActiveQt" -I".moc" -I"." I"qt433\mkspecs\win32-g++" -o .objs\bonjour_gui.o bonjour_gui.cpp


> g++ -enable-stdcall-fixup -Wl,-enable-auto-import -Wl,-enable-runtime-pseudoreloc -Wl,-s -mthreads -Wl -Wl,-subsystem,windows -o bin\bonjour_gui.exe .objs/
bonjour_gui.o -L"c:\Programmes\qt433\lib" -lmingw32 -lqtmain -lQtGui4 -lQtCore4

Non seulement cette compilation se fait en deux tapes, mais en plus les lignes de
commande excuter sont particulirement charges. Il est tout simplement hors de
question de saisir des commandes aussi complexes chaque compilation. La solution
consiste utiliser un fichier de construction, dans le genre de ceux voqus au chapitre
prcdent.
Par chance, Qt fournit un moyen dobtenir aisment ce fichier partir de quelques
informations beaucoup plus simples contenues dans un fichier, dit fichier projet,
dextension .pro. Voici le contenu dun tel fichier, que nous nommerons ici
bonjour_gui.pro, que vous pouvez saisir avec votre diteur de texte favori :
TEMPLATE = app
QT += gui
CONFIG += release
OBJECTS_DIR = .objs
MOC_DIR = .moc
DESTDIR = bin
SOURCES += bonjour_gui.cpp

Cela pourrait presque ressembler un programme Python dans lequel on ne ferait


quaffecter des valeurs dans des variables. La premire, TEMPLATE, indique de quel
type de projet il sagit. Ici, nous voulons une application, donc on donne la valeur app.
Dautres valeurs sont possibles, comme lib pour crer une bibliothque. Les deux
variables suivantes, QT et CONFIG, prcisent quelques paramtres pour la compilation.
Ici, on demande simplement utiliser les outils graphiques de Qt (gui) et obtenir
une version optimise (release). Notez lutilisation du symbole +=, qui a pour effet
dajouter des valeurs ces variables, comme si on effectuait une concatnation de
chanes de caractres (ce qui est en ralit le cas, mais pour lheure vous navez pas
vous en soucier). Les trois variables suivantes concernent lorganisation des rpertoires, notamment le stockage des fichiers intermdiaires utiliss lors de la phase de
compilation : ces fichiers sont crs par le compilateur lui-mme pour ses besoins
propres. On demande ce quils soient rangs dans deux sous-rpertoires (par
MOC_DIR et OBJECTS_DIR), afin de ne pas encombrer le rpertoire contenant les

Program Livre Page 162 Vendredi, 16. mai 2008 8:13 08

162

Initiation la programmation avec Python et C++

fichiers source. DESTDIR indique le rpertoire de destination final, cest--dire celui


qui contiendra effectivement le fichier excutable rsultant de la compilation.
Enfin, la variable la plus importante est SOURCES : placez dans celle-ci tous les fichiers
de code source constituant votre programme. Une solution simple la compilation
spare voque au chapitre prcdent.
Une fois ce fichier cr, on fait appel lutilitaire qmake fourni par Qt :
$ qmake bonjour_gui.pro

Le rsultat est un fichier de construction (nomm normalement Makefile), que nous


pouvons utiliser immdiatement par la commande :
$ make

ou si vous tes sous systme Windows :


> mingw32-make

lissue de quoi, vous devriez obtenir un fichier excutable nomm bonjour_gui


(avec lextension .exe sur Windows) dans un sous-rpertoire nomm bin, comme
nous lavons demand dans le fichier projet. Le nom de ce fichier excutable est dduit
du nom du fichier projet, moins que vous ne layez prcis en affectant une valeur
la variable TARGET. Mais inutile de compliquer les choses.
Figure 12.1
Premier programme
graphique.

La Figure 12.1 montre quoi devrait ressembler ce premier programme graphique sur
systme Windows. Si vous tes un peu dpit par son aspect lmentaire, considrez le
chemin parcouru depuis le dbut de cet ouvrage

12.4. Un widget personnalis et boutonn


Naturellement, dans limmense majorit des cas, votre programme ne saurait se
contenter dun simple label affichant un texte statique. Il devra offrir un minimum
dinteractions lutilisateur et adapter son affichage. Reprenons lexemple de la salutation. Nous nous proposons maintenant de raliser un programme peine moins
minimaliste (voir Figure 12.2). Deux boutons sont offerts. Le premier affiche au

Program Livre Page 163 Vendredi, 16. mai 2008 8:13 08

Chapitre 12

Ouverture des fentres : programmation graphique

163

dpart simplement "Bonjour !" ; lorsque lutilisateur clique dessus, le programme lui
demande son nom puis laffiche dans le bouton. Le deuxime permet simplement de
quitter le programme.
Figure 12.2
Salutations la souris.

Chacun de ces boutons est un widget devant ragir une action de lutilisateur. Mais,
en fait, ce sont au total trois widgets qui seront crs et affichs : les deux boutons,
plus un widget les "contenant" et assurant leur disposition harmonieuse, autant que
possible. Chaque widget peut, en effet, en contenir dautres, crant ainsi une sorte
dembotements des lments graphiques les uns dans les autres. Un logiciel
complexe, comme un traitement de texte, peut ainsi contenir des centaines de widgets,
dont une partie nexistent que pour en contenir dautres et les agencer correctement.
Par exemple, les boutons dune barre doutils sont tous des widgets, "contenus" dans
le widget quest la barre doutils, elle-mme "contenue" avec dautres dans le widget
quest la fentre gnrale du logiciel.
Il existe diffrentes techniques pour raliser cela. Celle prsente ici nest pas toujours
la plus simple, mais certainement celle qui offre le plus de souplesse. Elle consiste
crer un nouveau widget, en sappuyant sur la classe de base QWidget fournie par Qt.
Tous les contrles graphiques de Qt hritent de cette classe y compris la classe
QLabel vue prcdemment.
partir de maintenant, il est vivement recommand de placer les fichiers composant
chacun des programmes que nous allons raliser dans un rpertoire particulier : un
rpertoire par programme. ventuellement, distinguez les fichiers propres Python et
les fichiers propres C++, afin de bien vous y retrouver. Car ds prsent, nos
programmes ne seront plus stocks dans un unique fichier.
En effet, lorsque lon ralise une application complexe dans laquelle on cre de
nouveaux widgets, il est dusage de placer chacun deux dans un fichier qui lui est
ddi. Si cela peut paratre superflu pour cet exemple, qui reste simple, cette sparation devient indispensable ds que vous vous attaquez quelque chose de plus sophistiqu. Sans elle, vous aboutirez des fichiers contenant des milliers de lignes au sein
desquelles il deviendra extrmement difficile de naviguer.

Program Livre Page 164 Vendredi, 16. mai 2008 8:13 08

164

Initiation la programmation avec Python et C++

12.4.1. En Python
La situation la plus simple est, comme souvent, en langage Python. La sparation est
lmentaire : un widget, un fichier, plus un fichier supplmentaire pour le programme
principal. Voici la dclaration de notre widget personnalis, dans un fichier nomm
salutations.py :
from PyQt4 import QtCore, QtGui
class Salutation(QtGui.QWidget):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.bt_bonjour = QtGui.QPushButton("Bonjour!", self)
self.bt_quitter = QtGui.QPushButton("Quitter", self)
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.bt_bonjour)
layout.addWidget(self.bt_quitter)
self.connect(self.bt_bonjour, \
QtCore.SIGNAL("clicked()"), \
self.demanderNom)
self.connect(self.bt_quitter, \
QtCore.SIGNAL("clicked()"), \
self, \
QtCore.SLOT("close()"))
def demanderNom(self):
nom = QtGui.QInputDialog.getText(self, "Salutation", "Quel est votre nom?")
self.bt_bonjour.setText(QtCore.QString("Bonjour, %1!").arg(nom[0]))

Ne vous laissez pas impressionner par lapparente longueur de ce code, cest en fait
assez simple. On dclare une classe Salutation, en signalant quelle drive de la
classe QWidget contenue dans le module QtGui (lui-mme issu du module PyQt4 et
import par la ligne prcdente). Le constructeur de notre classe prend (au moins) un
paramtre parent, dsignant le widget dans lequel il sera ventuellement contenu, la
valeur par dfaut tant aucun (None). La premire action de ce constructeur est dinvoquer celui de la classe anctre : ce nest pas syntaxiquement obligatoire, mais il est
plus que vivement recommand de toujours commencer par invoquer ce constructeur
anctre.
Ensuite, nous crons les deux boutons en instanciant deux instances de la classe QPushButton : celle-ci reprsente, en effet, un simple bouton lcran sur lequel lutilisateur peut cliquer. Le premier paramtre dinstanciation est le texte afficher dans le
bouton, tandis que le second est le widget dans lequel sera contenu chaque bouton.
Comme nous sommes prcisment en train de construire un widget destin contenir
ces boutons, on donne donc la valeur self, linstance elle-mme de Salutation en
cours de cration. Du point de vue des boutons (qui sont galement des widgets), cette

Program Livre Page 165 Vendredi, 16. mai 2008 8:13 08

Chapitre 12

Ouverture des fentres : programmation graphique

165

valeur sera reue par le paramtre parent de leur propre constructeur. On tablit
ainsi une relation parent-enfant entre les widgets composants une application (un
parent pouvant avoir plusieurs enfants, mais un enfant ne pouvant avoir quun seul
parent).
Les trois lignes qui suivent concernent lagencement de ces widgets lcran. Il est
toujours possible de positionner " la main" les lments graphiques, en donnant
leurs coordonnes et leurs dimensions. Mais lexprience a montr que cette manire
de faire tait loin dtre satisfaisante : les fentres et les botes de dialogue taient
alors dune taille fixe, impossible modifier, ce qui est parfois gnant. Aussi, utilisons-nous ici un layout, cest--dire un outil fourni par Qt pour agencer automatiquement les lments graphiques. En lespce, il sagit dune instance de la classe
QVBoxLayout. Cette instance (nomme layout) est attache notre widget personnalis (qui lui est donn en paramtre) : elle va alors en contrler automatiquement
certaines contraintes concernant son aspect. On ajoute layout les deux boutons
pralablement crs par la mthode addWidget(). Cet "agenceur" particulier a pour
effet de placer les widgets quon lui donne en colonne (le VBox dans QVBoxLayout
signifiant bote verticale), le premier ajout tant celui du haut. Nous navons ainsi
pas nous proccuper de la position ni de la taille des boutons : ces proprits, particulirement rbarbatives rgler, seront automatiquement prises en charge par
layout.
Nous en arrivons la partie essentielle de la cration de notre widget : paramtrer la
raction du programme lorsque lutilisateur clique sur chacun des boutons. Pour cela,
Qt utilise la mtaphore des signaux et des slots. Le terme anglais de slot na pas, dans
ce contexte, de bon quivalent en franais ; pensez un emplacement, une cible, voire
une prise (au sens prise de courant). Lide est quun objet Qt (par exemple, un
bouton) peut "mettre" des signaux (par exemple, "on ma cliqu dessus"). Ces
signaux peuvent ensuite tre capts par dautres objets Qt, en provoquant lexcution
de mthodes particulires, les fameux slots. noter quun mme objet peut la fois
mettre et recevoir des signaux. La seule vritable contrainte tant que les classes
impliques doivent obligatoirement driver, directement ou non, de la classe
QObject. Ce qui est le cas de la classe QWidget sur laquelle nous nous appuyons ici.
Lassociation entre un signal mis par un contrle et une mthode devant y rpondre
est ralise par la mthode connect(). Celle-ci demande trois ou quatre paramtres,
selon la situation. Le premier est linstance mettrice du signal. Le deuxime est un
objet dcrivant le signal concern, obtenu en utilisant la fonction SIGNAL() du
module QtCore, laquelle attend une chane de caractres.

Program Livre Page 166 Vendredi, 16. mai 2008 8:13 08

166

Initiation la programmation avec Python et C++

Ensuite :

Si la fonction ou mthode devant tre excute lorsque le signal est mis est une
mthode que vous avez vous-mme dfinie, au sein de la mme classe, le troisime
et dernier paramtre est simplement le nom de cette mthode, prcd de linstance
concerne, comme cest le cas ici pour la mthode demanderNom().

Sil sagit au contraire dune mthode propre Qt, cest--dire venant dune classe
de base de Qt comme QWidget, alors le troisime paramtre est linstance devant
recevoir ce signal et le quatrime une rfrence sur la mthode concerne, obtenue
par un appel la fonction SLOT() du module QtCore.

Dans notre exemple, la mthode close() vient directement de la classe QWidget,


aussi utilisons-nous lcriture quatre paramtres.
Il ne nous reste plus qu utiliser cette classe graphique, ce qui est fait dans un
programme principal, dans un fichier nomm salutation_gui.py :
1.
2.
3.
4.
5.
6.
7.
8.

import sys
from PyQt4 import QtCore, QtGui
import salutations
app = QtGui.QApplication(sys.argv)
salut = salutations.Salutation()
salut.show()
sys.exit(app.exec_())

Voyez comment nous signalons laccs aux outils (unique dans notre cas) que nous
avons dfinis dans le fichier salutations.py : simplement avec la ligne import
salutations (ligne 3). Nous pouvons ds lors crer une instance de la classe Salutation (ligne 6) et lancer la boucle dvnements.

12.4.2. En C++
Comme lon pouvait sy attendre, la situation est sensiblement plus complexe en C++. Non
pas au niveau du code lui-mme, mais dans la faon de lorganiser. En premier lieu, nous
devons dclarer la classe Salutation dans un fichier den-tte, de la faon suivante :
1.
2.
3.
4.
5.
6.
7.
8.

#ifndef SALUTATIONS_H__
#define SALUTATIONS_H__
#include <QtGui>
class Salutation: public QWidget
{
Q_OBJECT
public:
Salutation(QWidget* parent = 0);

Program Livre Page 167 Vendredi, 16. mai 2008 8:13 08

Chapitre 12

Ouverture des fentres : programmation graphique

167

9.
public slots:
10.
void demanderNom();
11.
private:
12.
QPushButton* bt_bonjour;
13.
QPushButton* bt_quitter;
14. };
15. #endif // SALUTATIONS_H__

Remarquez que, pour la premire fois, ce fichier ne contient aucun code. De la mme
manire que nous avons, au chapitre prcdent, dclar des fonctions sans en donner le
code, ici nous dcrivons simplement ce que contient la classe Salutation. Quelques
remarques :

Le mot Q_OBJECT la ligne 6 est ncessaire au fonctionnement interne de Qt : ds


que vous crez une classe drivant directement ou non de la classe QObject (ce qui
est le cas de la classe QWidget), vous devez inscrire Q_OBJECT ainsi (cest ce quon
appelle une macro, qui sera interprte par le prprocesseur).

Lindication de section public slots:, ligne 9, nest pas une criture standard du
langage C++ : elle est utilise par les mcanismes de Qt permettant dassocier un
signal une mthode devant tre excute lorsquil est mis. Ne cherchez pas
utiliser une telle criture dans un programme nutilisant pas la bibliothque Qt,
cela ne compilera tout simplement pas.

Nous devons maintenant donner le code devant tre excut par les diffrentes mthodes
de notre classe, dans un fichier salutations.cpp :
#include "salutations.h"
Salutation::Salutation(QWidget* parent):
QWidget(parent),
bt_bonjour(0)
{
this->bt_bonjour = new QPushButton("Bonjour!", this);
this->bt_quitter = new QPushButton("Quitter", this);
QVBoxLayout* layout = new QVBoxLayout(this);
layout->addWidget(this->bt_bonjour);
layout->addWidget(this->bt_quitter);
this->connect(this->bt_bonjour,
SIGNAL(clicked()),
SLOT(demanderNom()));
this->connect(this->bt_quitter,
SIGNAL(clicked()),
this,
SLOT(close()));
}
void Salutation::demanderNom()
{
QString nom = QInputDialog::getText(this,

Program Livre Page 168 Vendredi, 16. mai 2008 8:13 08

168

Initiation la programmation avec Python et C++

"Salutations",
"Quel est votre nom?");
this->bt_bonjour->setText(QString("Bonjour, %1!").arg(nom));
}

Voyez comment ce code est donn : chacune des mthodes est rpte, son nom
prcd du nom de la classe, spar par oprateur:: (quon appelle loprateur de
rsolution de porte). En dehors de cette criture particulire, le code mme est tout
fait analogue celui utilis dans la version en langage Python. Remarquez, toutefois,
quon ne donne pas de chanes de caractres aux fonctions SIGNAL() et SLOTS() (qui
sont en ralit des macrofonctions, destines au prprocesseur), mais simplement les
noms des mthodes concernes. De mme, on utilise intensivement les possibilits de
cration dobjets dynamiques, comme nous lavons vu au chapitre prcdent.
En termes de code, il ne nous reste plus qu crire le programme principal, dans un
fichier salutation_gui.cpp :
#include <QApplication>
#include "salutations.h"
int main(int argc, char* argv[])
{
QApplication appli(argc, argv);
Salutation salut;
salut.show();
return appli.exec();
}

Enfin, on termine par la cration dun fichier projet, nomm par exemple salutations.pro :
TEMPLATE = app
Qt += gui
CONFIG += release
OBJECTS_DIR = .objs
MOC_DIR = .moc
DESTDIR = bin
HEADERS += salutations.h
SOURCES += salutations.cpp \
salutation_gui.cpp

Voyez comme on donne explicitement la liste des fichiers den-tte (dans la variable
HEADERS) et de code (dans la variable SOURCES) que nous avons crs. Dune manire
gnrale, vous aurez autant de fichiers de code que de fichiers den-tte, plus un contenant
le programme principal.

Program Livre Page 169 Vendredi, 16. mai 2008 8:13 08

Chapitre 12

Ouverture des fentres : programmation graphique

169

Vous pouvez maintenant gnrer un fichier de construction laide de lutilitaire


qmake, construire votre programme par la commande make (ou mingw32-make sur
Windows), pour enfin pouvoir lexcuter, le fichier excutable se trouvant dans un
sous-rpertoire bin.

12.5. Hano graphique


Il est maintenant temps de raliser une version graphique de notre programme des
Tours de Hano. Nous allons ltudier de faon exhaustive (du moins la partie graphique), puisque cest le premier programme aussi complexe que nous ralisons. Par la
suite, seules certaines parties pertinentes du code seront examines.
La Figure 12.3 montre quoi ressemblera notre version graphique des Tours de Hano.
Figure 12.3
Hano graphique.

Les rectangles reprsentent les diffrents layouts (outils dagencement) qui seront
utiliss : on peut en distinguer un grand principal, qui en contient deux autres plus
petits pour les boutons. Il est en effet possible dimbriquer les layouts les uns dans les
autres, ce qui rend possible la cration dune interface pouvant tre particulirement
complexe sans avoir trop se proccuper de la position et de la taille de ses lments.

12.5.1. En Python
Pour commencer, crez un fichier nomm hanoi.py et contenant la classe Hanoi que
nous avons vue prcdemment (ainsi que la fonction calculant une solution). Ne nous
attardons pas sur cette partie dj connue.
La partie centrale du programme est une reprsentation de ltat du jeu un moment
donn. Plusieurs solutions sont possibles pour obtenir une telle reprsentation. Le
choix a t fait ici de recourir tout simplement au dessin, cest--dire que nous allons
afficher une image qui sera cre la demande. La cration et laffichage mme de

Program Livre Page 170 Vendredi, 16. mai 2008 8:13 08

170

Initiation la programmation avec Python et C++

cette image seront pris en charge par un widget ddi, nomm Dessin. Voici donc le
fichier dessin.py, contenant la dfinition de ce widget :
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.

# -*- coding: utf8 -*from PyQt4 import QtCore, QtGui


import hanoi
hauteur_disque = 10
rayon_unite = 10
rayon_aiguille = 5
espace_inter_disques = 2
espace_inter_tours = 20
marge = 8
couleur_disques = QtGui.QColor(255, 160, 96)
couleur_supports = QtGui.QColor(128, 48, 16)
couleur_fond = QtGui.QColor(255, 255, 255)
def rayon_max_tour(taille_max):
return \
(taille_max + 1) * rayon_unite + \
rayon_aiguille
def hauteur_max_tour(taille_max):
return \
taille_max * (hauteur_disque + espace_inter_disques) + \
2 * (2*rayon_aiguille)
class Dessin(QtGui.QWidget):
def __init__(self, le_jeu, parent=None):
QtGui.QWidget.__init__(self, parent)
self.jeu = le_jeu
largeur_max = \
3 * (2*rayon_max_tour(self.jeu.Hauteur())) + \
2 * espace_inter_tours + \
2 * marge
hauteur_max = \
hauteur_max_tour(self.jeu.Hauteur()) + \
2 * marge
self.setFixedSize(largeur_max, hauteur_max)
self.image = QtGui.QPixmap(largeur_max, hauteur_max)
self.Mettre_A_Jour()
def Centre_Etage(self, tour, etage):
centre_tour_x = \
marge + \
(1+2*tour)*rayon_max_tour(self.jeu.Hauteur()) + \
tour * espace_inter_tours
base_y = self.height() - (marge + (2*rayon_aiguille))
etage_y = base_y - \
(espace_inter_disques+hauteur_disque)*(etage+1) + \
(hauteur_disque/2)
return (centre_tour_x, etage_y)
def Mettre_A_Jour(self):
self.image.fill(couleur_fond);

Program Livre Page 171 Vendredi, 16. mai 2008 8:13 08

Chapitre 12

Ouverture des fentres : programmation graphique

51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.

p = QtGui.QPainter(self.image);
# dessin des supports
p.setPen(QtGui.QColor(0, 0, 0))
p.setBrush(couleur_supports)
base_y = self.height() - marge
for tour in range(3):
centre_tour_x = \
marge + \
(1+2*tour)*rayon_max_tour(self.jeu.Hauteur()) + \
tour * espace_inter_tours
rayon = rayon_max_tour(self.jeu.Hauteur())
# aiguille
p.drawRoundRect(centre_tour_x - rayon_aiguille, \
base_y - hauteur_max_tour(self.jeu.Hauteur()), \
2 * rayon_aiguille, \
hauteur_max_tour(self.jeu.Hauteur()), \
20, 20)
# base
p.drawRoundRect(centre_tour_x - rayon, \
base_y - (2*rayon_aiguille), \
2 * rayon, \
2 * rayon_aiguille, \
20, 20)
# dessin des disques
p.setBrush(couleur_disques)
for tour in range(3):
nb_disques = self.jeu.Une_Tour(tour).Nombre_Disques()
for etage in range(nb_disques):
disque = self.jeu.Une_Tour(tour).Disque_Etage(etage)
centre = self.Centre_Etage(tour, etage)
self.Dessiner_Disque(disque, centre, p)
p.end()

171

def Dessiner_Disque(self, disque, centre, p):


x = centre[0] - rayon_aiguille - disque*rayon_unite
y = centre[1] - hauteur_disque/2
largeur = 2*(disque*rayon_unite + rayon_aiguille)
p.drawRoundRect(x, y, largeur, hauteur_disque, 20, 20)
def paintEvent(self, event):
p = QtGui.QPainter(self)
p.drawPixmap(0, 0, self.image)
p.end()
QtGui.QWidget.paintEvent(self, event)

Les premires lignes (4 12) ne sont destines qu "nommer" les diffrentes dimensions que nous allons utiliser par la suite. Pour la clart du programme, il est en effet
prfrable dutiliser des noms que des valeurs brutes : les noms des variables devraient
parler deux-mmes. Par exemple, la formule la ligne 87, qui calcule la largeur dun
disque lcran, est plus parlante ainsi que si nous avions crit :
largeur = 2*(disque * 10 + 5)

Program Livre Page 172 Vendredi, 16. mai 2008 8:13 08

172

Initiation la programmation avec Python et C++

Lunit de ces dimensions est le pixel (contraction de picture element, lment


dimage), cest--dire le point le plus petit que votre cran puisse afficher. Lorsquon
dit quun cran a une rsolution de 1 024 768 (par exemple), on indique par l quon peut
afficher un tableau de pixels, dune largeur de 1 024 colonnes de 768 pixels, ou dune
hauteur de 768 lignes de 1 024 pixels (ce qui en fait au total un peu moins dun demimillion). Plus la rsolution est leve, plus les pixels sont "petits", la taille relle dun
pixel dpendant naturellement de la taille relle de lcran.
Incidemment, le fait dutiliser ainsi des variables pour stocker les dimensions permet
de faciliter grandement toute modification ultrieure, si vous voulez que la reprsentation
du jeu soit plus grande ou plus petite.
Les trois dernires variables sont destines contenir les couleurs qui seront appliques pour le dessin (voir, ci-aprs, lencadr consacr au codage des couleurs). Ces
couleurs sont reprsentes par des instances de la classe QColor, une classe fournie
par la bibliothque Qt et obtenue dans le module QtGui.
Suivent deux fonctions "utilitaires", permettant de calculer lespace total occup par
chaque tour en fonction des valeurs prcdentes et de la hauteur maximale choisie
par lutilisateur. Il ne sagit que de calculs lmentaires ne prsentant pas de difficults
particulires. une petite subtilit prs : par convention choisie voil bien des annes
(mme des dcennies), les coordonnes de chaque pixel de lcran sont exprimes
partir du coin suprieur gauche de lcran (ou de limage si on dessine dans une
image). Dans une rsolution de 1 024 768, la colonne (ou labscisse, ou la coordonne x) de chaque pixel va de la gauche vers la droite, de 0 1 023 ; la ligne de chaque
pixel (ou lordonne, ou la coordonne y) va de haut en bas, de 0 767. Lordonne est
inverse par rapport aux reprsentations graphiques usuelles. Le pixel de coordonnes
(0, 0) est celui tout en haut gauche, celui de coordonnes (1 023, 0) celui tout en haut
droite, (0, 767) est tout en bas gauche et (1023, 767) dans le coin en bas droite.
Cette inversion de laxe des ordonnes peut tre droutante dans un premier temps,
mais vous vous y ferez vite avec un peu de pratique. Notamment, cherchez comprendre
les formules utilises dans le programme.
Les couleurs de lcran : codage RGB
Les couleurs innombrables que nos crans modernes sont capables dafficher sont constitues du mlange de trois couleurs de base, le rouge (red), le vert (green) et le bleu (blue),
dans cet ordre, do le nom de codage RGB (en anglais) ou RVB (en franais). Une couleur
est obtenue en mlangeant ces trois couleurs fondamentales selon diverses proportions,
tout comme un peintre le fait laide de son pinceau. Par exemple, le jaune est obtenu en
mlangeant parts gales du rouge et du vert. Il existe de multiples faons de spcifier la

Program Livre Page 173 Vendredi, 16. mai 2008 8:13 08

Chapitre 12

Ouverture des fentres : programmation graphique

173

quantit, ou lintensit, de chacune des couleurs de base ; toutefois, la plus utilise


consiste donner un nombre entier entre 0 et 255, la valeur 0 correspondant une intensit nulle (noir) et la valeur 255, une intensit maximale. Ainsi, un jaune pur serait
donn par la combinaison (255, 255, 0). Une combinaison comme (128, 128, 0) produirait
un jaune plus sombre, tandis que (255, 255, 128) donnerait un jaune plus clair.
En effet, le mlange des trois couleurs pleine intensit, soit la combinaison (255, 255,
255), donne un blanc parfait. Au contraire, le noir absolu est donn par (0, 0, 0). Dun
extrme lautre, vous disposez de 16 777 216 couleurs diffrentes, exactement.
Notez quil existe dautres faons de donner la proportion de chacune de ces couleurs, par
exemple par le biais dun nombre dcimal entre 0.0 et 1.0. Lexactitude de la reprsentation dpend en gnral du matriel disponible : ainsi, certaines cartes graphiques associes
des crans de trs haut de gamme peuvent afficher des milliards de couleurs.
Enfin, dautres reprsentations des couleurs existent, comme le codage HSV (fond sur un
cercle chromatique) ou le codage CMYK (mlange de cyan, magenta, jaune et noir,
couramment utilis sur les imprimantes). Nous ne les aborderons pas ici.

Nous en arrivons la classe Dessin, le widget charg dafficher une image reprsentant ltat du jeu. Le constructeur de cette classe reoit en paramtre une instance de la
classe Hanoi, ncessaire afin de calculer les diverses dimensions et de tracer la reprsentation. La mthode setFixedSize() (ligne 34), issue de la classe QWidget, permet
de fixer une taille au widget : ainsi, les layouts ne pourront pas changer sa taille, ce qui
permet dviter des effets disgracieux sur le dessin (il serait cependant possible
dadapter le dessin un changement de taille, mais cela deviendrait vraiment compliqu). Enfin, limage elle-mme est reprsente par une instance de la classe QPixmap,
classe optimise spcialement pour tre affiche lcran. Son constructeur attend en
paramtre les dimensions de limage, dabord la largeur, puis la hauteur.
Lessentiel se passe dans la mthode Mettre_A_Jour() ( partir de la ligne 49). Cest
l que va effectivement tre dessine limage reprsentant le jeu. La premire action
est de "vider" limage, cest--dire dobtenir une image vierge, remplie de la couleur
de fond choisie au dbut du fichier (ici du blanc). Cela est ralis par la mthode
fill() de la classe QPixmap : tous les pixels de limage sont alors blancs, prts recevoir
un nouveau dessin.
Pour dessiner, il faut un pinceau, justement fourni par la classe QPainter de Qt. Celleci contient de nombreuses mthodes permettant de tracer les points individuels, mais
aussi des rectangles, des ellipses, des courbes de Bzier La plupart des formes
pouvant tre dessines acceptent une couleur de bord et une couleur de remplissage.
La premire est dfinie par la mthode setPen() de QPainter, la seconde par la
mthode setBrush().

Program Livre Page 174 Vendredi, 16. mai 2008 8:13 08

174

Initiation la programmation avec Python et C++

Les supports des disques sont dessins en premier. Il importe ici de comprendre que le
dessin que nous ralisons est tout fait comparable celui quon ferait avec de lencre
sur une feuille de papier : ce qui est dessin apparatra toujours "par-dessus" ce qui se
trouvait pralablement au mme endroit.
Le dessin lui-mme est effectu par la mthode drawRoundRect() de QPainter, qui
permet dobtenir des rectangles aux coins arrondis. Les valeurs numriques qui lui sont
passes sont des coordonnes et des dimensions exprimes en pixels, toujours avec
lorigine des coordonnes dans le coin suprieur gauche.
Enfin, la dernire mthode intressante est la mthode paintEvent(), hrite de la
classe QWidget. Cest elle qui est invoque lorsquil est ncessaire de redessiner
le widget lcran, que cela soit lors de sa premire apparition ou lorsquil se retrouve
expos aprs avoir t cach par une autre fentre. Nous utilisons nouveau une
instance de QPainter, simplement pour "plaquer" limage sur le widget. Remarquez
que nous invoquons pour terminer la mthode paintEvent() de la classe anctre, ce
qui est gnralement recommand.
La classe principale reprsentant lensemble de la fentre, ainsi que le programme
principal permettant de lancer lapplication, se trouve dans un fichier hanoi_gui.py :
# -*- coding: utf8 -*import sys
from PyQt4 import QtCore, QtGui
import hanoi
import dessin
class Hanoi_Gui(QtGui.QWidget):
def __init__(self, le_jeu, parent):
QtGui.QWidget.__init__(self, parent)
self.jeu = le_jeu
self.nb_mouvements = 0
self.tour_depart = -1
colonne = QtGui.QVBoxLayout(self)
ligne_boutons_tours = QtGui.QHBoxLayout()
self.bt_tour_1 = QtGui.QPushButton("Tour 1", self)
ligne_boutons_tours.addWidget(self.bt_tour_1)
self.connect(self.bt_tour_1,
QtCore.SIGNAL("clicked()"),
self.Tour_Choisie)
self.bt_tour_2 = QtGui.QPushButton("Tour 2", self)
ligne_boutons_tours.addWidget(self.bt_tour_2)
self.connect(self.bt_tour_2,
QtCore.SIGNAL("clicked()"),
self.Tour_Choisie)

Program Livre Page 175 Vendredi, 16. mai 2008 8:13 08

Chapitre 12

Ouverture des fentres : programmation graphique

self.bt_tour_3 = QtGui.QPushButton("Tour 3", self)


ligne_boutons_tours.addWidget(self.bt_tour_3)
self.connect(self.bt_tour_3,
QtCore.SIGNAL("clicked()"),
self.Tour_Choisie)
colonne.addLayout(ligne_boutons_tours)
self.dessin = dessin.Dessin(self.jeu, self)
colonne.addWidget(self.dessin)
ligne_boutons_fonctions = QtGui.QHBoxLayout()
bt_solution = QtGui.QPushButton("Solution", self)
ligne_boutons_fonctions.addWidget(bt_solution)
self.connect(bt_solution,
QtCore.SIGNAL("clicked()"),
self.Solution)
bt_quitter = QtGui.QPushButton("Quitter", self)
ligne_boutons_fonctions.addWidget(bt_quitter)
self.connect(bt_quitter,
QtCore.SIGNAL("clicked()"),
QtGui.qApp,
QtCore.SLOT("quit()"))
colonne.addLayout(ligne_boutons_fonctions)
self.dessin.Mettre_A_Jour()
def Jeu(self):
return self.jeu
def Tour_Choisie(self):
if ( self.tour_depart < 0 ):
# on a choisi la tour de dpart
self.tour_depart = self.Bouton_Tour_Choisie(self.sender())
if ( self.tour_depart < 0 ):
return
if ( self.Jeu().Tour_Depart_Valide(self.tour_depart) ):
if ( self.tour_depart!= 0 ):
self.bt_tour_1.setEnabled(
self.Jeu().Tour_Arrivee_Valide(self.tour_depart, 0))
if ( self.tour_depart!= 1 ):
self.bt_tour_2.setEnabled(
self.Jeu().Tour_Arrivee_Valide(self.tour_depart, 1))
if ( self.tour_depart!= 2 ):
self.bt_tour_3.setEnabled(
self.Jeu().Tour_Arrivee_Valide(self.tour_depart, 2))
else:
self.tour_depart = -1;
QtGui.QMessageBox.warning(self,
self.trUtf8("Tours de Hano"),
self.trUtf8("Tour de dpart invalide!"))

175

Program Livre Page 176 Vendredi, 16. mai 2008 8:13 08

176

Initiation la programmation avec Python et C++

else:
# on a choisi la tour darrive
tour_arrivee = self.Bouton_Tour_Choisie(self.sender())
if ( tour_arrivee!= self.tour_depart ):
if ( self.Jeu().Tour_Arrivee_Valide(self.tour_depart, tour_arrivee) ):
self.Jeu().Deplacer_Disque(self.tour_depart, tour_arrivee)
self.nb_mouvements += 1
self.dessin.Mettre_A_Jour()
self.dessin.update()
else:
QtGui.QMessageBox.warning(self,
self.trUtf8("Tours de Hano"),
self.trUtf8("Tour darrive invalide!"))
if ( self.Jeu().Jeu_Termine() ):
self.bt_tour_1.setEnabled(False)
self.bt_tour_2.setEnabled(False)
self.bt_tour_3.setEnabled(False)
QtGui.QMessageBox.information(self,
self.trUtf8("Tours de Hano"),
self.trUtf8("Bravo! Rsolu en %1 mouvements!").arg(self.nb_mouvements))
else:
self.bt_tour_1.setEnabled(True)
self.bt_tour_2.setEnabled(True)
self.bt_tour_3.setEnabled(True)
self.tour_depart = -1
def Solution(self):
pass
def Bouton_Tour_Choisie(self, bt):
if ( bt == self.bt_tour_1 ):
return 0
elif ( bt == self.bt_tour_2 ):
return 1
elif ( bt == self.bt_tour_3 ):
return 2
else:
return -1

app = QtGui.QApplication(sys.argv)
hauteur_max = \
QtGui.QInputDialog.getInteger(None,
app.trUtf8("Tours de Hano"),

Program Livre Page 177 Vendredi, 16. mai 2008 8:13 08

Chapitre 12

Ouverture des fentres : programmation graphique

177

app.trUtf8("Nombre de disques?"),
3, 2, 8)
le_jeu = hanoi.Hanoi()
le_jeu.Initialiser(hauteur_max[0])
gui = Hanoi_Gui(le_jeu, None)
gui.show()
sys.exit(app.exec_())

Lide est que lutilisateur choisit les tours de dpart et darrive laide de trois
boutons prvus cet effet. Ces trois boutons sont connects une unique mthode,
Tour_Choisie(), dont le rle sera de vrifier les choix et de raliser effectivement le
dplacement dun disque (ainsi que de demander linstance de Dessin, que la classe
contient, de se remettre jour). Une petite subtilit a t introduite : en fonction de la
tour choisie comme point de dpart, les autres boutons sont ventuellement griss
(mthode setEnabled() issue de QWidget) afin que lutilisateur ne puisse pas choisir
un dplacement interdit.
Autre subtilit, la mthode Bouton_Tour_Choisie() qui permet de retrouver le
numro dune tour partir du bouton cliqu. Cela est permis par le fait quil existe une
mthode sender(), charge de fournir une rfrence sur lobjet, lmetteur, ayant
provoqu lexcution de la mthode Tour_Choisie() (qui est un slot dans le vocable
Qt). Sans cette possibilit, il aurait t ncessaire de connecter chacun des boutons
une mthode diffrente, afin de les identifier.
En regardant ce code, sans doute vous tes-vous demand ce qutait cette fonction
(une mthode en ralit, fournie par la classe de base QObject) trUtf8() frquemment employe pour les chanes de caractres afficher. Son rle est de transformer la
chane de caractres qui lui est donne en paramtre en une instance de la classe
QString. Cette classe de Qt est galement un type permettant la manipulation de chanes de caractres, mais la chane est code selon le standard Unicode. Cela donne
laccs un catalogue beaucoup plus vaste de caractres, facilitant ainsi laffichage
dans des langues possdant dautres alphabets (langues arabes ou asiatiques, mais
galement certaines langues europennes comme le polonais ou le norvgien). Qt
offre en effet un support assez complet de linternationalisation, cest--dire un
ensemble doutils permettant dadapter un programme en diffrentes langues. Nous
naborderons pas cet aspect ici.
Il naura pas chapp au lecteur attentif que rien ne semble prvu pour montrer,
lutilisateur, la solution au problme. Nous verrons cela au Chapitre 14, qui mettra en
uvre une petite animation pour le dplacement des disques.

Program Livre Page 178 Vendredi, 16. mai 2008 8:13 08

178

Initiation la programmation avec Python et C++

12.5.2. En C++
Comme on pouvait sy attendre, la situation en C++ est un peu plus complexe. Pour
commencer, il est ncessaire de sparer en deux fichiers les dclarations de nos classes
et le code quelles devront effectivement excuter. Voici le fichier dessin.h, fichier
den-tte pour la classe Dessin (charge dafficher ltat du jeu) :
#ifndef DESSIN_H__
#define DESSIN_H__ 1
#include <QWidget>
#include <QPixmap>
#include "hanoi.h"
class Dessin: public QWidget
{
Q_OBJECT
public:
Dessin(const Hanoi& le_jeu,
QWidget* parent = 0);
QPoint Centre_Etage(int tour,
int etage) const;
public slots:
void Mettre_A_Jour();
protected:
void Dessiner_Disque(int disque,
const QPoint& centre,
QPainter& p) const;
virtual void paintEvent(QPaintEvent* event);
private:
QPixmap image;
const Hanoi& jeu;
}; // class Dessin
#endif // DESSIN_H__

Rien de bien exceptionnel ici. La classe QPoint, fournie par Qt, reprsente simplement les coordonnes dun point lcran. Elle apporte un moyen commode pour
"transporter" des coordonnes dune fonction lautre.
Remarquez, tout de mme, la faon dont nous conservons linstance sur le jeu (donne
jeu). On utilise une rfrence constante, faon de garantir que cette classe daffichage
ne va pas modifier le jeu lui-mme.
Voyons maintenant limplmentation de tout cela, dans le fichier dessin.cpp.
#include <QPainter>
#include "dessin.h"
namespace
{

Program Livre Page 179 Vendredi, 16. mai 2008 8:13 08

Chapitre 12

Ouverture des fentres : programmation graphique

179

const int hauteur_disque = 10;


const int rayon_unite = 10;
const int rayon_aiguille = 5;
const int espace_inter_disques = 2;
const int espace_inter_tours = 20;
const int marge = 8;
const QColor couleur_disques(255, 160, 96);
const QColor couleur_supports(128, 48, 16);
const QColor couleur_fond(Qt::white);
int rayon_max_tour(int taille_max)
{
return
(taille_max + 1) * rayon_unite +
rayon_aiguille;
}
int hauteur_max_tour(int taille_max)
{
return
taille_max * (hauteur_disque + espace_inter_disques) +
2 * (2*rayon_aiguille);
}
}

On retrouve ici les diverses constantes dcrivant la gomtrie de notre affichage, ainsi
que les deux fonctions utilitaires rayon_max_tour() et hauteur_max_tour(). Voyez
comment chacune des valeurs est qualifie par const, toujours pour leur garantir une
certaine cohrence et viter des modifications intempestives. De plus, tous ces
lments sont contenus dans un bloc namespace{}.
Le mot cl namespace signifie espace de nommage. Il permet de dfinir une sorte de
catgorie au sein de laquelle les diffrents lments doivent avoir un nom unique.
Depuis le dbut de cet ouvrage, nous utilisons couramment un espace de nommage :
celui nomm std, rserv pour les outils de la bibliothque standard du langage C++.
Et cest bien l le sens des critures telles que std::cout : on dsigne lobjet nomm
cout, dans la catgorie nomme std. De cette faon, vous pouvez parfaitement crer
vous-mme un objet nomm cout au sein de votre programme : il ny aura pas de
confusion sur les noms.
Nous utilisons ici un espace de nommage anonyme, cest--dire sans nom. Sa fonction
est uniquement dindiquer que ce quil contient est propre au fichier dans lequel il se
trouve et ne saurait tre accessible depuis une autre partie du programme. On retrouve
une exigence de cohrence et de sparation nette entre les diffrentes parties dun
programme.
Mais poursuivons dans ce fichier dessin.cpp.

Program Livre Page 180 Vendredi, 16. mai 2008 8:13 08

180

Initiation la programmation avec Python et C++

Dessin::Dessin(const Hanoi& le_jeu,


QWidget* parent):
image(0,0),
jeu(le_jeu)
{
const int largeur_max =
3 * (2*rayon_max_tour(this->jeu.Hauteur())) +
2 * espace_inter_tours +
2 * marge;
const int hauteur_max =
hauteur_max_tour(this->jeu.Hauteur()) +
2 * marge;
this->setFixedSize(largeur_max, hauteur_max);
image = QPixmap(largeur_max, hauteur_max);
this->Mettre_A_Jour();
}
QPoint Dessin::Centre_Etage(int tour,
int etage) const
{
const int centre_tour_x =
marge +
(1+2*tour)*rayon_max_tour(this->jeu.Hauteur()) +
tour * espace_inter_tours;
const int base_y = this->height() - (marge + (2*rayon_aiguille));
const int etage_y = base_y (espace_inter_disques+hauteur_disque)*(etage+1) +
(hauteur_disque/2);
return QPoint(centre_tour_x, etage_y);
}
void Dessin::Mettre_A_Jour()
{
this->image.fill(couleur_fond);
QPainter p(&(this->image));
// dessin des supports
p.setPen(Qt::black);
p.setBrush(couleur_supports);
const int base_y = this->height() - marge;
for(int tour = 0; tour < 3; ++tour)
{
const int centre_tour_x =
marge +
(1+2*tour)*rayon_max_tour(this->jeu.Hauteur()) +
tour * espace_inter_tours;
const int rayon = rayon_max_tour(this->jeu.Hauteur());
// aiguille

Program Livre Page 181 Vendredi, 16. mai 2008 8:13 08

Chapitre 12

Ouverture des fentres : programmation graphique

181

p.drawRoundRect(centre_tour_x - rayon_aiguille,
base_y - hauteur_max_tour(this->jeu.Hauteur()),
2 * rayon_aiguille,
hauteur_max_tour(this->jeu.Hauteur()),
20, 20);
// base
p.drawRoundRect(centre_tour_x - rayon,
base_y - (2*rayon_aiguille),
2 * rayon,
2 * rayon_aiguille,
20, 20);
}
// dessin des disques
p.setPen(Qt::black);
p.setBrush(couleur_disques);
for(int tour = 0; tour < 3; ++tour)
{
const int nb_disques = this->jeu.Une_Tour(tour).Nombre_Disques();
for(int etage = 0; etage < nb_disques; ++etage)
{
const int disque = this->jeu.Une_Tour(tour).Disque_Etage(etage);
QPoint centre = this->Centre_Etage(tour, etage);
this->Dessiner_Disque(disque, centre, p);
}
}
p.end();
}
void Dessin::Dessiner_Disque(int disque,
const QPoint& centre,
QPainter& p) const
{
const int x = centre.x() - rayon_aiguille - disque*rayon_unite;
const int y = centre.y() - hauteur_disque/2;
const int largeur = 2*(disque*rayon_unite + rayon_aiguille);
p.drawRoundRect(x, y, largeur, hauteur_disque, 20, 20);
}
void Dessin::paintEvent(QPaintEvent* event)
{
QPainter p(this);
p.drawPixmap(0, 0, this->image);
p.end();
QWidget::paintEvent(event);
}

Comme vous pouvez le constater, le code est parfaitement comparable celui que
nous avons vu dans la version Python. Aussi nallons-nous pas le dtailler davantage.
Les diffrences essentielles sont imposes par le changement de langage, notamment
dans lutilisation explicite de rfrences et de pointeurs en C++.

Program Livre Page 182 Vendredi, 16. mai 2008 8:13 08

182

Initiation la programmation avec Python et C++

Le fichier hanoi_gui.h contient la dclaration de la classe Hanoi_Gui, qui est,


comme prcdemment, la classe reprsentant linterface de notre programme :
#ifndef HANOI_GUI_H__
#define HANOI_GUI_H__ 1
#include <QWidget>
#include <QPushButton>
#include "hanoi.h"
#include "dessin.h"
class Hanoi_Gui: public QWidget
{
Q_OBJECT
public:
Hanoi_Gui(Hanoi& le_jeu,
QWidget* parent = 0);
Hanoi& Jeu();
const Hanoi& Jeu() const;
public slots:
void Tour_Choisie();
void Solution();
protected:
int Bouton_Tour_Choisie(QObject* bt) const;
private:
Dessin*
dessin;
QPushButton* bt_tour_1;
QPushButton* bt_tour_2;
QPushButton* bt_tour_3;
int
tour_depart;
int
nb_mouvements;
Hanoi&
jeu;
}; // class Hanoi_Gui
#endif // HANOI_GUI_H__

Encore une fois, rien de bien spcial ici. Limplmentation des mthodes ne prsente
aucune difficult particulire, le code tant presque quivalent celui du programme
Python (toujours aux diffrences de syntaxe prs). Aussi, pouvons-nous passer directement
au programme principal, dans main.cpp :
#include <QApplication>
#include <QInputDialog>
#include "hanoi.h"
#include "hanoi_gui.h"
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
int hauteur_max;

Program Livre Page 183 Vendredi, 16. mai 2008 8:13 08

Chapitre 12

Ouverture des fentres : programmation graphique

183

hauteur_max =
QInputDialog::getInteger(0,
app.trUtf8("Tours de Hano"),
app.trUtf8("Nombre de disques?"),
3, 2, 8);
Hanoi le_jeu;
le_jeu.Initialiser(hauteur_max);
Hanoi_Gui gui(le_jeu);
gui.show();
return app.exec();
}

Et mme ce programme principal est tout ce quil y a de plus classique ! Si vous prenez
lhabitude de structurer ainsi vos programmes, mme les plus complexes, vous ne
devriez pas rencontrer de problmes majeurs pour vous y retrouver.

Program Livre Page 184 Vendredi, 16. mai 2008 8:13 08

Program Livre Page 185 Vendredi, 16. mai 2008 8:13 08

13
Stockage de masse :
manipuler les fichiers
Au sommaire de ce chapitre :

criture

Lecture

Apart : la ligne de commande

Program Livre Page 186 Vendredi, 16. mai 2008 8:13 08

186

Initiation la programmation avec Python et C++

On dsigne habituellement par stockage de masse lenregistrement de donnes sur


un support physique, gnralement de grande capacit, et capable de conserver ces
donnes mme sans tre aliment lectriquement. Par exemple, un disque dur (dont
vous pouvez voir lintrieur la Figure 13.1 mais ne faites pas cela, un disque dur
ouvert ne fonctionne plus !). La diffrence essentielle avec les donnes que vous
pouvez stocker en mmoire lors de lexcution dun programme est justement que
le stockage de masse permet den mmoriser beaucoup plus et de faon permanente.
Figure 13.1
Vue de lintrieur
dun disque dur.

Les donnes ainsi stockes sont organises en fichiers, chacun possdant sa propre
organisation "interne" dfinie par le programme layant cr. On distingue habituellement
deux types de fichiers :

Les fichiers texte, dont le contenu est constitu uniquement de caractres lisibles
par un tre humain. Ces caractres sont organiss en lignes, chaque fin de ligne
tant marque par un caractre spcial (ou deux caractres spciaux, selon le
systme sur lequel vous vous trouvez). Les fichiers contenant les codes source de vos
programmes, une page Web visualise dans un navigateur, sont des fichiers texte.

Les fichiers binaires, dont le contenu est comparable ce qui se trouve rellement
dans la mmoire de lordinateur lors de lexcution du programme. Les informations sont alors gnralement plus "compactes" que dans le cas dun fichier texte et
ne sont pas lisibles par un il humain ( moins quil ne soit extrmement entran).

Program Livre Page 187 Vendredi, 16. mai 2008 8:13 08

Chapitre 13

Stockage de masse : manipuler les fichiers

187

Les fichiers rsultants dune compilation, les photos issues dun appareil numrique
ou dun scanner, sont des fichiers binaires.
Le type de fichier utiliser dpend du programme que vous dveloppez. Dune
manire gnrale, les fichiers binaires sont privilgis lorsque lencombrement sur
disque et la rapidit de lecture et dcriture sont des considrations importantes. On
prfrera plutt les fichiers texte si on souhaite obtenir un fichier pouvant tre lu facilement par nimporte quel diteur de texte.
Nous allons ici nous limiter dlibrment aux fichiers texte, car ce sont encore les plus
simples manipuler. Comme vous allez le constater, il ny a pas grande diffrence
avec les techniques dentres-sorties que nous avons vues depuis le Chapitre 4.

13.1. criture
Pour commencer, nous allons crer un programme qui permet de stocker dans un
fichier la liste des dplacements ncessaires pour rsoudre le jeu des Tours de Hano.
Cela peut tre utile pour transmettre cette information capitale un ami, sans devoir
noter scrupuleusement ces dplacements la main, avec tous les risques derreurs que
cela comporte.
Voici donc le programme ecrire_solution :
Python

C++

# -*- coding: utf8 -*import hanoi


print "Nombre de disques?",
hauteur_max = int(raw_input())
le_jeu = hanoi.Hanoi()
le_jeu.Initialiser(hauteur_max);
solution = []
le_jeu.Solution(solution)
fichier = file("solution", "w")
for mouvement in solution:
print >> fichier, \
mouvement[0]+1, \
mouvement[1]+1
fichier.close()

#include <iostream>
#include <fstream>
#include "hanoi.h"
int main(int argc, char* argv[])
{
int nb_disques = 3;
std::cout << "Nombre de disques? ";
std::cin >> nb_disques;
Hanoi jeu;
jeu.Initialiser(nb_disques);
std::vector<std::pair<int, int> >solution;
jeu.Solution(solution);
std::ofstream fichier("solution");
for(int mouvement = 0;
mouvement < solution.size();
++mouvement)

Program Livre Page 188 Vendredi, 16. mai 2008 8:13 08

188

Initiation la programmation avec Python et C++

Python

C++
{
fichier
<< solution[mouvement].first+1
<< " "
<< solution[mouvement].second+1
<< std::endl;
}
fichier.close();
return 0;
}

Lcriture dans un fichier se fait en crant une instance dun type particulier : le type
file en Python, le type std::ofstream en C++. La simple cration de cette instance
provoque louverture du fichier, cest--dire que le programme signale au systme
dexploitation quil sapprte manipuler un fichier. Le nom de ce fichier est pass en
argument la cration de linstance (vous laurez compris, file et std::ofstream
sont en ralit des classes).
Le type C++ std::ofstream reprsente un flux de sortie vers un fichier (o pour out,
sortie ; f pour file, fichier ; stream est langlais de flux). Cela signifie que linstance ne
permet que dcrire dans le fichier, pas den lire le contenu. Pour Python, le "sens
douverture" (lecture ou criture) est dtermin par un second paramtre pass au
constructeur de file, une chane de caractres. Ici, la chane "w" (pour write, crire)
indique que nous allons crire dans le fichier : linstance ne nous permettra pas de lire
depuis le fichier.
Dans les deux cas, si un fichier du mme nom existe dj (dans le mme rpertoire), il
est tout simplement "cras", cest--dire que le contenu prcdent est irrmdiablement perdu. Si ce nest pas ce que vous souhaitez, si vous voulez ajouter des lignes
un fichier existant sans perdre celles dj prsentes :

En C++, passez en second paramtre la valeur std::ios::app.

En Python, passez la chane "w+" en second paramtre.

Lcriture en elle-mme est tout fait comparable laffichage lcran que nous
avons vu depuis le dbut de cet ouvrage. Elle est mme parfaitement quivalente en
C++, tandis que Python ncessite lemploi dune notation particulire pour linstruction print : la faire suivre de deux chevrons fermants (>>), suivis de linstance de
file prcdemment cre, avant la liste de ce que lon veut inscrire.

Program Livre Page 189 Vendredi, 16. mai 2008 8:13 08

Chapitre 13

Stockage de masse : manipuler les fichiers

189

Lorsque lcriture est termine, il convient de fermer le fichier, au moyen de la


mthode close(). On signale ainsi au systme dexploitation que lon a termin.
Ces deux oprations symtriques, louverture et la fermeture dun fichier, sont assez
importantes pour le systme sous-jacent : elles permettent la mise en uvre de
diverses techniques doptimisation qui conduisent un meilleur "rpondant" du
systme.

13.2. Lecture
Maintenant que nous avons stock dans un fichier les mouvements ncessaires la
rsolution du problme, il serait tout aussi intressant de relire ce fichier afin den
extraire cette prcieuse information. Voici le programme lire_solution :
Python

C++

# -*- coding: utf8 -*fichier = file("solution", "r")


while ( fichier ):
ligne = fichier.readline()
mouvement = ligne.split()
if ( len(mouvement) == 2 ):
print int(mouvement[0]), \
"->", \
int(mouvement[1])
fichier.close()

#include <iostream>
#include <fstream>
int main(int argc, char* argv[])
{
std::ifstream fichier("solution");
while ( not fichier.eof() )
{
std::string ligne;
int depart;
int arrivee;
fichier >> depart;
fichier >> arrivee;
std::cout << depart << " -> "
<< arrivee << "\n";
}
fichier.close();
return 0;
}

La lecture se fait comme lcriture, en obtenant une instance sur un type ddi cette
tche. On retrouve le type file en Python, cette fois construit avec en second paramtre la chane "r" (pour read, lire). En C++, on utilise plutt le type std::ifstream (i
pour input, entre). Si vous remplacez mentalement lutilisation des objets fichier dans
ces programmes par les moyens usuels pour recevoir des donnes de lutilisateur, vous
constaterez une grande similarit avec ce que nous avions lors de ltude de linteractivit, notamment la saisie de plusieurs valeurs (voir la section 4 du Chapitre 4).

Program Livre Page 190 Vendredi, 16. mai 2008 8:13 08

190

Initiation la programmation avec Python et C++

Le problme essentiel consiste "dtecter" lorsquon arrive la fin du fichier. En effet,


cela naurait pas de sens de chercher lire au-del. Cest pour cela quon utilise une
boucle while pour la lecture : le fichier est lu ligne ligne (pour Python, en utilisant la
mthode readline() de linstance de file) ou information par information (pour
C++, en utilisant les chevrons comme nous le faisons habituellement avec std::cin),
tant que nous ne sommes pas la fin. Dans le cas de Python, il suffit de tester
linstance de file, test qui chouera ds quon aura termin la lecture. Dans le cas de
C++, on fait appel la mthode eof() de std::ifstream (pour End Of File, fin de
fichier), laquelle retourne vrai (true) ds que lon est effectivement la fin du fichier.
Incidemment, remarquez que nous navons absolument pas utilis les outils raliss
pour les Tours de Hano. Un bon exercice consisterait modifier ce programme, afin
de lui faire "simuler" la solution du jeu, comme nous lavons vu au Chapitre 10.

13.3. Apart : la ligne de commande


Probablement serez-vous tent un jour dcrire un programme qui lit les lignes dun
fichier, effectue une certaine opration sur chacune, puis crit un nouveau fichier rsultat
de la transformation. Cest ce quon appelle un filtre. Idalement, ce filtre pourrait fonctionner par la ligne de commande, de la mme faon que les commandes classiques que
sont copy, cd ou mkdir. Autrement dit, il doit tre possible de passer des paramtres au
programme, qui sont par nature inconnus au moment de son criture.
Il existe un moyen assez simple pour cela, tant en Python quen C++. Modifions le
programme dcriture prcdent, afin quil accepte en paramtre le nombre de disques
et le fichier devant les stocker :
Python

C++

# -*- coding: utf8 -*import sys


import hanoi
hauteur_max = int(sys.argv[1])
le_jeu = hanoi.Hanoi()
le_jeu.Initialiser(hauteur_max);
solution = []
le_jeu.Solution(solution)
fichier = file(sys.argv[2], "w")
for mouvement in solution:
print >> fichier, \
mouvement[0]+1, \

#include <iostream>
#include <fstream>
#include <cstdlib>
#include "hanoi.h"
int main(int argc, char* argv[])
{
int nb_disques = atoi(argv[1]);
Hanoi jeu;
jeu.Initialiser(nb_disques);
std::vector<std::pair<int,
int> > solution;

Program Livre Page 191 Vendredi, 16. mai 2008 8:13 08

Chapitre 13

Python

Stockage de masse : manipuler les fichiers

191

C++

mouvement[1]+1
fichier.close()

jeu.Solution(solution);
std::ofstream fichier(argv[2]);
/*

Exemple dutilisation (solution pour trois


disques stocke dans un fichier soluce.txt) :
$ python ecriture.py 3 soluce.txt

inchang...
*/
return 0;
}

Exemple dutilisation (solution pour trois


disques stocke dans un fichier soluce.txt) :
$ ./ecriture 3 soluce.txt

ou sur Windows :
> ecriture 3 soluce.txt

Les lignes modifies ont t mises en vidence. Et vous voyez maintenant le sens des
paramtres argc et argv passs la fonction principale main() en C++.
Le paramtre argv est, strictement parler, un tableau de pointeurs sur donnes de type
char, soit un tableau de tableaux de caractres autrement dit, un tableau de chanes
de caractres "brutes", ne fournissant aucune des facilits du type std::string que
nous utilisons depuis le dbut. Chacune de ces chanes contient lun des paramtres
passs au programme lors de son excution. Lquivalent en Python est le tableau de
chanes de caractres argv, auquel on accde par le module sys. Le paramtre C++
argc, quant lui, contient le nombre de paramtres. Normalement, ce nombre vaut
toujours au moins 1, le premier paramtre (celui dindice 0) tant, par convention, le
nom du programme lui-mme. Pour retrouver cette valeur en Python, il suffit de
demander la taille du tableau par len(sys.argv).
Chacun des lments de ces tableaux peut donc tre vu comme une chane de caractres.
Or, dans notre cas, nous attendons en premier paramtre un nombre entier. Si cela est
simple en Python, par lutilisation de la pseudo-fonction int(), en C++ nous devons faire
appel une fonction de conversion spcialise pour ce genre de situation : la fonction
atoi(), qui attend en paramtre un type char* (une chane de caractres standard du
langage C) et retourne un entier. Cette fonction est disponible dans len-tte <cstdlib>.
Naturellement, dans un vritable programme, il conviendrait de vrifier quon ne cherche pas obtenir plus de paramtres quil nen a t pass. Si vous ne donnez quun
seul paramtre aux programmes prcdents, ceux-ci planteront coup sr.

Program Livre Page 192 Vendredi, 16. mai 2008 8:13 08

Program Livre Page 193 Vendredi, 16. mai 2008 8:13 08

14
Le temps qui passe
Au sommaire de ce chapitre :

Ralisation dun chronomtre

Animation

Program Livre Page 194 Vendredi, 16. mai 2008 8:13 08

194

Initiation la programmation avec Python et C++

Comme annonc au Chapitre 12, nous allons ajouter la version graphique des Tours
de Hano laffichage de la solution au problme. Une faon de faire serait de tout
simplement simuler les dplacements en mettant jour laffichage aprs chacun deux.
Mais cela poserait au moins deux problmes :

Des changements limits la position dun unique disque dune image lautre
sont assez difficiles suivre pour lil humain : un tel affichage ne serait donc pas
trs attrayant, voire pas suffisamment dmonstratif pour lutilisateur.

Plus grave, les machines actuelles sont tellement puissantes et performantes que
les diffrentes images se succderaient une vitesse bien suprieure ce que lil
humain est capable de suivre : un utilisateur ne verrait quun bref clignotement
entre le dbut et la fin de la dmonstration.

Bref, il faut faire mieux que cela. Une ide consiste alors animer les dplacements
des disques, cest--dire proposer une simulation plus proche de la ralit. Mais qui
dit animation, dit succession dimages dans le temps Nous devons donc avoir une
certaine matrise du temps.
Dans le monde de la programmation, la notion de temps est parfois assez floue et
incertaine. Notamment dans le cas de systmes dexploitation qui "font comme si"
ils permettaient plusieurs programmes de sexcuter simultanment. En ralit,
sur une machine ne disposant que dun unique processeur, un seul programme
sexcute un instant donn. Sur une machine quipe de deux processeurs ou dun
processeur double cur, deux programmes au plus peuvent sexcuter simultanment. Et on entend ici "programme" au sens large, ce qui inclut le systme luimme.
Ce qui se passe en ralit, cest que le systme "prend la main" intervalles rguliers
et attribue une certaine quantit de temps chacun des programmes qui sexcutent,
lun aprs lautre. Cette opration est tellement rapide quelle passe presque inaperue
(sauf dans le cas dune activit particulirement intense). Mais cela signifie surtout
que le temps coul entre le moment o votre programme dmarre et le moment o il
sarrte, nest pas quivalent au temps "rel" durant lequel son code a effectivement
t excut le second est gnralement infrieur au premier.
Un mot justement, au sujet du terme temps rel. Un systme est dit temps rel si son
bon fonctionnement dpend de rponses devant imprativement tre donnes dans
un intervalle de temps prcis, et ce quelle que soit la charge du systme. Cet intervalle nest pas ncessairement court (bien que cela soit souvent le cas). Par exemple,
sur une voiture, le systme vitant le blocage des roues en cas de freinage brutal est

Program Livre Page 195 Vendredi, 16. mai 2008 8:13 08

Chapitre 14

Le temps qui passe

195

un systme temps rel : quelles que soient les tches en cours dexcution sur le
calculateur, celui-ci doit imprativement donner une rponse en un temps donn et
fix.
Cette notion ne doit pas tre confondue avec celle de haute performance. Celle-ci
consiste effectuer un grand nombre de calculs en un minimum de temps, sans que
lexactitude du rsultat dpende de ce temps. Les jeux dits temps rel nont (le plus
souvent) rien de temps rel : ils sefforcent dutiliser au mieux la puissance disponible
pour fournir des ractions aussi rapides que possible. Mais que la charge du systme
augmente, par exemple du fait dune autre application consommatrice de puissance, et
les temps de rponse du jeu seront inexorablement augments.
Fermons cette parenthse pour nous pencher sur notre problme.

14.1. Ralisation dun chronomtre


Avant de nous attaquer au gros morceau du programme des Tours de Hano, commenons modestement par la ralisation dun simple chronomtre : un bouton pour dmarrer,
un bouton pour arrter et laffichage du temps coul. La Figure 14.1 montre le
programme que nous allons raliser.
Figure 14.1
Un chronomtre
lmentaire.

Lorsque lutilisateur clique sur le bouton Go, le programme dclenche un minuteur,


un objet tout fait comparable ces horribles instruments quon trouve communment dans les cuisines : vous demandez un temps dattente, subissez le tic-tac, puis
sursautez linfernale sonnerie. La classe QTimer de Qt propose une fonction
semblable, sauf quelle est beaucoup plus prcise (de lordre de quelques dizaines de
millisecondes) et que la sonnerie prend la forme dun signal auquel le programme va
ragir. Les calculs sur des dures sont, quant eux, effectus laide de la classe
QTime.

Program Livre Page 196 Vendredi, 16. mai 2008 8:13 08

196

Initiation la programmation avec Python et C++

Voici le code du programme chrono, limit la classe contenant linterface :


Python

C++

class Chrono(QtGui.QWidget):
def __init__(self, \
le_jeu, \
parent=None):
QtGui.QWidget.__init__(self, parent)
self.colonne = \
QtGui.QVBoxLayout(self)
self.affichage = \
QtGui.QLCDNumber(12, self)
self.colonne.addWidget( \
self.affichage)
self.bt_go = \
QtGui.QPushButton("Go!", self)
self.colonne.addWidget(self.bt_go)
self.connect(self.bt_go, \
QtCore.SIGNAL("clicked()"), \
self.Go)
self.bt_stop = \
QtGui.QPushButton("Stop", self)
self.colonne.addWidget(self.bt_stop)
self.connect(self.bt_stop, \
QtCore.SIGNAL("clicked()"), \
self.Stop)
self.chrono = QtCore.QTimer(self)
self.connect(self.chrono, \
QtCore.SIGNAL("timeout()"), \
self.timeout)
def Go(self):
self.debut = \
QtCore.QTime.currentTime()
self.chrono.start(20)
def Stop(self):
self.chrono.stop()
self.timeout()
def timeout(self):
maintenant = \
QtCore.QTime.currentTime()
ecart = \
QtCore.QTime().addMSecs( \

Chrono::Chrono(QWidget* parent):
QWidget(parent),
chrono(0),
affichage(0)
{
QVBoxLayout* colonne =
new QVBoxLayout(this);
this->affichage =
new QLCDNumber(12, this);
colonne->addWidget(this->affichage);
QPushButton* bt_go =
new QPushButton("Go!", this);
colonne->addWidget(bt_go);
connect(bt_go,
SIGNAL(clicked()),
this,
SLOT(Go()));
QPushButton* bt_stop =
new QPushButton("Stop", this);
colonne->addWidget(bt_stop);
connect(bt_stop,
SIGNAL(clicked()),
this,
SLOT(Stop()));
this->chrono = new QTimer(this);
connect(this->chrono,
SIGNAL(timeout()),
this,
SLOT(timeout()));
}
void Chrono::Go()
{
this->debut = QTime::currentTime();
this->chrono->start(20);
}
void Chrono::Stop()
{
this->chrono->stop();
this->timeout();
}

Program Livre Page 197 Vendredi, 16. mai 2008 8:13 08

Chapitre 14

Python
self.debut.msecsTo(maintenant))
self.affichage.display( \
ecart.toString("m:ss:zzz"))

Le temps qui passe

197

C++
void Chrono::timeout()
{
QTime maintenant =
QTime::currentTime();
QTime ecart = QTime().addMSecs(
this->debut.msecsTo(maintenant));
this->affichage->display(
ecart.toString("m:ss:zzz"));
}

Le signal qui nous intresse dans la classe QTimer est timeout(), mis lorsque la
dure demande est coule et connect une mthode ponyme dans la classe
Chrono. Le minuteur est dclench par un appel la mthode start(), laquelle prend
en paramtre la dure dattente en millisecondes. Si vous examinez le code de la
mthode Go() de la classe Chrono, vous voyez quon demande une dure de 20 millisecondes, autrement dit on souhaite afficher la dure coule 50 fois par seconde.
Dans la ralit, il est assez peu probable quon obtienne un rythme aussi lev, encore
moins de faon fiable. Les systmes dexploitation auxquels nous nous intressons ne
sont pas des systmes temps rel. Cela signifie que le systme va "faire de son mieux"
pour nous dlivrer un message (un signal) toutes les vingt millisecondes, mais sans
aucune garantie.
La consquence est quon ne peut pas savoir avec certitude quel rythme notre
mthode timeout() sera invoque. Autrement dit, on ne peut pas calculer le temps
coul simplement en ajoutant vingt millisecondes un compteur.
Cest l quintervient la classe QTime. Au moment o on dmarre le minuteur, on
mmorise lheure courante (mthode currentTime() de QTime, utilisable sans crer
dinstance). Lorsque la mthode timeout() est invoque, on calcule le temps coul
en millisecondes entre la nouvelle heure courante et celle qui a t mmorise. Avec
les outils dont nous disposons, cest la seule faon davoir une mesure qui ne soit pas
trop entache derreurs.
Accessoirement, remarquez lutilisation de la classe QLCDNumber, laquelle propose un
affichage de type montre quartz.

Program Livre Page 198 Vendredi, 16. mai 2008 8:13 08

198

Initiation la programmation avec Python et C++

14.2. Animation
Fort de notre exprience, nous pouvons maintenant attaquer le gros morceau : modifier
le programme des Tours de Hano, pour enfin pouvoir montrer la solution lutilisateur.
La Figure 14.2 montre le programme alors quil est en train dafficher cette solution.
Remarquez que les boutons permettant de choisir une tour ont t dsactivs.
Figure 14.2
Une solution anime
pour Hano.

Pour commencer, nous allons mmoriser les positions de chaque disque dans un
tableau. Cela nous permettra par la suite de savoir prcisment o chacun se trouve et
de facilement modifier cette position. La mthode Mettre_A_Jour() de la classe
Dessin se trouve rduite simplement calculer ces positions (version Python) :
def Mettre_A_Jour(self):
if ( self.anim_chrono.isActive() ):
self.anim_chrono.stop()
self.emit(QtCore.SIGNAL("Fin_Deplacement"))
for tour in range(3):
nb_disques = self.jeu.Une_Tour(tour).Nombre_Disques()
for etage in range(nb_disques):
disque = self.jeu.Une_Tour(tour).Disque_Etage(etage)
centre = self.Centre_Etage(tour, etage)
self.positions[disque-1] = list(centre)
self.Dessiner()

Laissons de ct pour linstant les trois premires lignes de cette mthode modifie,
bien que le nom de la variable anim_chrono vous laisse probablement dj deviner
certains changements venir. Voyez comment les positions des disques sont mmorises
dans le tableau positions.
Le dessin effectif de limage a t dplac dans une mthode Dessiner(), qui ressemble beaucoup lancienne mthode Mettre_A_Jour() : son contenu est pratiquement

Program Livre Page 199 Vendredi, 16. mai 2008 8:13 08

Chapitre 14

Le temps qui passe

199

le mme, la seule diffrence est quelle parcourt le tableau positions plutt que de
calculer le centre de chaque disque. Voici un extrait de son contenu (en C++) :
void Dessin::Dessiner()
{
this->image.fill(couleur_fond);
/* lignes identiques non recopies... */
// dessin des disques
p.setPen(Qt::black);
p.setBrush(couleur_disques);
for(int disque = 0; disque < this->jeu.Hauteur(); ++disque)
this->Dessiner_Disque(disque+1,
this->positions[disque],
p);
p.end();
}

Lanimation elle-mme est rpartie en deux mthodes. Une premire,


Deplacer_Disque(), lance lanimation ainsi quun minuteur, comme pour le chronomtre de la section prcdente. La seconde, timeout(), sera invoque priodiquement
par ce minuteur pour modifier la position courante du disque en cours de dplacement
et mettre jour limage. Voici Deplacer_Disque(), en Python :
def Deplacer_Disque(self, tour_depart, tour_arrivee):
self.anim_disque = self.jeu.Une_Tour(tour_depart).Sommet() - 1
self.anim_depart = tuple(self.positions[self.anim_disque])
etage_arrivee = \
self.jeu.Une_Tour(tour_arrivee).Nombre_Disques()
self.anim_arrivee = self.Centre_Etage(tour_arrivee, etage_arrivee)
self.anim_deplacement = (0, -1)
self.anim_chrono.start(10)

Le membre donne de la classe Dessin nomm anim_disque contient le disque en


cours de dplacement, cest--dire en fait sa taille. Dans anim_depart, on mmorise
les coordonnes de ce disque au dbut de lanimation, tandis quanim_arrivee reoit
les coordonnes "cibles" auxquelles on veut amener ce disque. La donne
anim_deplacement, pour sa part, contient le dplacement effectuer pour obtenir la
nouvelle position du disque au cours de lanimation : dplacement horizontal et dplacement vertical. Initialement, le disque va monter le long de laiguille : le dplacement
horizontal est donc nul, tandis que le dplacement vertical est ngatif (rappelez-vous
que la coordonne verticale va de haut en bas). Ensuite, il sera dplac latralement,
jusqu se positionner au-dessus de laiguille de destination (dplacement (1,0) ou
(-1,0), selon quil va vers la droite ou vers la gauche). Enfin, il descendra le long de
cette aiguille (dplacement (0,1)).

Program Livre Page 200 Vendredi, 16. mai 2008 8:13 08

200

Initiation la programmation avec Python et C++

Cette donne anim_deplacement permet donc de savoir "o nous en sommes". Elle
est utilise cette fin dans la mthode timeout(), ventuellement modifie, chaque
tape de lanimation. La dernire ligne de la mthode Deplacer_Disque() montre
quon demande que le minuteur mette un signal toutes les 10 millisecondes ce qui
a fort peu de chances darriver, en fait.
Voici les actions qui sont effectues chaque tape de lanimation :
void Dessin::timeout()
{
QPoint distance = this->positions[this->anim_disque] this->anim_arrivee;
if ( this->anim_deplacement.y() < 0 )
{
// dplacement vertical vers le haut
if ( this->positions[this->anim_disque].y() <=
marge + hauteur_disque )
{
// on est en haut, on prpare le dplacement latral
this->positions[this->anim_disque].ry() = marge + hauteur_disque;
if ( this->anim_depart.x() < this->anim_arrivee.x() )
this->anim_deplacement = QPoint(1, 0);
else
this->anim_deplacement = QPoint(-1, 0);
}
else
this->positions[this->anim_disque] += this->anim_deplacement;
}
else if ( this->anim_deplacement.y() == 0 )
{
// dplacement latral
if ( std::abs(distance.x()) < 2 )
{
// fin du dplacement latral, on descend le disque
this->positions[this->anim_disque].rx() = this->anim_arrivee.x();
this->anim_deplacement = QPoint(0, 1);
}
else
this->positions[this->anim_disque] += this->anim_deplacement;
}
else
{
// dplacement vertical vers le bas
if ( (std::abs(distance.x())+std::abs(distance.y())) < 2 )
{
// dplacement termin
this->anim_chrono->stop();
this->positions[this->anim_disque] = this->anim_arrivee;
emit Fin_Deplacement();
}

Program Livre Page 201 Vendredi, 16. mai 2008 8:13 08

Chapitre 14

Le temps qui passe

201

else
this->positions[this->anim_disque] += this->anim_deplacement;
}
this->Dessiner();
this->update();
}

On commence par calculer le chemin quil reste parcourir en calculant la diffrence


entre la position actuelle du disque et la position cible tablie prcdemment. Puis on
dplace le disque en modifiant sa position courante, ou alors on modifie le dplacement
pour passer dune tape lautre.
Ce code peut sembler compliqu mais, si vous le suivez calmement, vous verrez quil
ne lest pas tant que cela. La partie la plus intressante est la ligne contenant le mot
emit. Ce nest rien dautre que le moyen propos par Qt pour mettre explicitement un
signal (lequel doit avoir t pralablement dclar dans une section signals: au sein
de la classe, dans le cas du langage C++). Cette instruction est ici utilise pour littralement signaler que le dplacement du disque est termin, donc que lanimation est
arrive son terme. Le mme signal est mis dans la mthode Mettre_A_Jour(),
dont nous avons vu la version en Python au dbut de cette section. Nous allons voir
comment ce signal est utilis par le reste de lapplication.
Car nous devons maintenant modifier lgrement la classe Hanoi_Gui, qui contient
lensemble de linterface, ne serait-ce que pour bnficier de la nouvelle fonctionnalit
de Dessin : celle qui anime le dplacement dun disque. Un premier changement se
situe au sein de la mthode Tour_Choisie(), dans le cas o on a effectivement choisi
une tour darrive valide :
def Tour_Choisie(self):
if ( self.tour_depart < 0 ):
# on a choisi la tour de dpart
# lignes identiques non recopies...
else:
# on a choisi la tour darrive
tour_arrivee = self.Bouton_Tour_Choisie(self.sender())
if ( tour_arrivee!= self.tour_depart ):
if ( self.Jeu().Tour_Arrivee_Valide(self.tour_depart, \
tour_arrivee) ):
self.dessin.Deplacer_Disque(self.tour_depart, tour_arrivee)
self.Jeu().Deplacer_Disque(self.tour_depart, tour_arrivee)
self.nb_mouvements += 1
else:
# etc.

On ne se contente plus de mettre jour le dessin : dsormais, on lui demande de dplacer


le disque, cest--dire de faire fonctionner lanimation.

Program Livre Page 202 Vendredi, 16. mai 2008 8:13 08

202

Initiation la programmation avec Python et C++

Mais la vritable volution se situe dans la mthode Solution(), charge de montrer


la solution lutilisateur. Comme nous lavions fait dans la version en pur mode texte
(voir le Chapitre 10), cette mthode commence par calculer la solution pour la mmoriser dans un tableau (version en C++) :
void Hanoi_Gui::Solution()
{
this->jeu.Initialiser(this->jeu.Hauteur());
this->dessin->Mettre_A_Jour();
this->jeu.Solution(this->solution);
this->nb_mouvements = 0;
this->bt_tour_1->setEnabled(false);
this->bt_tour_2->setEnabled(false);
this->bt_tour_3->setEnabled(false);
this->en_simulation = true;
this->Fin_Deplacement();
}

Les boutons de choix des tours sont dsactivs (grce la mthode setEnabled()
issue de QWidget), manire simple dviter une interfrence de lutilisateur durant
lanimation. La donne en_simulation est de type boolen (valant soit vrai soit faux)
et elle nest utilise que pour indiquer que lon se trouve dans un tat particulier,
savoir la simulation du jeu pour afficher la solution. Lutilisation de variables telles
que celle-ci pour savoir tout instant dans quel "tat" se trouve un programme est une
technique couramment utilise, qui vite bien souvent de sombrer dans des abmes de
rflexion face une situation complexe. Nhsitez donc pas en faire usage.
Le compteur du nombre de mouvements, nb_mouvements, va tre utilis pour parcourir le tableau contenant la liste des mouvements de la solution (tableau contenu dans la
variable solution). Le lancement rel de la simulation est effectu par un appel
Fin_Deplacement(), que voici :
void Hanoi_Gui::Fin_Deplacement()
{
if ( not this->en_simulation )
return;
if ( this->Jeu().Jeu_Termine() )
{
this->en_simulation = false;
this->bt_tour_1->setEnabled(true);
this->bt_tour_2->setEnabled(true);
this->bt_tour_3->setEnabled(true);
}
else
{
std::pair<int, int> deplacement =
this->solution[this->nb_mouvements];

Program Livre Page 203 Vendredi, 16. mai 2008 8:13 08

Chapitre 14

Le temps qui passe

203

this->dessin->Deplacer_Disque(deplacement.first, deplacement.second);
this->Jeu().Deplacer_Disque(deplacement.first, deplacement.second);
this->nb_mouvements += 1;
}
}

Cette mthode est connecte au signal ponyme de la classe Dessin : elle est donc
invoque lissue de chaque animation du dplacement dun disque. Dans notre exemple, si on nest pas en train dafficher la solution du jeu, aucune action particulire nest
effectuer : cest l quentre en jeu la donne en_simulation, comme vous pouvez le
constater sur les deux premires lignes.
Ensuite, si le jeu nest pas termin (autrement dit, si tous les disques nont pas t
dplacs, faon de dtecter la fin de la simulation), on recherche le dplacement
suivant partir du tableau solution et du compteur nb_mouvements. Ce dplacement
est simul, autant pour le dessin que pour le jeu.
Lvolution dans les mouvements est assure par le signal Fin_Deplacement(), mis
par la classe Dessin, lissue dun dplacement. Cest pourquoi laffichage de la solution nutilise pas de boucle en tant que telle, contrairement la version texte du Chapitre 10. Vous voyez l une traduction du fait que la programmation graphique est une
programmation dite vnementielle. Ici, lvnement est lexpiration dun dlai, dune
attente contrle par un minuteur. Cet vnement en dclenche dautres, qui leur tour
finissent par aboutir au relancement de ce minuteur. On a bien ainsi une boucle (une
mme srie dactions rptes plusieurs reprises), bien quelle ne soit pas explicitement
crite.

Program Livre Page 204 Vendredi, 16. mai 2008 8:13 08

Program Livre Page 205 Vendredi, 16. mai 2008 8:13 08

Partie

Pour aller plus loin


CHAPITRE 15. Rassembler et diffuser : les bibliothques
CHAPITRE 16. Introduction OpenGL
CHAPITRE 17. Aperu de la programmation parallle
CHAPITRE 18. Aperu dautres langages
CHAPITRE 19. Le mot de la fin

Program Livre Page 206 Vendredi, 16. mai 2008 8:13 08

Program Livre Page 207 Vendredi, 16. mai 2008 8:13 08

15
Rassembler et diffuser :
les bibliothques
Au sommaire de ce chapitre :

Hirarchie dun logiciel

Dcoupage de Hano

Program Livre Page 208 Vendredi, 16. mai 2008 8:13 08

208

Initiation la programmation avec Python et C++

Au cours des chapitres prcdents, mesure que nous avancions dans la ralisation de
notre programme, nous avons dment dupliqu les parties communes en particulier,
la classe Hanoi qui contient le moteur interne du jeu, indpendamment de toute interface.
Au fil du temps, vous crerez vous-mme des outils (fonctions ou classes) que vous
serez amen rutiliser dans vos nouveaux projets. Il est gnralement prfrable de
sappuyer sur des outils existants, plutt que de sans cesse "rinventer la roue". De
plus, si vous utilisez un seul et mme outil (ou un seul et mme ensemble doutils)
dans plusieurs de vos programmes, toute correction ou amlioration en son sein profitera automatiquement tous ces programmes : la maintenance et mme lvolutivit
sen trouvent grandement facilites.
Ce double besoin, rutiliser le travail dj effectu et mutualiser les oprations de
maintenance, a donn naissance la notion de bibliothque. Au sens "programmation"
du terme, une bibliothque est un ensemble de fonctions et de classes rassembles en
un objet unique et cohrent, lesquelles fonctions et classes peuvent tre utilises par
diffrents programmes, voire dautres bibliothques. Par exemple, Qt est une bibliothque de classes en langage C++ : nous avons utilis ces classes, sans pour autant
devoir dupliquer quelque partie de code source de Qt que ce soit.
Techniquement, une bibliothque est compose (en gnral) dune partie binaire,
ressemblant un fichier excutable et donc incomprhensible, et dune partie "visible"
qui sera utilise par les clients de la bibliothque pour exploiter ses services. Cette
partie visible est dsigne sous le terme dAPI, pour Application Programming Interface, en franais interface de programmation. En C++, une API se prsente sous la
forme de fichiers den-tte.
Ce chapitre concerne essentiellement le langage C++ et la compilation dune bibliothque grce aux facilits offertes par Qt. Pour Python, pratiquement tout fichier peut
tre vu comme une minibibliothque, ce que lon nomme module dans le jargon
propre Python. Pour lutiliser en tant que telle, il suffit gnralement de copier les
fichiers un endroit appropri dpendant du systme.
Nous allons rcuprer le travail effectu au cours des chapitres prcdents, afin de
transformer les lments de notre programme des Tours de Hano en autant de bibliothques. Si cela est certainement inutile sur un programme aussi restreint, limportant
est lide sous-jacente et la technique utilise.

Program Livre Page 209 Vendredi, 16. mai 2008 8:13 08

Chapitre 15

Rassembler et diffuser : les bibliothques

209

15.1. Hirarchie dun logiciel


Tout logiciel non trivial est organis de faon hirarchique, le programme principal
dpendant de bibliothques elles-mmes dpendant dautres bibliothques. La
Figure 15.1 prsente la hirarchie que nous allons mettre en place pour le programme
des Tours de Hano, les flches se lisant " dpend de". La notion de dpendance
est importante lorsquon manipule des bibliothques : dire que A dpend de B signifie
que A utilise des fonctions fournies par B, donc, incidemment, que A ne peut fonctionner sans B. Par ailleurs, Qt a t insr au schma comme rfrence, bien quelle
ne fasse pas partie elle-mme de "nos" bibliothques.
Figure 15.1
Hirarchie des bibliothques
pour le programme Hano.

hanoi_lib

hanoi_txt

qt

hanoi_gui

hanoi

Rptons-le, cet exemple est extrmement simple. On peut donc sy retrouver sans
difficult. Mais dans un programme de plus grande envergure, une telle hirarchie peut
rapidement devenir trs complexe. Si on ny prend garde, on peut mme arriver la
situation o deux bibliothques sont interdpendantes, cest--dire o une bibliothque A dpend dune bibliothque B laquelle, en mme temps, dpend de A. Mais
ce qui arrive le plus souvent est lapparition dun cycle de dpendances, o plusieurs
bibliothques dpendent indirectement les unes des autres, formant une boucle infernale. La Figure 15.2 illustre ces deux cas.
Figure 15.2
Exemples dinterdpendances.

inter-dpendance
directe

cycle de
dpendances

biblio_1

biblio_1

biblio_2

biblio_2

biblio_3

Program Livre Page 210 Vendredi, 16. mai 2008 8:13 08

210

Initiation la programmation avec Python et C++

Inutile de prciser que, dans ce genre de situations, on se retrouve rapidement


confront des problmes inextricables, comparables la grande question de savoir
qui de luf ou de la poule est apparu le premier. Aussi, prenez soin dviter cela dans
vos dveloppements.

15.2. Dcoupage de Hano


Le dcoupage que nous allons effectuer est le suivant :

hanoi_lib (le suffixe lib signifiant library, langlais de bibliothque) contiendra


la classe Hanoi, le moteur interne du jeu.

hanoi_txt contiendra linterface en mode texte Hanoi_Txt.

hanoi_gui contiendra linterface en mode graphique Hanoi_Gui.

Enfin, hanoi sera tout simplement le programme principal qui fera usage des
bibliothques prcdentes.

Vous laurez compris, nous allons obtenir un programme pouvant sexcuter aussi bien en
mode texte quen mode graphique. La Figure 15.3 montre comment les fichiers vont
tre rpartis en diffrents rpertoires.
Figure 15.3
Rpartition des fichiers
pour un programme
Hano modulaire.

Program Livre Page 211 Vendredi, 16. mai 2008 8:13 08

Chapitre 15

Rassembler et diffuser : les bibliothques

211

Dans un rpertoire quelconque, crez quatre sous-rpertoires nomms comme les


bibliothques que nous allons crer. Crez galement un fichier, nomm hanoi.pro,
ayant le contenu suivant :
TEMPLATE = subdirs
SUBDIRS = hanoi_lib \
hanoi_txt \
hanoi_gui \
hanoi

Il sagit dun fichier projet "matre" (la variable TEMPLATE vaut subdirs), dont le seul
rle est de construire les sous-projets rfrencs. Chacun des sous-projets se trouve
dans un rpertoire, dont le nom est donn la variable SUBDIRS. Dans chacun de ces
rpertoires, lutilitaire qmake va chercher un fichier projet portant le mme nom (mais
avec lextension .pro, naturellement). Lordre a son importance : les sous-projets
seront compils dans lordre o ils apparaissent, qui dcoule des dpendances entre les
bibliothques. Cela naurait en effet pas de sens de chercher compiler hanoi_gui
avant hanoi_lib, sachant que la premire a besoin de la seconde.
Voici par exemple le contenu du fichier hanoi_lib.pro, dans le sous-rpertoire
hanoi_lib :
TEMPLATE = lib
CONFIG -= qt
CONFIG += release console
MOC_DIR = .moc
OBJECTS_DIR = .objs
DESTDIR = ../bin
HEADERS += hanoi.h
SOURCES += hanoi.cpp

Cette fois-ci, la variable TEMPLATE vaut lib, la valeur quil faut donner lorsquon
souhaite crer une bibliothque. La ligne CONFIG-=qt a pour effet de "retirer" la
bibliothque Qt des dpendances du projet : en effet, cette bibliothque de base
nutilise aucun des services de Qt. Enfin, la ligne DESTDIR indique dans quel rpertoire on va crer le fichier bibliothque (qui nest pas un fichier excutable), en
donnant un chemin relatif au rpertoire dans lequel se trouve le fichier projet. Autrement dit, le rsultat de la compilation se retrouvera dans un rpertoire bin voisin des
quatre rpertoires que vous venez de crer.
Petite remarque concernant la spcification de chemins. Il est vivement recommand de
toujours utiliser la notation Unix pour sparer les noms des rpertoires, donc de toujours
utiliser la barre oblique (ou slash) /, mme si vous vous trouvez sur un systme

Program Livre Page 212 Vendredi, 16. mai 2008 8:13 08

212

Initiation la programmation avec Python et C++

Windows (qui utilise normalement la barre inverse \, ou backslash). Le compilateur,


tout comme qmake, sadaptera de lui-mme.
Voyons maintenant le fichier projet pour construire linterface texte :
TEMPLATE = lib
CONFIG -= qt
CONFIG += release console
MOC_DIR = .moc
OBJECTS_DIR = .objs
LIBS += -L../bin -lhanoi_lib
DESTDIR = ../bin
INCLUDEPATH += ..
HEADERS += hanoi_txt.h
SOURCES += hanoi_txt.cpp

On retrouve les mmes lments que prcdemment avec, en plus, deux nouvelles
variables : INCLUDEPATH et LIBS.
La premire permet dindiquer au compilateur les rpertoires dans lesquels chercher les
fichiers den-tte. En effet, le fichier den-tte hanoi.h, qui contient lAPI (la partie
visible et utilisable par dautres) de la bibliothque hanoi_lib, ne se trouve plus dans
le mme rpertoire. Une convention parmi dautres, applique ici, consiste indiquer,
dans le code source, le fichier den-tte prcd du rpertoire qui le contient, lequel
rpertoire se trouve galement tre le nom de la bibliothque laquelle correspond cet
en-tte. Ainsi, le dbut du fichier hanoi_txt.h ressemble ceci :
#ifndef HANOI_TXT_H__
#define HANOI_TXT_H__ 1
#include <string>
#include <hanoi_lib/hanoi.h>
class Hanoi_Text
{
public:
Hanoi_Text(Hanoi& jeu);
/*
etc.
*/
}; // Hanoi_Text
#endif // HANOI_TXT_H__

Pour trouver effectivement len-tte hanoi.h, il est en fait ncessaire de "remonter"


dun cran dans les rpertoires, par rapport lendroit o se trouve hanoi_txt.h. Et
peut-tre voudrez-vous un jour dplacer la bibliothque hanoi_lib un endroit
compltement diffrent. Le fait de renseigner INCLUDEPATH permet de savoir
partir de quels rpertoires il convient de chercher les en-ttes. Ainsi, le compilateur

Program Livre Page 213 Vendredi, 16. mai 2008 8:13 08

Chapitre 15

Rassembler et diffuser : les bibliothques

213

cherchera en ralit <../hanoi_lib/hanoi.h>. Si vous dplacez hanoi_lib, il


suffira dadapter INCLUDEPATH, sans quil soit besoin de toucher au code source. Vous
pouvez rapprocher cette variable de la clbre variable denvironnement PATH, utilise
pour la recherche des fichiers excutables.
Remarquez, par ailleurs, que dans le code source nous utilisons galement la convention
Unix pour la sparation des noms des rpertoires.
La variable LIBS permet dindiquer les bibliothques dont dpend le projet. Elle suit
pour cela une syntaxe particulire : dabord lindication de rpertoires o chercher les
bibliothques, chaque rpertoire devant tre prcd de -L ; ensuite, les noms des
bibliothques, chacun prcd de -l (la lettre "l" en minuscule). Remarquez que lon
donne bien le nom de chaque bibliothque, non pas le nom du fichier qui la contient.
Ce nom de chaque bibliothque est dduit du nom du fichier projet.
Pour le programme principal, qui dpend des trois autres bibliothques, la ligne LIBS
dans le fichier hanoi.pro ressemble ceci :
LIBS += -L../bin -lhanoi_lib -lhanoi_txt -lhanoi_gui

Et voici le contenu abrg du programme principal :


#include <string>
#include <iostream>
#include <QApplication>
#include <QInputDialog>
#include <hanoi_lib/hanoi.h>
#include <hanoi_txt/hanoi_txt.h>
#include <hanoi_gui/hanoi_gui.h>
int main(int argc, char* argv[])
{
bool mode_texte = true;
if ( argc > 1 )
{
if ( std::string(argv[1]) == "gui" )
mode_texte = false;
}
if ( mode_texte )
{
// interface texte
}
else
{
// interface graphique
}
}

Program Livre Page 214 Vendredi, 16. mai 2008 8:13 08

214

Initiation la programmation avec Python et C++

Ne soyez pas effray par la profusion de directives #include au dbut : il est trs
commun den avoir beaucoup, surtout dans un logiciel de grande taille.
Voyez comme nous exploitons la ligne de commande, suivant ce que nous avons vu
la fin du Chapitre 13. Si on trouve le mot "gui" en premier paramtre, alors on utilise
linterface graphique fonde sur Qt. Sinon, on utilise linterface en mode texte.

Vous trouverez le code source complet sur le site web de Pearson France. Mais cest
un bon exercice que dessayer de remplir par vous-mme les blancs qui nont pas t
traits ici.
Une fois tout cela en place, vous pouvez vous placer dans le rpertoire qui contient
tous les autres (celui contenant le fichier projet rfrenant les sous-projets, le premier
que nous avons vu) et excuter tout simplement :
$ qmake
$ make

Remplacez make par mingw32-make si vous tes sous Windows. lissue dune
compilation qui devrait tre assez rapide, vous obtiendrez dans un nouveau sousrpertoire bin trois fichiers contenant les bibliothques, ainsi quun fichier excutable
hanoi. vous de le tester maintenant !

Program Livre Page 215 Vendredi, 16. mai 2008 8:13 08

16
Introduction OpenGL
Au sommaire de ce chapitre :

Installation

Triangle et cube de couleur

La GLU et autres bibliothques

Program Livre Page 216 Vendredi, 16. mai 2008 8:13 08

216

Initiation la programmation avec Python et C++

Tout ce que nous avons fait jusquici a consist "dessiner" dans un plan, comme sur une
feuille de papier. Ce qui semble tout fait naturel, tant donn que nos crans noffrent que
deux dimensions (sachez que des recherches sont en cours pour proposer des dispositifs
daffichage qui soient rellement en trois dimensions, cest--dire en volume).
Pourtant, que cela soit dans le monde des jeux vido ou dans celui de limage de
synthse (utilise pour les effets spciaux au cinma, par exemple), les exemples
abondent de programmes affichant des objets en trois dimensions sur un cran en deux
dimensions. Nous allons, au fil de ce chapitre, approcher lune des techniques disponibles
pour obtenir un tel rsultat.
De nos jours, il existe essentiellement deux grandes bibliothques concurrentes facilitant
grandement laffichage dobjets en volume (ou objets 3D) sur un cran :

Direct3D, bibliothque dfinie et dveloppe par Microsoft pour ses systmes


Windows et ses consoles de jeu, faisant partie de lensemble plus vaste DirectX
(voir http://msdn2.microsoft.com/en-us/directx/).

OpenGL, bibliothque dfinie initialement par SGI pour ses stations de travail et
dsormais maintenue par un consortium de plusieurs entreprises (dont Microsoft
sest retir il y a peu), destine tre utilisable sur (presque) toutes les platesformes (voir http://www.opengl.org).

Nous pourrions discuter pendant quelques centaines de pages des mrites et inconvnients de chacune de ces deux bibliothques, dans la continuit dune longue srie de
dbats parfois passionns entre les "pro-Direct3D" et les "pro-OpenGL", dbats qui
pouvaient parfois prendre lallure de guerre de religion. Nous nous pargnerons cette
peine, en constatant simplement que notre exigence initiale dcrire un programme
unique pouvant fonctionner sur plusieurs plates-formes nous impose delle-mme
lutilisation dOpenGL.
Il sagit donc dune bibliothque, dfinie en langage C, grce laquelle un programme
peut afficher des objets 3D sur un cran, qui est, par nature, une surface plane. La
bibliothque prend en charge la plupart des calculs (compliqus) permettant de projeter un point dfini dans un espace 3D une certaine position sur lcran, ainsi que bien
dautres aspects, comme de ne pas afficher les objets masqus par dautres ou reprsenter un effet dclairage par des sources lumineuses.
Depuis quelques annes, une part croissante de ces calculs est effectue directement
par la carte graphique plutt que par le processeur central de votre ordinateur. On parle
alors de carte graphique acclre 3D. La spcialisation du processeur graphique (ou

Program Livre Page 217 Vendredi, 16. mai 2008 8:13 08

Chapitre 16

Introduction OpenGL

217

GPU) fait quil devient possible dobtenir des performances inimaginables il ny a pas
si longtemps.
Mais voyons comment nous pouvons utiliser cette bibliothque OpenGL.

16.1. Installation
La bibliothque OpenGL et les lments ncessaires son utilisation en C++ sont
normalement toujours disponibles, pratiquement quel que soit le systme que vous
utilisez. Il nen va pas de mme avec Python : il est ncessaire dinstaller un petit
module, nomm PyOpenGL.
Si vous utilisez Windows ou Mac OS, tlchargez le fichier http://peak.telecommunity.com/dist/ez_setup.py. Excutez ensuite ce fichier :
$ python ez_setup.py

Il suffit ensuite dexcuter la commande suivante pour installer PyOpenGL :


$ easy_install PyOpenGL

Cela va prendre en charge le tlchargement et linstallation du module.


Si vous utilisez un systme Linux, installez simplement le paquetage PyOpenGL, il
devrait tre disponible quelle que soit votre distribution.

16.2. Triangle et cube de couleur


Prcisons ds maintenant que OpenGL est une bibliothque qui peut sembler assez
pauvre premire vue, en ce sens quelle ne fournit pas doutils de haut niveau pour
des oprations apparemment aussi simples que, par exemple, afficher un texte. Nous
nallons donc pas pouvoir saluer le monde pour nos premires exprimentations.
De plus, laffichage 3D lui-mme doit seffectuer dans un contexte OpenGL, lequel
doit tre fourni par le systme graphique du systme dexploitation. Chaque systme
proposant naturellement ses propres fonctions pour obtenir ce contexte, ce qui complique
encore la tche.
Heureusement, la bibliothque Qt propose un widget spcialement destin laffichage utilisant OpenGL, par la classe QGLWidget. Nous pouvons donc obtenir aisment un contexte OpenGL, une zone rectangulaire lcran dans laquelle nous

Program Livre Page 218 Vendredi, 16. mai 2008 8:13 08

218

Initiation la programmation avec Python et C++

pourrons "dessiner" en trois dimensions, simplement en drivant cette classe et en


surchargeant quelques mthodes.

16.2.1. Le triangle
Comme premier exemple, voici comment afficher un simple triangle en OpenGL en
nous basant sur QGLWidget :
Python

C++

# -*- coding: utf8 -*import sys


from PyQt4 import QtCore, QtGui,QtOpenGL
from OpenGL.GL import *
class Triangle(QtOpenGL.QGLWidget):
def __init__(self, parent):
QtOpenGL.QGLWidget.__init__(self, \
parent)

#include "triangle.h"
Triangle::Triangle(QWidget* parent):
QGLWidget(parent)
{
}
void Triangle::initializeGL()
{
glEnable(GL.GL_DEPTH_TEST);
}
void Triangle::resizeGL(int w, int h)
{
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
}
void Triangle::paintGL()
{
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
glClear(GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glShadeModel(GL_SMOOTH);
glBegin(GL_TRIANGLES);
glColor3d(1.0, 0.0, 0.0);
glVertex3d(0.0, 0.0, 0.0);
glColor3d(0.0, 1.0, 0.0);
glVertex3d(1.0, 0.0, 0.0);
glColor3d(0.0, 0.0, 1.0);
glVertex3d(0.0, 1.0, 0.0);
glEnd();
}

def initializeGL(self):
glEnable(GL_DEPTH_TEST)
def resizeGL(self, w, h):
glViewport(0, 0, w, h)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
def paintGL(self):
glClearColor(0.0, 0.0, 0.0, 1.0)
glClear(GL_COLOR_BUFFER_BIT | \
GL_DEPTH_BUFFER_BIT)
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
glShadeModel(GL_SMOOTH)
glBegin(GL_TRIANGLES)
glColor3d(1.0, 0.0, 0.0)
glVertex3d(0.0, 0.0, 0.0)
glColor3d(0.0, 1.0, 0.0)
glVertex3d(1.0, 0.0, 0.0)
glColor3d(0.0, 0.0, 1.0)
glVertex3d(0.0, 1.0, 0.0)
glEnd()

Program Livre Page 219 Vendredi, 16. mai 2008 8:13 08

Chapitre 16

Introduction OpenGL

219

Nous navons montr ici que le contenu dune classe Triangle, laquelle drive de
QGLWidget. Remarquez les modules que nous importons par rapport aux programmes
prcdents en Python : OpenGL.GL pour les fonctions OpenGL elles-mmes,
QtOpenGL ( partir de PyQt4) pour la classe QGLWidget. Ct C++, une simple ligne
#include <QGLWidget> suffit dans le fichier den-tte correspondant cette classe
Triangle (essayez de reconstruire vous-mme cet en-tte, cest un bon exercice). Par
contre, toujours pour le C++, il est ncessaire de prciser, dans le fichier projet, que
nous allons utiliser les fonctionnalits OpenGL, par les variables CONFIG et QT :
CONFIG += release opengl
QT += gui opengl

Le programme principal se rduit sa plus simple expression, ne faisant rien dautre


que crer une instance de Triangle, lafficher et lancer la boucle dvnements,
comme pour tout programme utilisant Qt.
Mais examinons un peu le contenu de cette classe Triangle, justement. Trois mthodes
doivent tre surcharges partir de QGLWidget :

initializeGL(), appele une seule fois, est destine paramtrer le contexte


OpenGL (dans la pratique, pour une application complexe, on ne lutilise que rarement).

resizeGL() est invoque chaque fois que le widget change de taille ; en effet, la
faon dafficher un "monde" en trois dimensions dpend fortement des dimensions
du rectangle plan dans lequel il doit sinscrire.

paintGL(), enfin, est la mthode invoque lorsquil est ncessaire de dessiner


effectivement les objets en trois dimensions : cest en son sein que seffectue le
vritable travail daffichage.

Toutes les fonctions de la bibliothque OpenGL commencent par le suffixe gl. La


premire que nous rencontrons, glEnable() dans initializeGL(), est une fonction
assez gnrique permettant dactiver certaines fonctionnalits. Son miroir est glDisable(), pour au contraire en dsactiver certaines. Par exemple, dans notre cas, on
demande OpenGL dactiver le test de profondeur, qui a en gros pour effet de ne pas
afficher les objets qui se trouveraient "derrire" dautres objets, selon la direction dans
laquelle on regarde.
Sans doute est-il temps de voir comment OpenGL passe de la modlisation dun
monde en trois dimensions une reprsentation en deux dimensions sur lcr an.

Program Livre Page 220 Vendredi, 16. mai 2008 8:13 08

220

Initiation la programmation avec Python et C++

Ce que nous allons voir maintenant est un peu simplifi par rapport la ralit, mais
cela devrait vous donner une ide.
Pour cela, considrez un point dans lespace. Un point au sens mathmatique du
terme, cest--dire nayant pas dpaisseur. Une tte dpingle vue dun peu loin,
par exemple. Ce point est localis laide de coordonnes, tout comme les pixels
sur lcran, sauf que ces coordonnes sont (le plus souvent) des nombres virgule
flottante et quelles sont au nombre de trois do le terme trois dimensions, ou
3D en abrg. Ces coordonnes sont exprimes par rapport un repre, lequel est
dfini par la matrice modle/vue (model/view matrix en anglais). Le terme matrice
renvoie lobjet mathmatique utilis, une matrice de transformation : il sagit
grossirement dun tableau de quatre lignes et de quatre colonnes, contenant donc
seize valeurs numriques. Dune certaine faon, cette matrice dtermine la position et lorientation de votre il dans lespace. Chaque point ncessaire la
description des objets reprsenter est ainsi spcifi par ses coordonnes dans cet
espace 3D.
Imaginez maintenant que, juste devant votre il, vous placiez une immense plaque
de verre. Sur cette plaque, laide dun feutre, vous tracez un point lendroit o
vous voyez la tte dpingle. Et vous faites de mme avec tous les objets que vous
voyez et que vous voulez reprsenter. Vous obtenez finalement un dessin plat, en
deux dimensions, dun ensemble dobjets en trois dimensions. Ce que vous venez de
raliser est une projection, consistant "craser" le monde en trois dimensions sur
un plan en deux dimensions. La taille de la plaque de verre dtermine directement
les limites du monde que vous reprsentez. La faon dont cette projection doit tre
effectue est dtermine par la matrice de projection (projection matrix
en anglais). Pour notre tte dpingle, le rsultat est le passage de coordonnes 3D
en coordonnes 2D.
Enfin, il reste obtenir une image, de la mme faon quon obtient une photo : il faut
dfinir les dimensions de cette photo, dans quelle zone de la feuille de papier elle va se
situer Cest ce quon appelle le cadrage. Cette opration est rgie par la fonction
glViewport(), cest--dire la faon dont limage (plane) finale sera insre dans la
fentre graphique. Ses paramtres sont les coordonnes du coin infrieur gauche et
les dimensions dun rectangle, par rapport au widget contenant le contexte OpenGL.
Le plus souvent, on donne simplement (0, 0) comme origine et les largeur et hauteur
du widget, comme ici. Limage finale, rsultant de la transformation des objets 3D en
un dessin plan en deux dimensions, sera insre dans ce rectangle. Petite subtilit,
source de nombreuses confusions : contrairement lusage dans la programmation

Program Livre Page 221 Vendredi, 16. mai 2008 8:13 08

Chapitre 16

Introduction OpenGL

221

dune interface graphique, OpenGL considre que lorigine des coordonnes se situe
dans le coin infrieur gauche, non pas dans le coin suprieur gauche.
Le cadrage est la dernire opration de transformation gomtrique, conclusion dune
chane illustre la Figure 16.1.
Figure 16.1
La chane des transformations
OpenGL.

point dans
lespace 3D
transformation
modle / vue
point dans
lespace
de la camra
transformation
de projection
point dans un
espace plan
transformation
de cadrage

pixel lcran

Gnralement, cadrage et projection sont dfinis dans la mthode resizeGL(), car ces
deux transformations dpendent directement des dimensions du widget. glMatrixMode() indique OpenGL sur quelle matrice on sapprte agir : la constante
GL_PROJECTION dsigne la matrice de projection. glLoadIdentity() place dans
celle-ci la matrice identit, cest--dire que le cadre de notre widget reprsente
exactement le volume despace que nous allons projeter.
Enfin, le dessin effectif est effectu dans la mthode paintGL(). Les deux premires fonctions utilises, glClearColor() et glClear(), ont pour effet de "nettoyer" le contexte
daffichage OpenGL en le remplissant de noir. Puis on positionne notre il, en
donnant la matrice modle/vue ( laide de glMatrixMode() et glLoadIdentity()).
La combinaison de transformations telle que nous lavons dfinie ici place lorigine des
coordonnes 3D au centre du widget, laxe X (premire coordonne) allant de la gauche
vers la droite, entre 1.0 et 1.0, laxe Y (deuxime coordonne) allant de bas en haut,
entre 1.0 et 1.0, laxe Z (troisime coordonne) "jaillissant" de lcran comme pour vous
rentrer dans lil. Pour vous reprsenter ce systme de coordonnes, utilisez votre main
droite, poing ferm. cartez le pouce vers lextrieur de la main : il reprsente laxe X.
Tendez compltement lindex, qui devient ainsi perpendiculaire au pouce : il reprsente

Program Livre Page 222 Vendredi, 16. mai 2008 8:13 08

222

Initiation la programmation avec Python et C++

laxe Y. Enfin, dpliez les deux dernires phalanges du majeur, qui devient ainsi perpendiculaire la fois au pouce et lindex : il reprsente laxe Z.
Figure 16.2
Repre direct ou repre
main droite.

Z
Y

Votre main ainsi ouverte reprsente un repre direct, aussi appel repre main droite.
Si vous placez le pouce de faon quil pointe vers la droite de votre cran et lindex
vers le haut, vous voyez que le majeur semble "sortir" de lcran.
Toutes les oprations effectues jusquici nont fait que prparer le contexte OpenGL
pour le dessin. Celui-ci est effectu sur la base de primitives, cest--dire dobjets
lmentaires quOpenGL "sait" effectivement reprsenter. Ils sont trs peu nombreux,
les plus utiliss tant le triangle et la ligne brise. Tout autre objet plus complexe,
comme un cylindre, une sphre ou un simple cercle, est en ralit obtenu en dessinant
un nombre plus ou moins lev de ces primitives de base. Comme illustration, voyez
la Figure 16.3, qui montre une mme scne du monde virtuel SecondLife : limage de
gauche est celle qui est normalement expose lutilisateur, limage de droite montre
la dcomposition en triangles de chacun des objets affichs.
Le dessin dune primitive (ou plusieurs) est ralis en donnant les coordonnes de chacun
de ses sommets, dans une suite dinstructions encadres par la paire glBegin() /
glEnd(). La premire annonce le commencement dun dessin, son paramtre identifiant
quelle primitive on sapprte dessiner. La seconde signale la fin du dessin.
Entre les deux, on trouve une suite dappels la fonction glVertex3d(), chacun
deux dfinissant les coordonnes dun sommet de la primitive. Dans notre exemple,
comme nous dessinons un simple triangle, trois appels sont ncessaires.

Program Livre Page 223 Vendredi, 16. mai 2008 8:13 08

Chapitre 16

Introduction OpenGL

223

Figure 16.3
Un monde virtuel en
OpenGL : ce que vous
voyez et la face cache.

Vous pouvez constater quavant chaque appel glVertex3d() nous invoquons


glColor3d() : cette fonction permet de spcifier la couleur de chacun des sommets.
Le modle de couleur est ici aussi le modle RGB (voir la section 5 du Chapitre 12), sauf
que les proportions de rouge, vert et bleu sont donnes par un nombre virgule flottante
compris entre 0.0 (noir) et 1.0 (pleine intensit). Aprs un appel glColor3d(), tous les
sommets suivants seront de la couleur indique. Donc, nous indiquons un premier

Program Livre Page 224 Vendredi, 16. mai 2008 8:13 08

224

Initiation la programmation avec Python et C++

sommet en rouge, un deuxime en vert et un troisime en bleu. Le rsultat est un triangle


montrant un dgrad entre ces trois couleurs, comme le montre la Figure 16.4.
Figure 16.4
Dgrad de couleur
dans un triangle.

16.2.2. Le cube
Notre triangle est sans doute trs esthtique, mais il reste dsesprment plat : nous
aurions pu obtenir le mme rsultat en utilisant les possibilits offertes par la classe
QPainter de Qt. Voyons maintenant comment nous pouvons passer rellement en
trois dimensions.
Nous ne pouvons plus nous contenter des matrices identit pour la projection et la vue.
Commencez donc par modifier ainsi la mthode resizeGL() (en Python) :
def resizeGL(self, w, h):
glViewport(0, 0, w, h)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
glOrtho(-1.0, 2.0, -1.0, 2.0, -2.0, 3.0)

La fonction glOrtho() dfinit une projection orthogonale, cest--dire dans laquelle


deux droites parallles dans lespace restent parallles une fois projetes. Ce type de
projection est adapt pour des reprsentations techniques mais ne correspond pas la
ralit. La vision humaine est mieux reprsente par une projection utilisant des points
de fuite. Par exemple, si vous conduisez une voiture sur une route longue et droite, les
bords de celle-ci semblent sincurver pour se rejoindre lhorizon. Une telle perspective peut tre obtenue avec glFrustum(), mais elle est plus dlicate mettre en
uvre. Les quatre paramtres passs glOrtho() dterminent les limites du volume
visualis.
Voici maintenant paintGL() (en C++), dans laquelle on dessine trois faces dun cube :
void Cube::paintGL()
{

Program Livre Page 225 Vendredi, 16. mai 2008 8:13 08

Chapitre 16

Introduction OpenGL

225

glClearColor(0.0, 0.0, 0.0, 0.0);


glClear(GL_COLOR_BUFFER_BIT);
glClear(GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glRotated(-45.0, 1.0, 0.0, 0.0);
glRotated(-30.0, 0.0, 0.0, 1.0);
glShadeModel(GL_SMOOTH);
glBegin(GL_QUADS);
// premire face (avant)
glColor3d(0.0, 0.0, 0.0);
glVertex3d(0.0, 0.0, 0.0);
glColor3d(1.0, 0.0, 0.0);
glVertex3d(1.0, 0.0, 0.0);
glColor3d(1.0, 0.0, 1.0);
glVertex3d(1.0, 0.0, 1.0);
glColor3d(0.0, 0.0, 1.0);
glVertex3d(0.0, 0.0, 1.0);
// deuxime face (ct)
glColor3d(1.0, 0.0, 0.0);
glVertex3d(1.0, 0.0, 0.0);
glColor3d(1.0, 1.0, 0.0);
glVertex3d(1.0, 1.0, 0.0);
glColor3d(1.0, 1.0, 1.0);
glVertex3d(1.0, 1.0, 1.0);
glColor3d(1.0, 0.0, 1.0);
glVertex3d(1.0, 0.0, 1.0);
// troisime face (dessus)
glColor3d(0.0, 0.0, 1.0);
glVertex3d(0.0, 0.0, 1.0);
glColor3d(1.0, 0.0, 1.0);
glVertex3d(1.0, 0.0, 1.0);
glColor3d(1.0, 1.0, 1.0);
glVertex3d(1.0, 1.0, 1.0);
glColor3d(0.0, 1.0, 1.0);
glVertex3d(0.0, 1.0, 1.0);
glEnd();
glBegin(GL_LINES);
glColor3d(0.0, 0.0, 0.0);
glVertex3d(1.0, 0.0, 1.0);
glVertex3d(1.0, 0.0, 0.0);
glVertex3d(1.0, 0.0, 1.0);
glVertex3d(1.0, 1.0, 1.0);
glVertex3d(1.0, 0.0, 1.0);
glVertex3d(0.0, 0.0, 1.0);
glEnd();
}

Les appels la fonction glRotated() ont pour effet de faire tourner notre il autour
dun axe donn cest--dire, en pratique, de modifier la matrice modle/vue.

Program Livre Page 226 Vendredi, 16. mai 2008 8:13 08

226

Initiation la programmation avec Python et C++

Notez que ces transformations se composent les unes avec les autres : le premier appel
glRotated() fait tourner autour de laxe X, le deuxime autour dun axe Z rsultant de la rotation prcdente. Par des combinaisons plus ou moins complexes, vous
pouvez ainsi obtenir des effets sophistiqus. Vous pouvez galement obtenir trs facilement une image parfaitement noire, ne montrant rien du tout : les combinaisons de
transformations peuvent tre assez dlicates manipuler, au point de vous retrouver
perdu quelque part dans lespace.
Les instructions qui suivent dessinent tout simplement trois rectangles (primitive
GL_QUADS), puis trois segments de droite (primitive GL_LINES), ces derniers nayant
dautre but que de souligner les bords du cube. Les rectangles ncessitent chacun
quatre points, tandis que les segments nen ncessitent que deux. Voyez comment
plusieurs primitives dun mme type sont dessines au sein dun unique bloc glBegin()/glEnd(). La Figure 16.5 montre le rsultat de ce programme.
Figure 16.5
Le cube en couleur.

16.3. La GLU et autres bibliothques


Nous navons voqu ici que quelques fonctions de la bibliothque OpenGL. Des
centaines dautres sont disponibles. Pourtant, la plupart restent dassez "bas niveau" :
si OpenGL fournit tous les outils pour dessiner une simple sphre, aucune fonction ne
permet de la dessiner facilement.
Aussi, de nombreuses bibliothques ont-elles t cres "par-dessus" OpenGL, afin de
fournir des outils de plus haut niveau. La premire dentre elles est la GLU (pour

Program Livre Page 227 Vendredi, 16. mai 2008 8:13 08

Chapitre 16

Introduction OpenGL

227

OpenGL Utilities), qui offre plusieurs possibilits pour le dessin dobjets gomtriques
plus complexes, le rglage des matrices de transformation selon diverses situations,
etc. Limportance de cette bibliothque est telle quelle est normalement toujours
disponible aux cts de toutes les implmentations dOpenGL.
Concernant laffichage de texte, encore une fois, OpenGL ne fournit aucune facilit.
Pourtant, une spcification a t tablie, nomme GLC, pour dfinir une API permettant laffichage de caractres dans un contexte OpenGL. Une implmentation libre et
multi-plate-forme de cette spcification est offerte par la bibliothque QuesoGLC
(http://quesoglc.sourceforge.net/).
mesure que vous progresserez dans lutilisation dOpenGL, vous ressentirez invitablement le besoin de structurer le monde que vous voudrez reprsenter, dassocier
des lments entre eux, de permettre lutilisateur de les slectionner la souris, voire
de pouvoir changer librement langle de vue En rsum, vous aurez le besoin dun
vritable moteur 3D de haut niveau. Citons quelques bibliothques.

Coin3D (http://www.coin3d.org/) utilise une reprsentation hirarchique dune


scne 3D pour son affichage, permettant ainsi la reprsentation densembles
complexes et structurs. Elle offre, en plus, diverses options pour linteraction avec
les objets de la scne, ce qui simplifie considrablement le travail de dveloppement. Ajoutons quelle sintgre fort bien la bibliothque Qt. Coin3D est adapte
pour les applications vocation technique.

OpenSceneGraph (http://www.openscenegraph.org/projects/osg) offre des


fonctionnalits analogues celles de Coin3D, mais avec une encapsulation plus
pousse des possibilits avances dOpenGL. Elle est galement plus gnraliste,
permettant la cration aussi bien dapplications scientifiques que de jeux.

Soya3D (http://home.gna.org/oomadness/en/soya3d/index.html) est une


bibliothque spcialement ddie au langage Python pour la cration dapplications 3D, aussi bien jeux que programmes de visualisation ou simulation scientifique.
Et ce qui ne gte rien, les dveloppeurs de Soya3D sont principalement franais.

Gardez lesprit que ces bibliothques ne sont pas des jouets destins un enfant
Pour puissantes quelles soient, elles peuvent tre plus ou moins difficiles apprhender. Si vous souhaitez vous lancer dans le dveloppement dune application 3D
complexe, que cela soit un jeu ou un programme scientifique, nhsitez pas passer du
temps pour exprimenter les possibilits offertes et dterminer ainsi laquelle vous
conviendra le mieux.

Program Livre Page 228 Vendredi, 16. mai 2008 8:13 08

Program Livre Page 229 Vendredi, 16. mai 2008 8:13 08

17
Aperu de
la programmation
parallle
Au sommaire de ce chapitre :

Solution simple pour Hano

Solution paralllise

Prcautions et contraintes

Program Livre Page 230 Vendredi, 16. mai 2008 8:13 08

230

Initiation la programmation avec Python et C++

Si vous vous intressez un tant soit peu au march des ordinateurs, il ne vous aura pas
chapp que la tendance actuelle en matire de processeurs est la multiplication des
curs, cest--dire en fait fournir des ordinateurs contenant plusieurs processeurs au
lieu dun seul.
Les ordinateurs quips de plusieurs processeurs ne sont pas une nouveaut. Cela fait
des dizaines dannes que ces configurations sont couramment utilises dans les domaines scientifiques et techniques, particulirement gourmands en puissance de calcul.
Un exemple clbre est la machine Deep Blue, premier ordinateur vaincre le grand
matre des checs Garry Kasparov en 1997 : elle contenait 30 processeurs gnraux,
assists de 480 processeurs spcialiss pour les checs. Toutefois, les systmes quips de plusieurs processeurs demeuraient trs onreux et rservs des domaines
assez spcialiss, tels que les prvisions mto.
Dans le monde des micro-ordinateurs destins au grand public, durant de nombreuses annes, la course la puissance sest traduite par une course la frquence :
chaque anne, le nouveau processeur tait plus rapide que celui de lanne prcdente, cette vitesse tant grossirement exprime par la frquence de lhorloge
interne, qui reprsentait plus ou moins le nombre dinstructions lmentaires
(trs lmentaires) que le processeur tait capable deffectuer par seconde. Les
progrs ont t considrables, depuis le premier 8086 du dbut des annes 1980,
affichant une frquence de 4,77 MHz, aux derniers processeurs atteignant des frquences de lordre de 4 GHz : en une vingtaine dannes, la frquence a t multiplie par
mille.
Mais malgr toute lhabilet des ingnieurs, les lois physiques sont ttues. Il est
devenu impossible de miniaturiser davantage, daugmenter la frquence davantage, du
moins pour un cot raisonnable. Dune certaine manire, on peut dire que la puissance
brute a atteint ses limites, au moins jusqu la prochaine innovation technologique
majeure. Laugmentation de la frquence nest ainsi plus la voie suivre pour augmenter
la vitesse de calcul de nos processeurs "domestiques".
Alors, les fabricants de processeurs se sont tourns vers une autre voie : multiplier les
curs au sein dun mme processeur, cest--dire, en fait, fournir plusieurs processeurs en un seul. Ds lors, du point de vue du programmeur, une nouvelle possibilit
pour amliorer les performances de son programme consiste exploiter les multiples
processeurs disponibles en effectuant des traitements en parallle, cest--dire, littralement, en faisant plusieurs choses la fois.

Program Livre Page 231 Vendredi, 16. mai 2008 8:13 08

Chapitre 17

Aperu de la programmation parallle

231

17.1. Solution simple pour Hano


Comme illustration, nous allons reprendre la simulation de la solution de notre jeu des
Tours de Hano. La solution ne sera pas rellement calcule, mais simplement nous
allons compter le nombre de mouvements effectus. Voici le programme, tout ce quil
y a de plus classique :
#include <cstdlib>
#include <iostream>
#include <QTime>
void Hanoi_Simple(int nb_disques,
int depart,
int pivot,
int arrivee,
long long& nb_mouvements)
{
if ( nb_disques == 1 )
{
++nb_mouvements;
}
else
{
Hanoi_Simple(nb_disques - 1,
depart, arrivee, pivot,
nb_mouvements);
Hanoi_Simple(1,
depart, pivot, arrivee,
nb_mouvements);
Hanoi_Simple(nb_disques - 1,
pivot, depart, arrivee,
nb_mouvements);
}
}
int main(int argc, char* argv[])
{
int nb_disques = atoi(argv[1]);
long long nb_mouvements = 0;
QTime chrono;
chrono.start();
Hanoi_Simple(nb_disques, 1, 2, 3, nb_mouvements);
std::cout << nb_mouvements << " "
<< chrono.elapsed() / 1000.0 << std::endl;
return 0;
}

Program Livre Page 232 Vendredi, 16. mai 2008 8:13 08

232

Initiation la programmation avec Python et C++

On retrouve la classe QTime, fournie par Qt, que nous utilisons ici pour mesurer le
temps ncessaire la simulation. Le type long long utilis dans ce programme
est un type entier sign, mais dune plus grande capacit que le type habituel int.
Son utilisation est ncessaire, car nous allons dnombrer un trs grand nombre de
mouvements (plusieurs milliards), valeurs quun simple int ne pourrait pas reprsenter.
Lorsquon excute ce programme en lui donnant en paramtre 34 disques, on obtient
17 179 869 183 mouvements en environ cent secondes. Naturellement, cette dure
peut varier dune machine lautre, selon sa puissance.

17.2. Solution paralllise


Limmense majorit des langages de programmation ne fournissent par eux-mmes
aucun support pour la mise en uvre dune programmation parallle. Une exception notable est le langage Ada, qui prvoyait la dfinition de tches pouvant
sexcuter simultanment ds sa conception, la fin des annes 1970. Mais les
langages Python et C++ doivent explicitement sappuyer sur des bibliothques
externes.
Naturellement, il existe diffrentes faons de "faire du paralllisme" ; de plus,
pratiquement chaque systme propose son propre ensemble de fonctions pour cela.
La solution que nous allons exposer ici sappuie sur la bibliothque Qt, qui a
limmense mrite de fournir une interface de programmation unique pour tous les
systmes.
Le principe consiste crer, au sein dun mme programme, plusieurs flux
dinstructions. Les programmes que nous avons raliss jusquici ne contenaient
quun seul flux, les boucles et les alternatives ntant en fait que des "sauts" dans ce
flux. Nous allons crer deux flux qui vont sexcuter en parallle. On utilise plus
couramment le terme anglais de thread, dont flux nest quune traduction approximative. Ainsi, parle-t-on de programmation multithread, ou encore dun programme
multithread.
Dans notre exemple consistant compter le nombre de mouvements ncessaires au
dplacement de n disques, chaque thread sera charg de simuler le dplacement de
n1 disques. La cration dun thread se fait simplement en drivant la classe QThread

Program Livre Page 233 Vendredi, 16. mai 2008 8:13 08

Chapitre 17

Aperu de la programmation parallle

233

et en surchargeant la mthode run(), laquelle doit contenir les instructions excuter.


Voici le programme (lgrement abrg) :
#include <cstdlib>
#include <iostream>
#include <QTime>
#include <QCoreApplication>
#include <QThread>
void Hanoi_Simple(int nb_disques,
int depart,
int pivot,
int arrivee,
long long& nb_mouvements)
{
/* inchange par rapport au prcdent */
}
class Hanoi_Thread: public QThread
{
public:
Hanoi_Thread(int nb_disques,
int depart,
int pivot,
int arrivee):
nb_disques(nb_disques),
depart(depart),
pivot(pivot),
arrivee(arrivee),
nb_mouvements(0)
{
}
virtual void run()
{
Hanoi_Simple(this->nb_disques,
this->depart,
this->pivot,
this->arrivee,
this->nb_mouvements);
}
int nb_disques;
int depart;
int pivot;
int arrivee;
long long nb_mouvements;
}; // class Hanoi_Thread
void Hanoi_Parallele(int nb_disques,
int depart,
int pivot,
int arrivee,
long long& nb_mouvements)

Program Livre Page 234 Vendredi, 16. mai 2008 8:13 08

234

Initiation la programmation avec Python et C++

{
Hanoi_Thread thread_1(nb_disques-1,
depart,
arrivee,
pivot);
thread_1.start();
Hanoi_Thread thread_2(nb_disques-1,
pivot,
depart,
arrivee);
thread_2.start();
thread_1.wait();
thread_2.wait();
nb_mouvements = thread_1.nb_mouvements +
1 +
thread_2.nb_mouvements;
}
int main(int argc, char* argv[])
{
int nb_disques = atoi(argv[1]);
QCoreApplication app(argc, argv);
long long nb_mouvements = 0;
QTime chrono;
chrono.start();
Hanoi_Parallele(nb_disques, 1, 2, 3, nb_mouvements);
std::cout << nb_mouvements << " "
<< chrono.elapsed() / 1000.0 << std::endl;
return 0;
}

Pour des raisons qui sont propres Qt, nous devons imprativement crer une
instance de la classe QCoreApplication avant de crer toute instance de QThread
(ou dune classe drive). Cette classe reprsente en quelque sorte lapplication
elle-mme, de la mme manire que la classe QApplication, rencontre pour les
programmes graphiques, sauf que QCoreApplication est utilisable dans une application non graphique. Incidemment, remarquons que QApplication drive de
QCoreApplication.
Les threads sont crs dans la fonction Hanoi_Parallle(), tout simplement
travers la cration de deux instances de la classe Hanoi_Thread, celle-ci drivant de
QThread. Lorsquune instance est cre, les instructions que nous avons places dans
la mthode run() ne sont pas encore excutes : le thread est bel et bien cr, mais il

Program Livre Page 235 Vendredi, 16. mai 2008 8:13 08

Chapitre 17

Aperu de la programmation parallle

235

est inactif. Il ne sexcutera effectivement quaprs avoir invoqu la mthode start()


(issue de QThread).
Cette mthode start() se termine trs rapidement, quelle que soit la longueur du traitement effectu au sein de la mthode run(). Aussi, est-il gnralement ncessaire
dattendre que ce traitement se termine : cest le rle de la mthode wait(). En pratique, invoquer cette mthode wait() suspend lexcution du programme tant que
linstance de QThread partir de laquelle elle a t invoque na pas termin son
excution. Celle-ci est termine au moment o lon sort de la mthode run().
La Figure 17.1 schmatise le droulement des oprations. Les lignes en pointills
symbolisent les moments o les diffrents threads sont inactifs, cest--dire en attente
de "quelque chose". Remarquez la prsence dun thread principal : en ralit, tout
programme contient au moins un thread.
Figure 17.1
Hano paralllis.

thread
principal

cration de
thread_1
thread_1

thread_1.start ( )

cration de
thread_2

thread_1.run ( )
thread_2

thread_2.start ( )

thread_2.run ( )
thread_1.wait ( )

thread_2.wait ( )

Program Livre Page 236 Vendredi, 16. mai 2008 8:13 08

236

Initiation la programmation avec Python et C++

Lexcution de ce programme, sur une machine quipe dun processeur double cur
(donc comme si elle disposait de deux processeurs), donne 17 179 869 183 mouvements
en environ cinquante secondes. Il est heureux de constater que lon obtient le mme
nombre de mouvements. Mais le plus intressant est que le temps dexcution a bien t
divis par deux : par une rorganisation somme toute assez simple de lalgorithme, nous
exploitons la puissance du matriel disponible et augmentons ainsi considrablement les
performances.

17.3. Prcautions et contraintes


Naturellement, il est bien rare quun algorithme se prte aussi aisment au paralllisme. Dans la majorit des cas, la mise en uvre dune programmation parallle peut
tre extrmement complexe.
Les problmes surgissent ds quil devient ncessaire que deux threads sexcutant en
parallle puissent lire et modifier une mme zone mmoire. On peut alors se trouver
confront des problmes subtils o un thread lit une valeur, tandis que lautre modifie
cette valeur : le premier risque de se retrouver avec une valeur errone, conduisant presque
immanquablement un dysfonctionnement. De plus, il est pratiquement impossible de
savoir dans quel ordre sont excutes les instructions contenues dans chacun des threads :
si lordre au sein dun thread est connu, il est impossible de savoir si des instructions
dautres threads ne vont pas venir sintercaler. Et cet ordre peut varier dune excution
lautre dun mme programme, ce qui conduit une grande incertitude.
Pour pallier ce problme, des techniques existent qui consistent fixer des moments o
les threads sont srialiss, cest--dire explicitement ordonns, comme sil sagissait de
fonctions classiques (non parallles). Mais ces techniques ne sont elles-mmes pas sans
risques : elles peuvent conduire des situations o deux threads sattendent lun lautre,
provoquant un blocage complet du programme. Celui-ci nest pas "plant" proprement
parler, il est tout simplement pris dans une attente infinie.
Dans un programme non trivial, lutilisation de la programmation parallle peut
donc rapidement devenir trs dlicate. Elle nest simple que dans les cas o
chaque thread ne manipule quun ensemble limit des donnes qui lui est ddi .
Ds que deux threads doivent lire ou modifier la mme zone mmoire, les problmes
peuvent surgir, parfois de faon tout fait inattendue. Aussi, soyez particulirement
prcautionneux si vous vous lancez dans ce genre daventure, nhsitez pas vous documenter abondamment.

Program Livre Page 237 Vendredi, 16. mai 2008 8:13 08

18
Aperu dautres langages
Au sommaire de ce chapitre :

Ada

Basic

C#

OCaml

Pascal

Ruby

Program Livre Page 238 Vendredi, 16. mai 2008 8:13 08

238

Initiation la programmation avec Python et C++

Pour terminer, nous allons voquer ici quelques autres langages de programmation parmi
les plus rpandus ou les plus clbres. Nous nentrerons pas dans les dtails cet ouvrage
nest pas une encyclopdie ! , mais nous verrons simplement comment pourrait scrire
notre programme donnant la solution du problme des Tours de Hano.
Sans doute est-il galement temps de dvoiler une petite vrit. Vous trouverez dans ce
qui suit des langages dits interprts, dautres dits compils. En ralit, aujourdhui,
pratiquement aucun langage nest rellement interprt. Tous les langages modernes
dits interprts sappuient en ralit sur une machine virtuelle, vritable dnomination
de linterprteur. Les programmes en langage Python que nous avons crits tout au
long de cet ouvrage, au moment de leur excution, sont en fait transforms en une
reprsentation intermdiaire et binaire nomme bytecode. Ce nest pas exactement
une compilation, mais ce nest pas non plus une "bte" interprtation des instructions
une une. Cette reprsentation est ensuite excute par la machine virtuelle, dont le
rle est en fait dexcuter ce bytecode en lui fournissant un environnement parfaitement dfini une sorte de modle dordinateur idal. Le clbre langage Java, par
exemple, se rfre clairement sa JVM (pour Java Virtual Machine, machine virtuelle
Java) pour sexcuter.
Notez bien que la slection propose ici est purement subjective et ne saurait
traduire ni les prfrences de lauteur, ni la popularit de tel ou tel langage de
programmation. Signalons tout de mme que la plupart font partie du groupe des
langages impratifs.
On dsigne par ce terme les langages de programmation consistant en la description dun tat du programme, reprsent par ses variables, ainsi que les oprations
excuter pour modifier cet tat. Il sagit du plus ancien paradigme de programmation et de loin le plus rpandu. Les langages Python et C++ font partie de ce
groupe.
Enfin, les langages voqus maintenant appliquent tous les principes de la programmation oriente objet.

18.1. Ada
Le langage Ada a t dvelopp la fin des annes 1970 par une quipe franaise dirige par Jean Ichbiah, au sein de lentreprise franaise CII-Honeywell Bull. Ce langage
fait suite un appel doffres international lanc par le dpartement de la Dfense
des tats-Unis dAmrique, qui cherchait alors rduire le nombre de langages
de programmation utiliss afin de gagner en cohrence et en efficacit globale.

Program Livre Page 239 Vendredi, 16. mai 2008 8:13 08

Chapitre 18

Aperu dautres langages

239

Les contraintes imposes taient extrmement svres, lobjectif tant moins la rapidit du dveloppement et de lexcution que la robustesse et la fiabilit des programmes
obtenus. Le nom Ada a t choisi en hommage lady Augusta Ada King, comtesse de
Lovelace, seule fille lgitime du pote anglais lord Byron. Lady Ada vcut de 1815
1852, brves annes durant lesquelles elle entretint une correspondance soutenue avec
linventeur de la machine analytique Charles Babbage. Elle a longtemps t perue
comme lauteur du premier programme informatique.
Sans plus attendre, voici un exemple de programme en Ada :
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
procedure Hanoi_Ada is
procedure Solution(nb_disques: in integer;
depart
: in integer;
pivot
: in integer;
arrivee : in integer) is
begin
if nb_disques = 1
then
Put(depart);
Put(" -> ");
Put(arrivee);
New_Line;
else
Solution(nb_disques-1, depart, arrivee, pivot);
Solution(1, depart, pivot, arrivee);
Solution(nb_disques-1, pivot, depart, arrivee);
end if;
end Solution;
nb_disques: integer;
begin
Put("Nombre de disques? ");
Get(nb_disques);
Solution(nb_disques, 1, 2, 3);
end Hanoi_Ada;

La syntaxe du langage emprunte beaucoup au langage Pascal, qui avait lui-mme t


conu comme le langage idal pour apprendre la programmation. Sa caractristique
principale, qui rebute plus dun programmeur, est une extrme rigueur sur le plan des
types : sil est possible en C++ daffecter la valeur dune variable dun type entier
une variable dun type en virgule flottante, cela est interdit en Ada sans utiliser explicitement une conversion de type (ou transtypage). Cette contrainte trs forte, associe
dautres, fait que lcriture dun programme en Ada est gnralement perue comme

Program Livre Page 240 Vendredi, 16. mai 2008 8:13 08

240

Initiation la programmation avec Python et C++

plus difficile que dans aucun autre langage. En contrepartie, une fois que le
programme passe ltape de la compilation, il est presque garanti quil sexcute
correctement.
Le langage Ada offre des facilits pour la programmation parallle ou le mlange de
plusieurs langages au sein dun mme logiciel. Il est aujourdhui utilis dans le cadre
de missions critiques, comme la fuse Ariane 5, la gestion du trafic arien ou ferroviaire ou par certaines institutions financires.
Le compilateur le plus rpandu pour le langage Ada est celui dvelopp par la socit
AdaCore, nomm GNAT, dsormais intgr la suite de compilation GCC (celle que
nous avons utilise depuis le dbut de cet ouvrage).

18.2. Basic
Ce langage de programmation, qui fut un moment peut-tre le plus populaire, a t
invent en 1964. Le mot "BASIC" est un acronyme de Beginners All-purpose Symbolic
Instruction Code, qui signifie quelque chose comme "langage symbolique gnraliste
pour dbutants". Lun des fondements du Basic tait en effet quil soit simple manipuler, notamment par des non-informaticiens.
Les premires versions du langage furent de fait assez... basiques. Les lignes du
programme devaient tre numrotes, il nexistait pas de boucles bornes (for)
ni de sous-programmes. Loutil principal pour passer dun endroit lautre dans un
programme consistait utiliser linstruction GOTO, suivie du numro de ligne
excuter. On aboutissait ainsi un programme non structur, comparable un plat
de spaghettis... Ce qui fit dire de nombreux informaticiens que le langage Basic
faisait plus de mal que de bien et produisait plus de "mauvais" programmes
quaucun autre.
Pourtant, ce langage a connu une popularit considrable, du fait de sa simplicit et
de sa diffusion dans pratiquement tous les ordinateurs personnels, ds la fin des
annes 1970. Il a depuis considrablement volu, abandonnant la numrotation
des lignes et intgrant jusquaux notions de la Programmation Oriente Objet. Lune de
ses incarnations les plus connues est le langage VisualBasic, dvelopp par Microsoft,
mais de nombreuses autres existent comme FreeBasic (multiplate-formes) ou Gambas
(principalement sous Linux).
Sub Solution(ByVal nb_disques As Integer, ByVal depart As Integer, ByVal pivot
As Integer, ByVal arrivee As Integer)
If nb_disques = 1 Then

Program Livre Page 241 Vendredi, 16. mai 2008 8:13 08

Chapitre 18

Aperu dautres langages

241

Print Using "# -> #"; depart, arrivee


Else
Solution(nb_disques-1, depart, arrivee, pivot)
Solution(1, depart, pivot, arrivee)
Solution(nb_disques-1, pivot, depart, arrivee)
End If
End Sub
Dim nb_disques As integer
Input "Nombre de disques ? ", nb_disques
Solution(nb_disques, 1, 2, 3)

En-dehors de ses "dfauts" intrinsques, qui font de Basic lun des langages les plus
dcris parmi les informaticiens professionnels ou chercheurs, un problme majeur est
la prolifration des "dialectes" du langage. Le programme prcdent a t test en
utilisant FreeBasic, mais il nest pas garanti quil fonctionne avec un autre compilateur
ou interprteur Basic.

18.3. C#
Le langage C# (prononcez "ci-sharp") a t dvelopp par la socit Microsoft en
2001 afin de fournir un langage ddi sa plate-forme .NET (prononcez "dotte-net"),
laquelle constitue lquivalent dune machine virtuelle pour les systmes Windows.
Du point de vue de nombreux observateurs, C# et .NET ont t dvelopps pour
concurrencer directement le langage Java cr par Sun. Si le seul compilateur "officiel" de C# est Visual C#, dvelopp par Microsoft uniquement pour les systmes
Windows, dautres compilateurs sont apparus pour dautres plates-formes. Citons
notamment les projets Mono et DotGNU.
using System;
namespace Hanoi
{
class Solution
{
public static void Solution(int nb_disques,
int depart,
int pivot,
int arrivee)
{
if ( nb_disques == 1 )
{
System.Console.Write(depart);
System.Console.Write(" -> ");

Program Livre Page 242 Vendredi, 16. mai 2008 8:13 08

242

Initiation la programmation avec Python et C++

System.Console.WriteLine(arrivee);
}
else
{
Solution(nb_disques-1, depart, arrivee, pivot);
Solution(1, depart, pivot, arrivee);
Solution(nb_disques-1, pivot, depart, arrivee);
}
}
static void Main(string[] args)
{
System.Console.Write("Nombre de disques? ");
int nb_disques = System.Convert.ToInt32(System.Console.ReadLine());
Solution(nb_disques, 1, 2, 3);
System.Environment.Exit(0);
}
}
}

La syntaxe gnrale du langage sinspire fortement de celle de Java, tout en reprenant


des lments au C++ et au Pascal. Cette dernire influence vient probablement du fait
que le crateur de C#, Anders Hejlsberg, fut galement lauteur du compilateur quon
trouve dans les produits TurboPascal puis Delphi de Borland ; il fut responsable du
dveloppement de ce dernier avant de rejoindre Microsoft.

18.4. OCaml
Le langage OCaml, contraction dObjective Caml, a t cr en 1996 par Xavier
Leroy, directeur de recherche lINRIA. Cest, en fait, limplmentation la plus
avance du langage de programmation Caml, dvelopp depuis 1985. Il sagit
essentiellement dun langage fonctionnel. La programmation fonctionnelle considre un programme comme une suite dvaluations de fonctions mathmatiques,
plutt que comme une suite de changements dtat comme le fait la programmation
imprative. Bien que droutante au premier abord, elle est particulirement bien
adapte dans les situations o il est ncessaire de manipuler des structures de
donnes complexes et rcursives.
OCaml propose toutefois des possibilits permettant de mlanger les paradigmes
impratif et fonctionnel : il entre donc dans la catgorie des langages multiparadigmes.
Dans une certaine mesure, Python entre galement dans cette catgorie, car il offre
quelques possibilits de programmation fonctionnelle.

Program Livre Page 243 Vendredi, 16. mai 2008 8:13 08

Chapitre 18

Aperu dautres langages

243

let rec solution nb_disques depart pivot arrivee =


if nb_disques = 1
then
begin
print_int(depart);
print_string(" -> ");
print_int(arrivee);
print_newline();
end
else
begin
solution (nb_disques-1) depart arrivee pivot;
solution 1 depart pivot arrivee;
solution (nb_disques-1) pivot depart arrivee;
end;;
print_string("Nombre de disques? ");
let nb_disques = read_int() in
solution nb_disques 1 2 3;
exit 0

18.5. Pascal
Le langage Pascal doit son nom au philosophe Blaise Pascal, auquel il rend hommage.
Cr au dbut des annes 1970, son objectif initial tait dtre utilis dans lenseignement de la programmation en mettant laccent sur la rigueur et la clart des programmes, rle quil remplit encore de nos jours. Il a connu une volution assez
remarquable, notamment par la diffusion des outils TurboPascal puis Delphi par la
socit Borland. Ainsi est-il utilis dans lindustrie, pour le dveloppement dapplications professionnelles aussi diverses que varies.
program Hanoi(input, output);
var nb_disques: integer;
procedure Solution(nb_disques: integer;
depart
: integer;
pivot
: integer;
arrivee
: integer);
begin
if nb_disques = 1
then
writeln(depart, -> , arrivee)
else
begin
Solution(nb_disques-1, depart, arrivee, pivot);

Program Livre Page 244 Vendredi, 16. mai 2008 8:13 08

244

Initiation la programmation avec Python et C++

Solution(1, depart, pivot, arrivee);


Solution(nb_disques-1, pivot, depart, arrivee);
end
end;
begin
write(Nombre de disques ? );
readln(nb_disques);
writeln;
Solution(nb_disques, 1, 2, 3);
END.

Si vous comparez ce programme avec celui donn en langage Ada, vous remarquerez
une certaine similarit : cela nest pas un hasard. En effet, le Pascal a t une source
dinspiration pour le langage Ada, ce dernier en ayant conserv lexigence de clart
(entre autres).
En-dehors du clbre Delphi de Borland, citons le projet FreePascal, proposant un
compilateur Pascal efficace, que lon retrouve dans lenvironnement graphique Lazarus. FreePascal et Lazarus prsentent lintrt dtres multiplate-formes, en plus dtre
libres et gratuits.

18.6. Ruby
Le langage Ruby a t cr en 1993 par Yukihiro Matsumoto, alors frustr de ne pas
trouver satisfaction dans les langages interprts existants (principalement Perl et
Python). Il a t influenc par les concepts prsents dans les langages Eiffel et Ada.
En particulier, tout lment dun programme Ruby est un objet (ou une classe) au
sens de la programmation objet, mme les variables numriques simples. Ainsi
Ruby est-il lun des rares langages purement objet, bien que permettant une
programmation procdurale.
def Solution(nb_disques, depart, pivot, arrivee)
if ( nb_disques == 1 )
print depart.to_s + " -> " + arrivee.to_s + "\n"
else
Solution(nb_disques-1, depart, arrivee, pivot)
Solution(1, depart, pivot, arrivee)
Solution(nb_disques-1, pivot, depart, arrivee)
end
end

Program Livre Page 245 Vendredi, 16. mai 2008 8:13 08

Chapitre 18

Aperu dautres langages

245

print "Nombre de disques? "


nb_disques = gets.to_i
Solution(nb_disques, 1, 2, 3)

La simplicit de la syntaxe nest pas sans rappeler celle des langages Ada et Python.
Aujourdhui, Ruby est surtout connu dans le monde des applicatifs Web, par le biais
de lenvironnement RoR (Ruby On Rails) destin faciliter la cration de vritables
logiciels sexcutant sur un serveur distant et saffichant dans un navigateur Web.
Dans ce domaine, il offre une forte concurrence au clbre PHP.

Program Livre Page 246 Vendredi, 16. mai 2008 8:13 08

Program Livre Page 247 Vendredi, 16. mai 2008 8:13 08

19
Le mot de la fin
Nous voil arrivs au terme de notre initiation la programmation. Naturellement,
beaucoup daspects ont t passs sous silence. En particulier, nous navons pas
abord certaines structures de donnes, comme les arbres ou les graphes, essentielles pour modliser correctement bien des domaines de la ralit. De mme, nous
navons pas trait des environnements de dveloppement, qui sont des logiciels
spcialement conus pour lcriture de logiciels. Il est toutefois prfrable davoir
au pralable "tt" de la ligne de commande, comme nous lavons fait, pour tirer le
maximum de ces outils sophistiqus.
Si vous voulez progresser, nhsitez pas vous plonger dans les nombreuses documentations qui existent, aussi bien en librairie que sur Internet. Un bon moyen de trouver des ides et dapprendre des techniques consiste galement examiner le code
source dun programme crit par quelquun dautre. Cest trs formateur, bien que cela
ne soit videmment possible que dans le cas de logiciels Open Source.

Program Livre Page 248 Vendredi, 16. mai 2008 8:13 08

248

Initiation la programmation avec Python et C++

Cest galement un aspect de la programmation que nous avons laiss de ct :


lpineux problme des licences logicielles. En caricaturant un peu, on distingue deux
grandes familles.

Les logiciels propritaires, ceux que vous trouvez gnralement en vente, aussi
bien en magasin que sur Internet. Lorsque vous achetez un logiciel propritaire,
vous nachetez en ralit quun droit dutilisation du logiciel, pas le logiciel en
lui-mme. En particulier, il vous est le plus souvent interdit de le copier, de le
distribuer, de le modifier, et mme ne serait-ce que tenter de comprendre la
faon dont il fonctionne.

Les logiciels libres, qui sont principalement diffuss par Internet. Ceux-ci
suivent une philosophie pratiquement loppos de la prcdente : lorsque vous
obtenez un logiciel (que cela soit contre paiement ou gratuitement), vous obtenez
galement le droit de le diffuser, de ltudier, de le modifier. La seule contrepartie demande (mais pas vraiment exige) est de faire bnficier tout le monde de
vos amliorations.

Notez que cette distinction stablit sur la philosophie du logiciel, pas sur son prix. Un
logiciel propritaire peut tre gratuit, un logiciel libre peut tre payant. Mme si dans
la pratique, cest plutt linverse qui est vrifi.
Pratiquement tous les logiciels mentionns dans cet ouvrage sont des logiciels libres. Les
deux exceptions majeures tant les systmes dexploitation Windows et Mac OS X (bien
que ce dernier sappuie largement sur le systme Unix libre BSD).
Si cette question des licences logicielles vous semble une argutie juridique sans
grand intrt, nous ne saurions toutefois trop vous recommander dy prter quelque
attention. Une activit trs en vogue aux tats-Unis, qui arrive doucement en
Europe, consiste lancer des procs tort et travers dans le seul but dobtenir des
indemnits financires en rponse des prjudices, parfois bien illusoires. Si vous
voulez utiliser tel outil ou telle bibliothque, consultez toujours soigneusement sa
licence dutilisation. Et posez-vous la question de la destine de votre programme :
sil ne sagit que de raliser un profit maximal court terme ou sil sagit de rendre
service au plus grand nombre.
Cest maintenant vous de jouer. Attrapez votre clavier, dominez-le, pour crer les
programmes qui vous feront rver. Comme vous avez pu le constater, laventure
nest pas sans cueils. Soyez ambitieux dans vos projets, sans tre irraliste.

Program Livre Page 249 Vendredi, 16. mai 2008 8:13 08

Chapitre 19

Le mot de la fin

249

Commencez doucement, montez en puissance mesure que vous gagnez en exprience. Il en va de la programmation comme de bien dautres domaines : un moment
survient o vous dpasserez la technique pour enfin pouvoir vous exprimer pleinement.
Alors, vous apparatra lart de la programmation.

Program Livre Page 250 Vendredi, 16. mai 2008 8:13 08

Program Livre Page 251 Vendredi, 16. mai 2008 8:13 08

Glossaire

API
Acronyme dApplication Programming Interface. Ensemble des dclarations de
fonctions (ou classes) destines lutilisateur dune bibliothque, pour en utiliser les
services.

Bibliothque
Ensemble doutils (fonctions et classes) constituant un tout cohrent et regroup en
une seule entit. Divers langages utilisent plutt les termes de modules (Python),
paquetages (Ada) ou unit (Pascal), quoique ces notions ne soient pas exactement
quivalentes. Les outils dune bibliothque sont utiliss par lintermdiaire de son
API.

Classe
Type de donne, au mme titre quun entier ou une chane de caractres, dfini par les
moyens de la programmation oriente objet.

Compilation
Opration consistant transformer un fichier contenant le code source dun
programme crit dans un certain langage de programmation, en une reprsentation
binaire exploitable par le processeur de lordinateur.

Program Livre Page 252 Vendredi, 16. mai 2008 8:13 08

252

Initiation la programmation avec Python et C++

Constructeur
Mthode spciale dans une classe, excute lorsquon cre une instance de cette
classe.

Instance
Variable dont le type est une classe, comme 10 est une instance du type entier ou
"bonjour" est une instance du type chane de caractres.

Mthode
Fonction dfinie lintrieur dune classe, agissant sur les donnes de celle-ci.

Thread
Flux dinstructions dans un programme. Tout programme dexcution contient au
moins un thread. Il est possible de crer plusieurs threads, ce qui donne un programme
multithread pouvant exploiter la puissance offerte par plusieurs processeurs.

Type
Ensemble de valeurs et oprations pouvant leur tre appliques. De l est gnralement
dduite la faon de stocker ces valeurs dans la mmoire de lordinateur.

Variable
Nom donn une zone mmoire contenant une information et moyen daccder cette
zone. Le plus souvent, une variable est associ un type, dfinissant la nature de
linformation et la manire de la stocker.

Program Livre Page 253 Vendredi, 16. mai 2008 8:13 08

Index

A
Accesseur 108
Adresse 140
Alatoire 49
Allocation 142
API 208
Append 135
argc 191
argv 191

Conteneur 61
Couleurs 172

D
Dbordement 21
Dclarations 145
Drivation 105
Directive
#define 145
#endif 145
#ifndef 145
#include 146

Bytecode 238

C
Cast 43
Code source 10
Concatnation 25

E
diteur de texte 4
dition des liens 144
Enregistrements 68
perluette 75

Program Livre Page 254 Vendredi, 16. mai 2008 8:13 08

254

Initiation la programmation avec Python et C++

Espace de nommage 179


vnementielle 203

F
Fichiers
binaires 186
fermer 189
ouverture 188
textes 186
Fonctions 31
Fuite de mmoire 144

H
Hasard 48
Hritage 105

I
#include 146
Indentation 32

L
Library 210

O
Opration 67
Ordinogramme 88
Organigramme 88

P
Paramtres
par rfrence 75
par valeur 73
Pixel 172
Pointeur 140
Polymorphisme 114
Porte 35
Prcision 22
Private 117
Procdures 31
Projection 220
Protected 117

Q
QLCDNumber 197
QTime 195
QTimer 195

M
Machine virtuelle 238
Macro 167
make 149
Mthode 106
Mode texte 154
Modle/vue 220
Module 159

N
Namespace 179
Notation scientifique 22

R
Rptitive 56
RGB 172
RVB 172

S
Signal 165
Signature 111
Slot 165
Surdfinir 111

Program Livre Page 255 Vendredi, 16. mai 2008 8:13 08

Index

T
Temps rel 194
Thread 232
Transtypage 43, 239
Tuple 135
Type 19
constructeur 71
double 20
instance 69
int 20
notation pointe 69

prtraitement 146
std
string 20
vector<> 62

U
UML 89

V
Valeur de retour 37

255

Initiation
la programmation

Mise en place

avec Python et C++

Des programmes dans un programme :


les fonctions

Stockage de l'information : les variables

L'interactivit : changer avec l'utilisateur

La programmation porte de tous !


En moins de 300 pages, Yves Bailly russit la prouesse de
vous prsenter dans un style clair et concis toutes les notions
fondamentales de la programmation et de vous apprendre construire progressivement un vritable programme,
le jeu des Tours de Hano.
Louvrage aborde aussi bien les aspects des langages interprts (Python) que ceux des langages compils (C++).
Les diffrentes problmatiques sont illustres simultanment
dans ces deux langages, tablissant ainsi un parallle efficace et pdagogique.
Les bases acquises, vous apprendrez les techniques de la
programmation objet et crerez des interfaces graphiques
attrayantes. Enfin, en dernire partie, quelques pistes vous
aideront poursuivre votre apprentissage, de laffichage
en trois dimensions la programmation parallle, sans
oublier une petite prsentation dautres langages courants.
Trs concret, extrmement pdagogique, cet ouvrage est
accessible tous, et nexige pas de connaissances pralables en programmation.

Ronds-points et embranchements :
boucles et alternatives
Des variables composes
Les Tours de Hano
Le monde modlis : la programmation
oriente objet (POO)
Hano en objets
Mise en abme : la rcursivit
Spcificits propres au C++
Ouverture des fentres : programmation
graphique
Stockage de masse : manipuler les fichiers
Le temps qui passe
Rassembler et diffuser : les bibliothques
Introduction OpenGL
Aperu de la programmation parallle
Aperu d'autres langages

propos de lauteur
Yves Bailly est lauteur de nombreux articles parus dans Linux Magazine et
Linux Pratique, consacrs lutilisation de la bibliothque graphique Qt et
la dcouverte des langages Python, Ada et C++.

Niveau : Dbutant / Intermdiaire


Catgorie : Programmation
Configuration : Multiplate-forme

Pearson Education France


47 bis, rue des Vinaigriers
75010 Paris
Tl. : 01 72 74 90 00
Fax : 01 42 05 22 17
www.pearson.fr

ISBN : 978-2-7440-4086-3