You are on page 1of 193

Exercices en langage C++

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 problm 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 dj 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 upplm 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 cificits de C+ + ;notions de clas s e , constructe ur e t
destructe ur ;propri ts des fonctions m e m bre ;construction, de s truction e t initialisation de s obje ts ;les
fonctions am ie s ;la surdfinition d'op rate urs ;les conve rsions de type dfinie 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 dbute par un "rappe ldtaill"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 cdents).

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 plte 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 rdaction possible parm i d'autre s ).
Vous y trouve re z une analyse dtaille du problm 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 problm e abord .

1 Le cours com pl
et corre s pondant ces rsum 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 atrise 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 :

- dcide r du bie n-fond de la surdfinition 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 possibilits 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 urcrot, 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 possibilits de "fonctions gnriques" (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 ntionnes 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
its entre C et C+ + ..............................................................1

II. Les spcificits de C+ + ........................................................................7

III. Notions de cl
asse, constructeur et destructeur ......................................... 23

IV. Propri ts des fonctions m em bre .......................................................... 45

V. Construction, destruction et initial


isation des objets................................... 59

VI. Les fonctions am ies ........................................................................... 81

VII. La surdfinition d'oprateurs ............................................................. 9 1

VIII. Les conversions de type dfinies par l


'util
isateur ..................................121

IX. La tech nique de l


'h ritage .................................................................133

X. L'h ritage m ul
tipl
e ...........................................................................153

XI. Les fonctions virtuel


les .....................................................................167

XII. Les fl
ots d'entre et de sortie.............................................................179

XIII. Les patrons de fonctions .................................................................19 5

XIV. Les patrons de cl


asses .....................................................................205

XV. Exercices de synth se.......................................................................225


CH A PITRE I :
INCO M PA TIBILITES ENTRE C ET C+ +

RAPPELS

C+ + e s t "pre s q ue " un sur-e ns e m ble du C, te lq u'ile s t dfini 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 utilise dans un fich ie r source doit obligatoire m e nt avoir fait l'obje t :

- soit d'une dclaration 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 dfinition 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 problm 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 dclar e (auq ue lcas, on considrait, par d faut, q ue s a valeur de
re tour tait de type int), ou e ncore dclar e partie llem e nt (sans fournir le type de ses argum e nts), com m e
dans :

float fexp () ;

Fonctions s ans argum e nts

En C+ + , une fonction sans argum e nt se dfinit (au nive au de l'e n-t te ) e t se dclare (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 :

float fct (void)


2 Exe rcice s e n langage C+ +

Fonctions s ans val


e ur de re tour

En C+ + , une fonction sans valeur de re tour se dfinit (e n-t te ) e t se dclare (prototype ) obligatoire m e nt
l'aide du m ot cl void com m e dans :

void fct (int, double) ;

En C, l'e m ploi du m ot cl void tait, dans ce cas, facultatif.

Le q ual
ificatif cons t

En C+ + , un sym bole globaldclar ave c le q ualificatif const :

- 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 "dfinition" 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".

Exe rcice I.1

___________________________________________________________________________

Enonc

Quelles e rre urs s e ront dte 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 dclaration (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

int g (int, int) ;

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 rle 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 dclares 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 dfinie s par l'utilisate ur"), faire
l'obje t d'un prototype . Nature llem e nt, iln'e s t pas nce 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 dj 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!).

Exe rcice I.2

___________________________________________________________________________

Ecrire corre cte m e nt e n C ce program m e q ui e s t corre ct e n C+ + :

#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
dfinir 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 facilits 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 . (surdfinition 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 possibilits 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 :

cin > > l


val
ue 1 > > l
val
ue 2 > > ......... > > l
val
ue n

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 considr 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 nce s s aire de re groupe r les dclarations e n dbut de fonction ou e n dbut 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 ;
.....

La trans m is s ion par r f re nce 1

En faisant pr cder 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 .

Le s argum e nts par d faut

D ans la dclaration 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 :

float fct (char, int = 10, float = 0.0) ;

Ce s valeurs par d faut s e ront alors utilises 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 cdente dclaration, 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 illgal.

Surd finition de fonctions

En C+ + , ile s t possible, au s e in d'un m m e program m e , q ue plusieurs fonctions poss dent le m m e nom .


D ans ce cas, lors q ue le com pilate ur re ncontre l'appe ld'une te lle fonction, ile ffe ctue le ch oix de la "bonne
fonction" e n te nant com pte de la nature des argum e nts e ffe ctifs. Le s r gles utilises dans une te lle s ituation
ont volu ave c les diff re nte s ve rsions de C+ + , g n ralem e nt dans le s e ns d'un "affine m e nt" : pris e e n
com pte de l'attribut de signe depuis la ve rsion 1.2, distinction de s type s ch ar, sh ort e t int depuis la ve rsion
2.0, pris e e n com pte de l'attribut const pour les pointe urs depuis la ve rsion 2.0 e t pour les r f re nces depuis
la ve rsion 3. D 'une m ani re g n rale, si les r gles utilises par le com pilate ur pour sa re ch e rch e s ont as s e z
intuitive s , leur nonc pr cis e s t as s e z com plexe e t nous ne le rappe lerons pas ici2. Signalons sim plem e nt
q u'e lles pe uve nt faire inte rve nir toute s les conve rsions usuelles (prom otions num riq ue s e t conve rsions
standards - ve ntue llem e nt "dgradante s " depuis la ve rsion 2.0), ainsi que les "conve rsions dfinie s par
l'utilisate ur" e n cas d'argum e nt de type clas s e , condition q u'aucune am bigut n'apparais s e .

1 La notion de rfrence est pl


us gnrale q ue celle de trans m ission d'argum ents. C'e s t toutefois dans cette derni re situation q u'elle
e s t la plus e m ploye.

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+ + ", publis galem ent par les Editions EYROLLES.
II. Le s s pcificits de C+ + 9

R e m arque :

Avant la ve rsion 2, toute dfinition 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 ") surdfinie devait tre s ignale au com pilate ur par une dclaration
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

new type [n]


alloue l'e m place m e nt n ce s s aire pour n l
m ents du type indiq u e t fournit e n r s ultat un pointe ur (de type
type *) sur le pre m ie r lm e nt. O n obtie nt un pointe ur nulsi l'allocation a ch ou . L'indication n e s t
facultative : ave c new type, on obtie nt un e m place m e nt pour un l m ent du type indiq u .

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 nce s s aire de r p te r
le nom bre d'lm 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 dfinir une fonction de votre ch oix e t dem ande r q u'e lle s oit appe le 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 "dve 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 prsente 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
dfinie e n m m e te m ps q u'e lle e s t dclar e (e lle ne pe ut plus tre com pile s par m e nt) e t son en-t te e s t
pr cd du m ot cl inline com m e dans :

inline fct ( ...)


{
.....
}

Exe rcice II.1

___________________________________________________________________________

3 Gnral
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
possibilits d'entr e s -sorties de C+ + , donc e n re m plaant 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

#include <iostream.h> // pour pouvoir utiliser les flots cin et cout


// suivant les implmentations, on pourra rencontrer
// h, hxx ...
main()
{
int n ; float x ;
cout << "donnez un entier et un flottant\n" ;
cin >> n >> x ;
cout << "le produit de " << n << " par " << x << "\n'est : " << n*x ;
}

Exe rcice II.2

___________________________________________________________________________

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 pcificits de C+ + 11

void echange (int *, int *) ; // prototype de la fonction echange


int n=15, p=23 ;
cout << "avant : " << n << " " << p << "\n" ;
echange (&n, &p) ;
cout << "aprs : " << n << " " << p << "\n" ;
}
void echange (int * a, int * b)
{ int c ; // pour la permutation
c = *a ;
*a = *b ;
*b = c ;
}

b) Avec une transm ission par r frence

#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 << "aprs : " << n << " " << p << "\n" ;
}
void echange (int & a, int & b)
{ int c ; // pour la permutation
c = a ;
a = b ;
b = c ;
}

Exe rcice II.3


___________________________________________________________________________

Enonc

Soit le m od le de structure s uivant :

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

a) Avec une transm ission d'adresse

#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 aprs raz : " << s.n << " " << s.x ;
}

b) Avec une transm ission par r frence

#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 aprs raz : " << s.n << " " << s.x ;
}

Exe rcice II.4

___________________________________________________________________________

Enonc

Soie nt les dclarations (C+ + ) suivante s :

overload fct // inutile depuis la version 2.0


int fct (int) ; // fonction I
int fct (float) ; // fonction II
void fct (int, float) ; // fonction III
void fct (float, int) ; // fonction IV
II. Le s s pcificits de C+ + 13

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 le 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

Le s cas a, b, c e t d ne pos e nt aucun problm e , q ue lle q ue s oit la ve rsion de C+ + utilise. Il y a


re s pe ctive m e nt appe ldes fonctions I, II, III e t IV, sans qu'aucune conve rsion d'argum e nt ne s oit n ce s s aire .

e ) Appelde la fonction I, apr s conve rsion de la valeur de c e n int.

f) Appelincorre ct, com pte te nu de s on am bigut ;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.

g) Appelde la fonction III, apr s conve rsion de c en float.

h ) Appelde la fonction III, apr s conve rsion (dgradante ) 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 bigut de l'appe l).

i) Appelincorre ct, com pte te nu de s on am bigut ;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 dgradante 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 !).

Exe rcice II.5

___________________________________________________________________________

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 possibilits de dclarations dynam iq ue s offe rte s par C+ + (c'e s t- -
dire q ue l'on pe ut introduire une dclaration n'im porte q ue le m place m e nt d'un program m e ), e t crire , par
e xe m ple :

int * adi = new int ;


double * add = new double [100] ;

Exe rcice II.6

___________________________________________________________________________

Enonc

Ecrire plus sim plem e nt e n C+ + , e n utilisant les s p cificits 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] ;

Exe rcice II.7

___________________________________________________________________________
II. Le s s pcificits 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 dbordem e nt m m oire .
L'e x cution s e pr s e nte ra ainsi :

Taille souhaite ? 6000


Allocation bloc numro : 1
Allocation bloc numro : 2
Allocation bloc numro : 3
Allocation bloc numro : 4
Allocation bloc numro : 5
Mmoire insuffisante - arrt excution

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) :

#include <stdlib.h> // pour exit


#include <iostream.h>
main()
{
long taille ;
int * adr ;
int nbloc ;

cout << "Taille souhaite ? " ;


cin >> taille ;

for (nbloc=1 ; ; nbloc++)


{ adr = new int [taille] ;
if (adr) cout << "Allocation bloc numro : " << nbloc << "\n" ;
else { cout << "Mmoire insuffisante - arrt excution \n " ;
exit (1) ;
}
}
}

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 le 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 nce 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+ +

#include <stdlib.h> // pour exit


#include <iostream.h>
main()
{
void deborde () ; // proto fonction appele en cas manque mmoire
void set_new_handler ( void (*) () ) ; // proto de set_new_handler
set_new_handler (&deborde) ; // prcise quelle sera la fonction
// appele en cas de manque mmoire
long taille ;
int * adr ;
int nbloc ;

cout << "Taille souhaite ? " ;


cin >> taille ;
for (nbloc=1 ; ; nbloc++)
{ adr = new int [taille] ;
cout << "Allocation bloc numro : " << nbloc << "\n" ;
}
}

void deborde () // fonction appele en cas de manque mmoire


{
cout << "Mmoire insuffisante - arrt excution \n " ;
exit (1) ;
}

Exe rcice II.8

___________________________________________________________________________

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) ; // dclaration (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) // dfinition 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-ilprocder si l'on souh aitait q ue la fonciton fct soit com pile s par m e nt?
II. Le s s pcificits de C+ + 17

___________________________________________________________________________

Sol
ution

a) Nous devons donc d'abord d clare r (e t dfinir 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 dclaration de fct n'y e s t plus
n ce s s aire puis q u'e lle appara
t dj 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 pile 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 dm 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 dclaration de la clas s e e n q ue s tion, l'int rie ur de laq ue lle appara tront les
"dclarations-dfinitions" des fonctions "e n ligne ".
CH A PITRE III :
NO TIO NS D E CLASSE,
CO NSTRUCTEUR ET D ESTRUCTEUR

RAPPELS

Le s possibilits 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 dfini 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 donne") e t des fonctions (on parle de "fonctions m e m bre " ou de m th ode s ).
En P.O .O . pure , les donnes sont "e ncapsule 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 donnes 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 donne 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 :

/* ------------ Dclaration de la classe point ------------- */


class point
{ /* dclaration des membres privs */
int x ;
int y ;
/* dclaration des membres publics */
public :
void initialise (int, int) ;
void deplace (int, int) ;
void affiche () ;
} ;

La d finition d'une clas s e consiste fournir les dfinitions 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

1 En toute rigueur, C+ + vous perm et gal


em ent d'as s ocier des fonctions m em bre des structures, des unions ou des num rations.
D ans ce cas, toutefois, aucune encaps ulation n'est possible (ce q ui revient dire que tous les m em bres sont "publics ").
24 Exe rcice s e n langage C+ +

dfinition 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 dfinition de
la fonction initialise de la clas s e pr cdente :

void point::initialise (int abs, int ord)


{
x = abs ; y = ord ;
}

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 dclaration 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 cder 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 accder 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.

A ffe ctation e ntre obje ts

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 donnes (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 "surdfinissant"
l'op rate ur d'affe ctation pour la clas s e conce rn e (voye z le ch apitre consacr la surdfinition
d'op rate urs).
III. Notions de clas s e , cons tructe ur e t de s tructe ur 25

Cons tructe ur e t de s tructe ur

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 dclare 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 dfinition 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 dclaration ou sa dfinition).

Une fonction m e m bre portant le m m e nom q ue s a clas s e , pr cd du sym bole tilda (~ ), s e nom m e un
destructeur. Le destructe ur e s t appe l avant la libration de l'e s pace m m oire associ l'obje t. Par
dfinition 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 ).

M e m bre s donn e s tatiq ue s

Un m e m bre donne dclar 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 dclar . Un m e m bre donne statiq ue e s t initialis par
dfaut 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 spcifie 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 dclaration 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 dfinition de la clas s e , c'e s t-
-dire la dfinition de s e s fonctions m e m bre .

Exe rcice III.1

___________________________________________________________________________

Enonc

R aliser une clas s e point pe rm e ttant de m anipuler un point d'un plan. O n pr voira :

- un constructe ur re ce vant e n argum e nts les coordonn e s (float) d'un point,


- une fonction m e m bre de place e ffe ctuant une translation d finie par ses deux argum e nts (float),
- une fonction m e m bre affich e s e conte ntant d'affich e r les coordonn e s cart s ie nnes du point.
Le s coordonnes du point s e ront des m e m bres donne priv s .

O n crira s par m e nt :

- un fich ie r source constituant la dclaration de la clas s e ,


- un fich ie r source corre s pondant sa dfinition.
26 Exe rcice s e n langage C+ +

Ecrire , par ailleurs, un pe tit program m e d'essai (m ain) dclarant un point, l'affich ant, le dplaant e t
l'affich ant nouve au.

___________________________________________________________________________

Sol
ution

a) Fich ier source (nom m point.h ) contenant l


a d cl
aration de l
a cl
asse

/* fichier POINT1.H */
/* dclaration de la classe point */
class point
{
float x, y ; // coordonnes (cartsiennes) du point
public :
point (float, float) ; // constructeur
void deplace (float, float) ; // dplacement
void affiche () ; // affichage
} ;

b) Fich ier source contenant l


a d finition de l
a cl
asse

/* dfinition de la classe point */


#include "point1.h"
#include <iostream.h>
point::point (float abs, float ord)
{ x = abs ; y = ord ;
}
void point::deplace (float dx, float dy)
{ x = x + dx ; y = + dy ;
}
void point::affiche ()
{ cout << "Mes coordonnes cartsiennes sont " << x << " " << y << "\n" ;
}

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 dclaration de la clas s e point.

c) Exem pl
e d'util
isation de l
a cl
asse

/* exemple d'utilisation de la classe point */


#include <iostream.h>
#include "point1.h"
main ()
{
point p (1.25, 2.5) ; // construction d'un point de coordonnes 1.25 2.5
p.affiche () ; // affichage de ce point
p.deplace (2.1, 3.4) ; // dplacement de ce point
p.affiche () ; // nouvel affichage
}
III. Notions de clas s e , cons tructe ur e t de s tructe ur 27

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 dfinition 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 procder ainsi :

#ifndef POINT_H
#define POINT_H
.....

dclaration de la clas s e .....

.....

#endif

Exe rcice III.2

___________________________________________________________________________

Enonc

R aliser une clas s e point, analogue la pr cdente , m ais ne com portant pas de fonction affich e . Pour
re s pe cte r le principe d'encapsulation des donnes, prvoir 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'ordonne d'un point. Adapte r le
pe tit program m e d'essai pr cdent 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 dclaration de la clas s e e s t alors :

/* fichier POINT2.H */
/* dclaration de la classe point */
class point
{
float x, y ; // coordonnes (cartsiennes) du point
public :
point (float, float) ; // constructeur
void deplace (float, float) ; // dplacement
float abscisse () ; // abscisse du point
float ordonnee () ; // ordonne du point
} ;

Voici sa nouve lle dfinition :

/* dfinition de la classe point */


#include "point2.h"
#include <iostream.h>
point::point (float abs, float ord)
{ x = abs ; y = ord ;
}
28 Exe rcice s e n langage C+ +

void point::deplace (float dx, float dy)


{ x = x + dx ; y = + dy ;
}
float point::abscisse ()
{ return x ;
}
float point::ordonnee ()
{ return y ;
}

Et le nouve au program m e d'essai :

/* exemple d'utilisation de la classe point */


#include <iostream.h>
#include "point2.h"
main ()
{
point p (1.25, 2.5) ; // construction
// affichage
cout << "Coordonnes cartsiennes : " << p.abscisse () << " "
<< p.ordonnee () << "\n" ;
p.deplace (2.1, 3.4) ; // dplacement
// affichage
cout << "Coordonnes cartsiennes : " << p.abscisse () << " "
<< p.ordonnee () << "\n" ;
}

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 accder (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 plm 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 plutt 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.

Exe rcice III.3

___________________________________________________________________________

Enonc

Ajoute r la clas s e pr cdente (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 coordonnes pol
aires du point.
___________________________________________________________________________
III. Notions de clas s e , cons tructe ur e t de s tructe ur 29

Sol
ution

La dclaration de la nouve lle clas s e point dcoule directe m e nt de l' nonc :

/* fichier POINT3.H */
/* dclaration de la classe point */
class point
{
float x, y ; // coordonnes (cartsiennes) du point
public :
point (float, float) ; // constructeur
void deplace (float, float) ; // dplacement
void homothetie (float) ; // homothetie
void rotation (float) ; // rotation
float abscisse () ; // abscisse du point
float ordonnee () ; // ordonne du point
float rho () ; // rayon vecteur
float theta () ; // angle
} ;

Sa dfinition 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 dfinition 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 gnralem e nt priv e s ).

Voici finalem e nt la dfinition de notre clas s e point :

/**************** dclarations de service *****************/


#include "point3.h"
#include <iostream.h>
#include <math.h> // pour sqrt et atan
const float pi = 3.141592653 ; // valeur de pi
float angle (float, float) ; // fonction de service (non membre)

/*************** dfinition des fonctions membre **********/


point::point (float abs, float ord)
{ x = abs ; y = ord ;
}
void point::deplace (float dx, float dy)
{ x += dx ; y += dy ;
}
void point::homothetie (float hm)
{ x *= hm ; y *= hm ;
}
30 Exe rcice s e n langage C+ +

void point::rotation (float th)


{ float r = sqrt (x*x + y*y) ; // passage en
float t = angle (x, y) ; // coordonnes polaires
t += th ; // rotation th
x = r * cos (t) ; // retour en
y = r * sin (t) ; // coordonnes cartsiennes
}

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) ;

} /********** dfinition des fonctions de service ***********/


/* fonction de calcul de l'angle correspondant aux coordonnes */
/* cartsiennes fournies en argument */
/* On choisit une dtermination entre -pi et +pi (0 si x=0) */

float angle (float x, float y)


{ float a = x ? atan (y/x) : 0 ;
if (y<0) if (x>=0) return a + pi ;
else return a - pi ;
return a ;
}

Exe rcice III.4

___________________________________________________________________________

Enonc

