Académique Documents
Professionnel Documents
Culture Documents
dition
Auteur
2.0
Jean-Franois Rabasse
Sommaire
1
Introduction
1.1
1.2
Mise en oeuvre
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.3
Compatibilits
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.4
Spcicits du langage
1.5
1.6
Le langage
Notes commentaires
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . .
Objets et classes
Lobjet logiciel
2.2
Classes et instances
2.3
Mcanismes de spcication
2.4
Interface de classe
2.5
Instanciations
2.6
Implmentation de classe
2.7
Visibilits
2.8
Cycle de vie
2.9
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . .
7
7
. . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . .
10
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
11
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . .
12
14
17
Appels et surcharges
3.1
Mthodes de classes
3.2
Surcharges de slection
3.3
Arguments optionnels
3.4
. . . . . . . . . . . . . . . . . . . . .
2.1
Notes commentaires
. . . . . . . . . . . . . . . . . . . . . . . . .
17
. . . . . . . . . . . . . . . . . . . . . . .
17
. . . . . . . . . . . . . . . . . . . . . . . .
18
. . . . . . . . . . . . . . . . . . . . . . . . .
19
Hritage
21
4.1
Comportement gnrique
. . . . . . . . . . . . . . . . . . . . . .
21
4.2
Classe de base
. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
22
4.3
4.4
Exploitation de lhritage
. . . . . . . . . . . . . . . . . . . .
23
. . . . . . . . . . . . . . . . . . . . . .
25
i
4.5
4.6
Objets composites
4.7
Accs privilgis
. . . . . . . . . . . . . . . . . . . . . . . . . . .
26
. . . . . . . . . . . . . . . . . . . . . . . . . .
26
Notes commentaires
. . . . . . . . . . . . . . . . . . . . . . . . .
27
29
Accs, protections
5.1
. . . . . . . . . . . . . . . . . . . . . . . .
29
5.2
Protection en criture
. . . . . . . . . . . . . . . . . . . . . . . .
30
5.3
Codage en ligne
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
32
5.4
5.5
Passages darguments
Conclusion
. . . . . . . . . . . . . . . . . . . . . . . . .
34
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
34
Polymorphisme
37
6.1
6.2
Mthodes virtuelles
6.3
Classes abstraites
6.4
Compatibilit hirarchique
. . . . . . . . . . . . . . . . . . . . .
37
. . . . . . . . . . . . . . . . . . . . . . . . .
38
. . . . . . . . . . . . . . . . . . . . . . . . . . .
40
Familles polymorphiques
. . . . . . . . . . . . . . . . . . . . . .
41
43
Complments
A.1
Membres statiques
A.2
Rsolution de porte
A.3
Qui suis-je ?
A.4
Structures
A.5
. . . . . . . . . . . . . . . . . . . . . . . . . .
43
. . . . . . . . . . . . . . . . . . . . . . . . .
45
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
45
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
46
. . . . . . . . . . . . . . . . . . . . . . .
47
49
Points dentre
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
49
B.2
Compatibilit C/C++
B.1
Interface objets
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
52
55
Surcharges doprateurs
C.1
. . . . . . . . . . . . . . . . . . . . . . .
55
C.2
. . . . . . . . . . . . . . . . . . . . . . .
57
C.3
Associativit
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
57
C.4
Surcharge de slection
C.5
Objets temporaires
C.6
Remarques
C.7
ii
Arithmtique complexe
Notes commentaires
. . . . . . . . . . . . . . . . . . . . . . . .
58
. . . . . . . . . . . . . . . . . . . . . . . . . .
59
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
61
. . . . . . . . . . . . . . . . . . . . . . . . .
62
1 - Sommaire
iii
Les streams
63
Les patrons
65
Exceptions
69
Index
71
iii
iv
Introduction
Ce manuel veut se donner un double objectif : prsenter les principes de la conception oriente objets1 et toutes les notions affrentes, et dautre part dtailler la mise
en oeuvre laide du langage C++.
En toute rigueur, ces deux points sont indpendants lun de lautre. La conception
oriente objets est un cadre de programmation, une nouvelle manire de penser les
applications informatiques, alors que C++ nest quun outil de mise en oeuvre parmi
dautres langages orients objets, SmallTalk, Java...
Ceci tant, il est souvent malcommode dexposer des concepts sans sappuyer sur
un matriel exemplaire. Ce manuel se voulant essentiellement pratique, les notions
objets seront donc illustres au fur et mesure par leur expression syntaxique en
C++ (voir note [ 1.1 ] page 4 en n de chapitre).
Il appartiendra au lecteur de sparer mentalement les deux aspects, concepts dune
part, programmation dautre part, pour pouvoir terme mettre en oeuvre un autre
outil de programmation (Java par exemple).
La structure de ce manuel va dailleurs dans ce sens. Tous les chapitres, partir du
chapitre 2 page 7, dtaillent les grands concepts de la programmation objets illustrs
par leur mise en oeuvre avec C++. Les annexes, elles, prsentent un certain nombre de fonctionnalits C++, utiles ou indispensables, mais quon ne retrouvera pas
ncessairement (ou pas sous la mme forme) dans dautres langages objets.
1.1
Le langage
Le langage C++, invent par Bjarne Stroustrup vers 1983 et dcrit par louvrage The
C++ Programming Language, est une volution oriente objets du langage C de Brian
Kernighan et Denis Ritchie.
Il sest enrichi, au cours de la dcennie 1980, paralllement la stabilisation et la
normalisation de C (norme C-Ansi de 1989).
C++ est actuellement en cours de normalisation, la rfrence syntaxique et fonctionnelle du langage tant louvrage de Stroustrup remis hauteur en 1992, The C++
Programming Language, 2nd edition.
Cet ouvrage est un "pav", relativement indigeste mais exhaustif et de qualit. Le
prsent manuel tant une initiation, il est loin de comporter toutes les nesses et
subtilits2 de la programmation en C++, louvrage de Stroustrup reste LA rfrence.
1.2
Mise en oeuvre
Il nexiste pas de standard universel pour dsigner les noms de chiers source C++.
Un certain nombre dextensions de noms existent : .cxx, .cc, .C, .cpp, .c++, ...,
selon les plateformes et compilateurs utiliss.
Lextension la plus universelle est .cxx et cest celle qui sera utilise dans tout ce
guide (voir note [ 1.2 ] page 4).
Les commandes de compilation les plus courantes sont :
DEC C++ sur machines VAX/VMS
cxx toto.cxx
DEC C++ sur machines Alpha/DEC-Unix
cxx -c toto.cxx
Sun C++ sur machines Sun/Solaris
CC -c toto.cxx
GNU C/C++ sur toutes machines Alpha, Sun, Linux
g++ -c toto.cxx
Voir galement la note [ 1.3 ] page 4.
Ldition de liens du ou des modules objets se fait de manire classique :
Linker VMS
link/exec=toto.exe toto.obj
Linkers Unix, selon plateforme
-o toto toto.o
1.3
Compatibilits
e
On remarquera que, contrairement la compilation de sources C, la compilation en
C++ nutilise pas doptions Ansi ou autres.
La syntaxe de lAnsi relative aux dclarations, prototypes de fonctions et autres, est
obligatoire en C++. Un compilateur C++ est capable de compiler un code source
crit en C-Ansi pur3.
(3)
1 - Introduction
Certains compilateurs, cest le cas du GNU C/C++, sont capables de traiter les deux
dialectes, sur dtection de lextension de nom du chier source (ou, parfois, via une
option spcique, cest le cas du Turbo-C/C++ de Borland).
Il y a donc compatibilit ascendante, C vers C++, au niveau de lcriture. Il nest par
contre pas possible de mlanger sans prcautions, au sein dun mme programme,
des modules objet compils en C et en C++ (voir lannexe B page 49).
1.4
1.5
page 55.
Entres/sorties par streams : cest une amlioration lgante des mcanismes de
lecture/criture de donnes. Lannexe D page 63 documente ces mcanismes.
Classe ou fonctions patrons (templates). Il sagit dun mcanisme daide
lcriture des codes C++, permettant de dnir, sous une forme semi-symbolique, des traitements similaires. Cette possibilit nest disponible que depuis
peu de temps dans les compilateurs C++. Elle est illustre en annexe E page 65.
Gestion derreurs et exceptions : C++ a introduit, tout rcemment, des principes
sophistiqus de gestion des erreurs et problmes en excution. Ces mcanismes
ne seront pas traits dans ce guide4.
1.5.2 Evolutions syntaxiques
Si la conception de programmes, en C++, na plus grand chose voir avec la conception en C, la syntaxe elle est trs peu modie par rapport au C-Ansi. Les diffrences
portent sur :
Introduction dun commentaire "jusqu n de ligne", symbolis par // :
int mini; // Valeur mini
en plus du commentaire "bloc" de C /* ... */, toujours reconnu.
Introduction de nouveaux mots-cls correspondant aux nouvelles fonctionnalits : class, public, private, protected, friend, virtual, this,
template, operator, new, delete, et au support derreurs quand il est
disponible : try, throw, rethrow, catch.
Modication ou extension de la smantique de certains mots-cls du C : extern, static, et de certains oprateurs : &, >>, <<. Cest sur ces derniers
points que les habitu(e)s de C devront se mer.
1.6
Notes commentaires
Le choix de C++, pour une introduction la conception objets, est du au fait que
cest aujourdhui LE langage de dveloppement orient objets.
Des langages antrieurs, comme SmallTalk, sont trop peu rpandus pour justier
un investissement. Dautres, comme le rcent langage Java, sont plus spcialiss et
prennent donc tout leur intrt dans des environnements bien particuliers (Internet).
[ 1.2 ] En tout tat de cause, il est fortement dconseill dutiliser des extensions par trop
spciques une plateforme. Des chiers source C, toto.c, et C++, toto.C, valides
sur une plateforme Unix vont provoquer des collisions de noms en cas de migration
vers un systme qui ne distingue pas la casse des noms de chiers (VMS, MS-DOS) !
[ 1.3 ] Le compilateur GNU est un compilateur mixte C/C++. On pourra compiler des
sources C++ en utilisant lune ou lautre des commandes gcc ou g++.
Pour ldition de liens, par contre, linvocation de g++ permet de congurer correctement toutes les options ncessaires au linker. En particulier, lorsquon utilise
des packages spciques C++, iostreams, nombres complexes, etc., les librairies seront
[ 1.1 ]
(4)
lheure actuelle tous les compilateurs ne supportent pas encore ces fonctionnalits.
1 - Introduction
Objets et classes
Le concept objet est lessence mme de la programmation oriente objets5. La vision
du monde (informatique) qua une application se ramne la construction et la manipulation dun ensemble dobjets. Partant du principe que tout, dans lunivers, peut
tre reprsent par un ou des objets6 la partie principale dune application objets se
rsoud : construire "lobjet application" (lequel met en oeuvre autant de sous-objets
que ncessaire) et le faire vivre jusqu la n du programme.
2.1
L'objet logiciel
Un objet est une entit informatique, autonome et modulaire, disposant de son propre
code de manipulation, les mthodes, et enfermant ses propres donnes, les attributs.
On parle galement de fonctions membres et donnes membres.
Les mthodes traduisent le : "que sait faire lobjet ?", les attributs traduisent le : "que
connait lobjet ?".
Lanalogie avec un objet concret, par exemple un tlviseur, est immdiate : un
tlviseur dispose de mthodes (boutons de face avant) permettant de lutiliser, "mise
en marche", "changement de canal", "rglage de volume", etc., et comporte en interne
les attributs (composants lectroniques) ncessaires son fonctionnement.
On remarquera, et cest galement vrai pour lobjet informatique, que lutilisation
dun objet suppose de savoir sen servir mais ne ncessite pas la connaissance intime
de son fonctionnement interne.
De plus, dans le cadre dun dveloppement (informatique ou industriel), la priorit
est donne la spcication des mthodes, ce que lobjet "doit pouvoir faire", sans
forcment savoir a priori comment le faire. Limplmentation interne sera ensuite la
mise en oeuvre des spcications.
2.2
Classes et instances
Dans la terminologie objets, on appelle classe la famille, le type, la nature de lobjet.
On appelle instance une occurence, une ralisation dun objet de classe donne.
"Mon tlviseur" (instance) nest pas le mme objet physique que celui (autre instance) de mon voisin. Et ceci mme si les deux appareils sont exactement du mme
modle (mme classe).
En programmation traditionnelle, ces notions existent sous les appellations type et
(5)
(6)
2.3
2.4
Interface de classe
En C++, une interface de classe est une description symbolique de lobjet, destine au
compilateur. A ce titre, elle comporte la dnition publique de la classe, mthodes utilisateurs, et limplmentation prive des attributs (et/ou mthodes internes) ncessaires
au fonctionnement.
Traditionnellement, une interface est crite dans un chier header spar, portant le
nom de la classe.
2 - Objets et classes
2.5
Instanciations
Le programme application dsirant utiliser une classe doit inclure dans son code
source le chier interface, puis dclarer une ou plusieurs instances de cette classe et
les manipuler :
int main()
{
float S;
Cercle toto;
// Variable locale
// Instance de cercle
/* Deplace toto */
toto.Move(150, 0);
/* Affiche sa surface */
S = toto.Area();
printf("Surface = %f\n", S);
/* Zoom et reaffiche */
toto.Zoom(1.5);
S = toto.Area();
printf("Surface apres zoom = %f\n", S);
return 0;
}
On remarquera la syntaxe des appels de mthodes. Le nom de la mthode est insufsant par lui-mme, il faut spcier lobjet auquel on veut lappliquer, i.e. le nom
de linstance.
2.6
Implmentation de classe
e
Lexemple ci-dessus montre que lon est capable dcrire du code source utilisant des
objets sans se proccuper de limplantation. (On peut savoir utiliser un tlviseur
sans tre lectronicien, sans savoir le construire !) Linterface et la connaissance de ce
que font les mthodes est sufsante.
Pratiquement, il faudra tout de mme assurer limplmentation de lobjet et lcriture
effective du code des mthodes. Traditionnellement, un objet est encod dans un
chier source portant le nom de la classe.
Par exemple, ici, cercle.cxx
/* Deplacement */
void Cercle::Move(float deltax, float deltay)
{
cX += deltax;
cY += deltay;
}
/* Echelle */
void Cercle::Zoom(float scale)
{
cR *= scale;
}
/* Surface */
float Cercle::Area()
{
return 3.14159 * cR * cR;
}
Lexemple est sufsament simpliste7 pour que la programmation nappelle pas de
(7)
10
2 - Objets et classes
11
commentaire particulier.
mthode :
NomClasse::NomMethode
Dans le code des mthodes, les attributs sont invoqus sous leur nom symbolique,
tel que dni dans la dclaration dinterface. En pratique, chaque instance dune
classe dispose de son propre jeu dattributs et cest au compilateur C++ de faire en
sorte que les donnes convenables soient manipules.
Ainsi, lorsquun code utilisateur manipule plusieurs instances :
Cercle c1, c2;
c1.Zoom(3.0);
c2.Zoom(0.5);
cest bien lattribut cR de linstance c1 qui sera multipli par 3, et lattribut cR de
linstance c2 qui sera divis par 2.
Le code dimplmentation dun objet est donc une programmation gnrique, se
rfrant "lobjet courant pour lappel".
Cest MAGIQUE !
Ou presque, voir le paragraphe A.3 page 45 en annexe.
2.7
Visibilits
e
On aura remarqu, dans la description dune interface de classe, paragraphe 2.4
page 8, la prsente de deux attributs de visibilit, public et private.
Pour des raisons lies limplmentation des compilateurs C++, une interface de
classe doit comporter la description exhaustive de lobjet, mthodes publiques mais
aussi tous les attributs et mthodes prives. Les attributs de visibilit ont pour rle
de signaler au compilateur les invocations autorises ou interdites :
le code interne lobjet, i.e. la programmation des mthodes de classe, a accs
tout, public ou priv !
le code externe, en gnral le code utilisateur, na accs quaux invocations
publiques. Laccs explicite un attribut priv nest pas autoris :
Cercle c1;
c1.Zoom(2.0);
c1.cR *= 2.0;
Ce mcanisme, appel aussi encapsulation des donnes, est l pour obliger le code
utilisateur passer par les mthodes prvues par le concepteur. Ce nest pas une
contrainte mais plutt une scurit.
Dune part, le concepteur de lobjet qui assure la maintenance des chiers relatifs
lobjet, chier interface machin.h et chier source machin.cxx, reste libre de
modier limplmentation interne, de changer des noms dattributs, den ajouter,
den supprimer. Le code utilisateur reste valide ( une recompilation prs) tant que
linterface publique est stable (et, en principe, elle doit ltre puisquelle rsulte dune
spcication initiale).
Dautre part, et cest souvent le cas en pratique, un objet important exige une cer11
taine cohrence sur ses attributs. Parfois, une simple modication de valeur peut
ncessiter un recalcul dautres attributs. En obligeant le code utilisateur passer par
des "guichets", le concepteur est assur de pouvoir maintenir un objet consistant.
Par dfaut, dans une classe C++, tout est priv. Ces attributs sont placer aux
endroits ad-hoc de la dnition de classe, on peut les alterner, la visibilit est valide
pour "tout ce qui suit" jusquau prochain attribut.
NB : dautres mcanismes de protection existent, voir le chapitre 5 page 29.
2.8
Cycle de vie
Un objet logiciel a une vie et une mort8.
Le langage offre la possibilit dimplmenter deux mthodes de classe particulires,
le constructeur et le destructeur. La compilation assure que le constructeur, sil est
spci, sera appel lors de la cration dune instance et avant la toute premire utilisation, et que le destructeur, sil est spci, sera appel juste avant la destruction
physique de lobjet.
Ainsi, dans lexemple ci-dessus, on ne sait pas, lorsquon instancie un objet cercle,
ce que valent ses attributs. Le rle typique dun constructeur est dassurer une
initialisation par dfaut qui soit convenable.
Ces mthodes se dclarent sans type et avec un nom impos, NomClasse pour un
constructeur, ~NomClasse pour un destructeur.
Linterface de classe (chier cercle.h) deviendrait :
class Cercle
{
// Constructeur, destructeur
public:
Cercle();
Cercle();
// Methodes utilisateurs
public:
void
Move(float deltax, float deltay);
etc...
(8)
12
On en est tous l !
2 - Objets et classes
13
/* Destructeur */
Cercle:: Cercle()
{
// Rien a faire de special !
}
Le mcanisme dinitialisation par constructeur est, en pratique, indispensable, cest
le moyen dassurer que tout objet commence sa vie avec une conguration plausible.
Dans un environnement objets bien conu, les mcanismes dinstanciation doivent
toujours produire des objets utilisables en ltat et le code utilisateur ne devrait avoir
recongurer que lorsque les valeurs par dfaut sont inacceptables.
Un autre mcanisme trs intressant est la possibilit qua le concepteur dobjet de
proposer diffrents constructeurs de classe. Par exemple, on pourrait proposer un
constructeur de cercles prenant une valeur initiale de rayon en argument.
Interface de classe :
class Cercle
{
// Constructeurs, destructeur
public:
Cercle();
Cercle(float rayon);
Cercle();
...
Implmentation :
/* Constructeur par defaut */
Cercle::Cercle()
{
cX = cY = 0.0;
// A lorigine par defaut
cR = 1.0;
// Rayon unite
}
/* Constructeur avec rayon */
Cercle::Cercle(float rayon)
{
cX = cY = 0.0;
// A lorigine par defaut
cR = rayon;
// Rayon utilisateur
}
Le code utilisateur pourra ensuite instancier des cercles en spciant tel ou tel con-
13
structeur :
Cercle c1;
c1.Cercle();
// Non !
Limplantation dun destructeur (qui lui, ne peut tre multiple ni comporter darguments) est moins frquente. Dans lexemple ci-dessus elle est inutile puisquon ne
fait rien. Elle se justie lorsquun objet ncessite des tches de "mnage" lors de sa
destruction : fermeture dun chier qui aurait t ouvert par lobjet, libration dune
zone mmoire alloue, etc. Comme le destructeur est galement appel automatiquement, on est ainsi sr de ne rien oublier.
2.9
// Global, statique
// Local
// Nouveau contexte
// Local
Un objet est cr (i.e. la mmoire est alloue puis le constructeur est appel) lors
de sa dclaration. Un objet global (c1 dans lexemple ci-dessus) est construit au
lancement du programme.
14
2 - Objets et classes
15
Un objet est dtruit (i.e. le destructeur est appel puis la mmoire est libre) lorsque
quil devient hors de porte, lorsque lon quitte le contexte qui le dclarait. Un objet
global est dtruit en n de programme.
NB : on voit ici toute la puissance apporte par la possibilit de programmer un
destructeur lorsquun objet ncessite des tches de "mnage", libration de ressources
et autres : un simple return, nimporte o dans une fonction, fera quitter le contexte et invoquera implicitement toutes les oprations de nettoyage sur tous les objets
locaux existants.
// Pointeur de Cercle
pc = new Cercle;
// Allocation
pc->Zoom(3.5);
// Appel methode
(*pc).Zoom(5.0);
S = pc->Area();
...
delete pc;
// Appel methode
// Liberation
A noter que ces oprateurs sont galement utilisables sur des variables simples et
15
// Allocation scalaire
// Allocation tableau
// Liberation scalaire
// Liberation tableau
16
3
3.1
Appels et surcharges
Mthodes de classes
e
Contrairement aux autres langages de programmation o la porte dun identicateur est unique pour une application donne, une et une seule fonction ou routine
peut sappeler toto pour tout un programme, en C++ la porte dun identicateur
dpend du contexte dappel.
Imaginons un autre type dobjet gomtrique, le rectangle, qui disposerait des mmes
mthodes que le cercle :
class Rectangle
{
// Methodes utilisateurs
public:
void
Move(float deltax, float deltay);
void
Zoom(float scale);
float
Area();
etc.
Dans le code utilisateur suivant :
Cercle c1;
Rectangle r1;
c1.Zoom(3.5);
r1.Zoom(4.0);
le compilateur lve les ambiguts de nom selon le contexte dappel, lcriture
c1.Zoom dsigne la mthode Zoom de la classe Cercle, applique lobjet c1, alors
que r1.Zoom dsigne la mthode Zoom de la classe Rectangle, applique r1.
La syntaxe dimplmentation des mthodes de classes (cf. 2.6 page 10), est dailleurs
parlante, les points dentre sont Cercle::Zoom ou Rectangle::Zoom.
3.2
Surcharges de slection
e
(Lappelation surcharge de slection est propre ce guide, voir la note [ 3.1 ] page 19
en n de chapitre.)
Un second mcanisme, trs puissant, permet dimplanter dans une mme classe
plusieurs mthodes de mme nom, ds linstant que la liste darguments dappel,
nombre et nature, peut lever lambigut.
17
Par exemple :
class Machin
{
void Calcul(int);
// Interface 1 entier, Ci
void Calcul(int, int);
// Interface 2 entiers, Cii
void Calcul(float, float); // Interface 2 reels, Cff
...
Lors de lutilisation, cest le compilateur qui va choisir lappel correct en fonction du
contexte :
int n;
float x, y;
Machin M;
M.Calcul(n,
M.Calcul(x,
M.Calcul(x,
M.Calcul(x,
3);
y);
1);
n);
//
//
//
//
Appel
Appel
Appel
Appel
Cii
Cff
Cff, conversion 1 -> 1.0
Cff avec cast (float)n
M.Calcul(5);
// Appel Ci
M.Calcul(x);
// ERREUR !
M.Calcul((int)x); // Cast explicite, appel Ci
Contrairement dautres langages o la mme fonction peut exister sous tout un
tas de noms diffrents selon les types darguments (voir en Fortran les sin, dsin,
imod, jmod, amod, etc.), en C++, et sous rserve davoir prvu diffrentes congurations opratoires, on invoque tel ou tel traitement sous une appelation fonctionnelle
gnrique en laissant au compilateur le soin de traiter les dtails dintendance !
Ce mcanisme a dj t utilis, sans explications, dans le cas du cercle et ses deux
constructeurs, Cercle() et Cercle(float). Cest exactement une mise en oeuvre
de ce mcanisme de slection.
3.3
Arguments optionnels
Un dernier mcanisme permet de dclarer des mthodes avec des arguments optionnels et valeurs par dfaut sils ne sont pas fournis lors de lappel.
Dans lexemple du cercle, on aurait pu nimplanter quun seul constructeur, celui avec
un argument rayon optionnel. Si largument nest pas fourni lappel, le compilateur
utilisera la valeur par dfaut.
Linterface est la suivante :
class Cercle
{
Cercle(float rayon = 1.0);
...
18
3 - Appels et surcharges
19
Dans cet exemple, lintrt est une simplication de limplmentation, une seule
mthode au lieu de deux.
Un autre type dutilisation, trs utile en pratique, concerne lvolution de dveloppements C++. Supposons quun jour la librairie dobjets gomtriques, Cercle,
Rectangle, etc., volue vers une version 3D.
La classe Cercle devra tre revue, avec trois attributs coordonnes du centre, cX,
cY, cZ. La mthode effectuant un dplacement relatif deviendra :
void Move(float dx, float dy, float dz);
Doit-on remettre hauteur toutes les applications 2D existantes ? Pas ncessairement,
on peut dcider que les anciennes applications 2D travailleront dans le plan Z = 0;
il suft alors dimplanter linterface ainsi :
void Move(float dx, float dy, float dz = 0.0);
pour que tous les codes existants utilisant lancienne interface, deux arguments :
Cercle c1;
float dx, dy;
...
c1.Move(dx, dy);
restent recompilables en ltat !
3.4
[ 3.1 ]
Notes commentaires
Il existe une grosse ambigut lexicale, dans la terminologie franaise C++. La
terminologie anglo-saxonne utilise deux vocables : overstrike, traduit en franais par
surcharge, et overload, traduit en franais par ... surcharge !
Sans vouloir relancer les ternelles polmiques sur la dfense de la langue franaise
(dont je suis un fervent partisan), il nest pas acceptable de laisser en ltat de
telles dgradations smantiques. Cest pourquoi ce guide a choisi, arbitrairement,
dutiliser lappelation surcharge de slection pour la surcharge overstrike, prsente ici
et consistant dnir des homonymes parmi lesquels le compilateur fera son choix.
La surcharge overload, prsente plus loin dans ce manuel, consistant rednir par
hritage une mthode dune classe anctre, sera appele surcharge fonctionnelle.
19
20
Hritage
e
Aprs le concept de classe le concept dhritage est la seconde grande notion en
programmation oriente objets.
Il sagit, dans le cadre de dveloppements importants, de construire des ensembles
de classes, selon une structure arborescente qui rappelle les mcanismes de taxinomie
quon trouve en sciences de la nature : les chevaux sont des onguls qui sont des
mammifres qui sont des vertbrs qui sont des animaux. A chaque niveau, la classe
apporte ses spcicits un hritage commun, on parlera de ses classes anctres.
Pratiquement, la conception informatique dune arborescence dobjets est loin dtre
un exercice trivial. La rexion et lintuition jouent un rle important mais on chappe
rarement un processus itratif consistant dtecter, en cours de dveloppement,
un oubli de conception.
Cela na rien de tragique, cest mme le lot commun du concepteur, et on arrive
rectier une conception relativement facilement. Un cas de gure trs frquent est la
dtection, aprs coup, dun anctre commun :
4.1
Comportement gnrique
e e
Dans le cadre de la conception dune petite librairie de gures gomtriques,
reprenons lexemple du Cercle et ajoutons une classe Rectangle, disposant des
mmes mthodes application, savoir Move, Zoom et Area.
Limplmentation interne devra, elle, comporter deux attributs de dimensions, largeur
et hauteur, la place du rayon.
La programmation nave, analogique, conduit dnir linterface suivante (chier
rectangle.h) :
class Rectangle
{
// Methodes utilisateurs
public:
void
Move(float deltax, float deltay);
void
Zoom(float scale);
float
Area();
// Attributs implantation
private:
float
cX, cY;
// Position centre
float
cL, cH;
// Largeur, hauteur
};
21
4.2
Classe de base
On construira une classe, disposant de tout le support gomtrique souhait (ici 2D),
sans prjuger de laspect exact de cette gure. On pourra imaginer un constructeur
permettant de xer la position initiale.
(9)
22
4 - Hritage
e
23
4.3
23
24
4 - Hritage
e
4.4
25
Exploitation de l'hritage
e
Si lhritage est pris en compte explicitement, pour le travail de dveloppements
dobjets, il est par contre transparent au code utilisateur :
Cercle c1;
c1.Zoom(3.5);
c1.Move(10, 2);
Si lon compare aux exemples 2.5 page 9, on voit que la mise en oeuvre est inchange. Bien que la classe Cercle ne dispose pas explicitement dune mthode
Move, le simple fait quun cercle soit dabord une gure fait que Cercle hrite de
la fonctionnalit Figure::Move.
Autrement dit, une sousclasse sait faire, au minimum, tout ce que savent faire ses
anctres. On dispose l dun mcanisme remarquablement puissant et concis (au prix
de compilateurs remarquablement complexes10).
De la mme manire, on pourra driver de Figure, des sousclasses Rectangle,
Triangle, etc., toutes exploitant les mcanismes de position qui sont, on le rappelle,
crits une seule fois dans un seul chier source, figure.cxx.
Cette architecture se prte merveille aux volutions dun dveloppement : Lorsque
lon souhaitera, un jour, ajouter ce petit environnement exemple du code graphique,
il faudra implanter dans chaque classe une mthode de dessin spcique, un Draw()
pour un cercle est diffrent dun Draw() pour un rectangle. Par contre, tout le
code support dattributs graphiques (couleur, paisseur de trait, type de trait, plein,
(10)
25
pointill, etc.) pourra tre implant, une fois et une seule, dans la classe Figure.
Enn, ce mcanisme peut se rpter indniment. Toute sousclasse est utilisable
comme superclasse pour une drivation. Dans notre exemple il aurait t plus
rigoureux (mathmatiquement parlant) de driver de Figure une classe Ellipse
deux attributs, cA et cB, puis de driver de celle-ci la classe Cercle. Cette dernire
se bornerait assurer lgalit des demi-axes la construction, cA = cB = R.
Mme remarque pour une drivation de Rectangle en Carre.
4.5
Accs privilgis
e
e e
Les mcanismes de protection daccs (cf 2.7 page 11), grs en tout ou rien par les
attributs de visibilit public ou private, manquent un peu de souplesse.
En particulier, il peut arriver que pour lcriture dune classe drive, on ait besoin
daccder des attributs ou mthodes non publics de la superclasse (ou dune anctre
de la superclasse).
La rvision C++ de 1992 a introduit un troisime attribut de visibilit, protected,
qui est un intermdiaire entre les deux autres et qui rend accessible tout code dune
mthode de classe drive tout en verrouillant laccs depuis du code extrieur.
On pourra ainsi moduler assez nement ce qui est en accs public, dans une classe,
ce qui est strictement priv, ce qui nest pas public mais utile certaines classes
drives. Voir note [ 4.1 ] page 27 en n de chapitre.
4.6
Objets composites
Un objet peut tout fait contenir, parmi ses attributs, dautres objets. Par exemple,
on pourrait imaginer une gure composite comportant deux cercles concentriques :
class Machin : public Figure
{
public:
Machin(float xpos, float ypos);
private:
Cercle aC1;
Cercle aC2;
...
};
Par dfaut, la construction dun objet composite, tous les sous-objets sont construits
avec leur constructeur par dfaut (i.e. sans arguments) sil existe :
Machin::Machin(float xpos, float ypos)
: Figure(xpos, ypos)
{
...
}
Dans cet exemple, les instances de cercle aC1 et aC2 sont initialiss via le constructeur
26
4 - Hritage
e
27
Cercle().
On peut spcier un constructeur diffrent, par exemple avec des arguments, avant
le corps de fonction :
Machin::Machin(float xpos, float ypos)
: Figure(xpos, ypos),
aC1(xpos, ypos, 2.0), aC2(xpos, ypos, 0.5)
{
...
}
On notera la diffrence dcriture : le constructeur superclasse est invoqu par son
nom gnrique, Figure, alors que les constructions des attributs, ou objets membres,
utilisent le nom spcique des instances.
Une alternative consiste laisser les constructions par dfaut puis congurer les
sous-objets explicitement :
Machin::Machin(float xpos, float ypos)
: Figure(xpos, ypos)
{
aC1.Move(xpos, ypos);
aC1.Zoom(2.0);
aC2.Move(xpos, ypos);
aC2.Zoom(0.5);
}
Pour les destructeurs, on na strictement rien faire, le compilateur C++ gnre le
code ncessaire pour que les destructeurs des sous-objets soient invoqus lors de la
destruction de lobjet principal.
4.7
[ 4.1 ]
Notes commentaires
En rgle gnrale, au cours dun dveloppement, il est conseill de jouer la "scurit
maximale" (le mode par dfaut, en C++) en dclarant private tout ce qui ne
concerne pas strictement linterface publique telle que spcie.
Par la suite, on modiera au cas par cas les visibilits en fonction des besoins de
telle ou telle classe drive et en choisissant le meilleur mcanisme (voir galement
le paragraphe 5.3 page 32).
Surtout pour des dveloppements importants, cest toujours une faute de dclarer
a priori tout public, uniquement pour "ne plus avoir sembter ensuite avec les
protections" !
27
28
Accs, protections
e
Le bug est la plaie de linformatique ! Les causes de bugs11 sont multiples, proportionnelles aux capacits dimagination de ltre humain.
Cela dit, une des causes majeures est due aux effets de bords et pollutions de
donnes. Laphorisme sous-jacent est que moins on touche aux donnes, dans un
programme, moins on risque de les modier par inadvertance.
Le niveau de abilit dun langage de programmation est directement li aux capacits du-dit langage cacher le plus de choses possibles et tous les langages
invents depuis quarante ans ont cherch dvelopper ces mcanismes, ce qui fait
que le classement du moins able au plus able suit peu prs lordre historique :
Fortran, C, Pascal, C-Ansi, C++
5.1
Passages d'arguments
Comme C, C++ passe les arguments dappels par valeur (i.e. copie locale au code
appel de la valeur dappel). Si largument dappel est une variable, celle-ci nest
donc jamais modie dans le contexte appelant, quelles que soient les avanies que
peut subir largument dans le code appel.
Il existe toujours des cas de programmation o lon a besoin de passer une variable
de retour, pratiquement ds quun traitement doit fournir plus dun rsultat et que
donc la fonction informatique, avec sa valeur de retour unique, nest pas sufsante.
Le langage C, disposant de pointeurs de donnes, rsoud ce problme en utilisant
des adresses explicites :
void toto(int* arg)
{
*arg = 10;
}
...
/* Appel */
int var;
toto(&var);
C++ dispose des mmes mcanismes mais galement, et cest nouveau par rapport
C, dun passage par rfrence, analogue ce que fait Fortran en standard, utilisant la
(11)
Dsol pour les puristes, je ne sais pas me rsoudre crire : bogue comme le voudrait notre Druon
national...
29
syntaxe suivante :
void toto(int& arg)
{
arg = 10;
// Et non pas :
}
*arg !
...
/* Appel */
int var;
toto(var);
Leffet est analogue au passage explicite dadresses, mais on est affranchi des critures
dindirection, l encore cest le compilateur qui se dbrouille.
La seule obligation se situe au niveau du prototype de lappel. La coexistence dun
double mcanisme, par valeur ou par rfrence, suppose une dclaration explicite,
utilisant le symbole & :
void toto(int val, int& ref);
NB : les Pascaliennes et Pascaliens reconnatront :
procedure toto(val : integer, var ref : integer);
Attention : une donne passe par rfrence nest pas un pointeur sur donne mais
se manipule, syntaxiquement, comme une donne locale :
void toto(Cercle& a, Cercle* b)
{
a.Zoom(10);
// Invocation directe
b->Zoom(10);
// Invocation via pointeur
}
// Appel
Cercle c1;
Cercle c2;
toto(c1, &c2);
Mme si, au nal, le rsultat est le mme, la syntaxe est diffrente.
5.2
Protection en criture
e
Un mcanisme de protection, introduit dans le C-Ansi, utilise un qualieur const
pour indiquer que quelque chose est consultable mais ne peut tre modi.
30
5 - Acc`s, protections
e
31
Par exemple :
void toto(int *p1, const int *p2)
{
int a;
a =
a =
*p1
*p2
*p1;
*p2;
= 0;
= 0;
//
//
//
//
Correct
Correct
Correct
ERREUR !
31
5.3
Codage en ligne
Les volutions des architectures processeurs, pipeline, superscalaire, ont conduit les
compilateurs implanter des mcanismes de codage en ligne, consistant compiler
non plus des appels des fonctions, mais rpliquer sur place, lendroit de lappel,
le code de ces fonctions. Le but est de faciliter au maximum une excution en
squence, sans branchements, sans droutages.
Les langages ont suivi le mouvement et le C-Ansi a introduit un qualieur inline
permettant dcrire des fonctions en spciant un codage en ligne.
En C++, on pourra ainsi implanter, directement dans le chier interface (et non plus
dans le chier source, la dnition en ligne doit tre disponible lors de la compilation
de tous les modules application), des mthodes (courtes) :
class Cercle : public Figure
{
...
void Zoom(float scale);
...
};
inline void Cercle::Zoom(float scale)
{
cR *= scale;
}
On peut faire mieux, par convention du C++, toute mthode encode directement
dans la dclaration de classe est inline !
class Cercle : public Figure
{
...
void Zoom(float scale) { cR *= scale; }
...
};
32
5 - Acc`s, protections
e
33
On conserve ainsi une interface "propre", de type fonction, tout en ayant les avantages
en performances dun codage direct.
Ce mcanisme est utilis intensivement pour implanter ce quon appelle des accesseurs, savoir des mthodes permettant la manipulation des attributs dun objet.
Par exemple, on ajoute notre petite librairie un support graphique utilisant un
attribut "couleur de la gure". Cet attribut est un code numrique, index dans une
palette par exemple.
Le code application doit pouvoir dnir lindex et accder sa valeur courante :
class Figure
{
...
// Support couleur
private:
int
iColor;
public:
void SetColor(int color)
int
GetColor() const
};
{ iColor = color; }
{ return iColor; }
33
5.4
// Classe amie
// Fonction amie
private:
int mon_attrib;
};
Moyennant quoi, tous les accs sont possibles, sur des objets de la la classe Truc,
depuis le code de la fonction toto :
void toto(Truc& arg)
{
arg.mon_attrib = 0;
...
}
// Attribut prive !
5.5
Conclusion
Ce chapitre avait pour but de dtailler tous les mcanismes de protection disponibles
en C++.
Il faut casser un mythe : tout cela nest pas QUE du dtail !
Cest sans aucun intrt tant que lon se limite la programmation dun "Hello
world !", et lorsquun dveloppement, commenc tout petit, ni par devenir norme
(plusieurs dizaines ou centaines de classes, imbriques dans des arborescences complexes, possdant chacune des dizaines de mthodes) il est trop tard pour "commencer
penser la abilit du code" !
34
5 - Acc`s, protections
e
35
Un concepteur de classe doit cultiver sa paranoa, une bonne charte de dveloppement pourrait tre :
a priori, tout est verrouill, private, attributs et mthodes.
toute mthode qui laisse lobjet intact est dclare const
laccs un attribut se fera par un accesseur de type GetXXX, qui est toujours
const
le positionnement dun attribut se fera par un accesseur de type SetXXX.
les visibilits des mthodes (ou accesseurs) seront public uniquement si elles
font partie de linterface de spcication de la classe, sinon private
on assouplira la rgle prcdente en changeant la visibilit private en protected pour les mthodes qui savrent ncessaires limplantation de classes
drives
Pour tre honnte, il faut dire que le zro bug nexiste pas, mais cest une asymptote
et tout doit tre tent pour sen rapprocher, au moins en ce qui concerne les effets de
bords et les pollutions. Pour les bugs caractre algorithmique, il nexiste pas daide
spcique langage.
35
36
Polymorphisme
Le polymorphisme est le troisime grand concept de la programmation objets. Lide
de base est de pouvoir manipuler des objets dont on ne connait pas la nature exacte,
plus prcisement dont on connait simplement un anctre rel, on parlera de classe
support, ou un anctre thorique, sans existence informatique12, on parlera alors de
classe abstraite.
En poussant plus loin, ce concept permet dcrire aujourdhui du code qui manipule
des objets connus et des objets inconnus, qui ne seront dvelopps que dans le futur,
et ce sans mme avoir recompiler le code en question.
On peut poser le problme sur lexemple suivant, utilisant une petite librairie de
gures gomtriques13.
On se propose de grer un groupe de plusieurs gures, des cercles, des rectangles, et
pouvoir effectuer des oprations densemble : dplacer tout le monde, zoomer tout
le monde, calculer un barycentre partir des surfaces, etc.
De plus, on souhaite que le travail en question reste valide, lorsque dans quelques
mois, la librairie se sera enrichie dautres gures, triangles, trapzes, et tout ce qui
aura t imagin dici l et quon ignore aujourdhui.
La technique la plus classique, pour implanter un groupe quelconque, passe par une
table de pointeurs dobjets quon cre dynamiquement et quon remplira ensuite :
int
total = 20;
XXX** table;
table = new (XXX*)[total];
remplir(table, total);
//
//
//
//
Nombre dobjets
Table des pointeurs
Allocation
Remplissage, ouf !
Tout cela est bel et bon, un dtail prs : XXX ? Une table de pointeurs de quoi ?
6.1
Compatibilit hirarchique
e e
C++ assure une compatibilit ascendante des objets, savoir que tout objet, connu
par pointeur ou par rfrence, est utilisable en lieu et place dun de ses anctres,
puisque tout objet est une de ses superclasses.
Si une classe Cercle drive dune classe Figure, on peut crire des choses comme :
Figure* PF = new Cercle;
Linverse est videmment faux :
Cercle* PC = new Figure;
(12)
(13)
Cest un must informatique que dcrire des programmes qui manipulent des choses qui nont pas
dexistence informatique !
a alors ! On sy attendait si peu...
37
Le compilateur C++ rejettera une telle criture, et mme si lon voulait tricher en
utilisant un cast :
Cercle* PC = (Cercle*)(new Figure);
la sanction sera immdiate en excution14.
Ceci nous permet donc de manipuler tout ce qui drive dun anctre commun, ici
des Figure :
int
total = 20;
Figure** table;
// Nombre dobjets
// Table des pointeurs
// Allocation
// Remplissage, ouf !
6.2
Mthodes virtuelles
e
La premire technique consiste implanter une mthode virtuelle dans la superclasse :
class Figure
{
...
virtual void Zoom(float scale);
...
(14)
38
"Mettez un lapin la peau dun chacal, vous ne ferez pas croire que cest un chacal un autre
chacal !" (Proverbe touareg).
6 - Polymorphisme
39
39
Figure :
void remplir(Figure** table, int total)
{
table[0] = new Cercle;
table[1] = new Figure;
...
Rien ne linterdit et on aura un fonctionnement stupide !
Pratiquement, la surcharge fonctionnelle sert implanter dans une classe de base
des algorithmes gnraux, utilisables tels quels dans la majorit des cas. charge
pour une classe drive particulire, qui ncessite un traitement non standard, de
surcharger avec son propre algorithme spcialis.
De plus, il est possible dans une surcharge dutiliser le code de la mthode originale
de la classe anctre, sil prsente un intrt :
class Parent
{
...
virtual void Hello(int world);
...
class Enfant : public Parent
{
...
void Hello(int world);
...
// Methode virtuelle
// Heritage
// Surcharge
Si lon appelle la mthode Hello sur un objet Enfant, ou mme, par polymorphisme, partir dun pointeur de Parent qui est en ralit un Enfant, cest bien la
mthode Enfant::Hello qui est invoque.
Cette mthode peut utiliser explicitement le code de sa superclasse :
void Enfant::Hello(int world)
{
Parent::Hello(world); // Appel explicite
...
// Complements
}
La syntaxe Parent::Hello sappelle rsolution de porte. En effet, par dfaut, un
identicateur de mthode ou dattribut sapplique la classe courante et crire un
appel Hello dans la mthode Enfant::Hello va provoquer un appel rcursif
inni vite suivi du dsagrable :
Bus error !
6.3
Classes abstraites
Notre problme peut se traiter de manire plus lgante, et plus rjouissante intellectuellement parlant !
On veut disposer dune mthode Zoom dans la classe Figure pour pouvoir zoomer
nos objets, connus par pointeurs, comme si ctait des Figure. Donc mthode
40
6 - Polymorphisme
41
virtuelle.
Mais on sait quil sagit dun artice puisquune Figure ne se zoome pas. Un
Cercle oui, un Rectangle oui, mais pas une Figure. On va alors prciser que
la classe Figure possde bien une mthode Zoom, mais quen fait, cette mthode
nexiste pas15 !
Ce qui, en langue C++, se dit :
class Figure
{
...
virtual void Zoom(float scale) = 0;
...
La syntaxe est rude, mthode = 0, aucun doute le C++ est bien un digne hritier de
C ! Elle nexiste pas, ce qui ne lempche pas dtre virtual16.
On vient de crer une classe abstraite. Une classe abstraite sait faire (potentiellement)
tout un tas de choses, possde telle ou telle mthode, mais si une au moins des
mthodes de la classe nexiste pas, la classe toute entire ne peut avoir dexistence
lgale.
On ne pourra jamais instancier, directement ou indirectement, une telle classe :
Figure F1;
Figure* PF;
PF = new Figure;
// ERREUR
// ERREUR
6.4
Familles polymorphiques
Les mcanismes de surcharges fonctionnelle et dabstraction sont gnralisables sur
toute une arborescence. La conception de grosses applications nest pas un exercice
trivial et dborde du cadre de ce manuel dintroduction.
Certaines applications peuvent ncessiter la conception dune classe anctre, abstraite.
Cette classe est ensuite drive en sousclasses qui commencent implanter telles ou
telles mthodes virtuelles, tout en restant elles-mmes abstraites. La rgle de base,
pour pouvoir instancier une classe, est que toutes les mthodes de cette classe et de
tous ses anctres existent bel et bien.
(15)
(16)
41
Le problme est que le compilateur va dtruire des objets connus via des pointeurs
de Figure. Il na aucun moyen de savoir quen ralit il sagit de Cercle, de
Rectangle, ou mme de classes qui ont t cres par la suite et qui nexistaient pas
au moment de la compilation.
Si une classe drive a besoin de faire un traitement spcique dans son destructeur,
celui-ci ne sera jamais appel.
Sauf... si lon utilise le mcanisme de surcharge fonctionnelle pour le destructeur17 !
Une rgle, trs gnrale, est que toute classe destine servir de support polymorphique doit implmenter un destructeur virtuel, mme vide ! Ceci garanti un
fonctionnement correct lors de la destruction de classes drives :
class Figure
{
public:
// Constructeurs
Figure();
Figure(float xpos, float ypos);
// Destructeur vide, en ligne
virtual Figure() { }
NB : certains compilateurs sympathiques (gcc en particulier) pousseront la courtoisie
jusqu signaler par un warning que telle classe possde des mthodes virtuelles mais
que son destructeur ne lest pas.
(17)
42
Annexe A
Complments
e
Contrairement un membre de classe, attribut ou mthode, qui na de sens que relativement une instance ralise de cette classe, un membre statique existe toujours,
en un seul exemplaire dans le cas dun attribut, mme en labsence totale dinstances
de cette classe.
Il sagit, en quelque sorte, dun patrimoine commun toutes les instances de cette
classe.
Pour illustrer ceci, imaginons dimplanter dans une classe un systme de comptage
dinstances. Le code application souhaite pouvoir connatre, tout moment, le nombre dobjets raliss de cette classe. Ce compteur doit tre unique, bien sr, et non
dupliqu dans chaque instance. Dautre part il doit exister et tre accessible mme
si aucune instance de la classe nest disponible (on voudra savoir sil nexiste, un
moment donn, aucune instance).
Dans linterface de classe, on implantera un compteur statique, et un accesseur public,
galement statique :
class Truc
{
// Constructeur, destructeur
public:
Truc();
Truc();
// Comptage
private:
static unsigned
public:
static unsigned
...
iTotal;
GetTotal()
{ return iTotal; }
43
Implmentation :
/* Attribut(s) statique(s) */
unsigned Truc::iTotal = 0;
/* Constructeur */
Truc::Truc()
{
...
iTotal++;
}
/* Destructeur */
Truc:: Truc()
{
iTotal--;
}
On remarquera que, contrairement un attribut "normal", un attribut statique
simplmente explicitement. Cest, en fait, une variable globale dont la dure de vie
est celle du programme et qui DOIT tre initialise correctement la dclaration.
Les mthodes statiques, elles, simplantent de la manire classique, codage explicite
dans limplmentation ou codage en ligne dans la dclaration (cest le cas ici).
Le mcanisme ci-dessus est parfaitement sr. La variable est prive, et ne peut tre
modie que par cration ou destruction dune instance de cette classe (ou dune
classe drive). (Ne pas oublier, si lon implante plusieurs variantes de constructeurs,
dincrmenter le compteur dans chaque constructeur !)
Laccs public peut se faire de deux manires. Si lon dispose dune instance "sous la
main", on effectue une invocation classique :
Truc t1;
...
unsigned total = t1.GetTotal();
printf("Il existe %u Trucs !\n", total);
Sinon, on peut utiliser le mcanisme dj signal de rsolution de porte :
unsigned total = Truc::GetTotal();
NB : cet exemple est assez souvent mis en oeuvre, au moins lors de dveloppements
importants. Lorsquune application manipule des objets par centaines, en gestion
dynamique travers new et delete, une bonne ide est de vrier, juste en n de
programme, quon na pas oubli des delete ici ou l !
Ce nest pas gnant en soi puisque, lors de larrt dun processus, toute la mmoire
est libre, mais cest le symptme dune programmation malpropre quelque part ou
dune erreur de conception.
44
7 - Complments
e
45
sqrt(double x);
calcul(double x);
Que se passe-t-il si, dans une mthode de cette classe, par exemple la mthode calcul, on veut utiliser la fonction racine carre de la librairie C ? On peut dabord
considrer que cest bien fait pour nous, on navait qu rchir et choisir un autre
nom pour notre mthode !
On peut aussi utiliser la rsolution de porte, sans nom de classe, puisque sqrt de
la librairie mathmatique C nest pas une mthode de classe :
double Toto::calcul(double x)
{
double y;
y = sqrt(x);
// Appelle notre methode
y = :$_:sqrt(x);
// Appelle LA sqrt de la libm C
}
A.4 Structures
Pour des raisons de compatibilit indispensable, C++ supporte les structures de
donnes du langage C.
Comme les structures, au sens du C, nont aucun intrt dans un langage objets, en
C++ les structures sont en fait des classes !
Syntaxiquement, cela se traduit par le fait quon peut dclarer des donnes de type
structure comme on instancie des classes, le dclarateur struct est facultatif :
struct Toto {
int a;
int b;
};
// Definition structure C
(On rappelle que, par dfaut, dans une classe et sans spcication de visibilit tout
est private.)
Comme il sagit en fait de classes, il devrait donc tre possible dimplanter dans des
structures des mthodes, un constructeur, etc. :
struct Toto {
Toto() { a = b = 0; }
int a;
int b;
};
// Constructeur en ligne
En effet, cest possible. Lcriture ci-dessus est tout fait valide. Et a na AUCUN
INTRT !
Ou bien on porte du code C existant dans une application C++, auquel cas on laisse
ce qui existe dj et le code C nutilisait srement pas de mthodes de structures, ou
alors on fait un nouveau dveloppement C++ et on utilise des classes, uniquement.
La structure, en C++, est un outil de compatibilit, pas un outil de dveloppement.
46
7 - Complments
e
47
// Forward
// Attribut pointeur
// Forward
// Attribut pointeur
On peut remarquer que, lors de linclusion des headers dans un module source qui a
besoin des deux interfaces de classes :
#include "toto.h"
#include "truc.h"
lune des prdclarations devient en fait une postdclaration. Cest tolr, savoir
que le compilateur ignore une prdclaration sil a dj vu linterface de classe.
47
class Toto
{
...
};
#define TOTO_H
#endif
48
Annexe B
Compatibilit C/C++
e
Le langage C++ est une extension, oriente objets, du C-Ansi. Les deux dialectes sont
sufsament proches pour que beaucoup de compilateurs soient capable de traiter les
deux. On parle de compilateurs mixtes, C/C++.
On dira que C++ est un surensemble du C-Ansi dans la mesure o toute la syntaxe
du C-Ansi est valide en C++. Et donc, tout programme crit en C-Ansi peut tre
compil avec un compilateur C++. Le C-Ansi est impratif, la syntaxe ancienne du
C-K&R, sans prototypes de fonctions, est rejette en C++.
Linverse est videmment faux, un source C++ ne sera pas, en gnral, compilable
par un compilateur C. Sera rejett tout ce qui concerne les classes, mais galement,
dans des critures de fonctions classiques, les passages par rfrences inconnus en C
et les surcharges de slection : C nautorise pas plusieurs fonctions de mme nom,
mme si les arguments dappel diffrent.
49
50
8 - Compatibilit C/C++
e
51
/* Pointeur quelconque C */
1
2
/* Interface */
figure figCreate(int type);
void
figDelete(figure F);
/* Methodes */
void figMove(figure F, float dx, float dy);
void figZoom(figure F, float scale);
float figArea(figure F);
#ifdef __cplusplus
}
#endif
52
8 - Compatibilit C/C++
e
53
// Classes
#include "libfig.h"
// Interface C
/* Constructeur */
figure figCreate(int type)
{
switch( type ) {
case FIG_CERCLE
: return new Cercle;
case FIG_RECTANGLE : return new Rectangle;
default : return (Figure*)0;
}
}
/* Destructeur */
void figDelete(figure F)
{
delete F;
}
/* Methodes */
void figMove(figure F, float dx, float dy)
{
F->Move(dx, dy);
}
void figZoom(figure F, float scale)
{
F->Zoom(scale);
}
float figArea(figure F)
{
return F->Area();
}
Comme on peut le constater, cest un petit travail simple, mme sil na rien de
passionnant. On notera galement toute la puissance du polymorphisme qui permet
de ne sintresser la nature exacte des objets que lors de la construction. Tout le
reste du code est gnrique, pointeur de gure quelconque.
NB : cest parce que toutes les fonctions de ce module sont dclares extern "C",
dans le chier header, quelle pourront tre lies aux appels venant du code C.
53
54
Annexe C
Surcharges d'oprateurs
e
55
// Avec initialisation
// Clonage sur c2
Que peut-on faire maintenant, de ces objets ? Pas grand chose en fait ! On peut
vouloir accder aux attributs, privs, donc par un mcanisme daccesseurs (cf. 5.3
page 32), SetReal, SetImag, GetReal, GetImag.
La technique manque un peu dlgance et on va trs vite se trouver face du code
comme :
// Affectation : c1 = c3
c1.SetReal(c3.GetReal());
c1.SetImag(c3.GetImag());
// Somme composite : c1 += c2
c1.SetReal(c1.GetReal() + c2.GetReal());
c1.SetImag(c1.GetImag() + c2.GetImag());
Tout ce quon peut dire de la monstruosit prcdente est que cela fonctionne !
56
9 - Surcharges doprateurs
e
57
C.3 Associativit
e
En C++ comme en C, certains oprateurs sont associatifs, en particulier laffectation,
ce qui permet dcrire des choses telles que :
int i, j, k;
i = j = k = 3;
Cela fonctionne parce que laffectation est une opration qui retourne son oprande
de droite. Syntaxiquement, le rsultat dune affectation est donc un oprande et peut
gurer dans une expression.
57
Modions donc notre oprateur daffection pour quil retourne son oprande de
droite, savoir une rfrence sur un objet. Linterface devient :
class complex
{
...
complex& operator=(complex& arg);
et limplmentation :
complex& complex::operator=(complex& arg)
{
pR = arg.pR;
pI = arg.pI;
return arg;
}
Maintenant, des critures telles que :
complex c1, c2;
complex c3(2.5, -1);
c1 = c2 = c3;
sont licites18.
NB : dans lcriture de loprateur =, on retourne loprande de droite. Si lon a
besoin, dans un oprateur, de retourner loprande de gauche, cest dire lobjet
courant, on se souviendra du pointeur this (cf. A.3 page 45).
Attention, this est un pointeur sur nous, mais ce nest pas nous ! Nous, cest lobjet
point par this, et donc :
complex& complex::operator=(complex& arg)
{
pR = arg.pR;
pI = arg.pI;
return *this;
// Mais oui !
}
(18)
(19)
58
Isnt it great ?
Mais o cela va-t-il sarrter ?
9 - Surcharges doprateurs
e
59
59
// Temporaires
__1 = 3 + j;
__2 = __1 + k;
i = __2;
On utilisera le mme principe en crant, dans les oprateurs, un objet temporaire
rsultat. Par contre, on ne devra plus implanter des oprateurs internes la classe,
utilisant lobjet courant, mais des oprateurs externes deux oprandes. Pour quils
puissent tout de mme accder aux attributs, ils seront dclars friend.
Ajoutons une addition dans linterface :
class complex
{
...
friend complex operator+(complex& a, complex& b);
et dans limplmentation :
complex operator+(complex& a, complex& b)
{
float R = a.pR + b.pR;
float I = a.pI + b.pI;
complex result(R, I);
// Nouvel objet
return result;
}
NB : le type retour est bien un objet, complex, et non une rfrence, complex&.
Le compilateur doit tre prvenu que lopration a cr une instance, temporaire, et
quelle devra tre dtruite aprs utilisation, donc en n de traitement de lexpression.
On dispose maintenant dune vritable addition (voir note [ 9.2 ] page 62) :
complex
complex
complex
complex
c4 = c1
c1(1, 0);
c2(1, 1);
c3(c2);
c4;
+ c2 + c3;
9 - Surcharges doprateurs
e
61
C.6 Remarques
Les exemples prcdents illustraient les principes de la surcharge, ou rednition
doprateurs.
Tous les oprateurs de C peuvent tre rednis, arithmtiques mais aussi boolens.
On pourrait comparer des nombres complexes, par des critures telles que :
complex c1, c2;
...
if( c1 == c2 ) ...
en implantant des oprateurs ==, ~!=, etc.
Il est conseill de conserver un minimum de bon sens. Mme si C++ le permet, ce
nest PAS une bonne ide dimplanter une addition de complexes sur loprateur *
et une multiplication sur loprateur +, dautant que les priorits dvaluation restent
celles de larithmtique :
(a + b * c)
Enn, ne pas oublier que C++ ne sert pas qu construire des objets mais quon peut
aussi crire des fonctions. Le mcanisme de surcharge de slection permettant de
lever les ambiguts de nom partir de la nature des arguments dappel, on pourra,
pour les besoins dun module comme celui de ce chapitre, rimplanter des fonctions
existantes en version scalaire :
complex sqrt(complex& arg);
et autres...
61
[ 9.1 ]
c4 = c1 + c2 + c3;
il ne faut pas perdre de vue que se cache un traitement assez important : instanciations dobjets, appels des constructeurs, appels de mthodes oprateurs, calculs,
destruction !
Dans le cas dobjets assez consquents, par exemple une implantation dalgbre
linaire avec toutes les oprations ncessaires implantes sur vecteurs, matrices, on
peut aboutir de vritables monstres arithmtiques.
Cest pourquoi, trs souvent, on favorise les oprateurs daffectation compose, plus
simples crire et excuter.
Ainsi, on trouvera du calcul matriciel encod comme suit :
matrice M1, M2, M3, M4;
...
M4 = M1;
M4 += M2;
M4 *= M3;
plutt que :
M4 = (M1 + M2) * M3;
mme si la seconde criture est plus jolie !
62
Annexe D
Les streams
// E/S du C
// E/S du C++
float x;
x = 3.14159;
// Affichage a la mode C
fprintf(stdout, "x = %g\n", x);
// Affichage a la mode C++
cout << "x = " << x << \n;
On apprciera llgance de la syntaxe, on "vide20" vers le stream une suite dentits,
chane de caractres, rel, caractre de contrle, etc. En particulier, la spcication de
format a disparu.
Le but de cette annexe nest pas de dtailler la librairie iostream mais dexpliciter
le mcanisme pour pouvoir ladapter nos propres besoins.
Cette librairie repose sur des classes, istream pour les entres, ostream pour les
sorties. La classe istream surcharge loprateur >>, la classe ostream surcharge
loprateur <<.
Par surcharge de slection, ces oprateurs sont implants pour tous les types
darguments de base (les oprations de formatage en fonction de la nature, entier,
rel, chane, ..., sont donc internes loprateur) et, pour permettre lcriture associative, ces oprateurs retournent leur oprande de gauche qui est une rfrence sur un
ostream (dans le cas des sorties).
On peut complter lexemple de lannexe C page 55, construisant une classe de nombres complexes, en ajoutant par exemple un oprateur dafchage de complexe. On
peut imaginer dimprimer les parties relle et imaginaire, entre parenthses, spares
par une virgule.
(20)
Cest la raison du choix de ces oprateurs. Aucun rapport avec des dcalages de bits, mais leffet
visuel est vocateur !
63
64
Annexe E
Les patrons
Le dernier gros apport de C++, par rapport C, concerne la possibilit de dnir des
patrons (templates en anglais) ou modles. Il ny a rien de conceptuel, cest simplement
un outil de programmation mais trs puissant.
Lide est dviter de devoir crire et recrire du code presque identique, et donc de
donner au compilateur les informations ncessaires pour gnrer automatiquement
du code. Un patron C++ est, en quelque sorte, du code source paramtr.
Lutilisation intensive de patrons est un vaste sujet, cette annexe se borne prsenter
le principe.
Dans lannexe C page 55, on avait initi le dveloppement dune petite classe de
nombres complexes. Ceux-ci comportaient des attributs parties entire et imaginaire,
encods en rels simple prcision, float
Si lon dsire galement travailler avec des complexes en double prcision, il faudrait
reconstruire une classe analogue, dupliquer les chiers, remplacer les types float
par des types double, etc. Bref, un abominable travail qui nest plus de la programmation. Quant la maintenance, ajout de nouvelles oprations, nen parlons
pas.
La solution cest le patron de classe. On va dcrire, non plus une interface de classe
C++ mais un modle pour construire des classes C++, le type des attributs tant un
paramtre symbolique que lon appellera : VERB (ou TOTO si lon prfre).
E.1
Interface
Nouveau chier interface, complex.h, avec le maximum de code en ligne (on
prcisera pourquoi ensuite) :
template<class VERB>
class complex
{
// Constructeurs
public:
complex()
{ pR = pI = (VERB)0; }
complex(VERB real, VERB imag)
{ pR = real; pI = imag; }
complex(complex<VERB>& model)
{ pR = model.pR; pI = model.pI; }
// Attributs
private:
VERB pR, pI;
65
// Operateurs
public:
complex<VERB>& operator=(complex<VERB>& arg)
{ pR = arg.pR; pI = arg.pI; return arg; }
void
operator+=(complex<VERB>& arg)
{ pR += arg.pR; pI += arg.pI; }
... etc
};
Lcriture est similaire, quelques dtails prs. Les attributs sont typs par le type
symbolique VERB, les rfrences une classe patron scrivent, ici, complex<VERB>
et non plus simplement complex. En fait, le nom de classe lui-mme est paramtr.
Lutilisation dune classe patron est immdiate, sous rserve de rsoudre le paramtrage :
complex<float> c1; // Un complexe simple precision
complex<double> c2; // et un autre, double precision
et cest tout ! Le compilateur a tout ce quil faut pour implanter une "vraie" classe
de complexes attributs float et une attributs double.
NB : si lon trouve la notation un peu disgracieuse, on peut la contourner facilement :
typedef complex<float> acomplex;
typedef complex<double> dcomplex;
acomplex c1;
dcomplex c2;
E.2
Implmentation
e
On peut, bien sr, implmenter du code de patron de manire classique, dans un
chier source spar, moyennant quelques petites prcautions syntaxiques, lcriture
est un peu plus lourde que pour du codage de mthodes classique :
/* Constructeur initialisation */
template<class VERB>
complex<VERB>(VERB real, VERB imag)
{
pR = real;
pI = imag;
}
/* Somme composee */
template<class VERB>
void complex<VERB>::operator+=(complex<VERB>& arg)
{
pR += arg.pR;
pI += arg.pI;
}
Lintrt de la gnration en ligne, quand elle est envisageable (quelques lignes de
code), est que dune part le compilateur gnre le code sur place, et dautre part
nutilise le patron que par fragments, selon les besoins.
66
11 - Les patrons
67
Une classe comme celle de notre exemple, des complexes, pourrait comporter
plusieurs dizaines doprateurs, avec diffrents types darguments. Si le code application nen utilise que deux ou trois, les autres nauront mme jamais exist dans le
programme !
Enn, si tout le codage est possible en ligne, seul le chier de dclaration, complex.h, existe. Plus de chier source, plus ddition de liens avec tel ou tel module
librairie pour le code utilisateur.
E.3
Remarques
La puissance de cet outil est assez remarquable ! On se borne dcrire comment
construire des classes, de tel ou tel type. Cest un peu un mcanisme dapprentissage,
ensuite le compilateur se dbrouille (en gnral bien, et toujours mieux quun humain
qui fait du copier/coller et du cherche/remplace lditeur de texte) !
De plus, il est possible de dnir des patrons plusieurs paramtres :
template<class P1, class P2>
class machin
{
...
et on pourra ensuite instancier par :
machin<float, float> m1;
machin<int, double> m2;
...
67
68
Annexe F
Exceptions
C++ a introduit rcemment un mcanisme de gestion derreurs par exceptions logicielles. Ce mcanisme nest pas encore stabilis, tous les compilateurs C++ nen
disposent pas, ou sur options, ou avec des variantes.
Ce manuel ne traite pas le sujet ! Si ncessaire, se rfrer au manuel de B. Stroustrup
et tudier la documentation du compilateur utilis.
69
70
Index
@
superclasse 25
__cplusplus 51
cxx 2
delete 15, 16
Destructeur 12
virtuel 42
Dynamique,
instance 15
Drive,
classe 23
& 30
<< 63
>> 63
D
Abstraite,
classe 40
Accesseur 33
Ami,
membre 34
oprateur 63
Attribut 7
de visibilit 11, 26
E
B
Base,
classe de
dition de liens 2
Entres/sorties 63
extern "C" 51
22
C
catch 69
CC 2
class 8
Classe 7
abstraite 40
de base 22
drive 23
forward 47
implmentation 10
instance 9
interface 8
mthode 17
oprateur 57
patron 65
structure 46
support 38
Cloneur,
constructeur 55
Code en ligne 32
Compilation 2
options 2
const 30
Constructeur 12
cloneur 55
d objets membres 27
F
Fichier,
implmentation 10
inclusion 47
interface 8, 47
Fonctionnelle,
surcharge 38
Forward,
classe 47
friend 34
G
gcc 2
H
Hritage 21
public 23
71
L
Locale,
instance 14
N
50
O
Objets membres,
constructeur d 27
operator 57
Options,
compilation 2
Oprateur,
ami 63
classe 57
surcharge 55, 63
overload 38
overstrike 17
72
rethrow 69
Rfrence 30
Rsolution de porte 40, 44,
45
Membre 7
ami 34
statique 43
Mthode 7
classe 17
virtuelle 38
Name mangling
new 15
P
Patron 65
classe 65
Pointeur 15
Polymorphisme 37
private 8, 11
protected 26
Prdclaration 47
public 8, 11, 23
hritage 23
Implmentation,
classe 10
chier 10
Inclusion,
chier 47
inline 32
Instance 7
classe 9
dynamique 15
locale 14
temporaire 59
Interface,
classe 8
chier 8, 47
Sousclasse 23, 24
static 43
Statique,
membre 43
struct 46
Structure,
classe 46
Superclasse 22, 24
constructeur 25
Support,
classe 38
Surcharge,
fonctionnelle 38
oprateur 55, 63
de slection 17
Slection,
surcharge de 17
T
template 65
Temporaire,
instance 59
this 45
throw 69
try 69
13 - Index
73
73