Académique Documents
Professionnel Documents
Culture Documents
C. Delannoy
A VANT-PRO PO S
La m aî trise d'un langage de program m ation pas s e obligatoire m e nt par la pratiq ue , c'e s t-à -dire la re ch e rch e
pe rsonne lle d'une solution à un problèm e donné. Cette affirm ation re s te vraie pour le program m e ur
ch e vronné q ui é tudie un nouve au langage . C'e s t dans ce tte s ituation q ue s e trouve gé né ralem e nt une
pe rsonne q ui aborde le C+ + : e lle connaî t déjà le langage C sur leq ue ls'appuie e ffe ctive m e nt C+ + ;
toute fois, ce dernie r langage introduit suffisam m e nt de possibilité s s upplém e ntaire s e t surtout de nouve aux
conce pts (e n particulie r, ce ux de la Program m ation O rie nté e O b je t) pour q ue s on appre ntissage s 'appare nte à
ce lui d'un nouve au langage .
Ce livre vous propose d'accom pagne r votre é tude du C+ + e t de la prolonge r, ce ci à l'aide d'exercices
approprié s , varié s e t de difficulté croissante .
Les diffé re nts ch apitre s , à l'e xce ption du de rnie r, corre s ponde nt à une progre s s ion classique d'un "cours de
C+ + " : incom patiblité s e ntre C e t C+ + ;les s pé cificités de C+ + ;notions de clas s e , constructe ur e t
destructe ur ;proprié tés des fonctions m e m bre ;construction, de s truction e t initialisation de s obje ts ;les
fonctions am ie s ;la surdéfinition d'opé rate urs ;les conve rsions de type définie s par l'utilisate ur ;la
te ch niq ue de l'h é ritage ;les fonctions virtue lles ;les flots d'entré e e t de sortie , les patrons de fonctions e t les
patrons de clas s e s . Le dernie r ch apitre , e nfin, propose des "e xe rcices de synth è s e ".
Ch aq ue ch apitre débute par un "rappe ldétaillé"1 des connaissance s né ce s s aire s pour aborde r les e xe rcice s
corre s pondants (nature llem e nt, un e xe rcice d'un ch apitre donné peut faire inte rve nir de s points ré s um és dans
les ch apitre s pré cédents).
Au sein de ch aq ue ch apitre , les e xe rcice s propos é s vont d'une application im m édiate du cours à des
ré alisations de clas s e s re lative m e nt com plète s . Au filde votre progre s s ion dans l'ouvrage , vous ré aliserez
des classes de plus e n plus "ré aliste s e t opé rationne lles " e t ayant un inté rê t gé né ral;citons, par e xe m ple :
- les e ns e m bles ,
- les ve cte urs dynam iq ue s ,
- les tableaux dynam iq ue s à plusieurs dim e nsions,
- les liste s ch aî
né e s ,
- les tableaux de bits,
- les (vraie s ) ch aî
nes de caractè re s ,
- les piles ,
- les com plexe s ,
- e tc.
Nature llem e nt, tous les e xe rcice s s ont "corrigé s ". Pour la plupart, la solution propos é e ne s e lim ite pas à une
sim ple liste d'un program m e (laq ue lle ne re pré s e nte finalem e nt q u'une rédaction possible parm i d'autre s ).
Vous y trouve re z une analyse détaillée du problèm e e t, si besoin, les justifications de ce rtains ch oix. D e s
com m e ntaire s vie nne nt, le cas é ch é ant, é claire r les partie s q ue lque pe u dé licate s . Fré q ue m m e nt, vous
trouve re z des suggestions de prolonge m e nt ou de gé né ralisation du problèm e abordé .
1 Le cours com pl
et corre s pondant à ces résum é s peut ê tre trouvé dans "Program m er en langage C+ + " ou dans "Program m er en
Turbo C+ + ", du m ê m e auteur, aux Editions Eyrolles.
O utre la m aîtrise du langage C+ + propre m e nt dit, les e xe rcice s propos é s vous perm e ttront de vous forge r
une "m é th odologie de conce ption de vos propre s clas s e s ". Notam m e nt, vous saure z :
- décide r du bie n-fondé de la surdéfinition de l'opé rate ur d'affe ctation ou du constructe ur par re copie ,
- e xploite r, lors q ue vous juge re z q ue ce la e s t opportun, les possibilités de "conve rsions autom atiq ue s "
q ue le com pilate ur pe ut m e ttre e n place ,
- com m e nt tire r parti de l'h é ritage (sim ple ou m ultiple) e t q ue ls avantage s pré s e nte la cré ation d'une
"biblioth è q ue de clas s e s ", notam m e nt par le biais du "typage dynam iq ue des obje ts" q ui dé coule de
l'e m ploi de s fonctions virtue lles .
D e s urcroît, vous aure z re ncontré un ce rtain nom bre de te ch niq ue s fondam e ntales te lles q ue : la ré alisation
d'un "ité rate ur" ou les "clas s e s conte ne urs" e t l'utilisation de s possibilités de "fonctions génériques" (patrons
de fonctions) e t de "clas s e s gé né riq ue s " (patrons de clas s e s ).
N.B.
En atte ndant la norm alisation de C+ + , ce s ont des publications de AT& T q ui s e rve nt de "ré fé re nce s " :
ve rsion 1.1 e n 86, 1.2 e n 87, 2.0 e n 89 , 3.0 e n 9 1 (ce tte derniè re s e rvant de bas e au travaildu com ité
ANSI). Le s rares diffé re nce s e xistant e ntre les ve rsions sont claire m e nt m e ntionnées dans les ré s um és des
diffé re nts ch apitre s .
Le s e xe m ples de program m e s fournis e n solution ont é té te s té s ave c le com pilate ur du logicie lTurbo C+ +
pour W indow s, ve rsion 3.0.
TA BLE D ES M A TIERES
Avant-propos ......................................................................................... V
I. Incom patibil
ités entre C et C+ + ..............................................................1
III. Notions de cl
asse, constructeur et destructeur ......................................... 23
X. L'h éritage m ul
tipl
e ...........................................................................153
XII. Les fl
ots d'entrée et de sortie.............................................................179
RAPPELS
C+ + e s t "pre s q ue " un sur-e ns e m ble du C, te lq u'ile s t défini par la norm e ANSI. Se ules e xiste nt q ue lque s
"incom patibilité s " dont nous vous rappe lons ici les principales .
D é cl
arations d e fonctions
En C+ + , toute fonction utilisée dans un fich ie r source doit obligatoire m e nt avoir fait l'obje t :
- soit d'une déclaration sous form e d'un prototype (ilpré cis e à la fois le nom de la fonction, le type de
s e s argum e nts é ve ntue ls e t le type de sa valeur de re tour), com m e dans ce t e xe m ple :
float fexp (int, double, char *) ;
- soit d'une définition pré alable au s e in du m ê m e fich ie r source (ce dernie r cas é tant d'ailleurs peu
cons e illé, dans la m e s ure où de s problèm e s ris q ue nt d'apparaî
tre dè s lors q u'on s é pare ladite fonction du
fich ie r source e n q ue s tion).
En C, une fonction pouvait ne pas ê tre déclaré e (auq ue lcas, on considérait, par dé faut, q ue s a valeur de
re tour é tait de type int), ou e ncore déclaré e partie llem e nt (sans fournir le type de ses argum e nts), com m e
dans :
float fexp () ;
En C+ + , une fonction sans argum e nt se définit (au nive au de l'e n-tê te ) e t se déclare (au nive au du
prototype ) e n fournissant une "liste d'argum e nts vide" com m e dans :
float fct () ;
En C, on pouvait indiffé re m m e nt utiliser cette notation ou faire appe lau m ot clé void com m e dans :
En C+ + , une fonction sans valeur de re tour se définit (e n-tê te ) e t se déclare (prototype ) obligatoire m e nt à
l'aide du m ot clé void com m e dans :
Le q ual
ificatif cons t
- a une porté e lim ité e au fich ie r source conce rné , tandis q u'e n C ilpouvait é ve ntue llem e nt ê tre utilisé
dans un autre fich ie r source (e n utilisant le m ot clé e xte rn),
- pe ut ê tre utilisé dans une "expression constante " (e xpre s s ion calculable au m om e nt de la com pilation),
alors q u'ilne pouvait pas l'ê tre e n C ;ce dernie r point pe rm e t notam m e nt d'utiliser de te ls sym boles
pour dé finir la taille d'un tableau (e n C, ilfallait obligatoire m e nt avoir re cours à une "définition" de
sym boles par la dire ctive #de fine ).
Le type void *
En C+ + , un pointe ur de type void *ne pe ut pas ê tre conve rti "im plicite m e nt" lors d'une affe ctation e n un
pointe ur d'un autre type ;la ch os e é tait pe rm ise en C. Bie n e nte ndu, e n C+ + , ilre s te possible de faire
appe là l'opé rate ur de "cast".
___________________________________________________________________________
Enoncé
Quelles e rre urs s e ront déte cté e s par un com pilate ur C+ + dans ce fich ie r source q ui e s t acce pté par un
com pilate ur C?
main()
{
int a=10, b=20, c ;
c = g(a, b) ;
printf ("valeur de g(%d,%d) = %d", a, b, c) ;
}
g(int x, int y)
{
return (x*x + 2*x*y + y*y) ;
}
___________________________________________________________________________
Sol
ution
1) La fonction g doit obligatoire m e nt faire l'obje t d'une déclaration (sous form e d'un prototype ) dans la
fonction m ain. Par e xe m ple, on pourrait introduire (n'im porte où avant l'appe lde g) :
I. Incom patibilité s e ntre C e t C+ + 3
ou e ncore :
int g (int x, int y) ;
R appe lons q ue , dans ce dernie r cas, les nom s x e t y sont fictifs : ils n'ont aucun rôle dans la suite e t ils
n'inte rfè re nt nullem e nt ave c d'autre s variables de m ê m e nom q ui pourraie nt ê tre déclarées dans la m ê m e
fonction (ici m ain).
2) La fonction printf doit, e lle aussi, com m e toute s les fonctions C+ + (le com pilate ur n'é tant pas e n
m e s ure de distingue r les fonctions de la biblioth è q ue des "fonctions définie s par l'utilisate ur"), faire
l'obje t d'un prototype . Nature llem e nt, iln'e s t pas néce s s aire de l'é crire e xplicite m e nt : ile s t obte nu par
incorporation du fich ie r e n-tê te corre s pondant :
#include <stdio.h>
Note z q ue ce rtains com pilate urs C refus e nt déjà l'abs e nce de prototype pour une fonction de la
biblioth è q ue s tandard te lle q ue printf (m ais la norm e ANSI n'im posait rie n à ce s uje t!).
___________________________________________________________________________
#include <stdio.h>
const int nb = 10 ;
const int exclus = 5 ;
main()
{
int valeurs [nb] ;
int i, nbval = 0 ;
printf ("donnez %d valeurs :\n", nb) ;
for (i=0 ; i<nb ; i++) scanf ("%d", &valeurs[i]) ;
for (i=0 ; i<nb ; i++)
switch (valeurs[i])
{ case exclus-1 :
case exclus :
case exclus+1 : nbval++ ;
}
printf ("%d valeurs sont interdites", nbval) ;
}
___________________________________________________________________________
Sol
ution
En C, les sym boles nb e t e xclus ne s ont pas utilisables dans des "e xpre s s ions constante s ". Ilfaut donc les
définir soit com m e des variables , soit à l'aide d'une directive #de fine com m e s uit :
#include <stdio.h>
#define NB 10
#define EXCLUS 5
4 Exe rcice s e n langage C+ +
main()
{
int valeurs [NB] ;
int i ;
int nbval=0 ;
printf ("donnez %d valeurs :\n", NB) ;
for (i=0 ; i<NB ; i++) scanf ("%d", &valeurs[i]) ;
for (i=0 ; i<NB ; i++)
switch (valeurs[i])
{ case EXCLUS-1 :
case EXCLUS :
case EXCLUS+1 : nbval++ ;
}
printf ("%d valeurs sont interdites", nbval) ;
}
CH A PITRE II :
LES SPECIFICITES D E C+ +
RAPPELS
C+ + dispose, par rapport au C ANSI, d'un ce rtain nom bre de spé cificité s q ui ne s ont pas vé ritablem e nt
axé e s s ur la Program m ation O rie nté e O b je t.
Nouve l
le s pos s ibil
ité s d'e ntré e s -s ortie s
C+ + dispose de nouve lles facilités d'entré e s -sortie s . Bie n q u'e lles s oie nt forte m e nt lié e s à des aspects
P.O .O . (surdéfinition d'opé rate ur e n particulie r), e lles s ont parfaite m e nt utilisables e n de h ors de ce conte xte .
C'e s t tout particuliè re m e nt le cas des possibilités d'entré e s -sortie s conve rsationne lles (clavie r, é cran) q ui
re m place nt avantage us e m e nt les fonctions printf e t scanf. Ainsi :
cout < < e xpression1 < < e xpression2 < < ......... < < e xpressionn
affich e s ur le "flot" cout (conne cté par dé faut à la sortie s tandard stdout) les valeurs des diffé re nte s
e xpre s s ions indiq ué e s , suivant une pré s e ntation adapté e à leur type (depuis la ve rsion 2.0 de C+ + , on sait
distingue r les attributs de signe et on pe ut affich e r de s valeurs de pointe urs). De m ê m e :
lit sur le "flot" cin (conne cté par dé faut à l'e ntré e s tandard stdin) des inform ations de l'un de s type s ch ar,
sh ort, int, long, float, double ou ch ar *(depuis la ve rsion 2.0 de C+ + , on sait distingue r les attributs de
signe ;e n re vanch e , les pointe urs ne sont pas adm is). Le s conve ntions d'analyse des caractè re s lus sont
com parables à ce lles de scanf ave c ce tte principale diffé re nce q ue la lecture d'un caractè re com m e nce par
saute r les s é parate urs.
Nouve l
le form e de com m e ntaire
Les deux caractè re s // pe rm e tte nt d'introduire des "com m e ntaires de fin de ligne " : tout ce q ui suit ce s
caractè re s , jus q u'à la fin de la ligne , e s t considéré com m e un com m e ntaire .
8 Exe rcice s e n langage C+ +
Em pl
ace m e nt l
ibre de s dé cl
arations
En C+ + , iln'e s t plus néce s s aire de re groupe r les déclarations e n début de fonction ou e n début de bloc. Il
devie nt ainsi possible d'em ploye r de s e xpre s s ions dans des initialisations com m e dans ce t e xe m ple :
int n ;
.....
n = ... ;
.....
int q = 2*n - 1 ;
.....
En faisant pré céder du sym bole & le nom d'un argum e nt dans l'e n-tê te (e t dans le prototype ) d'une fonction,
on ré alise une "transm ission par ré fé re nce ". Ce la signifie q ue les é ve ntue lles m odifications e ffe ctué e s au
s e in de la fonction porte ront sur l'argum e nt e ffe ctif de l'appe le t non plus sur une copie .
D ans la déclaration d'une fonction (prototype ), ile s t possible de pré voir pour un ou plusieurs argum e nts
(obligatoire m e nt les dernie rs de la liste ) des "valeurs par dé faut" : e lles s ont indiq ué e s par le s igne =, à la
suite du type de l'argum e nt com m e dans :
Ce s valeurs par dé faut s e ront alors utilisées en cas d'appe lde ladite fonction ave c un nom bre d'argum e nts
infé rie ur à ce lui pré vu. Par e xe m ple, ave c la pré cédente déclaration, l'appe lfct ('a') s e ra é q uivalent à fct
('a', 10, 0.0) ;de m ê m e , l'appe lfct ('x', 12) s e ra é q uivalent à fct ('x', 12, 0.0). En re vanch e , l'appe lfct ()
s e ra illégal.
2 On l
e trouvera, par exem ple, dans l'annexe A de notre ouvrage "Program m er en langage C+ + ", ou dans le ch apitre V de
"Program m er en Turbo C+ + ", publiés égalem ent par les Editions EYROLLES.
II. Le s s pécificités de C+ + 9
R e m arque :
Avant la ve rsion 2, toute définition ou utilisation d'une fonction "ordinaire " (c'e s t-à -dire autre q u'une
"fonction m e m bre d'une clas s e ") surdéfinie devait ê tre s ignalée au com pilate ur par une déclaration
pré alable de la form e : ove rload nom _fonction.
Ge s tion dynam iq ue de l
a m é m oire
En C+ + , les fonctions m alloc, calloc... e t fre e sont re m placé e s avantage us e m e nt par les "opé rate urs" new
e t del
ete.
Si type re pré s e nte la description d'un type absolum e nt q ue lconq ue e t si n re pré s e nte une e xpre s s ion d'un type
e ntie r3
del
ete adresse
libè re un e m place m e nt pré alablem e nt alloué par ne w à l'adre s s e indiq ué e . Iln'e s t pas néce s s aire de ré pé te r
le nom bre d'élém e nts, du m oins lors q u'ilne s 'agit pas d'obje ts, m ê m e lors q ue ce lui-ci e s t diffé re nt de 1. Le
cas des tableaux d'obje ts e s t e xam iné dans le ch apitre V.
En outre , vous pouve z définir une fonction de votre ch oix e t dem ande r q u'e lle s oit appe lée e n cas de m anq ue
de m é m oire . Ilvous suffit d'en transm e ttre l'adre s s e e n argum e nt à la fonction set_new _h andl
er.
Le s fonctions "e n l
igne "
Une fonction "e n ligne " (on dit aussi "déve loppé e ") e s t une fonction dont les instructions sont incorporé e s
par le com pilate ur (dans le m odule obje t corre s pondant) à ch aq ue appe l. Ce la é vite la pe rte de te m ps
né ce s s aire à un appe lusuel(ch ange m e nt de conte xte , copie des valeurs des argum e nts sur la "pile", ...) ;e n
re vanch e , les instructions e n q ue s tion sont gé né ré e s plusieurs fois. Le s fonctions "e n ligne " offre nt le m ê m e
inté rê t q ue les m acros, sans présente r de ris q ue s "d'effe ts de bord". Une fonction e n ligne e s t né ce s s aire m e nt
définie e n m ê m e te m ps q u'e lle e s t déclaré e (e lle ne pe ut plus ê tre com pilée s é paré m e nt) e t son en-tê te e s t
pré cédé du m ot clé inline com m e dans :
___________________________________________________________________________
3 Général
em ent long ou unsigned long.
10 Exe rcice s e n langage C+ +
Enoncé
Ecrire le program m e s uivant (corre ct e n C com m e e n C+ + ), e n ne faisant appe l q u'aux nouve lles
possibilités d'entré e s -sorties de C+ + , donc e n re m plaçant les appe ls à printf e t scanf :
#include <stdio.h>
main()
{
int n ; float x ;
printf ("donnez un entier et un flottant\n") ;
scanf ("%d %e", &n, &x) ;
printf ("le produit de %d par %e\n'est : %e", n, x, n*x) ;
}
___________________________________________________________________
Sol
ution
___________________________________________________________________________
Enoncé
Ecrire une fonction pe rm e ttant d'éch ange r les conte nus de 2 variables de type int fournie s e n argum e nt :
- e n transm e ttant l'adresse des variables conce rné e s (s e ule m é th ode utilisable e n C),
- e n utilisant la transm ission par ré fé re nce .
D ans les deux cas, on é crira un pe tit program m e d'essai (m ain) de la fonction.
___________________________________________________________________________
Sol
ution
a) avec l
a transm ission des adresses des variabl
es
#include <iostream.h>
main()
{
II. Le s s pécificités de C+ + 11
#include <iostream.h>
main()
{
void echange (int &, int &) ; // prototype de la fonction echange
int n=15, p=23 ;
cout << "avant : " << n << " " << p << "\n" ;
echange (n, p) ; // attention n et non &n, p et non &p
cout << "après : " << n << " " << p << "\n" ;
}
void echange (int & a, int & b)
{ int c ; // pour la permutation
c = a ;
a = b ;
b = c ;
}
Enoncé
struct essai
{ int n ;
float x ;
} ;
Ecrire une fonction nom m é e raz pe rm e ttant de re m e ttre à zé ro les 2 ch am ps d'une structure de ce type ,
transm ise en argum e nt :
- par adre s s e ,
- par ré fé re nce .
D ans les deux cas, on é crira un pe tit program m e d'essai de la fonction ;ilaffich e ra les valeurs d'une
structure de ce type , aprè s appe lde ladite fonction.
12 Exe rcice s e n langage C+ +
___________________________________________________________________________
Sol
ution
#include <iostream.h>
struct essai
{ int n ;
float x ;
} ;
void raz (struct essai * ads)
{ ads->n = 0 ; // ou encore (*ads).n = 0 ;
ads->x = 0.0 ; // ou encore (*ads).x = 0.0 ;
}
main()
{ struct essai s ;
raz (&s) ;
cout << "valeurs après raz : " << s.n << " " << s.x ;
}
#include <iostream.h>
struct essai
{ int n ;
float x ;
} ;
void raz (struct essai & s)
{ s.n = 0 ;
s.x = 0.0 ;
}
main()
{ struct essai s ;
raz (s) ; // notez bien s et non &s !
cout << "valeurs après raz : " << s.n << " " << s.x ;
}
___________________________________________________________________________
Enoncé
int n, p ;
float x, y ;
char c ;
double z ;
Le s appe ls suivants sont-ils corre cts e t, si oui, q ue lles s e ront les fonctions e ffe ctive m e nt appe lée s e t les
conve rsions éve ntue llem e nt m ises en place ?
a) fct (n) ;
b) fct (x) ;
c) fct (n, x) ;
d) fct (x, n) ;
e) fct (c) ;
f) fct (n, p) ;
g) fct (n, c) ;
h) fct (n, z) ;
i) fct (z, z) ;
___________________________________________________________________________
Sol
ution
f) Appelincorre ct, com pte te nu de s on am biguïté ;deux possibilité s e xiste nt e n e ffe t : cons e rve r n, conve rtir
p e n float e t appe ler la fonction III ou, au contraire , conve rtir n e n float, cons e rve r p e t appe ler la fonction
IV.
h ) Appelde la fonction III, aprè s conve rsion (dégradante ) de z en float (du m oins, depuis la ve rsion 2.0, car
auparavant, on aboutissait à une e rre ur de com pilation lié e à l'am biguïté de l'appe l).
i) Appelincorre ct, com pte te nu de s on am biguïté ;deux possibilité s e xiste nt e n e ffe t : conve rtir le pre m ie r
argum e nt e n float e t le s e cond e n int e t appe ler la fonction III ou, au contraire , conve rtir le pre m ie r
argum e nt e n int e t le s e cond e n float e t appe ler la fonction IV. Note z q ue , dans le deux cas, ils'agit de
conve rsions dégradante s (dans les ve rsions anté rie ure s à la 2.0, ces derniè re s n'é taie nt pas acce pté e s , e t
l'appe lé tait re je té parce q u'alors aucune corre s pondance n'é tait trouvé e !).
___________________________________________________________________________
Enoncé
Ecrire plus sim plem e nt e n C+ + les instructions suivante s , e n utilisant les opé rate urs ne w e t de lete :
int * adi ;
double * add ;
.....
adi = malloc (sizeof (int) ) ;
add = malloc (sizeof (double) * 100 ) ;
14 Exe rcice s e n langage C+ +
___________________________________________________________________________
Sol
ution
int * adi ;
double * add ;
.....
adi = new int ;
add = new double [100] ;
O n pe ut é ve ntue llem e nt te nir com pte des possibilités de déclarations dynam iq ue s offe rte s par C+ + (c'e s t-à -
dire q ue l'on pe ut introduire une déclaration à n'im porte q ue le m place m e nt d'un program m e ), e t é crire , par
e xe m ple :
___________________________________________________________________________
Enoncé
Ecrire plus sim plem e nt e n C+ + , e n utilisant les s pé cificités de ce langage , les instructions C suivante s :
double * adtab ;
int nval ;
.....
printf ("combien de valeurs ? ") ;
scanf ("%d", &nval) ;
adtab = malloc (sizeof (double) * nval) ;
___________________________________________________________________________
Sol
ution
double * adtab ;
int nval ;
.....
cout << "combien de valeurs ? " ;
cin >> nval ;
adtab = new double [nval] ;
___________________________________________________________________________
II. Le s s pécificités de C+ + 15
Enoncé
Ecrire un program m e allouant des em place m e nts pour de s tableaux d'e ntie rs dont la taille e s t fournie e n
donné e . Le s allocations s e poursuivront jus q u'à ce q ue l'on aboutisse à un débordem e nt m é m oire .
L'e xé cution s e pré s e nte ra ainsi :
O n propos e ra de ux solutions, l'une ne faisant pas appe là s e t_ne w _h andler (ilfaudra donc vé rifie r la valeur
fournie par ne w ), l'autre faisant appe là s e t_ne w _h andler.
___________________________________________________________________________
Sol
ution
a) Sans util
iser set_new _h andl
er
O n s e conte nte de ré pé te r l'allocation par ne w de blocs ayant la taille indiq ué e , jus q u'à ce q ue l'adre s s e
fournie e n re tour soit nulle (NULL) :
b) En util
isant set_new _h andl
er
Ce tte fois, on com m e nce par appe ler la fonction s e t_ne w _h andler, à laq ue lle on pré cis e l'adresse d'une
fonction (nom m é e ici de borde ). Ce tte derniè re s e ra appe lée autom atiq ue m e nt dè s q u'une allocation m é m oire
aura é ch oué , de sorte q u'iln'e s t plus néce s s aire de pré voir de te s t à ch aq ue appe lde ne w .
16 Exe rcice s e n langage C+ +
___________________________________________________________________________
Enoncé
a) Transform e r le program m e s uivant pour q ue la fonction fct devie nne une fonction "e n ligne ".
#include <iostream.h>
main()
{
int fct (char, int) ; // déclaration (prototype) de fct
int n = 150, p ;
char c = 's' ;
p = fct ( c , n) ;
cout << "fct (\'" << c << "\', " << n << ") vaut : " << p ;
}
int fct (char c, int n) // définition de fct
{
int res ;
if (c == 'a') res = n + c ;
else if (c == 's') res = n - c ;
else res = n * c ;
return res ;
}
b) Com m e nt faudrait-ilprocéder si l'on souh aitait q ue la fonciton fct soit com pilée s é paré m e nt?
II. Le s s pécificités de C+ + 17
___________________________________________________________________________
Sol
ution
a) Nous devons donc d'abord dé clare r (e t définir e n m ê m e te m ps) la fonction fct com m e une fonction "e n
ligne ". Le program m e m ain s'é crit de la m ê m e m aniè re , si ce n'e s t q ue la déclaration de fct n'y e s t plus
né ce s s aire puis q u'e lle apparaî
t déjà auparavant.
#include <iostream.h>
inline int fct (char c, int n)
{ int res ;
if (c == 'a') res = n + c ;
else if (c == 's') res = n - c ;
else res = n * c ;
return res ;
}
main ()
{ int n = 150, p ;
char c = 's' ;
p = fct (c, n) ;
cout << "fct (\'" << c << "\', " << n << ") vaut : " << p ;
}
b) Ils'agit e n fait d'une q ue s tion piè ge . En e ffe t, la fonction fct é tant "e n ligne ", e lle ne pe ut plus ê tre
com pilée s é paré m e nt. Ile s t ce pe ndant possible de la cons e rve r dans un fich ie r d'e xte nsion h e t d'incorpore r
sim plem e nt ce fich ie r par #include pour com piler le m ain. Ce tte dém arch e s e re ncontre ra d'ailleurs
fré q ue m m e nt dans le cas de clas s e s com portant des fonctions "e n ligne ". D ans ce cas, dans un fich ie r
d'exte nsion h , on trouve ra la déclaration de la clas s e e n q ue s tion, à l'inté rie ur de laq ue lle apparaî tront les
"déclarations-définitions" des fonctions "e n ligne ".
CH A PITRE III :
NO TIO NS D E CLASSE,
CO NSTRUCTEUR ET D ESTRUCTEUR
RAPPELS
Le s possibilités de Program m ation O rie nté e O b je t de C+ + re pos e nt sur le conce pt de clas s e . Une clas s e e s t
la gé né ralisation de la notion de type défini par l'utilisate ur, dans leq ue ls e trouve nt associé e s à la fois des
donné e s (on parle de "m e m bres donnée") e t des fonctions (on parle de "fonctions m e m bre " ou de m é th ode s ).
En P.O .O . pure , les données sont "e ncapsulée s ", ce q ui signifie q ue leur accè s ne peut s e faire q ue par le
biais des m é th ode s . C+ + vous autoris e à n'e ncapsuler q u'une partie s e ulem e nt des données d'une clas s e 1.
D é cl
aration e t dé finition d'une cl
as s e
La d é cl aration d'une clas s e pré cis e q ue ls sont les m e m bre s (donné e s ou fonctions) publics (c'e s t-à -dire
acce s s ibles à l'utilisate ur de la clas s e ) e t q ue ls sont les m e m bre s privé s (inacce s s ibles à l'utilisate ur de la
clas s e ). O n utilise pour cela le m ot clé publ ic, com m e dans ce t e xe m ple dans leq ue lla clas s e point com porte
deux m e m bres donnée privé s x e t y e t trois fonctions m e m bre s publiq ue s initialise , de place e t affich e :
La d é finition d'une clas s e consiste à fournir les définitions des fonctions m e m bre . Dans ce cas, on indiq ue
le nom de la clas s e corre s pondante , à l'aide de l'opé rate ur de ré s olution de porté e (::). Au sein de la
définition m ê m e , les m e m bre s (privé s ou publics - donné e s ou fonctions) sont dire cte m e nt acce s s ibles s ans
q u'ilsoit né ce s s aire de pré cis e r le nom de la clas s e . Voici, par e xe m ple, ce q ue pourrait ê tre la définition de
la fonction initialise de la clas s e pré cédente :
Ici, x e t y re pré s e nte nt im plicite m e nt les m e m bre s x e t y d'un obje t de la clas s e point.
R e m arque :
D e puis la ve rsion 1.2 de C+ + , on pe ut é galem e nt utiliser le m ot clé private. Iln'e s t alors plus
né ce s s aire de scinde r e n de ux partie s (publiq ue e t privé e ) les m e m bres d'une clas s e . Une déclaration te lle
q ue la suivante e s t tout à fait e nvisage able :
class X
{ private :
...
public :
...
private :
...
public :
...
} ;
Util
is ation d'une cl
as s e
O n dé clare un "obje t" d'un type classe donné en faisant pré céder son nom du nom de la clas s e , com m e dans
l'instruction suivante q ui dé clare deux obje ts a e t b de type point :
point a, b ;
O n pe ut accéder à n'im porte q ue lm e m bre public (donné e ou fonction) d'une clas s e e n utilisant l'opé rate ur .
(point). Par e xe m ple :
a.initialise (5, 2) ;
appe lle la fonction m e m bre initialise de la clas s e à laq ue lle appartie nt l'obje t a, c'e s t-à -dire , ici, la clas s e
point.
C+ + autoris e l'affe ctation d'un obje t d'un type donné à un autre obje t de m ê m e type . Dans ce cas, ily a
(tout nature llem e nt) re copie des valeurs des ch am ps de données (qu'ils soie nt publics ou privé s ). Toute fois,
si, parm i ce s ch am ps, s e trouve nt des pointe urs, les e m place m e nts pointé s ne s e ront pas soum is à ce tte
re copie . Si un te le ffe t e s t né ce s s aire (e t ille s e ra souve nt!), ilne pourra ê tre obte nu q u'e n "surdéfinissant"
l'opé rate ur d'affe ctation pour la clas s e conce rné e (voye z le ch apitre consacré à la surdéfinition
d'opé rate urs).
III. Notions de clas s e , cons tructe ur e t de s tructe ur 25
Une fonction m e m bre portant le m ê m e nom q ue s a clas s e s e nom m e un constructeur. Dè s lors q u'une clas s e
com porte un constructe ur (au m oins un), iln'e s t plus possible de déclare r un obje t du type corre s pondant,
sans fournir de s valeurs pour les argum e nts re q uis par ce constructe ur (sauf si ce dernie r ne possè de aucun
argum e nt). Le constructe ur e s t appe lé aprè s l'allocation de l'e s pace m é m oire destiné à l'obje t.
Par définition m ê m e , un constructe ur ne re nvoie pas de valeur (aucune indication de type , pas m ê m e void,
ne doit figure r de vant sa déclaration ou sa définition).
Une fonction m e m bre portant le m ê m e nom q ue s a clas s e , pré cédé du sym bole tilda (~ ), s e nom m e un
destructeur. Le destructe ur e s t appe lé avant la libération de l'e s pace m é m oire associé à l'obje t. Par
définition m ê m e , un de s tructe ur ne pe ut pas com porte r d'argum e nts e t ilne re nvoie pas de valeur (aucune
indication de type ne doit ê tre pré vue ).
Un m e m bre donnée déclaré ave c l'attribut static e s t partagé par tous les obje ts de la m ê m e clas s e . Ile xiste
m ê m e lors q ue aucun obje t de ce tte clas s e n'a é té déclaré . Un m e m bre donnée statiq ue e s t initialisé par
défaut à zé ro (com m e toute s les variables s tatiq ue s !). Ilpe ut ê tre initialisé explicite m e nt, à l'e xté rie ur de la
clas s e (m ê m e s 'ile s t privé ), e n utilisant l'opé rate ur de ré s olution de porté e (::) pour spécifie r sa clas s e .
Expl
oitation d'une cl
as s e
En pratiq ue , à l'utilisate ur d'une clas s e (on dit souve nt le "clie nt"), on fournira :
- un fich ie r e n-tê te conte nant la déclaration de la clas s e : l'utilisate ur l'e m ployant devra l'inclure dans
tout program m e faisant appe là la clas s e e n q ue s tion,
- un m odule obje t ré s ultant de la com pilation du fich ie r source conte nant la définition de la clas s e , c'e s t-
à -dire la définition de s e s fonctions m e m bre .
___________________________________________________________________________
Enoncé
R é aliser une clas s e point pe rm e ttant de m anipuler un point d'un plan. O n pré voira :
O n é crira s é paré m e nt :
Ecrire , par ailleurs, un pe tit program m e d'essai (m ain) déclarant un point, l'affich ant, le déplaçant e t
l'affich ant à nouve au.
___________________________________________________________________________
Sol
ution
/* fichier POINT1.H */
/* déclaration de la classe point */
class point
{
float x, y ; // coordonnées (cartésiennes) du point
public :
point (float, float) ; // constructeur
void deplace (float, float) ; // déplacement
void affiche () ; // affichage
} ;
Note z q ue , pour com piler ce fich ie r source , ile s t né ce s s aire d'inclure le fich ie r source , nom m é ici point1.h ,
conte nant la déclaration de la clas s e point.
c) Exem pl
e d'util
isation de l
a cl
asse
Bie n e nte ndu, pour pouvoir e xé cute r ce program m e , ils e ra né ce s s aire d'introduire , lors de l'édition de lie ns,
le m odule obje t ré s ultant de la com pilation du fich ie r source conte nant la définition de la clas s e point.
Note z q ue , gé né ralem e nt, le fich ie r point.h contie ndra des directive s conditionne lles de com pilation, afin
d'évite r les ris q ues d'inclusion m ultiple. Par e xe m ple, on pourra procéder ainsi :
#ifndef POINT_H
#define POINT_H
.....
.....
#endif
___________________________________________________________________________
Enoncé
R é aliser une clas s e point, analogue à la pré cédente , m ais ne com portant pas de fonction affich e . Pour
re s pe cte r le principe d'encapsulation des données, prévoir de ux fonctions m e m bre publiq ue s (nom m é e s
abscis s e e t ordonne e ) fournissant e n re tour re s pe ctive m e nt l'abscis s e e t l'ordonnée d'un point. Adapte r le
pe tit program m e d'essai pré cédent pour q u'ilfonctionne ave c ce tte nouve lle clas s e .
___________________________________________________________________________
Sol
ution
Ilsuffit d'introduire deux nouve lles fonctions m e m bre abscis s e e t ordonné e e t de supprim e r la fonction
affich e . La nouve lle déclaration de la clas s e e s t alors :
/* fichier POINT2.H */
/* déclaration de la classe point */
class point
{
float x, y ; // coordonnées (cartésiennes) du point
public :
point (float, float) ; // constructeur
void deplace (float, float) ; // déplacement
float abscisse () ; // abscisse du point
float ordonnee () ; // ordonnée du point
} ;
D is cus s ion
Ce t e xe m ple m ontre com m e nt ile s t toujours possible de re s pe cte r le principe d'encapsulation e n introduisant
ce q ue l'on nom m e des "fonctions d'accè s". Ils'agit de fonctions m e m bre destiné e s à accéder (aussi bien en
consultation - com m e ici - q u'e n m odification) aux m e m bre s privé s . L'inté rê t de leur e m ploi (par rapport à
un accè s direct aux donné e s q u'ilfaudrait alors re ndre publiq ue s ) ré s ide dans la souplesse de m odification de
l'im plém e ntation de la clas s e q ui e n dé coule. Ainsi, ici, ile s t tout à fait possible de m odifie r la m aniè re dont
un point e s t re pré s e nté (par e xe m ple, e n utilisant s e s coordonné e s polaire s plutôt q ue s e s coordonné e s
carté s ie nne s ), sans q ue l'utilisate ur de la clas s e n'ait à s e s oucie r de ce t aspect. C'e s t d'ailleurs ce q ue vous
m ontre ra l'e xe rcice III.4 ci-aprè s.
___________________________________________________________________________
Enoncé
Ajoute r à la clas s e pré cédente (com portant un constructe ur e t 3 fonctions m e m bre de place , abscis s e e t
ordonne e ) de nouve lles fonctions m e m bre :
- h om oth e tie q ui e ffe ctue une h om oth é tie dont le rapport e s t fourni e n argum e nt,
- rotation q ui e ffe ctue une rotation dont l'angle e s t fourni e n argum e nt,
- rh o e t th e ta q ui fournis s e nt e n re tour les coordonnées pol
aires du point.
___________________________________________________________________________
III. Notions de clas s e , cons tructe ur e t de s tructe ur 29
Sol
ution
/* fichier POINT3.H */
/* déclaration de la classe point */
class point
{
float x, y ; // coordonnées (cartésiennes) du point
public :
point (float, float) ; // constructeur
void deplace (float, float) ; // déplacement
void homothetie (float) ; // homothetie
void rotation (float) ; // rotation
float abscisse () ; // abscisse du point
float ordonnee () ; // ordonnée du point
float rho () ; // rayon vecteur
float theta () ; // angle
} ;
Sa définition m é rite q ue lque s re m arq ue s . En e ffe t, si h om oth e tie ne pré s e nte aucune difficulté , la fonction
m e m bre rotation, q uant à e lle, né ce s s ite une transform ation inte rm édiaire des coordonné e s carté s ie nnes du
point e n coordonné e s polaire s . De m ê m e , la fonction m e m bre rh o doit calculer le rayon ve cte ur d'un point
dont on connaî t les coordonné e s carté s ie nne s tandis q ue la fonction m e m bre th e ta doit calculer l'angle d'un
point dont on connaî t les coordonné e s carté s ie nne s .
Le calculde rayon ve cte ur é tant sim ple, nous l'avons lais s é figure r dans les deux fonctions conce rné e s
(rotation e t rh o). En re vanch e , le calculd'angle a é té ré alisé par ce que nous nom m ons une "fonction de
s e rvice ", c'e s t-à -dire une fonction q ui n'a d'inté rê t q ue dans la définition de la clas s e e lle-m ê m e . Ici, il
s'agit d'une fonction indé pe ndante m ais, bie n e nte ndu, on pe ut pré voir de s fonctions de service s ous form e
de fonctions m e m bre (e lles s e ront alors généralem e nt privé e s ).
float point::abscisse ()
{ return x ;
}
float point::ordonnee ()
{ return y ;
}
float point::rho ()
{ return sqrt (x*x + y*y) ;
}
float point::theta ()
{ return angle (x, y) ;
___________________________________________________________________________
Enoncé
M odifie r la clas s e point pré cédente , de m aniè re à ce q ue les données (privé e s ) soient m ainte nant les
coordonné e s polaires d'un point, e t non plus s e s coordonné e s carté s ie nne s . O n é vite ra de m odifie r la
déclaration de s m e m bre s publics, de sorte q ue l'inte rface de la clas s e (ce q ui e s t visible pour l'utilisate ur) ne
ch ange pas.
___________________________________________________________________________
Sol
ution
/* fichier POINT4.H */
/* déclaration de la classe point */
III. Notions de clas s e , cons tructe ur e t de s tructe ur 31
class point
{
float r, t ; // coordonnées (polaires) du point
public :
point (float, float) ; // constructeur
void deplace (float, float) ; // déplacement
void homothetie (float) ; // homothétie
void rotation (float) ; // rotation
float abscisse () ; // abscisse du point
float ordonnee () ; // ordonnée du point
float rho () ; // rayon vecteur
float theta () ; // angle
} ;
- q ue le constructe ur re çoit toujours e n argum e nt les coordonné e s carté s ie nnes d'un point ;ildoit donc
opé re r les transform ations approprié e s ,
- q ue la fonction de place re çoit un dé place m e nt e xprim é e n coordonné e s carté s ie nne s ;ilfaut donc tout
d'abord dé te rm ine r les coordonné e s carté s ie nnes du point aprè s déplace m e nt, avant de re pas s e r e n
coordonné e s polaire s .
En re vanch e , les fonctions h om oth e tie e t rotation s'e xprim e nt trè s sim plem e nt.
Voici la définition de notre nouve lle clas s e (nous avons fait appe là la m ê m e "fonction de s e rvice " angle q ue
dans l'e xe rcice pré cédent) :
#include "point4.h"
#include <iostream.h>
#include <math.h> // pour cos, sin, sqrt et atan
const int pi = 3.141592635 ; // valeur de pi
{ r *= hm ;
}
void point::rotation (float th)
{ t += th ;
}
float point::abscisse ()
{ return r * cos (t) ;
}
float point::ordonnee ()
{ return r * sin (t) ;
}
float point::rho ()
{ return r ;
}
float point::theta ()
{ return t ;
}
___________________________________________________________________________
Enoncé
Soit la clas s e point crée dans l'e xe rcice III.1, e t dont la déclaration é tait la suivante :
class point
{
float x, y ;
public :
point (float, float) ;
void deplace (float, float) ;
void affiche () ;
}
Adapte r ce tte clas s s e , de m aniè re à ce q ue la fonction m e m bre affich e fournis s e , e n plus des coordonnées du
point, le nom bre d'obje ts de type point.
___________________________________________________________________________
Sol
ution
Ilfaut donc dé finir un com pte ur du nom bre d'obje ts e xistant à un m om e nt donné . Ce com pte ur doit ê tre
incré m e nté à ch aq ue cré ation d'un nouve lobje t, donc par le constructe ur point. De m ê m e , ildoit ê tre
décré m e nté à ch aq ue destruction d'un obje t, donc par le destructe ur de la clas s e point ;ilfaudra donc
ajoute r ici une fonction m e m bre nom m é e ~ point.
Quant au com pte ur propre m e nt dit, nous pourrions ce rte s e n faire une variable globale, définie par e xe m ple,
e n m ê m e te m ps q ue la clas s e ;ce tte dém arch e pré s e nte toute fois des ris q ues d'effe ts de bord (m odification
III. Notions de clas s e , cons tructe ur e t de s tructe ur 33
accide nte lle de la valeur de ce tte variable, depuis n'im porte q ue l program m e utilisate ur). Il e s t plus
judicie ux d'e n faire un m e m bre privé s tatiq ue 2.
/* fichier POINT5.H */
/* déclaration de la classe point */
class point
{
static nb_pts ; // compteur du nombre d'objets créés
float x, y ; // coordonnées (cartésiennes) du point
public :
point (float, float) ; // constructeur
~point () ; // destructeur
void deplace (float, float) ; // déplacement
void affiche () ; // affichage
} ;
#include "point5.h"
#include <iostream.h>
point::point (float abs, float ord) // constructeur
{ x = abs ; y = ord ;
nb_pts++ ; // actualisation nb points
}
point::~point () // destructeur
{ nb_pts-- ; // actualisation nb points
}
void point::deplace (float dx, float dy)
{ x = x + dx ; y = + dy ;
}
void point::affiche ()
{ cout << "Je suis un point parmi " << nb_pts
<< " de coordonnées "<< x << " " << y << "\n" ;
}
R e m arque :
Ilpourraî t ê tre judicie ux de m unir notre clas s e point d'une fonction m e m bre fournissant le nom bre
d'obje ts de type point e xistant à un m om e nt donné . C'e s t ce q ue nous vous proposerons dans un exercice
du proch ain ch apitre .
___________________________________________________________________________
Enoncé
R é aliser une clas s e nom m é e s e t_ch ar pe rm e ttant de m anipuler de s e ns e m bles de caractè re s . O n de vra
pouvoir ré aliser sur un te le ns e m ble les opé rations classiques suivante s : lui ajoute r un nouve lé lém e nt,
connaî tre s on "cardinal" (nom bre d'élém e nts), savoir si un caractè re donné lui appartie nt.
Ecrire , e n outre , un program m e (m ain) utilisant la clas s e s e t_ch ar pour dé te rm ine r le nom bre de caractè re s
diffé re nts conte nus dans un m ot lu e n donné e .
___________________________________________________________________________
Sol
ution
Com pte te nu de s contrainte s im pos é e s par l'é noncé (pas de gestion dynam iq ue ), une s olution consiste à
pré voir un tableau dans leq ue lun é lém e nt de rang i pré cis e s i le caractè re de code i appartie nt ou non à
l'e ns e m ble. Note z q u'il e s t né ce s s aire q ue i soit positif ou nul;on travaillera donc toujours sur de s
caractè re s non signés. La taille du tableau doit ê tre é gale au nom bre de caractè re s q u'ile s t possible de
re pré s e nte r dans une im plém e ntation donné e (gé né ralem e nt 256).
/* fichier SETCHAR1.H */
/* déclaration de la classe set_char */
#define N_CAR_MAX 256 // on pourrait utiliser UCHAR_MAX défini
// dans <limits.h>
class set_char
{
unsigned char ens [N_CAR_MAX] ;
// tableau des indicateurs (présent/absent)
// pour chacun des caractères possibles
public :
set_char () ; // constructeur
void ajoute (unsigned char) ; // ajout d'un élément
int appartient (unsigned char) ; // appartenance d'un élément
int cardinal () ; // cardinal de l'ensemble
} ;
{ return ens[c] ;
}
int set_char::cardinal ()
{ int i, n ;
for (i=0, n=0 ; i<N_CAR_MAX ; i++) if (ens[i]) n++ ;
return n ;
}
R e m arque :
Si l'on avait déclaré de type ch ar les argum e nts de ajoute e t appartie nt, on aurait alors pu aboutir, soit au
type unsigne d ch ar, soit au type signe d ch ar, s e lon l'e nvironne m e nt utilisé. Dans le dernie r cas, on aurait
couru le ris q ue de transm e ttre , à l'une des fonctions m e m bre cité e s , une valeur né gative , e t partant
d'accéder à l'e xté rie ur du tableau e ns.
D is cus s ion
Le tableau e ns [N_CH AR _M AX] occupe un octe t par caractè re ;ch acun de ce s octe ts ne prend que l'une des
valeurs 0 ou 1 ;on pourrait é conom iser de l'e s pace m é m oire e n pré voyant s e ulem e nt 1 bit par caractè re . Le s
fonctions m e m bre y perdraient toute fois e n sim plicité , ainsi qu'en vite s s e .
Bie n e nte ndu, be aucoup d'autre s im plém e ntations sont possibles ;c'e s t ainsi, par e xe m ple, q ue l'on pourrait
fournir au constructe ur un nom bre m axim ald'élém e nts, e t alloue r dynam iq ue m e nt l'e m place m e nt m é m oire
corre s pondant ;toute fois, là e ncore , on pe rdrait le béné fice de la corre s pondance im m édiate e ntre un
caractè re e t la position de s on indicate ur. Note z, toute fois, q ue ce s e ra la s e ule possibilité ré aliste lors q u'il
s'agira de re pré s e nte r de s e ns e m bles dans les q ue ls le nom bre m axim ald'élém e nts s e ra trè s grand.
___________________________________________________________________________
Enoncé
M odifie r la clas s e s e t_ch ar pré cédente , de m aniè re à ce q ue l'on dispose de ce que l'on nom m e un
"ité rate ur" sur les diffé re nts é lém e nts de l'e ns e m ble. O n nom m e ainsi un m é canism e pe rm e ttant d'accéder
s é q ue ntie llem e nt aux diffé re nts é lém e nts. O n pré voira trois nouve lles fonctions m e m bre : init, q ui initialise
36 Exe rcice s e n langage C+ +
le proce s s us d'exploration ;proch ain, q ui fournit l'é lém e nt suivant lors q u'ile xiste e t e xiste , q ui pré cis e s 'il
e xiste e ncore un é lém e nt non e xploré .
O n com pléte ra alors le program m e d'utilisation pré cédent, de m aniè re à ce q u'ilaffich e les diffé re nts
caractè re s conte nus dans le m ot fourni e n donné e .
___________________________________________________________________________
Sol
ution
Com pte te nu de l'im plém e ntation de notre clas s e , la ge s tion du m é canism e d'ité ration né ce s s ite l'e m ploi
d'un pointe ur (q ue nous nom m e rons courant) sur un é lém e nt du tableau e ns. Nous convie ndrons q ue courant
désigne le pre m ie r é lém e nt de e ns non e ncore traité dans l'ité ration, c'e s t-à -dire non e ncore re nvoyé par la
fonction m e m bre suivant (nous aurions pu adopte r la conve ntion contraire , à savoir q ue courant désigne le
dernie r é lém e nt traité ).
En outre , pour nous facilite r la re connaissance de la fin de l'ité ration, nous utiliserons un m e m bre donnée
supplém e ntaire (fin) valant 0 dans les cas usuels, e t 1 lors q ue aucun é lém e nt ne s e ra disponible (pour
suivant).
Le rôle de la fonction init s e ra donc de faire pointe r courant sur la pre m iè re valeur non nulle de e ns s'ile n
e xiste une ;dans le cas contraire , fin s e ra placé à 1.
La fonction suivant fournira e n re tour l'é lém e nt pointé par courant lors q u'ile xiste (fin non nul) ou la valeur
0 dans le cas contraire (ils'agit là d'une conve ntion de s tiné e à proté ge r l'utilisate ur ayant appe lé ce tte
fonction, alors q u'aucun é lém e nt n'é tait plus disponible). Dans le pre m ie r cas, suivant re ch e rch e ra le
proch ain é lém e nt de l'e ns e m ble (e n m odifiant la valeur de fin lors q u'un te lé lém e nt n'e xiste pas). Note z bie n
q ue la fonction suivant doit re nvoye r, non pas le proch ain é lém e nt, m ais l'é lém e nt courant.
Enfin la fonction e xiste s e conte nte ra de re nvoye r la valeur de fin puis q ue ce tte derniè re indiq ue l'e xiste nce
ou l'ine xiste nce d'un élém e nt courant.
/* fichier SETCHAR2.H */
/* déclaration de la classe set_char */
#define N_CAR_MAX 256 // on pourrait utiliser UCHAR_MAX défini
// dans <limits.h>
class set_char
{
unsigned char ens [N_CAR_MAX] ;
// tableau des indicateurs (présent/absent)
// pour chacun des caractères possibles
int courant ; // position courante dans le tableau ens
int fin ; // indique si fin atteinte
public :
set_char () ; // constructeur
void ajoute (unsigned char) ; // ajout d'un élément
int appartient (unsigned char) ; // appartenance d'un élément
int cardinal () ; // cardinal de l'ensemble
void init () ; // initialisation itération
unsigned char suivant () ; // caractère suivant
int existe () ; //
} ;
III. Notions de clas s e , cons tructe ur e t de s tructe ur 37
Voici la définition de s trois nouve lles fonctions m e m bre init, suivant e t e xiste :
void set_char::init ()
{ courant=0 ; fin = 0 ;
while ( (++courant<N_CAR_MAX) && (!ens[courant]) ) ;
// si la fin de ens est atteinte, courant vaut N_CAR_MAX
if (courant>=N_CAR_MAX) fin = 1 ;
}
int set_char::existe ()
{ return (!fin) ;
}
#include <iostream.h>
#include <string.h>
#include "setchar2.h"
main()
{ set_char ens ;
char mot [81] ;
cout << "donnez un mot " ;
cin >> mot ;
int i ;
for (i=0 ; i<strlen(mot) ; i++) ens.ajoute (mot[i]) ;
cout << "il contient " << ens.cardinal () << " caractères différents"
<< " qui sont :\n" ;
ens.init() ; // init itération sur les caractères de l'ensemble
while (ens.existe())
cout << ens.suivant () ;
}
CH A PITRE IV :
PRO PRIETES D ES FO NCTIO NS M EM BRE
RAPPELS
Ils'agit sim plem e nt de la gé né ralisation aux fonctions m e m bre des possibilités déjà offe rte s par C+ + pour
les "fonctions ordinaire s ".
Fonctions m e m bre e n l
igne
Ils'agit é galem e nt de la gé né ralisation aux fonctions m e m bre d'une possibilité offe rte pour les fonctions
ordinaire s , ave c une pe tite nuance conce rnant sa m ise en oeuvre ;pour re ndre "e n ligne " une fonction
m e m bre , on pe ut :
- soit fournir dire cte m e nt la définition de la fonction dans la déclaration m ê m e de la clas s e ;dans ce cas
le q ualificatif inline n'a pas à ê tre utilisé, com m e dans ce t e xe m ple :
class truc
{ ...
int fctenlig (int, float)
{ définition de fcte nlig
}
.....
} ;
- soit procéder com m e pour une fonction "ordinaire ", e n fournissant une définition e n de h ors de la
déclaration de la clas s e ;dans ce cas, le q ualificatif inline doit apparaî
tre , à la fois devant la déclaration
e t devant l'e n-tê te .
Une fonction m e m bre re çoit im plicite m e nt l'adresse de l'obje t l'ayant appe lé. M ais, e n outre , ile s t toujours
possible de lui transm e ttre e xplicite m e nt un argum e nt (ou plusieurs argum e nts) du type de sa clas s e , ou
m ê m e du type d'une autre clas s e . Dans le pre m ie r cas, la fonction m e m bre aura accè s aux m e m bre s privé s
de l'argum e nt e n q ue s tion (car, e n C+ + , l'unité d'encapsulation e s t la clas s e e lle-m ê m e e t non l'obje t!). En
re vanch e , dans le s e cond cas, la fonction m e m bre n'aura accè s q u'aux m e m bre s publics de l'argum e nt.
46 Exe rcice s e n langage C+ +
Un te l argum e nt pe ut ê tre transm is classiquem e nt par valeur, par adre s s e ou par ré fé re nce . Ave c la
transm ission par valeur, ily a re copie des valeurs des m e m bres donnée dans un em place m e nt localà la
fonction appe lée . Des problèm e s pe uve nt surgir dè s lors q ue l'obje t transm is en argum e nt contie nt des
pointe urs sur de s parties dynam iq ue s . Ils s e ront ré glés par l'e m ploi d'un "constructe ur par re copie " (voir
ch apitre s uivant).
R e m arque :
Bie n q ue ce s oit d'un usage plus lim ité , une fonction ordinaire pe ut é galem e nt re ce voir un argum e nt de
type clas s e . Bie n e nte ndu, dans ce cas, e lle n'aura accè s q u'aux m e m bre s publics de ce t argum e nt.
Une fonction m e m bre pe ut fournir com m e valeur de re tour un obje t du type de sa clas s e ou d'un autre type
clas s e (dans ce dernie r cas, e lle n'accè dera bie n sûr q u'aux m e m bre s publics de l'obje t e n q ue s tion). La
transm ission peut, là e ncore , s e faire par valeur, par adre s s e ou par ré fé re nce .
La transm ission par valeur im pliq ue une re copie q ui pose donc les m ê m e s problèm e s q ue ce ux é voq ué s ci-
desssus pour les obje ts com portant des pointe urs sur de s parties dynam iq ue s . Quant aux transm issions par
adre s s e ou par ré fé re nce , e lles doive nt ê tre utilisées ave c beaucoup de pré cautions, dans la m e s ure où, dans
ce cas, on re nvoie (gé né ralem e nt) l'adresse d'un obje t alloué autom atiq ue m e nt, c'e s t-à -dire dont la duré e d e
vie coïncide ave c ce lle de la fonction.
Auto-ré fé re nce : l
e m ot cl
é th is
Au sein d'une fonction m e m bre , th is re pré s e nte un pointe ur sur l'obje t ayant appe lé ladite fonction m e m bre .
Lors q u'une fonction m e m bre a une action indé pe ndante d'un q ue lconq ue obje t de sa clas s e , on pe ut la
déclare r ave c l'attribut static. D ans ce cas, une te lle fonction pe ut ê tre appe lée , sans m e ntionne r d'obje t
particulie r, e n pré fixant sim plem e nt son nom du nom de la clas s e conce rné e , suivi de l'opé rate ur de
ré s olution de porté e (::).
O n pe ut déclare r de s obje ts constants (à l'aide du qualificatif const). Dans ce cas, seules les fonctions
m e m bre déclaré e s (e t définie s ) ave c ce m ê m e q ualificatif (e xe m ple de déclaration : void affich e () const)
pe uve nt re ce voir (im plicite m e nt ou e xplicite m e nt) e n argum e nt un obje t constant.
___________________________________________________________________________
IV. Propriétés des fonctions m e m bre 47
Enoncé
O n souh aite ré aliser une clas s e ve cte ur3d pe rm e ttant de m anipuler de s ve cte urs à trois com posante s . O n
pré voit q ue s a déclaration s e pré s e nte ainsi :
class vecteur3d
{ float x, y, z ; // pour les 3 composantes (cartésiennes)
.....
} ;
O n souh aite pouvoir dé clare r un ve cte ur, soit e n fournissant e xplicite m e nt s e s trois com posante s , soit e n e n
fournissant aucune , auq ue l cas le ve cte ur cré é possè dera trois com posante s nulles . Ecrire le ou les
constructe ur(s) corre s pondant(s) :
Sol
ution
Ils'agit de sim ples applications des possibilités de surdé finition, d'argum e nts par dé faut e t d'écriture "e n
ligne " des fonctions m e m bre s .
a)
/* déclaration de la classe vecteur3d */
class vecteur3d
{
float x, y, z ;
public :
vecteur3d () ; // constructeur sans arguments
vecteur3d (float, float, float) ; // constructeur 3 composantes
.....
} ;
b)
/* déclaration de la classe vecteur3d */
class vecteur3d
{
float x, y, z ;
public :
vecteur3d (float=0.0, float=0.0, float=0.0) ; // constructeur (unique)
.....
} ;
/* définition du constructeur de la classe vecteur3d */
48 Exe rcice s e n langage C+ +
O n note ra toute fois q u'ave c ce constructe ur ile s t possible de déclare r un point e n fournissant non s e ulem e nt
0 ou 3 com posante s , m ais é ve ntue llem e nt s e ulem e nt une ou de ux. Ce tte s olution n'e s t donc pas
rigoure us e m e nt é q uivalente à la pré cédente .
c)
/* déclaration de la classe vecteur3d */
class vecteur3d
{ float x, y, z ;
public :
// constructeur unique "en ligne"
vecteur3d (float c1=0.0, float c2=0.0, float c3=0.0) ;
..{ x = c1 ; y = c2 ; z = c3 ;
}.
.....
} ;
Ici, iln'y a plus aucune définition de constructe ur, puis q ue ce dernie r e s t "e n ligne ".
___________________________________________________________________________
Enoncé
class vecteur3d
{ float x, y, z ;
public :
vecteur3d (float c1=0.0, float c2=0.0, float c3=0.0)
{ x = c1 ; y = c2 ; z = c3 ;
}
...
} ;
Introduire une fonction m e m bre nom m é e coincide pe rm e ttant de savoir si deux ve cte urs ont m ê m e s
com posante s :
___________________________________________________________________________
IV. Propriétés des fonctions m e m bre 49
Sol
ution
La fonction coincide e s t m e m bre de la clas s e ve cte ur3d ;e lle re ce vra donc im plicite m e nt l'adresse du
ve cte ur l'ayant appe lé. Elle ne possédera donc q u'un s e ulargum e nt, lui-m ê m e de type ve cte ur3d. Nous
supposerons q u'e lle fournit une valeur de re tour de type int (1 pour la coïncide nce , 0 dans le cas contraire ).
R e m arque :
En utilisant th is, la définition de coincide pourrait faire m oins de distinction e ntre ses deux argum e nts
(l'un im plicite , l'autre e xplicite ) :
int vecteur3d::coincide (vecteur3d * adv)
{ if ( (adv->x == this->x) && (adv->y == this->y) && (adv->z == this->z) )
return 1 ;
else return 0 ;
}
Voici les 3 appe ls de coincide corre s pondant re s pe ctive m e nt aux 3 dé finitions précédente s :
D is cus s ion
La surdéfinition d'opé rate ur offrira une m ise en oeuvre plus agré able de ce te s t de coïncide nce de deux
ve cte urs. C'e s t ainsi qu'il s e ra possible de surdé finir l'opé rate ur de com paraison == (pour la clas s e
ve cte ur3d) e t, partant, d'e xprim e r ce te s t sous la sim ple form e : v1 == v2.
___________________________________________________________________________
Enoncé
class vecteur3d
{ float x, y, z ;
public :
vecteur3d (float c1=0.0, float c2=0.0, float c3=0.0)
{ x = c1 ; y = c2 ; z = c3 ;
}
.....
} ;
Introduire , dans ce tte clas s e , une fonction m e m bre nom m é e norm ax pe rm e ttant d'obte nir, parm i deux
ve cte urs, ce lui q ui a la plus grande norm e . O n pré voira de ux situations :
Sol
ution
a) La s e ule difficulté ré s ide dans la m aniè re de re nvoye r la valeur de l'obje t ayant appe lé une fonction
m e m bre , à savoir *th is. Voici la définition de la fonction norm ax (la déclaration e n dé coule
im m édiate m e nt) :
Voici un e xe m ple d'utilisation (on suppose que v1, v2 e t w sont de type ve cte ur3d) :
w = v1.normax (v2) ;
Note z bie n q ue l'affe ctation ne pos e aucun problèm e ici, puis q ue notre clas s e ne com porte aucun pointe ur
sur des parties dynam iq ue s .
b) Aucun nouve au problèm e ne s e pos e . Ilsuffit de m odifie r ainsi l'e n-tê te de notre fonction, sans e n
m odifie r le corps :
c) Ilfaut, ce tte fois, adapte r e n cons é q ue nce l'e n-tê te e t le corps de la fonction :
Ici, l'utilisation de la fonction né ce s s ite q ue lque s pré cautions. En voici un e xe m ple (v1, v2 e t w sont toujours
de type ve cte ur3d) :
w = *(v1.normax(&v2)) ;
D is cus s ion
En ce q ui conce rne la transm ission de l'uniq ue argum e nt e xplicite de norm ax, il faut note r q u'il e s t
im possible de la pré voir par valeur, dè s lors q ue norm ax doit re s titue r son ré s ultat par adre s s e ou par
ré fé re nce . En e ffe t, dans ce cas, on obtie ndrait e n re tour l'adre s s e ou la ré fé re nce d'un ve cte ur alloué
autom atiq ue m e nt au s e in de la fonction. Note z q u'un te lproblèm e ne s e pos e pas pour l'argum e nt im plicite
(th is), car ilcorre s pond toujours à l'adresse d'un ve cte ur (transm is autom atiq ue m e nt par ré fé re nce ), e t non à
une valeur.
___________________________________________________________________________
Enoncé
R é aliser une clas s e ve cte ur3d pe rm e ttant de m anipuler de s ve cte urs à 3 com posante s (de type float). O n y
pré voira :
_______________________________________________________________
Sol
ution
La fonction m e m bre calculant la som m e de deux ve cte urs (nous la nom m e rons som m e ) re çoit im plicite m e nt
(par ré fé re nce ) un argum e nt de type ve cte ur3d. Elle com porte ra donc un s e ulargum e nt, lui aussi de type
ve cte ur3d. O n pe ut, a priori, le transm e ttre par adre s s e , par valeur ou par ré fé re nce . En fait, la transm ission
par adre s s e , e n C+ + , n'a plus guè re de raison d'ê tre , dans la m e s ure où la transm ission par ré fé re nce fait la
m ê m e ch os e , m oye nnant une é criture plus agré able.
Le ch oix doit donc s e faire e ntre transm ission par valeur ou par ré fé re nce . Lors q u'ils'agit de transm e ttre un
obje t (com portant plusieurs m e m bres donnée), la transm ission par ré fé re nce e s t plus e fficace (e n te m ps
d'exécution). Qui plus e s t, la fonction som m e re çoit déjà im plicite m e nt un ve cte ur par ré fé re nce , de sorte
q u'iln'y a aucune raison de lui transm e ttre diffé re m m e nt le s e cond ve cte ur.
Le m ê m e raisonne m e nt s'appliq ue à la fonction de calculdu produit scalaire (q ue nous nom m ons prodscal).
En ce q ui conce rne la valeur de re tour de som m e , laq ue lle e s t é galem e nt de type ve cte ur3d, iln'e s t, e n
re vanch e , pas possible de la tranm e ttre par ré fé re nce . En e ffe t, ce "ré s ultat" (de type ve cte ur3d) s e ra cré é au
s e in de la fonction e lle-m ê m e , ce q ui signifie q ue l'obje t corre s pondant s e ra de clas s e autom atiq ue , donc
détruit à la fin de l'e xé cution de la fonction. Ilfaut donc absolum e nt e n transm e ttre la valeur.
Voici ce q ue pourrait ê tre la déclaration de notre clas s e ve cte ur3d (ici, s e ulle constructe ur a é té pré vu "e n
ligne " :
Voici sa définition :
{ cout << "< " << x << ", " << y << ", " << z << ">" ;
}
Voici un pe tit program m e d'essai de la clas s e ve cte ur3d, accom pagné des ré s ultats produit par son
e xé cution :
v1 = < 1, 2, 3>
v2 = < 3, 0, 2>
w = < 0, 0, 0>
w = < 4, 2, 5>
V1.V2 = 9
___________________________________________________________________________
Enoncé
Com m e nt pourrait-on adapte r la clas s e point cré é e d ans l'e xe rcice III.5, pour q u'e lle dispose d'une fonction
m e m bre nom bre fournissant le nom bre de points e xistant à un instant donné ?
___________________________________________________________________________
Sol
ution
O n pourrait ce rte s introduire une fonction m e m bre classique. Toute fois, ce tte s olution pré s e nte rait
l'inconvé nie nt d'oblige r l'utilisate ur à appliq ue r une te lle fonction à un obje t de type point. Que pe ns e r alors
d'un appe lte lq ue (p é tant un point) p.com pte () pour connaî tre le nom bre de points?Qui plus e s t, com m e nt
faire appe là com pte s'iln'e xiste aucun point?
La solution la plus agré able consiste à faire de com pte une "fonction statiq ue ". O n la déclare ra donc ainsi :
Voici sa définition :
int point::compte ()
{ return nb_pts ;
}
Voici un e xe m ple d'appe lde com pte au s e in d'un program m e dans leq ue lla clas s e point a é té déclaré e :
RAPPELS
Appe l
s du cons tructe ur e t du de s tructe ur
D ans tous les cas (obje ts statiq ue s , autom atiq ue s ou dynam iq ue s ), s'ily a appe ldu constructeur, ce lui-ci a
lie u aprè s l'allocation de l'e m place m e nt m é m oire destiné à l'obje t. De m ê m e , s'ile xiste un destructeur, ce
dernie r e s t appe lé avant la libération de l'e s pace m é m oire associé à l'obje t.
Le s obje ts autom atiques sont cré é s par une déclaration soit dans une fonction, soit au s e in d'un bloc. Ils
sont cré é s au m om e nt de l'e xé cution de la déclaration (laq ue lle, e n C+ + , pe ut apparaî
tre n'im porte où dans
un program m e ). Ils sont détruits lors q ue l'on sort de la fonction ou du bloc.
Le s obje ts statiques sont cré é s par une déclaration situé e e n de h ors de toute fonction ou par une déclaration
pré cédée du m ot clé static (dans une fonction ou dans un bloc). Ils sont cré é s avant l'e ntrée dans la fonction
m ain e t détruits aprè s la fin de s on e xé cution.
Le s obje ts te m poraire s
L'appe le xplicite , au s e in d'une e xpre s s ion, du constructe ur d'un obje t provoq ue la cré ation d'un obje t
te m poraire (on n'a pas accè s à son adre s s e ) q ui pourra ê tre autom atiq ue m e nt détruit dè s lors q u'ilne s e ra
plus utile (m ais le langage ne pré cis e pas q uand aura e ffe ctive m e nt lie u ce tte destruction!). Par e xe m ple, si
une clas s e point possè de le constructe ur point (float, float) e t si a e s t de type point, nous pouvons é crire 1 :
Ce tte instruction provoq ue la cré ation d'un obje t te m poraire de type point (ave c appe ldu constructe ur
conce rné ), suivie de l'affe ctation de ce t obje t à a.
Le s obje ts dynam iq ue s
Ils sont cré é s par l'opé rate ur new , auq ue lon doit fournir, le cas é ch é ant, les valeurs des argum e nts destiné s
à un constructe ur2, com m e dans ce t e xe m ple (q ui suppose qu'il e xiste une clas s e point possédant le
constructe ur point (float, float) :
point * adp ;
.....
adp = new point (2.5, 5.32) ;
L'accè s au m e m bres d'un obje t dynam iq ue e s t ré alisé com m e pour les variables ordinaire s . Par e xe m ple, si
point possè de une m é th ode nom m é e affich e , on pourra l'appe ler par (*adp).affich e () ou e ncore par adp-
> affich e ().
Le s obje ts dynam iq ue s n'ont pas de durée de vie définie a priori. Ils sont détruits à la dem ande e n utilisant
ete com m e dans : de lete adr3.
l'opé rate ur del
Initial
is ation d'obje ts
En C+ + , ile s t trè s im portant de distingue r les deux opé rations q ue s ont l'initialisation e t l'affe ctation.
Ily a initialisation d'un obje t dans l'une des trois situations suivante s :
- transm ission d'un obje t par valeur e n argum e nt d'appe ld'une fonction,
- transm ission d'un obje t par valeur e n valeur de re tour d'une fonction.
L'initialisation d'un obje t provoq ue toujours l'appe ld'un constructe ur particulie r, nom m é constructeur par
recopie ;plus précisém e nt :
- si aucun constructe ur de l'une des form e s type (type & ) ou type (const type & ) (type désignant le type
de l'obje t) n'a é té défini, ily aura appe ld'un "constructe ur de re copie par dé faut" ;ce dernie r re copie les
valeurs des diffé re nts m e m bres donnée de l'obje t, com m e le fait l'affe ctation. D e s problèm e s pe uve nt
alors s e pos e r dè s lors q ue l'obje t contie nt des pointe urs sur de s parties dynam iq ue s .
- si un constructe ur de la form e type (type & ) ou, m ie ux, type (const type & ) e xiste , ils e ra appe lé. Note z
q u'alors le constructe ur par re copie par dé faut n'e s t plus appe lé. C'e s t donc au constructe ur par re copie
de prévoir la re copie de tous les m e m bres de l'obje t.
Lors q ue l'on initialise un obje t par un sim ple d'appe ld'un constructe ur com m e dans (e n supposant q ue le
type point dispose d'un constructe ur approprié ) :
point a = point (1.3, 4.5)
iln'y a pas d'appe ld'un constructe ur par re copie , pré cédée de la cré ation d'un obje t te m poraire . La
déclaration pré cédente e s t rigoure us e m e nt é q uivalente à :
point a (1.3, 4.5) ;
2 Com m e pour l
e s variables ordinaires, on peut préciser un nom bre d'objets m ais des restrictions apparais s e nt alors q uant au
cons tructeur qu'ile s t possible d'appeler (voyez ci-dessous la rubriq ue "tableaux d'objets ").
3 D ans l
e cas d'un tableau d'objets, on devra préciser, en plus, son nom bre d'élém ents.
V. Construction, de s truction e t initialisation de s obje ts 61
Le s tabl
e aux d'obje ts
Si point e s t un type obje t possédant un constructe ur sans argum e nt (ou, situation gé né ralem e nt décons e illée ,
sans constructe ur), la déclaration :
cré e un tableau courbe de 20 obje ts de type point e n appe lant, le cas é ch é ant, le constructe ur pour ch acun
d'entre e ux. Note z toute fois q ue courbe n'e s t pas lui-m ê m e un obje t.
De m ê m e :
alloue l'e m place m e nt m é m oire né ce s s aire à vingt obje ts (cons é cutifs) de type point, e n appe lant, le cas
é ch é ant, le constructe ur pour ch acun d'e ntre e ux, puis place l'adresse du pre m ie r dans adcourbe .
La destruction d'un tableau d'obje ts s e fait e n utilisant l'opé rate ur new (e n pré cisant, dans les ve rsions
infé rie ure s à la 3.0, le nom bre d'élém e nts du tableau). Par e xe m ple, pour dé truire le tableau pré cédent, on
é crira :
ou :
R e m arque :
En th é orie , depuis la ve rsion 2.0 de C+ + , on pe ut com pléte r la déclaration d'un tableau d'obje ts par un
initialiseur conte nant une liste de valeurs (e lles pe uve nt é ve ntue llem e nt ê tre de types diffé re nts). Ch aq ue
valeur e s t alors transm ise à un constructe ur approprié . Ce tte facilité ne pe ut toute fois pas s'appliq ue r aux
tableaux dynam iq ue s (ile n va de m ê m e pour les tableaux ordinaire s !).
Une clas s e pe ut posséder un m e m bre donnée qui est lui-m ê m e de type clas s e . En voici un e xe m ple ;si nous
avons défini :
class point
{ float x, y ;
public :
point (float, float) ;
.....
} ;
nous pouvons définir une clas s e pointcol, dont un m e m bre e s t de type point :
class pointcol
{ point p ;
int couleur ;
public :
pointcol (float, float, int) ;
.....
62 Exe rcice s e n langage C+ +
} ;
D ans ce cas, lors de la cré ation d'un obje t de type pointcol, ily aura tout d'abord appe ld'un constructe ur de
pointcol, puis appe ld'un constructe ur de point ;ce dernie r re ce vra les argum e nts q u'on aura m e ntionné
dans l'e n-tê te de la définition du constructe ur de pointcol ;par e xe m ple, ave c :
on pré cis e q ue le constructe ur du m e m bre p re ce vra e n argum e nt les valeurs abs e t ord.
Si l'e n-tê te de pointcolne m e ntionnait rie n conce rnant p, ilfaudrait alors q ue le type point possè de un
constructe ur sans argum e nt pour q ue ce la soit corre ct.
Initial
is ation d'obje ts conte nant de s obje ts m e m bre
D e puis la ve rsion 2.0 de C+ + , le constructe ur par re copie par dé faut procè de "m e m bre par m e m bre " ;ce la
signifie q ue s i l'un de s m e m bre s e s t lui-m ê m e un obje t, ile s t re copié e n appe lant son propre constructe ur par
re copie (q ui pourra ê tre s oit un constructe ur par dé faut, soit un constructe ur dé fini dans la clas s e
corre s pondante ). Dans les ve rsions anté rie ure s , la re copie s e faisait de façon globale ;autre m e nt dit, la
"valeur" d'un obje t é tait re porté e ("bit par bit") dans une autre , sans te nir com pte de sa structure .
Les diffé re nce s e ntre ces deux m odes de re copie s e ront particuliè re m e nt s e nsibles dans la situation suivante :
- obje t m e m bre com portant des pointe urs sur de s e m place m e nts dynam iq ue s e t ayant défini son
constructe ur par re copie ;
- obje t principaln'ayant pas défini de constructe ur par re copie .
D e puis la ve rsion 2.0, la construction par re copie d'un te lobje t fonctionne ra conve nablem e nt (s'ilne
contie nt pas lui-m ê m e de pointe ur), puis q u'ily aura bie n appe ldu constructe ur par re copie de l'obje t
m e m bre . Dans les ve rsions anté rie ure s , e n re vanch e , ce constructe ur par re copie , bie n q u'e xistant, ne s e ra
pas utilisé et les ch os e s s e ront beaucoup m oins satisfaisante s .
___________________________________________________________________________
Enoncé
main()
{
chose x ;
cout << "bonjour\n" ;
}
main()
{
chose * adc = new chose
}
___________________________________________________________________________
Sol
ution
D ans ce cas, le deuxiè m e program m e fournira sim plem e nt à l'e xé cution (puis q u'ilcré e un obje t de type
ch os e sans jam ais le détruire ) :
___________________________________________________________________________
Enoncé
Quels s e ront les ré s ultats fournis par l'e xé cution du program m e s uivant (ici, la déclaration de la clas s e de m o,
sa définition e t le program m e d'utilisation ont é té re groupé s e n un s e ulfich ie r) :
#include <iostream.h>
class demo
{ int x, y ;
public :
demo (int abs=1, int ord=0) // constructeur I (0, 1 ou 2 arguments)
{ x = abs ; y = ord ;
cout << "constructeur I : " << x << " " << y << "\n" ;
}
demo (demo&) ; // constructeur II (par recopie)
~demo () ; // destructeur
} ;
main ()
{
void fct (demo, demo *) ; // proto fonction indépendante fct
cout << "début main\n" ;
demo a ;
demo b = 2 ;
demo c = a ;
demo * adr = new demo (3,3) ;
fct (a, adr) ;
demo d = demo (4,4) ;
c = demo (5,5) ;
cout << "fin main\n" ;
}
Sol
ution
Voici les ré s ultats (com m e nté s ) q ue fournit le program m e (ici, nous avons utilisé le com pilate ur Turbo
C+ + 4) :
début main
constructeur I : 1 0 dem o a ;
constructeur I : 2 0 dem o b = 2 ;
constructeur II (recopie) : 1 0 dem o c = a ;
constructeur I : 3 3 new dem o (3,3)
constructeur II (recopie) : 1 0 recopie de la val
eur de a dans fct (a, ..)
(crée un objet tem poraire)
entrée fct
destruction : 3 3 del
ete add ; (dans fct)
sortie fct
constructeur I : 4 4 dem o d = dem o (4, 4) ;
constructeur I : 5 5 c = dem o (5,5) (cons truction objet tem poraire)
fin main
destruction : 5 5 destruction objet tem poraire (peut s e
faire à un autre m om ent
destruction : 4 4 destruction d
destruction : 1 0 destruction objet tem poraire créé pour l 'appel
de fct (peut apparaî tre ail
leurs)
destruction : 5 5 destruction c
destruction : 2 0 destruction b
destruction : 1 0 destruction a
Note z bie n q ue l'affe ctation c = de m o (5,5) e ntraî ne la cré ation d'un obje t te m poraire par appe l du
constructe ur de de m o (argum e nts 5 et 5) ;ce t obje t e s t e nsuite affe cté à a. O n constate d'ailleurs q ue ce t
obje t e s t e ffe ctive m e nt détruit (ici, aprè s l'e xé cution du m ain, m ais, dans d'autre s im plém e ntations, ce la
pe ut s e produire plus tôt).
Par ailleurs, l'appe lde fct a e ntraî né la construction d'un obje t te m poraire , par appe ldu constructe ur par
re copie . Nous aurions pu pe ns e r q ue ce t obje t s e rait libéré à la sortie de la fonction. Ici, ce n'e s t pas le cas
m ais ce la pourrait l'ê tre dans d'autre s im plém e ntations.
D is cus s ion
Ce t e xe m ple devrait vous m e ttre e n garde contre le fait q ue de nom bre ux obje ts te m poraire s pe uve nt pre ndre
naissance dans un program m e , sans m ê m e q u'on s'e n re nde com pte ;des problèm es d'encom bre m e nt de la
m é m oire pe uve nt apparaî tre dans les im plém e ntations q ui ne libè re nt pas les e m place m e nt te m poraire s , dè s
q ue ce la e s t deve nu possible.
___________________________________________________________________________
Enoncé
Cré e r une clas s e point ne conte nant q u'un constructe ur sans argum e nts, un de s tructe ur e t un m e m bre donnée
privé re pré s e ntant un num é ro de point (le pre m ie r cré é porte ra le num é ro 1, le s uivant le num é ro 2, e t ainsi
de suite ...). Le constructe ur affich e ra le num é ro du point cré é e t le destructe ur affich e ra le num é ro du point
détruit. Ecrire un pe tit program m e d'utilisation cré ant dynam iq ue m e nt un tableau de 4 points e t le
détruisant.
___________________________________________________________________________
Sol
ution
Pour pouvoir num é rote r nos points, ilnous faut pouvoir com pte r le nom bre de fois où le constructe ur a é té
appe lé, ce q ui nous perm e ttra bie n d'attribue r un num é ro diffé re nt à ch aq ue point. Pour ce faire , nous
définissons, au s e in de la clas s e point, un m e m bre donnée statiq ue nb_points. Ici, ils e ra incré m e nté par le
constructe ur m ais le destructe ur n'aura pas d'action sur lui.
Voici la déclaration (dé finition) de notre clas s e , accom pagnée du program m e d'utilisation de m andé :
#include <iostream.h>
class point
{ int num;
static nb_points ;
public :
point ()
{ num = ++nb_points ;
cout << "création point numéro : " << num << "\n" ;
}
~point ()
{ cout << "Destruction point numéro : " << num << "\n" ;
}
} ;
main()
{ point * adcourb = new point [4] ;
66 Exe rcice s e n langage C+ +
int i ;
delete [4] adcourb ;
}
___________________________________________________________________________
Enoncé
1) R é aliser une clas s e nom m é e s e t_int pe rm e ttant de m anipuler de s e ns e m bles de nom bre s e ntie rs. O n de vra
pouvoir ré aliser sur un te le ns e m ble les opé rations classiques suivante s : lui ajoute r un nouve lé lém e nt,
connaî tre s on "cardinal" (nom bre d'élém e nts), savoir si un entie r donné lui appartie nt.
Ici, on cons e rve ra les diffé re nts é lém e nts de l'e ns e m ble dans un tableau alloué dynam iq ue m e nt par le
constructe ur. Un argum e nt (auq ue lon pourra pré voir une valeur par dé faut) lui pré cis e ra le nom bre m axim al
d'élém e nts de l'e ns e m ble.
2) Ecrire , e n outre , un program m e (m ain) utilisant la clas s e s e t_int pour dé te rm ine r le nom bre d'entie rs
diffé re nts conte nus dans un tableau d'e ntie rs lus e n donné e .
3) Que faudrait-il faire pour q u'un obje t du type s e t_int puis s e ê tre transm is par valeur, soit com m e
argum e nt d'appe l, soit com m e valeur de re tour d'une fonction.
___________________________________________________________________________
Sol
ution
/* fichier SETINT1.H */
/* déclaration de la classe set_int */
class set_int
{
int * adval ; // adresse du tableau des valeurs
int nmax ; // nombre maxi d'éléments
int nelem ; // nombre courant d'éléments
public :
set_int (int = 20) ; // constructeur
~set_int () ; // destructeur
void ajoute (int) ; // ajout d'un élément
int appartient (int) ; // appartenance d'un élément
int cardinal () ; // cardinal de l'ensemble
} ;
Le m e m bre donnée advale s t destiné à pointe r sur le tableau d'e ntie rs q ui s e ra alloué par le constructe ur. Le
m e m bre nm ax re pré s e nte ra la taille de ce tableau, tandis q ue ne le m fournira le nom bre e ffe ctif d'entie rs
stock és dans ce tableau. Ce s e ntie rs s e ront, ce tte fois, rangés dans l'ordre où ils s e ront fournis à ajoute , e t
non plus à un e m place m e nt prédéte rm iné , com m e nous l'avions fait pour les caractè re s (dans les e xe rcice s
du ch apitre pré cédent).
Com m e la cré ation d'un obje t e ntraî ne ici une allocation dynam iq ue d'un em place m e nt m é m oire , ile s t
raisonnable de pré voir la libération de ce t e m place m e nt lors de la destruction de l'obje t ;ce tte opé ration doit
donc ê tre pris e e n ch arge par le destructe ur, d'où la pré s e nce de ce tte fonction m e m bre .
#include "setint1.h"
set_int::set_int (int dim)
{ adval = new int [nmax = dim] ; // allocation tableau de valeurs
nelem = 0 ;
}
set_int::~set_int (
{ delete adval ; // libération tableau de valeurs
}
Note z q ue , dans la fonction m e m bre ajoute , nous avons utilisé la fonction m e m bre appartie nt pour vé rifie r
q ue le nom bre à ajoute r ne figurait pas déjà dans notre e ns e m ble.
Par ailleurs, l'é noncé ne pré voit rie n pour le cas où l'on ch e rch e à ajoute r un é lém e nt à un e ns e m ble déjà
"plein" ;ici, nous nous som m e s conte nté de ne rie n faire dans ce cas. Dans la pratiq ue , ilfaudrait, soit
pré voir un m oye n pour l'utilisate ur d'ê tre pré ve nu de ce tte s ituation, soit, m ie ux, pré voir autom atiq ue m e nt
l'agrandis s e m e nt de la zone dynam iq ue associé e à l'e ns e m ble.
#include <iostream.h>
#include "setint1.h"
main()
{ set_int ens ;
cout << "donnez 20 entiers \n" ;
int i, n ;
for (i=0 ; i<20 ; i++)
{ cin >> n ;
ens.ajoute (n) ;
}
cout << "il y a : " << ens.cardinal () << " entiers différents\n" ;
}
3) Te lq u'e s t actue llem e nt pré vue notre clas s e s e t_int, si un obje t de ce type e s t transm is par valeur, soit e n
argum e nt d'appe l, soit e n re tour d'une fonction, ily a appe ldu constructe ur de re copie par dé faut ;or, ce
dernie r s e conte nte d'effe ctue r une copie des m e m bres donnée de l'obje t conce rné . Ce q ui signifie q u'on s e
re trouve alors e n pré s e nce de deux obje ts conte nant deux pointe urs diffé re nts sur un m ê m e tableau d'e ntie rs.
Un problèm e va donc s e pos e r, dè s lors q ue l'obje t copié s e ra dé truit (ce q ui pe ut s e produire , dè s la sortie
68 Exe rcice s e n langage C+ +
de la fonction) ;e n e ffe t, dans ce cas, le tableau dynam iq ue d'entie rs s e ra dé truit, alors m ê m e q ue l'obje t
d'origine continue ra à "pointe r" de s s us.
Indé pe ndam m e nt de ce la, d'autre s problèm e s s im ilaire s pourrait s e pos e r si la fonction é tait am e né e à
m odifie r le conte nu de l'e ns e m ble : dans ce cas, e n e ffe t, on m odifie rait le tableau d'e ntie rs original, ch os e à
laq ue lle on ne s 'atte nd pas dans le cas de transm ission par valeur.
Pour ré gler ce s problèm e s , ile s t né ce s s aire de m unir notre classe d'un constructe ur par re copie approprié .
Que doit-ilfaire ? Plusieurs solutions e xiste nt ;la plus é légante e s t ce rtaine m e nt ce q ue l'on nom m e la
ge s tion d'un "com pte ur de ré fé re nce ". Nous la re ncontre rons plus tard, associé e , gé né ralem e nt à la
surdéfinition de l'opé rate ur d'affe ctation (q ui pose des problèm e s analogue s à ce ux q ue nous ve nons
d'évoq ue r).
Pour l'instant, nous utiliserons une dém arch e plus sim ple q ui consiste à re copie r totalem e nt l'obje t conce rné ,
c'e s t-à -dire e n te nant com pte de sa "partie dynam iq ue " (on parle parfois de "copie profonde "). Pour ce faire ,
on alloue un s e cond e m place m e nt pour un tableau d'e ntie rs, dans leq ue lon re copie les valeurs du prem ie r
e ns e m ble. Nature llem e nt, ilne faut pas oublie r alors de procéder égalem e nt à la re copie des m e m bre s
donné e , puis q ue ce lle-ci n'e s t plus assuré e par le constructe ur de re copie par dé faut (leq ue ln'e s t plus appe lé,
dè s lors q u'un constructe ur par re copie a é té défini).
D is cus s ion
La m é th ode propos é e a le m é rite d'ê tre facile à program m e r e t e lle ne né ce s s ite pas d'autre s m odifications
q ue l'ajout d'une m é th ode s upplém e ntaire . Elle a l'inconvé nie nt de dupliq ue r totalem e nt l'obje t, ce q ui
e ntraîne une pe rte de m é m oire . La m é th ode du com pte ur de ré fé re nce (q ue nous re ncontre rons plus tard)
é vite ce tte duplication ;e n contre partie , sa m ise en oeuvre e s t beaucoup plus délicate e t e lle ne se borne
plus au sim ple ajout d'une m é th ode .
Quelle q ue s oit la m é th ode e m ployé e , on constate q ue la surdéfinition du constructe ur par re copie e s t q uasi
indispensable pour toute clas s e com portant un pointe ur sur une partie dynam iq ue . Dans l'é tat actue lde
C+ + , iln'e s t pas possible d'inte rdire la transm ission par valeur d'un obje t q ui n'e n posséderait pas! Et ilne
s e m ble pas raisonnable de livre r à un "clie nt" une te lobje t, e n lui de m andant de ne jam ais le transm e ttre par
valeur! Ce q ui signifie q ue la plupart des clas s e s "inté re s s ante s " devront définir un te lconstructe ur par
re copie .
Nous ve rrons q ue les m ê m e s ré flexions s'appliq ue ront à l'affe ctation e ntre obje ts e t q u'e lles conduiront à la
conclusion q ue la plupart des clas s e s "inté re s s ante s " devront redéfinir l'opé rate ur d'affe ctation.
___________________________________________________________________________
V. Construction, de s truction e t initialisation de s obje ts 69
Enoncé
M odifie r l'im plém e ntation de la clas s e pré cédente (ave c son constructe ur par re copie ) de façon à ce q ue
l'e ns e m ble d'entie rs soit m ainte nant re pré s e nté par une l iste ch aînée (ch aq ue e ntie r e s t rangé dans une
structure com portant un ch am p de s tiné à conte nir un nom bre e t un ch am p de s tiné à conte nir un pointe ur sur
la structure s uivante ). L'inte rface de la clas s e (la partie publiq ue de sa déclaration) devra re s te r inch angé e ,
ce q ui signifie q u'un clie nt de la clas s e continue ra à l'e m ploye r de la m ê m e façon.
___________________________________________________________________________
Sol
ution
Com m e nous le s uggè re l'é noncé , nous allons donc dé finir une s tructure q ue nous nom m e rons é lé m e nt :
struct noeud
{ int valeur ; // valeur d'un élément de l'ensemble
noeud * suivant ; // pointeur sur le noeud suivant de la liste
} ;
Note z q ue nous pouvons, e n C+ + , é crire s im plem e nt noe ud *suivant, là où C nous aurait im pos é struct
noe ud *suivant.
Notre s tructure noe ud pe ut ê tre définie indiffé re m m e nt dans la déclaration de la clas s e s e t_int ou e n de h ors.
En ce q ui conce rne les m e m bres donnée privés de la clas s e , nous ne cons e rve rons q ue ne le m q ui, bie n q ue
non indispe nsable, nous é vite ra de parcourir toute la liste pour dé te rm ine r le cardinalde notre e ns e m ble.
En re vanch e , nous y introduirons un pointe ur nom m é de but, destiné à conte nir l'adresse du pre m ie r é lém e nt
de la liste , s'ile xiste (au dé part, ils e ra initialisé à NULL).
En ce q ui conce rne le constructe ur de s e t_int, nous lui cons e rve rons son argum e nt (de type int), bie n q u'ici il
n'ait plus aucun inté rê t, e t ce la dans le but de ne pas m odifie r l'inte rface de notre clas s e (com m e le
dem andait l'é noncé ).
/* fichier SETINT3.H */
/* déclaration de la classe set_int */
struct noeud
{ int valeur ; // valeur d'un élément de l'ensemble
noeud * suivant ; // pointeur sur le noeud suivant de la liste
} ;
class set_int
{
noeud * debut ; // pointeur sur le début de la liste
int nelem ; // nombre courant d'éléments
public :
set_int (int = 20) ; // constructeur (argument inutile ici)
set_int (set_int &) ; // constructeur par recopie
~set_int () ; // destructeur
void ajoute (int) ; // ajout d'un élément
int appartient (int) ; // appartenance d'un élément
int cardinal () ; // cardinal de l'ensemble
} ;
70 Exe rcice s e n langage C+ +
La définition du nouve au constructe ur ne pré s e nte pas de difficulté . La fonction m e m bre ajoute ré alise une
classique insertion d'un noe ud e n début de liste e t incré m e nte le nom bre d'élém e nts de l'e ns e m ble. La
fonction appartie nt e ffe ctue une e xploration de liste , tant q u'e lle n'a pas trouvé la valeur conce rné e ou
atte int la fin de la liste . En re vanch e , le nouve au constructe ur par re copie doit re copie r la liste ch aî
né e . Il
ré alise à la fois une exploration de la liste d'origine e t une ins e rtion dans la liste copié e . Quant au
destructe ur, ildoit m ainte nant libérer systé m atiq ue m e nt tous les e m place m e nts des diffé re nts noeuds cré é s
pour la liste .
set_int::~set_int ()
{ noeud * adn ;
noeud * courant = debut ;
while (courant)
{ adn = courant ; // libération de tous
courant = courant->suivant ; // les noeuds
delete adn ; // de la liste
}
}
int set_int::cardinal ()
{ return nelem ;
}
Note z q ue le program m e d'utilisation proposé dans l'e xe rcice V.4 re s te valable ici, puis q ue nous n'avons
pré cis é m e nt pas m odifié l'inte rface de notre clas s e .
Par ailleurs, le problèm e é voq ué à propos de l'ajout d'un é lém e nt à un e ns e m ble "plein" ne s e pos e plus ici,
com pte te nu de la nouve lle im plém e ntation de notre clas s e .
___________________________________________________________________________
Enoncé
M odifie r la clas s e s e t_int pré cédente (im plém e nté e s ous form e d'une liste ch aî né e - ave c ou sans son
constructe ur par re copie ) pour q u'e lle dispose de ce q ue l'on nom m e un "ité rate ur" sur les diffé re nts
é lém e nts de l'e ns e m ble. R appe lons q u'on nom m e ainsi un m é canism e pe rm e ttant d'accéder s é q ue ntie llem e nt
aux diffé re nts é lém e nts de l'e ns e m ble. O n pré voira trois nouve lles fonctions m e m bre : init, pour intialiser le
proce s s us d'ité ration ;proch ain , pour fournir l'é lém e nt suivant lors q u'ile xiste e t e xiste , pour te s te r s'il
e xiste e ncore un é lém e nt non e xploré .
O n com pléte ra alors le program m e d'utilisation pré cédent (e n fait, ce lui de l'e xe rcice V.4), de m aniè re à ce
q u'ilaffich e les diffé re nts e ntie rs conte nus dans les valeurs fournie s e n donné e .
N.B. Ce t e xe rcice s e ra plus profitable s 'il e s t traité aprè s l'e xe rcice du ch apitre III q ui proposait
l'introduction d'un te l ité rate ur dans une clas s e re pré s e ntant des ens e m bles de caractè re s (m ais dont
l'im plém e ntation é tait diffé re nte de l'actue lle clas s e ).
___________________________________________________________________________
Sol
ution
Ici, la ge s tion du m é canism e d'ité ration né ce s s ite l'e m ploi d'un pointe ur (q ue nous nom m e rons courant) sur
un noe ud de notre liste . Nous convie ndrons q u'ilpointe s ur le pre m ie r é lém e nt non e ncore traité dans
l'ité ration, c'e s t-à -dire dont la valeur corre s pondante n'a pas e ncore é té re nvoyé e par la fonction proch ain. Il
n'e s t pas utile, ici, de pré voir un m e m bre donnée pour indiq ue r si la fin de liste a é té atte inte ;e n e ffe t, ave c
la conve ntion adopté e , ilnous suffit de te s te r la valeur de courant (q ui s e ra é gale à NULL, lors q ue l'on s e ra
e n fin de liste ).
Le rôle de la fonction init s e lim ite à l'initialisation de courant à la valeur du pointe ur sur le début de la liste
(de but).
La fonction suivant fournira e n re tour la valeur e ntiè re associé e au noe ud pointé par courant lors q u'ile xiste
(courant diffé re nt de NULL) ou la valeur 0 dans le cas contraire (ils'agit, là e ncore , d'une conve ntion
destiné e à proté ge r l'utilisate ur ayant appe lé ce tte fonction, alors q ue la fin de liste é tait déjà atte inte e t,
donc, q u'aucun é lém e nt de l'e ns e m ble n'é tait disponible). De plus, dans le pre m ie r cas (usuel), la fonction
suivant actualisera la valeur de courant, de m aniè re à ce q u'ilpointe s ur le noe ud suivant.
72 Exe rcice s e n langage C+ +
Enfin, la fonction e xiste e xam ine ra sim plem e nt la valeur de de but pour savoir s'ile xiste e ncore un é lém e nt à
traite r.
/* fichier SETINT4.H */
/* déclaration de la classe set_int */
struct noeud
{ int valeur ; // valeur d'un élément de l'ensemble
noeud * suivant ; // pointeur sur le noeud suivant de la liste
} ;
class set_int
{
noeud * debut ; // pointeur sur le début de la liste
int nelem ; // nombre courant d'éléments
noeud * courant ; // pointeur sur noeud courant
public :
set_int (int = 20) ; // constructeur
set_int (set_int &) ; // constructeur par recopie
~set_int () ; // destructeur
void ajoute (int) ; // ajout d'un élément
int appartient (int) ; // appartenance d'un élément
int cardinal () ; // cardinal de l'ensemble
void init () ; // initialisation itération
int prochain () ; // entier suivant
int existe () ; // test fin liste
} ;
Voici la définition de s trois nouve lles fonctions m e m bre init, suivant e t e xiste :
void set_int::init ()
{ courant = debut ;
}
int set_int::prochain ()
{ if (courant)
{ int val = courant->valeur ;
courant = courant->suivant ;
return val ;
}
else return 0 ; // par convention
}
int set_int::existe ()
{ return (courant != NULL) ;
}
RAPPELS
En C+ + , l'unité de prote ction e s t la clas s e , e t non pas l'obje t. Ce la signifie q u'une fonction m e m bre d'une
clas s e pe ut accéder à tous les m e m bre s privés de n'im porte q ue lobje t de sa clas s e . En re vanch e , ce s
m e m bre s privé s re s te nt inacce s s ibles à n'im porte q ue lle fonction m e m bre d'une autre clas s e ou à n'im porte
q ue lle fonction indé pe ndante .
La notion de fonction am ie , ou plus e xacte m e nt de "déclaration d'am itié ", pe rm e t de déclare r dans une
clas s e , les fonctions q ue l'on autoris e à accéder à s e s m e m bre s privé s (donné e s ou fonctions). Ile xiste
plusieurs situations d'am itié .
class A
{
......
friend --- fct (-----) ;
.....
}
La fonction fct ayant le prototype s pé cifié e s t autoris é e à accéder aux m e m bre s privés de la clas s e A .
Gé né ralem e nt, la fonction m e m bre fct possè dera un argum e nt ou une valeur de re tour de type A (ce q ui
justifie ra sa déclaration d'am itié ). Pour traduire s a définition, le com pilate ur de vra posséder les
caracté ristiq ues de A, donc dispose r de s a déclaration.
class A
{
.....
friend --- B:fct (-----) ;
.....
} ;
82 Exe rcice s e n langage C+ +
La fonction m e m bre fct de la clas s e B (ayant le prototype s pé cifié ) e s t autoris é e à accéder aux m e m bre s
privés de la clas s e A .
Pour q u'il puis s e com piler conve nablem e nt la déclaration ci-de s s us, le com pilate ur de vra sim plem e nt
"savoir" q ue B e s t une clas s e . Si la définition de la clas s e B n'a pas e ncore é té com pilée , on lui pré cis e ra
sim plem e nt ce tte inform ation par la déclaration :
class A ;
Gé né ralem e nt la fonction m e m bre fct possè dera un argum e nt ou une valeur de re tour de type A (ce q ui
justifie ra sa déclaration d'am itié ). Pour com piler sa déclaration (au s e in de la déclaration de A ), le
com pilate ur de vra, là e ncore , savoir sim plem e nt q ue A e s t une clas s e . En re vanch e , pour com piler la
définition de fct, le com pilate ur de vra posséder les caracté ristiq ues de A, donc dispose r de s a déclaration.
c) Toute s l
e s fonctions d'une cl
as s e B s ont am ie s d'une autre cl
as s e A
D ans ce cas, plutôt q ue d'utiliser autant de déclarations d'am itié q ue de fonctions m e m bre , on utilise (dans
la déclaration de la clas s e A ) la déclaration (globale) suivante :
friend class B ;
Pour com piler la déclaration de A , on pré cis e ra sim plem e nt q ue A e s t une clas s e par class A ;q uant à la
déclaration de la clas s e B, e lle né ce s s ite ra gé né ralem e nt (dè s q u'une de ses fonctions m e m bre possè dera un
argum e nt ou une valeur de re tour de type A ) la déclaration de la clas s e A .
___________________________________________________________________________
Enoncé
class point
{ int x, y ;
public :
point (int abs=0, int ord=0)
{ x = abs ; y = ord ;
}
} ;
Ecrire une fonction indé pe ndante affich e , am ie de la clas s e point, pe rm e ttant d'affich e r les coordonnées d'un
point. O n fournira s é paré m e nt un fich ie r source conte nant la nouve lle déclaration (dé finition) de point e t un
fich ie r source conte nant la définition de la fonction affich e . Ecrire un pe tit program m e (m ain) q ui cré e un
point de clas s e autom atiq ue e t un point de classe dynam iq ue e t q ui e n affich e les coordonné e s .
___________________________________________________________________________
VI. Le s fonctions am ie s 83
Sol
ution
Nous devons donc ré aliser une fonction indé pe ndante , nom m é e affich e , am ie de la clas s e point. Une te lle
fonction, contraire m e nt à une fonction m e m bre , ne re çoit plus d'argum e nt im plicite ;affich e devra donc
re ce voir un argum e nt de type point. Son prototype s e ra donc de la form e :
si l'on souh aite transm e ttre un point par ré fé re nce . Ici, nous ch oisirons cette derniè re possibilité e t, com m e
affich e n'a aucune raison de m odifie r les valeurs du point re çu, nous le proté ge rons à l'aide du qualificatif
const :
M anife s te m e nt, affich e devra pouvoir accéder aux m e m bre s privé s x e t y de la clas s e point. Ilfaut donc
pré voir une déclaration d'am itié au s e in de ce tte clas s e , dont voici la nouve lle déclaration :
/* fichier POINT1.H */
/* déclaration de la classe point */
class point
{ int x, y ;
public :
friend void affiche (const point &) ;
point (int abs=0, int ord=0)
{ x=abs ; y=ord ;
}
} ;
Pour é crire affich e , ilnous suffit d'accéder aux m e m bre s (privé s ) x e t y de son argum e nt de type point. Si ce
dernie r s e nom m e p, les m e m bre s e n q ue s tion s e note nt (classiquem e nt) p.x e t p.y. Voici la définition de
affich e :
#include <iostream.h>
#include "point1.h" // nécessaire pour compiler affiche
void affiche (const point & p)
{ cout << "Coordonnées : " << p.x << " " << p.y << "\n" ;
}
Note z bie n q ue la com pilation de affich e né ce s s ite la déclaration de la clas s e point, e t pas s e ulem e nt une
déclaration te lle q ue class point, car le com pilate ur doit connaî tre les caracté ristiq ues de la clas s e point
(notam m e nt, ici, la localisation de s m e m bre s x e t y).
#include "point1.h"
main()
{ point a(1,5) ;
affiche (a) ;
point * adp ;
adp = new point (2, 12) ;
affiche (*adp) ; // attention *adp et non adp
84 Exe rcice s e n langage C+ +
Note z q ue nous n'avons pas eu à fournir le prototype de la fonction indé pe ndante affich e , car ilfigure dans
la déclaration de la clas s e point. Le faire constitue rait d'ailleurs une erreur.
___________________________________________________________________________
Enoncé
Soit la clas s e ve cte ur3d définie dans l'e xe rcice IV.2 par :
class vecteur3d
{ float x, y, z ;
public :
vecteur3d (float c1=0.0, float c2=0.0, float c3=0.0)
{ x = c1 ; y = c2 ; z = c3 ;
}
.....
} ;
Ecrire une fonction indé pe ndante coincide , am ie de la clas s e ve cte ur3d, pe rm e ttant de savoir si deux
ve cte urs ont m ê m e s com posante s (ce tte fonction re m place ra la fonction m e m bre coincide q u'on de m andait
d'écrire dans l'e xe rcice IV.2).
Si v1 e t v2 désignent deux ve cte urs de type ve cte ur3d, com m e nt s'é crit m ainte nant le te s t de coïncide nce de
ces deux ve cte urs?
___________________________________________________________________________
Sol
ution
La fonction coincide va donc dispose r de deux argum e nts de type ve cte ur3d. Si l'on pré voit de les
transm e ttre par ré fé re nce , e n inte rdisant leur é ve ntue lle m odification dans la fonction, le prototype de
coincide s e ra :
/* fichier vect3D.H */
/* déclaration de la classe vecteur3d */
class vecteur3d
{ float x, y, z ;
public :
friend int coincide (const vecteur3d &, const vecteur3d &) ;
vecteur3d (float c1=0, float c2=0, float c3=0)
{ x = c1 ; y = c2 ; z = c3 ;
}
} ;
int coincide (const vecteur3d & v1, const vecteur3d & v2)
{ if ( (v1.x == v2.x) && (v1.y == v2.y) && (v1.z == v2.z) )
return 1 ;
else return 0 ;
}
O n note ra q ue , tant dans la définition de coincide q ue dans ce te s t, on voit s e m anife s te r la sym é trie du
problèm e , ce q ui n'é tait pas le cas lors q ue nous avions fait de coincide une fonction m e m bre de la clas s e
ve cte ur3d.
___________________________________________________________________________
Enoncé
- l'une , nom m é e ve ct, pe rm e ttant de re pré s e nte r de s ve cte urs à 3 com posantes de type double ;e lle
com porte ra un constructe ur e t une fonction m e m bre d'affich age ,
- l'autre nom m é e m atrice , pe rm e ttant de re pré s e nte r de s m atrice s carrées de dim e nsion 3x3 ;e lle
com porte ra un constructe ur ave c un argum e nt (adresse d'un tableau de 3x3 valeurs) q ui initialisera la
m atrice ave c les valeurs corre s pondante s .
R é aliser une fonction indé pe ndante prod pe rm e ttant de fournir le ve cte ur corre s pondant au produit d'une
m atrice par un ve cte ur. Ecrire un pe tit program m e de te s t. O n fournira s é paré m e nt les deux dé clarations de
ch acune des clas s e s , leurs deux définitions, la définition de prod e t le program m e de te s t.
___________________________________________________________________________
Sol
ution
Ici, pour nous facilite r l'é criture , nous re pré s e nte rons les com posantes d'un ve cte ur par un tableau à trois
é lém e nts e t les valeurs d'une m atrice par un tableau à 2 indice s . La fonction de calculdu produit d'un
ve cte ur par une m atrice doit obligatoire m e nt pouvoir accéder aux données des deux clas s e s , ce q ui signifie
q u'e lle devra ê tre déclaré e "am ie " dans ch acune de ces deux clas s e s .
En ce q ui conce rne ses deux argum e nts (de type ve ct e t m at), nous avons ch oisi la transm ission la plus
e fficace , c'e s t-à -dire la transm ission par ré fé re nce . Quant au ré s ultat (de type ve ct), ildoit obligatoire m e nt
ê tre re nvoyé par valeur (nous e n re parlerons dans la définition de prod).
/* fichier vect1.h */
class matrice ; // pour pouvoir compiler la déclaration de vect
86 Exe rcice s e n langage C+ +
class vect
{ double v[3] ; // vecteur à 3 composantes
public :
vect (double v1=0, double v2=0, double v3=0) // constructeur
{ v[0] = v1 ; v[1]=v2 ; v[2]=v3 ; }
friend vect prod (const matrice &, const vect &) ;// fonction amie indépendante
void affiche () ;
} ;
e t la déclaration de la clas s e m at :
/* fichier mat1.h */
class vect ; // pour pouvoir compiler la déclaration de matrice
class matrice
{ double mat[3] [3] ; // matrice 3 X 3
public :
matrice () ; // constructeur avec initialisation à zéro
matrice (double t [3] [3] ) ; // constructeur à partir d'un tableau 3 x 3
friend vect prod (const matrice &, const vect &) ; // fonction amie indépendante
} ;
Note z q ue nous avons déclaré constants les argum e nts de la fonction m atrice . Ils'agit sim plem e nt d'une
pré caution de s tiné e à pré ve nir le ris q ue d'une faute de program m ation conduisant, au s e in de la définition de
m atrice , à une m odification desdits argum e nts.
La définition de s fonctions m e m bre (e n fait affich e ) de la clas s e ve ct ne pré s e nte pas de difficulté s :
#include <iostream.h>
#include "vect1.h"
void vect::affiche ()
{ int i ;
for (i=0 ; i<3 ; i++) cout << v[i] << " " ;
cout << "\n" ;
}
#include <iostream.h>
#include "mat1.h"
matrice::matrice (double t [3] [3])
{ int i ; int j ;
for (i=0 ; i<3 ; i++)
for (j=0 ; j<3 ; j++)
mat[i] [j] = t[i] [j] ;
}
#include "vect1.h"
#include "mat1.h"
vect prod (const matrice & m, const vect & x)
{ int i, j ;
double som ;
vect res ; // pour le résultat du produit
for (i=0 ; i<3 ; i++)
{ for (j=0, som=0 ; j<3 ; j++)
VI. Le s fonctions am ie s 87
Note z q ue ce tte fonction cré e un obje t autom atiq ue re s de clas s e ve ct. Ilne pe ut donc ê tre re nvoyé q ue par
valeur. D ans le cas contraire , la fonction appe lante ré cupé re rait l'adresse d'un e m place m e nt libéré au
m om e nt de la sortie de la fonction.
#include "vect1.h"
#include "mat1.h"
main()
{
vect w (1,2,3) ;
vect res ;
double tb [3][3] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 } ;
matrice a = tb ;
res = prod(a, w) ;
res.affiche () ;
}
CH A PITRE VII :
LA SURD EFINITIO N D 'O PERA TEURS
RAPPELS
C+ + vous perm e t de surdéfinir les opé rate urs e xistants, c'e s t-à -dire de leur donne r une nouve lle
signification lors q u'ils porte nt (e n partie ou e n totalité ) sur des obje ts de type clas s e .
Le m é canis m e de l
a surdé finition d'opé rate urs
Pour surdéfinir un opé rate ur e xistant op, on dé finit une fonction nom m é e ope rator op (on pe ut place r un ou
plusieurs espaces entre le m ot ope rator e t l'opé rate ur, m ais ce n'e s t pas une obligation) :
- soit sous form e d'une fonction indé pe ndante (gé né ralem e nt am ie d'une ou de plusieurs clas s e s ),
- soit sous form e d'une fonction m e m bre d'une clas s e .
D ans le pre m ie r cas, si op e s t un opé rate ur binaire , la notation a op b e s t é q uivalente à :
operator op (a, b)
a.operator op (b)
Le s pos s ibil
ité s e t l
es l
im ite s de l
a surdé finition d'opé rate urs
O n doit s e lim ite r aux opé rate urs e xistants, e n cons e rvant leur "pluralité " (unaire , binaire ). Le s opé rate urs
ainsi surdéfinis garde nt leur priorité e t leur associativité h abitue lle (voir tableau ré capitulatif, e n page 9 3).
Un opé rate ur surdéfini doit toujours posséder un opérande de type clas s e (on ne pe ut donc pas m odifie r les
significations des opérate urs usuels). Ildoit donc s'agir :
- soit d'une fonction m e m bre , auq ue lcas e lle dispose obligatoire m e nt d'un argum e nt im plicite du type de
sa clas s e (th is),
- soit d'une fonction indé pe ndante (ou plutôt am ie ) possédant au m oins un argum e nt de type clas s e .
Ilne faut pas faire d'h ypoth è s e s ur la signification a priori d'un opé rate ur ;par e xe m ple, la signification de
+ = pour une clas s e ne pe ut, e n aucun cas, ê tre déduite de la significaiton de + e t de = pour ce tte m ê m e
clas s e .
92 Exercices en langage C+ +
Cas particul
ie rs
Le s opé rate urs [], (), -> , ne w e t de lete doive nt obligatoire m e nt ê tre définis com m e fonctions m e m bre .
Le s opé rate urs = (affe ctation) e t & (pointe ur sur) possè dent une s ignification prédéfinie pour les obje ts de
n'im porte q ue ltype clas s e . Ce la ne les e m pê ch e nullem e nt d'ê tre s urdé finis.
La surdéfinition de ne w , pour un type classe donné, se fait par une fonction de prototype :
Elle re çoit, e n uniq ue argum e nt, la taille de l'obje t à alloue r (ce t argum e nt s e ra gé né ré autom atiq ue m e nt par
le com pilate ur, lors d'un appe lde ne w ), e t e lle doit fournir e n re tour l'adresse de l'obje t alloué .
La surdéfinition de de lete , pour un type donné , s e fait par une fonction de prototype :
Tabl
e au ré capitul
atif
____________________________________________________________________________________
PLURALITE OPERATEURS ASSOCIATIVITE
_____________________________________________________________________________________
(3) (3) (2)(3)
Binaire () [] -> ->
Binaire * / % ->
Binaire + - ->
Binaire == != ->
Binaire ^ ->
Binaire || ->
Binaire | ->
(1)(3)
Binaire = += -= *= /= %= <-
&= ^= |= <<= >>=
(2)
Binaire , ->
_____________________________________________________________________________________
R e m arque:
M ê m e lors q ue l'on a surdéfini les opé rate urs ne w e t de lete pour une clas s e , ilre s te possible de faire appe l
aux opé rate urs ne w e t de lete usuels, e n utilisant l'opé rate ur de ré s olution de porté e (::).
___________________________________________________________________________
Enoncé
class vecteur3d
{ float x, y, z ;
public :
vecteur3d (float c1=0.0, float c2=0.0, float c3=0.0)
{ x = c1 ; y = c2 ; z = c3 ;
}
} ;
D é finir les opé rate urs == e t !=, de m aniè re à ce q u'ils perm e tte nt de te s te r la coïncide nce ou la non-
coïncide nce de deux points :
Sol
ution
Ilsuffit donc de pré voir, dans la clas s e ve cte ur3d, deux fonctions m e m bre de nom ope rator == e t ope rator
!=, re ce vant un argum e nt de type ve cte ur3d corre s pondant au s e cond argum e nt des opérate urs (le pre m ie r
opé rande é tant fourni par l'argum e nt im plicte - th is - des fonctions m e m bre ). Voici la déclaration com plète
de notre clas s e , accom pagnée des définitions des opérate urs :
class vecteur3d
{ float x, y, z ;
public :
vecteur3d (float c1=0.0, float c2=0.0, float c3=0.0)
{ x = c1 ; y = c2 ; z = c3 ;
}
int operator == (vecteur3d) ;
int operator != (vecteur3d) ;
94 Exercices en langage C+ +
} ;
Note z q ue , dans la définition de !=, nous nous som m e s s e rvi de l'opé rate ur ==. En pratiq ue , on s e ra
souve nt am e né à ré é crire e ntiè re m e nt la définition d'un te lopé rate ur, pour de s im ples raisons d'efficacité
(d'ailleurs, pour les m ê m e s raisons, on place ra "e n ligne " les fonctions ope rator == e t ope rator !=).
Ilfaut donc pré voir de déclare r com m e am ie s , dans la clas s e ve cte ur3d, deux fonctions (ope rator == e t
ope rator !=), re ce vant deux argum e nts de type ve cte ur3d corre s pondant aux de ux opé randes des opé rate urs
Voici la nouve lle déclaration de notre clas s e , accom pagnée des définitions des opérate urs :
class vecteur3d
{ float x, y, z ;
public :
vecteur3d (float c1=0.0, float c2=0.0, float c3=0.0)
{ x = c1 ; y = c2 ; z = c3 ;
}
friend int operator == (vecteur3d, vecteur3d) ;
friend int operator != (vecteur3d, vecteur3d) ;
} ;
int operator == (vecteur3d v, vecteur3d w)
{ if ( (v.x == w.x) && (v.y == w.y) && (v.z == w.z) ) return 1 ;
else return 0 ;
}
int operator != (vecteur3d v, vecteur3d w)
{ return ! ( v == w ) ;
}
R e m arque:
Voici, à titre indicatif, un e xe m ple de program m e utilisant n'im porte laq ue lle des deux clas s e s ve cte ur3d q ue
nous ve nons de définir.
#include "vecteur3d.h"
#include <iostream.h>
main()
{ vecteur3d v1 (3,4,5), v2 (4,5,6), v3 (3,4,5) ;
cout << "v1==v2 : " << (v1==v2) << " v1!=v2 : " << (v1!=v2) << "\n" ;
cout << "v1==v3 : " << (v1==v3) << " v1!=v3 : " << (v1!=v3) << "\n" ;
}
___________________________________________________________________________
VII. La surdéfinition d'opé rateurs 95
Enoncé
class vecteur3d
{ float x, y, z ;
public :
vecteur3d (float c1=0.0, float c2=0.0, float c3=0.0)
{ x = c1 ; y = c2 ; z = c3 ;
}
} ;
D é finir l'opé rate ur binaire + pour q u'ilfournis s e la som m e de deux ve cte urs, e t l'opé rate ur binaire *pour
q u'ilfournis s e le produit scalaire de deux ve cte urs. O n ch oisira ici de s fonctions am ie s .
___________________________________________________________________________
Sol
ution
Ilsuffit de cré e r de ux fonctions am ie s nom m é e s ope rator + e t ope rator *. Elles re ce vront deux argum e nts
de type ve cte ur3d ;la pre m iè re fournira e n re tour un obje t de type ve cte ur3d, la s e conde fournira e n re tour
un float.
class vecteur3d
{ float x, y, z ;
public :
vecteur3d (float c1=0.0, float c2=0.0, float c3=0.0)
{ x = c1 ; y = c2 ; z = c3 ;
}
friend vecteur3d operator + (vecteur3d, vecteur3d) ;
friend float operator * (vecteur3d, vecteur3d) ;
} ;
R e m arque :
Ile s t possible de transm e ttre par ré fé re nce les argum e nts des deux fonctions am ie s conce rné e s . En re vanch e ,
iln'e s t pas possible de dem ande r à ope rator + de transm e ttre s on ré s ultat par ré fé re nce , puis q ue ce dernie r
e s t créé dans la fonction m ê m e , sous form e d'un obje t de clas s e autom atiq u e . En toute rigue ur, ope rator *
pourrait transm e ttre s on ré s ultat (float) par ré fé re nce , m ais ce la n'a guè re d'inté rê t e n pratiq ue .
96 Exercices en langage C+ +
___________________________________________________________________________
Enoncé
class vecteur3d
{ float v[3] ;
public :
vecteur3d (float c1=0.0, float c2=0.0, float c3=0.0)
{ // à compléter
}
} ;
Com pléte r la définition du constructe ur ("e n ligne "), puis définir l'opé rate ur [] pour q u'ilpe rm e tte d'accéder
à l'une des trois com posantes d'un ve cte ur, e t ce la, aussi bien au s e in d'une e xpre s s ion ( ... = v1[i]) q u'à
gauch e d'un opé rate ur d'affe ctation (v1[i] = ...) ;de plus, on ch e rch e ra à s e proté ge r contre d'éve ntue ls
ris q ues de déborde m e nt d'indice .
___________________________________________________________________________
Sol
ution
La définition du constructe ur ne pos e aucun problèm e . En ce q ui conce rne l'opé rate ur [], le C+ + ne pe rm e t
de le s urdé finir q ue s ous form e d'une fonction m e m bre (ce tte e xce ption e s t justifié e par le fait q ue l'obje t
conce rné ne doit pas ris q ue r d'ê tre s oum is à une conve rsion, lors q u'ilapparaî t à gauch e d'une affe ctation).
Elle possédera donc un s e ulargum e nt de type int e t e lle re nve rra un ré s ultat de type ve cte ur3d.
Pour que notre opérateur puisse ê tre util isé à gauch e d'une affectation, ilfaut absol um ent que l e
résul tat soit renvoyé par référence. Pour nous proté ge r d'un é ve ntue ldébordem e nt d'indice , nous avons
sim plem e nt pré vu q ue toute te ntative d'accè s à un é lém e nt e n de h ors des lim ite s conduirait à accéder au
pre m ie r é lém e nt.
Voici la déclaration de notre clas s e , accom pagné e d e la définition de la fonction ope rator [].
class vecteur3d
{ float v [3] ;
public :
vecteur3d (float c1=0.0, float c2=0.0, float c3=0.0)
{ v[0] = c1 ; v[1] = c2 ; v[2] = c3 ;
}
float & operator [] ( int) ;
} ;
R e m arque:
VII. La surdéfinition d'opé rateurs 97
A titre indicatif, voici un pe tit e xe m ple de program m e faisant appe là notre clas s e ve cte ur3d :
#include "vecteur3d.h"
#include <iostream.h>
main()
{ vecteur3d v1 (3,4,5) ;
int i ;
cout << "v1 = " ;
for (i=0 ; i<3 ; i++) cout << v1[i] << " " ;
for (i=0 ; i<3 ; i++) v1[i] = i ;
cout << "\nv1 = " ;
for (i=0 ; i<3 ; i++) cout << v1[i] << " " ;
}
___________________________________________________________________________
Enoncé
L'e xe rcice V.4 vous avait proposé de cré e r une clas s e s e t_int pe rm e ttant de re pré s e nte r de s e ns e m bles de
nom bre s e ntie rs :
class set_int
{
int * adval ; // adresse du tableau des valeurs
int nmax ; // nombre maxi d'éléments
int nelem ; // nombre courant d'éléments
public :
set_int (int = 20) ; // constructeur
~set_int () ; // destructeur
...... // autres fonctions membre
} ;
Son im plém e ntation pré voyait de place r les diffé re nts é lém e nts dans un tableau alloué dynam iq ue m e nt ;
aussi l 'affectation e ntre obje ts de type s e t_int posait-e lle des problèm e s , puis q u'e lle aboutissait à des obje ts
diffé re nts com portant des pointe urs sur un m ê m e e m place m e nt dynam iq ue .
M odifie r la clas s e s e t_int pour q u'e lle ne pré s e nte plus de te lles lacune s . O n pré voira q ue tout obje t de type
s e t_int com porte s on propre e m place m e nt dynam iq ue , com m e on l'avait fait pour pe rm e ttre la transm ission
par valeur (ce ne s e rait pas le cas si l'on ch e rch ait à utiliser une te ch niq ue de "com pte ur de ré fé re nce "). De
plus, on s'arrange ra pour q ue l'affe ctation m ultiple s oit utilisable.
___________________________________________________________________________
Sol
ution
Nous som m e s e n pré s e nce d'un problèm e voisin de ce lui pos é par le constructe ur par re copie ;nous l'avions
ré s olu e n pré voyant ce q ue l'on appe lle une "copie profonde " de l'obje t conce rné (c'e s t-à -dire une copie ,
non s e ulem e nt de l'obje t lui-m ê m e , m ais de toute s s e s parties dynam iq ue s ). Que lques diffé re nce s
supplém e ntaire s s urgis s e nt né anm oins ;e n e ffe t, ici :
- avant affe ctation, ile xiste deux obje ts "com plets" (ave c leur partie dynam iq ue ), alors q ue dans le cas
du constructe ur par re copie , iln'e xistait q u'un s e ulobje t, le s e cond é tant à cré e r.
Voici com m e nt nous traite rons une affe ctation te lle q ue b = a, dans le cas où b sera diffé re nt de a :
Par ailleurs, pour q ue l'affe ctation m ultiple (te lle q ue c = b= a) fonctionne corre cte m e nt, ile s t né ce s s aire
q ue notre opé rate ur re nvoie une valeur de re tour (e lle s e ra de type s e t_int) re pré s e ntant la valeur de s on
pre m ie r opé rande (aprè s affe ctation, c'e s t-à -dire , la valeur de b aprè s b=a).
Voici ce q ue pourraî t ê tre la définition de notre fonction ope rator = (e n ce q ui conce rne la déclaration de la
clas s e s e t_int, ilsuffirait d'y ajoute r s e t_int & ope rator = (s e t_int & ) :
R e m arques:
1) Une te lle s urdé finition d'un opé rate ur d'affe ctation pour une clas s e possédant des parties dynam iq ue s
ne s e ra valable q ue s i e lle e s t associé e à une s urdé finition com parable du constructe ur par re copie (pour
la clas s e s e t_int, ce lle proposée dans la solution de l'e xe rcice V.4 convie nt parfaite m e nt).
2) A priori, se ule la valeur du pre m ie r opé rande a vraim e nt besoin d'ê tre transm ise par réfé re nce (pour
q ue = puis s e le m odifie r!) ;ce tte condition e s t obligatoire m e nt re m plie puis q ue notre opé rate ur doit ê tre
surdéfini com m e fonction m e m bre . Toute fois, e n pratiq ue , on utilisera égalem e nt la transm ission par
ré fé re nce , à la fois pour le s e cond opé rande e t pour la valeur de re tour, de façon à ê tre plus e fficace (e n
te m ps). Note z d'ailleurs q ue s i l'opé rate ur = re nvoyait son ré s ultat par valeur, ily aurait alors appe ldu
constructe ur de re copie (la re m arq ue pré cédente s 'appliq ue rait alors à une s im ple affe ctation).
___________________________________________________________________________
Enoncé
Considérer à nouve au la clas s e s e t_int cré é e d ans l'e xe rcice V.4 (e t sur laq ue lle e s t é galem e nt fondé
l'e xe rcice pré cédent) :
VII. La surdéfinition d'opé rateurs 99
class set_int
{
int * adval ; // adresse du tableau des valeurs
int nmax ; // nombre maxi d'éléments
int nelem ; // nombre courant d'éléments
public :
set_int (int = 20) ; // constructeur
~set_int () ; // destructeur
......
} ;
- l'on puis s e ajoute r un é lém e nt à un e ns e m ble de type s e t_int par (e désignant un obje t de type s e t_int e t
n un e ntie r) : e < n ;
- e [n] pre nne la valeur 1 si n appartie nt à e e t la valeur 0 dans le cas contraire . O n s'arrange ra pour
q u'une instruction de la form e e [i] = ... soit rejeté e à l
a com pil
ation.
___________________________________________________________________________
Sol
ution
Ilnous faut donc surdéfinir l'opé rate ur binaire < , de m aniè re à ce q u'ilre çoive com m e opé rande s un obje t
de type s e t_int e t un e ntie r. Bie n q ue l'é noncé ne pré voie rie n, ile s t probable q ue l'on souh aite ra pouvoir
é crire des ch os e s te lles q ue (e é tant de type s e t_int, n e t p de s e ntie rs) :
e < n < p ;
Ce la signifie donc q ue notre opé rate ur de vra fournir com m e valeur de re tour l'e ns e m ble conce rné , aprè s
q u'on lui a ajouté l'é lém e nt voulu.
Nous pouvons indiffé re m m e nt utiliser ici une fonction m e m bre ou une fonction am ie . Nous ch oisirons la
pre m iè re possibilité . Par ailleurs, nous transm e ttrons les opé rande s e t la valeur de re tour par ré fé re nce (ici,
c'e s t possible car l'obje t corre s pondant n'e s t pas cré é au s e in de l'opé rate ur m ê m e , c'e s t-à -dire q u'iln'e s t
pas de clas s e autom atiq ue ) ;ainsi notre opé rate ur fonctionne ra m ê m e s i le constructe ur par re copie n'a pas
é té s urdé fini (e n pratiq ue , toute fois, ilfaudra le faire dè s q u'on souh aite ra pouvoir transm e ttre la valeur d'un
obje t de type s e t_int e n argum e nt).
En ce q ui conce rne l'opé rate ur [], ildoit ê tre s urdé fini com m e fonction m e m bre , com m e l'im pos e le C+ + ,
e t ce la bien q u'ici une affe ctation te lle e [i] = soit inte rdite (alors q ue c'e s t pré cis é m e nt pour l'autoris e r q ue
C+ + oblige d'en faire une fonction m e m bre !). Pour inte rdire de te lles affe ctations, la dém arch e la plus
sim ple consiste à faire e n sorte q ue le ré s ultat fourni par l'opé rate ur ne s oit pas une "lvalue ", e n la
transm ettant par val eur.
Voici q ue lle pourrait ê tre la définition de nos deux opérate urs (note z q ue nous utilisons [] pour dé finir < ) :
/* surdéfinition de < */
set_int & set_int::operator < (int nb)
{ // on examine si nb appartient déjà à l'ensemble
// en utilisant l'opérateur []
if ( ! (*this)[nb] && (nelem < nmax) ) adval [nelem++] = nb ;
return (*this) ;
}
/* surdéfinition de [] */
int set_int::operator [] (int nb) // attention résultat par valeur
100 Exercices en langage C+ +
{ int i=0 ;
// on examine si nb appartient déjà à l'ensemble
// (dans ce cas i vaudra nele en fin de boucle)
while ( (i<nelem) && (adval[i] != nb) ) i++ ;
return (i<nelem) ;
}
___________________________________________________________________________
Enoncé
class vecteur3d
{ float v [3] ;
public :
vecteur3d (float c1=0.0, float c2=0.0, float c3=0.0)
{ v[0] = c1 ; v[1] = c2 ; v[2] = c3 ;
}
// à compléter
} ;
- ilpe rm e tte d'accéder "norm alem e nt" à un é lém e nt d'un obje t non constant de type ve cte ur3d, e t ce la
aussi bien dans une expression qu'en opérande de gauch e d'une affe ctation,
- ilne pe rm e tte q ue la consultation (e t non la m odification) d'un obje t constant de type ve cte ur3d
(autre m e nt dit, si v e s t un te lobje t, une instruction de la form e v[i] = ... devra ê tre re je té e à la
com pilation).
___________________________________________________________________________
Sol
ution
R appe lons q ue lors q ue l'on dé finit des obje ts constants (q ualificatif const), iln'e s t pas possible, a priori, de
leur appliq ue r une fonction m e m bre publiq ue , sauf si ce tte derniè re a é té déclaré e ave c le q ualificatif const
(auq ue lcas, une te lle fonction pe ut indiffé re m m e nt ê tre utilisée ave c des obje ts constants ou non constants).
Ici, nous devons donc dé finir une fonction m e m bre constante de nom ope rator [].
Par ailleurs, pour q u'une affe ctation de la form e v[i] = ... soit inte rdite , ile s t né ce s s aire q ue notre opé rate ur
re nvoie s on ré s ultat par valeur (e t non par adre s s e com m e on a gé né ralem e nt l'h abitude de le faire ).
D ans ce s conditions, on voit q u'ile s t né ce s s aire de pré voir de ux fonctions m e m bre diffé re nte s , pour traite r
ch acun des deux cas : obje t constant ou obje t non constant. Le ch oix de la "bonne fonction" s e ra assuré par
le com pilate ur, e n fonction de la pré s e nce ou de l'abs e nce de l'attribut const pour l'obje t conce rné .
Voici la définition com plète de notre clas s e , accom pagné e d e la définition des deux fonctions ope rator [] :
class vecteur3d
{ float v [3] ;
public :
vecteur3d (float c1=0.0, float c2=0.0, float c3=0.0)
VII. La surdéfinition d'opé rateurs 101
A titre indicatif, voici un pe tit program m e utilisant la clas s e ve cte ur3d ainsi définie , accom pagné du résultat
produit par son exécution :
#include "vecteur3d.h"
#include <iostream.h>
main()
{ int i ;
vecteur3d v1 (3,4,5) ;
const vecteur3d v2 (1,2,3) ;
cout << "V1 : " ;
for (i=0 ; i<3 ; i++) cout << v1[i] << " " ;
cout << "\nV2 : " ;
for (i=0 ; i<3 ; i++) cout << v2[i] << " " ;
for (i=0 ; i<3 ; i++) v1[i] = i ;
cout << "\nV1 : " ;
for (i=0 ; i<3 ; i++) cout << v1[i] << " " ;
// v2[1] = 3 ; est bien rejeté à la compilation
}
___________________________
V1 : 3 4 5
V2 : 1 2 3
V1 : 0 1 2
___________________________________________________________________________
Enoncé
D é finir une clas s e ve ct pe rm e ttant de re pré s e nte r de s "ve cte urs dynam iq ue s ", c'e s t-à -dire des ve cte urs dont
la dim e nsion pe ut ne pas ê tre connue lors de la com pilation. Plus précisém e nt, on pré voira de déclare r de
te ls ve cte urs par une instruction de la form e :
vect t(exp) ;
dans laq ue lle e xp désigne une expression quelconq ue (de type e ntie r).
102 Exercices en langage C+ +
O n dé finira, de façon approprié e , l'opé rate ur [] de m aniè re à ce q u'ilpe rm e tte d'accéder à des élém e nts
d'un obje t d'un type ve ct com m e on le fe rait ave c un tableau classique.
O n ne ch e rch e ra pas à ré s oudre les problèm e s pos é s é ve ntue llem e nt par l'affe ctation ou la transm ission par
valeur d'obje ts de type ve ct. En re vanch e , on s'arrange ra pour q u'aucun ris q ue de "débordem e nt" d'indice
n'e xiste .
___________________________________________________________________________
Sol
ution
Le s é lém e nts d'un obje t de type ve ct doive nt obligatoire m e nt ê tre rangé s e n m é m oire dynam iq ue .
L'e m place m e nt corre s pondant s e ra donc alloué par le constructe ur q ui e n re ce vra la taille e n argum e nt. Le
destructe ur de vra donc, nature llem e nt, libérer cet e m place m e nt. En ce q ui conce rne l'accè s à un é lém e nt, il
s e fe ra e n surdéfinissant l'opé rate ur [], com m e nous l'avons déjà fait au cours des précédents e xe rcice s ;
rappe lons q u'ilfaudra obligatoire m e nt le faire s ous form e d'une fonction m e m bre .
Pour nous proté ge r d'un é ve ntue ldébordem e nt d'indice , nous fe rons e n sorte q u'une te ntative d'accè s à un
é lém e nt situé e n de h ors du ve cte ur conduis e à accéder à l'é lém e nt de rang 0.
#include <iostream.h>
#include "vect.h"
main()
{ vect v(6) ;
int i ;
for (i=0 ; i<6 ; i++) v[i] = i ;
for (i=0 ; i<6 ; i++) cout << v[i] << " " ;
}
VII. La surdéfinition d'opé rateurs 103
___________________________________________________________________________
Enoncé
En s'inspirant de l'e xe rcice pré cédent, on souh aite cré e r une clas s e int2d pe rm e ttant de re pré s e nte r de s
tableaux dynam iq ues d'entie rs à deux indice s , c'e s t-à -dire des tableaux dont les dim e nsions peuve nt ne pas
ê tre connue s lors de la com pilation. Plus précisém e nt, on pré voira de déclare r de te ls tableaux par une
déclaration de la form e :
dans laq ue lle e xp1 e t e xp2 désignent une e xpre s s ion q ue lconq ue (de type e ntie r).
a) D ire pourq uoi iln'e s t pas possible, ici, de s urdé finir ici l'opé rate ur [] pour accéder à des élém e nts d'un
te ltableau.
b) Surdé finir, de façon approprié e , l'opé rate ur () de m aniè re à ce q u'ilpe rm e tte d'accéder à des élém e nts
d'un obje t d'un type int2d com m e on le fe rait ave c un tableau classique. Là e ncore , on ne ch e rch e ra pas à
ré s oudre les problèm e s pos é s é ve ntue llem e nt par l'affe ctation ou la transm ission par valeur d'obje ts de type
int2d. En re vanch e , on s'arrange ra pour q u'iln'e xiste aucun ris q ue de débordem e nt d'indice .
___________________________________________________________________________
Sol
ution
a) L'opé rate ur [] ne pe ut ê tre s urdé fini q ue s ous form e binaire . Iln'e s t donc pas possible de l'e m ploye r sous
la form e t[i, j] pour accéder à un é lém e nt d'un tableau dynam iq ue . O n pourrait alors penser à l'utiliser sous
la form e h abitue lle t[i][j] . O r, ce tte é criture doit ê tre inte rpré té e com m e (t[i]) [j]), ce q ui signifie q u'on n'y
appliq ue plus le s e cond opé rate ur à un obje t de type int2d.
Com pte te nu de ce q ue , de surcroî t, [] doit ê tre défini com m e fonction m e m bre , l'é criture e n q ue s tion
dem ande rait de définir [] pour une clas s e int2d, e n s'arrange ant pour q u'ilfournis s e un ré s ultat ("ve cte ur
ligne ") lui-m ê m e de type clas s e , afin de pouvoir, dans ce nouve au type clas s e , surdéfinir à nouve au [] pour
q u'ilfournis s e un ré s ultat de type int. Iln'e s t donc pas possible d'obte nir le ré s ultat souh aité s ans définir
d'autre s clas s e s q ue int2d.
b) Com m e dans l'e xe rcice pré cédent, les é lém e nts d'un obje t de type int2d doive nt obligatoire m e nt ê tre
rangé s e n m é m oire dynam iq ue . L'e m place m e nt corre s pondant s e ra donc alloué par le constructe ur q ui e n
déduira la taille des deux dim e nsions reçue s e n argum e nts Le destructe ur libérera cet e m place m e nt.
Nous devons décide r de la m aniè re dont s e ront rangé s les diffé re nts é lém e nts e n m é m oire , à savoir "par
ligne " ou "par colonne " (e n toute rigue ur, ce tte te rm inologie fait ré fé re nce à la façon dont on a l'h abitude de
visualiser des tableaux à deux dim e nsions, ce que l'on nom m e une ligne corre s pondant e n fait à des élém e nts
ayant m ê m e valeur du pre m ie r indice ). Nous ch oisirons ici la pre m iè re s olution (c'e s t ce lle utilisée par C ou
C+ + pour les tableaux à deux dim e nsions). Ainsi, un é lém e nt re pé ré par les valeurs i et j des 2 indices sera
situé à l'adre s s e (adv désignant l'adresse de l'e m place m e nt dynam iq ue alloué au tableau) :
adv + i * ncol + j
En ce q ui conce rne l'accè s à un é lém e nt, il s e fe ra e n surdéfinissant l'opé rate ur (), d'une m aniè re
com parable à ce q ue nous avions fait pour [] ;là e ncore , C+ + im pos e q ue () soit défini com m e fonction
m e m bre .
Pour nous proté ge r d'un é ve ntue ldébordem e nt d'indice , nous fe rons e n sorte q u'une valeur incorre cte d'un
indice conduis e à "faire com m e s i" on lui avait attribué la valeur 0.
104 Exercices en langage C+ +
#include <iostream.h>
#include "int2d.h"
main()
{ int2d t1 (4,3) ;
int i, j ;
for (i=0 ; i<4 ; i++)
for (j=0 ; j<3 ; j++)
t1(i, j) = i+j ;
for (i=0 ; i<4 ; i++)
{ for (j=0 ; j<3 ; j++)
cout << t1 (i, j) << " " ;
cout << "\n" ;
}
}
R e m arques :
1) D ans la pratiq ue , on s e ra am e né à définir de ux opé rate urs () com m e nous l'avions fait dans l'e xe rcice
pré cédent pour l'opé rate ur [], à savoir : un pour de s obje ts non constants (ce lui dé fini ici), l'autre pour
des obje ts constants (ils e ra pré vu de m aniè re à ne pas pouvoir m odifie r l'obje t sur leq ue lilporte ).
VII. La surdéfinition d'opé rateurs 105
2) Gé né ralem e nt, par souci d'e fficacité , les opé rate urs te ls q ue () s e ront définis "e n ligne ".
___________________________________________________________________________
Enoncé
Cré e r une clas s e nom m é e h isto pe rm e ttant de m anipuler de s "h istogram m e s ". O n rappe lle q ue l'on obtie nt
un h istogram m e à partir d'un e ns e m ble de valeurs x(i), e n dé finissant n tranch e s (inte rvalles ) contiguës
(souve nt de m ê m e am plitude ) e t e n com ptabilisant le nom bre de valeurs x(i) apparte nant à ch acune de ce s
tranch e s .
O n pré voira :
- un constructe ur de la form e h isto (float m in, float m ax, int ninte r), dont les argum e nts précisent les
borne s (m in e t m ax ) des valeurs à pre ndre e n com pte e t le nom bre de tranch e s (ninte r) supposées de
m ê m e am plitude ,
- un opé rate ur < < d é fini de m aniè re te lle q ue h < < x ajoute la valeur x à l'h istogram m e h , c'e s t-à -dire
q u'e lle incré m e nte de 1 le com pte ur re latif à la tranch e à laq ue lle appartie nt x. Le s valeurs sortant des
lim ite s (m in - m ax) ne s e ront pas com ptabilisées,
- un opé rate ur [] défini de m aniè re te lle q ue h [i] re pré s e nte le nom bre de valeurs ré pe rtoriées dans la
tranch e de rang i (la pre m iè re tranch e portant le num é ro 1 ;un num é ro incorre ct de tranch e conduira à
considérer celle de rang 1). O n s'arrange ra pour q u'une instruction de la form e h [i] = ... soit re je té e e n
com pilation.
O n ne ch e rch e ra pas ici à ré gler les problèm e s pos é s par l'affe ctation ou la transm ission par valeur d'obje ts
du type h isto.
___________________________________________________________________________
Sol
ution
Nous n'avons pas besoin de conserve r les diffé re nte s valeurs x(i), m ais s e ulem e nt les com pte urs re latifs à
ch aq ue tranch e . En re vanch e , ilfaut pré voir d'alloue r dynam iq ue m e nt (dans le constructe ur) l'e m place m e nt
né ce s s aire à ce s com pte urs, puis q ue le nom bre de tranch e s n'e s t pas connu lors de la com pilation de la clas s e
h isto. Ilfaudra nature llem e nt pré voir de libérer cet e m place m e nt dans le destructe ur de la clas s e . Le s
m e m bres donnée de h isto s e ront donc : les valeurs e xtrê m e s (m inim ale e t m axim ale), le nom bre de tranch e s
e t un pointe ur sur l'e m place m e nt conte nant les com pte urs.
L'opé rate ur < < pe ut ê tre s urdé fini, soit com m e fonction m e m bre , soit com m e fonction am ie ;nous
ch oisirons ici la pre m iè re s olution. En re vanch e , l'opé rate ur [] doit absolum e nt ê tre s urdé fini com m e
fonction m e m bre . Pour q u'ilne s oit pas possible d'écrire des affe ctations de la form e h [i] = ..., on fe ra e n
sorte q ue ce t opé rate ur fournis s e s on ré s ultat par valeur.
Note z q ue nous avons prévu de s valeurs par dé faut pour les argum e nts du constructe ur (ce lles -ci n'é taie nt
pas im pos é e s par l'é noncé ).
En ce q ui conce rne la définition des diffé re nts m e m bre s , ilfaut note r q u'ile s t indispensable q u'une te lle
clas s e s oit proté gé e contre toute utilisation incorre cte . Ce rte s , ce la pas s e par un contrôle de la valeur du
num é ro de tranch e fourni à l'opé rate ur [], ou par le re fus de prendre en com pte une valeur h ors lim ite s (q ui
fournirait un num é ro de tranch e conduisant à un e m place m e nt situé e n de h ors de ce lui alloué par le
constructe ur).
M ais, de surcroî t, nous devons nous assurer que les valeurs des argum e nts fournis au constructe ur ne
ris q ue nt pas de m e ttre e n caus e le fonctionne m e nt ulté rie ur des diffé re nte s fonctions m e m bre . En particulie r,
ile s t bon de vé rifie r q ue le nom bre de tranch e s n'e s t pas négatif (notam m e nt une valeur nulle conduirait
dans < < à une division par zé ro) e t q ue les valeurs du m inim um e t du m axim um sont conve nablem e nt
ordonné e s e t diffé re nte s l'une de l'autre (dans ce dernie r cas, on aboutirait e ncore à une division par zé ro).
Ici, nous avons prévu de ré gler ce problèm e e n attribuant le cas é ch é ant des valeurs par dé faut arbitraire s
(m ax = m in + 1, nint = 1).
Voici ce q ue pourrait ê tre la définition des diffé re nte s fonctions m e m bre de notre clas s e h isto :
Voici, à titre indicatif, un pe tit program m e d'essai de la clas s e h isto, accom pagné du résultat fourni par son
e xé cution :
#include "histo.h"
#include <iostream.h>
main()
{ const int nint = 4 ;
int i ;
histo h (0., 5., nint) ; // 4 tranches entre 0 et 5
h << 1.5 << 2.4 << 3.8 << 3.0 << 2.0 << 3.5 << 2.8 << 4.6 ;
h << 12.0 << -3.5 ;
for (i=0 ; i<10 ; i++) h << i/2.0 ;
cout << "valeurs des tranches \n" ;
for (i=1 ; i<=nint ; i++)
cout << "numéro " << i << " : " << h[i] << "\n" ;
}
_____________________
___________________________________________________________________________
Enoncé
R é aliser une clas s e nom m é e stack _int pe rm e ttant de gérer une pile d'entie rs. Ces dernie rs s e ront cons e rvé s
dans un em place m e nt alloué dynam iq ue m e nt ;sa dim e nsion sera déte rm iné e par l'argum e nt fourni à son
constructe ur (on lui pré voira une valeur par dé faut de 20). Ce tte classe devra com porte r les opé rate urs
suivants (nous supposons que p est un obje t de type stack _int e t n un e ntie r) :
< < , te lq ue p<<n ajoute l'e ntie r n à la pile p (si la pile e s t pleine , rie n ne s e pas s e ),
> > , te lq ue p> > n place dans n la valeur du h aut de la pile, e n la supprim ant de la pile (si la pile e s t
vide, la valeur de n ne s e ra pas m odifié e ),
+ + , te lq ue p+ + vale 1 si la pile e s t pleine e t 0 dans le cas contraire ,
--, te lq ue p-- vale 1 si la pile e s t vide et 0 dans le cas contraire .
O n pré voira q ue les opé rate urs < < e t > > pourront ê tre utilisés sous les form e s s uivante s (n1, n2 e t n3
é tant des entie rs) :
O n fe ra e n sorte q u'ilsoit possible de transm e ttre une pile par valeur. En re vanch e , l'affe ctation e ntre piles
ne s e ra pas perm ise, et on s'arrange ra pour q ue ce tte s ituation aboutisse à un arrê t de l'e xé cution.
___________________________________________________________________________
Sol
ution
La clas s e stack _int contie ndra com m e m e m bres donnée : la taille de l'e m place m e nt ré s e rvé pour la pile
(nm ax), le nom bre d'élém e nts placé s à un m om e nt donné s ur la pile (ne le m ) e t un pointe ur sur
l'e m place m e nt q ui s e ra alloué par le constructe ur pour y range r les é lém e nts de la pile (adv). Note z q u'il
n'e s t pas néce s s aire de pré voir une donnée supplém e ntaire pour un é ve ntue l"pointe ur" de pile, dans la
m e s ure où c'e s t le nom bre d'élém e nts ne le m q ui joue ce rôle ici.
Le s opé rate urs re q uis peuve nt indifé re m m e nt ê tre définis com m e fonctions m e m bre ou com m e fonctions
am ie s . Nous ch oisirons ici la pre m iè re s olution. Pour q ue les opé rate urs < < e t > > puis s e nt ê tre utilisés à
plusieurs reprises dans une m ê m e e xpre s s ion, ile s t né ce s s aire q ue , par e xe m ple :
soit é q uivalent à :
Pour ce faire , les opé rate urs < < e t > > doive nt fournir com m e ré s ultat la pile re çue e n pre m ie r opé rande ,
aprè s q u'e lle a subit l'opé ration voulue (e m pilage ou dé pilage ). Ile s t pré fé rable q ue ce ré s ultat soit transm is
par ré fé re nce (on é vite ra la pe rte de te m ps due au constructe ur par re copie ).
En ce q ui conce rne la transm ission par valeur d'un obje t de type stack _int, nous ne pouvons pas nous
conte nte r du constructe ur par re copie par dé faut puis q ue ce dernie r ne re copie rait pas la partie dynam iq ue de
l'obje t, ce q ui pos e rait les "problèm e s h abitue ls". Nous devons donc surdéfinir le constructe ur par re copie ;
ici, nous prévoirons une duplication com plète de l'obje t (une autre dém arch e , plus com pliq ué e , consiste rait à
e m ploye r un "com pte ur de ré fé re nce ", ce q ui pe rm e ttrait de ne plus dupliq ue r la partie dynam iq ue ).
Pour satisfaire à la contrainte im pos é e par l'é noncé s ur l'affe ctation, nous allons surdé finir l'opé rate ur
d'affe ctation. Com m e ce dernie r doit s e conte nte r d'affich e r un m e s s age d'erreur et d'inte rrom pre
l'e xé cution, iln'e s t pas néce s s aire q u'ilre nvoie une q ue lconq ue valeur (d'où la déclaration void).
#include <iostream.h>
#include <stdlib.h>
class stack_int
{ int nmax ; // nombre maximal de la valeurs de la pile
int nelem ; // nombre courant de valeurs de la pile
int * adv ; // pointeur sur les valeurs
public :
stack_int (int = 20) ; // constructeur
~stack_int () ; // destructeur
stack_int (stack_int &) ; // constructeur par recopie
void operator = (stack_int &) ; // affectation
stack_int & operator << (int) ; // opérateur d'empilage
stack_int & operator >> (int &) ; // opérateur de dépilage (attention int &)
int operator ++ () ; // opérateur de test pile pleine
int operator -- () ; // opérateur de test pile vide
VII. La surdéfinition d'opé rateurs 109
} ;
Note z q ue l'opé rate ur > > doit absolum e nt re ce voir son deuxiè m e opé rande par ré fé re nce , puis q u'ildoit
pouvoir e n m odifie r la valeur.
#include "stack-int.h"
stack_int::stack_int (int n)
{ nmax = n ;
adv = new int [nmax] ;
nelem = 0 ;
}
stack_int::~stack_int ()
{ delete adv ;
}
stack_int::stack_int (stack_int & p)
{ nmax = p.nmax ; nelem = p.nelem ;
adv = new int [nmax] ;
int i ;
for (i=0 ; i<nelem ; i++)
adv[i] = p.adv[i] ;
}
void stack_int::operator = (stack_int & p)
{ cout << "*** Tentative d'affectation entre piles - arrêt exécution ***\n" ;
exit (1) ;
}
stack_int & stack_int::operator << (int n)
{ if (nelem < nmax) adv[nelem++] = n ;
return (*this) ;
}
stack_int & stack_int::operator >> (int & n)
{ if (nelem > 0) n = adv[--nelem] ;
return (*this) ;
}
int stack_int::operator ++ ()
{ return (nelem == nmax) ;
}
int stack_int::operator -- ()
{ return (nelem == 0) ;
}
A titre indicatif, voici un pe tit program m e d'utilisation de notre clas s e , accom pagné d'un exem ple
d'exécution :
int n, p ;
pile >> n >> p ; // on dépile 2 valeurs
cout << "haut de la pile au retour de fct : " << n << " " << p << "\n" ;
stack_int pileb (25) ;
pileb = pile ; // tentative d'affectation
}
pleine : 0 vide : 1
haut de la pile reçue par fct : 4 3
haut de la pile au retour de fct : 4 3
*** Tentative d'affectation entre piles - arrêt exécution ***
___________________________________________________________________________
Enoncé
class point
{ int x, y ;
// .......
public :
point (int abs=0, int ord=0) // constructeur
// .....
} ;
___________________________________________________________________________
Sol
ution
Ici, iln'e s t plus possible d e s e conte nte r de com ptabiliser les appe ls au constructe ur e t au de s tructe ur. Il
faut, e n outre , com ptabiliser les appe ls à ne w e t de lete . La s e ule possibilité pour y parve nir consiste à
surdéfinir ces deux opé rate urs pour la clas s e point. Le nom bre de points totale t le nom bre de points
dynam iq ue s s e ront cons e rvés dans des m e m bre s s tatiq ue s (n'e xistant q u'une s e ule fois pour l'e ns e m ble des
obje ts du type point). Quant aux fonctions m e m bre fournissant les inform ations voulue s , ile s t pré fé rable
d'en faire des fonctions m e m bre s tatiq ue s (déclaré e s , e lles aussi, ave c l'attribut static), ce q ui pe rm e ttra de
les e m ploye r plus facilem e nt q ue s i on e n avait fait des fonctions m e m bre ordinaire s (puis q u'on pourra les
appe ler sans avoir à les faire porte r sur un obje t particulie r).
#include <iostream.h>
#include <stddef.h> // pour size_t
class point
{ static int npt ; // nombre total de points
static int nptd ; // nombre de points "dynamiques"
int x, y ;
public :
point (int abs=0, int ord=0) // constructeur
{ x=abs ; y=ord ;
npt++ ;
}
~point () // destructeur
{ npt-- ;
}
void * operator new (size_t sz) // new surdéfini
{ nptd++ ;
return ::new char[sz] ;
}
void operator delete (void * dp)
{ nptd-- ;
::delete (dp) ;
}
static int npt_tot ()
{ return npt ;
}
static int npt_dyn ()
{ return nptd ;
}
} ;
Note z q ue , dans la surdéfinition de ne w e t de lete , nous avons fait appe laux opé rate urs prédéfinis (par
e m ploi de ::) pour ce q ui conce rne la ge s tion de la m é m oire .
#include "point.h"
main()
{
point * ad1, * ad2 ;
point a(3,5) ;
cout << point::npt_tot () << " " << point::npt_dyn () << "\n" ;
ad1 = new point (1,3) ;
point b ;
cout << point::npt_tot () << " " << point::npt_dyn () << "\n" ;
ad2 = new point (2,0) ;
delete ad1 ;
cout << point::npt_tot () << " " << point::npt_dyn () << "\n" ;
point c(2) ;
delete ad2 ;
cout << point::npt_tot () << " " << point::npt_dyn () << "\n" ;
}
CH A PITRE VIII :
LES CO NVERSIO NS D E TYPE
D EFINIES PAR L'UTILISATEUR
RAPPELS
C+ + vous perm e t de définir de s conve rsions d'un type clas s e ve rs un autre type clas s e ou un type de bas e .
O n parle de conve rsions définie s par l'utilisate ur (e n abré gé : C.D .U.). Ce s conve rsions peuve nt alors ê tre
é ve ntue llem e nt m ises en oeuvre , de façon "autom atiq ue " par le com pilate ur, afin de donner une signification
à un appe lde fonction ou à un opé rate ur (alors q ue , sans conve rsion, l'appe lou l'opé rate ur s e rait illégal).
O n re trouve ainsi des possibilité s com parables à ce lles q ui nous sont offe rte s par le C e n m atiè re de
conve rsions im plicite s .
D e ux sortes de fonctions (obligatoire m e nt des fonctions m e m bre ) pe rm e tte nt de définir de s C.D .U. :
- les constructe urs à un argum e nt (q ue lq ue s oit le type de ce t argum e nt) : un te lconstructe ur ré alise une
conve rsion du type de ce t argum e nt dans le type de sa clas s e ,
- les opé rate urs de "cast" : dans une clas s e A , on dé finira un opé rate ur de conve rsion d'un type x
(q ue lconq ue , c'e s t-à -dire aussi bien un type de bas e q u'un autre type clas s e ) e n introduisant la fonction
m e m bre de prototype :
operator x () ;
note z q ue le type de la valeur de re tour (obligatoire m e nt défini par son nom ) ne doit pas figure r dans
l'e n-tê te ou le prototype d'une te lle fonction.
Le s rè gles d'utilisation de s C.D .U. re joigne nt ce lles conce rnant le ch oix d'une fonction surdéfinie :
___________________________________________________________________________
122 Exercices en langage C+ +
Enoncé
class point
{ int x, y ;
public :
point (int abs=0, int ord=0)
{ x = abs ; y = ord ;
}
// .....
} ;
a) La m unir d'un opé rate ur de "cast" pe rm e ttant de conve rtir un point e n un e ntie r (corrre s pondant à son
abscis s e ).
point p ;
int n ;
void fct (int) ;
___________________________________________________________________________
Sol
ution
a) Ilsuffit de définir une fonction m e m bre , de nom ope rator int, sans argum e nt e t re nvoyant la valeur de
l'abscisse du point l'ayant appe lé. R appe lons q ue le type de la valeur de re tour (q ue C+ + déduit du nom de
la fonction - ici int) ne doit pas figure r dans l'e n-tê te ou le prototype . Voici ce q ue pourraie nt ê tre la
déclaration e t la définition de ce tte fonction, ici ré unie s e n une s e ule déclaration d'une fonction "e n ligne " :
operator int ()
{ return x ;
}
b)
L'instruction 1 e s t traduite par le com pilate ur e n une conve rsion de p en int (par appe lde ope rator int),
suivie d'une affe ctation du ré s ultat à n. Note z bie n q u'iln'y a pas d'appe ld'un q ue lconq ue opé rate ur
d'affe ction de la clas s e point, ni d'un constructe ur par re copie (car le s e ulargum e nt transm is à la fonction
ope rator int e s t l'argum e nt im plicite th is).
L'instruction 2 e s t traduite par le com pilate ur e n une conve rsion de p en int (par appe lde ope rator int),
suivie d'un appe lde la fonction fct, à laq ue lle on transm e t le ré s ultat de ce tte conve rsion. Note z q u'iln'y
pas, là non plus, d'appe ld'un constructe ur par re copie , ni pour fct (puis q u'e lle re çoit un argum e nt d'un type
de bas e ), ni pour ope rator int (pour la m ê m e raison q ue pré cédem m e nt).
___________________________________________________________________________
Enoncé
#include <iostream.h>
class point
{ int x, y ;
public :
point (int abs, int ord) // constructeur 2 arguments
{ x = abs ; y = ord ;
}
operator int() // "cast" point --> int
{ cout << "*** appel int() pour le point " << x << " " << y << "\n" ;
return x ;
}
} ;
main()
{
point a(1,5), b(2,8) ;
int n1, n2, n3 ;
n1 = a + 3 ; // instruction 1
cout << "n1 = " << n1 << "\n" ;
n2 = a + b ; // instruction 2
cout << "n2 = " << n2 << "\n" ;
double z1, z2 ;
z1 = a + 3 ; // instruction 3
cout << "z1 = " << z1 << "\n" ;
z2 = a + b ; // instruction 4
cout << "z2 = " << z2 << "\n" ;
}
___________________________________________________________________________
Sol
ution
Lors q ue le com pilate ur re ncontre , dans l'instruction 1, l'e xpre s s ion a+ 3, ilch e rch e tout d'abord s'ile xiste
un opé rate ur surdéfini corre s pondant aux type s point e t int (dans ce t ordre ). Ici, iln'e n trouve pas. Ilva
alors ch e rch e r à m e ttre e n place des conve rsions perm e ttant d'aboutir à une opé ration e xistante ;ici,
l'opé rate ur de cast (ope rator int) lui pe rm e t de se ram e ne r à une addition d'e ntie rs (par conve rsion de p en
int), e t c'e s t la s e ule possibilité . Le ré s ultat e s t alors, bie n sûr, de type int e t ile s t affe cté à n1 sans
conve rsion.
Le m ê m e raisonne m e nt s'appliq ue à l'instruction 2 ;l'é valuation de a+ b se fait alors par conve rsion de a et
de b en int (s e ule possibilité de donner une signification à l'opé rate ur + ). Le ré s ultat e s t de type int e t ile s t
affe cté s ans conve rsion à n2.
Le s e xpre s s ions figurant à droite des affe ctations des instructions 3 et 4 sont é valué e s e xacte m e nt suivant les
m ê m e s rè gles q ue les e xpre s s ions des instructions 1 et 2. Ce n'e s t q u'au m om e nt de l'affe ctation du ré s ultat
(de type int) à la variable m e ntioné e q ue l'on opè re une conve rsion int --> double (analogue à ce lles q ui
sont m ises en place e n langage C).
___________________________________________________________________________
Enoncé
#include <iostream.h>
class point
{ int x, y ;
public :
point (int abs, int ord) // constructeur 2 arguments
{ x = abs ; y = ord ;
}
operator int() // "cast" point --> int
{ cout << "** appel int() pour le point " << x << " " << y << "\n" ;
return x ;
}
} ;
void fct (double v)
{ cout << "$$ appel fct avec argument : " << v << "\n" ;
}
main()
{
point a(1,4) ;
int n1 ;
double z1, z2 ;
n1 = a + 1.75 ; // instruction 1
cout << "n1 = " << n1 << "\n" ;
z1 = a + 1.75 ; // instruction 2
cout << "z1 = " << z1 << "\n" ;
z2 = a ; // instruction 3
cout << "z2 = " << z2 << "\n" ;
fct (a) ; // instruction 4
}
___________________________________________________________________________
Sol
ution
Pour é value r l'e xpre s s ion a + 1.75 de l'instruction 1, le com pilate ur m e t e n place une ch aî
ne de conve rsions
de a e n double (point --> int suivie de int --> double) de m aniè re à aboutir à l'addition de deux valeurs de
type double ;le ré s ultat, de type double, e s t e nsuite conve rti pour ê tre affe cté à n1 (conve rsion forcé e par
l'affe ctation, com m e d'h abitude e n C).
VIII. Les conversions de type définies par l'utilisateur 125
Note z bie n q u'iln'e s t pas q ue s tion pour le com pilate ur de pré voir la conve rsion en int de la valeur 1.75 (de
façon à s e ram e ne r à l'addition de deux int, aprè s conve rsion de a en int) car ils'agit là d'une "conve rsion
dégradante " q ui n'e s t jam ais m ise en oeuvre de m aniè re im plicite dans un calculd'expression. Iln'y a donc
pas d'autre ch oix possible (note z q ue s 'ily en avait e ffe ctive m e nt un autre , ilne s 'agirait pas pour autant
d'une s ituation d'am biguïté dans la m e s ure où le com pilate ur appliq ue rait alors les rè gles h abitue lles de
ch oix d'une fonction surdéfinie ).
L'instruction 2 corre s pond à un raisonne m e nt sim ilaire ave c ce tte s e ule diffé re nce q ue le ré s ultat de
l'addition (de type double) pe ut ê tre affe cté à z1 sans conve rsion.
___________________________________________________________________________
Enoncé
Que se pas s e rtai-il si, dans la clas s e point du pré cédent e xe rcice , nous avions introduit, e n plus de
l'opé rate ur ope rator int, un autre opé rate ur de cast ope rator double.
___________________________________________________________________________
Sol
ution
D ans ce cas, les instructions 1 et 2 auraie nt conduit à une s ituation d'am biguïté . En e ffe t, le com pilate ur
aurait disposé de deux ch aî nes de conve rsions perm e ttant de conve rtir un point e n double : soit point -->
double soit point --> int suivie de int -> double. En re vanch e , les instructions 3 et 4 auraie nt toujours é té
acce pté e s .
___________________________________________________________________________
Enoncé
class complexe
{ double x, y ;
126 Exercices en langage C+ +
public :
complexe (double r=0, double i=0) ;
complexe (complexe &) ;
} ;
complexe z (1,3) ;
void fct (complexe) ;
z = 3.75 ; // instruction 1
fct (2.8) ; // instruction 2
z = 2 ; // instruction 3
fct (4) ; // instruction 4
___________________________________________________________________________
Sol
ution
L'instruction 1 conduit une conve rsion de la valeur double 3.75 e n un com plexe par appe ldu constructe ur à
un argum e nt (com pte te nu de s argum e nts par dé faut), suivie d'une affe ctation à z.
L'instruction 2 conduit à une conve rsion de la valeur double 2.8 e n un com plexe (com m e pré cédem m e nt par
appe ldu constructe ur à un argum e nt) ;ily aura e nsuite cré ation d'une copie , par appe ldu constructe ur par
re copie de la clas s e com plexe , copie q ui s e ra transm ise à la fonction fct.
Le s instructions 3 et 4 joue nt le m ê m e rôle q ue les deux pré cédente s , ave c ce tte s e ule diffé re nce q u'e lles
font inte rve nir de s conve rsions int --> com plexe obte nue s par une conve rsion int --> double suivie d'une
conve rsion double --> com plexe .
___________________________________________________________________________
Enoncé
#include <iostream.h>
class point
{ int x, y ;
public :
point (int abs=0, int ord=0) // constructeur 0, 1 ou 2 arguments
{ x = abs ; y = ord ;
cout << "$$ construction point : " << x << " " << y << "\n" ;
}
friend point operator + (point, point) ; // point + point --> point
void affiche ()
{ cout << "Coordonnées : " << x << " " << y << "\n" ;
}
} ;
point operator+ (point a, point b)
VIII. Les conversions de type définies par l'utilisateur 127
{ point r ;
r.x = a.x + b.x ; r.y = a.y + b.y ;
return r ;
}
main()
{
point a, b(2,4) ;
a = b + 6 ; // affectation 1
a.affiche() ;
a = 6 + b ; // affectation 2
b.affiche() ;
}
Que se pas s e rait-ilsi l'opé rate ur + avait é té s urdé fini com m e une fonction m e m bre e t non plus com m e une
fonction am ie ?
___________________________________________________________________________
Sol
ution
L'é valuation de l'e xpre s s ion b+ 6 de la pre m iè re affe ctation s e fait e n utilisant l'opé rate ur + surdéfini pour
la clas s e point, aprè s avoir conve rti l'e ntie r 6 e n un point (par appe ldu constructe ur à un argum e nt). Le
ré s ultat, de type point, e s t alors affe cté à a.
L'instruction d'affe ctation 2 se déroule de façon sim ilaire (conve rsion de l'e ntie r 6 e n un point).
En re vanch e , si l'opé rate ur + avait é té s urdé fini par une fonction m e m bre , la s e conde instruction
d'affe ctation aurait é té re je té e à la com pilation ;e n e ffe t, e lle aurait é té inte rpré té e com m e :
6.operator + (b)
O n voit ici q ue s e ule la fonction am ie pe rm e t de traite r de façon ide ntiq ue les deux opé randes d'un opérate ur
binaire , notam m e nt e n ce q ui conce rne les possibilités de conve rsions im plicite s .
___________________________________________________________________________
Enoncé
class A
{ // ...
friend operator + (A, A) ;
public :
A (int) ; // constructeur à un argument entier
A (B) ; // constructeur à un argument de type B
// ...
} ;
class B
{ // ...
public :
B (int) ; // constructeur à un argument entier
128 Exercices en langage C+ +
// ...
} ;
A a1, a2, a3 ;
B b1, b2, b3 ;
les instructions suivante s s e ront-e lles corre cte s e t , si oui, q ue fe ront-e lles ?
a1 = b1 ; // instruction 1
b1 = a1 ; // instruction 2
a3 = a1 + a2 ; // instruction 3
a3 = b1 + b2 ; // instruction 4
b3 = a1 + a2 ; // instruction 5
___________________________________________________________________________
Sol
ution
a)
Instruction 1 :
Instruction 2 :
Là e ncore , ilfaut distingue r les deux cas précédents. Si B n'a pas surdé fini d'opé rate ur d'affe ctation de la
form e A & ope rator = (B), l'instruction 2 conduira à une e rre ur de com pilation puis q u'iln'e xiste ra
aucune possibilité de conve rtir un obje t de type A e n un obje t de type B. En re vanch e , si l'opé rate ur
d'affe ctation e n q ue s tion e xiste , ils e ra tout sim plem e nt utilisé.
Instruction 3 :
Instruction 4 :
Ici, b1 e t b2 seront conve rtis dans le type A e n utilisant le constructe ur A(B) avant d'ê tre transm is à
l'opé rate ur + (de A). Le ré s ultat, de type A , s e ra affe cté à a3, e n utilisant l'opé rate ur d'affe ctation de A
- soit ce lui par dé faut, soit ce lui é ve ntue llem e nt surdéfini.
Instruction 5 :
VIII. Les conversions de type définies par l'utilisateur 129
L'e xpre s s ion a1 + a2 ne pos e pas de problèm e puis q u'e lle e s t é valué e à l'aide de l'opé rate ur + de la
clas s e A ;e lle fournit un ré s ultat de type A . En re vanch e , pour l'affe ctation du ré s ultat à b3, ilfaut à
nouve au distingue r les 2 cas déjà é voq ué s . Si B a surdéfini un opé rate ur d'affe ctation de la form e A &
ope rator = (B), ils e ra utilisé ;si un te lopé rate ur n'a pas é té s urdé fini, l'instruction s e ra re je té e par le
com pilate ur (puis q u'iln'e xiste ra alors aucune possibilité de conve rsion de B e n A).
operator B ()
CH A PITRE IX :
LA TECH NIQUE D E L'H ERITAGE
RAPPELS
Le conce pt d'h é ritage constitue l'un de s fonde m e nts de la Program m ation O rie nté e O b je t. Ilpe rm e t de
définir une nouve lle clas s e B dite "dérivé e ", à partir d'une clas s e e xistante A , dite "de bas e " ;pour ce faire ,
on procè de ainsi :
Ave c public A, on parle de "dérivation publiq ue " ;ave c private A, on parle de "dérivation privé e " ;ave c
prote cte d A, on parle de "dérivation proté gé e ".
M odal
ité s d'accè s à l
a cl
as s e de bas e
Le s m e m bre s privés d'une classe de bas e ne s ont jam ais acce s s ibles aux fonctions m e m bre de sa clas s e
dérivé e .
O utre les "statuts" public ou privé (pré s e ntés dans le ch apitre III), ile xiste un statut "proté gé ". Un m e m bre
proté gé s e com porte com m e un m e m bre privé pour un utilisate ur q ue lconq ue de la clas s e ou de la clas s e
dérivé e , m ais com m e un m e m bre public pour la classe dérivé e .
- publ ique : les m e m bres de la classe de bas e cons e rve nt leur statut dans la classe dérivé e ;c'e s t la
situation la plus usuelle,
- privé e : tous les m e m bres de la classe de base devie nne nt privés dans la classe dérivé e ,
- proté gé e (depuis la ve rsion 3) : les m e m bre s publics de la classe de base devie nne nt m e m bre s proté gé s
de la classe dérivé e ;les autre s m e m bre s cons e rve nt leur statut.
Lors q u'un m e m bre (donné e ou fonction) e s t redéfini dans une classe dérivé e , ilre s te toujours possible (soit
dans les fonctions m e m bre de ce tte clas s e , soit pour un clie nt de ce tte clas s e ) d'accéder aux m e m bres de
m ê m e nom de la classe de bas e , m oye nnant l'utilisation de l'opé rate ur de ré s olution de porté e (::), sous
ré s e rve , bie n sûr, q u'un te laccè s soit autoris é .
134 Exercices en langage C+ +
Soit B une classe dérivée d'une classe de bas e A . Nature llem e nt, dè s lors q ue B possè de au m oins un
constructe ur, la cré ation d'un obje t de type B im pliq ue obligatoire m e nt l'appe ld'un constructe ur de B. M ais,
de plus, ce constructe ur de B doit pré voir de s argum e nts à destination d'un constructe ur de A (une e xce ption
a lie u, soit si A n'a pas de constructe ur, soit si A possè de un constructe ur sans argum e nt). Ce s argum e nts
sont pré cis é s , dans la définition du constructe ur de B, com m e dans ce t e xe m ple :
Le s argum e nts m e ntionné s pour A peuve nt é ve ntue llem e nt l'ê tre s ous form e d'expressions.
Cas particul
ie r du cons tructe ur par re copie
En plus des rè gles ci-de s s us, ilfaut ajoute r q ue s i la classe dérivé e B ne possè de pas de constructe ur par
re copie , ily aura appe ldu constructe ur par re copie par dé faut de B, leq ue lprocédera ainsi :
- appe ldu constructe ur par re copie de A (soit ce lui q ui y a é té défini, soit le constructe ur par re copie par
défaut),
- initialisation de s m e m bres donnée de B q ui ne s ont pas h é rités de A.
En re vanch e , un problèm e s e pos e lors q ue la classe dérivé e d é finit e xplicite m e nt un constructe ur par
re copie . En e ffe t, dans ce cas, ilfaut te nir com pte de ce q ue l'appe lde ce constructe ur par re copie
e ntraî
ne ra l'appe l:
- du constructe ur de la classe de bas e m e ntionné dans son e n-tê te , com m e dans ce t e xe m ple (ils'agit ici
d'un constructe ur par re copie de la classe de bas e , m ais ilpourrait s'agir de n'im porte q ue lautre
constructe ur) :
B (B & b) : A(b) ; // appel du constructeur par recopie de A
// auquel sera transmise la partie de B héritée de A
// (possible uniquement grâce aux règles de compatibilité
// entre classe de base et classe dérivée)
- d'un constructe ur sans argum e nt, si aucun constructe ur de la classe de bas e n'e s t m e ntionné dans l'e n-
tê te ;dans ce cas, ile s t né ce s s aire q ue la classe de base dispose d'un te lconstructe ur sans argum e nt,
faute d e q uoi, on obtie ndra une e rre ur de com pilation.
Cons é q ue nce s de l
'h é ritage
Considérons la situation suivante , dans laq ue lle la clas s e A possè de une fonction m e m bre f (dont nous ne
pré cisons pas les argum e nts) fournissant un ré s ultat de type t (q ue lconq ue : type de bas e ou type défini par
l'utilisate ur, é ve ntue llem e nt type clas s e ) :
A a ; // a est du type A
B b ; // b est du type B, dérivé de A
IX. La tech nique de l'h é ritage 135
Nature llem e nt, un appe lte lq ue a.f(...) a un s e ns e t ilfournit un ré s ultat de type t. Le fait q ue B h é rite
publiq ue m e nt de A perm e t alors de donner un sens à un appe lte lq ue :
b.f (...)
La fonction f agira sur b, com m e s 'ilé tait de type A . Le résul tat fourni par f sera cependant toujours de
type t, m ê m e , notam m e nt, lors q ue le type t e s t pré cis é m e nt le type A (le ré s ultat de f pourra toute fois ê tre
soum is à d'éve ntue lles conve rsions dans le cas où ile s t affe cté à une lvalue ).
Cas particul
ie r de l
'opé rate ur d'affe ctation
Si la classe dérivé e B n'a pas surdé fini l'opé rate ur d'affe ctation, l'affe ctation de deux obje ts de type B s e
déroule m e m bre à m e m bre , e n considérant q ue la "partie h é rité e d e A " constitue un m e m bre . Ainsi, les
m e m bre s propre s à B sont traité s par l'affe ctation pré vue pour leur type (par dé faut ou surdéfinie , suivant le
cas). La partie h é rité e d e A e s t traité e par l'affe ctation pré vue dans la clas s e A .
Si la classe dérivé e B a surdéfini l'opé rate ur =, l'affe ctation de deux obje ts de type B fe ra né ce s s aire m e nt
appe là l'opé rate ur = défini dans B. Ce lui de A ne s e ra pas appe lé, m ê m e s 'ila é té s urdé fini. Ilfaudra
donc que l 'opérateur = de B prenne en ch arge tout ce qui concerne l 'affectation d'objets de type B, y
com pris pour ce q ui e s t des m e m bre s h é rités de A.
Com patibil
ité e ntre obje ts d'une cl
as s e de bas e e t obje ts d'une cl
as s e dé rivé e
Considérons :
A a ; // a est du type A
B b ; // b est du type B, dérivé de A
A * ada ; // ada est un pointeur sur des objets de type A
B * adb ; // adb est un pointeur sur des objets de type B
- d'un obje t d'un type dérivé dans un obje t d'un type de bas e . Ainsi l'affe ctation a=b e s t légale : e lle
re vie nt à conve rtir b dans le type A (c'e s t-à -dire , e n fait, à ne considérer de b que ce qui est du type A ) e t
à affe cte r ce ré s ultat à a (ave c appe l, soit de l'opé rate ur d'affe ctation de A s i ce lui-ci a é té s urdé fini, soit
de l'opé rate ur d'affe ctation par dé faut de A). L'affe ctation inve rs e b=a e s t, q uant à e lle, illégale.
- d'un pointe ur sur une classe dérivé e e n un pointe ur sur une classe de bas e . Ainsi l'affe ctation ada=adb
e s t légale, tandis q ue adb=ada e s t illégale (e lle pe ut ce pe ndant ê tre forcé e par e m ploi de l'opé rate ur de
"cast" : adb = (B*) ada).
___________________________________________________________________________
Enoncé
O n dispose d'un fich ie r nom m é point.h conte nant la déclaration suivante de la clas s e point :
136 Exercices en langage C+ +
class point
{ float x, y ;
public :
void initialise (float abs=0.0, float ord=0.0)
{ x = abs ; y = ord ;
}
void affiche ()
{ cout << "Point de coordonnées : " << x << " " << y << "\n" ;
}
float abs () { return x ; }
float ord () { return y ; }
} ;
a) Cré e r une clas s e pointa, dérivé e d e point com portant sim plem e nt une nouve lle fonction m e m bre nom m é e
rh o, fournissant la valeur du rayon ve cte ur (pre m iè re coordonné e polaire ) d'un point.
b) M ê m e q ue s tion, e n supposant q ue les m e m bre s x e t y ont é té déclaré s proté gé s (prote cte d) dans point, e t
non plus privé s .
d) Quelles s ont les fonctions m e m bre utilisables pour un obje t de type pointb ?
___________________________________________________________________________
Sol
ution
a) Ilsuffit de prévoir, dans la déclaration de point, une nouve lle fonction m e m bre de prototype :
float rho () ;
Toute fois, com m e les m e m bre s x e t y sont privé s , ils re s te nt privé s pour les fonctions m e m bre de sa clas s e
dérivé e pointb ;ce q ui signifie q u'au s e in de la définition de rh o, ilfaut faire appe laux "fonctions d'accè s"
de point q ue s ont abs e t ord. Voici ce q ue pourrait ê tre notre clas s e pointb (ici, nous avons fourni rh o sous
form e d'une fonction "e n ligne ") :
Note z q ue , te lle q u'e lle a é té définie , la clas s e point n'a pas donné naissance à un fich ie r obje t (puis q ue
toute s s e s fonctions m e m bre s ont "e n ligne "). Ile n va de m ê m e ici pour pointb. Aussi, pour utiliser pointb
au s e in d'un program m e , ilsuffira d'inclure les fich ie rs conte nant les définitions de point e t de pointb.
Nature llem e nt, dans la pratiq ue , ile n ira rare m e nt ainsi ;e n gé né ral ;on de vra fournir non s e ulem e nt les
déclarations de la classe de bas e e t de sa classe dérivé e , m ais é galem e nt les fich ie rs obje t corre s pondant à
leurs com pilations re s pe ctive s .
b) La définition pré cédente re s te valable m ais, né anm oins, com m e les m e m bre s x e t y de point ont é té
déclaré s proté gé s , ils sont acce s s ibles aux fonctions m e m bre de sa classe dérivé e ;aussi est-ilpossible, dans
la définition de rh o, de les utiliser "directe m e nt". Voici ce q ue pourrait deve nir notre fonction rh o (toujours
ici "e n ligne ") :
IX. La tech nique de l'h é ritage 137
Note z q u'ici iln'e s t pas possible au constructe ur de pointb d'appe ler un q ue lconq ue constructe ur de point
puis q ue ce dernie r type ne possè de pas de constructe ur.
c) Voici ce q ue pourrait ê tre un constructe ur à deux argum e nts (ave c valeurs par dé faut) :
Là e ncore , si les m e m bre s x e t y de point ont é té déclaré s "proté gé s ", ile s t possible d'écrire ainsi notre
constructe ur :
d) Un obje t de type point pe ut utiliser n'im porte laq ue lle des fonctions m e m bre publiq ues de point, c'e s t-à -
dire initialise , affich e , abs e t ord, ainsi que n'im porte laq ue lle des fonctions m e m bre publiq ues de pointb,
c'e s t-à -dire rh o ou le constructe ur pointb. Note z d'ailleurs q u'ici le constructe ur e t initialise font double
e m ploi : ce la provie nt d'une part de ce q ue point ne dispose pas de vé ritable constructe ur, d'autre part de ce
q ue pointb n'a pas défini de m e m bres donnée supplém e ntaire s , de sorte q u'iln'y a rie n de plus à faire pour
initialiser un obje t de type pointb q ue pour initialiser un obje t de type point.
___________________________________________________________________________
Enoncé
O n dispose d'un fich ie r point.h conte nant la déclaration suivante de la clas s e point :
#include <iostream.h>
class point
{ float x, y ;
public :
point (float abs=0.0, float ord=0.0)
{ x = abs ; y = ord ;
}
void affiche ()
{ cout << "Coordonnées : " << x << " " << y << "\n" ;
}
- un m e m bre donnée supplém e ntaire cl, de type int, destiné à conte nir la "couleur" d'un point
138 Exercices en langage C+ +
___________________________________________________________________________
Sol
ution
a) La fonction colore ne pos e aucun problèm e particulie r puis q u'e lle agit uniq ue m e nt sur un m e m bre donnée
propre à pointcool. En ce q ui conce rne affich e , ile s t né ce s s aire q u'e lle puis s e affich e r les valeurs des
m e m bre s x e t y, h é rités de point. Com m e ce s m e m bre s s ont privé s (e t non proté gé s ), iln'e s t pas possible
q ue la nouve lle m é th ode affich e de pointcoly accè de directe m e nt. Elle doit donc obligatoire m e nt faire appe l
à la m é th ode affich e du type point ;ilsuffit, pour ce la, d'utiliser l'opé rate ur de ré s olution de porté e . Enfin,
le constructe ur de pointcoldoit re transm e ttre au constructe ur de point les coordonné e s q u'ilaura re çue s par
ses deux pre m ie rs argum e nts .
Voici ce q ue pourrait ê tre notre clas s e pointcol(ici, toute s les fonctions m e m bre , sauf le constructe ur, sont
"e n ligne ") :
Note z bie n q ue l'on pré cis e le constructe ur de point devant ê tre appe lé par ce lui de pointcol, au nive au du
constructe ur de pointcol, e t non de s a déclaration.
b) La déclaration pointcol (2.5, 3.25, 5) e ntraî ne la cré ation d'un e m place m e nt pour un obje t de type
pointcol, leq ue le s t initialisé par appel, succe s s ive m e nt :
- du constructe ur de point, q ui re çoit e n argum e nt les valeurs 2.5 e t 3.25 (com m e pré vu dans l'e n-tê te du
constructe ur de pointcol),
IX. La tech nique de l'h é ritage 139
- du constructe ur de pointcol.
___________________________________________________________________________
Enoncé
O n suppose qu'on dispose de la m ê m e clas s e point (e t donc du fich ie r point.h ) q ue dans l'e xe rcice pré cédent.
Cré e r une clas s e pointcol possédant les m ê m e s caracté ristiq ue s q ue ci-de s s us, m ais sans faire appe l à
l'h é ritage . Que lles diffé re nce s apparaî
tront e ntre ce tte clas s e pointcole t ce lle de l'e xe rcice pré cédent, au
nive au de s possibilités d'utilisation ?
___________________________________________________________________________
Sol
ution
La s e ule dém arch e possible consiste à cré e r une clas s e pointcoldans laq ue lle un de s m e m bres donnée est lui-
m ê m e de type point. Sa déclaration e t sa définition s e pré s e nte raie nt alors ainsi :
Appare m m e nt, ile xiste une analogie é troite e ntre ce tte clas s e pointcol e t ce lle de l'e xe rcice pré cédent.
Né anm oins, l'utilisate ur de ce tte nouve lle clas s e ne pe ut plus faire directe m e nt appe laux fonctions m e m bre
h é rité e s d e point. Ainsi, pour appliq ue r la m é th ode de place à un obje t a de type point, ildevrait absolum e nt
é crire : a.p.de place (...) ;or, ce la n'e s t pas autoris é ici, com pte te nu de ce q ue p e s t un m e m bre privé de
pointcol.
140 Exercices en langage C+ +
___________________________________________________________________________
Enoncé
Soit une clas s e point définie ainsi (nous ne fournissons pas la définition de s on constructe ur) :
class point
{ int x, y ;
public :
point (int = 0, int = 0) ;
friend int operator == (point &, point &) ;
} ;
Si a e t b sont de type pointcole t p de type point, les instructions suivante s s ont-e lles corre cte s e t, si oui, q ue
font-e lles ?
if (a == b) ... // instruction 1
if (a == p) ... // instruction 2
if (p == a) ... // instruction 3
if (a == 5) ... // instruction 4
if (5 == a) ... // instruction 5
b) M ê m e q ue s tion, e n supposant, ce tte fois, q ue l'opé rate ur + a é té défini au s e in de point, sous form e
d'une fonction m e m bre .
___________________________________________________________________________
Sol
ution
a) Le s 5 instructions proposées sont corre cte s . D'une m aniè re gé né rale, x == y e s t inte rpré té e com m e
ope rator == (x, y). Si x e t y sont de type point, aucun problèm e ne s e pose bien sûr. Si l'un de s opé rande s
(ou les deux) e s t de type pointcol, ils e ra (ils s e ront) conve rti im plicite m e nt dans le type point. Si l'un de s
opé rande s e s t de type int, ils e ra conve rti im plicite m e nt dans le type point (par utilisation du constructe ur à
un argum e nt de point).
En ce q ui conce rne la signification de la com paraison, on voit q u'e lle re vie nt à ne considérer d'un obje t de
type pointcol, q ue les coordonné e s . Pour un e ntie r, e lle re vie nt à le considérer com m e un point ayant ce t
e ntie r pour abscis s e e t une ordonné e nulle.
IX. La tech nique de l'h é ritage 141
b) Ce tte fois, x == y e s t inte rpré té e com m e x.ope rator == (y). Si x e s t de type point e t y d'un type
pouvant s e ram e ne r au type point (c'e s t-à -dire s oit du type pointcolq ui s e ra conve rti im plicite m e nt e n un
type de bas e point, soit d'un type e ntie r q ui s e ra conve rti im plicite m e nt e n un type point par l'inte rm édiaire
du constructe ur), aucun problèm e ne s e pos e (c'e s t le cas de la troisiè m e instruction).
Si x e s t de type pointcole t y d'un type pouvant s e ram e ne r au type point, on e s t ram e né au cas précédent,
dans la m e s ure où la fonction m e m bre ope rator ==, h é rité e d e point pe ut toujours s'appliq ue r à un obje t de
type point (c'e s t le cas des instructions 1, 2 e t 4).
En re vanch e , si x est de type int, iln'e s t plus possible de lui appliq ue r une fonction m e m bre . C'e s t ce q ui s e
passe dans la derniè re instruction q ui s e ra donc re je té e à la com pilation.
___________________________________________________________________________
Enoncé
Soit une clas s e ve ct pe rm e ttant de m anipuler de s "ve cte urs dynam iq ue s " d'entie rs (c'e s t-à -dire dont la
dim e nsion pe ut ê tre fixé e au m om e nt de l'e xé cution) dont la déclaration (fournie dans le fich ie r ve ct.h ) s e
pré s e nte ainsi :
class vect
{ int nelem ; // nombre d'éléments
int * adr ; // adresse zone dynamique contenant les éléments
public :
vect (int) ; // constructeur (précise la taille du vecteur)
~vect () ; // destructeur
int & operator [] (int) ; // accès à un élément par son "indice"
} ;
O n supppose q ue le constructe ur alloue e ffe ctive m e nt l'e m place m e nt né ce s s aire pour le nom bre d'entie rs
re çu e n argum e nt e t q ue l'opé rate ur [] pe ut ê tre utilisé indiffé re m m e nt dans une expression ou à gauch e
d'une affe ctation.
Cré e r une clas s e ve ctb, dérivé e d e ve ct, pe rm e ttant de m anipuler de s ve cte urs dynam iq ue s , dans les q ue ls on
pe ut fixe r les "borne s " des indices, les q ue lles s e ront fournie s au constructe ur de ve ctb. La clas s e ve ct
apparaî tra ainsi com m e un cas particulie r de ve ctb (un obje t de type ve ct é tant un obje t de type ve ctb dans
leq ue lla lim ite infé rie ure de l'indice e s t 0).
O n ne s e pré occupe ra pas, ici, de s problèm e s é ve ntue llem e nt pos é s par la re copie ou l'affe ctation d'obje ts de
type ve ctb.
___________________________________________________________________________
Sol
ution
Nous pré voirons, dans ve ctb, deux m e m bres donnée supplém e ntaire s (de but e t fin) pour cons e rve r les bornes
de l'indice (e n toute rigue ur, on pourrait s e conte nte r d'un m e m bre s upplém e ntaire conte nant la lim ite
infé rie ure , sach ant q ue la valeur supérieure pourrait s'e n déduire à partir de la connaissance de la taille du
ve cte ur ;toute fois, ce tte derniè re inform ation n'é tant pas publiq ue , nous re ncontre rions des problèm e s
d'accè s!).
M anife s te m e nt, ve ctb né ce s s ite un constructe ur à deux argum e nts e ntie rs corre s pondant aux bornes de
l'indice ;son en-tê te pourrait com m e nce r ainsi :
142 Exercices en langage C+ +
Com m e l'appe lde ce constructe ur e ntraî ne ra autom atiq ue m e nt ce lui du constructe ur de ve ct, iln'e s t pas
q ue s tion de faire l'allocation dynam iq ue de notre ve cte ur dans ve ctb. Au contraire , nous ré utilisons le travail
e ffe ctué par ve ct, auq ue lnous trnsm e ttrons sim plem e nt le nom bre d'élém e nts souh aité s , c'e s t-à -dire ici f-
d+ 1. Voici l'e n-tê te com plet du constructe ur de ve ctb :
La tâ ch e s pé cifiq ue de ve ctb s e lim ite ra à re ns e igne r les valeurs des m e m bres donnée de but e t fin.
Aucun de s tructe ur n'e s t né ce s s aire pour ve ctb, dans la m e s ure où son constructe ur n'alloue aucun autre
e m place m e nt dynam iq ue q ue ce lui alloué par ve ct.
En ce q ui conce rne l'opé rate ur [], on pe ut pe ns e r q ue ve ctb l'h é rite de ve ct e t q ue , par cons é q ue nt, iln'e s t
pas néce s s aire de le s urdé finir. Toute fois, la notation t[i] ne désigne plus forcé m e nt l'é lém e nt de rang i d'un
obje t de type ve ctb. O r, m anife s te m e nt, on souh aite ra q u'ile n aille toujours ainsi. Ilfaut donc redéfinir []
pour ve ctb, q uitte d'ailleurs à ré utiliser l'opé rate ur dé fini dans ve ct.
Voici ce q ue pourrait ê tre notre clas s e ve ctb (ici, on ne trouve q u'une définition, dans la m e s ure où les deux
fonctions m e m bre ont é té définie s "e n ligne ") :
#include "vect.h"
class vectb : public vect
{ int debut, fin ;
public :
vectb ( int d, int f) : vect (f-d+1)
{ debut = d ; fin = f ;
}
int & operator [] (int i)
{ return vect::operator [] (i-debut) ;
}
}
R e m arques :
1) Si le m e m bre donnée adr avait é té déclaré proté gé (prote cte d) dans la clas s e ve ct, nous aurions pu
redéfinir l'opé rate ur [] de ve ctb, sans faire appe là ce lui de ve ct :
2) Aucune prote ction d'indice s n'e s t à pré voir dans ve ctb, dè s lors q u'e lle a déjà é té pré vue dans ve ct.
___________________________________________________________________________
Enoncé
Soit une clas s e int2d (te lle q ue ce lle cré é e d ans l'e xe rcice VII.8) pe rm e ttant de m anipuler de s "tableaux
dynam iq ue s " d'entie rs à deux dim e nsions dont la déclaration (fournie dans le fich ie r int2d.h ) s e pré s e nte
ainsi :
O n suppose que le constructe ur alloue e ffe ctive m e nt l'e m place m e nt né ce s s aire e t q ue l'opé rate ur [] pe ut ê tre
utilisé indifé re m m e nt dans une expression ou à gauch e d'une affe ctation.
Cré e r une clas s e int2db , dérivé e d e int2d, pe rm e ttant de m anipuler de s tableaux dynam iq ue s , dans les q ue ls
on pe ut fixe r les "borne s " (valeur m inim ale e t valeur m axim ale) des deux indices ;les q uatre valeurs
corre s pondante s s e ront fournie s e n argum e nts du constructe ur de int2db.
O n ne s e pré occupe ra pas, ici, de s problèm e s é ve ntue llem e nt pos é s par la re copie ou l'affe ctation d'obje ts de
type int2db.
___________________________________________________________________________
Sol
ution
Ilsuffit, e n fait, de généraliser à la clas s e int2d, le travailré alisé dans l'e xe rcice pré cédent pour la clas s e
ve ct. Voici ce q ue pourrait ê tre notre clas s e int2db (ici, on ne trouve q u'une définition, dans la m e s ure où les
fonctions m e m bre de int2db ont é té fournie s "e n ligne ") :
#include "int2d.h"
class int2db : public int2d
{ int ligdeb, ligfin ; // bornes (mini, maxi) premier indice
int coldeb, colfin ; // bornes (mini, maxi) second indice
public : // constructeur
int2db (int ld, int lf, int cd, int cf) : int2d (lf-ld+1, cf-cd+1)
{ ligdeb = ld ; ligfin = lf ;
coldeb = cd ; colfin = cf ;
}
int & int2db::operator () (int i, int j) // rédéfinition de operator ()
{ return int2d::operator () (i-ligdeb, j-coldeb) ;
}
} ;
Note z q ue , là non plus, aucune prote ction d'indice s upplém e ntaire n'e s t à pré voir dans int2db, dè s lors
q u'e lle a déjà é té pré vue dans int2d.
___________________________________________________________________________
Enoncé
Soit une clas s e ve ct pe rm e ttant de m anipuler de s "ve cte urs dynam iq ue s " d'entie rs, dont la déclaration
(fournie dans un fich ie r ve ct.h ) s e pré s e nte ainsi (note z la pré s e nce de m e m bre s "proté gé s ") :
144 Exercices en langage C+ +
class vect
{ protected : // en prévision d'une éventuelle classe dérivée
int nelem ; // nombre d'éléments
int * adr ; // adresse zone dynamique contenant les éléments
public :
vect (int) ; // constructeur
~vect () ; // destructeur
int & operator [] (int) ; // accès à un élément par son "indice"
} ;
O n suppose que le constructe ur alloue e ffe ctive m e nt l'e m place m e nt né ce s s aire e t q ue l'opé rate ur [] pe ut ê tre
utilisé indifé re m m e nt dans une expression ou à gauch e d'une affe ctation. En re vanch e , com m e on pe ut le
voir, ce tte clas s e n'a pas prévu de constructe ur par re copie e t e lle n'a pas surdé fini l'opé rate ur d'affe ctation.
L'affe ctation e t la transm ission par valeur d'obje ts de type ve ct pos e nt donc les "problèm e s h abitue ls".
Cré e r une clas s e ve ctok , dérivé e d e ve ct, de m aniè re à ce q ue l'affe ctation e t la transm ission par valeur
d'obje ts de type ve ctok se déroule conve nablem e nt. Pour facilite r l'utilisation de ce tte nouve lle clas s e ,
introduire une fonction m e m bre taille fournissant la dim e nsion d'un ve cte ur.
___________________________________________________________________________
Sol
ution
M anife s te m e nt, la clas s e ve ctok n'a pas besoin de définir de nouve aux m e m bres donnée. Pour dé clare r de s
obje ts de type ve ctok , il faudra pouvoir e n pré cis e r la dim e nsion, ce q ui signifie q ue ve ctok devra
absolum e nt disposer d'un constructe ur approprié . Ce dernie r s e conte nte ra toute fois de re transm e ttre la
valeur re çue e n argum e nt au constructe ur de ve ct ;ilaura donc un corps vide. Note z q u'iln'e s t pas
né ce s s aire de pré voir un de s tructe ur (car ce lui de ve ct s e ra appe lé e n cas de destruction d'un obje t de type
ve ctok e t iln'y a rie n de plus à faire ).
Note z q ue pour gé re r conve nablem e nt la re copie ou l'affe ctation d'obje ts, nous nous conte nte rons de la
m é th ode déjà re ncontré e q ui consiste à dupliq ue r les obje ts conce rné s (e n e n faisant une "copie profonde ").
Pour satisfaire aux contraintes de l'é noncé , ilnous faut donc pré voir de définir, dans ve ctok , un constructe ur
par re copie . Ilfaut alors te nir com pte de ce q ue la re copie d'un obje t de type ve ctok (q ui fe ra donc appe là
ce constructe ur) e ntraî ne ra alors l'appe l du constructe ur de ve ct q ui s e ra indiq ué dans l'e n-tê te du
constructe ur par re copie de ve ctok , du m oins si une te lle inform ation e s t pré cis é e (dans le cas contraire , ily
aurait appe ld'un constructe ur sans argum e nt de ve ct, ce q ui n'e s t pas possible ici). Ici, nous disposons donc
de deux possibilité s :
- dem ande r l'appe ldu constructe ur par re copie de ve ct, ce q ui conduit à ce t e n-tê te :
vectok::vectok (vectok & v) : vect (v)
(rappe lons q ue l'argum e nt v, de type ve ctok , s e ra im plicite m e nt conve rti e n type ve ct, pour pouvoir ê tre
transm is au constructe ur ve ct).
Ce tte façon de faire conduit à la cré ation d'un obje t de type ve ct, obte nu par re copie (par dé faut) de v. Il
faudra alors com pléte r le travaile n cré ant un nouve le m place m e nt dynam iq ue pour le ve cte ur e t e n
adaptant corre cte m e nt la valeur de adr.
- dem ande r l'appe ldu constructe ur à un argum e nt de ve ct, ce q ui conduit à ce t e n-tê te :
vectok::vectok (vectok & v) : vect (v.nelem)
IX. La tech nique de l'h é ritage 145
Ce tte fois, ily aura cré ation d'un nouve lobje t de type ve ct, ave c son propre e m place m e nt dynam iq ue ,
dans leq ue l il faudra né anm oins re copie r les valeurs de v. C'e s t ce tte derniè re s olution q ue nous
ch oisirons ici.
Toujours pour satisfaire aux contraintes de l'é noncé , nous devons surdé finir l'affe ctation dans la clas s e
ve ctok . Ici, aucun ch oix ne s e pré s e nte . Nous utiliserons l'algorith m e pré s e nté dans l'e xe rcice VII.4.
R e m arque :
Voici, à titre indicatif, ce q ue s e rait la définition du constructe ur par re copie de ve ctok , dans le cas où
l'on fe rait appe lau constructe ur par re copie (par dé faut) de ve ct :
vectok::vectok (vectok & v) : vect (v)
{ nelem = v.nelem ;
adr = new int [nelem] ;
int i ;
for (i=0 ; i<nelem ; i++)
adr[i] = v.adr[i] ;
}
Ici, ila fallu alloue r un nouve le m place m e nt pour un ve cte ur, ce q ui n'é tait pas le cas lors q ue l'on faisait
appe lau constructe ur à un argum e nt de ve ct (puis q ue ce dernie r faisait déjà une te lle allocation).
#include <iostream.h>
146 Exercices en langage C+ +
#include "vectok.h"
main()
{ void fct (vectok) ;
vectok v(6) ;
int i ;
for (i=0 ; i<v.taille() ; i++) v[i] = i ;
cout << "vecteur v : " ;
for (i=0 ; i<v.taille() ; i++) cout << v[i] << " " ;
cout << "\n" ;
vectok w(3) ;
w = v ;
cout << "vecteur w : " ;
for (i=0 ; i<w.taille() ; i++) cout << w[i] << " " ;
cout << "\n" ;
fct (v) ;
}
void fct (vectok v)
{ cout << "vecteur reçu par fct : " << "\n" ;
int i ;
for (i=0 ; i<v.taille() ; i++) cout << v[i] << " " ;
}
CH A PITRE X :
L'H ERITAGE M ULTIPLE
RAPPELS
D e puis la ve rsion 2.0, C+ + autoris e l'h é ritage m ultiple : une clas s e pe ut h é rite r de plusieurs autre s clas s e s ,
com m e dans ce t e xe m ple où la clas s e pointcolh é rite s im ultané m e nt des clas s e s point e t coul ;
class pointcol : public point, public coul // chaque dérivation, ici publique
// pourrait être privée
{ // définition des membres supplémentaires (données ou fonctions)
// ou redéfinition de membres existants déjà dans point ou coul
} ;
Ch acune des dérivations peut ê tre publiq ue ou privé e . Le s m odalités d'accè s aux m e m bres de ch acune des
classes de bas e re s te nt les m ê m e s q ue dans le cas d'une dérivation "sim ple". L'opé rate ur de ré s olution de
porté e (::) pe ut ê tre utilisé :
- soit lors q ue l'on ve ut accéder à un m e m bre d'une des classes de bas e , alors q u'ile s t redéfini dans la
classe dérivé e ,
- soit lors q ue deux classes de bas e possè dent un m e m bre de m ê m e nom e t q u'ilfaut alors préciser celui
q ui nous inté re s s e .
Le constructe ur de la classe dérivé e pe ut m e ntionne r, dans son e n-tê te , des inform ations à re transm e ttre à
ch acun de s constructe urs des classes de bas e (ce s e ra gé né ralem e nt indispensable, sauf si une classe de bas e
possè de un constructe ur sans argum e nt ou si elle ne dispose pas du tout de constructe ur). En voici un
e xe m ple :
Cl
as s e s virtue l
les
Par le biais de dérivations succe s s ive s , ile s t tout à fait possible q u'une clas s e h é rite "deux fois" d'une m ê m e
clas s e . En voici un e xe m ple dans leq ue lD h é rite deux fois de A :
class B : public A
{ ..... } ;
class C : public A
{ ..... } ;
class D : public B, public C
{ ..... } ;
D ans ce cas, les m e m bres donnée de la clas s e e n q ue s tion (A dans notre e xe m ple) apparais s e nt deux fois
dans la classe dérivée de deuxiè m e nive au (ici D ). Nature llem e nt, il e s t né ce s s aire de faire appe l à
l'opé rate ur de ré s olution de porté e (::) pour leve r l'am biguïté . Si l'on souh aite q ue de te ls m e m bre s
n'apparais s e nt q u'une s e ule fois dans la classe dérivée de deuxiè m e nive au, ilfaut, dans les déclarations des
dérivé e s d e pre m ie r nive au (ici B e t C) déclare r ave c l'attribut virtualla classe dont on ve ut é vite r la
duplication (ici A).
Voici com m e nt on procéderait dans l'e xe m ple pré cédent (le m ot virtualpe ut ê tre indiffé re m m e nt placé avant
ou aprè s le m ot public ou le m ot private ) :
Lors q ue l'on a dé claré ainsi une "clas s e virtue lle", ile s t né ce s s aire q ue les constructe urs d'éve ntue lles
classes dérivé e s puis s e nt pré cis e r les inform ations à transm e ttre au constructe ur de ce tte clas s e virtue lle
(dans le cas "usuel" où l'on autoris e la duplication, ce problèm e ne s e pos e plus ;e n e ffe t, ch aq ue
constructe ur transm e t les inform ations aux clas s e s asce ndantes dont les constructe urs transm e tte nt, à leur
tour, les inform ations aux constructe urs de ch acune des "occurre nce s " de la clas s e e n q ue s tion - ce s
inform ations pouvant é ve ntue llem e nt ê tre diffé re nte s ). Dans ce cas, on le pré cise dans l'e n-tê te du
constructe ur de la classe dérivé e , e n plus des argum e nts destiné s aux constructe urs des classes du nive au
im m édiate m e nt supérieur, com m e dans ce t e xe m ple :
D e plus, dans ce cas, les constructe urs des clas s e s B e t C (q ui ont déclaré q ue A é tait "virtue lle") n'auront
plus à spécifie r d'inform ations pour un constructe ur de A
Enfin, le constructe ur d'une clas s e virtue lle e s t toujours appe lé avant les autre s .
___________________________________________________________________________
X. L'h é ritage m ultiple 155
Enoncé
#include <iostream.h>
class A
{ int n ;
float x ;
public :
A (int p = 2)
{ n = p ; x = 1 ;
cout << "** construction objet A : " << n << " " << x << "\n" ;
}
} ;
class B
{ int n ;
float y ;
public :
B (float v = 0.0)
{ n = 1 ; y = v ;
cout << "** construction objet B : " << n << " " << y << "\n" ;
}
} ;
main()
{ C c1 ;
C c2 (10, 11, 12, 5.0) ;
}
___________________________________________________________________________
Sol
ution
L'obje t c1 e s t cré é par appe lsucce s s if des constructe urs de B, puis de A (ordre im pos é par la déclaration de
la clas s e C, e t non par l'e n-tê te du constructe ur de C!). Le je u de la transm ission des argum e nts e t des
argum e nts par dé faut conduit au ré s ultat suivant :
** construction objet B : 1 0
** construction objet A : 1 1
** construction objet C : 3 3
** construction objet B : 1 5
** construction objet A : 10 1
156 Exercices en langage C+ +
** construction objet C : 12 21
___________________________________________________________________________
Enoncé
M ê m e q ue s tion q ue pré cédem m e nt, e n re m plaçant sim plem e nt l'e n-tê te du constructe ur de C par :
___________________________________________________________________________
Sol
ution
Ici, com m e le constructe ur de C n'a pré vu aucun argum e nt pour un é ve ntue lconstructe ur de A , ily aura
appe ld'un constructe ur sans argum e nt, c'e s t-à -dire , e n fait, appe ldu constructe ur de A , ave c toute s les
valeurs prévue s par dé faut. Voici le ré s ultat obte nu :
** construction objet B : 1 0
** construction objet A : 2 1
** construction objet C : 3 3
** construction objet B : 1 5
** construction objet A : 2 1
** construction objet C : 12 21
___________________________________________________________________________
Enoncé
M ê m e q ue s tion q ue dans l'e xe rcice X.1, en supposant q ue l'e n-tê te du constructe ur de C e s t la suivante :
_______________________________________________________________
Sol
ution
** construction objet B : 1 0
** construction objet A : 2 1
** construction objet C : 3 3
** construction objet B : 1 0
** construction objet A : 2 1
** construction objet C : 12 21
X. L'h é ritage m ultiple 157
___________________________________________________________________________
Enoncé
#include <iostream.h>
class A
{ int na ;
public :
A (int nn=1)
{ na = nn ;
cout << "$$construction objet A " << na << "\n" ;
}
} ;
class B : public A
{ float xb ;
public :
B (float xx=0.0)
{ xb = xx ;
cout << "$$construction objet B " << xb << "\n" ;
}
} ;
class C : public A
{ int nc ;
public :
C (int nn= 2) : A (2*nn+1)
{ nc = nn ;
cout << "$$construction objet C " << nc << "\n" ;
}
} ;
main()
{ D d (10, 20, 5.0) ;
}
___________________________________________________________________________
158 Exercices en langage C+ +
Sol
ution
La construction d'un obje t de type D e ntraî ne ra l'appe ldes constructe urs de B e t de C, les q ue ls, avant leur
e xé cution, appe lleront ch acun un constructe ur de A : dans le cas de B, ily aura appe ld'un constructe ur sans
argum e nt (puis q ue l'e n-tê te de B ne m e ntionne pas de liste d'argum e nts pour A) ;e n re vanch e , dans le cas
de C, ils'agira (plus classiquem e nt) d'un constructe ur à un argum e nt, com m e m e ntionné dans l'e n-tê te de
C).
Note z bie n q u'ily a cré ation de deux obje ts de type A . Voici les ré s ultats obte nus :
$$construction objet A 1
$$construction objet B 5
$$construction objet A 21
$$construction objet C 10
$$construction objet D 20
___________________________________________________________________________
Enoncé
Transform e r le program m e pré cédent, de m aniè re à ce q u'un obje t de type D ne contie nne q u'une s e ule fois
les m e m bres de A (q ui s e réduisent e n fait à l'e ntie r na). O n s'arrange ra pour q ue le constructe ur de A s oit
appe lé ave c la valeur 2*nn+ 1, dans laq ue lle nn désigne l'argum e nt du constructe ur de C.
___________________________________________________________________________
Sol
ution
A titre indicatif, voici les ré s usltats q ue fournirait le program m e pré cédent ainsi transform é :
$$construction objet A 21
$$construction objet B 5
X. L'h é ritage m ultiple 159
$$construction objet C 10
$$construction objet D 20
___________________________________________________________________________
Enoncé
La fonction ajoute devra ajoute r, e n début de liste , un é lém e nt pointant sur l'inform ation dont l'adre s s e e s t
fournie e n argum e nt (void *). Pour "e xplore r" la liste , on a pré vu trois fonctions :
- pre m ie r, q ui fournira l'adresse de l'inform ation associé e au pre m ie r noe ud de la liste e t q ui, e n m ê m e
te m ps, pré pare ra le proce s s us de parcours de la liste ,
160 Exercices en langage C+ +
- proch ain, q ui fournira l'adresse de l'inform ation associé e au "proch ain noe ud" ;des appe ls succe s s ifs
de proch ain devront pe rm e ttre de parcourir la liste (sans q u'il soit né ce s s aire d'appe ler une autre
fonction),
- fini, q ui pe rm e ttra de s avoir si la fin de liste e s t atte inte ou non.
1) Com pléte r la déclaration pré cédente de la clas s e liste e t e n fournir la définition de m aniè re à ce q u'e lle
fonctionne com m e dem andé .
class point
{ int x, y ;
public :
point (int abs=0, int ord=0) { x=abs ; y=ord ; }
void affiche () { cout << "Coordonnées : " << x << " " << y << "\n" ; }
} ;
Cré e r une clas s e liste _points, dérivé e à la fois de liste e t de point, pour q u'e lle puis s e pe rm e ttre de
m anipuler de s liste s ch aîné e s d e points, c'e s t-à -dire des liste s com parables à ce lles pré s e nté e s ci-de s s us, e t
dans les q ue lles l'inform ation associé e e s t de type point. O n de vra pouvoir, notam m e nt :
_______________________________________________________________
Sol
ution
1) M anife s te m e nt, les fonctions pre m ie r e t proch ain né ce s s ite nt un "pointe ur sur un é lém e nt courant". Ils e ra
m e m bre donnée de la clas s e liste .
Nous convie ndrons (classiquem e nt) q ue la fin de liste e s t m até rialisée par un noe ud com portant un pointe ur
"nul". La clas s e liste devra disposer d'un constructe ur dont le rôle s e lim ite ra à l'initialiser à une "liste
vide", ce q ui s'obtie ndra sim plem e nt e n plaçant un pointe ur nulcom m e adresse de début de liste (ce tte façon
de procéder sim plifie grande m e nt l'algorith m e d'ajout d'un é lém e nt e n début de liste , puis q u'e lle é vite
d'avoir à distingue r de s autre s le cas de la liste vide).
Com m e un obje t de type liste e s t am e né à cré e r diffé re nts e m place m e nts dynam iq ue s , ile s t né ce s s aire de
pré voir la libération de ce s e m place m e nts lors q ue l'obje t e s t détruit. Ilfaudra donc pré voir un de s tructe ur,
ch argé de détruire les diffé re nts noeuds de la liste . A ce propos, note z q u'iln'e s t pas possible ici de
dem ande r au de s tructe ur de détruire é galem e nt les inform ations associé e s ;e n e ffe t, d'une part, ce n'e s t pas
l'obje t de type liste q ui a alloué ce s e m place m e nts : ils sont sous la re s ponsabilité de l'utilisate ur de la clas s e
liste .
Voici ce q ue pourraî
t ê tre notre clas s e liste com plète :
{ debut = NULL ;
courant = debut ; // par sécurité
}
~liste () ; // destructeur
void ajoute (void *) ; // ajoute un élément en début de liste
void * premier () // positionne sur premier élément
{ courant = debut ; return (courant->contenu) ; }
void * prochain () // positionne sur prochain élément
{ if (courant != NULL) courant = courant->suivant ;
return (courant->contenu) ;
}
int fini () { return (courant == NULL) ; }
} ;
liste::~liste ()
{ element * suiv ;
courant = debut ;
while (courant != NULL )
{ suiv = courant->suivant ; delete courant ; courant = suiv ; }
}
void liste::ajoute (void * chose)
{ element * adel = new element ;
adel->suivant = debut ;
adel->contenu = chose ;
debut = adel ;
}
2) Com m e nous le dem ande l'é noncé , nous allons donc cré e r une clas s e liste _points par :
Note z q ue ce t h é ritage , appare m m e nt nature l, conduit né anm oins à introduire , dans la clas s e liste _points,
deux m e m bres donnée (x et y) n'ayant aucun inté rê t par la suite .
En re vanch e , la cré ation de s fonctions m e m bre dem andé e s d e vie nt e xtrê m e m e nt sim ple. En e ffe t, la fonction
d'insertion d'un point e n début de liste pe ut ê tre la fonction ajoute de la clas s e liste : nous n'aurons donc
m ê m e pas besoin de la surdéfinir. En ce q ui conce rne la fonction d'affich age de tous les points de la liste
(q ue nous nom m e rons é galem e nt affich e ), illui suffira de faire appe l:
- aux fonctions pre m ie r, proch ain e t fini de la clas s e liste pour le parcours de la liste de points,
- à la fonction affich e de la clas s e point pour l'affich age d'un point.
Nous aboutissons à ce ci :
#include "listepts.h"
main()
{ liste_points l ;
point a(2,3), b(5,9), c(0,8) ;
162 Exercices en langage C+ +
Coordonnées : 2 3
---------
Coordonnées : 5 9
Coordonnées : 2 3
---------
Coordonnées : 0 8
Coordonnées : 5 9
Coordonnées : 2 3
---------
CH A PITRE XI :
LES FO NCTIO NS VIRTUELLES
RAPPELS
Le s rè gles de com patibilité e ntre une classe de bas e e t une classe dérivé e pe rm e tte nt d'affe cte r à un pointe ur
sur une classe de bas e la valeur d'un pointe ur sur une classe dérivé e . Toute fois, par dé faut, le type des obje ts
pointé s e s t défini lors de la com pilation. Par e xe m ple, ave c :
A * pta ;
B * ptb ;
une affe ctation te lle q ue pta = ptb e s t autoris é e . Né anm oins, q ue lq ue s oit le conte nu de pta (autre m e nt dit,
q ue lq ue s oit l'obje t pointé par pta), pta-> fct(...) appel le toujours l a fonction fctde l a cl
asse A.
Le s fonctions virtue l
les
L'e m ploi de s fonctions virtue lles pe rm e t d'évite r les problèm e s inh é re nts au typage s tatiq ue . Lors q u'une
fonction e s t déclaré e virtue lle (m ot clé virtual ) dans une clas s e , les appe ls à une te lle fonction ou à
n'im porte laq ue lle de ses redéfinitions dans des classes dérivé e s s ont "ré s olus" au m om e nt de l'e xé cution, e n
fonction du type de l'obje t conce rné . O n parle de typage dynam iq ue des obje ts (ou de ligature dynam iq ue
des fonctions). Par e xe m ple, ave c :
A * pta ;
B * ptb ;
l'instruction pta-> fct (...) appe llera la fonction fct de la clas s e corre s pondant ré e llem e nt au type de l'obje t
pointé par pta.
N.B. : ilpe ut y avoir ligature dynam iq ue , m ê m e e n de h ors de l'utilisation de pointe urs (voye z, par e xe m ple,
l'e xe rcice XI.2).
Rè gl
es
- le m ot clé virtualne s 'e m ploie q u'une fois pour une fonction donné e ;plus précisém e nt, ilne doit pas
accom pagne r les redéfinitions de ce tte fonction dans les classes dérivé e s ,
- une m é th ode déclaré e virtue lle dans une classe de bas e pe ut ne pas ê tre redéfinie dans s e s clas s e s
dérivé e s ,
- une fonction virtue lle pe ut ê tre s urdé finie (ch aq ue fonction surdéfinie pouvant ê tre ou ne pas ê tre
virtue lle),
- un constructe ur ne pe ut pas ê tre virtue l, un de s tructe ur pe ut l'ê tre .
- de par sa nature m ê m e , le m é canism e de ligature dynam iq ue e s t lim ité à une h ié rarch ie de clas s e s ;
souve nt, pour q u'ilpuis s e s 'appliq ue r à toute une biblioth è q ue de clas s e s , on s e ra am e né à faire h é rite r
toute s les classes de la biblioth è q ue d'une m ê m e classe de bas e .
Le s fonctions virtue l
le s pure s
Une "fonction virtue lle pure " se déclare ave c une initialisation à zé ro, com m e dans :
Lors q u'une clas s e com porte au m oins une fonction virtue lle pure , e lle e s t considérée com m e "abstraite ",
c'e s t-à -dire q u'iln'e s t plus possible de cré e r de s obje ts de son type .
Une fonction dé claré e virtue lle pure dans une classe de base doit obligatoire m e nt ê tre redéfinie dans une
classe dérivé e ou dé claré e à nouve au virtue lle pure (depuis la ve rsion 3.0 de C+ + , une fonction virtue lle
pure pe ut ne pas ê tre déclarée dans une classe dérivé e e t, dans ce cas, e lle e s t à nouve au im plicite m e nt
fonction virtue lle pure de ce tte classe dérivé e ).
___________________________________________________________________________
Enoncé
#include <iostream.h>
class point
{ protected : // pour que x et y soient accessibles à pointcol
int x, y ;
XI. Les fonctions virtuelles 169
public :
point (int abs=0, int ord=0) { x=abs ; y=ord ; }
virtual void affiche ()
{ cout << "Je suis un point \n" ;
cout << " mes coordonnées sont : " << x << " " << y << "\n" ;
}
} ;
_______________________________________________________________
Sol
ution
D ans les instructions 1, adp (de type point *) pointe s ur un obje t de type point, tandis q ue adpc (de type
pointcol*) pointe s ur un obje t de type pointcol. Ily a appe lde la fonction affich e , re s pe ctive m e nt de point e t
de pointcol;l'e xiste nce du "typage dynam iq ue " n'apparaî t pas claire m e nt puis q ue , m ê m e e n son absence
(c'e s t-à -dire s i la fonction affich e n'avait pas é té déclaré e virtue lle), on aurait obte nu le m ê m e ré s ultat.
En re vanch e , dans les instructions 2, adp (de type point *) pointe m ainte nant sur un obje t de type pointcol.
Grâ ce au typage dynam iq ue , adp-> affich e () appe lle bien la fonction affich e du type pointcol.
Je suis un point
mes coordonnées sont : 3 5
Je suis un point coloré
mes coordonnées sont : 8 6 et ma couleur est : 2
-----------------
Je suis un point coloré
mes coordonnées sont : 8 6 et ma couleur est : 2
Je suis un point coloré
mes coordonnées sont : 8 6 et ma couleur est : 2
___________________________________________________________________________
Enoncé
#include <iostream.h>
class point
{ int x, y ;
public :
point (int abs=0, int ord=0) { x=abs ; y=ord ; }
virtual void identifie ()
{ cout << "Je suis un point \n" ;
}
void affiche ()
{ identifie () ;
cout << "Mes coordonnées sont : " << x << " " << y << "\n" ;
}
} ;
main()
{ point p(3,4) ;
pointcol pc(5,9,5) ;
p.affiche () ;
pc.affiche () ;
cout << "---------------\n" ;
point * adp = &p ;
pointcol * adpc = &pc ;
adp->affiche () ; adpc->affiche () ;
cout << "---------------\n" ;
adp = adpc ;
adp->affiche () ; adpc->affiche () ;
}
_______________________________________________________________
Sol
ution
D ans la fonction affich e de point, l'appe lde ide ntifie fait l'obje t d'une ligature dynam iq ue (puis q ue ce tte
derniè re fonction a é té déclaré e virtue lle). Lors q u'un obje t de type pointcolappe lle une fonction affich e , ce
s e ra bie n la fonction affich e de point q ui s e ra appe lée (puis q ue affich e n'e s t pas redéfinie dans pointcol).
M ais ce tte derniè re fe ra appe l, dans ce cas, à la fonction ide ntifie de pointcol. Bie n e nte ndu, lors q u'un obje t
XI. Les fonctions virtuelles 171
de type point appe lle affich e , ce tte derniè re fe ra toujours appe là la fonction ide ntifie de point (le m ê m e
ré s ultat s e rait obte nu sans ligature dynam iq ue ).
Je suis un point
Mes coordonnées sont : 3 4
Je suis un point coloré de couleur : 5
Mes coordonnées sont : 5 9
---------------
Je suis un point
Mes coordonnées sont : 3 4
Je suis un point coloré de couleur : 5
Mes coordonnées sont : 5 9
---------------
Je suis un point coloré de couleur : 5
Mes coordonnées sont : 5 9
Je suis un point coloré de couleur : 5
Mes coordonnées sont : 5 9
___________________________________________________________________________
Enoncé
O n souh aite cré e r une clas s e nom m é e e ns_h e te r pe rm e ttant de m anipuler de s e ns e m bles dont le type des
é lém e nts e s t non s e ulem e nt inconnu de la clas s e , m ais é galem e nt susce ptible de varie r d'un é lém e nt à un
autre . Pour q ue la ch os e s oit possible, on im pos e ra sim plem e nt la contrainte s uivante : tous les type s
conce rnés devront dérive r d'un m ê m e type de bas e nom m é base . Le type base s e ra supposé connu au
m om e nt où l'on dé finit la clas s e e ns_h e te r.
La clas s e base disposera au m oins d'une fonction virtue lle pure nom m é e affich e ;ce tte fonction de vra ê tre
redéfinie dans les classes dérivé e s pour affich e r les caracté ristiq ues de l'obje t conce rné .
- ajoute pour ajoute r un nouve lé lém e nt à l'e ns e m ble (e lle devra s'assure r q u'iln'e xiste pas déjà ),
- appartie nt pour te s te r l'apparte nance d'un élém e nt à l'e ns e m ble,
- cardinalq ui fournira le nom bre d'élém e nts de l'e ns e m ble.
D e plus, la classe devra ê tre m unie d'un "ité rate ur", c'e s t-à -dire d'un m é canism e pe rm e ttant de parcourir les
diffé re nts é lém e nts de l'e ns e m ble. O n pré voira 3 fonctions :
O n ré alisera ensuite un pe tit program m e d'essai de la clas s e e ns_h e te r, e n cré ant un e ns e m ble com portant
des obje ts de type point (deux coordonné e s e ntiè re s ) e t com plexe (une partie ré e lle e t une partie im aginaire ,
toutes deux de type float). Nature llem e nt, point e t com plexe devront dérive r de base .
172 Exercices en langage C+ +
O n ne s e pré occupe ra pas des éve ntue ls problèm e s pos é s par l'affe ctation ou la transm ission par valeur
d'obje ts du type e ns_h e te r.
_______________________________________________________________
Sol
ution
M anife s te m e nt, la clas s e e ns_h e te r ne contie ndra pas les obje ts corre s pondants aux é lém e nts de l'e ns e m ble,
m ais s e ulem e nt des pointe urs sur ces diffé re nts é lém e nts. A m oins de fixe r le nom bre m axim ald'élém e nts a
priori, ile s t né ce s s aire de cons e rve r ce s pointe urs dans un em place m e nt alloué dynam iq ue m e nt par le
constructe ur ;ce dernie r com porte ra donc un argum e nt pe rm e ttant de préciser le nom bre m axim al
d'élém e nts re q uis pour l'e ns e m ble.
/* fichier ensheter.H */
/* déclaration de la classe ens_heter */
class base ;
class ens_heter
{ int nmax ; // nombre maximal d'éléments
int nelem ; // nombre courant d'éléments
base * * adel ; // adresse zone de pointeurs sur les objets éléments
int courant ; // numéro d'élément courant (pour l'itération)
public :
ens_heter (int=20) ; // constructeur
~ens_heter () ; // destructeur
void ajoute (base &) ; // ajout d'un élément
int appartient (base &) ; // appartenance d'un élément
int cardinal () ; // cardinal de l'ensemble
void init () ; // initialisation itération
base & suivant () ; // passage élément suivant
int existe () ; //
void liste () ; // affiche les "valeurs" des différents éléments
} ;
class base
{ public :
virtual void affiche () = 0 ;
XI. Les fonctions virtuelles 173
} ;
Voici la définition de s fonctions m e m bre de e ns_h e te r (note z q ue leur com pilation né ce s s ite la déclaration
(définition) pré cédente de la clas s e base (e t non pas s e ulem e nt une s im ple indication de la form e class
base ) :
void ens_heter::init ()
{ courant = 0 ;
}
int ens_heter::existe ()
{ return (courant<nelem) ;
}
void ens_heter::liste ()
{ init () ;
while ( existe () )
suivant () . affiche () ;
}
Voici un pe tit program m e q ui dé finit deux clas s e s point e t com plexe , dérivé e s d e base e t q ui cré e un pe tit
e ns e m ble h é té rogè ne (sa com pilation né ce s s ite la déclaration de la clas s e base ). A la suite figure le ré s ultat
de son exécution :
#include <iostream.h>
#include "ens_heter.h"
#include "base.h"
174 Exercices en langage C+ +
Re m arq ue :
Si l'on ch e rch ait à ré s oudre les problèm e s pos é s par l'affe ctation e t la transm ission par valeur d'obje ts de
type e ns_h e te r, on s e rait am e né à ch oisir entre deux m é th ode s :
- e ffe ctue r de s "copie s com plète s " (on dit souve nt "profonde s ") de l'obje t, c'e s t-à -dire e n te nant
com pte , non s e ulem e nt de ses m e m bre s , m ais aussi de ses parties dynam iq ue s ,
- utiliser un "com pte ur de ré fé re nce " associé à ch aq ue partie dynam iq ue de façon à e n é vite r la
duplication.
O r, la pre m iè re m é th ode conduirait e ffe ctive m e nt à re copie r la partie dynam iq ue de l'e ns e m ble, c'e s t-à -
dire le tableau de pointe urs d'adre s s e base *. M ais, q ue faudrait-ilfaire pour les é lém e nts e ux-m ê m e s
(pointé s par les diffé re nte s valeurs du dit tableau)? M ê m e s i l'on adm e t q u'il faut é galem e nt les
dupliq ue r, iln'e s t pas possible de le faire s ans connaî tre la "structure " des obje ts conce rné s , à m oins
d'im pos e r à ch aq ue obje t de disposer de fonctions approprié e s pe rm e ttant d'en réaliser une copie
profonde .
La deuxiè m e m é th ode pose des problèm e s voisins en ce qui concerne le traite m e nt à appliq ue r aux obje ts
q ui sont des élém e nts d'un ensem ble de type e ns_h e te r. Disposent-ils d'un com pte ur de ré fé re nce ?Si oui,
com m e nt le m e ttre à jour?
D 'une m aniè re gé né rale, vous voye z q ue ce t e xe m ple, par les q ue s tions q u'ilsoulève , plaide large m e nt
e n fave ur de la constitution de biblioth è q ues d'obje ts, dans les q ue lles s ont définie s , a priori, un ce rtain
nom bre de rè gles com m une s . Parm i ce s rè gles , on pourrait notam m e nt im pos e r à ch aq ue obje t de
posséder une fonction (de nom uniq ue ) e n assurant la copie profonde ;nature llem e nt, pour pouvoir
l'utiliser correcte m e nt, ilfaudrait q ue la ligature dynam iq ue s oit possible, c'e s t-à -dire q ue tous les obje ts
conce rnés dérive nt d'un m ê m e obje t de bas e .
CH A PITRE XII :
LES FLO TS D 'ENTREE ET D E SO RTIE
RAPPELS
Un flot e s t un "canalre ce vant (flot "d'entré e ") ou fournissant (flot de "sortie ") de l'inform ation. Ce canal
e s t associé à un pé riph é riq ue ou à un fich ie r. Un flot d'entré e e s t un obje t de type istre am tandis q u'un flot
de sortie e s t un obje t de type ostre am . Le flot cout e s t un flot de sortie prédéfini, conne cté à la sortie
standard stdout ;de m ê m e , le flot cin e s t un flot d'entré e prédéfini, conne cté à l'e ntré e s tandard stdin. Le s
inform ations fournie s ici sont valables à partir de la ve rsion 2.0.
La cl
as s e os tre am
Elle s urdé finit l'opé rate ur < < s ous la form e d'une fonction m e m bre :
L'e xpre s s ion corre s pondant à son deuxiè m e opé rande pe ut ê tre d'un type de bas e q ue lconq ue , y com pris
ch ar, ch ar *(on obtie nt la ch aî ne pointé e ) ou un pointe ur sur un type q ue lconq ue autre q ue ch ar (on obtie nt
la valeur du pointe ur) ;pour obte nir la valeur de l'adresse d'une ch aî ne , on la conve rtit artificie llem e nt e n
un pointe ur de type void *.
Fonctions m em bre :
La cl
as s e is tre am
Elle s urdé finit l'opé rate ur > > sous form e d'une fonction m e m bre :
Le type _de _base pe ut ê tre q ue lconq ue , pour pe u q u'ilne s 'agis s e pas d'un pointe ur (ch ar *e s t ce pe ndant
acce pté - ilcorre s pond à l'e ntrée d'une ch aîne de caractè re s , e t non d'une adre s s e ).
Le s "e s pace s _blancs" (e s pace , tabulation h orizontale \t ou ve rticale \v , fin de ligne \n, re tour ch ariot \r e t
ch ange m e nt de page \f) s e rve nt de "délim ite urs" (com m e dans scanf), y com pris pour les ch aî nes de
caractè re s .
Principal
es fonctions m em bres :
istream & get (ch ar & c) : e xtrait un caractè re du flot d'entré e e t le range dans c.
int get () : e xtrait un caractè re du flot d'entré e e t e n re nvoie la valeur (sous form e d'un entie r) ;fournit
EOF e n cas de fin de fich ie r.
istream & read (void *adr, int tail
le) lit taille caractè re s s ur le flot e t les range à partir de l'adre s s e
adr.
La cl
as s e ios tre am
Elle e s t dérivé e d e istre am e t ostre am . Elle pe rm e t de ré aliser des entré e s s ortie s "conve rsationne lles ".
A ch aq ue flot e s t associé un e ns e m ble de bits d'un entie r form ant le "statut d'erreur du flot".
La clas s e ios (dont dérive nt istre am e t ostre am ) définit les constante s s uivante s :
e ofbit : fin de fich ie r (le flot n'a plus de caractè res disponibles ),
failbit : la proch aine opé ration sur le flot ne pourra pas aboutir,
badbit : le flot e s t dans un état irré cupé rable,
goodbit (valant, e n fait 0) : aucune d e s e rrre urs précédente s .
Une opé ration sur le flot a ré ussi lors q ue l'un des bits goodbit ou e ofbit e s t activé . La proch aine opé ration
sur le flot ne pourra ré ussir que si goodbit e s t activé (m ais iln'e s t pas ce rtain q u'e lle ré ussisse !).
Lors q u'un flot e s t dans un état d'erreur, aucune opé ration ne pe ut aboutir tant q ue la condition d'e rre ur n'a
pas é té corrigé e e t q ue le bit d'erreur corre s pondant n'a pas é té re m is à zé ro (à l'aide de la fonction clear).
void clear (int i=0) donne la valeur i au statut d'erreur. Pour active r un s e ulbit (par e xe m ple badbit),
on procédera ainsi (flé tant un flot) :
fl.clear (ios::badbit | fl rdstate() ) ;
Surdéfinition de () et de !
O n surdéfinira < < e t > > pour une clas s e q ue lconq ue , sous form e de fonctions am ie s , e n utilisant ce s
"cane vas" :
istream & operator >> (istream & entree, type_de_base & objet)
{
// Lecture des informations correspondant aux différents membres de objet
// en utilisant les possibilités classiques de >> pour les types de base
// c'est-à-dire des instructions de la forme :
// entree >> ..... ;
return entree ;
}
A ch aq ue flot, e s t associé un "statut de form atage " constitué d'un m ot d'état e t de 3 valeurs num é riq ue s
(gabarit, pré cision et caractè re de re m plissage ).
_____________________________________________________________________________________
NOM DE CHAMP NOM DU BIT SIGNIFICATION
(s'il existe) (quand activé)
_____________________________________________________________________________________
ios::basefield ios::dec conversion décimale
1 Ici, l
a trans m ission peut s e faire par val
e ur ou par ré fé re nce.
182 Exercices en langage C+ +
_____________________________________________________________________________________
ios::showbase affichage indicateur de base (en sortie)
_____________________________________________________________________________________
ios::floatfield ios::scientific notation "scientifique"
A ction sur l
e s tatut de form atage
O n pe ut utiliser, soit des "m anipulate urs" q ui pe uve nt ê tre "sim ples " ou "param é triq ue s ", soit des fonctions
m e m bre .
a) Les m anipul
ateurs non param é triques
Le s principaux m anipulate urs non param é triq ue s s ont pré s e nté s e n page s uivante :
_____________________________________________________________________________________
b) Les m anipul
ateurs param é triques
_____________________________________________________________________________________
La fonction m e m bre s e e k g (dé place m e nt, origine ) pe rm e t d'agir sur le pointe ur de fich ie r
_____________________________________________________________________________________
___________________________________________________________________________
Enoncé
Ecrire un program m e q ui lit un nom bre ré e le t q ui e n affich e le carré s ur un "gabarit" m inim alde 12
caractè re s , de 22 façons diffé re nte s :
- e n "point fixe ", ave c un nom bre de décim ales variant de 0 à 10,
- e n notation scie ntifiq ue , ave c un nom bre de décim ales variant de 0 à 10.
D ans tous les cas, on affich e ra les ré s ulats ave c ce tte pré s e ntation :
___________________________________________________________________________
Sol
ution
Ilfaut donc active r, d'abord le bit fixe d, e nsuite le bit scie ntific du ch am p floatfie ld. Nous utiliserons la
fonction s e tf, m e m bre de la clas s e ios. Note z bie n q u'ilfaut é vite r d'é crire , par e xe m ple :
setf (ios::fixed) ;
ce q ui aurait pour e ffe t d'active r le bit fixe d, sans m odifie r les autres donc, e n particulie r, sans m odifie r les
autres bits du ch am p floatfie ld.
Le gabarit d'affich age e s t déte rm iné par le m anipulate ur s e tw . Note z q u'ilfaut transm e ttre ce m anipulate ur
au flot conce rné , juste avant d'affich e r l'inform ation voulue .
#include <iostream.h>
#include <iomanip.h> //pour les "manipulateurs paramétriques"
main()
{ float val, carre ;
cout << "donnez un nombre réel : " ;
cin >> val ;
carre = val*val ;
cout << "Voici son carré : \n" ;
int i ;
cout << " en notation point fixe : \n" ;
cout.setf (ios::fixed, ios::floatfield) ; // met à 1 le bit ios::fixed
// du champ ios::floatfield
for (i=0 ; i<10 ; i++)
cout << " précision de " << setw (2) << i << " chiffres : "
<< setprecision (i) << setw (12) << carre << "\n" ;
cout << " en notation scientifique : \n" ;
cout.setf (ios::scientific, ios::floatfield) ;
for (i=0 ; i<10 ; i++)
cout << " précision de " << setw (2) << i << " chiffres : "
<< setprecision (i) << setw (12) << carre << "\n" ;
}
XII. Les flots d'entré e e tde sortie 185
___________________________________________________________________________
Enoncé
class point
{ int x, y ;
public :
// fonctions membre
} ;
Surdé finir les opé rate urs < < e t > > , de m aniè re à ce q u'ilsoit possible de lire un point sur un flot
d'entré e ou d'é crire un point sur un flot de sortie . O n pré voira q u'un te lpoint soit re pré s e nté s ous la form e :
<entier, entier>
ave c é ve ntue llem e nt des séparate urs "e s pace s _blancs" supplém e ntaire s , de part e t d'autre des nom bre s
e ntie rs.
___________________________________________________________________________
Sol
ution
Nous devons donc surdéfinir les opé rate urs < < e t > > pour q u'ils puissent re ce voir, e n de uxiè m e
opé rande , un argum e nt de type point. Ilne pourra s'agir q ue de fonctions am ie s , dont les prototype s s e
pré s e nte ront ainsi :
L'é criture de ope rator < < ne pré s e nte pas de difficulté s particuliè re s : on s e conte nte d'écrire , sur le flot
conce rné , les coordonnées du point, accom pagnées des sym boles < e t > .
En re vanch e , l'é criture de ope rator > > né ce s s ite un pe u plus d'atte ntion. En e ffe t, ilfaut s'assure r q ue
l'inform ation s e pré s e nte bien sous la form e re q uis e e t, si ce n'e s t pas le cas, pré voir de donner au flot
conce rné l'é tat bad, afin q ue l'utilisate ur puis s e s avoir q ue l'opé ration s'e s t m al déroulée (e n te s tant
"nature llem e nt" l'é tat du flot).
Voici ce q ue pourrait ê tre la déclaration de notre clas s e (nous l'avons sim plem e nt m unie d'un contructe ur) e t
la définition des deux fonctions am ie s voulue s :
#include <iostream.h>
class point
{ int x, y ;
public :
point (int abs=0, int ord=0)
{ x = abs ; y = ord ; }
int abscisse () { return x ; }
friend ostream & operator << (ostream &, point) ;
friend istream & operator >> (istream &, point &) ;
186 Exercices en langage C+ +
} ;
A titre indicatif, voici un pe tit program m e d'essai, accom pagné d'un exem ple d'exécution :
main()
{ char ligne [121] ;
point a(2,3), b ;
cout << "point a : " << a << " point b : " << b << "\n" ;
do
{ cout << "donnez un point : " ;
if (cin >> a) cout << "merci pour le point : " << a << "\n" ;
else { cout << "** information incorrecte \n" ;
cin.clear () ;
cin.getline (ligne, 120, '\n') ;
}
}
while ( a.abscisse () ) ;
}
____________________________
___________________________________________________________________________
Enoncé
Ecrire un program m e q ui e nre gistre (sous form e "binaire ", e t non pas form até e ), dans un fich ie r de nom
fourni par l'utilisate ur, une s uite de nom bre s e ntie rs fournis sur l'e ntré e s tandard. O n convie ndra q ue
l'utilisate ur fournira la valeur 0 (q ui ne s e ra pas e nre gistrée dans le fich ie r) pour pré cis e r q u'iln'a plus
d'entie rs à e ntre r.
___________________________________________________________________________
Sol
ution
pe rm e t de cré e r un flot de nom sortie , de l'associe r au fich ie r dont le nom figure dans nom fich e t d'ouvrir ce
fich ie r e n é criture .
L'é criture dans le fich ie r e n q ue s tion s e fe ra par la fonction w rite , appliq ué e au flot sortie .
sortie.close () ;
}
Note z q ue if (!sortie ) e s t é q uivalent à if (!sortie .good()) e t q ue w h ile (n & & sortie ) e s t é q uivalent à w h ile (n
& & sortie .good()).
___________________________________________________________________________
Enoncé
Ecrire un program m e pe rm e ttant de liste r (sur la sortie s tandard) les e ntie rs conte nus dans un fich ie r te lq ue
ce lui cré é par l'e xe rcice pré cédent.
___________________________________________________________________________
Sol
ution
entree.close () ;
}
___________________________________________________________________________
XII. Les flots d'entré e e tde sortie 189
Enoncé
Ecrire un program m e pe rm e ttant à un utilisate ur de re trouve r, dans un fich ie r te lq ue ce lui créé dans
l'e xe rcice XII.3, les e ntie rs dont ilfournit le "rang". O n convie ndra q u'un rang é galà 0 signifie q ue
l'utilisate ur souh aite m e ttre fin au program m e .
___________________________________________________________________________
Sol
ution
RAPPELS
Introduite par la ve rsion 3, la notion de patron de fonctions perm e t de définir ce q u'on nom m e s ouve nt des
"fonctions génériques". Plus précisém e nt, à l'aide d'une uniq ue définition com portant des "param è tres de
type ", on dé crit toute une fam ille de fonctions ;le com pilate ur "fabriq ue " (on dit aussi instancie ) la ou les
fonctions néce s s aire s à la dem ande (on nom m e s ouve nt ce s instances des fonctions patron).
La ve rsion 3 lim ite les param è tres d'un patron de fonctions à des param è tres de type ;beaucoup
d'im plém e ntations toute fois acce pte nt é galem e nt des "param è tre s e xpre s s ion" e t ce tte possibilité s e ra
probablem e nt re connue de la future norm e ANSI de C+ + . Toute fois, pour plus de clarté , nous la
pré s e nte rons s é paré m e nt.
O n pré cis e les param è tre s (m ue ts) de type , e n faisant pré céder ch acun du m ot (re lative m e nt arbitraire ) class
sous la form e te m plate < class ..., class ..., ...> . La définition de la fonction e s t classique, h orm is le fait
q ue les param è tre s m ue ts de type pe uve nt ê tre e m ployé s n'im porte où un type e ffe ctif e s t pe rm is. Par
e xe m ple :
R e m arque :
T x (3) ;
e s t légale m ê m e s i T n'e s t pas un type clas s e ;dans ce dernie r cas, e lle e s t sim plem e nt é q uivalente à :
T x = 3 ;
A ch aq ue fois q u'on utilise une fonction ayant un nom de patron, le com pilate ur ch e rch e à utiliser ledit
patron pour cré e r (instancie r) une fonction adé q uate . Pour ce faire , ilch e rch e à ré aliser une correspondance
exacte des type s (aucune conve rsion, q u'ils'agisse de prom otion num é riq ue ou de conve rsion standard n'e s t
pe rm ise)1.
fct (n, adi, x) ; // instancie la fonction void fct (int, int *, float)
fct (n, adi, p) // instancie la fonction void fct (int, int *, int)
fct (x, adf, p) ; // instancie la fonction void fct (float, float *, int)
fct (c, adi, x) ; // erreur char et int * ne correspondent pas à T et T*
// ( pas de conversion)
fct (&n, &adi, x) ; // instancie la fonction void fct (int *, int * *, float)
fct (p, adp, n) ; // instancie la fonction void fct (point, point *, int)
D 'une m aniè re gé né rale, ile s t né ce s s aire q ue ch aque param è tre de type apparais s e au m oins une fois dans
l
'en-tê te du patron.
R e m arque :
La définition d'un patron de fonctions ne peut pas ê tre com pilée s e ule ;de toute façon, e lle doit ê tre
connue du com pilate ur pour q u'ilpuis s e instancie r la bonne fonction patron. En gé né ral, les définitions
de patrons de fonctions figure ront dans des fich ie rs d'exte nsion h , de façon à é vite r d'avoir à e n fournir
systé m atiq ue m e nt la liste .
Bie n q ue ce tte notion n'apparais s e pas dans la ve rsion 3, e lle s e ra trè s probablem e nt introduite dans la norm e
ANSI de C+ + . Un param è tre e xpre s s ion d'un patron de fonctions s e pré s e nte com m e un argum e nt usuelde
fonction ;iln'apparaî t pas dans la liste de param è tres de type (te m plate ) e t ildoit apparaî tre dans l'e n-tê te
du patron. Par e xe m ple :
1 - A priori, m ê m e, l
a ve rsion 3 n'autoris e m ê m e pas l
e s conve rsions dite s trivial
es (te l
le s q ue T e n T& ou, m ie ux, T[] e n T*) ;toute fois,
ile s t probabl
e q ue ce l
les-ci s e ront acce pté e s par l
a norm e ANSI de C+ + (au m ê m e titre q u'e l
le s s ont acce ptées dans l
e s corre s pondance s
e xacte s pour l
e s fonctions s urdéfinie s ).
XIII. Les patrons de fonctions 19 7
Lors de l'instanciation d'une fonction patron, ile s t né ce s s aire de ré aliser une correspondance exacte s ur les
param è tre s e xpre s s ion (aucune conve rsion n'e s t possible, contraire m e nt à ce q ui s e produit dans le cas de
surdéfinition de fonction).
O n pe ut définir plusieurs patrons de m ê m e nom , possédant des param è tre s (de type ou e xpre s s ion) diffé re nts.
La s e ule rè gle à re s pe cte r dans ce cas e s t q ue l'appe ld'une fonction de ce nom ne doit pas conduire à une
am biguïté : un s e ulpatron de fonctions doit pouvoir ê tre utilisé à ch aq ue fois.
Par ailleurs, ile s t possible de fournir la définition d'une ou plusieurs fonctions particuliè re s q ui s e ront
utilisées en lie u e t place de ce lle instancié e par un patron. Par e xe m ple, ave c :
Al
gorith m e d'ins tanciation ou d'appe ld'une fonction
Pré cisons com m e nt doive nt ê tre am é nagé e s les rè gles de re ch e rch e d'une fonction surdéfinie , dans le cas où
ile xiste un ou plusieurs patrons de fonctions.
Lors d'un appe lde fonction, le com pilate ur re ch e rch e tout d'abord une corre s pondance e xacte ave c les
fonctions "ordinaire s ". S'ily a am biguïté , la re ch e rch e é ch oue (com m e à l'accoutum é e ). Si aucune fonction
"ordinaire " ne convie nt, on e xam ine alors tous les patrons ayant le nom voulu. Si une s e ule corre s pondance
e xacte e s t trouvé e , la fonction corre s pondante e s t instancié e 2 e t le problèm e e s t ré s olu. S'ily en a plusieurs,
la re ch e rch e é ch oue ..
Enfin, si aucun patron de fonction ne convie nt, on e xam ine à nouve au toute s les fonctions "ordinaire s " e n les
traitant ce tte fois com m e de sim ples fonctions surdé finie s (prom otions num é riq ue s , conve rsions
standards 3...).
___________________________________________________________________________
Enoncé
Cré e r un patron de fonctions perm e ttant de calculer le carré d'une valeur de type q ue lconq ue (le ré s ultat
possè dera le m ê m e type ). Ecrire un pe tit program m e utilisant ce patron.
2 - D u m oins, si ell
e ne l 'a pas déjà é té.
3 - R e voyez éve ntue l
le m e nt l
e paragraph e 5.3 du ch apitre 4.
19 8 Exercices en C+ +
___________________________________________________________________________
Sol
ution
Ici, notre patron ne com porte ra q u'un s e ulparam è tre de type (corre s pondant à la fois à l'uniq ue argum e nt e t
à la valeur de re tour de la fonction). Sa définition ne pos e pas de problèm e particulie r.
#include <iostream.h>
template <class T> T carre (T a)
{ return a * a ;
}
main()
{ int n = 5 ;
float x = 1.5 ;
cout << "carre de " << n << " = " << carre (n) << "\n" ;
cout << "carre de " << x << " = " << carre (x) << "\n" ;
}
___________________________________________________________________________
Enoncé
int n, p, q ;
float x ;
char t[20] ;
char c ;
Quels sont les appe ls corre cts e t, dans ce cas, q ue ls sont les prototypes des fonctions instancié e s ?
Sol
ution
l'appe l IV e s t th é oriq ue m e nt incorre ct dans la ve rsion 3 car ch ar * n'e s t pas considéré com m e une
corre s pondance e xacte pour un ch ar [20] ;il s e ra probablem e nt acce pté dans la norm e ANSI e t il
instancie ra alors la fonction :
___________________________________________________________________________
Enoncé
Cré e r un patron de fonctions perm e ttant de calculer la som m e d'un tableau d'é lém e nts de type q ue lconq ue ,
le nom bre d'élém e nts du tableau é tant fourni e n param è tre (on supposera q ue l'e nvironne m e nt utilisé acce pte
les "param è tre s e xpre s s ion"). Ecrire un pe tit program m e utilisant ce patron.
___________________________________________________________________________
Sol
ution
// exemple d'utilisation
#include <iostream.h>
main()
{ int ti[] = {3, 5, 2, 1} ;
float tf [] = {2.5, 3.2, 1.8} ;
char tc[] = { 'a', 'e', 'i', 'o', 'u' } ;
cout << somme (ti, 4) << "\n" ;
cout << somme (tf, 3) << "\n" ;
cout << somme (tc, 5) << "\n" ;
}
R e m arques :
1) Te lq u'ila é té conçu, le patron som m e ne pe ut ê tre appliq ué q u'à un type T pour leq ue l:
- l'opé ration d'addition a un s e ns ;ce la signifie donc q u'ilne pe ut pas s'agir d'un type pointe ur ;il
pe ut s'agir d'un type clas s e , à condition q ue ce tte derniè re ait surdéfini l'opé rate ur d'addition,
- la déclaration T som e s t corre cte ;ce la signifie q ue s i T e s t un type clas s e , ile s t né ce s s aire q u'il
dispose d'un constructe ur sans argum e nt,
- l'affe ctation som =0 e s t corre cte ;ce la signifie q ue s i T e s t un type clas s e , ile s t né ce s s aire q u'ilait
surdéfini l'affe ctation.
200 Exercices en C+ +
A ce propos, notons, q u'ile s t possible d'initialiser som lors de sa déclaration, e n procédant ainsi :
T som (0) ;
___________________________________________________________________________
Enoncé
int n, p, q ;
float x, y ;
double z ;
Quels sont les appe ls corre cts e t, dans ce cas, q ue ls sont les patrons utilisés et les prototypes des fonctions
instancié e s ?
Sol
ution
Ici, on fait appe là la fois à une s urdé finition de patrons (patrons I, II e t III) e t à une s pé cialisation de patron
(fonction IV).
R e m arque :
Le patron II ne pe ut jam ais ê tre utilisé ;e n e ffe t, à ch aq ue fois q u'ilpourrait l'ê tre , le patron I pe ut l'ê tre
é galem e nt, de sorte q u'ily a am biguïté . Le patron II e s t donc, ici, parfaite m e nt inutile.
Note z q ue s i nous avions défini sim ultané m e nt les deux patrons :
template <class T, class U> void fct (T a, U b) ;
template <class T> void fct (T a, T b)
le m ê m e ph é nom è ne d'am biguïté (e ntre ces deux patrons) s e rait apparu lors d'appe ls te ls q ue fct (n,p) ou
fct(x,y).
R appe lons q ue l'am biguïté n'e s t déte cté e q ue lors q ue le com pilate ur doit instancie r une fonction e t non
sim plem e nt au vu des définitions de patrons e lles -m ê m e s : ces derniè re s re s te nt donc acce pté e s tant q ue
l'am biguïté n'e s t pas m ise en évidence par un appe lla ré vé lant.
CH A PITRE XIV
LES PA TRO NS D E CLASSES
(D e puis l
a ve rs ion 3 s e ul
e m e nt)
RAPPELS
Introduite par la ve rsion 3, la notion de patron de clas s e s pe rm e t de définir ce q ue l'on nom m e aussi des
"clas s e s gé né riq ue s ". Plus précisém e nt, à l'aide d'une seule définition com portant des param è tres de type e t
des param è tre s e xpre s s ion1, on dé crit toute une fam ille de clas s e s ;le com pilate ur fabriq ue (instancie ) la ou
les clas s e s né ce s s aire s à la dem ande (on nom m e s ouve nt ce s instances des clas s e s patron).
O n pré cis e les param è tres de type e n les faisant pré céder du m ot-clé class e t les param è tre s e xpre s s ion e n
m e ntionnant leur type dans une liste de param è tre s introduite par le m ot te m plate (com m e pour les patrons
de fonctions, ave c ce tte diffé re nce q u'ici, tous les param è tre s - type ou e xpre s s ion - apparais s e nt). Par
e xe m ple :
Si une fonction m e m bre e s t définie (ce q ui e s t le cas usuel) à l'e xté rie ur de la définition du patron, ilfaut
rappe ler au com pilate ur la liste de param è tre s (te m plate ) e t pré fixe r l'e n-tê te de la fonction m e m bre du nom
du patron accom pagné de ses param è tre s 2. Par e xe m ple, pour un constructe ur de notre patron de clas s e s
pré cédent :
O n dé clare une clas s e patron e n fournissant à la suite du nom de patron un nom bre de param è tre s e ffe ctifs
(nom s de type s ou e xpre s s ions) corre s pondant aux param è tre s figurant dans la liste (te m plate ). Le s
param è tre s e xpre s s ion doive nt obligatoire m e nt ê tre d e s e xpre s s ions constantes du m ê m e type (e xact3) q ue
ce lui figurant dans la liste . Par e xe m ple, ave c notre pré cédent patron (on suppose que pt e s t une clas s e ) :
Un param è tre de type e ffe ctif pe ut lui-m ê m e ê tre une clas s e patron. Par e xe m ple, si nous avons défini un
patron de clas s e s point par :
Un patron de clas s e s pe ut com porte r de s m e m bre s (donné e ou fonction) statiq ue s ;dans ce cas, ch aq ue
instance de la classe dispose de son propre je u de m e m bre s s tatiq ue s .
Spé cial
is ation d'un patron de cl
as s e s
Un patron de clas s e s ne pe ut pas ê tre s urdé fini (on ne pe ut pas définir de ux patrons de m ê m e nom ). En
re vanch e , on pe ut spécialiser un patron de classes de diffé re nte s m aniè re s :
- en spécial
isant une fonction m em bre :
Nous pourrons é crire une ve rsion spécialisée de constructe ur pour le cas où T e s t le type point e t où n vaut
10 e n procédant ainsi :
- en spécialisant une cl asse (dans ce dernie r cas, on pe ut é ve ntue llem e nt spécialiser tout ou une partie des
fonctions m e m bre , m ais ce n'e s t pas néce s s aire ). Par e xe m ple, ave c ce patron :
3. Dans l
a norm e ANSI, les conversions triviales seront probablem ent acceptées.
XIV. Les patrons de cl
asses 205
nous pouvons fournir une ve rsion spécialisée pour le cas où T e s t le type ch ar e n procédant ainsi :
Ide ntité de cl
as s e s patron
O n ne pe ut affe cte r e ntre e ux q ue deux obje ts de m ê m e type . Dans le cas d'obje ts d'un type clas s e patron, on
considè re q u'ily a ide ntité de type lors q ue leurs param è tres de type s s ont identiq ue s e t q ue les param è tre s
e xpre s s ion ont les m ê m e s valeurs.
Cl
as s e s patron e t h é ritage
O n pe ut "com bine r" de plusieurs façons l'h é ritage ave c la notion de patron de clas s e s :
O n obtie nt une fam ille de clas s e s (de param è tre de type T).
- Patron de cl asses dérivé d 'un patron de cl
asses. Par e xe m ple, si A est une clas s e patron dé finie par
te m plate < class T> A, on pe ut :
*définir une nouve lle fam ille de fonctions dérivé e s par :
template <class T> class B : public A <T>
D ans ce cas, ile xiste autant de classes dérivé e s possibles q ue de classes de bas e possibles .
*définir une nouve lle fam ille de fonctions dérivé e s par :
template <class T, class U> class B : public A <T>
D ans ce cas, on pe ut dire q ue ch aq ue classe de bas e possible pe ut e nge ndre r une fam ille de clas s e s
dérivé e s (de param è tre de type U).
___________________________________________________________________________
Enoncé
public :
essai (T) ; // constructeur
} ;
- q u'e lle e s t fournie "à l'e xté rie ur" de la définition pré cédente ,
- q ue le constructe ur re copie la valeur re çue e n argum e nt dans ch acun de s é lém e nts du tableau tab .
b) D isposant ainsi de la définition pré cédente du patron e s s ai, de son constructe ur e t de ces déclarations :
const int n = 3 ;
int p = 5 ;
Quelles s ont les instructions corre cte s e t les clas s e s instancié e s : on e n fournira (dans ch aq ue cas) une
définition é q uivalente s ous form e d'une "clas s e ordinaire ", c'e s t-à -dire dans laq ue lle la notion de param è tre
a disparu.
Sol
ution
a) La définition du constructe ur e s t analogue à ce lle q ue l'on aurait é crite "e n ligne " ;ilfaut sim plem e nt
"pré fixe r" son en-tê te d'une liste de param è tre s introduite par te m plate . De plus, ilfaut pré fixe r l'e n-tê te de
la fonction m e m bre du nom du patron accom pagné de ses param è tre s (bie n q ue ce la soit redondant) :
b) AppelI : corre ct
class essai
{ int tab [10] ;
public :
essai (int) ; // constructeur
} ;
essai::essai (int a)
{ int i ;
for (i=0 ; i<n ; i++) tab[i] = a ;
}
b) AppelII : corre ct
class essai
{ float tab [n] ;
public :
essai (float) ; // constructeur
XIV. Les patrons de cl
asses 207
} ;
essai::essai (float a)
{ int i ;
for (i=0 ; i<n ; i++) tab[i] = a ;
}
___________________________________________________________________________
Enoncé
b) D ans q ue lles conditions peut-on instancie r une clas s e patron pointcolpour de s param è tres de type clas s e .
___________________________________________________________________________
Sol
ution
a) Voici ce q ue pourrait ê tre la définition du patron de m andé , e n pré voyant les fonctions m e m bre "e n
ligne " :
A titre indicatif, voici un e xe m ple d'utilisation (on y suppose q ue la définition pré cédente figure dans
pointcol.h :
#include <iostream.h>
#include "pointcol.h"
main()
{ pointcol <int, short int > p1 (5, 5, 2) ; p1.affiche () ;
pointcol <float, int> p2 (4, 6, 2) ; p2.affiche () ;
pointcol <double, unsigned short> p3 ; p3.affiche () ;
208 Exercices en C+ +
b) Ilsuffit q ue le type clas s e e n q ue s tion ait conve nablem e nt surdéfini l'opé rate ur < < , afin d'assure r
conve nablem e nt l'affich age s ur cout des inform ations corre s pondante s .
___________________________________________________________________________
Enoncé
b) Com m e nt faut-ilm odifie r la définition de notre patron pour q ue les instructions précédente s affich e nt
bien :
Coordonnees : 60 65
___________________________________________________________________________
Sol
ution
a) O n obtie nt l'affich age des caractè res de code 60 e t 65 (c'e s t-à -dire dans une im plém e ntation utilisant le
code A SCII : < et A) e t non les nom bre s 60 e t 65.
b) Ilfaut spécialiser notre patron point pour le cas où le type T e s t le type ch ar. Pour ce faire , on pe ut :
- soit fournir une définition com plète de point< ch ar> , ave c s e s fonctions m e m bre ,
- soit, puis q u'ici s e ule la fonction affich e e s t conce rné e , s e conte nte r de s urdé finir la fonction
point< ch ar> ::affich e , ce q ui conduit à ce tte nouve lle définition de notre patron :
// définition generale du patron point
template <class T> class point
{ T x, y ; // coordonnees
public :
point (T abs, T ord) { x = abs ; y = ord ; }
void affiche () ;
} ;
template <class T> void point<T>::affiche ()
XIV. Les patrons de cl
asses 209
{ cout << "Coordonnees : " << x << " " << y << "\n" ;
}
// version specialisee de la fonction affiche pour le type char
void point<char>::affiche ()
{ cout << "Coordonnees : " << (int)x << " " << (int)y << "\n" ;
}
R e m arque :
Bie n q ue ce la ne s oit pas totalem e nt form alisé, beaucoup d'im plém e ntations n'acce pte nt pas q ue l'on
spécialise une fonction m e m bre définie e n ligne ;c'e s t d'ailleurs pour ce tte raison q ue , dans notre
é noncé , nous avions défini affich e sous form e d'une fonction indé pe ndante .
___________________________________________________________________________
Enoncé
Cré e r un patron de clas s e s pe rm e ttant de re pré s e nte r de s "ve cte urs dynam iq ue s " c'e s t-à -dire des ve cte urs
dont la dim e nsion pe ut ne pas ê tre connue lors de la com pilation (ce n'e s t donc pas obligatoire m e nt une
e xpre s s ion constante com m e dans le cas de tableaux usuels). O n pré voira q ue les é lém e nts de ce s ve cte urs
puis s e nt ê tre de type q ue lconq ue .
O n surdéfinira conve nablem e nt l'opé rate ur [] pour q u'ilpe rm e tte l'accè s aux é lém e nts du ve cte ur (aussi bien
e n consultation q u'e n m odification) e t on s'arrange ra pour q u'iln'e xiste aucun ris q ue de "débordem e nt
d'indice ". En re vanch e , on ne ch e rch e ra pas à ré gler les problèm e s pos é s é ve ntue llem e nt par l'affe ctation ou
la transm ission par valeur d'obje ts du type conce rné .
___________________________________________________________________________
Sol
ution
En gé né ralisant ce q ui a é té fait dans l'e xe rcice VII.7 (m ais sans toute fois initialiser les é lém e nts du ve cte ur
lors de sa construction), nous aboutissons au patron de clas s e s s uivant4 :
4. La définition serait pl
us sim ple si les fonctions m em bre étaient "en ligne".
210 Exercices en C+ +
{ delete adr ;
}
Note z q ue , ici e ncore , nous avons fait e n sorte q u'une te ntative d'accè s à un é lém e nt situé e n de h ors du
ve cte ur conduis e à accéder à l'é lém e nt de rang 0. D ans la pratiq ue , on aura inté rê t à utiliser des prote ctions
plus é laboré e s .
A titre indicatif, voici un pe tit program m e utilisant ce patron (dont on suppose que la définition figure dans
ve ctge n.h :
#include <iostream.h>
#include "vectgen.h"
main()
{ vect<int> vi (10) ;
vi[5] = 5 ; vi[2] = 2 ;
cout << vi[2] << " " << vi[5] << "\n" ;
vect<double> vd (3) ;
vd[0] = 0.0 ; vd[1] = 0.1 ; vd[2] = 0.2 ;
cout << vd[0] << " " << vd[1] << " " << vd[2] << "\n" ;
cout << vd[12] ; vd[12] = 1.2 ; cout << vd[12] << " " << vd[0] ;
}
R e m arque :
Notre patron ve ct pe rm e t d'instancie r de s ve cte urs dynam iq ues dans les q ue ls les é lém e nts sont de type
absolum e nt q ue lconq ue , e n particulie r de type clas s e (pourvu q ue ladite classe dispose d'un constructe ur
sans argum e nt). Iln'e n s e rait pas allé ainsi si nous avions initialisé les é lém e nts du tableau lors de leur
construction par :
int i ;
for (i=0 ; i<nelem ; i++) adr[i] = 0 ;
En e ffe t, dans ce cas, ce s instructions auraie nt conve nablem e nt fonctionné pour n'im porte q ue ltype de
bas e (par conve rsion de l'e ntie r 0 dans le type voulu). En re vanch e , pour ê tre applicable à des élém e nts
de type clas s e , ilaurait fallu, e n outre , q ue la clas s e conce rnée dispose d'une conve rsion d'un int dans ce
type clas s e , c'e s t-à -dire d'un constructe ur à un argum e nt de type num é riq ue .
___________________________________________________________________________
Enoncé
Com m e dans l'e xe rcice pré cédent, ré aliser un patron de clas s e s pe rm e ttant de m anipuler de s ve cte urs dont
les é lém e nts sont de type q ue lconq ue m ais pour les q ue ls la dim e nsion, supposée ê tre ce tte fois une
e xpre s s ion constante , apparaî
tra com m e un param è tre (e xpre s s ion) du patron. H orm is ce tte diffé re nce , les
"fonctionnalité s " du patron re s te ront les m ê m e s .
___________________________________________________________________________
XIV. Les patrons de cl
asses 211
Sol
ution
Iln'e s t donc plus néce s s aire d'alloue r un e m place m e nt dynam iq ue pour notre ve cte ur q ui pe ut donc figure r
dire cte m e nt dans les m e m bres donnée de notre patron. Le constructe ur n'e s t plus néce s s aire (voir toute fois la
re m arq ue ci-de s s ous), pas plus q ue le destructe ur. Voici ce q ue pourrait ê tre la définition de notre patron5 :
#include "vectgen1.h"
#include <iostream.h>
main()
{ vect<int, 10> vi ;
vi[5] = 5 ; vi[2] = 2 ;
cout << vi[2] << " " << vi[5] << "\n" ;
vect<double, 3> vd ;
vd[0] = 0.0 ; vd[1] = 0.1 ; vd[2] = 0.2 ;
cout << vd[0] << " " << vd[1] << " " << vd[2] << "\n" ;
cout << vd[12] << " " ; vd[12] = 1.2 ; cout << vd[12] << " " << vd[0] ;
}
R e m arque :
Ici, nous n'avons pas eu besoin de faire du nom bre d'élém e nts un m e m bre donnée de nos clas s e s patron :
e n e ffe t, lors q u'on e n a be s oin, on l'obtie nt com m e é tant la valeur du second param è tre fourni lors de
l'instanciation. Si nous avions voulu cons e rve r ce nom bre d'élém e nts sous form e d'un m e m bre donnée, il
aurait é té né ce s s aire de pré voir un constructe ur, par e xe m ple :
template <class T, int n> class vect
{ int nelem ; // nombre d'elements
T v [n] ; // vecteur de n elements de type T
public :
vect () ;
T & operator [] (int) ; // operateur d'acces a un element
} ;
template <class T, int n> vect<T,n>::vect ()
{ nelem = n ;
}
5. La définition serait pl
us sim ple si les fonctions m em bre étaient "en ligne".
212 Exercices en C+ +
___________________________________________________________________________
Enoncé
a) Cré e r, par dé rivation, un patron de clas s e s pointcolpe rm e ttant de m anipuler de s "points coloré s " dans
les q ue ls les coordonné e s e t la couleur sont de m ê m e type . O n redéfinira conve nablem e nt les fonctions
m e m bre e n ré utilisant les fonctions m e m bre de la classe de bas e .
b) M ê m e q ue s tion, m ais e n pré voyant q ue les coordonné e s e t la couleur puis s e nt ê tre de deux type s
diffé re nts.
c) Toujours par dé rivation, cré e r ce tte fois une "clas s e ordinaire " (c'e s t-à -dire une clas s e q ui ne s oit plus un
patron de clas s e s , autre m e nt dit q ui ne dépe nde plus de param è tre s ...) dans laq ue lle les coordonné e s s ont de
type int, tandis q ue la couleur e s t de type sh ort.
___________________________________________________________________________
Sol
ution
a) Aucun problèm e particulie r ne s e pos e ;ilsuffit de faire dérive r pointcol< T> de point< T> . Voici ce
q ue pe ut ê tre la définition de notre patron (ici, nous avons lais s é le constructe ur "e n ligne " m ais nous avons
défini affich e e n de h ors de la clas s e ) :
Voici un pe tit e xe m ple d'utilisation (ilné ce s s ite les déclarations approprié e s ou l'incorporation de fich ie rs .h
corre s pondants) :
main()
{ pointcol <int> p1 (2, 5, 1) ; p1.affiche () ;
pointcol <float> p2 (2.5, 5.25, 4) ; p2.affiche () ;
}
XIV. Les patrons de cl
asses 213
b) Ce tte fois, la classe dérivé e d é pe nd de deux param è tre s (nom m é s ici T e t U). Voici ce q ue pourrait ê tre la
définition de notre patron (ave c, toujours, un constructe ur e n ligne e t une fonction affich e définie à
l'e xté rie ur de la clas s e ) :
Voici un e xe m ple d'utilisation (on suppose qu'ile s t m uni des déclarations approprié e s ) :
main()
{ pointcol <int, short> p1 (2, 5, 1) ; p1.affiche () ;
pointcol <float, int> p2 (2.5, 5.25, 4) ; p2.affiche () ;
}
c) Ce tte fois, pointcole s t une s im ple clas s e , ne dépe ndant plus d'aucun param è tre . Voici ce q ue pourrait ê tre
sa définition :
main()
{ pointcol p1 (2, 5, 1) ; p1.affiche () ;
pointcol p2 (2.5, 5.25, 4) ; p2.affiche () ;
}
___________________________________________________________________________
Enoncé
public :
point (T abs, T ord) { x = abs ; y = ord ; }
void affiche ()
{ cout << "Coordonnees : " << x << " " << y << "\n" ;
}
} ;
a) Lui ajoute r une ve rsion spécialisée de affich e pour le cas ou T e s t le type caractè re .
b) Com m e dans la q ue s tion a de l'e xe rcice pré cédent, cré e r un patron de clas s e s pointcolpe rm e ttant de
m anipuler de s "points coloré s " dans les q ue ls les coordonné e s e t la couleur sont de m ê m e type . O n redéfinira
conve nablem e nt les fonctions m e m bre e n ré utilisant les fonctions m e m bre de la classe de bas e e t l'on
pré voira une ve rsion spécialisée de affich e de pointcoldans le cas du type caractè re .
___________________________________________________________________________
Sol
ution
a) Pour dé finir une ve rsion spécialisée d'une fonction m e m bre , ile s t gé né ralem e nt né ce s s aire q ue ce tte
fonction m e m bre s oit définie e xté rie ure m e nt au patron de clas s e s (re voye z la re m arq ue de l'e xe rcice XIV.3).
Voici ce q ue pourrait ê tre la nouve lle définition (com plète ) de notre patron point :
b) Pour les m ê m e s raisons que précédem m e nt, la fonction affich e de pointcoldoit ê tre définie à l'e xté rie ur
du patron. Voici ce q ue pourrait ê tre ce tte définition :
void pointcol<char>::affiche ()
{ point<char>::affiche () ;
cout << " couleur : " << (int)cl << "\n" ;
}
XIV. Les patrons de cl
asses 215
R e m arque :
Se ule la q ue s tion a de l'e xe rcice XIV.6 s e prê tait à une s pé cialisation pour le type caractè re . En e ffe t,
pour la classe dem andé e e n c, n'ayant plus affaire à un patron de clas s e s , la q ue s tion n'aurait aucun s e ns.
En ce q ui conce rne la classe dem andé e e n b, e n re vanch e , on s e trouve e n pré s e nce d'une classe dérivé e
dépendant de 2 param è tre s T e t U. Ilfaudrait alors pouvoir spécialiser une clas s e , non plus pour de s
valeurs données de tous les (deux) param è tre s , m ais pour une valeur donné e (ch ar) de l'un d'e ntre e ux ;
ce tte notion de fam ille de spé cialisation n'e s t pas possible, du m oins dans l'é tat actue lde la définition de
C+ + .
___________________________________________________________________________
Enoncé
O n souh aite cré e r un patron de clas s e s ce rcle pe rm e ttant de m anipuler de s ce rcles , définis par leur ce ntre (de
type point) e t un rayon. O n n'y pré voira, com m e fonctions m e m bre , q u'un constructe ur e t une fonction
affich e s e conte ntant d'affich e r les coordonnées du ce ntre e t la valeur du rayon.
b) Le faire par com position d'obje ts m e m bre (un ce rcle possè de un point e t un rayon).
___________________________________________________________________________
Sol
ution
a) La dém arch e e s t analogue à ce lle de la q ue s tion a de l'e xe rcice XIV.6. O n a affaire à un patron dé pe ndant
de deux param è tre s (ici T e t U).
} ;
b) Le patron dé pe nd toujours de deux param è tre s (T e t U) m ais iln'y a plus de notion d'h é ritage :
Note z q ue , dans la définition du constructe ur ce rcle, nous avons transm is les argum e nts abs e t ord à un
constructe ur de point pour le m e m bre c. Nous aurions pu utiliser la m ê m e notation pour r, bie n q ue ce
m e m bre s oit d'un type de bas e e t non d'un type clas s e ;ce la nous aurait conduit au constructe ur suivant (de
corps vide) :
___________________________________________________________________________
Enoncé
R é aliser une clas s e nom m é e s e t_int pe rm e ttant de m anipuler de s e ns e m bles de nom bre s e ntie rs. Le nom bre
m axim ald'entie rs q ue pourra conte nir l'e ns e m ble s e ra pré cis é au constructe ur q ui alloue ra dynam iq ue m e nt
l'e s pace né ce s s aire . O n pré voira les opé rate urs suivants (e désigne un élém e nt de type s e t_int e t n un e ntie r :
La fonction m e m bre cardinalfournira le nom bre d'élém e nts de l'e ns e m ble. Enfin, on s'arrange ra pour q ue
l'affe ctation ou la transm ission par valeur d'obje ts de type s e t_int ne pos e aucun problèm e (on acce pte ra la
duplication com plète d'obje ts).
_______________________________________________________________
Sol
ution
Nature llem e nt, notre clas s e com porte ra, e n m e m bres donnée, le nom bre m axim al(nm ax) d'élém e nts de
l'e ns e m ble, le nom bre courant d'élém e nts (ne le m ) e t un pointe ur sur l'e m place m e nt conte nant les valeurs de
l'e ns e m ble.
Com pte te nu de ce q ue notre clas s e com porte une partie dynam iq ue , ile s t né ce s s aire , pour q ue l'affe ctation
e t la transm ission par valeur se déroulent conve nablem e nt, de surdéfinir l'opé rate ur d'affe ctation e t de m unir
notre classe d'un constructe ur par re copie . Les deux fonctions m e m bre (ope rator = e t s e t_int ) devront
pré voir une "copie profonde " des obje ts. Nous utiliserons pour cela une m é th ode q ue nous avons déjà
re ncontré e e t q ui consiste à considérer que deux obje ts diffé re nts disposent systé m atiq ue m e nt de deux partie s
dynam iq ues diffé re nte s , m ê m e s i e lles possè dent le m ê m e conte nu.
L'opé rate ur % doit ê tre s urdé fini obligatoire m e nt sous form e d'une fonction am ie , puis q ue s on pre m ie r
opé rande n'e s t pas de type clas s e . L'opé rate ur de s ortie dans un flot doit, lui aussi, ê tre s urdé fini sous form e
d'une fonction am ie , m ais pour une raison diffé re nte : son pre m ie r argum e nt e s t de type ostre am .
226 Exercices de C+ +
/* fichier SETINT.H */
/* déclaration de la classe set_int */
class set_int
{
int * adval ; // adresse du tableau des valeurs
int nmax ; // nombre maxi d'éléments
int nelem ; // nombre courant d'éléments
public :
set_int (int = 20) ; // constructeur
set_int (set_int &) ; // constructeur par recopie
set_int & operator = (set_int &) ; // opérateur d'affectation
~set_int () ; // destructeur
int cardinal () ; // cardinal de l'ensemble
set_int & operator << (int) ; // ajout d'un élément
friend int operator % (int, set_int &) ; // appartenance d'un élément
// envoi ensemble dans un flot
friend ostream & operator << (ostream &, set_int &) ;
} ;
Voici ce q ue pourrait ê tre la définition de notre clas s e (les points délicats sont com m e nté s au s e in m ê m e des
instructions) :
#include <iostream.h>
#include "setint.h"
Note z q u'ici iln'e s t pas possible d'agrandir l'e ns e m ble au-de là de la lim ite q ui lui a é té im partie lors de sa
construction. Ils e rait as s e z facile de ré m édier à ce tte lacune e n m odifiant s e nsiblem e nt la fonction d'ajout
d'un é lém e nt (ope rator < < ). Ilsuffirait, e n e ffe t, q u'e lle pré voie , lors q ue la lim ite e s t atte inte , d'alloue r
un nouve le m place m e nt dynam iq ue , par e xe m ple d'une taille double de l'e m place m e nt e xistant, d'y re copie r
l'actue lconte nu e t de libérer l'ancie n e m place m e nt (e n actualisant conve nablem e nt les m e m bres donnée de
l'obje t).
Voici un e xe m ple de program m e utilisant la clas s e s e t_int, accom pagné du résultat fourni par son exécution :
main()
228 Exercices de C+ +
___________________________________________________________________________
XV. Exercices de synth è se 229
Enoncé
Cré e r une clas s e ve ct pe rm e ttant de m anipuler de s "ve cte urs dynam iq ue s " d'entie rs, c'e s t-à -dire des tableaux
d'entie rs dont la dim e nsion pe ut ê tre définie au m om e nt de leur cré ation (une te lle clas s e a déjà é té
partie llem e nt ré alisée dans l'e xe rcice VII.7). Ce tte classe devra disposer des opérate urs suivants :
[] pour l'accè s à une des com posantes du ve cte ur, e t ce la aussi bien au s e in d'une e xpre s s ion q u'à gauch e
d'une affe ctation (m ais ce tte derniè re s ituation ne devra pas ê tre autoris é e s ur de s "ve cte urs constants"),
==, te lq ue s i v1 e t v2 sont deux obje ts de type ve ct, v1==v2 pre nne la valeur 1 si v1 e t v2 sont de
m ê m e dim e nsion et ont les m ê m e s com posante s e t la valeur 0 dans le cas contraire ,
< < , te lq ue flot< < v e nvoie le ve cte ur v sur le flot indiq ué , sous la form e :
<entier1, entier2, ... , entiern>
D e plus, on s'arrange ra pour q ue l'affe ctation e t la transm ission par valeur d'obje ts de type ve ct ne pos e
aucun problèm e ;pour ce faire , on acce pte ra de dupliq ue r com plète m e nt les obje ts conce rné s .
_______________________________________________________________
Sol
ution
R appe lons q ue lors q ue l'on dé finit des obje ts constants, iln'e s t pas possible de leur appliq ue r une fonction
m e m bre publiq ue , sauf si ce tte derniè re a é té déclaré e ave c le q ualificatif const (auq ue lcas, une te lle
fonction pe ut indiffé re m m e nt ê tre utilisée ave c des obje ts constants ou non constants). Pour obte nir l'e ffe t
dem andé de l'opé rate ur [], lors q u'ile s t appliq ué à un ve cte ur constant, ile s t né ce s s aire d'en prévoir de ux
définitions dont l'une s 'appliq ue aux ve cte urs constants ;pour é vite r q u'on ne puis s e , dans ce cas, l'utiliser à
gauch e d'une affe ctation, ile s t né ce s s aire q u'e lle re nvoie s on ré s ultat par valeur (e t non par adre s s e com m e
le fe ra la fonction applicable aux ve cte urs non constants).
class vect
{ int nelem ; // nombre de composantes du vecteur
int * adr ; // pointeur sur partie dynamique
public :
vect (int n=1) ; // constructeur "usuel"
vect (vect & v) ; // constructeur par recopie
~vect () ; // destructeur
friend ostream & operator << (ostream &, vect &) ;
vect operator = (vect & v) ; // surdéfinition opérateur affectation
int & operator [] (int i) ; // surdef [] pour vect non constants
int operator [] (int i) const ; // surdef [] pour vect constants
} ;
#include <stdio.h>
#include "a:\synthese\vect.h"
vect::vect (int n) // constructeur "usuel"
{ adr = new int [nelem = n] ;
}
vect::~vect () // destructeur
{ delete adr ;
}
#include <vect.h>
#include <iostream.h>
main()
{ int i ;
vect v1(5), v2(10) ;
for (i=0 ; i<5 ; i++) v1[i] = i ;
cout << "v1 = " << v1 << "\n" ;
for (i=0 ; i<10 ; i++) v2[i] = i*i ;
cout << "v2 = " << v2 << "\n" ;
v1 = v2 ;
cout << "v1 = " << v1 << "\n" ;
vect v3 = v1 ;
cout << "v3 = " << v3 << "\n" ;
vect v4 = v2 ;
cout << "v4 = " << v4 << "\n" ;
// const vect w(3) ; w[2] = 5 ; // conduit bien à erreur compilation
}
___________________________________________________________________________
Enoncé
En langage C+ + , com m e e n langage C, iln'e xiste pas, a priori, de vé ritable type ch aî ne , m ais sim plem e nt
une "conve ntion" de re pré s e ntation de s ch aî
ne s (suite de caractè re s , te rm iné e par un caractè re de code nul).
Un ce rtain nom bre de fonctions utilisant ce tte conve ntion pe rm e tte nt les "m anipulations classiques" (e n
particulie r la concaté nation).
Cré e r une clas s e nom m é e string, offrant des possibilité s plus proch es d'un vé ritable type ch aî
ne . O n de vra y
trouve r les opé rate urs suivants :
+ , te lq ue ch 1+ ch 2 fournis s e e n ré s ultat la ch aî
ne obte nue par concaté nation de s ch aî
ne s ch 1 e t ch 2
(sans m odifie r les ch aî
ne s ch 1 e t ch 2),,
==, te lq ue ch 1==ch 2 pre nne la valeur 1 si les deux ch aî
ne s ch 1 e t ch 2 sont é gales e t la valeur 0 dans
le cas contraire ,
[], te lq ue ch [i] re pré s e nte le caractè re de rang i de la ch aî
ne ch ;ce t opé rate ur de vra é galem e nt pouvoir
ê tre utilisé à gauch e d'une affe ctation (ch [i] = ...),
< < , te lq ue flot < < ch transm e tte au flot indiq ué le conte nu de la ch aî
ne ch .
Une fonction m e m bre long fournira la longue ur courante d'une ch aî
ne .
Enfin, si ch e s t de type string, on de vra pouvoir acce pte r de s e xpre s s ions de la form e s uivante , e t dont le
ré s ultat s e ra, lui aussi de type string :
ch + "hello"
"hello" + ch
_______________________________________________________________
Sol
ution
M anife s te m e nt, ilfaudra cons e rve r le conte nu d'une ch aî ne (suite de caractè re s ) dans un em place m e nt
dynam iq ue , de m aniè re à pouvoir e n faire é volue r ais é m e nt le conte nu. D ans les m e m bres donnée, on
trouve ra donc un pointe ur (ch ar *) sur un te le m place m e nt. O n pe ut se dem ande r s'ile s t né ce s s aire de
cons e rve r un caractè re de fin de ch aî ne dans l'e m place m e nt dynam iq ue e n q ue s tion. En fait, à partir du
m om e nt où l'on dé cide de cons e rve r la longue ur (courante ) d'une ch aî ne dans un m e m bre donnée, ce
caractè re de fin n'e s t plus indispensable ;né anm oins, sa pré s e nce facilite les traite m e nts à opé re r sur les
caractè res de la ch aî ne , dans la m e s ure où ilre s te alors possible de faire appe laux fonctions classiques du
langage C . 1
En ce q ui conce rne les constructe urs du type string, on pe ut nature llem e nt pré voir un constructe ur sans
argum e nt q ui initialise la ch aî
ne corre s pondante à une ch aî
ne vide. Ilfaut é galem e nt pré voir un constructe ur
par re copie .
M ais on pe ut é galem e nt ajoute r un constructe ur q ui fabriq ue une ch aî ne , à partir d'une ch aîne classique
(ch ar *) re çue e n argum e nt. En e ffe t, dans ce cas, on disposera alors d'une conve rsion ch ar *--> string q ui
pe rm e ttra de donner un sens à la concaté nation d'un obje t de type string e t d'une ch aî ne C classique.
Toute fois, ce la ne s e ra possible pour une e xpre s s ion de la form e "h e llo" + ch q ue s i l'opé rate ur + a é té
surdéfini sous form e d'une fonction am ie (son pre m ie r opé rande pourra alors ê tre s oum is à d'éve ntue lles
conve rsions) e t non pas sous form e d'une fonction m e m bre .
Par ailleurs, la valeur de re tour de ce t opé rate ur doit absolum e nt ê tre transm ise par valeur, dans la m e s ure
où le ré s ultat devra ê tre gé né ré dans une ch aî
ne de clas s e autom atiq ue (e lle disparaî
tra à la fin de l'e xé cution
de la fonction corre s pondante ).
}
return * this ; // pour pouvoir utiliser
} // la valeur de a=b (affectations multiples)
string operator + (string & ch1, string & ch2) // attention : la valeur de retour
{ string res ; // est à transmettre par valeur
res.adr = new char [res.lg = ch1.lg + ch2.lg] ;
strcpy (res.adr, ch1.adr) ;
strcpy (res.adr+ch1.lg, ch2.adr) ;
return res ;
}
Voici un program m e d'essai de la clas s e string, accom pagné du résultat de son exécution :
/* programme d'essai */
main()
{ string a ;
cout << "chaine a : " << a << " de longueur " << a.length() << "\n" ;
string b("bonjour") ;
cout << "chaine b : " << b << "\n" ;
string c=b ;
cout << "chaine c : " << c << "\n" ;
string d("hello") ;
a = b = d ;
cout << "chaine b : " << b << " de longueur " << b.length() << "\n" ;
cout << "chaine a : " << a << "\n" ;
cout << "a == b : " << (a == b) << "\n" ;
string x("salut "), y("chère "), z("madame");
a = x + y + z ;
cout << "chaine a : " << a << "\n" ;
a = a ;
cout << "chaine a : " << a << "\n" ;
a = a + " - Comment allez-vous ?\n" ;
cout << "chaine a : " << a ;
a = "*****" + a ;
cout << "chaine a : " << a ;
// a = "bon" + "jour" ; // serait rejeté en compilation
string e("xxxxxxxxxx") ;
for (char cr='a', i=0 ; cr<'f' ; cr++, i++ ) e[i] = cr ;
cout << "chaine e : " << e << "\n" ;
}
___________________________
234 Exercices de C+ +
chaine a : de longueur 0
chaine b : bonjour
chaine c : bonjour
chaine b : hello de longueur 5
chaine a : hello
a == b : 1
chaine a : salut chère madame
chaine a : salut chère madame
chaine a : salut chère madame - Comment allez-vous ?
chaine a : *****salut chère madame - Comment allez-vous ?
chaine e : abcdexxxxx
___________________________________________________________________________
Enoncé
R é aliser une clas s e nom m é e bit_array pe rm e ttant de m anipuler de s tableaux de bits (autre m e nt dit des
tableaux dans les q ue ls ch aq ue é lém e nt ne pe ut pre ndre q ue l'une des deux valeurs 0 ou 1. La taille d'un
tableau (c'e s t-à -dire le nom bre de bits) s e ra dé finie lors de sa cré ation (par un argum e nt pas s é à son
constructe ur). O n pré voira les opé rate urs suivants :
O n fe ra e n sorte q ue l'affe ctation e t la transm ission par valeur d'obje ts du type bit_array ne pos e aucun
problèm e . De plus, les opé rate urs m odifiant le conte nu d'un tableau ne devront pas pouvoir ê tre appliq ué s à
des obje ts constants (attribut const).
_______________________________________________________________
Sol
ution
Si l'on ch e rch e à m inim iser l'e m place m e nt m é m oire utilisé pour les obje ts de type bit_array, il e s t
né ce s s aire de n'e m ploye r q u'un s e ulbit pour re pré s e nte r un "é lém e nt" d'un tableau. Ces bits devront donc
ê tre re groupé s , par e xe m ple à raison de CH AR _BIT (défini dans lim its.h ) bits par caractè re .
M anife s te m e nt, ilfaut pré voir q ue l'e m place m e nt destiné à ces diffé re nts bits soit alloué dynam iq ue m e nt e n
fonction de la valeur fournie au constructe ur : pour n bits, ilfaudra n/CH A R _BIT+ 1 caractè re s .
En m e m bre donnée, ilnous suffit de disposer d'un pointe ur sur l'e m place m e nt dynam iq ue e n q ue s tion, ainsi
q ue du nom bre de bits du tableau. Pour sim plifie r ce rtaines des fonctions m e m bre , nous prévoirons
é galem e nt de cons e rve r le nom bre de caractè re s corre s pondant.
XV. Exercices de synth è se 235
Le s opé rate urs + =, -=, + + e t -- pe uve nt ê tre définis indiffé re m m e nt sous form e d'une fonction m e m bre
ou d'une fonction am ie . Ici, nous avons ch oisi des fonctions m e m bre .
L'é noncé ne pré cis e rie n q uant au ré s ultat fourni par ce s 4 opé rate urs. En fait, on pourrait pré voir q u'ils
re s titue nt le tableau aprè s q u'ils y ont e ffe ctué l'opé ration voulue , m ais, e n pratiq ue , ce la s e m ble de pe u
d'inté rê t. Ici, nous avons donc sim plem e nt pré vu q ue ce s opé rate urs ne fourniraie nt aucun ré s ultat.
Pour q ue [] ne s oit pas utilisable dans une affe ctation de la form e t[i] = ..., ilsuffit de prévoir q u'il
fournis s e s on ré s ultat par valeur (e t non par ré fé re nce com m e on a gé né ralem e nt l'h abitude de le faire ).
Le s opé rate urs + =, -=, + + e t -- m odifie nt la valeur de leur pre m ie r opé rande . Ils ne doive nt donc pas
pouvoir agir sur un obje t constant. En re vanch e , les autre s , c'e s t-à -dire [] (ici) e t < < , pe uve nt agir sans
ris q ue s ur un obje t constant ;pour q u'ils puissent y ê tre autoris é s , nous leur donne rons l'attribut const.
Nature llem e nt, ici e ncore , l'é noncé nous im pose de surdé finir l'opé rate ur d'affe ctation e t de prévoir un
constructe ur par re copie .
ostream & operator << (ostream & sortie, bit_array & t) const
{ sortie << "<* " ;
int i ;
for (i=0 ; i<t.nbits ; i++)
sortie << t[i] << " " ;
sortie << "*>" ;
return sortie ;
}
void bit_array::operator ++ ()
{ int i ;
for (i=0 ; i<ncar ; i++) adb[i] = 0xFFFF ;
}
XV. Exercices de synth è se 237
void bit_array::operator -- ()
{ int i ;
for (i=0 ; i<ncar ; i++) adb[i] = 0 ;
}
void bit_array::operator ~ ()
{ int i ;
for (i=0 ; i<ncar ; i++) adb[i] = ~ adb[i] ;
}
Voici un program m e d'essai de la clas s e bit_array, accom pagné du résultat fourni par son exécution :
t1 = <* 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 *>
t1 = <* 1 0 0 1 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 *>
t1 = <* 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 *>
t1 = <* 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 *>
t1 = <* 0 1 1 0 1 1 1 1 0 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 *>
t1 = <* 0 1 1 0 1 1 1 1 0 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 *>
t2 = <* 0 0 0 0 0 0 0 0 0 0 0 *>
t3 = <* 0 1 1 0 1 1 1 1 0 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 *>
___________________________________________________________________________
Enoncé
La capacité des nom bre s e ntie rs e s t lim ité e par la taille du type longint. Cré e r une clas s e big_int pe rm e ttant
de m anipuler de s nom bre s e ntie rs de val eur absol um ent quel conque.
Pour ne pas alourdir l'e xe rcice , on s e lim ite ra à des nom bre s s ans signe et à l'opé ration d'addition ;on
s'arrange ra toute fois pour q ue d e s e xpre s s ions m ixte s (c'e s t-à -dire m é lange ant des obje ts de type long_int
ave c des entie rs usuels) aie nt un s e ns.
238 Exercices de C+ +
O n dé finira l'opé rate ur < < pour q u'ilpe rm e tte d'envoye r un obje t de type big_int sur un flot. Parm i les
diffé re nts constructe urs, on e n pré voira un ave c un argum e nt de type ch aîne de caractè re s , corre s pondant
aux ch iffres d'un "grand e ntie r".
O n fe ra e n sorte q ue l'affe ctation e t la transm ission par valeur d'obje ts de type big_int ne pos e aucun
problèm e .
_______________________________________________________________
Sol
ution
Pour re pré s e nte r un "grand e ntie r", la dém arch e la plus nature lle (m ais pas la plus é conom iq ue e n place
m é m oire !) consiste à cons e rve r le nom bre s ous form e décim ale, à ch aq ue ch iffre é tant associé un caractè re .
Pour ce faire , on pe ut ch oisir de "coder" un te lch iffre par le caractè re corre s pondant ('0' pour 0, '1' pour 1,
...) ;on pe ut aussi ch oisir de place r une valeur é gale au ch iffre lui-m ê m e (0 pour 0, 1 pour 1, ...). La
derniè re s olution oblige à e ffe cte ur un "transcodage " lors q ue l'on doit pas s e r de la form e ch aî ne de caratè re s
à la form e big_int (dans le constructe ur corre s pondant, notam m e nt) ou, inve rs e m e nt, lors q ue l'on doit pas s e r
de la form e big_int à la form e s uite de caractè re s (pour l'affich age ). En re vanch e , e lle s im plifie q ue lque pe u
l'algorith m e d'addition, e t c'e s t e lle q ue nous avons ch oisie.
L'e m place m e nt pe rm e ttant de cons e rve r un grand e ntie r s e ra alloué dynam iq ue m e nt ;sa taille s e ra,
nature llem e nt, adapté e à la valeur du nom bre q ui s'y trouve ra. O n cons e rve ra é galem e nt le nom bre courant
de ch iffres de l'e ntie r ;on pourrait, e n toute rigue ur, s'e n pas s s e r m ais nous ve rrons q ue s a pré s e nce
sim plifie q ue lque pe u la program m ation. En ce q ui conce rne l'ordre de range m e nt des ch iffre s au s e in de
l'e m place m e nt corre s pondant, ily a m anife s te m e nt deux possibilité s . Ch acune possè de des avantage s e t des
inconvé nie nts ;nous avons ici ch oisi de ranger les ch iffres dans l'ordre inve rse de ce lui où on les é crit
(unité s , dizaine s , ce ntaine s ...).
Pour pouvoir acce pte r les e xpre s s ions m ixte s , on dispose de plusieurs solutions :
Par ailleurs, pour q ue les conve rsions envisagé e s s 'appliq ue nt au pre m ie r opé rande de l'addition, ile s t
né ce s s aire de surdé finir l'opé rate ur + com m e une fonction am ie .
Voci la déclaration de notre clas s e big_int (la pré s e nce d'un constructe ur privé à deux argum e nts e ntie rs s e ra
justifié e un pe u plus loin) :
friend ostream & operator << (ostream &, big_int &) ; // opérateur <<
} ;
L'opé rate ur + com m e nce par cré e r un e m place m e nt te m poraire pouvant re ce voir un nom bre com portant un
ch iffre de plus q ue le plus grand de ses deux opé rande s (on ne s ait pas e ncore com bien de ch iffre s
com porte ra e xacte m e nt le ré s ultat). O n y calcule la som m e s uivant un algorith m e calqué s ur le proce s s us
m anue ld'addition.
Puis on cré e un obje t de type big_int e n utilisant un constructe ur particulie r : big_int (int, int). En fait, nous
avons besoin d'un constructe ur cré ant un big_int com portant un nom bre de ch iffres donné, ce dont nous ne
disposons pas dans les constructe urs publics. De plus, nous ne pouvons pas utiliser un constructe ur de la
form e big_int (int) car, alors, les additions m ixte s faisant inte rve nir de s e ntie rs ch e rch e raie nt à l'e m ploye r
pour e ffe ctue r une conve rsion! C'e s t pourq uoi nous avons prévu un constructe ur à deux argum e nts, le s e cond
é tant fictif ;de plus, nous l'avons re ndu privé , dans la m e s ure où iln'a nullem e nt besoin d'ê tre acce s s ible à
un utilisate ur de la clas s e .
Voici un pe tit program m e d'utilisation de la clas s e big_int, accom pagné du résultat fourni par son
e xé cution :
XV. Exercices de synth è se 241
/* programme d'essai */
#include <iostream.h>
#include "bigint.h"
main()
{ big_int n1=12 ;
big_int n2(35) ;
big_int n3 ;
n3 = n1 + n2 ;
cout << n1 << " + " << n2 << " = " << n3 << "\n" ;
big_int n4 ("1234567890123456789"), n5("9876543210987654321"), n6 ;
n6 = n4 + n5 ;
cout << n4 << " + " << n5 << " = " << n6 << "\n" ;
cout << n6 << " + " << n1 << " = " << n6 + n1 << "\n" ;
}
_______________________
12 + 35 = 47
1234567890123456789 + 9876543210987654321 = 11111111101111111110
11111111101111111110 + 12 = 11111111101111111122
___________________________________________________________________________
Enoncé
Cré e r un patron de clas s e s nom m é stack , pe rm e ttant de m anipuler de s piles dont les é lém e nts sont de type
q ue lconq ue . Ces dernie rs s e ront cons e rvés dans un em place m e nt alloué dynam iq ue m e nt e t dont la dim e nsion
s e ra fournie au constructe ur (ilne s 'agira donc pas d'un param è tre e xpre s s ion du patron). La classe devra
com porte r les opé rate urs suivants :
< < , te lq ue p<<n ajoute l'é lém e nt n à la pile p (si la pile e s t pleine , ilne s e pas s e ra rie n),
> > , te lq ue p> > n place dans n la valeur du h aut de la pile p, e n la supprim ant de la pile (si la pile e s t
vide, ilne s e pas s e ra rie n),
+ + , te lq ue + + p vale 1 si la pile p e s t pleine e t 0 dans le cas contraire ,
--, te lq ue --p vale 1 si la pile p e s t vide et 0 dans le cas contraire ,
< < , te lq ue , flot é tant un flot de sortie , flot < < p affich e le conte nu de la pile p
sur le flot sous la form e : // valeur_1 valeur_2... valeur_n //.
O n supposera q ue les obje ts de type stack ne s e ront jam ais soum is à des transm issions par valeur ou à des
affe ctations ;on ne ch e rch e ra donc pas à surdéfinir le constructe ur par re copie ou l'opé rate ur d'affe ctation.
___________________________________________________________________________
Sol
ution
En fait, on pe ut s'inspire r de ce q ui a é té fait dans l'e xe rcice VII.10 pour ré aliser une pile d'entie rs e n
faisant e n sorte q ue int soit re m placé par un param è tre de type .
#include <iostream.h>
#include <stdlib.h>
242 Exercices de C+ +
A titre indicatif, voici un pe tit program m e d'utilisation de notre patron de clas s e s (dont on suppose que la
définition figure dans stack _ge n.h ) :
___________________________________________________________________________
Enoncé
En vous inspirant de l'e xe rcice XV.2, cré e r un patron de clas s e s pe rm e ttant de m anipuler de s ve cte urs
dynam iq ues dont les é lém e nts sont de type q ue lconq ue .
___________________________________________________________________________
Sol
ution
#include "vectgen.h"
template <class T> vect<T>::vect (int n) // constructeur "usuel"
{ adr = new T [nelem = n] ;
}
{ delete adr ;
adr = new T [nelem = v.nelem] ;
int i ;
for (i=0 ; i<nelem ; i++)
adr[i] = v.adr[i] ;
}
return * this ;
}
template <class T> ostream & operator << (ostream & sortie, vect<T> & v)
{ sortie << "<" ;
int i ;
for (i=0 ; i<v.nelem ; i++) sortie << v.adr[i] << " " ;
sortie << ">" ;
return sortie ;
}
#include <vectgen.h>
#include <iostream.h>
main()
{ int i ;
vect <int> v1(5) ; vect <int> v2(10) ;
for (i=0 ; i<5 ; i++) v1[i] = i ;
cout << "v1 = " << v1 << "\n" ;
for (i=0 ; i<10 ; i++) v2[i] = i*i ;
cout << "v2 = " << v2 << "\n" ;
v1 = v2 ;
cout << "v1 = " << v1 << "\n" ;
vect <int> v3 = v1 ;
// vect <double> v3 = v1 ; // serait rejete
cout << "v3 = " << v3 << "\n" ;
// const vect <float> w(3) ; w[2] = 5 ; // conduit bien a erreur compilation
// vect <float> v4(5) ; v4 = v1 ; // conduit bien a erreur compilation
}