M odifie r la clas s e point pr cdente , de m ani re ce q ue les donnes (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
dclaration 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

La dclaration de la nouve lle classe dcoule directe m e nt de l' nonc :

/* fichier POINT4.H */
/* dclaration 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 ; // coordonnes (polaires) du point
public :
point (float, float) ; // constructeur
void deplace (float, float) ; // dplacement
void homothetie (float) ; // homothtie
void rotation (float) ; // rotation
float abscisse () ; // abscisse du point
float ordonnee () ; // ordonne du point
float rho () ; // rayon vecteur
float theta () ; // angle
} ;

En ce q ui conce rne s a dfinition, ile s t m ainte nant n ce s s aire de re m arq ue r :

- 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 dplace 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 dfinition 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 cdent) :

#include "point4.h"
#include <iostream.h>
#include <math.h> // pour cos, sin, sqrt et atan
const int pi = 3.141592635 ; // valeur de pi

/********** dfinition des fonctions de service ***********/


/* fonction de calcul de l'angle correspondant aux coordonnes */
/* cartsiennes fournies en argument */
/* On choisit une dtermination entre -pi et +pi (0 si x=0) */
float angle (float x, float y)
{ float a = x ? atan (y/x) : 0 ;
if (y<0) if (x>=0) return a + pi ;
else return a - pi ;
return a ;
}
/********** dfinition des fonctions membre *****************/
point::point (float abs, float ord)
{ r = sqrt (abs*abs + ord*ord) ;
t = atan (ord/abs) ;
}
void point::deplace (float dx, float dy)
{ float x = r * cos (t) + dx ; // nouvelle abscisse
float y = r * sin (t) + dy ; // nouvelle ordonne
r = sqrt (x*x + y*y) ;
t = angle (x, y) ;
}
void point::homothetie (float hm)
32 Exe rcice s e n langage C+ +

{ 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 ;
}

Exe rcice III.5

___________________________________________________________________________

Enonc

Soit la clas s e point cre dans l'e xe rcice III.1, e t dont la dclaration 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 coordonnes 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
dcr 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, dfinie par e xe m ple,
e n m m e te m ps q ue la clas s e ;ce tte dm 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.

Voici la nouve lle dclaration de notre clas s e point :

/* fichier POINT5.H */
/* dclaration de la classe point */
class point
{
static nb_pts ; // compteur du nombre d'objets crs
float x, y ; // coordonnes (cartsiennes) du point
public :
point (float, float) ; // constructeur
~point () ; // destructeur
void deplace (float, float) ; // dplacement
void affiche () ; // affichage
} ;

Et voici sa nouve lle dfinition :

#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 coordonnes "<< 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 .

Exe rcice III.6

___________________________________________________________________________

2 En toute rigueur, ilreste toutefois possibl


e d'initialiser un telm em bre depuis l'extrieur de la classe.
34 Exe rcice s e n langage C+ +

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 lm e nt,
conna tre s on "cardinal" (nom bre d'lm e nts), savoir si un caract re donn lui appartie nt.

Ici, on n'e ffe ctue ra aucune al


location dynam ique d'em pl
acem ents m m oire. Ilfaudra donc pr voir, e n
m e m bre donne, un tableau de taille fixe .

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 lm 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 signs. 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 plm e ntation donn e (g n ralem e nt 256).

Le re s te de la dclaration de la classe dcoule de l' nonc .

/* fichier SETCHAR1.H */
/* dclaration de la classe set_char */
#define N_CAR_MAX 256 // on pourrait utiliser UCHAR_MAX dfini
// dans <limits.h>

class set_char
{
unsigned char ens [N_CAR_MAX] ;
// tableau des indicateurs (prsent/absent)
// pour chacun des caractres possibles
public :
set_char () ; // constructeur
void ajoute (unsigned char) ; // ajout d'un lment
int appartient (unsigned char) ; // appartenance d'un lment
int cardinal () ; // cardinal de l'ensemble
} ;

La dfinition de la clas s e e n d coule as s e z nature llem e nt :

/* dfinition de la classe set_char */


#include "setchar1.h"
set_char::set_char ()
{ int i ;
for (i=0 ; i<N_CAR_MAX ; i++) ens[i] = 0 ;
}

void set_char::ajoute (unsigned char c)


{ ens[c] = 1 ;
}

int set_char::appartient (unsigned char c)


III. Notions de clas s e , cons tructe ur e t de s tructe ur 35

{ 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 ;
}

Ile n va de m m e pour le program m e d'utilisation :

/* utilisation de la classe set_char */


#include <iostream.h>
#include <string.h>
#include "setchar1.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 () << " caractres diffrents" ;
}

R e m arque :

Si l'on avait dclar 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'accder 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 plm 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'lm 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 bn 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'lm e nts s e ra tr s grand.

Exe rcice III.7

___________________________________________________________________________

Enonc

M odifie r la clas s e s e t_ch ar pr cdente , 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 lm e nts de l'e ns e m ble. O n nom m e ainsi un m canism e pe rm e ttant d'accder
s q ue ntie llem e nt aux diff re nts lm 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' lm e nt suivant lors q u'ile xiste e t e xiste , q ui pr cis e s 'il
e xiste e ncore un lm e nt non e xplor .

O n com plte ra alors le program m e d'utilisation pr cdent, 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 plm 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 lm e nt du tableau e ns. Nous convie ndrons q ue courant
dsigne le pre m ie r lm 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 dsigne le
dernie r lm 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 donne
supplm e ntaire (fin) valant 0 dans les cas usuels, e t 1 lors q ue aucun lm e nt ne s e ra disponible (pour
suivant).

Le rle 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' lm 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 lm e nt n' tait plus disponible). Dans le pre m ie r cas, suivant re ch e rch e ra le
proch ain lm e nt de l'e ns e m ble (e n m odifiant la valeur de fin lors q u'un te l lm e nt n'e xiste pas). Note z bie n
q ue la fonction suivant doit re nvoye r, non pas le proch ain lm e nt, m ais l' lm 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 lm e nt courant.

Voici la nouve lle dfinition de la clas s e s e t_ch ar :

/* fichier SETCHAR2.H */
/* dclaration de la classe set_char */
#define N_CAR_MAX 256 // on pourrait utiliser UCHAR_MAX dfini
// dans <limits.h>

class set_char
{
unsigned char ens [N_CAR_MAX] ;
// tableau des indicateurs (prsent/absent)
// pour chacun des caractres 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 lment
int appartient (unsigned char) ; // appartenance d'un lment
int cardinal () ; // cardinal de l'ensemble
void init () ; // initialisation itration
unsigned char suivant () ; // caractre suivant
int existe () ; //
} ;
III. Notions de clas s e , cons tructe ur e t de s tructe ur 37

Voici la dfinition 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 ;
}

unsigned char set_char::suivant ()


{ if (fin) return 0 ; // au cas o on serait dj en fin de ens
unsigned char c = courant ; // conservation du caractre courant
// et recherche du suivant s'il existe
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 ; // s'il n'y a plus de caractre
return c ;
}

int set_char::existe ()
{ return (!fin) ;
}

Voici, e nfin l'adaptation du program m e d'utilisation :

#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 () << " caractres diffrents"
<< " qui sont :\n" ;
ens.init() ; // init itration sur les caractres de l'ensemble
while (ens.existe())
cout << ens.suivant () ;
}
CH A PITRE IV :
PRO PRIETES D ES FO NCTIO NS M EM BRE

RAPPELS

Surd finition de s fonctions m e m bre e t argum e nts par d faut

Ils'agit sim plem e nt de la g n ralisation aux fonctions m e m bre des possibilits dj 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 dfinition de la fonction dans la dclaration 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)
{ dfinition de fcte nlig
}
.....
} ;

- soit procder com m e pour une fonction "ordinaire ", e n fournissant une dfinition e n de h ors de la
dclaration de la clas s e ;dans ce cas, le q ualificatif inline doit appara
tre , la fois devant la dclaration
e t devant l'e n-t te .

Cas d e s obje ts trans m is e n argum e nt d'une fonction m e m bre

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 donne dans un em place m e nt local la
fonction appe le . Des problm 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 gls 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.

Cas d e s fonctions m e m bre fournis s ant un obje t e n re tour

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 sr 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 problm 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 utilises 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 concide 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 .

Fonctions m e m bre s tatiq ue s

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
dclare r ave c l'attribut static. D ans ce cas, une te lle fonction pe ut tre appe le , 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 (::).

Fonctions m e m bre cons tante s

O n pe ut dclare r de s obje ts constants ( l'aide du qualificatif const). Dans ce cas, seules les fonctions
m e m bre dclar e s (e t dfinie s ) ave c ce m m e q ualificatif (e xe m ple de dclaration : 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.

Exe rcice IV.1

___________________________________________________________________________
IV. Proprits 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 dclaration s e pr s e nte ainsi :

class vecteur3d
{ float x, y, z ; // pour les 3 composantes (cartsiennes)
.....
} ;

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) :

a) e n utilisant des fonctions m e m bre s urd finie s ,


b) e n utilisant une s e ule fonction m e m bre ,
c) e n utilisant une s e ule fonction "e n ligne ".
___________________________________________________________________________

Sol
ution

Ils'agit de sim ples applications des possibilits 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)
/* dclaration de la classe vecteur3d */
class vecteur3d
{
float x, y, z ;
public :
vecteur3d () ; // constructeur sans arguments
vecteur3d (float, float, float) ; // constructeur 3 composantes
.....
} ;

/* dfinition des constructeurs de la classe vecteur3d */


vecteur3d::vecteur3d ()
{ x = 0 ; y = 0 ; z = 0 ;
}
vecteur3d::vecteur3d (float c1, float c2, float c3)
{ x = c1 ; y = c2 ; z = c3 ;
}

b)
/* dclaration de la classe vecteur3d */
class vecteur3d
{
float x, y, z ;
public :
vecteur3d (float=0.0, float=0.0, float=0.0) ; // constructeur (unique)
.....
} ;
/* dfinition du constructeur de la classe vecteur3d */
48 Exe rcice s e n langage C+ +

vecteur3d::vecteur3d (float c1, float c2, float c3)


{ x = c1 ; y = c2 ; z = c3 ;
}

O n note ra toute fois q u'ave c ce constructe ur ile s t possible de dclare 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 cdente .

c)
/* dclaration 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 dfinition de constructe ur, puis q ue ce dernie r e s t "e n ligne ".

Exe rcice IV.2

___________________________________________________________________________

Enonc

Soit une clas s e ve cte ur3d dfinie com m e s uit :

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 :

a) e n utilisant une transm ission par valeur,


b) e n utilisant une transm ission par adre s s e ,
c) e n utilisant une transm ission par r f re nce .
Si v1 e t v2 d s igne nt 2 ve cte urs de type ve cte ur3d, com m e nt s' crit le te s t de concide nce de ce s 2 ve cte urs,
dans ch acun de s 3 cas considrs?

___________________________________________________________________________
IV. Proprits 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 possdera 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 concide nce , 0 dans le cas contraire ).

a) La dclaration de coincide pourra s e pr s e nte r ainsi :

int coincide (vecteur3d) ;

Voici ce q ue pourrait tre s a dfinition :

int vecteur3d::coincide (vecteur3d v)


{ if ( (v.x == x) && (v.y == y) && (v.z == z) ) return 1 ;
else return 0 ;
}

b) La dclaration de coincide devie nt :

int coincide (vecteur3d *) ;

Et sa nouve lle dfinition pourrait tre :

int vecteur3d::coincide (vecteur3d * adv)


{ if ( (adv->x == x) && (adv->y == y) && (adv->z == z) )
return 1 ;
else return 0 ;
}

R e m arque :

En utilisant th is, la dfinition 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 ;
}

c) La dclaration de coincide devie nt :

int coincide (vecteur3d &) ;

Et sa nouve lle dfinition e s t :

int vecteur3d::coincide (vecteur3d & v)


{ if ( (v.x == x) && (v.y == y) && (v.z == z) ) return 1 ;
else return 0 ;
}
50 Exe rcice s e n langage C+ +

Note z q ue le corps de la fonction e s t re s t le m m e q u'e n a.

Voici les 3 appe ls de coincide corre s pondant re s pe ctive m e nt aux 3 d finitions prcdente s :

a) v1.coincide (v2) ; ou v2.coincide (v1) ;

b) v1.coincide (&v2) ou v2.coincide (&v1) ;

c) v1.coincide (v2) ou v2.coincide (v1) ;

D is cus s ion

La surdfinition d'op rate ur offrira une m ise en oeuvre plus agr able de ce te s t de concide 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.

Exe rcice IV.3

___________________________________________________________________________

Enonc

Soit une clas s e ve cte ur3d dfinie com m e s uit :

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 :

a) le r s ultat e s t re nvoy par valeur,


b) le r s ultat e s t re nvoy par r f re nce , l'argum e nt (e xplicite ) tant galem e nt transm is par r f re nce .
c) le r s ultat e s t re nvoy par adre s s e , l'argum e nt (e xplicite ) tant galem e nt transm is par adre s s e .
___________________________________________________________________________

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 dfinition de la fonction norm ax (la dclaration e n d coule
im m diate m e nt) :

vecteur3d vecteur3d::normax (vecteur3d v)


{ float norm1 = x*x + y*y + z*z ;
float norm2 = v.x*v.x + v.y*v.y + v.z*v.z ;
if (norm1>norm2) return *this ;
else return v ;
IV. Proprits des fonctions m e m bre 51

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 problm 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 problm 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 :

vecteur3d & vecteur3d::normax (vecteur3d & v)

La fonction norm ax s'utilise com m e pr cdem m e nt.

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 :

vecteur3d * vecteur3d::normax (vecteur3d * adv)


{ float norm1 = x * x + y * y + z * z ;
float norm2 = adv->x * adv->x + adv->y * adv->y + adv->z * adv->z ;
if (norm1>norm2) return this ;
else return adv ;
}

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 lproblm 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.

Exe rcice IV.4

___________________________________________________________________________

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 :

- un constructe ur, ave c des valeurs par d faut (0),


- une fonction d'affich age des 3 com posantes du ve cte ur, sous la form e :
< composante1, composante2, composante3 >
52 Exe rcice s e n langage C+ +

- une fonction pe rm e ttant d'obte nir la som m e de 2 ve cte urs,


- une fonction pe rm e ttant d'obte nir le produit scalaire de 2 ve cte urs.
O n ch oisira les m odes de transm ission les m ie ux appropri s . O n crira un pe tit program m e utilisant la clas s e
ainsi ralise.

_______________________________________________________________

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 donne), la transm ission par r f re nce e s t plus e fficace (e n te m ps
d'excution). Qui plus e s t, la fonction som m e re oit dj 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
dtruit 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 dclaration de notre clas s e ve cte ur3d (ici, s e ulle constructe ur a t pr vu "e n
ligne " :

/* dclaration de la classe vecteur3d */


class vecteur3d
{ float x, y, z ;
public :
vecteur3d (float c1=0, float c2=0, float c3=0) // constructeur
{ x=c1 ; y=c2 ; z=c3;
}
vecteur3d somme (vecteur3d &) ; // somme (rsultat par valeur)
float prodscal (vecteur3d &) ; // produit scalaire
void affiche () ; // affichage composantes
} ;

Voici sa dfinition :

/* dfinition de la classe vect3d */


#include <iostream.h>
vecteur3d vecteur3d::somme (vecteur3d & v)
{ vecteur3d res ;
res.x = x + v.x ;
res.y = y + v.y ;
res.z = z + v.z ;
return res ;
}
float vecteur3d::prodscal (vecteur3d & v)
{ return ( v.x * x + v.y * y + v.z * z) ;
}
void vecteur3d::affiche ()
IV. Proprits des fonctions m e m bre 53

{ 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 :

/* programme d'essai de la classe vecteur3d */


main()
{ vecteur3d v1 (1,2,3), v2 (3,0, 2), w ;
cout << "v1 = " ; v1.affiche () ; cout << "\n" ;
cout << "v2 = " ; v2.affiche () ; cout << "\n" ;
cout << "w = " ; w.affiche () ; cout << "\n" ;
w = v1.somme (v2) ;
cout << "w = " ; w.affiche () ; cout << "\n" ;
cout << "V1.V2 = " << v1.prodscal (v2) << "\n" ;
}
________________________

v1 = < 1, 2, 3>
v2 = < 3, 0, 2>
w = < 0, 0, 0>
w = < 4, 2, 5>
V1.V2 = 9

Exe rcice IV.5

___________________________________________________________________________

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 dclare ra donc ainsi :

static int compte () ;

Voici sa dfinition :

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 dclar e :

cout << "il y a " << point::compte () << "points\n" ;


54 Exe rcice s e n langage C+ +
CH A PITRE V :
CO NSTRUCTIO N, D ESTRUCTIO N
ET INITIA LISATIO N D ES O BJETS

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 libration de l'e s pace m m oire associ l'obje t.

Le s obje ts autom atiq ue s e t s tatiq ue s

Le s obje ts autom atiques sont cr s par une dclaration 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 dclaration (laq ue lle, e n C+ + , pe ut appara
tre n'im porte o dans
un program m e ). Ils sont dtruits lors q ue l'on sort de la fonction ou du bloc.

Le s obje ts statiques sont cr s par une dclaration situ e e n de h ors de toute fonction ou par une dclaration
pr cde du m ot cl static (dans une fonction ou dans un bloc). Ils sont cr s avant l'e ntre dans la fonction
m ain e t dtruits 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 dtruit 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 :

a = point (1.5, 2.25) ;

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.

1 Ne confondez pas une tel


le affectation avec une initialisation d'un objet lors de sa dclaration,
60 Exe rcice s e n langage C+ +

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 possdant 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 dure de vie dfinie a priori. Ils sont dtruits 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 :

- dclaration d'un obje t ave c initialise ur, com m e dans :


point a = 5 ; // il doit exister un constructeur un argument de type entier
point b = a ; // il doit exister un constructeur un argument de type point

- 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 prcism e nt :

- si aucun constructe ur de l'une des form e s type (type & ) ou type (const type & ) (type dsignant le type
de l'obje t) n'a t dfini, 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 donne de l'obje t, com m e le fait l'affe ctation. D e s problm 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 prvoir la re copie de tous les m e m bres de l'obje t.

R e m arque : une exception

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 cde de la cr ation d'un obje t te m poraire . La
dclaration pr cdente 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 prciser 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 prciser, en plus, son nom bre d'lm 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 possdant un constructe ur sans argum e nt (ou, situation g n ralem e nt dcons e ille ,
sans constructe ur), la dclaration :

point courbe [20] ;

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 :

point * adcourbe = new point [20] ;

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'lm e nts du tableau). Par e xe m ple, pour d truire le tableau pr cdent, on
crira :

delete [] adcourbe ; // depuis la version 3.0

ou :

delete [20] adcourbe ; // versions antrieures la 3.0

R e m arque :

En th orie , depuis la ve rsion 2.0 de C+ + , on pe ut com plte r la dclaration 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 !).

Cons truction d'obje ts conte nant de s obje ts m e m bre

Une clas s e pe ut possder un m e m bre donne qui est lui-m m e de type clas s e . En voici un e xe m ple ;si nous
avons dfini :

class point
{ float x, y ;
public :
point (float, float) ;
.....
} ;

nous pouvons dfinir 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 dfinition du constructe ur de pointcol ;par e xe m ple, ave c :

pointcol::pointcol (float abs, float ord, int coul) : p (abs, ord)


{ ...
}

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 faon 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 dfini son
constructe ur par re copie ;
- obje t principaln'ayant pas dfini 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 .

Exe rcice V.1

___________________________________________________________________________

Enonc

Com m e nt conce voir le type clas s e ch os e de faon ce q ue ce pe tit program m e :

main()
{
chose x ;
cout << "bonjour\n" ;
}

fournis s e les r s ultats suivants :

cration objet de type chose


bonjour
destruction objet de type chose
V. Construction, de s truction e t initialisation de s obje ts 63

Que fournira alors l'e x cution de ce program m e (utilisant le m m e type ch os e ) :

main()
{
chose * adc = new chose
}

___________________________________________________________________________

Sol
ution

Ilsuffit de prvoir dans le constructe ur de ch os e , l'instruction :

cout << "cration objet de type chose\n" ;

e t dans le destructe ur, l'instruction :

cout << "destruction objet de type chose\n" ;

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 dtruire ) :

cration objet de type chose

Exe rcice V.2

___________________________________________________________________________

Enonc

Quels s e ront les r s ultats fournis par l'e x cution du program m e s uivant (ici, la dclaration de la clas s e de m o,
sa dfinition 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
} ;

demo::demo (demo & d)


{ cout << "constructeur II (recopie) : " << d.x << " " << d.y << "\n" ;
x = d.x ; y = d.y ;
}
demo::~demo ()
{ cout << "destruction : " << x << " " << y << "\n" ;
}
64 Exe rcice s e n langage C+ +

main ()
{
void fct (demo, demo *) ; // proto fonction indpendante fct
cout << "dbut 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" ;
}

void fct (demo d, demo * add)


{ cout << "entre fct\n" ;
delete add ;
cout << "sortie fct\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) :

dbut 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, ..)
(cre un objet tem poraire)
entre 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

4 Ce point n'a en fait d'im portance q ue pour l


e s objets tem poraires.
V. Construction, de s truction e t initialisation de s obje ts 65

obje t e s t e ffe ctive m e nt dtruit (ici, apr s l'e x cution du m ain, m ais, dans d'autre s im plm e ntations, ce la
pe ut s e produire plus tt).

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 libr 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 plm 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 problm es d'encom bre m e nt de la
m m oire pe uve nt appara tre dans les im plm 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.

Exe rcice V.3

___________________________________________________________________________

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 donne
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
dtruit. 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
dtruisant.

___________________________________________________________________________

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
dfinissons, au s e in de la clas s e point, un m e m bre donne 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 dclaration (d finition) de notre clas s e , accom pagne 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 << "cration point numro : " << num << "\n" ;
}
~point ()
{ cout << "Destruction point numro : " << num << "\n" ;
}
} ;

main()
{ point * adcourb = new point [4] ;
66 Exe rcice s e n langage C+ +

int i ;
delete [4] adcourb ;
}

Exe rcice V.4

___________________________________________________________________________

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 lm e nt,
conna tre s on "cardinal" (nom bre d'lm e nts), savoir si un entie r donn lui appartie nt.

Ici, on cons e rve ra les diff re nts lm 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'lm 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

1) La dclaration de la classe dcoule de l' nonc :

/* fichier SETINT1.H */
/* dclaration de la classe set_int */
class set_int
{
int * adval ; // adresse du tableau des valeurs
int nmax ; // nombre maxi d'lments
int nelem ; // nombre courant d'lments
public :
set_int (int = 20) ; // constructeur
~set_int () ; // destructeur
void ajoute (int) ; // ajout d'un lment
int appartient (int) ; // appartenance d'un lment
int cardinal () ; // cardinal de l'ensemble
} ;

Le m e m bre donne 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, rangs dans l'ordre o ils s e ront fournis ajoute , e t
non plus un e m place m e nt prdte rm in , com m e nous l'avions fait pour les caract re s (dans les e xe rcice s
du ch apitre pr cdent).

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 libration 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 .

Voici la dfinition de notre clas s e :


V. Construction, de s truction e t initialisation de s obje ts 67

#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 ; // libration tableau de valeurs
}

void set_int::ajoute (int nb)


{ // on examine si nb appartient dj l'ensemble
// en utilisant la fonction membre appartient
// s'il n'y appartient pas et si l'ensemble n'est pas plein
// on l'ajoute
if (!appartient (nb) && (nelem<nmax)) adval [nelem++] = nb ;
}
int set_int::appartient (int nb)
{ int i ;
// on examine si nb appartient dj l'ensemble
// (dans ce cas i vaudra nele en fin de boucle)
while ( (i<nelem) && (adval[i] != nb) ) i++ ;
return (i<nelem) ;
}
int set_int::cardinal ()
{ return nelem ;
}

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 dj 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 lm e nt un e ns e m ble dj
"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.

2) Voici le program m e d'utilisation de m and :

#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 diffrents\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 donne 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 problm 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 problm 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 problm 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 lgante 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
surdfinition de l'op rate ur d'affe ctation (q ui pose des problm e s analogue s ce ux q ue nous ve nons
d'voq ue r).

Pour l'instant, nous utiliserons une dm 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 procder 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 dfini).

Nous ajoute rons donc, dans la dclaration de notre clas s e :

set_int (set_int &) ; // constructeur par recopie

Et, dans sa dfinition :

set_int::set_int (set_int & e)


{ adval = new int [nmax = e.nmax] ; // allocation nouveau tableau
nelem = e.nelem ;
int i ;
for (i=0 ; i<nelem ; i++) // copie ancien tableau dans nouveau
adval[i] = e.adval[i] ;
}

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 upplm e ntaire . Elle a l'inconv nie nt de dupliq ue r totalem e nt l'obje t, ce q ui
e ntrane 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 dlicate 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 surdfinition 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 possderait 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 dfinir 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 redfinir l'op rate ur d'affe ctation.

Exe rcice V.5

___________________________________________________________________________
V. Construction, de s truction e t initialisation de s obje ts 69

Enonc

M odifie r l'im plm e ntation de la clas s e pr cdente (ave c son constructe ur par re copie ) de faon 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 ane (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 dclaration) 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 faon.

___________________________________________________________________________

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 lment 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 dfinie indiff re m m e nt dans la dclaration 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 donne privs 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 lm 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 ).

Voici donc la nouve lle dclaration de notre clas s e :

/* fichier SETINT3.H */
/* dclaration de la classe set_int */
struct noeud
{ int valeur ; // valeur d'un lment de l'ensemble
noeud * suivant ; // pointeur sur le noeud suivant de la liste
} ;

class set_int
{
noeud * debut ; // pointeur sur le dbut de la liste
int nelem ; // nombre courant d'lments
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 lment
int appartient (int) ; // appartenance d'un lment
int cardinal () ; // cardinal de l'ensemble
} ;
70 Exe rcice s e n langage C+ +

La dfinition 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 dbut de liste e t incr m e nte le nom bre d'lm 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 librer 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 .

Voici la nouve lle dfinition de notre clas s e :

#include <stdlib.h> // pour NULL


#include "setint3.h"

set_int::set_int (int dim) // dim est conserv pour la compatibilit


// avec l'ancienne classe
{ debut = NULL ;
nelem = 0 ;
}

set_int::set_int (set_int & e)


{ nelem = e.nelem ;
// cration d'une nouvelle liste identique l'ancienne
noeud * adsource = e.debut ;
noeud * adbut ;
debut = NULL ;
while (adsource)
{ adbut = new noeud ; // cration nouveau noeud
adbut->valeur = adsource->valeur ; // copie valeur
adbut->suivant = debut ; // insertion nouveau noeud
debut = adbut ; // dans nouvelle liste
adsource = adsource->suivant ; // noeud suivant ancienne liste
}
}

set_int::~set_int ()
{ noeud * adn ;
noeud * courant = debut ;
while (courant)
{ adn = courant ; // libration de tous
courant = courant->suivant ; // les noeuds
delete adn ; // de la liste
}
}

void set_int::ajoute (int nb)


{
if (!appartient (nb) ) // si nb n'appartient pas la liste
{ noeud * adn = new noeud ; // on l'ajoute en dbut de liste
adn->valeur = nb ;
adn->suivant = debut ;
debut = adn ;
nelem++ ;
}
}

int set_int::appartient (int nb)


{ noeud * courant = debut ;
// attention l'ordre des deux conditions
V. Construction, de s truction e t initialisation de s obje ts 71

while (courant && (courant->valeur != nb) ) courant = courant->suivant ;


return (courant != NULL) ;
}

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 problm e voq u propos de l'ajout d'un lm 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 plm e ntation de notre clas s e .

Exe rcice V.6

___________________________________________________________________________

Enonc

M odifie r la clas s e s e t_int pr cdente (im plm 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
lm 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'accder s q ue ntie llem e nt
aux diff re nts lm 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' lm e nt suivant lors q u'ile xiste e t e xiste , pour te s te r s'il
e xiste e ncore un lm e nt non e xplor .

O n com plte ra alors le program m e d'utilisation pr cdent (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 plm 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 lm 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 donne 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 rle de la fonction init s e lim ite l'initialisation de courant la valeur du pointe ur sur le dbut 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 dj atte inte e t,
donc, q u'aucun lm 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 lm e nt
traite r.

Voici la dclaration com plte de notre nouve lle clas s e :

/* fichier SETINT4.H */
/* dclaration de la classe set_int */
struct noeud
{ int valeur ; // valeur d'un lment de l'ensemble
noeud * suivant ; // pointeur sur le noeud suivant de la liste
} ;

class set_int
{
noeud * debut ; // pointeur sur le dbut de la liste
int nelem ; // nombre courant d'lments
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 lment
int appartient (int) ; // appartenance d'un lment
int cardinal () ; // cardinal de l'ensemble
void init () ; // initialisation itration
int prochain () ; // entier suivant
int existe () ; // test fin liste
} ;

Voici la dfinition 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) ;
}

Voici le nouve au program m e d'utilisation de m and :

/* utilisation de la classe set_int */


#include <iostream.h>
#include "setint1.h"
main()
V. Construction, de s truction e t initialisation de s obje ts 73

{ void fct (set_int) ;


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 diffrents\n" ;
cout << "Ce sont : \n" ;
ens.init () ;
while (ens.existe ())
cout << ens.prochain() << " " ;
}
CH A PITRE VI :
LES FO NCTIO NS A M IES

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 accder tous les m e m bre s privs 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 "dclaration d'am iti ", pe rm e t de dclare r dans une
clas s e , les fonctions q ue l'on autoris e accder s e s m e m bre s priv s (donn e s ou fonctions). Ile xiste
plusieurs situations d'am iti .

a) Fonction ind pe ndante , am ie d'une cl


as s e A

class A
{
......
friend --- fct (-----) ;
.....
}

La fonction fct ayant le prototype s p cifi e s t autoris e accder aux m e m bre s privs 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 dclaration d'am iti ). Pour traduire s a dfinition, le com pilate ur de vra possder les
caract ristiq ues de A, donc dispose r de s a dclaration.

b) Fonction m e m bre d'une cl


as s e B, am ie d'une autre cl
as s e A

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 accder aux m e m bre s
privs de la clas s e A .

Pour q u'il puis s e com piler conve nablem e nt la dclaration 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 dfinition de la clas s e B n'a pas e ncore t com pile , on lui pr cis e ra
sim plem e nt ce tte inform ation par la dclaration :

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 dclaration d'am iti ). Pour com piler sa dclaration (au s e in de la dclaration 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
dfinition de fct, le com pilate ur de vra possder les caract ristiq ues de A, donc dispose r de s a dclaration.

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, plutt q ue d'utiliser autant de dclarations d'am iti q ue de fonctions m e m bre , on utilise (dans
la dclaration de la clas s e A ) la dclaration (globale) suivante :

friend class B ;

Pour com piler la dclaration 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
dclaration 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 dclaration de la clas s e A .

Exe rcice VI.1

___________________________________________________________________________

Enonc

Soit la clas s e point suivante :

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 coordonnes d'un
point. O n fournira s par m e nt un fich ie r source conte nant la nouve lle dclaration (d finition) de point e t un
fich ie r source conte nant la dfinition 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 :

void affiche (point) ;

si l'on souh aite tranm e ttre un point par valeur, ou de la form e :

void affiche (point &) ;

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 :

void affiche (const point &) ;

M anife s te m e nt, affich e devra pouvoir accder aux m e m bre s priv s x e t y de la clas s e point. Ilfaut donc
pr voir une dclaration d'am iti au s e in de ce tte clas s e , dont voici la nouve lle dclaration :

/* fichier POINT1.H */
/* dclaration 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'accder 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 dfinition de
affich e :

#include <iostream.h>
#include "point1.h" // ncessaire pour compiler affiche
void affiche (const point & p)
{ cout << "Coordonnes : " << p.x << " " << p.y << "\n" ;
}

Note z bie n q ue la com pilation de affich e n ce s s ite la dclaration de la clas s e point, e t pas s e ulem e nt une
dclaration 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).

Voici le pe tit program m e d'essai de m and :

#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 dclaration de la clas s e point. Le faire constitue rait d'ailleurs une erreur.

Exe rcice VI.2

___________________________________________________________________________

Enonc

Soit la clas s e ve cte ur3d dfinie 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 dsignent deux ve cte urs de type ve cte ur3d, com m e nt s' crit m ainte nant le te s t de concide 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 :

int coincide (const vecteur3d &, const vecteur3d &) ;

Voici la nouve lle dclaration de notre clas s e :

/* fichier vect3D.H */
/* dclaration 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 ;
}
} ;

e t la dfinition de la fonction coincide :

#include "vect3d.h" // ncessaire pour compiler coincide


VI. Le s fonctions am ie s 85

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 ;
}

Le te s t de concide nce de deux ve cte urs s' crit m ainte nant :

coincide (v1, v2)

O n note ra q ue , tant dans la dfinition de coincide q ue dans ce te s t, on voit s e m anife s te r la sym trie du
problm 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.

Exe rcice VI.3

___________________________________________________________________________

Enonc

Cr e r de ux clas s e s (dont les m e m bres donne sont priv s ) :

- 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 carres 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 dfinitions, la dfinition 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
lm 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 accder aux donnes des deux clas s e s , ce q ui signifie
q u'e lle devra tre dclar 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 dfinition de prod).

Voici la dclaration de la clas s e ve ct :

/* fichier vect1.h */
class matrice ; // pour pouvoir compiler la dclaration 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 indpendante
void affiche () ;
} ;

e t la dclaration de la clas s e m at :

/* fichier mat1.h */
class vect ; // pour pouvoir compiler la dclaration de matrice
class matrice
{ double mat[3] [3] ; // matrice 3 X 3
public :
matrice () ; // constructeur avec initialisation zro
matrice (double t [3] [3] ) ; // constructeur partir d'un tableau 3 x 3
friend vect prod (const matrice &, const vect &) ; // fonction amie indpendante
} ;

Note z q ue nous avons dclar 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 dfinition de
m atrice , une m odification desdits argum e nts.

La dfinition 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" ;
}

Ile n va de m m e pour les fonctions m e m bre (e n fait le constructe ur) de la clas s e m at :

#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] ;
}

La dfinition prod n ce s s ite les fich ie rs conte nant les dclarations de ve ct e t de m at :

#include "vect1.h"
#include "mat1.h"
vect prod (const matrice & m, const vect & x)
{ int i, j ;
double som ;
vect res ; // pour le rsultat du produit
for (i=0 ; i<3 ; i++)
{ for (j=0, som=0 ; j<3 ; j++)
VI. Le s fonctions am ie s 87

som += m.mat[i] [j] * x.v[j] ;


res.v[i] = som ;
}
return res ;
}

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 libr au
m om e nt de la sortie de la fonction.

Voici, e nfin, un e xe m ple de program m e de te s t :

#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 surdfinir 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 surdfinir 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)

D ans le deuxi m e cas, la m m e notation e s t q uivalente :

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 surdfinis garde nt leur priorit e t leur associativit h abitue lle (voir tableau r capitulatif, e n page 9 3).

Un op rate ur surdfini doit toujours possder un oprande de type clas s e (on ne pe ut donc pas m odifie r les
significations des oprate 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 plutt am ie ) possdant 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 dduite 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 dfinis 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 prdfinie 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 surdfinition de ne w , pour un type classe donn, se fait par une fonction de prototype :

void * new (size_t)

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 surdfinition de de lete , pour un type donn , s e fait par une fonction de prototype :

void delete (type *)

Elle re oit, e n uniq ue argum e nt, l'adresse de l'obje t librer.

Tabl
e au r capitul
atif
____________________________________________________________________________________
PLURALITE OPERATEURS ASSOCIATIVITE
_____________________________________________________________________________________
(3) (3) (2)(3)
Binaire () [] -> ->

(5) (5) (1)


Unaire + - ++ -- ! ~ * & <-
(4) (4)
new delete (cast)

Binaire * / % ->

Binaire + - ->

Binaire << >> ->

Binaire < <= > ->

Binaire == != ->

Binaire & ->

Binaire ^ ->

Binaire || ->

Binaire && ->

Binaire | ->

(1)(3)
Binaire = += -= *= /= %= <-
&= ^= |= <<= >>=

(2)
Binaire , ->
_____________________________________________________________________________________

Le s op rate urs surd finissables e n C+ + (clas s s par priorit dcroissante )

(1) S'iln'e s t pas s urdfini, ilpos s de une signification par d faut.


(2) D e puis l
a ve rsion 2.0 seul
e m e nt.
VII. La surdfinition d'op rateurs 93

(3) D oit tre dfini com m e fonction m e m bre.


(4) A un "nive au gl
obal
" avant l
a ve rsion 2.0. Depuis l
a ve rsion 2.0, ilpe ut, en outre, tre s urdfini pour une cl
as s e ;dans ce cas, ildoit
l
' tre com m e fonction m e m bre.
(5) Jus q u' l
a ve rsion 3.0, on ne pe ut pas distingue r e ntre l
e s notations "pr " e t "pos t". D e puis l
a ve rsion 3.0, ce s op rate urs (l
ors q u'il
s
s ont dfinis de faon unaire ) corre s ponde nt l
a notation "pr " ;m ais ile n e xis te une dfinition binaire (ave c de uxi m e op rande fictif de
type int) q ui corre s pond l
a notation "pos t".

R e m arque:

M m e lors q ue l'on a surdfini 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 (::).

Exe rcice VII.1

___________________________________________________________________________

Enonc

Soit une clas s e ve cte ur3d dfinie com m e s uit :

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 concide nce ou la non-
concide nce de deux points :

- e n utilisant des fonctions m e m bre ,


- e n utilisant des fonctions am ie s .
___________________________________________________________________________

Sol
ution

a) Avec des fonctions m em bre

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 oprate 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 dclaration com plte
de notre clas s e , accom pagne des dfinitions des oprate 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+ +

} ;

int vecteur3d::operator == (vecteur3d v)


{ if ( (v.x == x) && (v.y == y) && (v.z ==z) ) return 1 ;
else return 0 ;
}
int vecteur3d::operator != (vecteur3d v)
{ return ! ( (*this) == v ) ;
}

Note z q ue , dans la dfinition 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 dfinition 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 !=).

b) Avec des fonctions am ies

Ilfaut donc pr voir de dclare 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 dclaration de notre clas s e , accom pagne des dfinitions des oprate 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 dfinir.

#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" ;
}

Exe rcice VII.2

___________________________________________________________________________
VII. La surdfinition d'op rateurs 95

Enonc

Soit la clas s e ve cte ur3d dfinie ainsi :

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) ;
} ;

vecteur3d operator + (vecteur3d v, vecteur3d w)


{ vecteur3d res ;
res.x = v.x + w.x ;
res.y = v.y + w.y ;
return res ;
}

float operator * (vecteur3d v, vecteur3d w)


{ return (v.x * w.x + v.y * w.y + v.z * w.z) ;
}

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+ +

Exe rcice VII.3

___________________________________________________________________________

Enonc

Soit la clas s e ve cte ur3d dfinie ainsi :

class vecteur3d
{ float v[3] ;
public :
vecteur3d (float c1=0.0, float c2=0.0, float c3=0.0)
{ // complter
}
} ;

Com plte r la dfinition du constructe ur ("e n ligne "), puis dfinir l'op rate ur [] pour q u'ilpe rm e tte d'accder
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 dborde m e nt d'indice .

___________________________________________________________________________

Sol
ution

La dfinition du constructe ur ne pos e aucun problm 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 possdera 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 oprateur puisse tre util is gauch e d'une affectation, ilfaut absol um ent que l e
rsul tat soit renvoy par rfrence. Pour nous prot ge r d'un ve ntue ldbordem e nt d'indice , nous avons
sim plem e nt pr vu q ue toute te ntative d'acc s un lm e nt e n de h ors des lim ite s conduirait accder au
pre m ie r lm e nt.

Voici la dclaration de notre clas s e , accom pagn e d e la dfinition 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) ;
} ;

float & vecteur3d::operator [] (int i)


{ if ( (i<0) || (i>2) ) i = 0 ; // pour viter un "dbordement"
return v[i] ;
}

R e m arque:
VII. La surdfinition 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] << " " ;
}

Exe rcice VII.4

___________________________________________________________________________

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'lments
int nelem ; // nombre courant d'lments
public :
set_int (int = 20) ; // constructeur
~set_int () ; // destructeur
...... // autres fonctions membre
} ;

Son im plm e ntation pr voyait de place r les diff re nts lm 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 problm 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 problm 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
supplm e ntaire s s urgis s e nt n anm oins ;e n e ffe t, ici :

- on pe ut s e trouve r e n pr s e nce d'une affe ctation d'un obje t lui-m m e ,


98 Exercices en langage C+ +

- 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 :

- libration de l'e m place m e nt point par b,


- cr ation dynam iq ue d'un nouve le m place m e nt dans leq ue lon re copie les valeurs de l'e m place m e nt
dsign par a,
- m ise en place des valeurs des m e m bres donne de b.
D ans le cas o a e t b dsignent le m m e obje t (on s'e n assure ra dans l'op rate ur d'affe ctation, e n com parant
les adresses des obje ts conce rn s ), on vite ra d'appliq ue r ce m canism e q ui conduirait un e m place m e nt
dynam iq ue point par "pe rsonne ", e t q ui, donc, ne pourrait jam ais tre libr. En fait, on s e conte nte ra de ...
ne rie n faire dans ce cas.

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 dfinition de notre fonction ope rator = (e n ce q ui conce rne la dclaration de la
clas s e s e t_int, ilsuffirait d'y ajoute r s e t_int & ope rator = (s e t_int & ) :

set_int & set_int::operator = (set_int & e)


// surdfinition de l'affectation - les commentaires correspondent b = a
{ if (this != &e) // on ne fait rien pour a = a
{ delete adval ; // libration partie dynamique de b
adval = new int [nmax = e.nmax] ; // allocation nouvel ensemble pour a
nelem = e.nelem ; // dans lequel on recopie
int i ; // entirement l'ensemble b
for (i=0 ; i<nelem ; i++) // avec sa partie dynamique
adval[i] = e.adval[i] ;
}
return * this ;
}

R e m arques:

1) Une te lle s urd finition d'un op rate ur d'affe ctation pour une clas s e possdant 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 propose 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 rf 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
surdfini 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 faon 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 cdente s 'appliq ue rait alors une s im ple affe ctation).

Exe rcice VII.5

___________________________________________________________________________

Enonc

Considrer 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 cdent) :
VII. La surdfinition d'op rateurs 99

class set_int
{
int * adval ; // adresse du tableau des valeurs
int nmax ; // nombre maxi d'lments
int nelem ; // nombre courant d'lments
public :
set_int (int = 20) ; // constructeur
~set_int () ; // destructeur
......
} ;

Adapte r ce tte clas s e , de m ani re ce q ue :

- l'on puis s e ajoute r un lm e nt un e ns e m ble de type s e t_int par (e dsignant 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 surdfinir 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' lm 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 dm 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 dfinition de nos deux oprate urs (note z q ue nous utilisons [] pour d finir < ) :

/* surdfinition de < */
set_int & set_int::operator < (int nb)
{ // on examine si nb appartient dj l'ensemble
// en utilisant l'oprateur []
if ( ! (*this)[nb] && (nelem < nmax) ) adval [nelem++] = nb ;
return (*this) ;
}

/* surdfinition de [] */
int set_int::operator [] (int nb) // attention rsultat par valeur
100 Exercices en langage C+ +

{ int i=0 ;
// on examine si nb appartient dj l'ensemble
// (dans ce cas i vaudra nele en fin de boucle)
while ( (i<nelem) && (adval[i] != nb) ) i++ ;
return (i<nelem) ;
}

Exe rcice VII.6

___________________________________________________________________________

Enonc

Soit une clas s e ve cte ur3d dfinie com m e s uit :

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 ;
}
// complter
} ;

D finir l'op rate ur [] de m ani re ce q ue :

- ilpe rm e tte d'accder "norm alem e nt" un lm e nt d'un obje t non constant de type ve cte ur3d, e t ce la
aussi bien dans une expression qu'en oprande 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 dclar e ave c le q ualificatif const
(auq ue lcas, une te lle fonction pe ut indiff re m m e nt tre utilise 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 dfinition com plte de notre clas s e , accom pagn e d e la dfinition 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 surdfinition d'op rateurs 101

{ v[0] = c1 ; v[1] = c2 ; v[2] = c3 ;


}
float operator [] (int) const ; // [] pour un vecteur constant
float & operator [] (int) ; // [] pour un vecteur non constant
} ;

/******** operator [] pour un objet non constant *******/


float & vecteur3d::operator [] (int i)
{ if ( (i<0) || (i>2) ) i = 0 ; // pour viter un "dbordement"
return v[i] ;
}

/********** operator [] pour un objet constant *********/


float vecteur3d::operator [] (int i) const
{ if ( (i<0) || (i>2) ) i = 0 ; // pour viter un "dbordement"
return v[i] ;
}

A titre indicatif, voici un pe tit program m e utilisant la clas s e ve cte ur3d ainsi dfinie , accom pagn du rsultat
produit par son excution :

#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

Exe rcice VII.7

___________________________________________________________________________

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 prcism e nt, on pr voira de dclare r de
te ls ve cte urs par une instruction de la form e :

vect t(exp) ;

dans laq ue lle e xp dsigne une expression quelconq ue (de type e ntie r).
102 Exercices en langage C+ +

O n d finira, de faon appropri e , l'op rate ur [] de m ani re ce q u'ilpe rm e tte d'accder des lm 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 problm 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 "dbordem e nt" d'indice
n'e xiste .

___________________________________________________________________________

Sol
ution

Le s lm 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, librer cet e m place m e nt. En ce q ui conce rne l'acc s un lm e nt, il
s e fe ra e n surdfinissant l'op rate ur [], com m e nous l'avons dj fait au cours des prcdents 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 ldbordem e nt d'indice , nous fe rons e n sorte q u'une te ntative d'acc s un
lm e nt situ e n de h ors du ve cte ur conduis e accder l' lm e nt de rang 0.

Voici ce q ue pourraie nt tre la dclaration e t la dfinition de notre clas s e :

/********** dclaration de la classe vect ********/


class vect
{ int nelem ; // nombre d'lments
int * adr ; // adresse zone dynamique contenant les lments
public :
vect (int) ; // constructeur
~vect () ; // destructeur
int & operator [] (int) ; // accs un lment par son "indice"
} ;

/*********** dfinition de la classe vect ********/


vect::vect (int n)
{ adr = new int [nelem = n] ;
int i ;
for (i=0 ; i<nelem ; i++) adr[i] = 0 ;
}
vect::~vect ()
{ delete adr ;
}
int & vect::operator [] (int i)
{ if ( (i<0) || (i>=nelem) ) i=0 ; // protection
return adr [i] ;
}

Voici un pe tit e xe m ple de program m e d'utilisation d'une te lle clas s e :

#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 surdfinition d'op rateurs 103

Exe rcice VII.8

___________________________________________________________________________

Enonc

En s'inspirant de l'e xe rcice pr cdent, 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 prcism e nt, on pr voira de dclare r de te ls tableaux par une
dclaration de la form e :

int2d t(exp1, exp2) ;

dans laq ue lle e xp1 e t e xp2 dsignent 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 accder des lm e nts d'un
te ltableau.

b) Surd finir, de faon appropri e , l'op rate ur () de m ani re ce q u'ilpe rm e tte d'accder des lm 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 problm 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 dbordem 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 accder un lm 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 dfini com m e fonction m e m bre , l' criture e n q ue s tion
dem ande rait de dfinir [] 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 , surdfinir 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 dfinir
d'autre s clas s e s q ue int2d.

b) Com m e dans l'e xe rcice pr cdent, les lm 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
dduira la taille des deux dim e nsions reue s e n argum e nts Le destructe ur librera cet e m place m e nt.

Nous devons dcide r de la m ani re dont s e ront rang s les diff re nts lm 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 faon 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 lm 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 utilise par C ou
C+ + pour les tableaux deux dim e nsions). Ainsi, un lm e nt re p r par les valeurs i et j des 2 indices sera
situ l'adre s s e (adv dsignant 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 lm e nt, il s e fe ra e n surdfinissant 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 dfini com m e fonction
m e m bre .

Pour nous prot ge r d'un ve ntue ldbordem 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+ +

Voici ce q ue pourraie nt tre la dclaration e t la dfinition de notre clas s e :

/******** dclaration de la classe int2d ********/


class int2d
{ int nlig ; // nombre de "lignes"
int ncol ; // nombre de "colonnes"
int * adv ; // adresse emplacement dynamique contenant les valeurs
public :
int2d (int nl, int nc) ; // constructeur
~int2d () ; // destructeur
int & operator () (int, int) ; // accs un lment, par ses 2 "indices"
} ;
/*********** dfinition du constructeur **********/
int2d::int2d (int nl, int nc)
{ nlig = nl ;
ncol = nc ;
adv = new int [nl*nc] ;
int i ;
for (i=0 ; i<nl*nc ; i++) adv[i] = 0 ; // mise zro
}

/*********** dfinition du destructeur ***********/


int2d::~int2d ()
{ delete adv ;
}

/********** dfinition de l'oprateur () *********/


int & int2d::operator () (int i, int j)
{ if ( (i<0) || (i>=nlig) ) i=0 ; // protections sur premier indice
if ( (j<0) || (j>=ncol) ) j=0 ; // protections sur second indice
return * (adv + i * ncol + j) ;
}

Voici un pe tit e xe m ple d'utilisation de notre clas s e int2d :

#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 dfinir de ux op rate urs () com m e nous l'avions fait dans l'e xe rcice
pr cdent 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 surdfinition 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 dfinis "e n ligne ".

Exe rcice VII.9

___________________________________________________________________________

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 ) contigus
(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 prcisent 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) supposes 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 ptabilises,
- un op rate ur [] dfini de m ani re te lle q ue h [i] re pr s e nte le nom bre de valeurs r pe rtories 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
considrer 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 problm 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 librer cet e m place m e nt dans le destructe ur de la clas s e . Le s
m e m bres donne 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.

Voici ce q ue pourrait tre la dclaration de notre clas s e h isto :

/*** fichier histo.h : dclaration de la classe histo ***/


class histo
{ float min ; // borne infrieure
float max ; // borne suprieure
int nint ; // nombre de tranches utiles
float * adc ; // pointeur sur les compteurs associs chaque intervalle
106 Exercices en langage C+ +

// adc [i-1] = compteur valeurs de la tranche de rang i


float ecart ; // larg d'une tranche (pour viter un recalcul systmatique)
public :
histo (float=0.0, float=1.0, int=10) ; // constructeur
~histo () ; // destructeur
histo & operator << (float) ; // ajoute une valeur
int operator [] (int) ; // nombre de valeurs dans chaque tranche
} ;

Note z q ue nous avons prvu 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 dfinition 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 contrle 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 ngatif (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 prvu de r gler ce problm 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 dfinition des diff re nte s fonctions m e m bre de notre clas s e h isto :

/************** dfinition de la classe histo **********/


#include "histo.h"
#include <iostream.h>
/********************* constructeur ********************/
histo::histo (float mini, float maxi, int ninter)
{ // protection contre arguments errons
if (maxi < mini)
{ float temp = maxi ; maxi = mini ; mini = temp ; }
if (maxi == mini) maxi = mini + 1.0 ; // arbitraire
if (ninter < 1) nint =1 ;
min = mini ; max = maxi ; nint = ninter ;
adc = new float [nint-1] ; // alloc emplacements compteurs
int i ;
for (i=0 ; i<=nint-1 ; i++) adc[i] = 0 ; // et r.a.z.
ecart = (max - min) / nint ; // largeur d'une tranche
}

/********************* destructeur *********************/


histo::~histo ()
{ delete adc ;
}

/********************* oprateur << ********************/


histo & histo::operator << (float v)
{ int nt = (v-min) / ecart ;
// on ne comptabilise que les valeurs "convenables"
if ( (nt>=0) && (nt<=nint-1) ) adc [nt] ++ ;
return (*this) ;
}
VII. La surdfinition d'op rateurs 107

/********************* oprateur [] ********************/


int histo::operator [] (int n)
{ if ( (n<1) || (n>nint) ) n=1 ; // protection "dbordement"
return adc[n-1] ;
}

Voici, titre indicatif, un pe tit program m e d'essai de la clas s e h isto, accom pagn du rsultat 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 << "numro " << i << " : " << h[i] << "\n" ;
}
_____________________

valeurs des tranches


numro 1 : 3
numro 2 : 5
numro 3 : 6
numro 4 : 4

Exe rcice VII.10

___________________________________________________________________________

Enonc

R aliser une clas s e nom m e stack _int pe rm e ttant de grer 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 dte 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 utiliss sous les form e s s uivante s (n1, n2 e t n3
tant des entie rs) :

p << n1 << n2 << n3 ; p > > n1 > > n2 < < n3 ;


108 Exercices en langage C+ +

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 donne : la taille de l'e m place m e nt r s e rv pour la pile
(nm ax), le nom bre d'lm 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 lm e nts de la pile (adv). Note z q u'il
n'e s t pas nce s s aire de pr voir une donne supplm 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'lm e nts ne le m q ui joue ce rle ici.

Le s op rate urs re q uis peuve nt indif re m m e nt tre dfinis 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 utiliss
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 :

p << n1 << n2 << n3 ;

soit q uivalent :

( ( (p << n1) << n2 ) << n3 ) ;

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 "problm e s h abitue ls". Nous devons donc surdfinir le constructe ur par re copie ;
ici, nous prvoirons une duplication com plte de l'obje t (une autre dm 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 nce s s aire q u'ilre nvoie une q ue lconq ue valeur (d'o la dclaration void).

Voici ce q ue pourrait tre la dclaration de notre clas s e :

#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) ; // oprateur d'empilage
stack_int & operator >> (int &) ; // oprateur de dpilage (attention int &)
int operator ++ () ; // oprateur de test pile pleine
int operator -- () ; // oprateur de test pile vide
VII. La surdfinition 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.

Voici la dfinition de s fonctions m e m bre de stack _int :

#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 - arrt excution ***\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'excution :

/************ programme d'essai de stack_int *********/


#include "stack_int.h"
#include <iostream.h>
main()
{ void fct (stack_int) ;
stack_int pile (40) ;
cout << "pleine : " << pile++ << " vide : " << pile-- << "\n" ;
pile << 1 << 2 << 3 << 4 ;
fct (pile) ;
110 Exercices en langage C+ +

int n, p ;
pile >> n >> p ; // on dpile 2 valeurs
cout << "haut de la pile au retour de fct : " << n << " " << p << "\n" ;
stack_int pileb (25) ;
pileb = pile ; // tentative d'affectation
}

void fct (stack_int pl)


{ cout << "haut de la pile reue par fct : " ;
int n, p ;
pl >> n >> p ; // on dpile 2 valeurs
cout << n << " " << p << "\n" ;
pl << 12 ; // on en ajoute une
}
_________________________________

pleine : 0 vide : 1
haut de la pile reue par fct : 4 3
haut de la pile au retour de fct : 4 3
*** Tentative d'affectation entre piles - arrt excution ***

Exe rcice VII.11

___________________________________________________________________________

Enonc

Adapte r la clas s e point :

class point
{ int x, y ;
// .......
public :
point (int abs=0, int ord=0) // constructeur
// .....
} ;

de m ani re ce q u'e lle dispose de deux fonctions m e m bre pe rm e ttant de conna


tre , tout instant, le nom bre
totald'obje ts de type point, ainsi que le nom bre d'obje ts dynam iq ue s (c'e s t- -dire cr s par ne w ) de ce
m m e type .

___________________________________________________________________________

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
surdfinir 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 rvs 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 (dclar 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).

Voici ce q ue pourrait tre notre clas s e point (dclaration e t dfinition) :


VII. La surdfinition d'op rateurs 111

#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 surdfini
{ 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 surdfinition de ne w e t de lete , nous avons fait appe laux op rate urs prdfinis (par
e m ploi de ::) pour ce q ui conce rne la ge s tion de la m m oire .

Voici un e xe m ple de program m e utilisant notre clas s e point :

#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 dfinir 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 dfinie 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 faon "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 illgal).
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 dfinir 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 dfini 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 surdfinie :

- les C.D .U. ne s ont m ises en oeuvre q ue s i ce la e s t n ce s s aire ,


- une s e ule C.D .U. pe ut inte rve nir dans une ch a
ne de conve rsions (d'un argum e nt d'une fonction ou
d'un op rande d'un op rate ur),
- ilne doit pas y avoir d'am bigut , c'e s t- -dire plusieurs ch a
nes de conve rsions conduisant au m m e
type , pour un argum e nt ou un op rande donn.

Exe rcice VIII.1

___________________________________________________________________________
122 Exercices en langage C+ +

Enonc

Soit la clas s e point suivante :

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 ).

b) Soie nt alors ces dclarations :

point p ;
int n ;
void fct (int) ;

Que font ce s instructions :


n = p ; // instruction 1
fct (p) ; // instruction 2

___________________________________________________________________________

Sol
ution

a) Ilsuffit de dfinir 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+ + dduit 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
dclaration e t la dfinition de ce tte fonction, ici r unie s e n une s e ule dclaration 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 cdem m e nt).

Exe rcice VIII.2


VIII. Les conversions de type dfinies par l'utilisateur 123

___________________________________________________________________________

Enonc

Quels r s ultats fournira le program m e s uivant :

#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 surdfini 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 sr, 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).

A titre indicatif, voici ce q ue fournit pr cis m e nt l'e x cution du program m e :

*** appel int() pour le point 1 5


n1 = 4
124 Exercices en langage C+ +

*** appel int() pour le point 1 5


*** appel int() pour le point 2 8
n2 = 3
*** appel int() pour le point 1 5
z1 = 4
*** appel int() pour le point 1 5
*** appel int() pour le point 2 8
z2 = 3

Exe rcice VIII.3

___________________________________________________________________________

Enonc

Quels r s ultats fournira le program m e s uivant :

#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 dfinies 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
faon 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
dgradante " 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 bigut 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 surdfinie ).

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.

Enfin les instructions 3 et 4 e ntra


ne nt une conve rsion de point e n double, par une s uite de conve rsions point
--> int e t int --> double.

Voici le r s ultat de l'e x cution du program m e :

** appel int() pour le point 1 4


n1 = 2
** appel int() pour le point 1 4
z1 = 2.75
** appel int() pour le point 1 4
z2 = 1
** appel int() pour le point 1 4
$$ appel fct avec argument : 1

Exe rcice VIII.4

___________________________________________________________________________

Enonc

Que se pas s e rtai-il si, dans la clas s e point du pr cdent 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 bigut . 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 .

Exe rcice VIII.5

___________________________________________________________________________

Enonc

Considrer la clas s e s uivante :

class complexe
{ double x, y ;
126 Exercices en langage C+ +

public :
complexe (double r=0, double i=0) ;
complexe (complexe &) ;
} ;

D ans un program m e conte nant les dclarations :

complexe z (1,3) ;
void fct (complexe) ;

q ue produiront les instructions suivante s :

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 cdem 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 rle q ue les deux pr cdente 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 .

Exe rcice VIII.6

___________________________________________________________________________

Enonc

Quels r s ultats fournira le program m e s uivant :

#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 << "Coordonnes : " << x << " " << y << "\n" ;
}
} ;
point operator+ (point a, point b)
VIII. Les conversions de type dfinies 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 + surdfini 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 droule de faon 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 faon ide ntiq ue les deux op randes d'un oprate ur
binaire , notam m e nt e n ce q ui conce rne les possibilits de conve rsions im plicite s .

Exe rcice VIII.7

___________________________________________________________________________

Enonc

Soie nt les deux clas s e s s uivante s :

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) D ans un program m e conte nant les dclarations :

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

b) Com m e nt obte nir le m m e r s ultat, sans dfinir, dans A, le constructe ur A(B)?

___________________________________________________________________________

Sol
ution

a)

Instruction 1 :

Ilfaut distingue r 2 cas :


Si A n'a pas surd fini d'op rate ur d'affe ctation de la form e A & ope rator = (B), ily aura conve rsion de
b1 dans le type de A (par appe ldu constructe ur A(B)), suivie d'une affe ctation a1 (e n utilisant soit
l'op rate ur d'affe ctation par d faut de A, soit ve ntue llem e nt ce lui q ui aurait pu y tre s urd fini).
En re vanch e , si A a surdfini un op rate ur d'affe ctation de la form e A & ope rator = (B) (pe rm e ttant
d'affe cte r un obje t de type B un obje t de type A ), ce dernie r s e ra utilis pour raliser l'instruction 1, e t,
dans ce cas, il n'y aura donc pas d'appe l de constructe ur de A , ni d' ve ntue l autre op rate ur
d'affe ctation. Ce com porte m e nt s'e xpliq ue par le fait q ue les C.D .U. ne s ont m ises en oeuvre q ue
lors q ue ce la e s t n ce s s aire .

Instruction 2 :

L e ncore , ilfaut distingue r les deux cas prcdents. 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 :

Elle ne pos e aucun problm e particulie r.

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 surdfini.

Instruction 5 :
VIII. Les conversions de type dfinies par l'utilisateur 129

L'e xpre s s ion a1 + a2 ne pos e pas de problm 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 dj voq u s . Si B a surdfini 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).

b) En introduisant, dans la clas s e B, un op rate ur de "cast" pe rm e ttant la conve rsion de B e n A, de la


form e :

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
dfinir une nouve lle clas s e B dite "driv e ", partir d'une clas s e e xistante A , dite "de bas e " ;pour ce faire ,
on proc de ainsi :

class B : public A // ou : private A ou (depuis la version 3) protected A


{ // dfinition des membres supplmentaires (donnes ou fonctions)
// ou redfinition de membres existants dans A (donnes ou fonctions)
} ;

Ave c public A, on parle de "drivation publiq ue " ;ave c private A, on parle de "drivation priv e " ;ave c
prote cte d A, on parle de "drivation 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 privs 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
driv e .

O utre les "statuts" public ou priv (pr s e nts 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
driv e , m ais com m e un m e m bre public pour la classe driv e .

D 'autre part, ile xiste trois sortes de drivation :

- publ ique : les m e m bres de la classe de bas e cons e rve nt leur statut dans la classe driv 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 privs dans la classe driv 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 driv 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 redfini dans une classe driv 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'accder 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 sr, q u'un te lacc s soit autoris .
134 Exercices en langage C+ +

Appe lde s cons tructe urs e t de s de s tructe urs

Soit B une classe drive 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 dfinition du constructe ur de B, com m e dans ce t e xe m ple :

B (int x, int y, char coul) : A (x, y) ;

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 driv 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 lprocdera ainsi :

- appe ldu constructe ur par re copie de A (soit ce lui q ui y a t dfini, soit le constructe ur par re copie par
dfaut),
- initialisation de s m e m bres donne de B q ui ne s ont pas h rits de A.
En re vanch e , un problm e s e pos e lors q ue la classe driv 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 hrite de A
// (possible uniquement grce aux rgles de compatibilit
// entre classe de base et classe drive)

- 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

Considrons 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 dfini par
l'utilisate ur, ve ntue llem e nt type clas s e ) :

class A class B : public A


{ ..... { .....
public : } ;
t f (...) ;
.....
} ;

A a ; // a est du type A
B b ; // b est du type B, driv 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 rsul 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

Considrons une clas s e B drivant d'une clas s e A .

Si la classe driv 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
droule m e m bre m e m bre , e n considrant 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 surdfinie , 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 driv e B a surdfini 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 = dfini 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 'oprateur = 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 rits 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

Considrons :

class A class B : public A


{ ..... { .....
} ; } ;

A a ; // a est du type A
B b ; // b est du type B, driv 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

Ile xiste deux conve rsions im plicite s :

- d'un obje t d'un type driv dans un obje t d'un type de bas e . Ainsi l'affe ctation a=b e s t lgale : e lle
re vie nt conve rtir b dans le type A (c'e s t- -dire , e n fait, ne considrer 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, illgale.
- d'un pointe ur sur une classe driv e e n un pointe ur sur une classe de bas e . Ainsi l'affe ctation ada=adb
e s t lgale, tandis q ue adb=ada e s t illgale (e lle pe ut ce pe ndant tre forc e par e m ploi de l'op rate ur de
"cast" : adb = (B*) ada).

Exe rcice IX.1

___________________________________________________________________________

Enonc

O n dispose d'un fich ie r nom m point.h conte nant la dclaration 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 coordonnes : " << x << " " << y << "\n" ;
}
float abs () { return x ; }
float ord () { return y ; }
} ;

a) Cr e r une clas s e pointa, driv 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 dclar s prot g s (prote cte d) dans point, e t
non plus priv s .

c) Introduire un constructe ur dans la clas s e pointb.

d) Quelles s ont les fonctions m e m bre utilisables pour un obje t de type pointb ?

___________________________________________________________________________

Sol
ution

a) Ilsuffit de prvoir, dans la dclaration 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
driv e pointb ;ce q ui signifie q u'au s e in de la dfinition 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 ") :

#include "point.h" // pour la dclaration de la classe point


#include <math.h>
class pointb : public point
{ public :
float rho ()
{ return sqrt (abs () * abs () + ord () * ord () ) ;
}
} ;

Note z q ue , te lle q u'e lle a t dfinie , 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 dfinitions 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
dclarations de la classe de bas e e t de sa classe driv 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 dfinition pr cdente 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
dclar s prot g s , ils sont acce s s ibles aux fonctions m e m bre de sa classe driv e ;aussi est-ilpossible, dans
la dfinition 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

float rho () // il faut que x et y soient


{ return sqrt (x * x + y * y) ; // dclars "protected" dans point
}

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) :

pointb (float c1=0.0, float c2=0.0)


{ initialise (c1, c2) ;
}

L e ncore , si les m e m bre s x e t y de point ont t dclar s "prot g s ", ile s t possible d'crire ainsi notre
constructe ur :

pointb (float c1=0.0, float c2=0.0)


{ x = c1 ; y = c2 ; // il faut que x et y soient
} // dclars "protected" dans point

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 dfini de m e m bres donne supplm 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.

Exe rcice IX.2

___________________________________________________________________________

Enonc

O n dispose d'un fich ie r point.h conte nant la dclaration 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 << "Coordonnes : " << x << " " << y << "\n" ;
}

void deplace (float dx, float dy)


{ x = x + dx ; y = y + dy ;
}
} ;

a) Cr e r une clas s e pointcol, driv e d e point, com portant :

- un m e m bre donne supplm e ntaire cl, de type int, destin conte nir la "couleur" d'un point
138 Exercices en langage C+ +

- les fonctions m e m bre s uivante s :


*affich e (redfinie ), q ui affich e les coordonn e s e t la couleur d'un obje t de type pointcol,
*colore (int couleur), q ui pe rm e t de dfinir la couleur d'un obje t de type pointcol,
*un constructe ur pe rm e ttant de dfinir la couleur e t les coordonn e s (on ne le dfinira pas "e n ligne ".
b) Que fe ra alors prcism e nt ce tte instruction :

pointcol (2.5, 3.25, 5) ;

___________________________________________________________________________

Sol
ution

a) La fonction colore ne pos e aucun problm e particulie r puis q u'e lle agit uniq ue m e nt sur un m e m bre donne
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 rits 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 ") :

/****** fichier pointcol.h :dclaration de pointcol ******/


#include "point.h"
class pointcol : public point
{ int cl ;
public :
pointcol (float = 0.0, float = 0.0, int = 0) ;
void colore (int coul)
{ cl = coul ;
}
void affiche () // affiche doit appeler affiche de
{ point::affiche () ; // point pour les coordonnes
cout << " couleur : " << cl ; // mais elle a accs la couleur
}
} ;

/****** dfinition du constructeur de pointcol *****/


#include "point.h"
#include "pointcol.h"
pointcol::pointcol (float abs, float ord, int coul) : point (abs, ord)
{ cl = coul ; // on pourrait aussi crire colore (coul) ;
}

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 dclaration.

b) La dclaration 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.

Exe rcice IX.3

___________________________________________________________________________

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 cdent.
Cr e r une clas s e pointcol possdant 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 cdent, au
nive au de s possibilits d'utilisation ?

___________________________________________________________________________

Sol
ution

La s e ule dm arch e possible consiste cr e r une clas s e pointcoldans laq ue lle un de s m e m bres donne est lui-
m m e de type point. Sa dclaration e t sa dfinition s e pr s e nte raie nt alors ainsi :

/***** fichier pointcol.h : dclaration de pointcol *****/


#include "point.h"
class pointcol
{ point p ;
int cl ;
public :
pointcol (float = 0.0, float = 0.0, int = 0) ;
void colore (int coul)
{ cl = coul ;
}
void affiche ()
{ p.affiche () ; // affiche doit appeler affiche
cout << " couleur : " << cl ; // du point p pour les coordonnes
}
} ;

/****** dfinition du constructeur de pointcol *****/


#include "point.h"
#include "pointcol.h"
pointcol::pointcol (float abs, float ord, int coul) : p (abs, ord)
{ cl = coul ;
}

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 cdent.
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+ +

Exe rcice IX.4

___________________________________________________________________________

Enonc

Soit une clas s e point dfinie ainsi (nous ne fournissons pas la dfinition de s on constructe ur) :

class point
{ int x, y ;
public :
point (int = 0, int = 0) ;
friend int operator == (point &, point &) ;
} ;

int operator == (point & a, point & b)


{ return a.x == b.x && a.y == b.y ;
}

Soit la clas s e pointcol, driv e d e point :

class pointcol : public point


{ int cl ;
public :
pointcol (int = 0, int = 0, int = 0) ;
// ventuelles fonctions membre
} ;

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 dfini au s e in de point, sous form e
d'une fonction m e m bre .

___________________________________________________________________________

Sol
ution

a) Le s 5 instructions proposes 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 problm e ne s e pose bien sr. 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 considrer d'un obje t de
type pointcol, q ue les coordonn e s . Pour un e ntie r, e lle re vie nt le considrer 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 problm 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 prcdent,
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.

Exe rcice IX.5

___________________________________________________________________________

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 dclaration (fournie dans le fich ie r ve ct.h ) s e
pr s e nte ainsi :

class vect
{ int nelem ; // nombre d'lments
int * adr ; // adresse zone dynamique contenant les lments
public :
vect (int) ; // constructeur (prcise la taille du vecteur)
~vect () ; // destructeur
int & operator [] (int) ; // accs un lment 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, driv 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 problm 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 donne supplm 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 upplm e ntaire conte nant la lim ite
inf rie ure , sach ant q ue la valeur suprieure pourrait s'e n dduire 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 problm 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+ +

vectb (int d, int f)

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'lm 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 :

vectb (int d, int f) : vect (f-d+1)

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 donne 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 nce s s aire de le s urd finir. Toute fois, la notation t[i] ne dsigne plus forc m e nt l' lm 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 redfinir []
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 dfinition, dans la m e s ure o les deux
fonctions m e m bre ont t dfinie 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 donne adr avait t dclar prot g (prote cte d) dans la clas s e ve ct, nous aurions pu
redfinir l'op rate ur [] de ve ctb, sans faire appe l ce lui de ve ct :

int & operator [] (int i)


{ return adr[i-debut] ;
}

2) Aucune prote ction d'indice s n'e s t pr voir dans ve ctb, d s lors q u'e lle a dj t pr vue dans ve ct.

Exe rcice IX.6

___________________________________________________________________________

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 dclaration (fournie dans le fich ie r int2d.h ) s e pr s e nte
ainsi :

/****** fichier int2d.h :dclaration de la classe int2d ******/


class int2d
IX. La tech nique de l'h ritage 143

{ int nlig ; // nombre de "lignes"


int ncol ; // nombre de "colonnes"
int * adv ; // adresse emplacement dynamique contenant les valeurs
public :
int2d (int nl, int nc) ; // constructeur
~int2d () ; // destructeur
int & operator () (int, int) ; // accs un lment, par ses 2 "indices"
} ;

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 , driv 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 problm 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 gnraliser la clas s e int2d, le travailr alis dans l'e xe rcice pr cdent 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 dfinition, 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) // rdfinition de operator ()
{ return int2d::operator () (i-ligdeb, j-coldeb) ;
}
} ;

Note z q ue , l non plus, aucune prote ction d'indice s upplm e ntaire n'e s t pr voir dans int2db, d s lors
q u'e lle a dj t pr vue dans int2d.

Exe rcice IX.7

___________________________________________________________________________

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 dclaration
(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 prvision d'une ventuelle classe drive
int nelem ; // nombre d'lments
int * adr ; // adresse zone dynamique contenant les lments
public :
vect (int) ; // constructeur
~vect () ; // destructeur
int & operator [] (int) ; // accs un lment 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 prvu 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 "problm e s h abitue ls".

Cr e r une clas s e ve ctok , driv 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 droule 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.

Ecrire un pe tit program m e d'essai.

___________________________________________________________________________

Sol
ution

M anife s te m e nt, la clas s e ve ctok n'a pas besoin de dfinir de nouve aux m e m bres donne. 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 dj 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 dfinir, 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 faon 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 plte 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.

Voici ce q ue pourraie nt tre la dclaration e t la dfinition de notre clas s e ve ctok :

/**** dclaration de la classe vectok ****/


#include "vect.h"
class vectok : public vect
{ // pas de nouveaux membres donne
public :
vectok (int dim) : vect (dim) // constructeur de vectok : se contente
{} // de passer dim au constructeur de vect
vectok (vectok &) ; // constructeur par recopie de vectok
vectok & operator = (vectok &); // surdfinition de l'affectation de vectok
int taille ()
{ return nelem ;
}
} ;
/***** dfinition du constructeur par recopie de vectok *****/
// il doit obligatoirement prvoir des arguments pour un constructeur
// (quelconque) de vect (ici le constructeur un argument)
vectok::vectok (vectok & v) : vect (v.nelem)
{ int i ;
for (i=0 ; i<nelem ; i++) adr[i] = v.adr[i] ;
}
/***** dfinition de l'affectation entre vectok *****/
vectok & vectok::operator = (vectok & v)
{ if (this != &v)
{ delete adr ;
adr = new int [nelem = v.nelem] ;
int i ;
for (i=0 ; i<nelem ; i++) adr[i] = v.adr[i] ;
}
return (*this) ;
}

R e m arque :

Voici, titre indicatif, ce q ue s e rait la dfinition 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 dj une te lle allocation).

Voici un e xe m ple de program m e utilisant la clas s e ve ctok :

#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 reu 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 drivation, ici publique
// pourrait tre prive
{ // dfinition des membres supplmentaires (donnes ou fonctions)
// ou redfinition de membres existants dj dans point ou coul
} ;

Ch acune des drivations peut tre publiq ue ou priv e . Le s m odalits 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 drivation "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 accder un m e m bre d'une des classes de bas e , alors q u'ile s t redfini dans la
classe driv 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 prciser celui
q ui nous int re s s e .

Appe lde s cons tructe urs e t de s de s tructe urs

La cr ation d'un obje t e ntra


ne l'appe ldu constructe ur de ch acune des classes de bas e , dans l'ordre o ce s
constructe urs sont m e ntionns dans la dclaration de la classe driv e (ici, point puis coulpuis q ue nous
avons crit class pointcol : public point, public coul). Les destructe urs sont appe ls dans l'ordre inve rs e .

Le constructe ur de la classe driv 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 :

pointcoul ( .....) : point (.....), coul (.....)


| | |
| | |
arguments arguments arguments
pointcoul point coul
154 Exercices en langage C+ +

Cl
as s e s virtue l
les

Par le biais de drivations 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 donne 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 drive 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 bigut . 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 drive de deuxi m e nive au, ilfaut, dans les dclarations des
driv e s d e pre m ie r nive au (ici B e t C) dclare r ave c l'attribut virtualla classe dont on ve ut vite r la
duplication (ici A).

Voici com m e nt on procderait dans l'e xe m ple pr cdent (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 ) :

class B : public virtual A


{ ..... } ;
class C : public virtual A
{ ..... } ;
class D : public B, public C
{ ..... } ;

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 driv 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 problm 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 driv e , e n plus des argum e nts destin s aux constructe urs des classes du nive au
im m diate m e nt suprieur, com m e dans ce t e xe m ple :

D ( .....) : B (.....), C ( ..... ), A ( ..... )


| | | |
| | | |
arguments arguments arguments arguments
de D pour B pour C pour A

D e plus, dans ce cas, les constructe urs des clas s e s B e t C (q ui ont dclar q ue A tait "virtue lle") n'auront
plus spcifie 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 .

Exe rcice X.1

___________________________________________________________________________
X. L'h ritage m ultiple 155

Enonc

Quels s e ront les r s ultats fournis par ce program m e :

#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" ;
}
} ;

class C : public B, public A


{ int n ;
int p ;
public :
C (int n1=1, int n2=2, int n3=3, float v=0.0) : A (n1), B(v)
{ n = n3 ; p = n1+n2 ;
cout << "** construction objet C : " << n << " " << p <<"\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 dclaration 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

Exe rcice X.2

___________________________________________________________________________

Enonc

M m e q ue s tion q ue pr cdem m e nt, e n re m plaant sim plem e nt l'e n-t te du constructe ur de C par :

C (int n1=1, int n2=2, int n3=3, float v=0.0) : B(v)

___________________________________________________________________________

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 prvue 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

Exe rcice X.3

___________________________________________________________________________

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 :

C (int n1=1, int n2=2, int n3=3, float v=0.0)

_______________________________________________________________

Sol
ution

Ce tte fois, la construction d'un obje t de type C e ntra


ne ra l'appe ld'un constructe ur sans argum e nt, la fois
pour B e t pour A. Voici les r s ultats obte nus :

** 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

Exe rcice X.4

___________________________________________________________________________

Enonc

Quels s e ront les r s ultats fournis par ce program m e :

#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" ;
}
} ;

class D : public B, public C


{ int nd ;
public :
D (int n1, int n2, float x) : C (n1), B (x)
{ nd = n2 ;
cout << "$$construction objet D " << nd << "\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

Exe rcice X.5

___________________________________________________________________________

Enonc

Transform e r le program m e pr cdent, 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 rduisent 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 dsigne l'argum e nt du constructe ur de C.

___________________________________________________________________________

Sol
ution

D ans la dclaration de s clas s e s B e t C, ilfaut indiq ue r q ue la clas s e A e s t "virtue lle", de m ani re ce


q u'e lle ne s oit inclus e q u'une fois dans d've ntue lles desce ndantes de ce s clas s e s . D'autre part, le
constructe ur de D doit pr voir, outre les argum e nts pour les constructe urs de B e t de C, ce ux de s tin s un
constructe ur de A .

En r s um , la dclaration de A re s te inch ang e , ce lle de B e s t transform e e n :

class B : public virtual A


{ // le reste est inchang
}

Ce lle de C e s t transform e d e faon analogue :

class C : public virtual A


{ // le reste est inchang
}

Enfin, dans D , l'e n-t te du constructe ur de vie nt :

D (int n1, int n2, float x) : C (n1), B (x), A (2*n1+1)

A titre indicatif, voici les r s usltats q ue fournirait le program m e pr cdent 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

Exe rcice X.6

___________________________________________________________________________

Enonc

O n souh aite cr e r une clas s e liste pe rm e ttant de m anipuler de s "liste s ch a


n e s " dans les q ue lles la nature de
l'inform ation associ e ch aq ue "noe ud" de la liste n'e s t pas connue (par la clas s e ). Une te lle liste
corrre s pondra au sch m a suivant :

dessin re pre ndre dans "Program m e r e n Turbo C+ + "

(C. DELANNO Y ) page 321

La dclaration de la clas s e liste s e pr s e nte ra ainsi :

struct element // structure d'un lment de liste


{ element * suivant ; // pointeur sur l'lment suivant
void * contenu ; // pointeur sur un objet quelconque
} ;
class liste
{ element * debut ; // pointeur sur premier lment
// autres membres donnes ventuels
public :
liste () ; // constructeur
~liste () ; // destructeur
void ajoute (void *) ; // ajoute un lment en dbut de liste
void * premier () ; // positionne sur premier lment
void * prochain () ; // positionne sur prochain lment
int fini () ;
} ;

La fonction ajoute devra ajoute r, e n dbut de liste , un lm 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 plte r la dclaration pr cdente de la clas s e liste e t e n fournir la dfinition de m ani re ce q u'e lle
fonctionne com m e dem and .

2) Soit la clas s e point suivante :

class point
{ int x, y ;
public :
point (int abs=0, int ord=0) { x=abs ; y=ord ; }
void affiche () { cout << "Coordonnes : " << x << " " << y << "\n" ; }
} ;

Cr e r une clas s e liste _points, driv 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 an 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 :

- ajoute r un point e n dbut d'une te lle liste ,


- disposer d'une fonction m e m bre affich e affich ant les inform ations associ e s ch acun de s points de la
liste de points.
3) Ecrire un pe tit program m e d'essai.

_______________________________________________________________

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 lm e nt courant". Ils e ra
m e m bre donne de la clas s e liste .

Nous convie ndrons (classiquem e nt) q ue la fin de liste e s t m at rialise par un noe ud com portant un pointe ur
"nul". La clas s e liste devra disposer d'un constructe ur dont le rle s e lim ite ra l'initialiser une "liste
vide", ce q ui s'obtie ndra sim plem e nt e n plaant un pointe ur nulcom m e adresse de dbut de liste (ce tte faon
de procder sim plifie grande m e nt l'algorith m e d'ajout d'un lm e nt e n dbut 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 libration de ce s e m place m e nts lors q ue l'obje t e s t dtruit. Ilfaudra donc pr voir un de s tructe ur,
ch arg de dtruire 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 dtruire 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 plte :

struct element // structure d'un lment de liste


{ element * suivant ; // pointeur sur l'lment suivant
void * contenu ; // pointeur sur un objet quelconque
} ;
class liste
{ element * debut ; // pointeur sur premier lment
element * courant ; // pointeur sur lment courant
public :
liste () // constructeur
X. L'h ritage m ultiple 161

{ debut = NULL ;
courant = debut ; // par scurit
}
~liste () ; // destructeur
void ajoute (void *) ; // ajoute un lment en dbut de liste
void * premier () // positionne sur premier lment
{ courant = debut ; return (courant->contenu) ; }
void * prochain () // positionne sur prochain lment
{ 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 :

class liste_points : public liste, public point

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 donne (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 dbut 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 surdfinir. 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 :

class liste_points : public liste, public point


{ public :
liste_points () {}
void affiche () ;
} ;
void liste_points::affiche ()
{ point * ptr = (point *) premier() ;
while ( ! fini() ) { ptr->affiche () ; ptr = (point *) prochain() ; }
}

3) Exe m ple de program m e d'essai :

#include "listepts.h"
main()
{ liste_points l ;
point a(2,3), b(5,9), c(0,8) ;
162 Exercices en langage C+ +

l.ajoute (&a) ; l.affiche () ; cout << "---------\n" ;


l.ajoute (&b) ; l.affiche () ; cout << "---------\n" ;
l.ajoute (&c) ; l.affiche () ; cout << "---------\n" ;
}

A titre indicatif, voici les r s ultats fournis par ce program m e :

Coordonnes : 2 3
---------
Coordonnes : 5 9
Coordonnes : 2 3
---------
Coordonnes : 0 8
Coordonnes : 5 9
Coordonnes : 2 3
---------
CH A PITRE XI :
LES FO NCTIO NS VIRTUELLES

RAPPELS

Typage s tatiq ue de s obje ts (ou l


igature dynam iq ue de s fonctions )

Le s r gles de com patibilit e ntre une classe de bas e e t une classe driv 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 driv e . Toute fois, par d faut, le type des obje ts
point s e s t dfini lors de la com pilation. Par e xe m ple, ave c :

class A class B : public A


{ ..... { .....
public : public :
void fct (...) ; void fct (...) ;
..... .....
}; } ;

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 problm e s inh re nts au typage s tatiq ue . Lors q u'une
fonction e s t dclar 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 redfinitions dans des classes driv 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 :

class A class B : public A


{ ..... { .....
public : public :
virtual void fct (...) ; void fct (...) ;
..... .....
}; } ;
168 Exercices en langage 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 prcism e nt, ilne doit pas
accom pagne r les redfinitions de ce tte fonction dans les classes driv e s ,
- une m th ode dclar e virtue lle dans une classe de bas e pe ut ne pas tre redfinie dans s e s clas s e s
driv e s ,
- une fonction virtue lle pe ut tre s urd finie (ch aq ue fonction surdfinie 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 dclare ave c une initialisation z ro, com m e dans :

virtual void affiche () = 0 ;

Lors q u'une clas s e com porte au m oins une fonction virtue lle pure , e lle e s t considre 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 redfinie dans une
classe driv 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 dclare dans une classe driv 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 driv e ).

Exe rcice X I.1

___________________________________________________________________________

Enonc

Quels r s ultats produira ce program m e :

#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 coordonnes sont : " << x << " " << y << "\n" ;
}
} ;

class pointcol : public point


{ short couleur ;
public :
pointcol (int abs=0, int ord=0, short cl=1) : point (abs, ord)
{ couleur = cl ;
}
void affiche ()
{ cout << "Je suis un point color \n" ;
cout << " mes coordonnes sont : " << x << " " << y ;
cout << " et ma couleur est : " << couleur << "\n" ;
}
} ;
main()
{ point p(3,5) ; point * adp = &p ;
pointcol pc (8,6,2) ; pointcol * adpc = &pc ;
adp->affiche () ; adpc->affiche () ; // instructions 1
cout << "-----------------\n" ;
adp = adpc ;
adp->affiche () ; adpc->affiche () ; // instructions 2
}

_______________________________________________________________

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 dclar 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.

Voici les r s ultats com plets fournis par le program m e :

Je suis un point
mes coordonnes sont : 3 5
Je suis un point color
mes coordonnes sont : 8 6 et ma couleur est : 2
-----------------
Je suis un point color
mes coordonnes sont : 8 6 et ma couleur est : 2
Je suis un point color
mes coordonnes sont : 8 6 et ma couleur est : 2

Exe rcice X I.2


170 Exercices en langage C+ +

___________________________________________________________________________

Enonc

Quels r s ultats produira ce program m e :

#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 coordonnes sont : " << x << " " << y << "\n" ;
}
} ;

class pointcol : public point


{ short couleur ;
public :
pointcol (int abs=0, int ord=0, int cl=1 ) : point (abs, ord)
{ couleur = cl ;
}
void identifie ()
{ cout << "Je suis un point color de couleur : " << couleur << "\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 dclar 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 le (puis q ue affich e n'e s t pas redfinie 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 ).

Voici les r s ultats com plets fournis par notre program m e :

Je suis un point
Mes coordonnes sont : 3 4
Je suis un point color de couleur : 5
Mes coordonnes sont : 5 9
---------------
Je suis un point
Mes coordonnes sont : 3 4
Je suis un point color de couleur : 5
Mes coordonnes sont : 5 9
---------------
Je suis un point color de couleur : 5
Mes coordonnes sont : 5 9
Je suis un point color de couleur : 5
Mes coordonnes sont : 5 9

Exe rcice X I.3

___________________________________________________________________________

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
lm 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 lm 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 rns devront drive 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
redfinie dans les classes driv e s pour affich e r les caract ristiq ues de l'obje t conce rn .

La clas s e e ns_h e te r disposera des fonctions m e m bre s uivante s :

- ajoute pour ajoute r un nouve l lm e nt l'e ns e m ble (e lle devra s'assure r q u'iln'e xiste pas dj ),
- appartie nt pour te s te r l'apparte nance d'un lm e nt l'e ns e m ble,
- cardinalq ui fournira le nom bre d'lm 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 lm e nts de l'e ns e m ble. O n pr voira 3 fonctions :

- init pour initialiser le m canism e d'it ration,


- suivant q ui fournira e n re tour le proch ain lm e nt (obje t d'un type driv de base ),
- e xiste pour pr cis e r s'ile xiste e ncore un lm e nt non e xam in .
Enfin, une fonction nom m e liste pe rm e ttra d'affich e r les caract ristiq ues de tous les lm e nts de l'e ns e m ble
(e lle fe ra, bie n sr, appe laux fonctions affich e des diff re nts obje ts conce rn s ).

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 drive r de base .
172 Exercices en langage C+ +

O n ne s e pr occupe ra pas des ve ntue ls problm 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 lm e nts de l'e ns e m ble,
m ais s e ulem e nt des pointe urs sur ces diff re nts lm e nts. A m oins de fixe r le nom bre m axim ald'lm 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 prciser le nom bre m axim al
d'lm e nts re q uis pour l'e ns e m ble.

O utre l'adre s s e (ade l) de ce tableau de pointe urs, on trouve ra e n m e m bres donne :

- le nom bre m axim ald'lm e nts (nm ax),


- le nom bre courant d'lm e nts (ne le m ),
- un e ntie r (courant) q ui s e rvira au m canism e d'it ration (il dsignera une adresse du tableau de
pointe urs...)
En ce q ui conce rne les fonctions ajoute e t appartie nt, e lles devront m anife s te m e nt re ce voir e n argum e nt un
obje t d'un type driv de base . Com pte te nu de ce q ue l'on ne fait aucune h ypoth s e a priori sur la nature de
te ls obje ts, ile s t pr f rable d'vite r une transm ission d'argum e nt par valeur. Par souci de s im plicit , nous
ch oisirons une transm ission par r f re nce .

Le s m m e s r flexions s'appliq ue nt la valeur de re tour de la fonction suivant.

Voici, e n d finitive , la dclaration de notre clas s e e ns_h e te r :

/* fichier ensheter.H */
/* dclaration de la classe ens_heter */
class base ;
class ens_heter
{ int nmax ; // nombre maximal d'lments
int nelem ; // nombre courant d'lments
base * * adel ; // adresse zone de pointeurs sur les objets lments
int courant ; // numro d'lment courant (pour l'itration)

public :
ens_heter (int=20) ; // constructeur
~ens_heter () ; // destructeur
void ajoute (base &) ; // ajout d'un lment
int appartient (base &) ; // appartenance d'un lment
int cardinal () ; // cardinal de l'ensemble
void init () ; // initialisation itration
base & suivant () ; // passage lment suivant
int existe () ; //
void liste () ; // affiche les "valeurs" des diffrents lments
} ;

La clas s e base (dclaration e t dfinition) dcoule directe m e nt de l' nonc :

class base
{ public :
virtual void affiche () = 0 ;
XI. Les fonctions virtuelles 173

} ;

Voici la dfinition 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 dclaration
(dfinition) pr cdente 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 ) :

/* dfinition de la classe ens_heter */


#include "ensheter.h"
ens_heter::ens_heter (int dim)
{ nmax = dim ;
adel = new base * [dim] ;
nelem = 0 ;
courant = 0 ; // prcaution
}
ens_heter::~ens_heter ()
{ delete adel ;
}
void ens_heter::ajoute (base & obj)
{ if ((nelem < nmax) && (!appartient (obj))) adel [nelem++] = & obj ;
}
int ens_heter::appartient (base & obj)
{ int trouve = 0 ;
init () ;
while ( existe () && !trouve) if ( &suivant() == & obj) trouve=1 ;
return trouve ;
}
int ens_heter::cardinal ()
{ return nelem ;
}

void ens_heter::init ()
{ courant = 0 ;
}

base & ens_heter::suivant ()


{ if (courant<nelem) return (* adel [courant++]) ;
// en pratique, il faudrait renvoyer un objet "bidon" si fin ensemble atteinte
}

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 , driv 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 dclaration de la clas s e base ). A la suite figure le r s ultat
de son excution :

#include <iostream.h>
#include "ens_heter.h"
#include "base.h"
174 Exercices en langage C+ +

class point : public base


{ int x, y ;
public :
point (int abs=0, int ord=0)
{ x = abs ; y = ord ;
}
void affiche ()
{ cout << "Point de coordonnes : " << x << " " << y << "\n" ;
}
} ;

class complexe : public base


{ float re, im ;
public :
complexe (float reel=0.0, float imag=0.0)
{ re = reel ; im = imag ;
}
void affiche ()
{ cout << "Complexe - partie relle : " << re
<< ", partie imaginaire : " << im << "\n" ;
}
} ;

/* utilisation de la classe ens_heter */


main()
{ point p (1,3) ;
complexe z (0.5, 3) ;
ens_heter e ;
cout << "cardinal de e : " << e.cardinal() << "\n" ;
cout << "contenu de e \n" ;
e.liste () ;
e.ajoute (p) ;
cout << "cardinal de e : " << e.cardinal() << "\n" ;
cout << "contenu de e \n" ;
e.liste () ;
e.ajoute (z) ;
cout << "cardinal de e : " << e.cardinal() << "\n" ;
cout << "contenu de e \n" ;
e.liste () ;
e.init () ; int n=0 ;
while (e.existe()) { e.suivant() ;
n++ ;
}
cout << "avec l'itrateur, on trouve : " << n << " lments\n" ;
}
______________________________
cardinal de e : 0
contenu de e
cardinal de e : 1
contenu de e
Point de coordonnes : 1 3
cardinal de e : 2
contenu de e
Point de coordonnes : 1 3
Complexe - partie relle : 0.5, partie imaginaire : 3
avec l'itrateur, on trouve : 2 lments
XI. Les fonctions virtuelles 175

Re m arq ue :

Si l'on ch e rch ait r s oudre les problm 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 plte 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 faon 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 lm 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 raliser une copie
profonde .
La deuxi m e m th ode pose des problm e s voisins en ce qui concerne le traite m e nt appliq ue r aux obje ts
q ui sont des lm 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'ilsoulve , 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 dfinie 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
possder 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 rns drive 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 prdfini, conne ct la sortie
standard stdout ;de m m e , le flot cin e s t un flot d'entr e prdfini, 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 :

ostream & operator << (expression)

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 :

ostream & put (ch ar c) : transm e t au flot corre s pondant le caract re c.


ostream & w rite (void *adr, int l
ong) : e nvoie long caract re s , pr lev s partir de l'adre s s e adr.

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 :

istream & operator >> (& type_de_base)


180 Exercices en langage C+ +

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 ntre d'une ch ane 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 "dlim 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 driv 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 ".

Le s tatut d'e rre ur d'un fl


ot

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".

Les bits d'erreur :

La clas s e ios (dont drive nt istre am e t ostre am ) dfinit 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 prcdente 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).

Acc s aux bits d'erreur : ios contie nt 5 fonctions m e m bre :

eof() : valeur de e ofbit,


bad() : valeur de badbit,
fail() : valeur de failbit,
good () : 1 si aucun bit du statut d'erreur n'est activ ,
rdstate () : valeur du statut d'erreur (entie r).

M odification du statut d'erreur :


XII. Les flots d'entr e e tde sortie 181

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 procdera ainsi (fl tant un flot) :
fl.clear (ios::badbit | fl rdstate() ) ;

Surdfinition de () et de !

Si fle s t un flot, (fl


) e s t vrai si aucun des bits d'erreur n'est activ (c'e s t- -dire s i good e s t vrai) ;de m m e ,
!fle s t vrai si un des bits d'erreur prcdents e s t activ (c'e s t- -dire s i good e s t faux).

Surd finition de << et > > pour de s type s cl


as s e

O n surdfinira < < 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" :

ostream & operator << (ostream sortie, type_classe objet1)


{
// Envoi sur le flot sortie des membres de objet en utilisant
// les possibilits classiques de << pour les types de base
// c'est--dire des instructions de la forme :
// sortie << ..... ;
return sortie ;
}

istream & operator >> (istream & entree, type_de_base & objet)
{
// Lecture des informations correspondant aux diffrents membres de objet
// en utilisant les possibilits classiques de >> pour les types de base
// c'est--dire des instructions de la forme :
// entree >> ..... ;
return entree ;
}

Le m ot d' tat du s tatut de form atage

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 ).

Voici (page ci-conre ) les principaux bits du m ot d'tat :

_____________________________________________________________________________________
NOM DE CHAMP NOM DU BIT SIGNIFICATION
(s'il existe) (quand activ)
_____________________________________________________________________________________
ios::basefield ios::dec conversion dcimale

ios::oct conversion octale

ios::hex conversion hexadcimale

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::showpoint affichage point dcimal (en sortie)

_____________________________________________________________________________________
ios::floatfield ios::scientific notation "scientifique"

ios::fixed notation "point fixe"


_____________________________________________________________________________________

Le m ot d'tat du statut de form atage (partie l)

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

Ils s'e m ploie nt sous la form e :

flot << manipulateur

flot >> manipulateur

Le s principaux m anipulate urs non param triq ue s s ont pr s e nt s e n page s uivante :

_____________________________________________________________________________________

MANIPULATEUR UTILISATION ACTION


____________________________________________________________________________________

dec Entre/Sortie Active le bit de conversion dcimale

hex Entre/Sortie Active le bit de conversion hexadcimale

oct Entre/Sortie Active le bit de conversion octale

endl Sortie Insre un saut de ligne et vide le tampon

ends Sortie Insre un caractre de fin de chane (\0)


_____________________________________________________________________________________

Le s principaux m anipulate urs non param triq ue s

b) Les m anipul
ateurs param triques

Ils s'utilisent sous la form e :

istream & manipulateur (argument)


XII. Les flots d'entr e e tde sortie 183

ostream & manipulateur (argument)

Voici les principaux :

_____________________________________________________________________________________

MANIPULATEUR UTILISATION ROLE


_____________________________________________________________________________________

setbase (int) Entre/Sortie Dfinit la base de conversion

setprecision (int) Entre/Sortie Dfinit la prcision des nombres


flottants

setw (int) Entre/Sortie


Dfinit le gabarit. Il retombe 0
aprs chaque opration
_____________________________________________________________________________________

Le s principaux m anipulate urs param triq ue s


A s s ociation d'un fl
ot un fich ie r

La clas s e ofstre am , drivant de ostre am pe rm e t de cr e r un flot de sortie associ un fich ie r :

ofstream flot (char * nomfich, mode_d_ouverture)

La fonction m e m bre s e e k p (dplace m e nt, origine ) pe rm e t d'agir sur le pointe ur de fich ie r.

D e m m e , la clas s e ifstre am , drivant de istre am , pe rm e t de cr e r un flot d'entr e associ un fich ie r :

ifstream flot (char * nomfich, mode_d_ouverture)

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

D ans tous les cas, la fonction clos e pe rm e t de fe rm e r le fich ie r.

L'utilisation de s clas s e s ofstre am e t ifstre am dem ande l'inclusion du fich ie r fstre am .h .

M odes d'ouverture d'un fich ier

_____________________________________________________________________________________

BIT DE MODE ACTION


D'OUVERTURE
_____________________________________________________________________________________

ios::in Ouverture en lecture (obligatoire pour la classe ifstream)

ios::out Ouverture en criture (obligatoire pour la classe ofstream)

ios::app Ouverture en ajout de donnes (criture en fin de fichier)

ios::ate Se place en fin de fichier aprs ouverture

ios::trunc Si le fichier existe, son contenu est perdu (obligatoire si


ios::out est activ sans ios::ate ni ios::app

ios::nocreate Le fichier doit exister

ios::noreplace Le fichier ne doit pas exister (sauf si ios::ate


ou ios::app est activ)
_____________________________________________________________________________________
184 Exercices en langage C+ +

Les diff re nts m odes d'ouve rture d'un fich ie r

Exe rcice X I.1

___________________________________________________________________________

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 faons diff re nte s :

- e n "point fixe ", ave c un nom bre de dcim ales variant de 0 10,
- e n notation scie ntifiq ue , ave c un nom bre de dcim 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 :

prcision de xx chiffres : cccccccccccc

___________________________________________________________________________

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 dte 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 paramtriques"
main()
{ float val, carre ;
cout << "donnez un nombre rel : " ;
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 << " prcision 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 << " prcision de " << setw (2) << i << " chiffres : "
<< setprecision (i) << setw (12) << carre << "\n" ;
}
XII. Les flots d'entr e e tde sortie 185

Exe rcice X II.2

___________________________________________________________________________

Enonc

Soit la clas s e point suivante :

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 sparate urs "e s pace s _blancs" supplm e ntaire s , de part e t d'autre des nom bre s
e ntie rs.

___________________________________________________________________________

Sol
ution

Nous devons donc surdfinir 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 :

ostream & operator << (ostream &, point) ;

istream & operator >> (istream &, point) ;

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 coordonnes du point, accom pagnes 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 droule (e n te s tant
"nature llem e nt" l' tat du flot).

Voici ce q ue pourrait tre la dclaration de notre clas s e (nous l'avons sim plem e nt m unie d'un contructe ur) e t
la dfinition 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+ +

} ;

ostream & operator << (ostream & sortie, point p)


{ sortie << "<" << p.x << "," << p.y << ">" ;
return sortie ;
}

istream & operator >> (istream & entree, point & p)


{ char c = '\0' ;
float x, y ;
int ok = 1 ;
entree >> c ;
if (c != '<') ok = 0 ;
else
{ entree >> x >> c ;
if (c != ',') ok = 0 ;
else
{ entree >> y >> c ;
if (c != '>') ok = 0 ;
}
}
if (ok) { p.x = x ; p.y = y ; } // on n'affecte p que si tout est OK
else entree.clear (ios::badbit | entree.rdstate () ) ;
return entree ;
}

A titre indicatif, voici un pe tit program m e d'essai, accom pagn d'un exem ple d'excution :

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 () ) ;
}
____________________________

point a : <2,3> point b : <0,0>


donnez un point : 4,5
** information incorrecte
donnez un point : <4,5<
** information incorrecte
donnez un point : <4,5>
merci pour le point : <4,5>
donnez un point : < 8, 9 >
merci pour le point : <8,9>
XII. Les flots d'entr e e tde sortie 187

donnez un point : bof


** information incorrecte
donnez un point : <0,0>
merci pour le point : <0,0>

Exe rcice X II.3

___________________________________________________________________________

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 gistre dans le fich ie r) pour pr cis e r q u'iln'a plus
d'entie rs e ntre r.

___________________________________________________________________________

Sol
ution

Si nom fich dsigne une ch a


ne de caract re s , la dclaration :

ofstream sortie (nomfich, ios::out) ;

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 .

Voici le program m e dem and :

const int LGMAX = 20 ;


#include <stdlib.h> // pour exit
#include <iostream.h>
#include <fstream.h>
#include <iomanip.h>
main()
{
char nomfich [LGMAX+1] ;
int n ;
cout << "nom du fichier crer : " ;
cin >> setw (LGMAX) >> nomfich ;
ofstream sortie (nomfich, ios::out) ;
if (!sortie) { cout << "cration impossible \n" ;
exit (1) ;
}
do
{ cout << "donnez un entier : " ;
cin >> n ;
if (n) sortie.write ((char *)&n, sizeof(int) ) ;
}
188 Exercices en langage C+ +

while (n && 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()).

Exe rcice X II.4

___________________________________________________________________________

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 cdent.

___________________________________________________________________________

Sol
ution

const int LGMAX = 20 ;


#include <stdlib.h> // pour exit
#include <iostream.h>
#include <fstream.h>
#include <iomanip.h>
main()
{
char nomfich [LGMAX+1] ;
int n ;
cout << "nom du fichier lister : " ;
cin >> setw (LGMAX) >> nomfich ;
ifstream entree (nomfich, ios::in) ;
if (!entree) { cout << "ouverture impossible \n" ;
exit (1) ;
}
while ( entree.read ( (char*)&n, sizeof(int) ) )
cout << n << "\n" ;

entree.close () ;
}

Exe rcice X II.5

___________________________________________________________________________
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

const int LGMAX_NOM_FICH = 20 ;


#include <stdlib.h> // pour exit
#include <iostream.h>
#include <fstream.h>
#include <iomanip.h>
main()
{
char nomfich [LGMAX_NOM_FICH + 1] ;
int n, num ;
cout << "nom du fichier consulter : " ;
cin >> setw (LGMAX_NOM_FICH) >> nomfich ;
ifstream entree (nomfich, ios::in) ;
if (!entree) { cout << "Ouverture impossible\n" ;
exit (1) ;
}
do
{ cout << "Numro de l'entier recherch : " ;
cin >> num ;
if (num)
{ entree.seekg (sizeof(int) * (num-1) , ios::beg ) ;
entree.read ( (char *) &n, sizeof(int) ) ;
if (entree) cout << "-- Valeur : " << n << "\n" ;
else { cout << "-- Erreur\n" ;
entree.clear () ;
}
}
}
while (num) ;
entree.close () ;
}
CH A PITRE XIII :
LES PA TRO NS D E FO NCTIO NS
(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 fonctions perm e t de dfinir ce q u'on nom m e s ouve nt des
"fonctions gnriques". Plus prcism e nt, l'aide d'une uniq ue dfinition 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 nce 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 plm 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.

D finition d'un patron de fonctions

O n pr cis e les param tre s (m ue ts) de type , e n faisant pr cder ch acun du m ot (re lative m e nt arbitraire ) class
sous la form e te m plate < class ..., class ..., ...> . La dfinition 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 :

template <class T, class U> void fct (T a, T * b, U c)


{
T x ; // variable locale x de type T
U * adr ; // variable locale adr de type U *
...
adr = new T [10] ; // allocation tableau de 10 lments de type T
...
n = sizeof (T) ; // une instruction utilisant le type T
...
}

R e m arque :

Une instruction te lle q ue (T d s ignant un type q ue lconq ue ) :


19 6 Exercices en C+ +

T x (3) ;

e s t lgale 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 ;

Ins tanciation d'une fonction patron

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.

Voici de s e xe m ples utilisant notre patron pr cdent :

int n, p ; float x ; char c ;


int * adi ; float * adf ;
class point ; point p ; point * adp ;

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 dfinition d'un patron de fonctions ne peut pas tre com pile s e ule ;de toute faon, 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 dfinitions
de patrons de fonctions figure ront dans des fich ie rs d'exte nsion h , de faon vite r d'avoir e n fournir
syst m atiq ue m e nt la liste .

Le s param tre s e xpre s s ion d'un patron de fonctions

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 :

template <class T> int compte (T * tab, int n)


{ // ici, on peut se servir de la valeur de l'entier n
// comme on le ferait dans n'importe quelle fonction ordinaire
}

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 ptes dans l
e s corre s pondance s
e xacte s pour l
e s fonctions s urdfinie 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
surdfinition de fonction).

Surd finition de patrons d e fonctions e t s p cial


is ation de fonctions de patrons

O n pe ut dfinir plusieurs patrons de m m e nom , possdant 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 bigut : un s e ulpatron de fonctions doit pouvoir tre utilis ch aq ue fois.

Par ailleurs, ile s t possible de fournir la dfinition d'une ou plusieurs fonctions particuli re s q ui s e ront
utilises en lie u e t place de ce lle instanci e par un patron. Par e xe m ple, ave c :

template <class T> T min (T a, T b) // patron de fonctions


{ ... }
char * min (char * cha, char * chb) // version spcialise pour le type char *
{ ... }
int n, p;
char * adr1, * adr2 ;

min (n, p) // appelle la fonction instancie par le patron gnral


// soit ici : int min (int, int)
min (adr1, adr2) // appelle la fonction spcialise
// char * min (char *, char *)

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 surdfinie , 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 bigut , 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 problm 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...).

Exe rcice X III.1

___________________________________________________________________________

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 dj 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 dfinition ne pos e pas de problm 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" ;
}

Exe rcice X III.2

___________________________________________________________________________

Enonc

Soit ce tte dfinition de patron de fonctions :

template <class T, class U> T fct (T a, U b, T c)


{ .....
}

Ave c les dclarations suivante s :

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 ?

fct (n, p, q) ; // appel I


fct (n, x, q) ; // appel II
fct (x, n, q) ; // appel III
fct (t, n, &c) ; // appel IV
___________________________________________________________________________

Sol
ution

l'appe lI e s t corre ct ;ilinstancie la fonction :

int fct (int, int, int)

l'appe lII e s t corre ct ;ilinstancie la fonction :

int fct (int, float, int)


XIII. Les patrons de fonctions 19 9

l'appe lIII e s t incorre ct.

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 considr 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 :

char * fct (char *, int, char *)

Exe rcice X III.3

___________________________________________________________________________

Enonc

Cr e r un patron de fonctions perm e ttant de calculer la som m e d'un tableau d' lm e nts de type q ue lconq ue ,
le nom bre d'lm 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

// dfinition du patron de fonctions


template <class T> T somme (T * tab, int nelem)
{ T som ;
int i ;
som = 0 ;
for (i=0 ; i<nelem ; i++) som = som + tab[i] ;
return som ;
}

// 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 conu, 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 surdfini l'op rate ur d'addition,
- la dclaration 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
surdfini l'affe ctation.
200 Exercices en C+ +

A ce propos, notons, q u'ile s t possible d'initialiser som lors de sa dclaration, e n procdant ainsi :
T som (0) ;

Ce la e s t q uivalent T som =0 si T e s t un type prdfini ;e n re vanch e , si T e s t de type clas s e , ce la


provoq ue l'appe ld'un constructe ur 1 argum e nt de T, e n lui transm e ttant la valeur 0 ;le problm e
re latif l'affe ctation som =0 ne s e pos e plus alors.
2) L'e x cution de l'e xe m ple propos fourni de s r s ultats peu satisfaisants dans le cas o l'on appliq ue
som m e un tableau de caract re s , com pte te nu de la capacit lim it e d e ce type . O n pourrait
am liore r la situation e n "spcialisant" notre patron pour les tableaux de caract re s (e n pr voyant, par
e xe m ple, une valeur de re tour de type int).

Exe rcice X III.4

___________________________________________________________________________

Enonc

Soie nt les dfinitions suivantes de patrons de fonctions :

template <class T, class U> void fct (T a, U b) { ... } // patron I


template <class T, class U> void fct (T * a, U b) { ... } // patron II
template <class T> void fct (T, T, T) { ... } // patron III
void fct (int a, float b) { .....} // fonction IV

Ave c ces dclarations :

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 utiliss et les prototypes des fonctions
instanci e s ?

fct (n, p) ; // appel I


fct (x, y ) ; // appel II
fct (n, x) ; // appel III
fct (n, z) ; // appel IV
fct (&n, p) ; // appel V
fct (&n, x) ; // appel VI
fct (&n, &p, &q) // appel VII
___________________________________________________________________________

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).

I) patron I void fct (int, int) ;


II) patron I void fct (float, float) ;
III) fonction IV void fct (int, float) ;
IV) patron I void fct (int, double) ;
V) erreur : ambigit entre fct (T, U) et fct (T*, U)
VI) erreur : ambigit entre fct (T, U) et fct (T*, U)
VII) patron III void fct (int *, int *, int *) ;
XIII. Les patrons de fonctions 201

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 bigut . Le patron II e s t donc, ici, parfaite m e nt inutile.
Note z q ue s i nous avions dfini 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 bigut (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 bigut n'e s t dte 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 dfinitions 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 bigut 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 dfinir ce q ue l'on nom m e aussi des
"clas s e s g n riq ue s ". Plus prcism e nt, l'aide d'une seule dfinition 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).

D finition d'un patron de cl


as s e s

O n pr cis e les param tres de type e n les faisant pr cder 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 :

template <class T, class U, int n> class gene


{ // ici, T dsigne un type quelconque, n une valeur entire quelconque
} ;

Si une fonction m e m bre e s t dfinie (ce q ui e s t le cas usuel) l'e xt rie ur de la dfinition 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 cdent :

template <class T, class U, int n> gene <T, U, n>::gene (...)


{ ..... }

1. Cette fois, ce s param tres sont dja prvus par l


a version 3 (alors q ue, dans le cas des patrons de fonctions , ils ris q uaient d' tre
introduits par la norm e ANSI.
2. En toute rigueur, ils'agit d'une redondance, constate, m ais non jus tifie, par l
e fondateur du langage lui-m m e (Strous trup).
204 Exercices en C+ +

Ins tanciation d'une cl


as s e patron

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 cdent patron (on suppose que pt e s t une clas s e ) :

class gene <int, float, 5> c1 ; // T = int, U = float, n = 5


class gene <int, int, 12> c2 ; // T = int, U = int, n = 12
const int NV=100 ;
class gene <pt, double, NV> c3 ; // T = pt, U = double, n=100
int n = 5 ;
class gene <int, double, n> c4 ; // erreur : n n'est pas constant
const char C = 'e' ;
class gene <int, double, C> c5 ; // erreur : C de type char et non int

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 dfini un
patron de clas s e s point par :

template <class T> class point { ..... } ;

Voici de s instance s possibles de ge ne :

class gene <point<int>, float, 10> c5 ; // T=point<int>, U=float, n=10


class gene <point<char>, point<float>, 5> c6 ; // T=point<int>, U=point<float>, n=5

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 dfinir de ux patrons de m m e nom ). En
re vanch e , on pe ut spcialiser un patron de classes de diff re nte s m ani re s :

- en spcial
isant une fonction m em bre :

Par e xe m ple, ave c ce patron :

template <class T, int n> class tableau { ..... } ;

Nous pourrons crire une ve rsion spcialise de constructe ur pour le cas o T e s t le type point e t o n vaut
10 e n procdant ainsi :

tableau <point, 10>:: tableau (...) { ..... }

- en spcialisant une cl asse (dans ce dernie r cas, on pe ut ve ntue llem e nt spcialiser tout ou une partie des
fonctions m e m bre , m ais ce n'e s t pas nce 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 acceptes.
XIV. Les patrons de cl
asses 205

template <class T> class point { ..... } ;

nous pouvons fournir une ve rsion spcialise pour le cas o T e s t le type ch ar e n procdant ainsi :

class point <char>


{ // nouvelle dfinition de la classe point pour les caractres
} ;

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 faons l'h ritage ave c la notion de patron de clas s e s :

- Cl asse "ordinaire" driv e d 'une cl


asse patron ;par e xe m ple, si A est une clas s e patron d finie par
te m plate < class T> A :
class B : public A <int> // B drive de la classe patron A<int>

O n obtie nt une s e ule clas s e nom m e B


- Patron de cl
asses driv d 'une cl
asse "ordinaire", par e xe m ple (A tant une clas s e ordinaire ) :
template <class T> class B : public A

O n obtie nt une fam ille de clas s e s (de param tre de type T).
- Patron de cl asses driv 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 :
*dfinir une nouve lle fam ille de fonctions driv e s par :
template <class T> class B : public A <T>

D ans ce cas, ile xiste autant de classes driv e s possibles q ue de classes de bas e possibles .
*dfinir une nouve lle fam ille de fonctions driv 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
driv e s (de param tre de type U).

Exe rcice X IV.1

___________________________________________________________________________

Enonc

Soit la dfinition suivante d'un patron de clas s e s :

template <class T, int n> class essai


{ T tab [n] ;
206 Exercices en C+ +

public :
essai (T) ; // constructeur
} ;

a) D onne z la dfinition du constructe ur e s s ai, e n supposant :

- q u'e lle e s t fournie " l'e xt rie ur" de la dfinition pr cdente ,


- q ue le constructe ur re copie la valeur re ue e n argum e nt dans ch acun de s lm e nts du tableau tab .
b) D isposant ainsi de la dfinition pr cdente du patron e s s ai, de son constructe ur e t de ces dclarations :

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
dfinition 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.

essai <int, 10> ei (3) ; // I


essai <float, n> ef (0.0) ; // II
essai <double, p> ed (2.5) ; // III
___________________________________________________________________________

Sol
ution

a) La dfinition 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) :

template <class T, int n> essai<T,n>::essai(T a)


{ int i ;
for (i=0 ; i<n ; i++) tab[i] = a ;
}

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 ;
}

c) AppelIII : incorre ct car p n'e s t pas une expression constante .

Exe rcice X IV.2

___________________________________________________________________________

Enonc

a) Cr e r un patron de clas s e s nom m pointcol, te lq ue ch aq ue clas s e instanci e pe rm e tte de m anipuler de s


points color s (deux coordonn e s e t une couleur) pour les q ue ls on puisse "ch oisir" la fois le type des
coordonn e s e t ce lui de la couleur. O n s e lim ite ra deux fonctions m e m bre : un constructe ur possdant trois
argum e nts (sans valeur par d faut) e t une fonction affich e affich ant les coordonn e s e t la couleur d'un "point
color ".

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 dfinition du patron de m and , e n pr voyant les fonctions m e m bre "e n
ligne " :

template <class T, class U> class pointcol


{ T x, y ; // coordonnees
U coul ; // couleur
public :
pointcol (T abs, T ord, U cl)
{ x = abs ; y = ord ; coul = cl ;
}
void affiche ()
{ cout << "point colore - coordonnees " << x << " " << y
<< " couleur " << coul << "\n" ;
}
}

A titre indicatif, voici un e xe m ple d'utilisation (on y suppose q ue la dfinition pr cdente 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 surdfini 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 .

Exe rcice X IV.3

___________________________________________________________________________

Enonc

O n a d fini le patron de clas s e s s uivant :

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 ()
{ cout << "Coordonnees : " << x << " " << y << "\n" ;
}

a) Que se pas s e -t-ilave c ce s instructions :

point <char> p (60, 65) ;


p.affiche () ;

b) Com m e nt faut-ilm odifie r la dfinition de notre patron pour q ue les instructions prcdente 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 plm e ntation utilisant le
code A SCII : < et A) e t non les nom bre s 60 e t 65.

b) Ilfaut spcialiser 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 dfinition com plte 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 dfinition de notre patron :
// dfinition 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 plm e ntations n'acce pte nt pas q ue l'on
spcialise une fonction m e m bre dfinie e n ligne ;c'e s t d'ailleurs pour ce tte raison q ue , dans notre
nonc , nous avions dfini affich e sous form e d'une fonction ind pe ndante .

Exe rcice X IV.4

___________________________________________________________________________

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 lm e nts de ce s ve cte urs
puis s e nt tre de type q ue lconq ue .

O n surdfinira conve nablem e nt l'op rate ur [] pour q u'ilpe rm e tte l'acc s aux lm 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 "dbordem e nt
d'indice ". En re vanch e , on ne ch e rch e ra pas r gler les problm 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 lm e nts du ve cte ur
lors de sa construction), nous aboutissons au patron de clas s e s s uivant4 :

template <class T> class vect


{ int nelem ; // nombre d'elements
T * adr ; // adresse zone dynamique contenant les elements
public :
vect (int) ; // constructeur
~vect () ; // destructeur
T & operator [] (int) ; // operateur d'acces a un element
} ;

template <class T> vect<T>::vect (int n)


{ adr = new T [nelem = n] ;
}

template <class T> vect<T>::~vect ()

4. La dfinition serait pl
us sim ple si les fonctions m em bre taient "en ligne".
210 Exercices en C+ +

{ delete adr ;
}

template <class T> T & vect<T>::operator [] (int i)


{ if ( (i<0) || (i>nelem) ) i = 0 ; // protection indice hors limites
return adr [i] ;
}

Note z q ue , ici e ncore , nous avons fait e n sorte q u'une te ntative d'acc s un lm e nt situ e n de h ors du
ve cte ur conduis e accder l' lm 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 dfinition 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 lm 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 lm 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 lm e nts
de type clas s e , ilaurait fallu, e n outre , q ue la clas s e conce rne 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 .

Exe rcice X IV.5

___________________________________________________________________________

Enonc

Com m e dans l'e xe rcice pr cdent, r aliser un patron de clas s e s pe rm e ttant de m anipuler de s ve cte urs dont
les lm e nts sont de type q ue lconq ue m ais pour les q ue ls la dim e nsion, suppose 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 nce 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 donne de notre patron. Le constructe ur n'e s t plus nce 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 dfinition de notre patron5 :

template <class T, int n> class vect


{ T v [n] ; // vecteur de n elements de type T
public :
T & operator [] (int) ; // operateur d'acces a un element
} ;

template <class T, int n> T & vect<T,n>::operator [] (int i)


{ if ( (i<0) || (i>n) ) i = 0 ; // protection indice hors limites
return v [i] ;
}

Voici, toujours titre indicatif, ce q ue devie ndrait le pe tit program m e d'essai :

#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'lm e nts un m e m bre donne 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'lm e nts sous form e d'un m e m bre donne, 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 ;
}

Exe rcice X IV.6

5. La dfinition serait pl
us sim ple si les fonctions m em bre taient "en ligne".
212 Exercices en C+ +

___________________________________________________________________________

Enonc

O n dispose du patron de clas s e s s uivant :

template <class T> class point


{ T x, y ; // coordonnees
public :
point (T abs, T ord) { x = abs ; y = ord ; }
void affiche ()
{ cout << "Coordonnees : " << x << " " << y << "\n" ;
}
} ;

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 redfinira 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 dpe 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 problm e particulie r ne s e pos e ;ilsuffit de faire drive r pointcol< T> de point< T> . Voici ce
q ue pe ut tre la dfinition de notre patron (ici, nous avons lais s le constructe ur "e n ligne " m ais nous avons
dfini affich e e n de h ors de la clas s e ) :

template <class T> class pointcol : public point<T>


{ T cl ;
public :
pointcol (T abs, T ord, T coul) : point<T> (abs, ord)
{ cl = coul ;
}
void affiche () ;
} ;

template <class T> void pointcol<T>::affiche ()


{ point<T>::affiche () ;
cout << " couleur : " << cl << "\n" ;
}

Voici un pe tit e xe m ple d'utilisation (iln ce s s ite les dclarations 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 driv e d pe nd de deux param tre s (nom m s ici T e t U). Voici ce q ue pourrait tre la
dfinition de notre patron (ave c, toujours, un constructe ur e n ligne e t une fonction affich e dfinie
l'e xt rie ur de la clas s e ) :

template <class T, class U> class pointcol : public point<T>


{ U cl ;
public :
pointcol (T abs, T ord, U coul) : point<T> (abs, ord)
{ cl = coul ;
}
void affiche () ;
} ;
template <class T, class U> void pointcol<T, U>::affiche ()
{ point<T>::affiche () ;
cout << " couleur : " << cl << "\n" ;
}

Voici un e xe m ple d'utilisation (on suppose qu'ile s t m uni des dclarations 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 dpe ndant plus d'aucun param tre . Voici ce q ue pourrait tre
sa dfinition :

class pointcol : public point<int>


{ short cl ;
public :
pointcol (int abs, int ord, short coul) : point<int> (abs, ord)
{ cl = coul ;
}
void affiche ()
{ point<int>::affiche () ;
cout << " couleur : " << cl << "\n" ;
}
} ;

Et un pe tit e xe m ple d'utilisation :

main()
{ pointcol p1 (2, 5, 1) ; p1.affiche () ;
pointcol p2 (2.5, 5.25, 4) ; p2.affiche () ;
}

Exe rcice X IV.7

___________________________________________________________________________

Enonc

O n dispose du m m e patron de clas s e s q ue pr cdem m e nt :

template <class T> class point


{ T x, y ; // coordonnees
214 Exercices en C+ +

public :
point (T abs, T ord) { x = abs ; y = ord ; }
void affiche ()
{ cout << "Coordonnees : " << x << " " << y << "\n" ;
}
} ;

a) Lui ajoute r une ve rsion spcialise 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 cdent, 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 redfinira
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 spcialise de affich e de pointcoldans le cas du type caract re .

___________________________________________________________________________

Sol
ution

a) Pour d finir une ve rsion spcialise 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 dfinie 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 dfinition (com plte ) de notre 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 ()


{ cout << "Coordonnees : " << x << " " << y << "\n" ;
}
void point<char>::affiche ()
{ cout << "Coordonnees : " << (int)x << " " << (int)y << "\n" ;
}

b) Pour les m m e s raisons que prcdem m e nt, la fonction affich e de pointcoldoit tre dfinie l'e xt rie ur
du patron. Voici ce q ue pourrait tre ce tte dfinition :

template <class T> class pointcol : public point<T>


{ T cl ;
public :
pointcol (T abs, T ord, T coul) : point<T> (abs, ord)
{ cl = coul ;
}
void affiche () ;
} ;

template <class T> void pointcol<T>::affiche ()


{ point<T>::affiche () ;
cout << " couleur : " << cl << "\n" ;
}

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 driv e
dpendant de 2 param tre s T e t U. Ilfaudrait alors pouvoir spcialiser une clas s e , non plus pour de s
valeurs donnes 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 dfinition de
C+ + .

Exe rcice X IV.8

___________________________________________________________________________

Enonc

O n dispose du patron de clas s e s s uivant :

template <class T> class point


{ T x, y ; // coordonnees
public :
point (T abs, T ord) { x = abs ; y = ord ; }
void affiche ()
{ cout << "Coordonnees : " << x << " " << y << "\n" ;
}
} ;

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 , dfinis 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 coordonnes du ce ntre e t la valeur du rayon.

a) Le faire par h ritage (un ce rcle e s t un point q ui poss de un 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 dm 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).

template <class T, class U> class cercle : public point<T>


{ U r ; // rayon
public :
cercle (T abs, T ord, U ray) : point<T> (abs, ord)
{ r = ray ;
}
void affiche ()
{ point<T>::affiche () ;
cout << " rayon : " << r ;
}
216 Exercices en C+ +

} ;

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 :

template <class T, class U> class cercle


{ point<T> c ; // centre
U r ; // rayon
public :
cercle (T abs, T ord, U ray) : c(abs, ord) // pourrait r(ray)
{ r = ray ;
}
void affiche ()
{ c.affiche () ;
cout << " rayon : " << r ;
}
} ;

Note z q ue , dans la dfinition 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) :

cercle (T abs, T ord, U ray) : c(abs, ord), r(ray)


{ }
CH A PITRE XV :
EXERCICES D E SYNTH ESE

Exe rcice X V.1

___________________________________________________________________________

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 dsigne un lm e nt de type s e t_int e t n un e ntie r :

< < , te lq ue e < < n ajoute l' lm e nt n l'e ns e m ble e ,


%, te lq ue n%e vale 1 si n appartie nt e e t 0 sinon,
< < , te lq ue flot < < e e nvoie le conte nu de l'e ns e m ble e sur le flot indiq u , sous la form e :
[entier1, entier2, ... entiern]

La fonction m e m bre cardinalfournira le nom bre d'lm 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 problm e (on acce pte ra la
duplication com plte d'obje ts).

_______________________________________________________________

Sol
ution

Nature llem e nt, notre clas s e com porte ra, e n m e m bres donne, le nom bre m axim al(nm ax) d'lm e nts de
l'e ns e m ble, le nom bre courant d'lm 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 droulent conve nablem e nt, de surdfinir 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 dj
re ncontr e e t q ui consiste considrer 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+ +

Voici la dclaration de notre clas s e s e t_int :

/* fichier SETINT.H */
/* dclaration de la classe set_int */
class set_int
{
int * adval ; // adresse du tableau des valeurs
int nmax ; // nombre maxi d'lments
int nelem ; // nombre courant d'lments
public :
set_int (int = 20) ; // constructeur
set_int (set_int &) ; // constructeur par recopie
set_int & operator = (set_int &) ; // oprateur d'affectation
~set_int () ; // destructeur
int cardinal () ; // cardinal de l'ensemble
set_int & operator << (int) ; // ajout d'un lment
friend int operator % (int, set_int &) ; // appartenance d'un lment
// envoi ensemble dans un flot
friend ostream & operator << (ostream &, set_int &) ;
} ;

Voici ce q ue pourrait tre la dfinition de notre clas s e (les points dlicats sont com m e nt s au s e in m m e des
instructions) :

#include <iostream.h>
#include "setint.h"

/*************** constructeur ********************/


set_int::set_int (int dim)
{ adval = new int [nmax = dim] ; // allocation tableau de valeurs
nelem = 0 ;
}

/****************** destructeur ******************/


set_int::~set_int ()
{ delete adval ; // libration tableau de valeurs
}

/********** constructeur par recopie *************/


set_int::set_int (set_int & e)
{ adval = new int [nmax = e.nmax] ; // allocation nouveau tableau
nelem = e.nelem ;
int i ;
for (i=0 ; i<nelem ; i++) // copie ancien tableau dans nouveau
adval[i] = e.adval[i] ;
}

/************ oprateur d'affectation ************/


set_int & set_int::operator = (set_int & e)
// surdfinition de l'affectation - les commentaires correspondent b = a
{ if (this != &e) // on ne fait rien pour a = a
{ delete adval ; // libration partie dynamique de b
adval = new int [nmax = e.nmax] ; // allocation nouvel ensemble pour a
XV. Exercices de synth se 227

nelem = e.nelem ; // dans lequel on recopie


int i ; // entirement l'ensemble b
for (i=0 ; i<nelem ; i++) // avec sa partie dynamique
adval[i] = e.adval[i] ;
}
return * this ;
}

/************ fonction membre cardinal ***********/


int set_int::cardinal ()
{ return nelem ;
}

/************ oprateur d'ajout << ***************/


set_int & set_int::operator << (int nb)
{ // on examine si nb appartient dj l'ensemble
// en utilisant l'oprateur %
// s'il n'y appartient pas, et s'il y a encore de la place
// on l'ajoute l'ensemble
if ( ! (nb % *this) && nelem < nmax ) adval [nelem++] = nb ;
return (*this) ;
}

/*********** oprateur d'appartenance % **********/


int operator % (int nb, set_int & e)
{ int i=0 ;
// on examine si nb appartient dj l'ensemble
// (dans ce cas i vaudra nele en fin de boucle)
while ( (i<e.nelem) && (e.adval[i] != nb) ) i++ ;
return (i<e.nelem) ;
}

/****** oprateur << pour sortie sur un flot *****/


ostream & operator << (ostream & sortie, set_int & e)
{ sortie << "[ " ;
int i ;
for (i=0 ; i<e.nelem ; i++)
sortie << e.adval[i] << " " ;
sortie << "]" ;
return sortie ;
}

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 lm 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 librer l'ancie n e m place m e nt (e n actualisant conve nablem e nt les m e m bres donne 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 rsultat fourni par son excution :

/************* test de la classe set_int *********/


#include <iostream.h>
#include "setint.h"

main()
228 Exercices de C+ +

{ void fct (set_int) ;


void fctref (set_int &) ;
set_int ens ;
cout << "donnez 10 entiers \n" ;
int i, n ;
for (i=0 ; i<10 ; i++)
{ cin >> n ;
ens << n ;
}
cout << "il y a : " << ens.cardinal () << " entiers diffrents\n" ;
cout << "qui forment l\'ensemble : " << ens << "\n" ;
fct (ens) ;
cout << "au retour de fct, il y en a " << ens.cardinal () << "\n" ;
cout << "qui forment l\'ensemble : " << ens << "\n" ;
fctref (ens) ;
cout << "au retour de fctref, il y en a " << ens.cardinal () << "\n" ;
cout << "qui forment l\'ensemble : " << ens << "\n" ;
cout << "appartenance de -1 : " << -1 % ens << "\n" ;
cout << "appartenance de 500 : " << 500 % ens << "\n" ;
set_int ensa, ensb ;
ensa = ensb = ens ;
cout << "ensemble a : " << ensa << "\n" ;
cout << "ensemble b : " << ensb << "\n" ;
}

void fct (set_int e)


{ cout << "ensemble reu par fct : " << e << "\n" ;
e << -1 << -2 << -3 ;
}

void fctref (set_int & e)


{ cout << "ensemble reu par fctref : " << e << "\n" ;
e << -1 << -2 << -3 ;
}
____________________
donnez 10 entiers
3 5 3 1 8 5 1 7 7 3
il y a : 5 entiers diffrents
qui forment l'ensemble : [ 3 5 1 8 7 ]
ensemble reu par fct : [ 3 5 1 8 7 ]
au retour de fct, il y en a 5
qui forment l'ensemble : [ 3 5 1 8 7 ]
ensemble reu par fctref : [ 3 5 1 8 7 ]
au retour de fctref, il y en a 8
qui forment l'ensemble : [ 3 5 1 8 7 -1 -2 -3 ]
appartenance de -1 : 1
appartenance de 500 : 0
ensemble a : [ 3 5 1 8 7 -1 -2 -3 ]
ensemble b : [ 3 5 1 8 7 -1 -2 -3 ]

Exe rcice X V.2

___________________________________________________________________________
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 dfinie au m om e nt de leur cr ation (une te lle clas s e a dj t
partie llem e nt r alise dans l'e xe rcice VII.7). Ce tte classe devra disposer des oprate 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 problm e ;pour ce faire , on acce pte ra de dupliq ue r com plte 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 dclar e ave c le q ualificatif const (auq ue lcas, une te lle
fonction pe ut indiff re m m e nt tre utilise 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 prvoir de ux
dfinitions 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).

Voici la dclaration de notre clas s e :

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) ; // surdfinition oprateur affectation
int & operator [] (int i) ; // surdef [] pour vect non constants
int operator [] (int i) const ; // surdef [] pour vect constants
} ;

Voici la dfinition des diff re nte s fonctions :

#include <stdio.h>
#include "a:\synthese\vect.h"
vect::vect (int n) // constructeur "usuel"
{ adr = new int [nelem = n] ;
}

vect::vect (vect & v) // constructeur par recopie


{ adr = new int [nelem = v.nelem] ;
int i ;
for (i=0 ; i<nelem ; i++)
adr[i] = v.adr[i] ;
230 Exercices de C+ +

vect::~vect () // destructeur
{ delete adr ;
}

vect vect::operator = (vect & v) // surdfinition oprateur affectation


{ if (this != &v) // on ne fait rien pour a=a
{ delete adr ;
adr = new int [nelem = v.nelem] ;
int i ;
for (i=0 ; i<nelem ; i++)
adr[i] = v.adr[i] ;
}
return * this ;
}

int & vect::operator [] (int i) // surdfinition oprateur []


{ return adr[i] ;
}

int vect::operator [] (int i) const // surdfinition oprateur [] pour cst


{ return adr[i] ;
}

ostream & operator << (ostream & sortie, vect & v)


{ sortie << "<" ;
int i ;
for (i=0 ; i<v.nelem ; i++) sortie << v.adr[i] << " " ;
sortie << ">" ;
return sortie ;
}

A titre indicatif, voici un pe tit program m e utilisant la clas s e ve ct :

#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
}

Exe rcice X V.3


XV. Exercices de synth se 231

___________________________________________________________________________

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 .

Par ailleurs, l'affe ctation e t la transm ission par valeur de ch a


nes devra pouvoir s e faire s ans problm e (on
acce pte ra de dupliq ue r les ch a
nes de m m e conte nu).

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 donne, 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 donne, 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 ane 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
surdfini 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 .

1 Ce q ui ne sera peut- tre pas l


e cas si l'on vise une grande efficacit en tem ps d'excution.
232 Exercices de C+ +

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 ).

Voici ce q ue pe ut tre la dclaration de notre clas s e string :

/* fichier chaine.h : dclaration de la classe chaine */


#include <iostream.h>
#include <string.h>
class string
{ int lg ; // longueur actuelle de la chane
char * adr ; // adresse zone contenant la chane
public :
string () ; // constructeur I
string (char *) ; // constructeur II
string (string &) ; // constructeur III (par recopie)
~string () // destructeur ("inline")
{ delete adr ;
}
string & operator = (string &) ;
long length () // longueur courante ("inline")
{ return lg ;
}
int operator == (string &) ;
char & operator [] (int) ;
friend string operator + (string &, string &) ;
friend ostream & operator << (ostream &, string &) ;
} ;

Voici la dfinition des diff re nte s fonctions :

/* dfinitions des fonctions membre de la classe string */


#include <chaine.h>
string::string () // constructeur I
{ lg = 0 ; adr=0 ;
}

string::string (char * adc) // constructeur II ( partir d'une chane C)


{ lg = strlen (adc) ;
adr = new char [lg+1] ;
strcpy (adr, adc) ;
}

string::string (string & ch) // constructeur III (par recopie)


{ lg = ch.lg ;
adr = new char [lg+1] ;
strcpy (adr, ch.adr) ;
}

string & string::operator = (string & ch)


{ if (this != & ch) // on ne fait rien pour a=a
{ delete adr ;
lg = ch.lg ;
adr = new char [lg+1] ;
strcpy (adr, ch.adr) ;
XV. Exercices de synth se 233

}
return * this ; // pour pouvoir utiliser
} // la valeur de a=b (affectations multiples)

int string::operator == (string & ch)


{ if (strcmp (adr, ch.adr) ) return 0 ;
else return 1 ;
}

char & string::operator [] (int i)


{ return adr[i] ; // ici, on n'a pas prvu de
} // vrification de la valeur de i

/* dfinition des fonctions amies de la classe string */


ostream & operator << (ostream & sortie, string & ch)
{ sortie << ch.adr ;
return sortie ;
}

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 rsultat de son excution :

/* 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("chre "), 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 chre madame
chaine a : salut chre madame
chaine a : salut chre madame - Comment allez-vous ?
chaine a : *****salut chre madame - Comment allez-vous ?
chaine e : abcdexxxxx

Exe rcice X V.4

___________________________________________________________________________

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 lm 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 :

+ =, te lq ue t+ =n m e tte 1 le bit de rang n du tableau t,


-=, te lq ue t-=n m e tte 0 le bit de rang n du tableau t,
[], te lq ue l'e xpre s s ion t[i] fournis s e la valeur du bit de rang i du tableau t (on ne pr voira pas, ici, de
pouvoir e m ploye r ce t op rate ur gauch e d'une affe ctation, com m e dans t[i] = ...),
+ + , te lq ue t+ + m e tte 1 tous les bits de t,
--, te lq ue t--m e tte 0 tous les bits de t,
< < , te lq ue flot < < t e nvoie le conte nu de t sur le flot indiq u , sous la form e
<* bit1, bit2, ... bitn *>

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
problm 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 " lm e nt" d'un tableau. Ces bits devront donc
tre re group s , par e xe m ple raison de CH AR _BIT (dfini 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 donne, 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 prvoirons
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 dfinis 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 prvoir 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 prvoir un
constructe ur par re copie .

Voici ce q ue pourrait tre la dclaration de notre clas s e bit_array :

/* fichier bitarray.h : dclaration de la classe bit_array */


#include <iostream.h>
class bit_array
{ int nbits ; // nombre courant de bits du tableau
int ncar ; // nombre de caractres ncessaires (redondant)
char * adb ; // adresse de l'emplacement contenant les bits
public :
bit_array (int = 16) ; // constructeur usuel
bit_array (bit_array &) ; // constructeur par recopie
~bit_array () ; // destructeur
// les oprateurs binaires
bit_array & operator = (bit_array &) ; // affectation
int operator [] (int) const ; // valeur d'un bit
void operator += (int) ; // activation d'un bit
void operator -= (int) ; // dsactivation d'un bit
// envoi sur flot
friend ostream & operator << (ostream &, bit_array &) const ;
// les oprateurs unaires
void operator ++ () ; // mise 1
void operator -- () ; // mise 0
void operator ~ () ; // complment 1
} ;

Voici la dfinition des diff re nte s fonctions.

/* dfinition des fonctions de la classe bit_array */


#include "bitarray.h"
#include <limits.h>

bit_array::bit_array (int nb)


{ nbits = nb ;
ncar = nbits / CHAR_BIT + 1 ;
adb = new char [ncar] ;
int i ;
for (i=0 ; i<ncar ; i++) adb[i] = 0 ; // raz
}
236 Exercices de C+ +

bit_array::bit_array (bit_array & t)


{ nbits = t.nbits ; ncar = t.ncar ;
adb = new char [ncar] ;
int i ;
for (i=0 ; i<ncar ; i++) adb[i] = t.adb[i] ;
}
bit_array::~bit_array()
{ delete adb ;
}
bit_array & bit_array::operator = (bit_array & t)
{ if (this != & t) // on ne fait rien pour t=t
{ delete adb ;
nbits = t.nbits ; ncar = t.ncar ;
adb = new char [ncar] ;
int i ;
for (i=0 ; i<ncar ; i++)
adb[i] = t.adb[i] ;
}
return *this ;
}

int bit_array::operator [] (int i) const


{ // le bit de rang i s'obtient en considrant le bit
// de rang i % CHAR_BIT du caractre de rang i / CHAR_BIT
int carpos = i / CHAR_BIT ;
int bitpos = i % CHAR_BIT ;
return ( adb [carpos] >> CHAR_BIT - bitpos -1 ) & 0x01 ;
}

void bit_array::operator += (int i)


{ int carpos = i / CHAR_BIT ;
if (carpos < 0 || carpos >= ncar) return ; // protection
int bitpos = i % CHAR_BIT ;
adb [carpos] |= (1 << (CHAR_BIT - bitpos - 1) ) ;
}

void bit_array::operator -= (int i)


{ int carpos = i / CHAR_BIT ;
if (carpos < 0 || carpos >= ncar) return ; // protection
int bitpos = i % CHAR_BIT ;
adb [carpos] &= ~(1 << CHAR_BIT - bitpos - 1) ;
}

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 rsultat fourni par son excution :

/* programme d'essai de la classe bit_array */


main ()
{ bit_array t1 (34) ;
cout << "t1 = " << t1 << "\n" ;
t1 +=3 ; t1 += 0 ; t1 +=8 ; t1 += 15 ; t1 += 33 ;
cout << "t1 = " << t1 << "\n" ;
t1-- ;
cout << "t1 = " << t1 << "\n" ;
t1++ ;
cout << "t1 = " << t1 << "\n" ;
t1 -= 0 ; t1 -= 3 ; t1 -= 8 ; t1 -= 15 ; t1 -= 33 ;
cout << "t1 = " << t1 << "\n" ;
cout << "t1 = " << t1 << "\n" ;
bit_array t2 (11), t3 (17) ;
cout << "t2 = " << t2 << "\n" ;
t2 = t3 = t1 ;
cout << "t3 = " << t3 << "\n" ;
}
_________________________________

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 *>

Exe rcice X V.5

___________________________________________________________________________

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 ane 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
problm e .

_______________________________________________________________

Sol
ution

Pour re pr s e nte r un "grand e ntie r", la dm 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 dcim 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 :

- soit surdfinir l'op rate ur + pour tous les cas possibles ,


- soit surdfinir + uniq ue m e nt lors q u'ilporte s ur de s grands e ntie rs e t pr voir un constructe ur re ce vant
un argum e nt de type unsigne d long ;ilpe rm e ttra ainsi la conve rsion en big_int de n'im porte q ue ltype
num riq ue .
C'e s t la deuxi m e s olution q ue nous avons adopt e . Note z toute fois q ue , si elle a le m rite d' tre la plus
sim ple program m e r, e lle n'e s t pas la plus e fficace e n te m ps d'excution.

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 dclaration 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) :

/* fichier bigint.h : dclaration de la classe big_int */


#define NCHIFMAX 32 // nombre maxi de chiffres d'un entier (dpend de
// l'implmentation
#include <iostream.h>
class big_int
{ int nchif ; // nombre de chiffres
char * adchif ; // adresse emplacement contenant les chiffres
big_int (int, int) ; // constructeur priv ( usage interne)
public :
big_int (unsigned long=0) ; // constructeur partir d'un nombre usuel
big_int (char *) ; // constructeur partir d'une chane
big_int (big_int &) ; // constructeur par recopie
big_int & operator = (big_int &) ; // affectation
friend big_int operator + (big_int &, big_int &) ; // oprateur +
XV. Exercices de synth se 239

friend ostream & operator << (ostream &, big_int &) ; // oprateur <<
} ;

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 prvu 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 la dfinition de s fonctions de la clas s e big_int

/* dfinition des fonctions de la classe big_int */


#include <string.h>
#include <iostream.h>
#include "bigint.h"

big_int::big_int (int n, int p) // l'argument p est fictif


{ nchif = n ;
adchif = new char [nchif] ;
}

big_int::big_int (char * ch)


{
nchif = strlen (ch) ;
adchif = new char [nchif] ;
int i ; char c ;
for (i=0 ; i<nchif ; i++)
{ c = ch[i] - '0' ;
if (c<0 || c>9) c=0 ; // prcaution
adchif[nchif-i-1] = c ; // attention l'ordre des chiffres !
}
}

big_int::big_int (unsigned long n)


{ // on cre le grand entier correspondant dans un emplacement temporaire
char * adtemp = new char [NCHIFMAX] ;
int i = 0 ;
while (n)
{ adtemp [i++] = n % 10 ;
n /= 10 ;
}
// ici i contient le nombre exact de chiffres
nchif = i ;
adchif = new char [nchif] ;
for (i=0 ; i<nchif ; i++)
adchif [i] = adtemp [i] ;
// on libre l'emplacement temporaire
delete adtemp ;
}
240 Exercices de C+ +

big_int::big_int (big_int & n)


{ nchif = n.nchif ;
adchif = new char [nchif] ;
int i ;
for (i=0 ; i<nchif ; i++)
adchif [i] = n.adchif [i] ;
}

big_int & big_int::operator = (big_int & n)


{ if (this != &n)
{ delete adchif ;
nchif = n.nchif ;
adchif = new char [nchif] ;
int i ;
for (i=0 ; i<nchif ; i++)
adchif [i] = n.adchif [i] ;
}
return * this ;
}

big_int operator + (big_int & n, big_int & p)


{ int nchifmax = (n.nchif > p.nchif) ? n.nchif : p.nchif ;
int ncar = nchifmax + 1 ;
// prparation du rsultat dans zone temporaire de taille ncar
char * adtemp = new char [ncar] ;
int i, s, chif1, chif2 ;
int ret = 0 ;
for (i=0 ; i<nchifmax ; i++)
{ chif1 = (i<n.nchif) ? n.adchif [i] : 0 ;
chif2 = (i<p.nchif) ? p.adchif [i] : 0 ;
s = chif1 + chif2 + ret ;
if (s>=10) { s -= 10 ;
ret = 1 ;
}
else ret = 0 ;
adtemp [i] = s ;
}
if (ret == 1) adtemp [ncar-1] = 1 ;
else ncar-- ;
// construction d'un objet de type big_int o l'on recopie le rsultat
big_int res (ncar, 0) ; // second argument fictif
res.nchif = ncar ;
for (i=0 ; i<ncar ; i++)
res.adchif [i] = adtemp [i] ;
delete adtemp ;
return res ;
}

ostream & operator << (ostream & sortie, big_int & n)


{ int i ;
for (i=n.nchif-1 ; i>=0 ; i--) // attention l'ordre !
sortie << n.adchif [i] + '0' ;
return sortie ;
}

Voici un pe tit program m e d'utilisation de la clas s e big_int, accom pagn du rsultat 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

Exe rcice X V.6

___________________________________________________________________________

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 lm e nts sont de type
q ue lconq ue . Ces dernie rs s e ront cons e rvs 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' lm 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 surdfinir 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 .

Voici ce q ue pourrait tre la dfinition de notre patron de clas s e s :

#include <iostream.h>
#include <stdlib.h>
242 Exercices de C+ +

template <class T> class stack


{ int nmax ; // nombre maximum de la valeurs de la pile
int nelem ; // nombre courant de valeurs de la pile
T * adv ; // pointeur sur les valeurs
public :
stack (int = 20) ; // constructeur
~stack () ; // destructeur
stack & operator << (T) ; // oprateur d'empilage
stack & operator >> (T &) ; // oprateur de dpilage (attention T &)
int operator ++ () ; // oprateur de test pile pleine
int operator -- () ; // oprateur de test pile vide
// oprateur << pour flot de sortie
friend ostream & operator << (ostream &, stack<T> &) ;
} ;
template <class T> stack<T>::stack (int n)
{ nmax = n ;
adv = new T [nmax] ;
nelem = 0 ;
}
template <class T> stack<T>::~stack ()
{ delete adv ;
}
template <class T> stack<T> & stack<T>::operator << (T n)
{ if (nelem < nmax) adv[nelem++] = n ;
return (*this) ;
}
template <class T> stack<T> & stack<T>::operator >> (T & n)
{ if (nelem > 0) n = adv[--nelem] ;
return (*this) ;
}
template <class T> int stack<T>::operator ++ ()
{ return (nelem == nmax) ;
}
template <class T> int stack<T>::operator -- ()
{ return (nelem == 0) ;
}
template <class T> ostream & operator << (ostream & sortie, stack<T> & p)
{ sortie << "// " ;
int i ;
for (i=0 ; i<p.nelem ; i++) sortie << p.adv[i] << " " ;
sortie << " " ;
}

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
dfinition figure dans stack _ge n.h ) :

/************ programme d'essai de stack *********/


#include "stack_gen.h"
#include <iostream.h>
main()
{
stack <int> pi(20) ; // pile de 20 entiers maxi
cout << "pi pleine : " << ++pi << " vide : " << --pi << "\n" ;
pi << 2 << 3 << 12 ;
cout << "pi = " << pi << "\n" ;
stack <float> pf(10) ; // pile de 10 flottants maxi
pf << 3.5 << 4.25 << 2 ; // 2 sera converti en float
XV. Exercices de synth se 243

cout << "pf = " << pf << "\n" ;


float x ; pf >> x ;
cout << "haut de la pile pf = " << x ;
cout << "pf = " << pf << "\n" ;
}

Exe rcice X V.7

___________________________________________________________________________

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 lm e nts sont de type q ue lconq ue .

___________________________________________________________________________

Sol
ution

Voici la dclaration de notre patron :

template <class T> class vect


{ int nelem ; // nombre de composantes du vecteur
T * 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 <T> &) ;
vect<T> operator = (vect<T> & v) ; // surdfinition oprateur affectation
T & operator [] (int i) ; // surdef [] pour vect non constants
T operator [] (int i) const ; // surdef [] pour vect constants
} ;

Voici la dfinition des diff re nte s fonctions m e m bre :

#include "vectgen.h"
template <class T> vect<T>::vect (int n) // constructeur "usuel"
{ adr = new T [nelem = n] ;
}

template <class T> vect<T>::vect (vect<T> & v) // constructeur par recopie


{ adr = new T [nelem = v.nelem] ;
int i ;
for (i=0 ; i<nelem ; i++)
adr[i] = v.adr[i] ;
}

template <class T> vect<T>::~vect ()


{ delete adr ;
}

template <class T> vect<T> vect<T>::operator = (vect<T> & v)


{ if (this != &v) // on ne fait rien pour a=a
244 Exercices de C+ +

{ 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> T & vect<T>::operator [] (int i)


{ return adr[i] ;
}

template <class T> T vect<T>::operator [] (int i) const


{ return adr[i] ;
}

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 ;
}

A titre indicatif, voici un pe tit program m e utilisant notre patron :

#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
}

You might also like