Vous êtes sur la page 1sur 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 problèm e donné. Cette affirm ation re s te vraie pour le program m e ur
ch e vronné q ui é tudie un nouve au langage . C'e s t dans ce tte s ituation q ue s e trouve gé né ralem e nt une
pe rsonne q ui aborde le C+ + : e lle connaî t déjà le langage C sur leq ue ls'appuie e ffe ctive m e nt C+ + ;
toute fois, ce dernie r langage introduit suffisam m e nt de possibilité s s upplém e ntaire s e t surtout de nouve aux
conce pts (e n particulie r, ce ux de la Program m ation O rie nté e O b je t) pour q ue s on appre ntissage s 'appare nte à
ce lui d'un nouve au langage .

Ce livre vous propose d'accom pagne r votre é tude du C+ + e t de la prolonge r, ce ci à l'aide d'exercices
approprié s , varié s e t de difficulté croissante .

Les diffé re nts ch apitre s , à l'e xce ption du de rnie r, corre s ponde nt à une progre s s ion classique d'un "cours de
C+ + " : incom patiblité s e ntre C e t C+ + ;les s pé cificités de C+ + ;notions de clas s e , constructe ur e t
destructe ur ;proprié tés des fonctions m e m bre ;construction, de s truction e t initialisation de s obje ts ;les
fonctions am ie s ;la surdéfinition d'opé rate urs ;les conve rsions de type définie s par l'utilisate ur ;la
te ch niq ue de l'h é ritage ;les fonctions virtue lles ;les flots d'entré e e t de sortie , les patrons de fonctions e t les
patrons de clas s e s . Le dernie r ch apitre , e nfin, propose des "e xe rcices de synth è s e ".

Ch aq ue ch apitre débute par un "rappe ldétaillé"1 des connaissance s né ce s s aire s pour aborde r les e xe rcice s
corre s pondants (nature llem e nt, un e xe rcice d'un ch apitre donné peut faire inte rve nir de s points ré s um és dans
les ch apitre s pré cédents).

Au sein de ch aq ue ch apitre , les e xe rcice s propos é s vont d'une application im m édiate du cours à des
ré alisations de clas s e s re lative m e nt com plète s . Au filde votre progre s s ion dans l'ouvrage , vous ré aliserez
des classes de plus e n plus "ré aliste s e t opé rationne lles " e t ayant un inté rê t gé né ral;citons, par e xe m ple :

- les e ns e m bles ,
- les ve cte urs dynam iq ue s ,
- les tableaux dynam iq ue s à plusieurs dim e nsions,
- les liste s ch aî
né e s ,
- les tableaux de bits,
- les (vraie s ) ch aî
nes de caractè re s ,
- les piles ,
- les com plexe s ,
- e tc.
Nature llem e nt, tous les e xe rcice s s ont "corrigé s ". Pour la plupart, la solution propos é e ne s e lim ite pas à une
sim ple liste d'un program m e (laq ue lle ne re pré s e nte finalem e nt q u'une rédaction possible parm i d'autre s ).
Vous y trouve re z une analyse détaillée du problèm e e t, si besoin, les justifications de ce rtains ch oix. D e s
com m e ntaire s vie nne nt, le cas é ch é ant, é claire r les partie s q ue lque pe u dé licate s . Fré q ue m m e nt, vous
trouve re z des suggestions de prolonge m e nt ou de gé né ralisation du problèm e abordé .

1 Le cours com pl
et corre s pondant à ces résum é s peut ê tre trouvé dans "Program m er en langage C+ + " ou dans "Program m er en
Turbo C+ + ", du m ê m e auteur, aux Editions Eyrolles.
O utre la m aîtrise du langage C+ + propre m e nt dit, les e xe rcice s propos é s vous perm e ttront de vous forge r
une "m é th odologie de conce ption de vos propre s clas s e s ". Notam m e nt, vous saure z :

- décide r du bie n-fondé de la surdéfinition de l'opé rate ur d'affe ctation ou du constructe ur par re copie ,
- e xploite r, lors q ue vous juge re z q ue ce la e s t opportun, les possibilités de "conve rsions autom atiq ue s "
q ue le com pilate ur pe ut m e ttre e n place ,
- com m e nt tire r parti de l'h é ritage (sim ple ou m ultiple) e t q ue ls avantage s pré s e nte la cré ation d'une
"biblioth è q ue de clas s e s ", notam m e nt par le biais du "typage dynam iq ue des obje ts" q ui dé coule de
l'e m ploi de s fonctions virtue lles .
D e s urcroît, vous aure z re ncontré un ce rtain nom bre de te ch niq ue s fondam e ntales te lles q ue : la ré alisation
d'un "ité rate ur" ou les "clas s e s conte ne urs" e t l'utilisation de s possibilités de "fonctions génériques" (patrons
de fonctions) e t de "clas s e s gé né riq ue s " (patrons de clas s e s ).

N.B.

En atte ndant la norm alisation de C+ + , ce s ont des publications de AT& T q ui s e rve nt de "ré fé re nce s " :
ve rsion 1.1 e n 86, 1.2 e n 87, 2.0 e n 89 , 3.0 e n 9 1 (ce tte derniè re s e rvant de bas e au travaildu com ité
ANSI). Le s rares diffé re nce s e xistant e ntre les ve rsions sont claire m e nt m e ntionnées dans les ré s um és des
diffé re nts ch apitre s .

Le s e xe m ples de program m e s fournis e n solution ont é té te s té s ave c le com pilate ur du logicie lTurbo C+ +
pour W indow s, ve rsion 3.0.
TA BLE D ES M A TIERES

Avant-propos ......................................................................................... V

I. Incom patibil
ités entre C et C+ + ..............................................................1

II. Les spécificités de C+ + ........................................................................7

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

IV. Proprié tés des fonctions m em bre .......................................................... 45

V. Construction, destruction et initial


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

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

VII. La surdéfinition d'opérateurs ............................................................. 9 1

VIII. Les conversions de type définies 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'entrée 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 défini par la norm e ANSI. Se ules e xiste nt q ue lque s
"incom patibilité s " dont nous vous rappe lons ici les principales .

D é cl
arations d e fonctions

En C+ + , toute fonction utilisée dans un fich ie r source doit obligatoire m e nt avoir fait l'obje t :

- soit d'une déclaration sous form e d'un prototype (ilpré cis e à la fois le nom de la fonction, le type de
s e s argum e nts é ve ntue ls e t le type de sa valeur de re tour), com m e dans ce t e xe m ple :
float fexp (int, double, char *) ;

- soit d'une définition pré alable au s e in du m ê m e fich ie r source (ce dernie r cas é tant d'ailleurs peu
cons e illé, dans la m e s ure où de s problèm e s ris q ue nt d'apparaî
tre dè s lors q u'on s é pare ladite fonction du
fich ie r source e n q ue s tion).
En C, une fonction pouvait ne pas ê tre déclaré e (auq ue lcas, on considérait, par dé faut, q ue s a valeur de
re tour é tait de type int), ou e ncore déclaré e partie llem e nt (sans fournir le type de ses argum e nts), com m e
dans :

float fexp () ;

Fonctions s ans argum e nts

En C+ + , une fonction sans argum e nt se définit (au nive au de l'e n-tê te ) e t se déclare (au nive au du
prototype ) e n fournissant une "liste d'argum e nts vide" com m e dans :

float fct () ;

En C, on pouvait indiffé re m m e nt utiliser cette notation ou faire appe lau m ot clé void com m e dans :

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 définit (e n-tê te ) e t se déclare (prototype ) obligatoire m e nt à
l'aide du m ot clé void com m e dans :

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 globaldéclaré 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 "définition" de
sym boles par la dire ctive #de fine ).

Le type void *

En C+ + , un pointe ur de type void *ne pe ut pas ê tre conve rti "im plicite m e nt" lors d'une affe ctation e n un
pointe ur d'un autre type ;la ch os e é tait pe rm ise en C. Bie n e nte ndu, e n C+ + , ilre s te possible de faire
appe là l'opé rate ur de "cast".

Exe rcice I.1

___________________________________________________________________________

Enoncé

Quelles e rre urs s e ront déte cté e s par un com pilate ur C+ + dans ce fich ie r source q ui e s t acce pté par un
com pilate ur C?

main()
{
int a=10, b=20, c ;
c = g(a, b) ;
printf ("valeur de g(%d,%d) = %d", a, b, c) ;
}
g(int x, int y)
{
return (x*x + 2*x*y + y*y) ;
}
___________________________________________________________________________

Sol
ution

1) La fonction g doit obligatoire m e nt faire l'obje t d'une déclaration (sous form e d'un prototype ) dans la
fonction m ain. Par e xe m ple, on pourrait introduire (n'im porte où avant l'appe lde g) :
I. Incom patibilité s e ntre C e t C+ + 3

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 rôle dans la suite e t ils
n'inte rfè re nt nullem e nt ave c d'autre s variables de m ê m e nom q ui pourraie nt ê tre déclarées dans la m ê m e
fonction (ici m ain).

2) La fonction printf doit, e lle aussi, com m e toute s les fonctions C+ + (le com pilate ur n'é tant pas e n
m e s ure de distingue r les fonctions de la biblioth è q ue des "fonctions définie s par l'utilisate ur"), faire
l'obje t d'un prototype . Nature llem e nt, iln'e s t pas néce s s aire de l'é crire e xplicite m e nt : ile s t obte nu par
incorporation du fich ie r e n-tê te corre s pondant :
#include <stdio.h>

Note z q ue ce rtains com pilate urs C refus e nt déjà l'abs e nce de prototype pour une fonction de la
biblioth è q ue s tandard te lle q ue printf (m ais la norm e ANSI n'im posait rie n à ce s uje t!).

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
définir soit com m e des variables , soit à l'aide d'une directive #de fine com m e s uit :

#include <stdio.h>
#define NB 10
#define EXCLUS 5
4 Exe rcice s e n langage C+ +

main()
{
int valeurs [NB] ;
int i ;
int nbval=0 ;
printf ("donnez %d valeurs :\n", NB) ;
for (i=0 ; i<NB ; i++) scanf ("%d", &valeurs[i]) ;
for (i=0 ; i<NB ; i++)
switch (valeurs[i])
{ case EXCLUS-1 :
case EXCLUS :
case EXCLUS+1 : nbval++ ;
}
printf ("%d valeurs sont interdites", nbval) ;

}
CH A PITRE II :
LES SPECIFICITES D E C+ +

RAPPELS

C+ + dispose, par rapport au C ANSI, d'un ce rtain nom bre de spé cificité s q ui ne s ont pas vé ritablem e nt
axé e s s ur la Program m ation O rie nté e O b je t.

Nouve l
le s pos s ibil
ité s d'e ntré e s -s ortie s

C+ + dispose de nouve lles facilités d'entré e s -sortie s . Bie n q u'e lles s oie nt forte m e nt lié e s à des aspects
P.O .O . (surdéfinition d'opé rate ur e n particulie r), e lles s ont parfaite m e nt utilisables e n de h ors de ce conte xte .
C'e s t tout particuliè re m e nt le cas des possibilités d'entré e s -sortie s conve rsationne lles (clavie r, é cran) q ui
re m place nt avantage us e m e nt les fonctions printf e t scanf. Ainsi :

cout < < e xpression1 < < e xpression2 < < ......... < < e xpressionn

affich e s ur le "flot" cout (conne cté par dé faut à la sortie s tandard stdout) les valeurs des diffé re nte s
e xpre s s ions indiq ué e s , suivant une pré s e ntation adapté e à leur type (depuis la ve rsion 2.0 de C+ + , on sait
distingue r les attributs de signe et on pe ut affich e r de s valeurs de pointe urs). De m ê m e :

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 considéré com m e un com m e ntaire .
8 Exe rcice s e n langage C+ +

Em pl
ace m e nt l
ibre de s dé cl
arations

En C+ + , iln'e s t plus néce s s aire de re groupe r les déclarations e n début de fonction ou e n début de bloc. Il
devie nt ainsi possible d'em ploye r de s e xpre s s ions dans des initialisations com m e dans ce t e xe m ple :

int n ;
.....
n = ... ;
.....
int q = 2*n - 1 ;
.....

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

En faisant pré céder du sym bole & le nom d'un argum e nt dans l'e n-tê te (e t dans le prototype ) d'une fonction,
on ré alise une "transm ission par ré fé re nce ". Ce la signifie q ue les é ve ntue lles m odifications e ffe ctué e s au
s e in de la fonction porte ront sur l'argum e nt e ffe ctif de l'appe le t non plus sur une copie .

Le s argum e nts par dé faut

D ans la déclaration d'une fonction (prototype ), ile s t possible de pré voir pour un ou plusieurs argum e nts
(obligatoire m e nt les dernie rs de la liste ) des "valeurs par dé faut" : e lles s ont indiq ué e s par le s igne =, à la
suite du type de l'argum e nt com m e dans :

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

Ce s valeurs par dé faut s e ront alors utilisées en cas d'appe lde ladite fonction ave c un nom bre d'argum e nts
infé rie ur à ce lui pré vu. Par e xe m ple, ave c la pré cédente déclaration, l'appe lfct ('a') s e ra é q uivalent à fct
('a', 10, 0.0) ;de m ê m e , l'appe lfct ('x', 12) s e ra é q uivalent à fct ('x', 12, 0.0). En re vanch e , l'appe lfct ()
s e ra illégal.

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 utilisées 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 utilisées 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 "dégradante s " depuis la ve rsion 2.0), ainsi que les "conve rsions définie s par
l'utilisate ur" e n cas d'argum e nt de type clas s e , à condition q u'aucune am biguïté n'apparais s e .

1 La notion de référence est pl


us générale 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 ployée.

2 On l
e trouvera, par exem ple, dans l'annexe A de notre ouvrage "Program m er en langage C+ + ", ou dans le ch apitre V de
"Program m er en Turbo C+ + ", publiés égalem ent par les Editions EYROLLES.
II. Le s s pécificités de C+ + 9

R e m arque :

Avant la ve rsion 2, toute définition ou utilisation d'une fonction "ordinaire " (c'e s t-à -dire autre q u'une
"fonction m e m bre d'une clas s e ") surdéfinie devait ê tre s ignalée au com pilate ur par une déclaration
pré alable de la form e : ove rload nom _fonction.

Ge s tion dynam iq ue de l
a m é m oire

En C+ + , les fonctions m alloc, calloc... e t fre e sont re m placé e s avantage us e m e nt par les "opé rate urs" new
e t del
ete.

Si type re pré s e nte la description d'un type absolum e nt q ue lconq ue e t si n re pré s e nte une e xpre s s ion d'un type
e ntie r3

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 é lém 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 néce s s aire de ré pé te r
le nom bre d'élém e nts, du m oins lors q u'ilne s 'agit pas d'obje ts, m ê m e lors q ue ce lui-ci e s t diffé re nt de 1. Le
cas des tableaux d'obje ts e s t e xam iné dans le ch apitre V.

En outre , vous pouve z définir une fonction de votre ch oix e t dem ande r q u'e lle s oit appe lée e n cas de m anq ue
de m é m oire . Ilvous suffit d'en transm e ttre l'adre s s e e n argum e nt à la fonction set_new _h andl
er.

Le s fonctions "e n l
igne "

Une fonction "e n ligne " (on dit aussi "déve loppé e ") e s t une fonction dont les instructions sont incorporé e s
par le com pilate ur (dans le m odule obje t corre s pondant) à ch aq ue appe l. Ce la é vite la pe rte de te m ps
né ce s s aire à un appe lusuel(ch ange m e nt de conte xte , copie des valeurs des argum e nts sur la "pile", ...) ;e n
re vanch e , les instructions e n q ue s tion sont gé né ré e s plusieurs fois. Le s fonctions "e n ligne " offre nt le m ê m e
inté rê t q ue les m acros, sans présente r de ris q ue s "d'effe ts de bord". Une fonction e n ligne e s t né ce s s aire m e nt
définie e n m ê m e te m ps q u'e lle e s t déclaré e (e lle ne pe ut plus ê tre com pilée s é paré m e nt) e t son en-tê te e s t
pré cédé du m ot clé inline com m e dans :

inline fct ( ...)


{
.....
}

Exe rcice II.1

___________________________________________________________________________

3 Général
em ent long ou unsigned long.
10 Exe rcice s e n langage C+ +

Enoncé

Ecrire le program m e s uivant (corre ct e n C com m e e n C+ + ), e n ne faisant appe l q u'aux nouve lles
possibilités d'entré e s -sorties de C+ + , donc e n re m plaçant les appe ls à printf e t scanf :

#include <stdio.h>
main()
{
int n ; float x ;
printf ("donnez un entier et un flottant\n") ;
scanf ("%d %e", &n, &x) ;
printf ("le produit de %d par %e\n'est : %e", n, x, n*x) ;
}
___________________________________________________________________

Sol
ution

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


// suivant les implémentations, 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 pécificités 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 << "après : " << 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é férence

#include <iostream.h>
main()
{
void echange (int &, int &) ; // prototype de la fonction echange
int n=15, p=23 ;
cout << "avant : " << n << " " << p << "\n" ;
echange (n, p) ; // attention n et non &n, p et non &p
cout << "après : " << n << " " << p << "\n" ;
}
void echange (int & a, int & b)
{ int c ; // pour la permutation
c = a ;
a = b ;
b = c ;
}

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

b) Avec une transm ission par ré férence

#include <iostream.h>
struct essai
{ int n ;
float x ;
} ;
void raz (struct essai & s)
{ s.n = 0 ;
s.x = 0.0 ;
}
main()
{ struct essai s ;
raz (s) ; // notez bien s et non &s !
cout << "valeurs après raz : " << s.n << " " << s.x ;
}

Exe rcice II.4

___________________________________________________________________________

Enoncé

Soie nt les déclarations (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 pécificités 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 lée s e t les
conve rsions éve ntue llem e nt m ises en place ?

a) fct (n) ;
b) fct (x) ;
c) fct (n, x) ;
d) fct (x, n) ;
e) fct (c) ;
f) fct (n, p) ;
g) fct (n, c) ;
h) fct (n, z) ;
i) fct (z, z) ;
___________________________________________________________________________

Sol
ution

Le s cas a, b, c e t d ne pos e nt aucun problèm e , q ue lle q ue s oit la ve rsion de C+ + utilisée. 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 biguïté ;deux possibilité s e xiste nt e n e ffe t : cons e rve r n, conve rtir
p e n float e t appe ler la fonction III ou, au contraire , conve rtir n e n float, cons e rve r p e t appe ler la fonction
IV.

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

h ) Appelde la fonction III, aprè s conve rsion (dégradante ) de z en float (du m oins, depuis la ve rsion 2.0, car
auparavant, on aboutissait à une e rre ur de com pilation lié e à l'am biguïté de l'appe l).

i) Appelincorre ct, com pte te nu de s on am biguïté ;deux possibilité s e xiste nt e n e ffe t : conve rtir le pre m ie r
argum e nt e n float e t le s e cond e n int e t appe ler la fonction III ou, au contraire , conve rtir le pre m ie r
argum e nt e n int e t le s e cond e n float e t appe ler la fonction IV. Note z q ue , dans le deux cas, ils'agit de
conve rsions dégradante s (dans les ve rsions anté rie ure s à la 2.0, ces derniè re s n'é taie nt pas acce pté e s , e t
l'appe lé tait re je té parce q u'alors aucune corre s pondance n'é tait trouvé e !).

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

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é cificités de ce langage , les instructions C suivante s :

double * adtab ;
int nval ;
.....
printf ("combien de valeurs ? ") ;
scanf ("%d", &nval) ;
adtab = malloc (sizeof (double) * nval) ;
___________________________________________________________________________

Sol
ution

double * adtab ;
int nval ;
.....
cout << "combien de valeurs ? " ;
cin >> nval ;
adtab = new double [nval] ;

Exe rcice II.7

___________________________________________________________________________
II. Le s s pécificités de C+ + 15

Enoncé

Ecrire un program m e allouant des em place m e nts pour de s tableaux d'e ntie rs dont la taille e s t fournie e n
donné e . Le s allocations s e poursuivront jus q u'à ce q ue l'on aboutisse à un débordem e nt m é m oire .
L'e xé cution s e pré s e nte ra ainsi :

Taille souhaitée ? 6000


Allocation bloc numéro : 1
Allocation bloc numéro : 2
Allocation bloc numéro : 3
Allocation bloc numéro : 4
Allocation bloc numéro : 5
Mémoire insuffisante - arrêt exécution

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 souhaitée ? " ;


cin >> taille ;

for (nbloc=1 ; ; nbloc++)


{ adr = new int [taille] ;
if (adr) cout << "Allocation bloc numéro : " << nbloc << "\n" ;
else { cout << "Mémoire insuffisante - arrêt exécution \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 lée autom atiq ue m e nt dè s q u'une allocation m é m oire
aura é ch oué , de sorte q u'iln'e s t plus néce s s aire de pré voir de te s t à ch aq ue appe lde ne w .
16 Exe rcice s e n langage C+ +

#include <stdlib.h> // pour exit


#include <iostream.h>
main()
{
void deborde () ; // proto fonction appelée en cas manque mémoire
void set_new_handler ( void (*) () ) ; // proto de set_new_handler
set_new_handler (&deborde) ; // précise quelle sera la fonction
// appelée en cas de manque mémoire
long taille ;
int * adr ;
int nbloc ;

cout << "Taille souhaitée ? " ;


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

void deborde () // fonction appelée en cas de manque mémoire


{
cout << "Mémoire insuffisante - arrêt exécution \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) ; // déclaration (prototype) de fct
int n = 150, p ;
char c = 's' ;
p = fct ( c , n) ;
cout << "fct (\'" << c << "\', " << n << ") vaut : " << p ;
}
int fct (char c, int n) // définition de fct
{
int res ;
if (c == 'a') res = n + c ;
else if (c == 's') res = n - c ;
else res = n * c ;
return res ;
}

b) Com m e nt faudrait-ilprocéder si l'on souh aitait q ue la fonciton fct soit com pilée s é paré m e nt?
II. Le s s pécificités de C+ + 17

___________________________________________________________________________

Sol
ution

a) Nous devons donc d'abord dé clare r (e t définir e n m ê m e te m ps) la fonction fct com m e une fonction "e n
ligne ". Le program m e m ain s'é crit de la m ê m e m aniè re , si ce n'e s t q ue la déclaration de fct n'y e s t plus
né ce s s aire puis q u'e lle apparaî
t déjà auparavant.

#include <iostream.h>
inline int fct (char c, int n)
{ int res ;
if (c == 'a') res = n + c ;
else if (c == 's') res = n - c ;
else res = n * c ;
return res ;
}
main ()
{ int n = 150, p ;
char c = 's' ;
p = fct (c, n) ;
cout << "fct (\'" << c << "\', " << n << ") vaut : " << p ;
}

b) Ils'agit e n fait d'une q ue s tion piè ge . En e ffe t, la fonction fct é tant "e n ligne ", e lle ne pe ut plus ê tre
com pilée s é paré m e nt. Ile s t ce pe ndant possible de la cons e rve r dans un fich ie r d'e xte nsion h e t d'incorpore r
sim plem e nt ce fich ie r par #include pour com piler le m ain. Ce tte dém arch e s e re ncontre ra d'ailleurs
fré q ue m m e nt dans le cas de clas s e s com portant des fonctions "e n ligne ". D ans ce cas, dans un fich ie r
d'exte nsion h , on trouve ra la déclaration de la clas s e e n q ue s tion, à l'inté rie ur de laq ue lle apparaî tront les
"déclarations-définitions" des fonctions "e n ligne ".
CH A PITRE III :
NO TIO NS D E CLASSE,
CO NSTRUCTEUR ET D ESTRUCTEUR

RAPPELS

Le s possibilités de Program m ation O rie nté e O b je t de C+ + re pos e nt sur le conce pt de clas s e . Une clas s e e s t
la gé né ralisation de la notion de type défini par l'utilisate ur, dans leq ue ls e trouve nt associé e s à la fois des
donné e s (on parle de "m e m bres donnée") e t des fonctions (on parle de "fonctions m e m bre " ou de m é th ode s ).
En P.O .O . pure , les données sont "e ncapsulée s ", ce q ui signifie q ue leur accè s ne peut s e faire q ue par le
biais des m é th ode s . C+ + vous autoris e à n'e ncapsuler q u'une partie s e ulem e nt des données d'une clas s e 1.

D é cl
aration e t dé finition d'une cl
as s e

La d é cl aration d'une clas s e pré cis e q ue ls sont les m e m bre s (donné e s ou fonctions) publics (c'e s t-à -dire
acce s s ibles à l'utilisate ur de la clas s e ) e t q ue ls sont les m e m bre s privé s (inacce s s ibles à l'utilisate ur de la
clas s e ). O n utilise pour cela le m ot clé publ ic, com m e dans ce t e xe m ple dans leq ue lla clas s e point com porte
deux m e m bres donnée privé s x e t y e t trois fonctions m e m bre s publiq ue s initialise , de place e t affich e :

/* ------------ Déclaration de la classe point ------------- */


class point
{ /* déclaration des membres privés */
int x ;
int y ;
/* déclaration 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 définitions des fonctions m e m bre . Dans ce cas, on indiq ue
le nom de la clas s e corre s pondante , à l'aide de l'opé rate ur de ré s olution de porté e (::). Au sein de la

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

définition m ê m e , les m e m bre s (privé s ou publics - donné e s ou fonctions) sont dire cte m e nt acce s s ibles s ans
q u'ilsoit né ce s s aire de pré cis e r le nom de la clas s e . Voici, par e xe m ple, ce q ue pourrait ê tre la définition de
la fonction initialise de la clas s e pré cédente :

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 déclaration te lle
q ue la suivante e s t tout à fait e nvisage able :
class X
{ private :
...
public :
...
private :
...
public :
...
} ;

Util
is ation d'une cl
as s e

O n dé clare un "obje t" d'un type classe donné en faisant pré céder son nom du nom de la clas s e , com m e dans
l'instruction suivante q ui dé clare deux obje ts a e t b de type point :

point a, b ;

O n pe ut accéder à n'im porte q ue lm e m bre public (donné e ou fonction) d'une clas s e e n utilisant l'opé rate ur .
(point). Par e xe m ple :

a.initialise (5, 2) ;

appe lle la fonction m e m bre initialise de la clas s e à laq ue lle appartie nt l'obje t a, c'e s t-à -dire , ici, la clas s e
point.

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 données (qu'ils soie nt publics ou privé s ). Toute fois,
si, parm i ce s ch am ps, s e trouve nt des pointe urs, les e m place m e nts pointé s ne s e ront pas soum is à ce tte
re copie . Si un te le ffe t e s t né ce s s aire (e t ille s e ra souve nt!), ilne pourra ê tre obte nu q u'e n "surdéfinissant"
l'opé rate ur d'affe ctation pour la clas s e conce rné e (voye z le ch apitre consacré à la surdéfinition
d'opé rate urs).
III. Notions de clas s e , cons tructe ur e t de s tructe ur 25

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 déclare r un obje t du type corre s pondant,
sans fournir de s valeurs pour les argum e nts re q uis par ce constructe ur (sauf si ce dernie r ne possè de aucun
argum e nt). Le constructe ur e s t appe lé aprè s l'allocation de l'e s pace m é m oire destiné à l'obje t.

Par définition m ê m e , un constructe ur ne re nvoie pas de valeur (aucune indication de type , pas m ê m e void,
ne doit figure r de vant sa déclaration ou sa définition).

Une fonction m e m bre portant le m ê m e nom q ue s a clas s e , pré cédé du sym bole tilda (~ ), s e nom m e un
destructeur. Le destructe ur e s t appe lé avant la libération de l'e s pace m é m oire associé à l'obje t. Par
définition m ê m e , un de s tructe ur ne pe ut pas com porte r d'argum e nts e t ilne re nvoie pas de valeur (aucune
indication de type ne doit ê tre pré vue ).

M e m bre s donné e s tatiq ue s

Un m e m bre donnée déclaré ave c l'attribut static e s t partagé par tous les obje ts de la m ê m e clas s e . Ile xiste
m ê m e lors q ue aucun obje t de ce tte clas s e n'a é té déclaré . Un m e m bre donnée statiq ue e s t initialisé par
défaut à zé ro (com m e toute s les variables s tatiq ue s !). Ilpe ut ê tre initialisé explicite m e nt, à l'e xté rie ur de la
clas s e (m ê m e s 'ile s t privé ), e n utilisant l'opé rate ur de ré s olution de porté e (::) pour spécifie r sa clas s e .

Expl
oitation d'une cl
as s e

En pratiq ue , à l'utilisate ur d'une clas s e (on dit souve nt le "clie nt"), on fournira :

- un fich ie r e n-tê te conte nant la déclaration de la clas s e : l'utilisate ur l'e m ployant devra l'inclure dans
tout program m e faisant appe là la clas s e e n q ue s tion,
- un m odule obje t ré s ultant de la com pilation du fich ie r source conte nant la définition de la clas s e , c'e s t-
à -dire la définition de s e s fonctions m e m bre .

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 coordonnées du point s e ront des m e m bres donnée privé s .

O n é crira s é paré m e nt :

- un fich ie r source constituant la déclaration de la clas s e ,


- un fich ie r source corre s pondant à sa définition.
26 Exe rcice s e n langage C+ +

Ecrire , par ailleurs, un pe tit program m e d'essai (m ain) déclarant un point, l'affich ant, le déplaçant e t
l'affich ant à nouve au.

___________________________________________________________________________

Sol
ution

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


a d é cl
aration de l
a cl
asse

/* fichier POINT1.H */
/* déclaration de la classe point */
class point
{
float x, y ; // coordonnées (cartésiennes) du point
public :
point (float, float) ; // constructeur
void deplace (float, float) ; // déplacement
void affiche () ; // affichage
} ;

b) Fich ier source contenant l


a d é finition de l
a cl
asse

/* définition 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 coordonnées cartésiennes 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 déclaration 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 coordonnées 1.25 2.5
p.affiche () ; // affichage de ce point
p.deplace (2.1, 3.4) ; // déplacement 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 définition de la clas s e point.

Note z q ue , gé né ralem e nt, le fich ie r point.h contie ndra des directive s conditionne lles de com pilation, afin
d'évite r les ris q ues d'inclusion m ultiple. Par e xe m ple, on pourra procéder ainsi :

#ifndef POINT_H
#define POINT_H
.....

déclaration de la clas s e .....

.....

#endif

Exe rcice III.2

___________________________________________________________________________

Enoncé

R é aliser une clas s e point, analogue à la pré cédente , m ais ne com portant pas de fonction affich e . Pour
re s pe cte r le principe d'encapsulation des données, prévoir de ux fonctions m e m bre publiq ue s (nom m é e s
abscis s e e t ordonne e ) fournissant e n re tour re s pe ctive m e nt l'abscis s e e t l'ordonnée d'un point. Adapte r le
pe tit program m e d'essai pré cédent pour q u'ilfonctionne ave c ce tte nouve lle clas s e .

___________________________________________________________________________

Sol
ution

Ilsuffit d'introduire deux nouve lles fonctions m e m bre abscis s e e t ordonné e e t de supprim e r la fonction
affich e . La nouve lle déclaration de la clas s e e s t alors :

/* fichier POINT2.H */
/* déclaration de la classe point */
class point
{
float x, y ; // coordonnées (cartésiennes) du point
public :
point (float, float) ; // constructeur
void deplace (float, float) ; // déplacement
float abscisse () ; // abscisse du point
float ordonnee () ; // ordonnée du point
} ;

Voici sa nouve lle définition :

/* définition 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 << "Coordonnées cartésiennes : " << p.abscisse () << " "
<< p.ordonnee () << "\n" ;
p.deplace (2.1, 3.4) ; // déplacement
// affichage
cout << "Coordonnées cartésiennes : " << 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 à accéder (aussi bien en
consultation - com m e ici - q u'e n m odification) aux m e m bre s privé s . L'inté rê t de leur e m ploi (par rapport à
un accè s direct aux donné e s q u'ilfaudrait alors re ndre publiq ue s ) ré s ide dans la souplesse de m odification de
l'im plém e ntation de la clas s e q ui e n dé coule. Ainsi, ici, ile s t tout à fait possible de m odifie r la m aniè re dont
un point e s t re pré s e nté (par e xe m ple, e n utilisant s e s coordonné e s polaire s plutôt q ue s e s coordonné e s
carté s ie nne s ), sans q ue l'utilisate ur de la clas s e n'ait à s e s oucie r de ce t aspect. C'e s t d'ailleurs ce q ue vous
m ontre ra l'e xe rcice III.4 ci-aprè s.

Exe rcice III.3

___________________________________________________________________________

Enoncé

Ajoute r à la clas s e pré cédente (com portant un constructe ur e t 3 fonctions m e m bre de place , abscis s e e t
ordonne e ) de nouve lles fonctions m e m bre :

- h om oth e tie q ui e ffe ctue une h om oth é tie dont le rapport e s t fourni e n argum e nt,
- rotation q ui e ffe ctue une rotation dont l'angle e s t fourni e n argum e nt,
- rh o e t th e ta q ui fournis s e nt e n re tour les coordonnées pol
aires du point.
___________________________________________________________________________
III. Notions de clas s e , cons tructe ur e t de s tructe ur 29

Sol
ution

La déclaration de la nouve lle clas s e point découle directe m e nt de l'é noncé :

/* fichier POINT3.H */
/* déclaration de la classe point */
class point
{
float x, y ; // coordonnées (cartésiennes) du point
public :
point (float, float) ; // constructeur
void deplace (float, float) ; // déplacement
void homothetie (float) ; // homothetie
void rotation (float) ; // rotation
float abscisse () ; // abscisse du point
float ordonnee () ; // ordonnée du point
float rho () ; // rayon vecteur
float theta () ; // angle
} ;

Sa définition m é rite q ue lque s re m arq ue s . En e ffe t, si h om oth e tie ne pré s e nte aucune difficulté , la fonction
m e m bre rotation, q uant à e lle, né ce s s ite une transform ation inte rm édiaire des coordonné e s carté s ie nnes du
point e n coordonné e s polaire s . De m ê m e , la fonction m e m bre rh o doit calculer le rayon ve cte ur d'un point
dont on connaî t les coordonné e s carté s ie nne s tandis q ue la fonction m e m bre th e ta doit calculer l'angle d'un
point dont on connaî t les coordonné e s carté s ie nne s .

Le calculde rayon ve cte ur é tant sim ple, nous l'avons lais s é figure r dans les deux fonctions conce rné e s
(rotation e t rh o). En re vanch e , le calculd'angle a é té ré alisé par ce que nous nom m ons une "fonction de
s e rvice ", c'e s t-à -dire une fonction q ui n'a d'inté rê t q ue dans la définition de la clas s e e lle-m ê m e . Ici, il
s'agit d'une fonction indé pe ndante m ais, bie n e nte ndu, on pe ut pré voir de s fonctions de service s ous form e
de fonctions m e m bre (e lles s e ront alors généralem e nt privé e s ).

Voici finalem e nt la définition de notre clas s e point :

/**************** déclarations 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)

/*************** définition 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) ; // coordonnées polaires
t += th ; // rotation th
x = r * cos (t) ; // retour en
y = r * sin (t) ; // coordonnées cartésiennes
}

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

} /********** définition des fonctions de service ***********/


/* fonction de calcul de l'angle correspondant aux coordonnées */
/* cartésiennes fournies en argument */
/* On choisit une détermination 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é cédente , de m aniè re à ce q ue les données (privé e s ) soient m ainte nant les
coordonné e s polaires d'un point, e t non plus s e s coordonné e s carté s ie nne s . O n é vite ra de m odifie r la
déclaration de s m e m bre s publics, de sorte q ue l'inte rface de la clas s e (ce q ui e s t visible pour l'utilisate ur) ne
ch ange pas.

___________________________________________________________________________

Sol
ution

La déclaration de la nouve lle classe découle directe m e nt de l'é noncé :

/* fichier POINT4.H */
/* déclaration de la classe point */
III. Notions de clas s e , cons tructe ur e t de s tructe ur 31

class point
{
float r, t ; // coordonnées (polaires) du point
public :
point (float, float) ; // constructeur
void deplace (float, float) ; // déplacement
void homothetie (float) ; // homothétie
void rotation (float) ; // rotation
float abscisse () ; // abscisse du point
float ordonnee () ; // ordonnée du point
float rho () ; // rayon vecteur
float theta () ; // angle
} ;

En ce q ui conce rne s a définition, 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 déplace m e nt, avant de re pas s e r e n
coordonné e s polaire s .
En re vanch e , les fonctions h om oth e tie e t rotation s'e xprim e nt trè s sim plem e nt.

Voici la définition de notre nouve lle clas s e (nous avons fait appe là la m ê m e "fonction de s e rvice " angle q ue
dans l'e xe rcice pré cédent) :

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

/********** définition des fonctions de service ***********/


/* fonction de calcul de l'angle correspondant aux coordonnées */
/* cartésiennes fournies en argument */
/* On choisit une détermination 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 ;
}
/********** définition 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 ordonnée
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 crée dans l'e xe rcice III.1, e t dont la déclaration é tait la suivante :

class point
{
float x, y ;
public :
point (float, float) ;
void deplace (float, float) ;
void affiche () ;
}

Adapte r ce tte clas s s e , de m aniè re à ce q ue la fonction m e m bre affich e fournis s e , e n plus des coordonnées du
point, le nom bre d'obje ts de type point.

___________________________________________________________________________

Sol
ution

Ilfaut donc dé finir un com pte ur du nom bre d'obje ts e xistant à un m om e nt donné . Ce com pte ur doit ê tre
incré m e nté à ch aq ue cré ation d'un nouve lobje t, donc par le constructe ur point. De m ê m e , ildoit ê tre
décré m e nté à ch aq ue destruction d'un obje t, donc par le destructe ur de la clas s e point ;ilfaudra donc
ajoute r ici une fonction m e m bre nom m é e ~ point.

Quant au com pte ur propre m e nt dit, nous pourrions ce rte s e n faire une variable globale, définie par e xe m ple,
e n m ê m e te m ps q ue la clas s e ;ce tte dém arch e pré s e nte toute fois des ris q ues d'effe ts de bord (m odification
III. Notions de clas s e , cons tructe ur e t de s tructe ur 33

accide nte lle de la valeur de ce tte variable, depuis n'im porte q ue l program m e utilisate ur). Il e s t plus
judicie ux d'e n faire un m e m bre privé s tatiq ue 2.

Voici la nouve lle déclaration de notre clas s e point :

/* fichier POINT5.H */
/* déclaration de la classe point */
class point
{
static nb_pts ; // compteur du nombre d'objets créés
float x, y ; // coordonnées (cartésiennes) du point
public :
point (float, float) ; // constructeur
~point () ; // destructeur
void deplace (float, float) ; // déplacement
void affiche () ; // affichage
} ;

Et voici sa nouve lle définition :

#include "point5.h"
#include <iostream.h>
point::point (float abs, float ord) // constructeur
{ x = abs ; y = ord ;
nb_pts++ ; // actualisation nb points
}
point::~point () // destructeur
{ nb_pts-- ; // actualisation nb points
}
void point::deplace (float dx, float dy)
{ x = x + dx ; y = + dy ;
}
void point::affiche ()
{ cout << "Je suis un point parmi " << nb_pts
<< " de coordonnées "<< x << " " << y << "\n" ;
}

R e m arque :

Ilpourraî t ê tre judicie ux de m unir notre clas s e point d'une fonction m e m bre fournissant le nom bre
d'obje ts de type point e xistant à un m om e nt donné . C'e s t ce q ue nous vous proposerons dans un exercice
du proch ain ch apitre .

Exe rcice III.6

___________________________________________________________________________

2 En toute rigueur, ilreste toutefois possibl


e d'initialiser un telm em bre depuis l'extérieur 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é lém e nt,
connaî tre s on "cardinal" (nom bre d'élém e nts), savoir si un caractè re donné lui appartie nt.

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 donnée, 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 é lém e nt de rang i pré cis e s i le caractè re de code i appartie nt ou non à
l'e ns e m ble. Note z q u'il e s t né ce s s aire q ue i soit positif ou nul;on travaillera donc toujours sur de s
caractè re s non signés. La taille du tableau doit ê tre é gale au nom bre de caractè re s q u'ile s t possible de
re pré s e nte r dans une im plém e ntation donné e (gé né ralem e nt 256).

Le re s te de la déclaration de la classe découle de l'é noncé .

/* fichier SETCHAR1.H */
/* déclaration de la classe set_char */
#define N_CAR_MAX 256 // on pourrait utiliser UCHAR_MAX défini
// dans <limits.h>

class set_char
{
unsigned char ens [N_CAR_MAX] ;
// tableau des indicateurs (présent/absent)
// pour chacun des caractères possibles
public :
set_char () ; // constructeur
void ajoute (unsigned char) ; // ajout d'un élément
int appartient (unsigned char) ; // appartenance d'un élément
int cardinal () ; // cardinal de l'ensemble
} ;

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

/* définition 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 () << " caractères différents" ;
}

R e m arque :

Si l'on avait déclaré de type ch ar les argum e nts de ajoute e t appartie nt, on aurait alors pu aboutir, soit au
type unsigne d ch ar, soit au type signe d ch ar, s e lon l'e nvironne m e nt utilisé. Dans le dernie r cas, on aurait
couru le ris q ue de transm e ttre , à l'une des fonctions m e m bre cité e s , une valeur né gative , e t partant
d'accéder à l'e xté rie ur du tableau e ns.

D is cus s ion

Le tableau e ns [N_CH AR _M AX] occupe un octe t par caractè re ;ch acun de ce s octe ts ne prend que l'une des
valeurs 0 ou 1 ;on pourrait é conom iser de l'e s pace m é m oire e n pré voyant s e ulem e nt 1 bit par caractè re . Le s
fonctions m e m bre y perdraient toute fois e n sim plicité , ainsi qu'en vite s s e .

Bie n e nte ndu, be aucoup d'autre s im plém e ntations sont possibles ;c'e s t ainsi, par e xe m ple, q ue l'on pourrait
fournir au constructe ur un nom bre m axim ald'élém e nts, e t alloue r dynam iq ue m e nt l'e m place m e nt m é m oire
corre s pondant ;toute fois, là e ncore , on pe rdrait le béné fice de la corre s pondance im m édiate e ntre un
caractè re e t la position de s on indicate ur. Note z, toute fois, q ue ce s e ra la s e ule possibilité ré aliste lors q u'il
s'agira de re pré s e nte r de s e ns e m bles dans les q ue ls le nom bre m axim ald'élém e nts s e ra trè s grand.

Exe rcice III.7

___________________________________________________________________________

Enoncé

M odifie r la clas s e s e t_ch ar pré cédente , de m aniè re à ce q ue l'on dispose de ce que l'on nom m e un
"ité rate ur" sur les diffé re nts é lém e nts de l'e ns e m ble. O n nom m e ainsi un m é canism e pe rm e ttant d'accéder
s é q ue ntie llem e nt aux diffé re nts é lém e nts. O n pré voira trois nouve lles fonctions m e m bre : init, q ui initialise
36 Exe rcice s e n langage C+ +

le proce s s us d'exploration ;proch ain, q ui fournit l'é lém e nt suivant lors q u'ile xiste e t e xiste , q ui pré cis e s 'il
e xiste e ncore un é lém e nt non e xploré .

O n com pléte ra alors le program m e d'utilisation pré cédent, de m aniè re à ce q u'ilaffich e les diffé re nts
caractè re s conte nus dans le m ot fourni e n donné e .

___________________________________________________________________________

Sol
ution

Com pte te nu de l'im plém e ntation de notre clas s e , la ge s tion du m é canism e d'ité ration né ce s s ite l'e m ploi
d'un pointe ur (q ue nous nom m e rons courant) sur un é lém e nt du tableau e ns. Nous convie ndrons q ue courant
désigne le pre m ie r é lém e nt de e ns non e ncore traité dans l'ité ration, c'e s t-à -dire non e ncore re nvoyé par la
fonction m e m bre suivant (nous aurions pu adopte r la conve ntion contraire , à savoir q ue courant désigne le
dernie r é lém e nt traité ).

En outre , pour nous facilite r la re connaissance de la fin de l'ité ration, nous utiliserons un m e m bre donnée
supplém e ntaire (fin) valant 0 dans les cas usuels, e t 1 lors q ue aucun é lém e nt ne s e ra disponible (pour
suivant).

Le rôle de la fonction init s e ra donc de faire pointe r courant sur la pre m iè re valeur non nulle de e ns s'ile n
e xiste une ;dans le cas contraire , fin s e ra placé à 1.

La fonction suivant fournira e n re tour l'é lém e nt pointé par courant lors q u'ile xiste (fin non nul) ou la valeur
0 dans le cas contraire (ils'agit là d'une conve ntion de s tiné e à proté ge r l'utilisate ur ayant appe lé ce tte
fonction, alors q u'aucun é lém e nt n'é tait plus disponible). Dans le pre m ie r cas, suivant re ch e rch e ra le
proch ain é lém e nt de l'e ns e m ble (e n m odifiant la valeur de fin lors q u'un te lé lém e nt n'e xiste pas). Note z bie n
q ue la fonction suivant doit re nvoye r, non pas le proch ain é lém e nt, m ais l'é lém e nt courant.

Enfin la fonction e xiste s e conte nte ra de re nvoye r la valeur de fin puis q ue ce tte derniè re indiq ue l'e xiste nce
ou l'ine xiste nce d'un élém e nt courant.

Voici la nouve lle définition de la clas s e s e t_ch ar :

/* fichier SETCHAR2.H */
/* déclaration de la classe set_char */
#define N_CAR_MAX 256 // on pourrait utiliser UCHAR_MAX défini
// dans <limits.h>

class set_char
{
unsigned char ens [N_CAR_MAX] ;
// tableau des indicateurs (présent/absent)
// pour chacun des caractères possibles
int courant ; // position courante dans le tableau ens
int fin ; // indique si fin atteinte
public :
set_char () ; // constructeur
void ajoute (unsigned char) ; // ajout d'un élément
int appartient (unsigned char) ; // appartenance d'un élément
int cardinal () ; // cardinal de l'ensemble
void init () ; // initialisation itération
unsigned char suivant () ; // caractère suivant
int existe () ; //
} ;
III. Notions de clas s e , cons tructe ur e t de s tructe ur 37

Voici la définition de s trois nouve lles fonctions m e m bre init, suivant e t e xiste :

void set_char::init ()
{ courant=0 ; fin = 0 ;
while ( (++courant<N_CAR_MAX) && (!ens[courant]) ) ;
// si la fin de ens est atteinte, courant vaut N_CAR_MAX
if (courant>=N_CAR_MAX) fin = 1 ;
}

unsigned char set_char::suivant ()


{ if (fin) return 0 ; // au cas où on serait déjà en fin de ens
unsigned char c = courant ; // conservation du caractère 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 caractère
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 () << " caractères différents"
<< " qui sont :\n" ;
ens.init() ; // init itération sur les caractères de l'ensemble
while (ens.existe())
cout << ens.suivant () ;
}
CH A PITRE IV :
PRO PRIETES D ES FO NCTIO NS M EM BRE

RAPPELS

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 possibilités déjà offe rte s par C+ + pour
les "fonctions ordinaire s ".

Fonctions m e m bre e n l
igne

Ils'agit é galem e nt de la gé né ralisation aux fonctions m e m bre d'une possibilité offe rte pour les fonctions
ordinaire s , ave c une pe tite nuance conce rnant sa m ise en oeuvre ;pour re ndre "e n ligne " une fonction
m e m bre , on pe ut :

- soit fournir dire cte m e nt la définition de la fonction dans la déclaration m ê m e de la clas s e ;dans ce cas
le q ualificatif inline n'a pas à ê tre utilisé, com m e dans ce t e xe m ple :
class truc
{ ...
int fctenlig (int, float)
{ définition de fcte nlig
}
.....
} ;

- soit procéder com m e pour une fonction "ordinaire ", e n fournissant une définition e n de h ors de la
déclaration de la clas s e ;dans ce cas, le q ualificatif inline doit apparaî
tre , à la fois devant la déclaration
e t devant l'e n-tê te .

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 donnée dans un em place m e nt localà la
fonction appe lée . Des problèm e s pe uve nt surgir dè s lors q ue l'obje t transm is en argum e nt contie nt des
pointe urs sur de s parties dynam iq ue s . Ils s e ront ré glés par l'e m ploi d'un "constructe ur par re copie " (voir
ch apitre s uivant).

R e m arque :

Bie n q ue ce s oit d'un usage plus lim ité , une fonction ordinaire pe ut é galem e nt re ce voir un argum e nt de
type clas s e . Bie n e nte ndu, dans ce cas, e lle n'aura accè s q u'aux m e m bre s publics de ce t argum e nt.

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 sûr q u'aux m e m bre s publics de l'obje t e n q ue s tion). La
transm ission peut, là e ncore , s e faire par valeur, par adre s s e ou par ré fé re nce .

La transm ission par valeur im pliq ue une re copie q ui pose donc les m ê m e s problèm e s q ue ce ux é voq ué s ci-
desssus pour les obje ts com portant des pointe urs sur de s parties dynam iq ue s . Quant aux transm issions par
adre s s e ou par ré fé re nce , e lles doive nt ê tre utilisées ave c beaucoup de pré cautions, dans la m e s ure où, dans
ce cas, on re nvoie (gé né ralem e nt) l'adresse d'un obje t alloué autom atiq ue m e nt, c'e s t-à -dire dont la duré e d e
vie coïncide ave c ce lle de la fonction.

Auto-ré fé re nce : l
e m ot cl
é th is

Au sein d'une fonction m e m bre , th is re pré s e nte un pointe ur sur l'obje t ayant appe lé ladite fonction m e m bre .

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
déclare r ave c l'attribut static. D ans ce cas, une te lle fonction pe ut ê tre appe lée , sans m e ntionne r d'obje t
particulie r, e n pré fixant sim plem e nt son nom du nom de la clas s e conce rné e , suivi de l'opé rate ur de
ré s olution de porté e (::).

Fonctions m e m bre cons tante s

O n pe ut déclare r de s obje ts constants (à l'aide du qualificatif const). Dans ce cas, seules les fonctions
m e m bre déclaré e s (e t définie s ) ave c ce m ê m e q ualificatif (e xe m ple de déclaration : void affich e () const)
pe uve nt re ce voir (im plicite m e nt ou e xplicite m e nt) e n argum e nt un obje t constant.

Exe rcice IV.1

___________________________________________________________________________
IV. Propriétés des fonctions m e m bre 47

Enoncé

O n souh aite ré aliser une clas s e ve cte ur3d pe rm e ttant de m anipuler de s ve cte urs à trois com posante s . O n
pré voit q ue s a déclaration s e pré s e nte ainsi :

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

O n souh aite pouvoir dé clare r un ve cte ur, soit e n fournissant e xplicite m e nt s e s trois com posante s , soit e n e n
fournissant aucune , auq ue l cas le ve cte ur cré é possè dera trois com posante s nulles . Ecrire le ou les
constructe ur(s) corre s pondant(s) :

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 possibilités de surdé finition, d'argum e nts par dé faut e t d'écriture "e n
ligne " des fonctions m e m bre s .

a)
/* déclaration de la classe vecteur3d */
class vecteur3d
{
float x, y, z ;
public :
vecteur3d () ; // constructeur sans arguments
vecteur3d (float, float, float) ; // constructeur 3 composantes
.....
} ;

/* définition 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)
/* déclaration de la classe vecteur3d */
class vecteur3d
{
float x, y, z ;
public :
vecteur3d (float=0.0, float=0.0, float=0.0) ; // constructeur (unique)
.....
} ;
/* définition du constructeur de la classe vecteur3d */
48 Exe rcice s e n langage C+ +

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 déclare r un point e n fournissant non s e ulem e nt
0 ou 3 com posante s , m ais é ve ntue llem e nt s e ulem e nt une ou de ux. Ce tte s olution n'e s t donc pas
rigoure us e m e nt é q uivalente à la pré cédente .

c)
/* déclaration de la classe vecteur3d */
class vecteur3d
{ float x, y, z ;
public :
// constructeur unique "en ligne"
vecteur3d (float c1=0.0, float c2=0.0, float c3=0.0) ;
..{ x = c1 ; y = c2 ; z = c3 ;
}.
.....
} ;

Ici, iln'y a plus aucune définition de constructe ur, puis q ue ce dernie r e s t "e n ligne ".

Exe rcice IV.2

___________________________________________________________________________

Enoncé

Soit une clas s e ve cte ur3d définie 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 coïncide nce de ce s 2 ve cte urs,
dans ch acun de s 3 cas considérés?

___________________________________________________________________________
IV. Propriétés des fonctions m e m bre 49

Sol
ution

La fonction coincide e s t m e m bre de la clas s e ve cte ur3d ;e lle re ce vra donc im plicite m e nt l'adresse du
ve cte ur l'ayant appe lé. Elle ne possédera donc q u'un s e ulargum e nt, lui-m ê m e de type ve cte ur3d. Nous
supposerons q u'e lle fournit une valeur de re tour de type int (1 pour la coïncide nce , 0 dans le cas contraire ).

a) La déclaration de coincide pourra s e pré s e nte r ainsi :

int coincide (vecteur3d) ;

Voici ce q ue pourrait ê tre s a définition :

int vecteur3d::coincide (vecteur3d v)


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

b) La déclaration de coincide devie nt :

int coincide (vecteur3d *) ;

Et sa nouve lle définition 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 définition de coincide pourrait faire m oins de distinction e ntre ses deux argum e nts
(l'un im plicite , l'autre e xplicite ) :
int vecteur3d::coincide (vecteur3d * adv)
{ if ( (adv->x == this->x) && (adv->y == this->y) && (adv->z == this->z) )
return 1 ;
else return 0 ;
}

c) La déclaration de coincide devie nt :

int coincide (vecteur3d &) ;

Et sa nouve lle définition 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 précédente 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 surdéfinition d'opé rate ur offrira une m ise en oeuvre plus agré able de ce te s t de coïncide nce de deux
ve cte urs. C'e s t ainsi qu'il s e ra possible de surdé finir l'opé rate ur de com paraison == (pour la clas s e
ve cte ur3d) e t, partant, d'e xprim e r ce te s t sous la sim ple form e : v1 == v2.

Exe rcice IV.3

___________________________________________________________________________

Enoncé

Soit une clas s e ve cte ur3d définie 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 définition de la fonction norm ax (la déclaration 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. Propriétés 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 problèm e ici, puis q ue notre clas s e ne com porte aucun pointe ur
sur des parties dynam iq ue s .

b) Aucun nouve au problèm e ne s e pos e . Ilsuffit de m odifie r ainsi l'e n-tê te de notre fonction, sans e n
m odifie r le corps :

vecteur3d & vecteur3d::normax (vecteur3d & v)

La fonction norm ax s'utilise com m e pré cédem 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 lproblèm e ne s e pos e pas pour l'argum e nt im plicite
(th is), car ilcorre s pond toujours à l'adresse d'un ve cte ur (transm is autom atiq ue m e nt par ré fé re nce ), e t non à
une valeur.

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 réalisée.

_______________________________________________________________

Sol
ution

La fonction m e m bre calculant la som m e de deux ve cte urs (nous la nom m e rons som m e ) re çoit im plicite m e nt
(par ré fé re nce ) un argum e nt de type ve cte ur3d. Elle com porte ra donc un s e ulargum e nt, lui aussi de type
ve cte ur3d. O n pe ut, a priori, le transm e ttre par adre s s e , par valeur ou par ré fé re nce . En fait, la transm ission
par adre s s e , e n C+ + , n'a plus guè re de raison d'ê tre , dans la m e s ure où la transm ission par ré fé re nce fait la
m ê m e ch os e , m oye nnant une é criture plus agré able.

Le ch oix doit donc s e faire e ntre transm ission par valeur ou par ré fé re nce . Lors q u'ils'agit de transm e ttre un
obje t (com portant plusieurs m e m bres donnée), la transm ission par ré fé re nce e s t plus e fficace (e n te m ps
d'exécution). Qui plus e s t, la fonction som m e re çoit déjà im plicite m e nt un ve cte ur par ré fé re nce , de sorte
q u'iln'y a aucune raison de lui transm e ttre diffé re m m e nt le s e cond ve cte ur.

Le m ê m e raisonne m e nt s'appliq ue à la fonction de calculdu produit scalaire (q ue nous nom m ons prodscal).

En ce q ui conce rne la valeur de re tour de som m e , laq ue lle e s t é galem e nt de type ve cte ur3d, iln'e s t, e n
re vanch e , pas possible de la tranm e ttre par ré fé re nce . En e ffe t, ce "ré s ultat" (de type ve cte ur3d) s e ra cré é au
s e in de la fonction e lle-m ê m e , ce q ui signifie q ue l'obje t corre s pondant s e ra de clas s e autom atiq ue , donc
détruit à la fin de l'e xé cution de la fonction. Ilfaut donc absolum e nt e n transm e ttre la valeur.

Voici ce q ue pourrait ê tre la déclaration de notre clas s e ve cte ur3d (ici, s e ulle constructe ur a é té pré vu "e n
ligne " :

/* déclaration 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 (résultat par valeur)
float prodscal (vecteur3d &) ; // produit scalaire
void affiche () ; // affichage composantes
} ;

Voici sa définition :

/* définition 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. Propriétés 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 déclare ra donc ainsi :

static int compte () ;

Voici sa définition :

int point::compte ()
{ return nb_pts ;
}

Voici un e xe m ple d'appe lde com pte au s e in d'un program m e dans leq ue lla clas s e point a é té déclaré e :

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 libération 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 déclaration soit dans une fonction, soit au s e in d'un bloc. Ils
sont cré é s au m om e nt de l'e xé cution de la déclaration (laq ue lle, e n C+ + , pe ut apparaî
tre n'im porte où dans
un program m e ). Ils sont détruits lors q ue l'on sort de la fonction ou du bloc.

Le s obje ts statiques sont cré é s par une déclaration situé e e n de h ors de toute fonction ou par une déclaration
pré cédée du m ot clé static (dans une fonction ou dans un bloc). Ils sont cré é s avant l'e ntrée dans la fonction
m ain e t détruits aprè s la fin de s on e xé cution.

Le s obje ts te m poraire s

L'appe le xplicite , au s e in d'une e xpre s s ion, du constructe ur d'un obje t provoq ue la cré ation d'un obje t
te m poraire (on n'a pas accè s à son adre s s e ) q ui pourra ê tre autom atiq ue m e nt détruit dè s lors q u'ilne s e ra
plus utile (m ais le langage ne pré cis e pas q uand aura e ffe ctive m e nt lie u ce tte destruction!). Par e xe m ple, si
une clas s e point possè de le constructe ur point (float, float) e t si a e s t de type point, nous pouvons é crire 1 :

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 déclaration,
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 possédant le
constructe ur point (float, float) :

point * adp ;
.....
adp = new point (2.5, 5.32) ;

L'accè s au m e m bres d'un obje t dynam iq ue e s t ré alisé com m e pour les variables ordinaire s . Par e xe m ple, si
point possè de une m é th ode nom m é e affich e , on pourra l'appe ler par (*adp).affich e () ou e ncore par adp-
> affich e ().

Le s obje ts dynam iq ue s n'ont pas de durée de vie définie a priori. Ils sont détruits à la dem ande e n utilisant
ete com m e dans : de lete adr3.
l'opé rate ur del

Initial
is ation d'obje ts

En C+ + , ile s t trè s im portant de distingue r les deux opé rations q ue s ont l'initialisation e t l'affe ctation.

Ily a initialisation d'un obje t dans l'une des trois situations suivante s :

- déclaration 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 précisém e nt :

- si aucun constructe ur de l'une des form e s type (type & ) ou type (const type & ) (type désignant le type
de l'obje t) n'a é té défini, ily aura appe ld'un "constructe ur de re copie par dé faut" ;ce dernie r re copie les
valeurs des diffé re nts m e m bres donnée de l'obje t, com m e le fait l'affe ctation. D e s problèm e s pe uve nt
alors s e pos e r dè s lors q ue l'obje t contie nt des pointe urs sur de s parties dynam iq ue s .
- si un constructe ur de la form e type (type & ) ou, m ie ux, type (const type & ) e xiste , ils e ra appe lé. Note z
q u'alors le constructe ur par re copie par dé faut n'e s t plus appe lé. C'e s t donc au constructe ur par re copie
de prévoir la re copie de tous les m e m bres de l'obje t.

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é cédée de la cré ation d'un obje t te m poraire . La
déclaration pré cédente e s t rigoure us e m e nt é q uivalente à :
point a (1.3, 4.5) ;

2 Com m e pour l
e s variables ordinaires, on peut préciser un nom bre d'objets m ais des restrictions apparais s e nt alors q uant au
cons tructeur qu'ile s t possible d'appeler (voyez ci-dessous la rubriq ue "tableaux d'objets ").
3 D ans l
e cas d'un tableau d'objets, on devra préciser, en plus, son nom bre d'élém ents.
V. Construction, de s truction e t initialisation de s obje ts 61

Le s tabl
e aux d'obje ts

Si point e s t un type obje t possédant un constructe ur sans argum e nt (ou, situation gé né ralem e nt décons e illée ,
sans constructe ur), la déclaration :

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

delete [] adcourbe ; // depuis la version 3.0

ou :

delete [20] adcourbe ; // versions antérieures à la 3.0

R e m arque :

En th é orie , depuis la ve rsion 2.0 de C+ + , on pe ut com pléte r la déclaration d'un tableau d'obje ts par un
initialiseur conte nant une liste de valeurs (e lles pe uve nt é ve ntue llem e nt ê tre de types diffé re nts). Ch aq ue
valeur e s t alors transm ise à un constructe ur approprié . Ce tte facilité ne pe ut toute fois pas s'appliq ue r aux
tableaux dynam iq ue s (ile n va de m ê m e pour les tableaux ordinaire s !).

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

Une clas s e pe ut posséder un m e m bre donnée qui est lui-m ê m e de type clas s e . En voici un e xe m ple ;si nous
avons défini :

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

nous pouvons définir une clas s e pointcol, dont un m e m bre e s t de type point :

class pointcol
{ point p ;
int couleur ;
public :
pointcol (float, float, int) ;
.....
62 Exe rcice s e n langage C+ +

} ;

D ans ce cas, lors de la cré ation d'un obje t de type pointcol, ily aura tout d'abord appe ld'un constructe ur de
pointcol, puis appe ld'un constructe ur de point ;ce dernie r re ce vra les argum e nts q u'on aura m e ntionné
dans l'e n-tê te de la définition du constructe ur de pointcol ;par e xe m ple, ave c :

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 façon globale ;autre m e nt dit, la
"valeur" d'un obje t é tait re porté e ("bit par bit") dans une autre , sans te nir com pte de sa structure .

Les diffé re nce s e ntre ces deux m odes de re copie s e ront particuliè re m e nt s e nsibles dans la situation suivante :

- obje t m e m bre com portant des pointe urs sur de s e m place m e nts dynam iq ue s e t ayant défini son
constructe ur par re copie ;
- obje t principaln'ayant pas défini de constructe ur par re copie .
D e puis la ve rsion 2.0, la construction par re copie d'un te lobje t fonctionne ra conve nablem e nt (s'ilne
contie nt pas lui-m ê m e de pointe ur), puis q u'ily aura bie n appe ldu constructe ur par re copie de l'obje t
m e m bre . Dans les ve rsions anté rie ure s , e n re vanch e , ce constructe ur par re copie , bie n q u'e xistant, ne s e ra
pas utilisé et les ch os e s s e ront beaucoup m oins satisfaisante s .

Exe rcice V.1

___________________________________________________________________________

Enoncé

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

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

fournis s e les ré s ultats suivants :

création 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 prévoir dans le constructe ur de ch os e , l'instruction :

cout << "création 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 détruire ) :

création 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 déclaration de la clas s e de m o,
sa définition e t le program m e d'utilisation ont é té re groupé s e n un s e ulfich ie r) :

#include <iostream.h>

class demo
{ int x, y ;
public :
demo (int abs=1, int ord=0) // constructeur I (0, 1 ou 2 arguments)
{ x = abs ; y = ord ;
cout << "constructeur I : " << x << " " << y << "\n" ;
}
demo (demo&) ; // constructeur II (par recopie)
~demo () ; // destructeur
} ;

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 indépendante fct
cout << "début main\n" ;
demo a ;
demo b = 2 ;
demo c = a ;
demo * adr = new demo (3,3) ;
fct (a, adr) ;
demo d = demo (4,4) ;
c = demo (5,5) ;
cout << "fin main\n" ;
}

void fct (demo d, demo * add)


{ cout << "entrée 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) :

début main
constructeur I : 1 0 dem o a ;
constructeur I : 2 0 dem o b = 2 ;
constructeur II (recopie) : 1 0 dem o c = a ;
constructeur I : 3 3 new dem o (3,3)
constructeur II (recopie) : 1 0 recopie de la val
eur de a dans fct (a, ..)
(crée un objet tem poraire)
entrée fct
destruction : 3 3 del
ete add ; (dans fct)
sortie fct
constructeur I : 4 4 dem o d = dem o (4, 4) ;
constructeur I : 5 5 c = dem o (5,5) (cons truction objet tem poraire)
fin main
destruction : 5 5 destruction objet tem poraire (peut s e
faire à un autre m om ent
destruction : 4 4 destruction d
destruction : 1 0 destruction objet tem poraire créé pour l 'appel
de fct (peut apparaî tre ail
leurs)
destruction : 5 5 destruction c
destruction : 2 0 destruction b
destruction : 1 0 destruction a

Note z bie n q ue l'affe ctation c = de m o (5,5) e ntraî ne la cré ation d'un obje t te m poraire par appe l du
constructe ur de de m o (argum e nts 5 et 5) ;ce t obje t e s t e nsuite affe cté à a. O n constate d'ailleurs q ue ce t

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 détruit (ici, aprè s l'e xé cution du m ain, m ais, dans d'autre s im plém e ntations, ce la
pe ut s e produire plus tôt).

Par ailleurs, l'appe lde fct a e ntraî né la construction d'un obje t te m poraire , par appe ldu constructe ur par
re copie . Nous aurions pu pe ns e r q ue ce t obje t s e rait libéré à la sortie de la fonction. Ici, ce n'e s t pas le cas
m ais ce la pourrait l'ê tre dans d'autre s im plém e ntations.

D is cus s ion

Ce t e xe m ple devrait vous m e ttre e n garde contre le fait q ue de nom bre ux obje ts te m poraire s pe uve nt pre ndre
naissance dans un program m e , sans m ê m e q u'on s'e n re nde com pte ;des problèm es d'encom bre m e nt de la
m é m oire pe uve nt apparaî tre dans les im plém e ntations q ui ne libè re nt pas les e m place m e nt te m poraire s , dè s
q ue ce la e s t deve nu possible.

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 donnée
privé re pré s e ntant un num é ro de point (le pre m ie r cré é porte ra le num é ro 1, le s uivant le num é ro 2, e t ainsi
de suite ...). Le constructe ur affich e ra le num é ro du point cré é e t le destructe ur affich e ra le num é ro du point
détruit. Ecrire un pe tit program m e d'utilisation cré ant dynam iq ue m e nt un tableau de 4 points e t le
détruisant.

___________________________________________________________________________

Sol
ution

Pour pouvoir num é rote r nos points, ilnous faut pouvoir com pte r le nom bre de fois où le constructe ur a é té
appe lé, ce q ui nous perm e ttra bie n d'attribue r un num é ro diffé re nt à ch aq ue point. Pour ce faire , nous
définissons, au s e in de la clas s e point, un m e m bre donnée statiq ue nb_points. Ici, ils e ra incré m e nté par le
constructe ur m ais le destructe ur n'aura pas d'action sur lui.

Voici la déclaration (dé finition) de notre clas s e , accom pagnée du program m e d'utilisation de m andé :

#include <iostream.h>
class point
{ int num;
static nb_points ;
public :
point ()
{ num = ++nb_points ;
cout << "création point numéro : " << num << "\n" ;
}
~point ()
{ cout << "Destruction point numéro : " << num << "\n" ;
}
} ;

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

int i ;
delete [4] adcourb ;
}

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

Ici, on cons e rve ra les diffé re nts é lém e nts de l'e ns e m ble dans un tableau alloué dynam iq ue m e nt par le
constructe ur. Un argum e nt (auq ue lon pourra pré voir une valeur par dé faut) lui pré cis e ra le nom bre m axim al
d'élém e nts de l'e ns e m ble.

2) Ecrire , e n outre , un program m e (m ain) utilisant la clas s e s e t_int pour dé te rm ine r le nom bre d'entie rs
diffé re nts conte nus dans un tableau d'e ntie rs lus e n donné e .

3) Que faudrait-il faire pour q u'un obje t du type s e t_int puis s e ê tre transm is par valeur, soit com m e
argum e nt d'appe l, soit com m e valeur de re tour d'une fonction.

___________________________________________________________________________

Sol
ution

1) La déclaration de la classe découle de l'é noncé :

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

Le m e m bre donnée advale s t destiné à pointe r sur le tableau d'e ntie rs q ui s e ra alloué par le constructe ur. Le
m e m bre nm ax re pré s e nte ra la taille de ce tableau, tandis q ue ne le m fournira le nom bre e ffe ctif d'entie rs
stock és dans ce tableau. Ce s e ntie rs s e ront, ce tte fois, rangés dans l'ordre où ils s e ront fournis à ajoute , e t
non plus à un e m place m e nt prédéte rm iné , com m e nous l'avions fait pour les caractè re s (dans les e xe rcice s
du ch apitre pré cédent).

Com m e la cré ation d'un obje t e ntraî ne ici une allocation dynam iq ue d'un em place m e nt m é m oire , ile s t
raisonnable de pré voir la libération de ce t e m place m e nt lors de la destruction de l'obje t ;ce tte opé ration doit
donc ê tre pris e e n ch arge par le destructe ur, d'où la pré s e nce de ce tte fonction m e m bre .

Voici la définition 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 ; // libération tableau de valeurs
}

void set_int::ajoute (int nb)


{ // on examine si nb appartient déjà à 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 déjà à 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 déjà dans notre e ns e m ble.

Par ailleurs, l'é noncé ne pré voit rie n pour le cas où l'on ch e rch e à ajoute r un é lém e nt à un e ns e m ble déjà
"plein" ;ici, nous nous som m e s conte nté de ne rie n faire dans ce cas. Dans la pratiq ue , ilfaudrait, soit
pré voir un m oye n pour l'utilisate ur d'ê tre pré ve nu de ce tte s ituation, soit, m ie ux, pré voir autom atiq ue m e nt
l'agrandis s e m e nt de la zone dynam iq ue associé e à l'e ns e m ble.

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 différents\n" ;
}

3) Te lq u'e s t actue llem e nt pré vue notre clas s e s e t_int, si un obje t de ce type e s t transm is par valeur, soit e n
argum e nt d'appe l, soit e n re tour d'une fonction, ily a appe ldu constructe ur de re copie par dé faut ;or, ce
dernie r s e conte nte d'effe ctue r une copie des m e m bres donnée de l'obje t conce rné . Ce q ui signifie q u'on s e
re trouve alors e n pré s e nce de deux obje ts conte nant deux pointe urs diffé re nts sur un m ê m e tableau d'e ntie rs.
Un problèm e va donc s e pos e r, dè s lors q ue l'obje t copié s e ra dé truit (ce q ui pe ut s e produire , dè s la sortie
68 Exe rcice s e n langage C+ +

de la fonction) ;e n e ffe t, dans ce cas, le tableau dynam iq ue d'entie rs s e ra dé truit, alors m ê m e q ue l'obje t
d'origine continue ra à "pointe r" de s s us.

Indé pe ndam m e nt de ce la, d'autre s problèm e s s im ilaire s pourrait s e pos e r si la fonction é tait am e né e à
m odifie r le conte nu de l'e ns e m ble : dans ce cas, e n e ffe t, on m odifie rait le tableau d'e ntie rs original, ch os e à
laq ue lle on ne s 'atte nd pas dans le cas de transm ission par valeur.

Pour ré gler ce s problèm e s , ile s t né ce s s aire de m unir notre classe d'un constructe ur par re copie approprié .
Que doit-ilfaire ? Plusieurs solutions e xiste nt ;la plus é légante e s t ce rtaine m e nt ce q ue l'on nom m e la
ge s tion d'un "com pte ur de ré fé re nce ". Nous la re ncontre rons plus tard, associé e , gé né ralem e nt à la
surdéfinition de l'opé rate ur d'affe ctation (q ui pose des problèm e s analogue s à ce ux q ue nous ve nons
d'évoq ue r).

Pour l'instant, nous utiliserons une dém arch e plus sim ple q ui consiste à re copie r totalem e nt l'obje t conce rné ,
c'e s t-à -dire e n te nant com pte de sa "partie dynam iq ue " (on parle parfois de "copie profonde "). Pour ce faire ,
on alloue un s e cond e m place m e nt pour un tableau d'e ntie rs, dans leq ue lon re copie les valeurs du prem ie r
e ns e m ble. Nature llem e nt, ilne faut pas oublie r alors de procéder égalem e nt à la re copie des m e m bre s
donné e , puis q ue ce lle-ci n'e s t plus assuré e par le constructe ur de re copie par dé faut (leq ue ln'e s t plus appe lé,
dè s lors q u'un constructe ur par re copie a é té défini).

Nous ajoute rons donc, dans la déclaration de notre clas s e :

set_int (set_int &) ; // constructeur par recopie

Et, dans sa définition :

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 upplém e ntaire . Elle a l'inconvé nie nt de dupliq ue r totalem e nt l'obje t, ce q ui
e ntraîne une pe rte de m é m oire . La m é th ode du com pte ur de ré fé re nce (q ue nous re ncontre rons plus tard)
é vite ce tte duplication ;e n contre partie , sa m ise en oeuvre e s t beaucoup plus délicate e t e lle ne se borne
plus au sim ple ajout d'une m é th ode .

Quelle q ue s oit la m é th ode e m ployé e , on constate q ue la surdéfinition du constructe ur par re copie e s t q uasi
indispensable pour toute clas s e com portant un pointe ur sur une partie dynam iq ue . Dans l'é tat actue lde
C+ + , iln'e s t pas possible d'inte rdire la transm ission par valeur d'un obje t q ui n'e n posséderait pas! Et ilne
s e m ble pas raisonnable de livre r à un "clie nt" une te lobje t, e n lui de m andant de ne jam ais le transm e ttre par
valeur! Ce q ui signifie q ue la plupart des clas s e s "inté re s s ante s " devront définir un te lconstructe ur par
re copie .

Nous ve rrons q ue les m ê m e s ré flexions s'appliq ue ront à l'affe ctation e ntre obje ts e t q u'e lles conduiront à la
conclusion q ue la plupart des clas s e s "inté re s s ante s " devront redéfinir l'opé rate ur d'affe ctation.

Exe rcice V.5

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

Enoncé

M odifie r l'im plém e ntation de la clas s e pré cédente (ave c son constructe ur par re copie ) de façon à ce q ue
l'e ns e m ble d'entie rs soit m ainte nant re pré s e nté par une l iste ch aînée (ch aq ue e ntie r e s t rangé dans une
structure com portant un ch am p de s tiné à conte nir un nom bre e t un ch am p de s tiné à conte nir un pointe ur sur
la structure s uivante ). L'inte rface de la clas s e (la partie publiq ue de sa déclaration) devra re s te r inch angé e ,
ce q ui signifie q u'un clie nt de la clas s e continue ra à l'e m ploye r de la m ê m e façon.

___________________________________________________________________________

Sol
ution

Com m e nous le s uggè re l'é noncé , nous allons donc dé finir une s tructure q ue nous nom m e rons é lé m e nt :

struct noeud
{ int valeur ; // valeur d'un élément de l'ensemble
noeud * suivant ; // pointeur sur le noeud suivant de la liste
} ;

Note z q ue nous pouvons, e n C+ + , é crire s im plem e nt noe ud *suivant, là où C nous aurait im pos é struct
noe ud *suivant.

Notre s tructure noe ud pe ut ê tre définie indiffé re m m e nt dans la déclaration de la clas s e s e t_int ou e n de h ors.

En ce q ui conce rne les m e m bres donnée privés de la clas s e , nous ne cons e rve rons q ue ne le m q ui, bie n q ue
non indispe nsable, nous é vite ra de parcourir toute la liste pour dé te rm ine r le cardinalde notre e ns e m ble.

En re vanch e , nous y introduirons un pointe ur nom m é de but, destiné à conte nir l'adresse du pre m ie r é lém e nt
de la liste , s'ile xiste (au dé part, ils e ra initialisé à NULL).

En ce q ui conce rne le constructe ur de s e t_int, nous lui cons e rve rons son argum e nt (de type int), bie n q u'ici il
n'ait plus aucun inté rê t, e t ce la dans le but de ne pas m odifie r l'inte rface de notre clas s e (com m e le
dem andait l'é noncé ).

Voici donc la nouve lle déclaration de notre clas s e :

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

class set_int
{
noeud * debut ; // pointeur sur le début de la liste
int nelem ; // nombre courant d'éléments
public :
set_int (int = 20) ; // constructeur (argument inutile ici)
set_int (set_int &) ; // constructeur par recopie
~set_int () ; // destructeur
void ajoute (int) ; // ajout d'un élément
int appartient (int) ; // appartenance d'un élément
int cardinal () ; // cardinal de l'ensemble
} ;
70 Exe rcice s e n langage C+ +

La définition du nouve au constructe ur ne pré s e nte pas de difficulté . La fonction m e m bre ajoute ré alise une
classique insertion d'un noe ud e n début de liste e t incré m e nte le nom bre d'élém e nts de l'e ns e m ble. La
fonction appartie nt e ffe ctue une e xploration de liste , tant q u'e lle n'a pas trouvé la valeur conce rné e ou
atte int la fin de la liste . En re vanch e , le nouve au constructe ur par re copie doit re copie r la liste ch aî
né e . Il
ré alise à la fois une exploration de la liste d'origine e t une ins e rtion dans la liste copié e . Quant au
destructe ur, ildoit m ainte nant libérer systé m atiq ue m e nt tous les e m place m e nts des diffé re nts noeuds cré é s
pour la liste .

Voici la nouve lle définition 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 ;
// création d'une nouvelle liste identique à l'ancienne
noeud * adsource = e.debut ;
noeud * adbut ;
debut = NULL ;
while (adsource)
{ adbut = new noeud ; // création 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 ; // libération 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 début 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 problèm e é voq ué à propos de l'ajout d'un é lém e nt à un e ns e m ble "plein" ne s e pos e plus ici,
com pte te nu de la nouve lle im plém e ntation de notre clas s e .

Exe rcice V.6

___________________________________________________________________________

Enoncé

M odifie r la clas s e s e t_int pré cédente (im plém e nté e s ous form e d'une liste ch aî né e - ave c ou sans son
constructe ur par re copie ) pour q u'e lle dispose de ce q ue l'on nom m e un "ité rate ur" sur les diffé re nts
é lém e nts de l'e ns e m ble. R appe lons q u'on nom m e ainsi un m é canism e pe rm e ttant d'accéder s é q ue ntie llem e nt
aux diffé re nts é lém e nts de l'e ns e m ble. O n pré voira trois nouve lles fonctions m e m bre : init, pour intialiser le
proce s s us d'ité ration ;proch ain , pour fournir l'é lém e nt suivant lors q u'ile xiste e t e xiste , pour te s te r s'il
e xiste e ncore un é lém e nt non e xploré .

O n com pléte ra alors le program m e d'utilisation pré cédent (e n fait, ce lui de l'e xe rcice V.4), de m aniè re à ce
q u'ilaffich e les diffé re nts e ntie rs conte nus dans les valeurs fournie s e n donné e .

N.B. Ce t e xe rcice s e ra plus profitable s 'il e s t traité aprè s l'e xe rcice du ch apitre III q ui proposait
l'introduction d'un te l ité rate ur dans une clas s e re pré s e ntant des ens e m bles de caractè re s (m ais dont
l'im plém e ntation é tait diffé re nte de l'actue lle clas s e ).

___________________________________________________________________________

Sol
ution

Ici, la ge s tion du m é canism e d'ité ration né ce s s ite l'e m ploi d'un pointe ur (q ue nous nom m e rons courant) sur
un noe ud de notre liste . Nous convie ndrons q u'ilpointe s ur le pre m ie r é lém e nt non e ncore traité dans
l'ité ration, c'e s t-à -dire dont la valeur corre s pondante n'a pas e ncore é té re nvoyé e par la fonction proch ain. Il
n'e s t pas utile, ici, de pré voir un m e m bre donnée pour indiq ue r si la fin de liste a é té atte inte ;e n e ffe t, ave c
la conve ntion adopté e , ilnous suffit de te s te r la valeur de courant (q ui s e ra é gale à NULL, lors q ue l'on s e ra
e n fin de liste ).

Le rôle de la fonction init s e lim ite à l'initialisation de courant à la valeur du pointe ur sur le début de la liste
(de but).

La fonction suivant fournira e n re tour la valeur e ntiè re associé e au noe ud pointé par courant lors q u'ile xiste
(courant diffé re nt de NULL) ou la valeur 0 dans le cas contraire (ils'agit, là e ncore , d'une conve ntion
destiné e à proté ge r l'utilisate ur ayant appe lé ce tte fonction, alors q ue la fin de liste é tait déjà atte inte e t,
donc, q u'aucun é lém e nt de l'e ns e m ble n'é tait disponible). De plus, dans le pre m ie r cas (usuel), la fonction
suivant actualisera la valeur de courant, de m aniè re à ce q u'ilpointe s ur le noe ud suivant.
72 Exe rcice s e n langage C+ +

Enfin, la fonction e xiste e xam ine ra sim plem e nt la valeur de de but pour savoir s'ile xiste e ncore un é lém e nt à
traite r.

Voici la déclaration com plète de notre nouve lle clas s e :

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

class set_int
{
noeud * debut ; // pointeur sur le début de la liste
int nelem ; // nombre courant d'éléments
noeud * courant ; // pointeur sur noeud courant
public :
set_int (int = 20) ; // constructeur
set_int (set_int &) ; // constructeur par recopie
~set_int () ; // destructeur
void ajoute (int) ; // ajout d'un élément
int appartient (int) ; // appartenance d'un élément
int cardinal () ; // cardinal de l'ensemble
void init () ; // initialisation itération
int prochain () ; // entier suivant
int existe () ; // test fin liste
} ;

Voici la définition de s trois nouve lles fonctions m e m bre init, suivant e t e xiste :

void set_int::init ()
{ courant = debut ;
}

int set_int::prochain ()
{ if (courant)
{ int val = courant->valeur ;
courant = courant->suivant ;
return val ;
}
else return 0 ; // par convention
}

int set_int::existe ()
{ return (courant != NULL) ;
}

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 différents\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 accéder à tous les m e m bre s privés de n'im porte q ue lobje t de sa clas s e . En re vanch e , ce s
m e m bre s privé s re s te nt inacce s s ibles à n'im porte q ue lle fonction m e m bre d'une autre clas s e ou à n'im porte
q ue lle fonction indé pe ndante .

La notion de fonction am ie , ou plus e xacte m e nt de "déclaration d'am itié ", pe rm e t de déclare r dans une
clas s e , les fonctions q ue l'on autoris e à accéder à s e s m e m bre s privé s (donné e s ou fonctions). Ile xiste
plusieurs situations d'am itié .

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 à accéder aux m e m bre s privés de la clas s e A .

Gé né ralem e nt, la fonction m e m bre fct possè dera un argum e nt ou une valeur de re tour de type A (ce q ui
justifie ra sa déclaration d'am itié ). Pour traduire s a définition, le com pilate ur de vra posséder les
caracté ristiq ues de A, donc dispose r de s a déclaration.

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 à accéder aux m e m bre s
privés de la clas s e A .

Pour q u'il puis s e com piler conve nablem e nt la déclaration ci-de s s us, le com pilate ur de vra sim plem e nt
"savoir" q ue B e s t une clas s e . Si la définition de la clas s e B n'a pas e ncore é té com pilée , on lui pré cis e ra
sim plem e nt ce tte inform ation par la déclaration :

class A ;

Gé né ralem e nt la fonction m e m bre fct possè dera un argum e nt ou une valeur de re tour de type A (ce q ui
justifie ra sa déclaration d'am itié ). Pour com piler sa déclaration (au s e in de la déclaration de A ), le
com pilate ur de vra, là e ncore , savoir sim plem e nt q ue A e s t une clas s e . En re vanch e , pour com piler la
définition de fct, le com pilate ur de vra posséder les caracté ristiq ues de A, donc dispose r de s a déclaration.

c) Toute s l
e s fonctions d'une cl
as s e B s ont am ie s d'une autre cl
as s e A

D ans ce cas, plutôt q ue d'utiliser autant de déclarations d'am itié q ue de fonctions m e m bre , on utilise (dans
la déclaration de la clas s e A ) la déclaration (globale) suivante :

friend class B ;

Pour com piler la déclaration de A , on pré cis e ra sim plem e nt q ue A e s t une clas s e par class A ;q uant à la
déclaration de la clas s e B, e lle né ce s s ite ra gé né ralem e nt (dè s q u'une de ses fonctions m e m bre possè dera un
argum e nt ou une valeur de re tour de type A ) la déclaration de la clas s e A .

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 coordonnées d'un
point. O n fournira s é paré m e nt un fich ie r source conte nant la nouve lle déclaration (dé finition) de point e t un
fich ie r source conte nant la définition de la fonction affich e . Ecrire un pe tit program m e (m ain) q ui cré e un
point de clas s e autom atiq ue e t un point de classe dynam iq ue e t q ui e n affich e les coordonné e s .

___________________________________________________________________________
VI. Le s fonctions am ie s 83

Sol
ution

Nous devons donc ré aliser une fonction indé pe ndante , nom m é e affich e , am ie de la clas s e point. Une te lle
fonction, contraire m e nt à une fonction m e m bre , ne re çoit plus d'argum e nt im plicite ;affich e devra donc
re ce voir un argum e nt de type point. Son prototype s e ra donc de la form e :

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 accéder aux m e m bre s privé s x e t y de la clas s e point. Ilfaut donc
pré voir une déclaration d'am itié au s e in de ce tte clas s e , dont voici la nouve lle déclaration :

/* fichier POINT1.H */
/* déclaration de la classe point */
class point
{ int x, y ;
public :
friend void affiche (const point &) ;
point (int abs=0, int ord=0)
{ x=abs ; y=ord ;
}
} ;

Pour é crire affich e , ilnous suffit d'accéder aux m e m bre s (privé s ) x e t y de son argum e nt de type point. Si ce
dernie r s e nom m e p, les m e m bre s e n q ue s tion s e note nt (classiquem e nt) p.x e t p.y. Voici la définition de
affich e :

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

Note z bie n q ue la com pilation de affich e né ce s s ite la déclaration de la clas s e point, e t pas s e ulem e nt une
déclaration te lle q ue class point, car le com pilate ur doit connaî tre les caracté ristiq ues de la clas s e point
(notam m e nt, ici, la localisation de s m e m bre s x e t y).

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 déclaration 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 définie dans l'e xe rcice IV.2 par :

class vecteur3d
{ float x, y, z ;
public :
vecteur3d (float c1=0.0, float c2=0.0, float c3=0.0)
{ x = c1 ; y = c2 ; z = c3 ;
}
.....
} ;

Ecrire une fonction indé pe ndante coincide , am ie de la clas s e ve cte ur3d, pe rm e ttant de savoir si deux
ve cte urs ont m ê m e s com posante s (ce tte fonction re m place ra la fonction m e m bre coincide q u'on de m andait
d'écrire dans l'e xe rcice IV.2).

Si v1 e t v2 désignent deux ve cte urs de type ve cte ur3d, com m e nt s'é crit m ainte nant le te s t de coïncide nce de
ces deux ve cte urs?

___________________________________________________________________________

Sol
ution

La fonction coincide va donc dispose r de deux argum e nts de type ve cte ur3d. Si l'on pré voit de les
transm e ttre par ré fé re nce , e n inte rdisant leur é ve ntue lle m odification dans la fonction, le prototype de
coincide s e ra :

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

Voici la nouve lle déclaration de notre clas s e :

/* fichier vect3D.H */
/* déclaration de la classe vecteur3d */
class vecteur3d
{ float x, y, z ;
public :
friend int coincide (const vecteur3d &, const vecteur3d &) ;
vecteur3d (float c1=0, float c2=0, float c3=0)
{ x = c1 ; y = c2 ; z = c3 ;
}
} ;

e t la définition de la fonction coincide :

#include "vect3d.h" // nécessaire 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 coïncide nce de deux ve cte urs s'é crit m ainte nant :

coincide (v1, v2)

O n note ra q ue , tant dans la définition de coincide q ue dans ce te s t, on voit s e m anife s te r la sym é trie du
problèm e , ce q ui n'é tait pas le cas lors q ue nous avions fait de coincide une fonction m e m bre de la clas s e
ve cte ur3d.

Exe rcice VI.3

___________________________________________________________________________

Enoncé

Cré e r de ux clas s e s (dont les m e m bres donnée 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 carrées de dim e nsion 3x3 ;e lle
com porte ra un constructe ur ave c un argum e nt (adresse d'un tableau de 3x3 valeurs) q ui initialisera la
m atrice ave c les valeurs corre s pondante s .
R é aliser une fonction indé pe ndante prod pe rm e ttant de fournir le ve cte ur corre s pondant au produit d'une
m atrice par un ve cte ur. Ecrire un pe tit program m e de te s t. O n fournira s é paré m e nt les deux dé clarations de
ch acune des clas s e s , leurs deux définitions, la définition de prod e t le program m e de te s t.

___________________________________________________________________________

Sol
ution

Ici, pour nous facilite r l'é criture , nous re pré s e nte rons les com posantes d'un ve cte ur par un tableau à trois
é lém e nts e t les valeurs d'une m atrice par un tableau à 2 indice s . La fonction de calculdu produit d'un
ve cte ur par une m atrice doit obligatoire m e nt pouvoir accéder aux données des deux clas s e s , ce q ui signifie
q u'e lle devra ê tre déclaré e "am ie " dans ch acune de ces deux clas s e s .

En ce q ui conce rne ses deux argum e nts (de type ve ct e t m at), nous avons ch oisi la transm ission la plus
e fficace , c'e s t-à -dire la transm ission par ré fé re nce . Quant au ré s ultat (de type ve ct), ildoit obligatoire m e nt
ê tre re nvoyé par valeur (nous e n re parlerons dans la définition de prod).

Voici la déclaration de la clas s e ve ct :

/* fichier vect1.h */
class matrice ; // pour pouvoir compiler la déclaration de vect
86 Exe rcice s e n langage C+ +

class vect
{ double v[3] ; // vecteur à 3 composantes
public :
vect (double v1=0, double v2=0, double v3=0) // constructeur
{ v[0] = v1 ; v[1]=v2 ; v[2]=v3 ; }
friend vect prod (const matrice &, const vect &) ;// fonction amie indépendante
void affiche () ;
} ;

e t la déclaration de la clas s e m at :

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

Note z q ue nous avons déclaré constants les argum e nts de la fonction m atrice . Ils'agit sim plem e nt d'une
pré caution de s tiné e à pré ve nir le ris q ue d'une faute de program m ation conduisant, au s e in de la définition de
m atrice , à une m odification desdits argum e nts.

La définition de s fonctions m e m bre (e n fait affich e ) de la clas s e ve ct ne pré s e nte pas de difficulté s :

#include <iostream.h>
#include "vect1.h"
void vect::affiche ()
{ int i ;
for (i=0 ; i<3 ; i++) cout << v[i] << " " ;
cout << "\n" ;
}

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 définition prod né ce s s ite les fich ie rs conte nant les déclarations 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 résultat du produit
for (i=0 ; i<3 ; i++)
{ for (j=0, som=0 ; j<3 ; j++)
VI. Le s fonctions am ie s 87

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 libéré 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 surdéfinir les opé rate urs e xistants, c'e s t-à -dire de leur donne r une nouve lle
signification lors q u'ils porte nt (e n partie ou e n totalité ) sur des obje ts de type clas s e .

Le m é canis m e de l
a surdé finition d'opé rate urs

Pour surdéfinir un opé rate ur e xistant op, on dé finit une fonction nom m é e ope rator op (on pe ut place r un ou
plusieurs espaces entre le m ot ope rator e t l'opé rate ur, m ais ce n'e s t pas une obligation) :

- soit sous form e d'une fonction indé pe ndante (gé né ralem e nt am ie d'une ou de plusieurs clas s e s ),
- soit sous form e d'une fonction m e m bre d'une clas s e .
D ans le pre m ie r cas, si op e s t un opé rate ur binaire , la notation a op b e s t é q uivalente à :

operator op (a, b)

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

Un opé rate ur surdéfini doit toujours posséder un opérande de type clas s e (on ne pe ut donc pas m odifie r les
significations des opérate urs usuels). Ildoit donc s'agir :

- soit d'une fonction m e m bre , auq ue lcas e lle dispose obligatoire m e nt d'un argum e nt im plicite du type de
sa clas s e (th is),
- soit d'une fonction indé pe ndante (ou plutôt am ie ) possédant au m oins un argum e nt de type clas s e .
Ilne faut pas faire d'h ypoth è s e s ur la signification a priori d'un opé rate ur ;par e xe m ple, la signification de
+ = pour une clas s e ne pe ut, e n aucun cas, ê tre déduite de la significaiton de + e t de = pour ce tte m ê m e
clas s e .
92 Exercices en langage C+ +

Cas particul
ie rs

Le s opé rate urs [], (), -> , ne w e t de lete doive nt obligatoire m e nt ê tre définis com m e fonctions m e m bre .

Le s opé rate urs = (affe ctation) e t & (pointe ur sur) possè dent une s ignification prédéfinie pour les obje ts de
n'im porte q ue ltype clas s e . Ce la ne les e m pê ch e nullem e nt d'ê tre s urdé finis.

La surdéfinition de ne w , pour un type classe donné, se fait par une fonction de prototype :

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 surdéfinition 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 à libérer.

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é décroissante )

(1) S'iln'e s t pas s urdéfini, 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 surdéfinition d'opé rateurs 93

(3) D oit ê tre défini 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 urdéfini 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 définis de façon unaire ) corre s ponde nt à l
a notation "pré " ;m ais ile n e xis te une définition 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 surdéfini les opé rate urs ne w e t de lete pour une clas s e , ilre s te possible de faire appe l
aux opé rate urs ne w e t de lete usuels, e n utilisant l'opé rate ur de ré s olution de porté e (::).

Exe rcice VII.1

___________________________________________________________________________

Enoncé

Soit une clas s e ve cte ur3d définie 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 coïncide nce ou la non-
coïncide 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 opérate urs (le pre m ie r
opé rande é tant fourni par l'argum e nt im plicte - th is - des fonctions m e m bre ). Voici la déclaration com plète
de notre clas s e , accom pagnée des définitions des opérate urs :

class vecteur3d
{ float x, y, z ;
public :
vecteur3d (float c1=0.0, float c2=0.0, float c3=0.0)
{ x = c1 ; y = c2 ; z = c3 ;
}
int operator == (vecteur3d) ;
int operator != (vecteur3d) ;
94 Exercices en langage C+ +

} ;

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 définition de !=, nous nous som m e s s e rvi de l'opé rate ur ==. En pratiq ue , on s e ra
souve nt am e né à ré é crire e ntiè re m e nt la définition d'un te lopé rate ur, pour de s im ples raisons d'efficacité
(d'ailleurs, pour les m ê m e s raisons, on place ra "e n ligne " les fonctions ope rator == e t ope rator !=).

b) Avec des fonctions am ies

Ilfaut donc pré voir de déclare r com m e am ie s , dans la clas s e ve cte ur3d, deux fonctions (ope rator == e t
ope rator !=), re ce vant deux argum e nts de type ve cte ur3d corre s pondant aux de ux opé randes des opé rate urs
Voici la nouve lle déclaration de notre clas s e , accom pagnée des définitions des opérate urs :

class vecteur3d
{ float x, y, z ;
public :
vecteur3d (float c1=0.0, float c2=0.0, float c3=0.0)
{ x = c1 ; y = c2 ; z = c3 ;
}
friend int operator == (vecteur3d, vecteur3d) ;
friend int operator != (vecteur3d, vecteur3d) ;
} ;
int operator == (vecteur3d v, vecteur3d w)
{ if ( (v.x == w.x) && (v.y == w.y) && (v.z == w.z) ) return 1 ;
else return 0 ;
}
int operator != (vecteur3d v, vecteur3d w)
{ return ! ( v == w ) ;
}

R e m arque:

Voici, à titre indicatif, un e xe m ple de program m e utilisant n'im porte laq ue lle des deux clas s e s ve cte ur3d q ue
nous ve nons de définir.

#include "vecteur3d.h"
#include <iostream.h>
main()
{ vecteur3d v1 (3,4,5), v2 (4,5,6), v3 (3,4,5) ;
cout << "v1==v2 : " << (v1==v2) << " v1!=v2 : " << (v1!=v2) << "\n" ;
cout << "v1==v3 : " << (v1==v3) << " v1!=v3 : " << (v1!=v3) << "\n" ;
}

Exe rcice VII.2

___________________________________________________________________________
VII. La surdéfinition d'opé rateurs 95

Enoncé

Soit la clas s e ve cte ur3d définie 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 définie ainsi :

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

Com pléte r la définition du constructe ur ("e n ligne "), puis définir l'opé rate ur [] pour q u'ilpe rm e tte d'accéder
à l'une des trois com posantes d'un ve cte ur, e t ce la, aussi bien au s e in d'une e xpre s s ion ( ... = v1[i]) q u'à
gauch e d'un opé rate ur d'affe ctation (v1[i] = ...) ;de plus, on ch e rch e ra à s e proté ge r contre d'éve ntue ls
ris q ues de déborde m e nt d'indice .

___________________________________________________________________________

Sol
ution

La définition du constructe ur ne pos e aucun problèm e . En ce q ui conce rne l'opé rate ur [], le C+ + ne pe rm e t
de le s urdé finir q ue s ous form e d'une fonction m e m bre (ce tte e xce ption e s t justifié e par le fait q ue l'obje t
conce rné ne doit pas ris q ue r d'ê tre s oum is à une conve rsion, lors q u'ilapparaî t à gauch e d'une affe ctation).
Elle possédera donc un s e ulargum e nt de type int e t e lle re nve rra un ré s ultat de type ve cte ur3d.

Pour que notre opérateur puisse ê tre util isé à gauch e d'une affectation, ilfaut absol um ent que l e
résul tat soit renvoyé par référence. Pour nous proté ge r d'un é ve ntue ldébordem e nt d'indice , nous avons
sim plem e nt pré vu q ue toute te ntative d'accè s à un é lém e nt e n de h ors des lim ite s conduirait à accéder au
pre m ie r é lém e nt.

Voici la déclaration de notre clas s e , accom pagné e d e la définition de la fonction ope rator [].

class vecteur3d
{ float v [3] ;
public :
vecteur3d (float c1=0.0, float c2=0.0, float c3=0.0)
{ v[0] = c1 ; v[1] = c2 ; v[2] = c3 ;
}
float & operator [] ( int) ;
} ;

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


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

R e m arque:
VII. La surdéfinition d'opé rateurs 97

A titre indicatif, voici un pe tit e xe m ple de program m e faisant appe là notre clas s e ve cte ur3d :

#include "vecteur3d.h"
#include <iostream.h>
main()
{ vecteur3d v1 (3,4,5) ;
int i ;
cout << "v1 = " ;
for (i=0 ; i<3 ; i++) cout << v1[i] << " " ;
for (i=0 ; i<3 ; i++) v1[i] = i ;
cout << "\nv1 = " ;
for (i=0 ; i<3 ; i++) cout << v1[i] << " " ;
}

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

Son im plém e ntation pré voyait de place r les diffé re nts é lém e nts dans un tableau alloué dynam iq ue m e nt ;
aussi l 'affectation e ntre obje ts de type s e t_int posait-e lle des problèm e s , puis q u'e lle aboutissait à des obje ts
diffé re nts com portant des pointe urs sur un m ê m e e m place m e nt dynam iq ue .

M odifie r la clas s e s e t_int pour q u'e lle ne pré s e nte plus de te lles lacune s . O n pré voira q ue tout obje t de type
s e t_int com porte s on propre e m place m e nt dynam iq ue , com m e on l'avait fait pour pe rm e ttre la transm ission
par valeur (ce ne s e rait pas le cas si l'on ch e rch ait à utiliser une te ch niq ue de "com pte ur de ré fé re nce "). De
plus, on s'arrange ra pour q ue l'affe ctation m ultiple s oit utilisable.

___________________________________________________________________________

Sol
ution

Nous som m e s e n pré s e nce d'un problèm e voisin de ce lui pos é par le constructe ur par re copie ;nous l'avions
ré s olu e n pré voyant ce q ue l'on appe lle une "copie profonde " de l'obje t conce rné (c'e s t-à -dire une copie ,
non s e ulem e nt de l'obje t lui-m ê m e , m ais de toute s s e s parties dynam iq ue s ). Que lques diffé re nce s
supplém e ntaire s s urgis s e nt né anm oins ;e n e ffe t, ici :

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

- libération 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
désigné par a,
- m ise en place des valeurs des m e m bres donnée de b.
D ans le cas où a e t b désignent 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 libéré. 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 définition de notre fonction ope rator = (e n ce q ui conce rne la déclaration de la
clas s e s e t_int, ilsuffirait d'y ajoute r s e t_int & ope rator = (s e t_int & ) :

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


// surdéfinition de l'affectation - les commentaires correspondent à b = a
{ if (this != &e) // on ne fait rien pour a = a
{ delete adval ; // libération partie dynamique de b
adval = new int [nmax = e.nmax] ; // allocation nouvel ensemble pour a
nelem = e.nelem ; // dans lequel on recopie
int i ; // entièrement 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 possédant des parties dynam iq ue s
ne s e ra valable q ue s i e lle e s t associé e à une s urdé finition com parable du constructe ur par re copie (pour
la clas s e s e t_int, ce lle proposée dans la solution de l'e xe rcice V.4 convie nt parfaite m e nt).
2) A priori, se ule la valeur du pre m ie r opé rande a vraim e nt besoin d'ê tre transm ise par réfé re nce (pour
q ue = puis s e le m odifie r!) ;ce tte condition e s t obligatoire m e nt re m plie puis q ue notre opé rate ur doit ê tre
surdéfini com m e fonction m e m bre . Toute fois, e n pratiq ue , on utilisera égalem e nt la transm ission par
ré fé re nce , à la fois pour le s e cond opé rande e t pour la valeur de re tour, de façon à ê tre plus e fficace (e n
te m ps). Note z d'ailleurs q ue s i l'opé rate ur = re nvoyait son ré s ultat par valeur, ily aurait alors appe ldu
constructe ur de re copie (la re m arq ue pré cédente s 'appliq ue rait alors à une s im ple affe ctation).

Exe rcice VII.5

___________________________________________________________________________

Enoncé

Considérer à nouve au la clas s e s e t_int cré é e d ans l'e xe rcice V.4 (e t sur laq ue lle e s t é galem e nt fondé
l'e xe rcice pré cédent) :
VII. La surdéfinition d'opé rateurs 99

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

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

- l'on puis s e ajoute r un é lém e nt à un e ns e m ble de type s e t_int par (e désignant un obje t de type s e t_int e t
n un e ntie r) : e < n ;
- e [n] pre nne la valeur 1 si n appartie nt à e e t la valeur 0 dans le cas contraire . O n s'arrange ra pour
q u'une instruction de la form e e [i] = ... soit rejeté e à l
a com pil
ation.
___________________________________________________________________________

Sol
ution

Ilnous faut donc surdéfinir l'opé rate ur binaire < , de m aniè re à ce q u'ilre çoive com m e opé rande s un obje t
de type s e t_int e t un e ntie r. Bie n q ue l'é noncé ne pré voie rie n, ile s t probable q ue l'on souh aite ra pouvoir
é crire des ch os e s te lles q ue (e é tant de type s e t_int, n e t p de s e ntie rs) :

e < n < p ;

Ce la signifie donc q ue notre opé rate ur de vra fournir com m e valeur de re tour l'e ns e m ble conce rné , aprè s
q u'on lui a ajouté l'é lém e nt voulu.

Nous pouvons indiffé re m m e nt utiliser ici une fonction m e m bre ou une fonction am ie . Nous ch oisirons la
pre m iè re possibilité . Par ailleurs, nous transm e ttrons les opé rande s e t la valeur de re tour par ré fé re nce (ici,
c'e s t possible car l'obje t corre s pondant n'e s t pas cré é au s e in de l'opé rate ur m ê m e , c'e s t-à -dire q u'iln'e s t
pas de clas s e autom atiq ue ) ;ainsi notre opé rate ur fonctionne ra m ê m e s i le constructe ur par re copie n'a pas
é té s urdé fini (e n pratiq ue , toute fois, ilfaudra le faire dè s q u'on souh aite ra pouvoir transm e ttre la valeur d'un
obje t de type s e t_int e n argum e nt).

En ce q ui conce rne l'opé rate ur [], ildoit ê tre s urdé fini com m e fonction m e m bre , com m e l'im pos e le C+ + ,
e t ce la bien q u'ici une affe ctation te lle e [i] = soit inte rdite (alors q ue c'e s t pré cis é m e nt pour l'autoris e r q ue
C+ + oblige d'en faire une fonction m e m bre !). Pour inte rdire de te lles affe ctations, la dém arch e la plus
sim ple consiste à faire e n sorte q ue le ré s ultat fourni par l'opé rate ur ne s oit pas une "lvalue ", e n la
transm ettant par val eur.

Voici q ue lle pourrait ê tre la définition de nos deux opérate urs (note z q ue nous utilisons [] pour dé finir < ) :

/* surdéfinition de < */
set_int & set_int::operator < (int nb)
{ // on examine si nb appartient déjà à l'ensemble
// en utilisant l'opérateur []
if ( ! (*this)[nb] && (nelem < nmax) ) adval [nelem++] = nb ;
return (*this) ;
}

/* surdéfinition de [] */
int set_int::operator [] (int nb) // attention résultat par valeur
100 Exercices en langage C+ +

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

Exe rcice VII.6

___________________________________________________________________________

Enoncé

Soit une clas s e ve cte ur3d définie 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 ;
}
// à compléter
} ;

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

- ilpe rm e tte d'accéder "norm alem e nt" à un é lém e nt d'un obje t non constant de type ve cte ur3d, e t ce la
aussi bien dans une expression qu'en opérande de gauch e d'une affe ctation,
- ilne pe rm e tte q ue la consultation (e t non la m odification) d'un obje t constant de type ve cte ur3d
(autre m e nt dit, si v e s t un te lobje t, une instruction de la form e v[i] = ... devra ê tre re je té e à la
com pilation).
___________________________________________________________________________

Sol
ution

R appe lons q ue lors q ue l'on dé finit des obje ts constants (q ualificatif const), iln'e s t pas possible, a priori, de
leur appliq ue r une fonction m e m bre publiq ue , sauf si ce tte derniè re a é té déclaré e ave c le q ualificatif const
(auq ue lcas, une te lle fonction pe ut indiffé re m m e nt ê tre utilisée ave c des obje ts constants ou non constants).
Ici, nous devons donc dé finir une fonction m e m bre constante de nom ope rator [].

Par ailleurs, pour q u'une affe ctation de la form e v[i] = ... soit inte rdite , ile s t né ce s s aire q ue notre opé rate ur
re nvoie s on ré s ultat par valeur (e t non par adre s s e com m e on a gé né ralem e nt l'h abitude de le faire ).

D ans ce s conditions, on voit q u'ile s t né ce s s aire de pré voir de ux fonctions m e m bre diffé re nte s , pour traite r
ch acun des deux cas : obje t constant ou obje t non constant. Le ch oix de la "bonne fonction" s e ra assuré par
le com pilate ur, e n fonction de la pré s e nce ou de l'abs e nce de l'attribut const pour l'obje t conce rné .

Voici la définition com plète de notre clas s e , accom pagné e d e la définition des deux fonctions ope rator [] :

class vecteur3d
{ float v [3] ;
public :
vecteur3d (float c1=0.0, float c2=0.0, float c3=0.0)
VII. La surdéfinition d'opé rateurs 101

{ 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 "débordement"
return v[i] ;
}

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


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

A titre indicatif, voici un pe tit program m e utilisant la clas s e ve cte ur3d ainsi définie , accom pagné du résultat
produit par son exécution :

#include "vecteur3d.h"
#include <iostream.h>
main()
{ int i ;
vecteur3d v1 (3,4,5) ;
const vecteur3d v2 (1,2,3) ;
cout << "V1 : " ;
for (i=0 ; i<3 ; i++) cout << v1[i] << " " ;
cout << "\nV2 : " ;
for (i=0 ; i<3 ; i++) cout << v2[i] << " " ;
for (i=0 ; i<3 ; i++) v1[i] = i ;
cout << "\nV1 : " ;
for (i=0 ; i<3 ; i++) cout << v1[i] << " " ;
// v2[1] = 3 ; est bien rejeté à la compilation
}
___________________________
V1 : 3 4 5
V2 : 1 2 3
V1 : 0 1 2

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 précisém e nt, on pré voira de déclare r de
te ls ve cte urs par une instruction de la form e :

vect t(exp) ;

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

O n dé finira, de façon approprié e , l'opé rate ur [] de m aniè re à ce q u'ilpe rm e tte d'accéder à des élém e nts
d'un obje t d'un type ve ct com m e on le fe rait ave c un tableau classique.

O n ne ch e rch e ra pas à ré s oudre les problèm e s pos é s é ve ntue llem e nt par l'affe ctation ou la transm ission par
valeur d'obje ts de type ve ct. En re vanch e , on s'arrange ra pour q u'aucun ris q ue de "débordem e nt" d'indice
n'e xiste .

___________________________________________________________________________

Sol
ution

Le s é lém e nts d'un obje t de type ve ct doive nt obligatoire m e nt ê tre rangé s e n m é m oire dynam iq ue .
L'e m place m e nt corre s pondant s e ra donc alloué par le constructe ur q ui e n re ce vra la taille e n argum e nt. Le
destructe ur de vra donc, nature llem e nt, libérer cet e m place m e nt. En ce q ui conce rne l'accè s à un é lém e nt, il
s e fe ra e n surdéfinissant l'opé rate ur [], com m e nous l'avons déjà fait au cours des précédents e xe rcice s ;
rappe lons q u'ilfaudra obligatoire m e nt le faire s ous form e d'une fonction m e m bre .

Pour nous proté ge r d'un é ve ntue ldébordem e nt d'indice , nous fe rons e n sorte q u'une te ntative d'accè s à un
é lém e nt situé e n de h ors du ve cte ur conduis e à accéder à l'é lém e nt de rang 0.

Voici ce q ue pourraie nt ê tre la déclaration e t la définition de notre clas s e :

/********** déclaration de la classe vect ********/


class vect
{ int nelem ; // nombre d'éléments
int * adr ; // adresse zone dynamique contenant les éléments
public :
vect (int) ; // constructeur
~vect () ; // destructeur
int & operator [] (int) ; // accès à un élément par son "indice"
} ;

/*********** définition 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 surdéfinition d'opé rateurs 103

Exe rcice VII.8

___________________________________________________________________________

Enoncé

En s'inspirant de l'e xe rcice pré cédent, on souh aite cré e r une clas s e int2d pe rm e ttant de re pré s e nte r de s
tableaux dynam iq ues d'entie rs à deux indice s , c'e s t-à -dire des tableaux dont les dim e nsions peuve nt ne pas
ê tre connue s lors de la com pilation. Plus précisém e nt, on pré voira de déclare r de te ls tableaux par une
déclaration de la form e :

int2d t(exp1, exp2) ;

dans laq ue lle e xp1 e t e xp2 désignent une e xpre s s ion q ue lconq ue (de type e ntie r).

a) D ire pourq uoi iln'e s t pas possible, ici, de s urdé finir ici l'opé rate ur [] pour accéder à des élém e nts d'un
te ltableau.

b) Surdé finir, de façon approprié e , l'opé rate ur () de m aniè re à ce q u'ilpe rm e tte d'accéder à des élém e nts
d'un obje t d'un type int2d com m e on le fe rait ave c un tableau classique. Là e ncore , on ne ch e rch e ra pas à
ré s oudre les problèm e s pos é s é ve ntue llem e nt par l'affe ctation ou la transm ission par valeur d'obje ts de type
int2d. En re vanch e , on s'arrange ra pour q u'iln'e xiste aucun ris q ue de débordem e nt d'indice .

___________________________________________________________________________

Sol
ution

a) L'opé rate ur [] ne pe ut ê tre s urdé fini q ue s ous form e binaire . Iln'e s t donc pas possible de l'e m ploye r sous
la form e t[i, j] pour accéder à un é lém e nt d'un tableau dynam iq ue . O n pourrait alors penser à l'utiliser sous
la form e h abitue lle t[i][j] . O r, ce tte é criture doit ê tre inte rpré té e com m e (t[i]) [j]), ce q ui signifie q u'on n'y
appliq ue plus le s e cond opé rate ur à un obje t de type int2d.

Com pte te nu de ce q ue , de surcroî t, [] doit ê tre défini com m e fonction m e m bre , l'é criture e n q ue s tion
dem ande rait de définir [] pour une clas s e int2d, e n s'arrange ant pour q u'ilfournis s e un ré s ultat ("ve cte ur
ligne ") lui-m ê m e de type clas s e , afin de pouvoir, dans ce nouve au type clas s e , surdéfinir à nouve au [] pour
q u'ilfournis s e un ré s ultat de type int. Iln'e s t donc pas possible d'obte nir le ré s ultat souh aité s ans définir
d'autre s clas s e s q ue int2d.

b) Com m e dans l'e xe rcice pré cédent, les é lém e nts d'un obje t de type int2d doive nt obligatoire m e nt ê tre
rangé s e n m é m oire dynam iq ue . L'e m place m e nt corre s pondant s e ra donc alloué par le constructe ur q ui e n
déduira la taille des deux dim e nsions reçue s e n argum e nts Le destructe ur libérera cet e m place m e nt.

Nous devons décide r de la m aniè re dont s e ront rangé s les diffé re nts é lém e nts e n m é m oire , à savoir "par
ligne " ou "par colonne " (e n toute rigue ur, ce tte te rm inologie fait ré fé re nce à la façon dont on a l'h abitude de
visualiser des tableaux à deux dim e nsions, ce que l'on nom m e une ligne corre s pondant e n fait à des élém e nts
ayant m ê m e valeur du pre m ie r indice ). Nous ch oisirons ici la pre m iè re s olution (c'e s t ce lle utilisée par C ou
C+ + pour les tableaux à deux dim e nsions). Ainsi, un é lém e nt re pé ré par les valeurs i et j des 2 indices sera
situé à l'adre s s e (adv désignant l'adresse de l'e m place m e nt dynam iq ue alloué au tableau) :

adv + i * ncol + j

En ce q ui conce rne l'accè s à un é lém e nt, il s e fe ra e n surdéfinissant l'opé rate ur (), d'une m aniè re
com parable à ce q ue nous avions fait pour [] ;là e ncore , C+ + im pos e q ue () soit défini com m e fonction
m e m bre .

Pour nous proté ge r d'un é ve ntue ldébordem e nt d'indice , nous fe rons e n sorte q u'une valeur incorre cte d'un
indice conduis e à "faire com m e s i" on lui avait attribué la valeur 0.
104 Exercices en langage C+ +

Voici ce q ue pourraie nt ê tre la déclaration e t la définition de notre clas s e :

/******** déclaration 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) ; // accès à un élément, par ses 2 "indices"
} ;
/*********** définition 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 à zéro
}

/*********** définition du destructeur ***********/


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

/********** définition de l'opérateur () *********/


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é à définir de ux opé rate urs () com m e nous l'avions fait dans l'e xe rcice
pré cédent pour l'opé rate ur [], à savoir : un pour de s obje ts non constants (ce lui dé fini ici), l'autre pour
des obje ts constants (ils e ra pré vu de m aniè re à ne pas pouvoir m odifie r l'obje t sur leq ue lilporte ).
VII. La surdéfinition d'opé rateurs 105

2) Gé né ralem e nt, par souci d'e fficacité , les opé rate urs te ls q ue () s e ront définis "e n ligne ".

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 ) contiguës
(souve nt de m ê m e am plitude ) e t e n com ptabilisant le nom bre de valeurs x(i) apparte nant à ch acune de ce s
tranch e s .

O n pré voira :

- un constructe ur de la form e h isto (float m in, float m ax, int ninte r), dont les argum e nts précisent les
borne s (m in e t m ax ) des valeurs à pre ndre e n com pte e t le nom bre de tranch e s (ninte r) supposées de
m ê m e am plitude ,
- un opé rate ur < < d é fini de m aniè re te lle q ue h < < x ajoute la valeur x à l'h istogram m e h , c'e s t-à -dire
q u'e lle incré m e nte de 1 le com pte ur re latif à la tranch e à laq ue lle appartie nt x. Le s valeurs sortant des
lim ite s (m in - m ax) ne s e ront pas com ptabilisées,
- un opé rate ur [] défini de m aniè re te lle q ue h [i] re pré s e nte le nom bre de valeurs ré pe rtoriées dans la
tranch e de rang i (la pre m iè re tranch e portant le num é ro 1 ;un num é ro incorre ct de tranch e conduira à
considérer celle de rang 1). O n s'arrange ra pour q u'une instruction de la form e h [i] = ... soit re je té e e n
com pilation.
O n ne ch e rch e ra pas ici à ré gler les problèm e s pos é s par l'affe ctation ou la transm ission par valeur d'obje ts
du type h isto.

___________________________________________________________________________

Sol
ution

Nous n'avons pas besoin de conserve r les diffé re nte s valeurs x(i), m ais s e ulem e nt les com pte urs re latifs à
ch aq ue tranch e . En re vanch e , ilfaut pré voir d'alloue r dynam iq ue m e nt (dans le constructe ur) l'e m place m e nt
né ce s s aire à ce s com pte urs, puis q ue le nom bre de tranch e s n'e s t pas connu lors de la com pilation de la clas s e
h isto. Ilfaudra nature llem e nt pré voir de libérer cet e m place m e nt dans le destructe ur de la clas s e . Le s
m e m bres donnée de h isto s e ront donc : les valeurs e xtrê m e s (m inim ale e t m axim ale), le nom bre de tranch e s
e t un pointe ur sur l'e m place m e nt conte nant les com pte urs.

L'opé rate ur < < pe ut ê tre s urdé fini, soit com m e fonction m e m bre , soit com m e fonction am ie ;nous
ch oisirons ici la pre m iè re s olution. En re vanch e , l'opé rate ur [] doit absolum e nt ê tre s urdé fini com m e
fonction m e m bre . Pour q u'ilne s oit pas possible d'écrire des affe ctations de la form e h [i] = ..., on fe ra e n
sorte q ue ce t opé rate ur fournis s e s on ré s ultat par valeur.

Voici ce q ue pourrait ê tre la déclaration de notre clas s e h isto :

/*** fichier histo.h : déclaration de la classe histo ***/


class histo
{ float min ; // borne inférieure
float max ; // borne supérieure
int nint ; // nombre de tranches utiles
float * adc ; // pointeur sur les compteurs associés à 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 systématique)
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 prévu de s valeurs par dé faut pour les argum e nts du constructe ur (ce lles -ci n'é taie nt
pas im pos é e s par l'é noncé ).

En ce q ui conce rne la définition des diffé re nts m e m bre s , ilfaut note r q u'ile s t indispensable q u'une te lle
clas s e s oit proté gé e contre toute utilisation incorre cte . Ce rte s , ce la pas s e par un contrôle de la valeur du
num é ro de tranch e fourni à l'opé rate ur [], ou par le re fus de prendre en com pte une valeur h ors lim ite s (q ui
fournirait un num é ro de tranch e conduisant à un e m place m e nt situé e n de h ors de ce lui alloué par le
constructe ur).

M ais, de surcroî t, nous devons nous assurer que les valeurs des argum e nts fournis au constructe ur ne
ris q ue nt pas de m e ttre e n caus e le fonctionne m e nt ulté rie ur des diffé re nte s fonctions m e m bre . En particulie r,
ile s t bon de vé rifie r q ue le nom bre de tranch e s n'e s t pas négatif (notam m e nt une valeur nulle conduirait
dans < < à une division par zé ro) e t q ue les valeurs du m inim um e t du m axim um sont conve nablem e nt
ordonné e s e t diffé re nte s l'une de l'autre (dans ce dernie r cas, on aboutirait e ncore à une division par zé ro).
Ici, nous avons prévu de ré gler ce problèm e e n attribuant le cas é ch é ant des valeurs par dé faut arbitraire s
(m ax = m in + 1, nint = 1).

Voici ce q ue pourrait ê tre la définition des diffé re nte s fonctions m e m bre de notre clas s e h isto :

/************** définition de la classe histo **********/


#include "histo.h"
#include <iostream.h>
/********************* constructeur ********************/
histo::histo (float mini, float maxi, int ninter)
{ // protection contre arguments erronés
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 ;
}

/********************* opérateur << ********************/


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 surdéfinition d'opé rateurs 107

/********************* opérateur [] ********************/


int histo::operator [] (int n)
{ if ( (n<1) || (n>nint) ) n=1 ; // protection "débordement"
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 résultat fourni par son
e xé cution :

#include "histo.h"
#include <iostream.h>
main()
{ const int nint = 4 ;
int i ;
histo h (0., 5., nint) ; // 4 tranches entre 0 et 5
h << 1.5 << 2.4 << 3.8 << 3.0 << 2.0 << 3.5 << 2.8 << 4.6 ;
h << 12.0 << -3.5 ;
for (i=0 ; i<10 ; i++) h << i/2.0 ;
cout << "valeurs des tranches \n" ;
for (i=1 ; i<=nint ; i++)
cout << "numéro " << i << " : " << h[i] << "\n" ;
}
_____________________

valeurs des tranches


numéro 1 : 3
numéro 2 : 5
numéro 3 : 6
numéro 4 : 4

Exe rcice VII.10

___________________________________________________________________________

Enoncé

R é aliser une clas s e nom m é e stack _int pe rm e ttant de gérer une pile d'entie rs. Ces dernie rs s e ront cons e rvé s
dans un em place m e nt alloué dynam iq ue m e nt ;sa dim e nsion sera déte rm iné e par l'argum e nt fourni à son
constructe ur (on lui pré voira une valeur par dé faut de 20). Ce tte classe devra com porte r les opé rate urs
suivants (nous supposons que p est un obje t de type stack _int e t n un e ntie r) :

< < , te lq ue p<<n ajoute l'e ntie r n à la pile p (si la pile e s t pleine , rie n ne s e pas s e ),
> > , te lq ue p> > n place dans n la valeur du h aut de la pile, e n la supprim ant de la pile (si la pile e s t
vide, la valeur de n ne s e ra pas m odifié e ),
+ + , te lq ue p+ + vale 1 si la pile e s t pleine e t 0 dans le cas contraire ,
--, te lq ue p-- vale 1 si la pile e s t vide et 0 dans le cas contraire .
O n pré voira q ue les opé rate urs < < e t > > pourront ê tre utilisés sous les form e s s uivante s (n1, n2 e t n3
é tant des entie rs) :

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 donnée : la taille de l'e m place m e nt ré s e rvé pour la pile
(nm ax), le nom bre d'élém e nts placé s à un m om e nt donné s ur la pile (ne le m ) e t un pointe ur sur
l'e m place m e nt q ui s e ra alloué par le constructe ur pour y range r les é lém e nts de la pile (adv). Note z q u'il
n'e s t pas néce s s aire de pré voir une donnée supplém e ntaire pour un é ve ntue l"pointe ur" de pile, dans la
m e s ure où c'e s t le nom bre d'élém e nts ne le m q ui joue ce rôle ici.

Le s opé rate urs re q uis peuve nt indifé re m m e nt ê tre définis com m e fonctions m e m bre ou com m e fonctions
am ie s . Nous ch oisirons ici la pre m iè re s olution. Pour q ue les opé rate urs < < e t > > puis s e nt ê tre utilisés à
plusieurs reprises dans une m ê m e e xpre s s ion, ile s t né ce s s aire q ue , par e xe m ple :

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 "problèm e s h abitue ls". Nous devons donc surdéfinir le constructe ur par re copie ;
ici, nous prévoirons une duplication com plète de l'obje t (une autre dém arch e , plus com pliq ué e , consiste rait à
e m ploye r un "com pte ur de ré fé re nce ", ce q ui pe rm e ttrait de ne plus dupliq ue r la partie dynam iq ue ).

Pour satisfaire à la contrainte im pos é e par l'é noncé s ur l'affe ctation, nous allons surdé finir l'opé rate ur
d'affe ctation. Com m e ce dernie r doit s e conte nte r d'affich e r un m e s s age d'erreur et d'inte rrom pre
l'e xé cution, iln'e s t pas néce s s aire q u'ilre nvoie une q ue lconq ue valeur (d'où la déclaration void).

Voici ce q ue pourrait ê tre la déclaration 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) ; // opérateur d'empilage
stack_int & operator >> (int &) ; // opérateur de dépilage (attention int &)
int operator ++ () ; // opérateur de test pile pleine
int operator -- () ; // opérateur de test pile vide
VII. La surdéfinition d'opé rateurs 109

} ;

Note z q ue l'opé rate ur > > doit absolum e nt re ce voir son deuxiè m e opé rande par ré fé re nce , puis q u'ildoit
pouvoir e n m odifie r la valeur.

Voici la définition 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 - arrêt exécution ***\n" ;
exit (1) ;
}
stack_int & stack_int::operator << (int n)
{ if (nelem < nmax) adv[nelem++] = n ;
return (*this) ;
}
stack_int & stack_int::operator >> (int & n)
{ if (nelem > 0) n = adv[--nelem] ;
return (*this) ;
}
int stack_int::operator ++ ()
{ return (nelem == nmax) ;
}
int stack_int::operator -- ()
{ return (nelem == 0) ;
}

A titre indicatif, voici un pe tit program m e d'utilisation de notre clas s e , accom pagné d'un exem ple
d'exécution :

/************ 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 dépile 2 valeurs
cout << "haut de la pile au retour de fct : " << n << " " << p << "\n" ;
stack_int pileb (25) ;
pileb = pile ; // tentative d'affectation
}

void fct (stack_int pl)


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

pleine : 0 vide : 1
haut de la pile reçue par fct : 4 3
haut de la pile au retour de fct : 4 3
*** Tentative d'affectation entre piles - arrêt exécution ***

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 à
surdéfinir ces deux opé rate urs pour la clas s e point. Le nom bre de points totale t le nom bre de points
dynam iq ue s s e ront cons e rvés dans des m e m bre s s tatiq ue s (n'e xistant q u'une s e ule fois pour l'e ns e m ble des
obje ts du type point). Quant aux fonctions m e m bre fournissant les inform ations voulue s , ile s t pré fé rable
d'en faire des fonctions m e m bre s tatiq ue s (déclaré e s , e lles aussi, ave c l'attribut static), ce q ui pe rm e ttra de
les e m ploye r plus facilem e nt q ue s i on e n avait fait des fonctions m e m bre ordinaire s (puis q u'on pourra les
appe ler sans avoir à les faire porte r sur un obje t particulie r).

Voici ce q ue pourrait ê tre notre clas s e point (déclaration e t définition) :


VII. La surdéfinition 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 surdéfini
{ nptd++ ;
return ::new char[sz] ;
}
void operator delete (void * dp)
{ nptd-- ;
::delete (dp) ;
}
static int npt_tot ()
{ return npt ;
}
static int npt_dyn ()
{ return nptd ;
}
} ;

Note z q ue , dans la surdéfinition de ne w e t de lete , nous avons fait appe laux opé rate urs prédéfinis (par
e m ploi de ::) pour ce q ui conce rne la ge s tion de la m é m oire .

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 définir de s conve rsions d'un type clas s e ve rs un autre type clas s e ou un type de bas e .
O n parle de conve rsions définie s par l'utilisate ur (e n abré gé : C.D .U.). Ce s conve rsions peuve nt alors ê tre
é ve ntue llem e nt m ises en oeuvre , de façon "autom atiq ue " par le com pilate ur, afin de donner une signification
à un appe lde fonction ou à un opé rate ur (alors q ue , sans conve rsion, l'appe lou l'opé rate ur s e rait illégal).
O n re trouve ainsi des possibilité s com parables à ce lles q ui nous sont offe rte s par le C e n m atiè re de
conve rsions im plicite s .

D e ux sortes de fonctions (obligatoire m e nt des fonctions m e m bre ) pe rm e tte nt de définir de s C.D .U. :

- les constructe urs à un argum e nt (q ue lq ue s oit le type de ce t argum e nt) : un te lconstructe ur ré alise une
conve rsion du type de ce t argum e nt dans le type de sa clas s e ,
- les opé rate urs de "cast" : dans une clas s e A , on dé finira un opé rate ur de conve rsion d'un type x
(q ue lconq ue , c'e s t-à -dire aussi bien un type de bas e q u'un autre type clas s e ) e n introduisant la fonction
m e m bre de prototype :
operator x () ;

note z q ue le type de la valeur de re tour (obligatoire m e nt défini par son nom ) ne doit pas figure r dans
l'e n-tê te ou le prototype d'une te lle fonction.
Le s rè gles d'utilisation de s C.D .U. re joigne nt ce lles conce rnant le ch oix d'une fonction surdéfinie :

- 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 biguïté , 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 déclarations :

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 définir une fonction m e m bre , de nom ope rator int, sans argum e nt e t re nvoyant la valeur de
l'abscisse du point l'ayant appe lé. R appe lons q ue le type de la valeur de re tour (q ue C+ + déduit du nom de
la fonction - ici int) ne doit pas figure r dans l'e n-tê te ou le prototype . Voici ce q ue pourraie nt ê tre la
déclaration e t la définition de ce tte fonction, ici ré unie s e n une s e ule déclaration d'une fonction "e n ligne " :

operator int ()
{ return x ;
}

b)

L'instruction 1 e s t traduite par le com pilate ur e n une conve rsion de p en int (par appe lde ope rator int),
suivie d'une affe ctation du ré s ultat à n. Note z bie n q u'iln'y a pas d'appe ld'un q ue lconq ue opé rate ur
d'affe ction de la clas s e point, ni d'un constructe ur par re copie (car le s e ulargum e nt transm is à la fonction
ope rator int e s t l'argum e nt im plicite th is).

L'instruction 2 e s t traduite par le com pilate ur e n une conve rsion de p en int (par appe lde ope rator int),
suivie d'un appe lde la fonction fct, à laq ue lle on transm e t le ré s ultat de ce tte conve rsion. Note z q u'iln'y
pas, là non plus, d'appe ld'un constructe ur par re copie , ni pour fct (puis q u'e lle re çoit un argum e nt d'un type
de bas e ), ni pour ope rator int (pour la m ê m e raison q ue pré cédem m e nt).

Exe rcice VIII.2


VIII. Les conversions de type définies 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 surdéfini corre s pondant aux type s point e t int (dans ce t ordre ). Ici, iln'e n trouve pas. Ilva
alors ch e rch e r à m e ttre e n place des conve rsions perm e ttant d'aboutir à une opé ration e xistante ;ici,
l'opé rate ur de cast (ope rator int) lui pe rm e t de se ram e ne r à une addition d'e ntie rs (par conve rsion de p en
int), e t c'e s t la s e ule possibilité . Le ré s ultat e s t alors, bie n sûr, de type int e t ile s t affe cté à n1 sans
conve rsion.

Le m ê m e raisonne m e nt s'appliq ue à l'instruction 2 ;l'é valuation de a+ b se fait alors par conve rsion de a et
de b en int (s e ule possibilité de donner une signification à l'opé rate ur + ). Le ré s ultat e s t de type int e t ile s t
affe cté s ans conve rsion à n2.

Le s e xpre s s ions figurant à droite des affe ctations des instructions 3 et 4 sont é valué e s e xacte m e nt suivant les
m ê m e s rè gles q ue les e xpre s s ions des instructions 1 et 2. Ce n'e s t q u'au m om e nt de l'affe ctation du ré s ultat
(de type int) à la variable m e ntioné e q ue l'on opè re une conve rsion int --> double (analogue à ce lles q ui
sont m ises en place e n langage C).

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 définies par l'utilisateur 125

Note z bie n q u'iln'e s t pas q ue s tion pour le com pilate ur de pré voir la conve rsion en int de la valeur 1.75 (de
façon à s e ram e ne r à l'addition de deux int, aprè s conve rsion de a en int) car ils'agit là d'une "conve rsion
dégradante " q ui n'e s t jam ais m ise en oeuvre de m aniè re im plicite dans un calculd'expression. Iln'y a donc
pas d'autre ch oix possible (note z q ue s 'ily en avait e ffe ctive m e nt un autre , ilne s 'agirait pas pour autant
d'une s ituation d'am biguïté dans la m e s ure où le com pilate ur appliq ue rait alors les rè gles h abitue lles de
ch oix d'une fonction surdéfinie ).

L'instruction 2 corre s pond à un raisonne m e nt sim ilaire ave c ce tte s e ule diffé re nce q ue le ré s ultat de
l'addition (de type double) pe ut ê tre affe cté à z1 sans conve rsion.

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é cédent e xe rcice , nous avions introduit, e n plus de
l'opé rate ur ope rator int, un autre opé rate ur de cast ope rator double.

___________________________________________________________________________

Sol
ution

D ans ce cas, les instructions 1 et 2 auraie nt conduit à une s ituation d'am biguïté . En e ffe t, le com pilate ur
aurait disposé de deux ch aî nes de conve rsions perm e ttant de conve rtir un point e n double : soit point -->
double soit point --> int suivie de int -> double. En re vanch e , les instructions 3 et 4 auraie nt toujours é té
acce pté e s .

Exe rcice VIII.5

___________________________________________________________________________

Enoncé

Considérer 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 déclarations :

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é cédem m e nt par
appe ldu constructe ur à un argum e nt) ;ily aura e nsuite cré ation d'une copie , par appe ldu constructe ur par
re copie de la clas s e com plexe , copie q ui s e ra transm ise à la fonction fct.

Le s instructions 3 et 4 joue nt le m ê m e rôle q ue les deux pré cédente s , ave c ce tte s e ule diffé re nce q u'e lles
font inte rve nir de s conve rsions int --> com plexe obte nue s par une conve rsion int --> double suivie d'une
conve rsion double --> com plexe .

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 << "Coordonnées : " << x << " " << y << "\n" ;
}
} ;
point operator+ (point a, point b)
VIII. Les conversions de type définies par l'utilisateur 127

{ point r ;
r.x = a.x + b.x ; r.y = a.y + b.y ;
return r ;
}
main()
{
point a, b(2,4) ;
a = b + 6 ; // affectation 1
a.affiche() ;
a = 6 + b ; // affectation 2
b.affiche() ;
}

Que se pas s e rait-ilsi l'opé rate ur + avait é té s urdé fini com m e une fonction m e m bre e t non plus com m e une
fonction am ie ?

___________________________________________________________________________

Sol
ution

L'é valuation de l'e xpre s s ion b+ 6 de la pre m iè re affe ctation s e fait e n utilisant l'opé rate ur + surdéfini pour
la clas s e point, aprè s avoir conve rti l'e ntie r 6 e n un point (par appe ldu constructe ur à un argum e nt). Le
ré s ultat, de type point, e s t alors affe cté à a.

L'instruction d'affe ctation 2 se déroule de façon sim ilaire (conve rsion de l'e ntie r 6 e n un point).

En re vanch e , si l'opé rate ur + avait é té s urdé fini par une fonction m e m bre , la s e conde instruction
d'affe ctation aurait é té re je té e à la com pilation ;e n e ffe t, e lle aurait é té inte rpré té e com m e :

6.operator + (b)

O n voit ici q ue s e ule la fonction am ie pe rm e t de traite r de façon ide ntiq ue les deux opé randes d'un opérate ur
binaire , notam m e nt e n ce q ui conce rne les possibilités de conve rsions im plicite s .

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 déclarations :

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 définir, 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 surdéfini 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 réaliser 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 précédents. Si B n'a pas surdé fini d'opé rate ur d'affe ctation de la
form e A & ope rator = (B), l'instruction 2 conduira à une e rre ur de com pilation puis q u'iln'e xiste ra
aucune possibilité de conve rtir un obje t de type A e n un obje t de type B. En re vanch e , si l'opé rate ur
d'affe ctation e n q ue s tion e xiste , ils e ra tout sim plem e nt utilisé.

Instruction 3 :

Elle ne pos e aucun problèm 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 surdéfini.

Instruction 5 :
VIII. Les conversions de type définies par l'utilisateur 129

L'e xpre s s ion a1 + a2 ne pos e pas de problèm e puis q u'e lle e s t é valué e à l'aide de l'opé rate ur + de la
clas s e A ;e lle fournit un ré s ultat de type A . En re vanch e , pour l'affe ctation du ré s ultat à b3, ilfaut à
nouve au distingue r les 2 cas déjà é voq ué s . Si B a surdéfini un opé rate ur d'affe ctation de la form e A &
ope rator = (B), ils e ra utilisé ;si un te lopé rate ur n'a pas é té s urdé fini, l'instruction s e ra re je té e par le
com pilate ur (puis q u'iln'e xiste ra alors aucune possibilité de conve rsion de B e n A).

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

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


{ // définition des membres supplémentaires (données ou fonctions)
// ou redéfinition de membres existants dans A (données ou fonctions)
} ;

Ave c public A, on parle de "dérivation publiq ue " ;ave c private A, on parle de "dérivation privé e " ;ave c
prote cte d A, on parle de "dérivation proté gé e ".

M odal
ité s d'accè s à l
a cl
as s e de bas e

Le s m e m bre s privés d'une classe de bas e ne s ont jam ais acce s s ibles aux fonctions m e m bre de sa clas s e
dérivé e .

O utre les "statuts" public ou privé (pré s e ntés dans le ch apitre III), ile xiste un statut "proté gé ". Un m e m bre
proté gé s e com porte com m e un m e m bre privé pour un utilisate ur q ue lconq ue de la clas s e ou de la clas s e
dérivé e , m ais com m e un m e m bre public pour la classe dérivé e .

D 'autre part, ile xiste trois sortes de dérivation :

- publ ique : les m e m bres de la classe de bas e cons e rve nt leur statut dans la classe dérivé e ;c'e s t la
situation la plus usuelle,
- privé e : tous les m e m bres de la classe de base devie nne nt privés dans la classe dérivé e ,
- proté gé e (depuis la ve rsion 3) : les m e m bre s publics de la classe de base devie nne nt m e m bre s proté gé s
de la classe dérivé e ;les autre s m e m bre s cons e rve nt leur statut.
Lors q u'un m e m bre (donné e ou fonction) e s t redéfini dans une classe dérivé e , ilre s te toujours possible (soit
dans les fonctions m e m bre de ce tte clas s e , soit pour un clie nt de ce tte clas s e ) d'accéder aux m e m bres de
m ê m e nom de la classe de bas e , m oye nnant l'utilisation de l'opé rate ur de ré s olution de porté e (::), sous
ré s e rve , bie n sûr, q u'un te laccè s soit autoris é .
134 Exercices en langage C+ +

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

Soit B une classe dérivée d'une classe de bas e A . Nature llem e nt, dè s lors q ue B possè de au m oins un
constructe ur, la cré ation d'un obje t de type B im pliq ue obligatoire m e nt l'appe ld'un constructe ur de B. M ais,
de plus, ce constructe ur de B doit pré voir de s argum e nts à destination d'un constructe ur de A (une e xce ption
a lie u, soit si A n'a pas de constructe ur, soit si A possè de un constructe ur sans argum e nt). Ce s argum e nts
sont pré cis é s , dans la définition du constructe ur de B, com m e dans ce t e xe m ple :

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 dérivé e B ne possè de pas de constructe ur par
re copie , ily aura appe ldu constructe ur par re copie par dé faut de B, leq ue lprocédera ainsi :

- appe ldu constructe ur par re copie de A (soit ce lui q ui y a é té défini, soit le constructe ur par re copie par
défaut),
- initialisation de s m e m bres donnée de B q ui ne s ont pas h é rités de A.
En re vanch e , un problèm e s e pos e lors q ue la classe dérivé e d é finit e xplicite m e nt un constructe ur par
re copie . En e ffe t, dans ce cas, ilfaut te nir com pte de ce q ue l'appe lde ce constructe ur par re copie
e ntraî
ne ra l'appe l:

- du constructe ur de la classe de bas e m e ntionné dans son e n-tê te , com m e dans ce t e xe m ple (ils'agit ici
d'un constructe ur par re copie de la classe de bas e , m ais ilpourrait s'agir de n'im porte q ue lautre
constructe ur) :
B (B & b) : A(b) ; // appel du constructeur par recopie de A
// auquel sera transmise la partie de B héritée de A
// (possible uniquement grâce aux règles de compatibilité
// entre classe de base et classe dérivée)

- d'un constructe ur sans argum e nt, si aucun constructe ur de la classe de bas e n'e s t m e ntionné dans l'e n-
tê te ;dans ce cas, ile s t né ce s s aire q ue la classe de base dispose d'un te lconstructe ur sans argum e nt,
faute d e q uoi, on obtie ndra une e rre ur de com pilation.

Cons é q ue nce s de l
'h é ritage

Considérons la situation suivante , dans laq ue lle la clas s e A possè de une fonction m e m bre f (dont nous ne
pré cisons pas les argum e nts) fournissant un ré s ultat de type t (q ue lconq ue : type de bas e ou type défini par
l'utilisate ur, é ve ntue llem e nt type clas s e ) :

class A class B : public A


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

A a ; // a est du type A
B b ; // b est du type B, dérivé de A
IX. La tech nique de l'h é ritage 135

Nature llem e nt, un appe lte lq ue a.f(...) a un s e ns e t ilfournit un ré s ultat de type t. Le fait q ue B h é rite
publiq ue m e nt de A perm e t alors de donner un sens à un appe lte lq ue :

b.f (...)

La fonction f agira sur b, com m e s 'ilé tait de type A . Le résul tat fourni par f sera cependant toujours de
type t, m ê m e , notam m e nt, lors q ue le type t e s t pré cis é m e nt le type A (le ré s ultat de f pourra toute fois ê tre
soum is à d'éve ntue lles conve rsions dans le cas où ile s t affe cté à une lvalue ).

Cas particul
ie r de l
'opé rate ur d'affe ctation

Considérons une clas s e B dérivant d'une clas s e A .

Si la classe dérivé e B n'a pas surdé fini l'opé rate ur d'affe ctation, l'affe ctation de deux obje ts de type B s e
déroule m e m bre à m e m bre , e n considérant q ue la "partie h é rité e d e A " constitue un m e m bre . Ainsi, les
m e m bre s propre s à B sont traité s par l'affe ctation pré vue pour leur type (par dé faut ou surdéfinie , suivant le
cas). La partie h é rité e d e A e s t traité e par l'affe ctation pré vue dans la clas s e A .

Si la classe dérivé e B a surdéfini l'opé rate ur =, l'affe ctation de deux obje ts de type B fe ra né ce s s aire m e nt
appe là l'opé rate ur = défini dans B. Ce lui de A ne s e ra pas appe lé, m ê m e s 'ila é té s urdé fini. Ilfaudra
donc que l 'opérateur = de B prenne en ch arge tout ce qui concerne l 'affectation d'objets de type B, y
com pris pour ce q ui e s t des m e m bre s h é rités de A.

Com patibil
ité e ntre obje ts d'une cl
as s e de bas e e t obje ts d'une cl
as s e dé rivé e

Considérons :

class A class B : public A


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

A a ; // a est du type A
B b ; // b est du type B, dérivé de A
A * ada ; // ada est un pointeur sur des objets de type A
B * adb ; // adb est un pointeur sur des objets de type B

Ile xiste deux conve rsions im plicite s :

- d'un obje t d'un type dérivé dans un obje t d'un type de bas e . Ainsi l'affe ctation a=b e s t légale : e lle
re vie nt à conve rtir b dans le type A (c'e s t-à -dire , e n fait, à ne considérer de b que ce qui est du type A ) e t
à affe cte r ce ré s ultat à a (ave c appe l, soit de l'opé rate ur d'affe ctation de A s i ce lui-ci a é té s urdé fini, soit
de l'opé rate ur d'affe ctation par dé faut de A). L'affe ctation inve rs e b=a e s t, q uant à e lle, illégale.
- d'un pointe ur sur une classe dérivé e e n un pointe ur sur une classe de bas e . Ainsi l'affe ctation ada=adb
e s t légale, tandis q ue adb=ada e s t illégale (e lle pe ut ce pe ndant ê tre forcé e par e m ploi de l'opé rate ur de
"cast" : adb = (B*) ada).

Exe rcice IX.1

___________________________________________________________________________

Enoncé

O n dispose d'un fich ie r nom m é point.h conte nant la déclaration suivante de la clas s e point :
136 Exercices en langage C+ +

class point
{ float x, y ;
public :
void initialise (float abs=0.0, float ord=0.0)
{ x = abs ; y = ord ;
}
void affiche ()
{ cout << "Point de coordonnées : " << x << " " << y << "\n" ;
}
float abs () { return x ; }
float ord () { return y ; }
} ;

a) Cré e r une clas s e pointa, dérivé e d e point com portant sim plem e nt une nouve lle fonction m e m bre nom m é e
rh o, fournissant la valeur du rayon ve cte ur (pre m iè re coordonné e polaire ) d'un point.

b) M ê m e q ue s tion, e n supposant q ue les m e m bre s x e t y ont é té déclaré s proté gé s (prote cte d) dans point, e t
non plus privé s .

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 prévoir, dans la déclaration de point, une nouve lle fonction m e m bre de prototype :

float rho () ;

Toute fois, com m e les m e m bre s x e t y sont privé s , ils re s te nt privé s pour les fonctions m e m bre de sa clas s e
dérivé e pointb ;ce q ui signifie q u'au s e in de la définition de rh o, ilfaut faire appe laux "fonctions d'accè s"
de point q ue s ont abs e t ord. Voici ce q ue pourrait ê tre notre clas s e pointb (ici, nous avons fourni rh o sous
form e d'une fonction "e n ligne ") :

#include "point.h" // pour la déclaration 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é définie , la clas s e point n'a pas donné naissance à un fich ie r obje t (puis q ue
toute s s e s fonctions m e m bre s ont "e n ligne "). Ile n va de m ê m e ici pour pointb. Aussi, pour utiliser pointb
au s e in d'un program m e , ilsuffira d'inclure les fich ie rs conte nant les définitions de point e t de pointb.
Nature llem e nt, dans la pratiq ue , ile n ira rare m e nt ainsi ;e n gé né ral ;on de vra fournir non s e ulem e nt les
déclarations de la classe de bas e e t de sa classe dérivé e , m ais é galem e nt les fich ie rs obje t corre s pondant à
leurs com pilations re s pe ctive s .

b) La définition pré cédente re s te valable m ais, né anm oins, com m e les m e m bre s x e t y de point ont é té
déclaré s proté gé s , ils sont acce s s ibles aux fonctions m e m bre de sa classe dérivé e ;aussi est-ilpossible, dans
la définition de rh o, de les utiliser "directe m e nt". Voici ce q ue pourrait deve nir notre fonction rh o (toujours
ici "e n ligne ") :
IX. La tech nique de l'h é ritage 137

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


{ return sqrt (x * x + y * y) ; // déclarés "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é déclaré 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
} // déclarés "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 défini de m e m bres donnée supplém e ntaire s , de sorte q u'iln'y a rie n de plus à faire pour
initialiser un obje t de type pointb q ue pour initialiser un obje t de type point.

Exe rcice IX.2

___________________________________________________________________________

Enoncé

O n dispose d'un fich ie r point.h conte nant la déclaration suivante de la clas s e point :

#include <iostream.h>
class point
{ float x, y ;
public :
point (float abs=0.0, float ord=0.0)
{ x = abs ; y = ord ;
}
void affiche ()
{ cout << "Coordonnées : " << x << " " << y << "\n" ;
}

void deplace (float dx, float dy)


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

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

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

- les fonctions m e m bre s uivante s :


*affich e (redéfinie ), 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 définir la couleur d'un obje t de type pointcol,
*un constructe ur pe rm e ttant de définir la couleur e t les coordonné e s (on ne le définira pas "e n ligne ".
b) Que fe ra alors précisém e nt ce tte instruction :

pointcol (2.5, 3.25, 5) ;

___________________________________________________________________________

Sol
ution

a) La fonction colore ne pos e aucun problèm e particulie r puis q u'e lle agit uniq ue m e nt sur un m e m bre donnée
propre à pointcool. En ce q ui conce rne affich e , ile s t né ce s s aire q u'e lle puis s e affich e r les valeurs des
m e m bre s x e t y, h é rités de point. Com m e ce s m e m bre s s ont privé s (e t non proté gé s ), iln'e s t pas possible
q ue la nouve lle m é th ode affich e de pointcoly accè de directe m e nt. Elle doit donc obligatoire m e nt faire appe l
à la m é th ode affich e du type point ;ilsuffit, pour ce la, d'utiliser l'opé rate ur de ré s olution de porté e . Enfin,
le constructe ur de pointcoldoit re transm e ttre au constructe ur de point les coordonné e s q u'ilaura re çue s par
ses deux pre m ie rs argum e nts .

Voici ce q ue pourrait ê tre notre clas s e pointcol(ici, toute s les fonctions m e m bre , sauf le constructe ur, sont
"e n ligne ") :

/****** fichier pointcol.h :déclaration 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 coordonnées
cout << " couleur : " << cl ; // mais elle a accès à la couleur
}
} ;

/****** définition 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 déclaration.

b) La déclaration pointcol (2.5, 3.25, 5) e ntraî ne la cré ation d'un e m place m e nt pour un obje t de type
pointcol, leq ue le s t initialisé par appel, succe s s ive m e nt :

- du constructe ur de point, q ui re çoit e n argum e nt les valeurs 2.5 e t 3.25 (com m e pré vu dans l'e n-tê te du
constructe ur de pointcol),
IX. La tech nique de l'h é ritage 139

- du constructe ur de pointcol.

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é cédent.
Cré e r une clas s e pointcol possédant les m ê m e s caracté ristiq ue s q ue ci-de s s us, m ais sans faire appe l à
l'h é ritage . Que lles diffé re nce s apparaî
tront e ntre ce tte clas s e pointcole t ce lle de l'e xe rcice pré cédent, au
nive au de s possibilités d'utilisation ?

___________________________________________________________________________

Sol
ution

La s e ule dém arch e possible consiste à cré e r une clas s e pointcoldans laq ue lle un de s m e m bres donnée est lui-
m ê m e de type point. Sa déclaration e t sa définition s e pré s e nte raie nt alors ainsi :

/***** fichier pointcol.h : déclaration 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 coordonnées
}
} ;

/****** définition 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é cédent.
Né anm oins, l'utilisate ur de ce tte nouve lle clas s e ne pe ut plus faire directe m e nt appe laux fonctions m e m bre
h é rité e s d e point. Ainsi, pour appliq ue r la m é th ode de place à un obje t a de type point, ildevrait absolum e nt
é crire : a.p.de place (...) ;or, ce la n'e s t pas autoris é ici, com pte te nu de ce q ue p e s t un m e m bre privé de
pointcol.
140 Exercices en langage C+ +

Exe rcice IX.4

___________________________________________________________________________

Enoncé

Soit une clas s e point définie ainsi (nous ne fournissons pas la définition de s on constructe ur) :

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

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


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

Soit la clas s e pointcol, dérivé 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é défini au s e in de point, sous form e
d'une fonction m e m bre .

___________________________________________________________________________

Sol
ution

a) Le s 5 instructions proposées sont corre cte s . D'une m aniè re gé né rale, x == y e s t inte rpré té e com m e
ope rator == (x, y). Si x e t y sont de type point, aucun problèm e ne s e pose bien sûr. Si l'un de s opé rande s
(ou les deux) e s t de type pointcol, ils e ra (ils s e ront) conve rti im plicite m e nt dans le type point. Si l'un de s
opé rande s e s t de type int, ils e ra conve rti im plicite m e nt dans le type point (par utilisation du constructe ur à
un argum e nt de point).

En ce q ui conce rne la signification de la com paraison, on voit q u'e lle re vie nt à ne considérer d'un obje t de
type pointcol, q ue les coordonné e s . Pour un e ntie r, e lle re vie nt à le considérer com m e un point ayant ce t
e ntie r pour abscis s e e t une ordonné e nulle.
IX. La tech nique de l'h é ritage 141

b) Ce tte fois, x == y e s t inte rpré té e com m e x.ope rator == (y). Si x e s t de type point e t y d'un type
pouvant s e ram e ne r au type point (c'e s t-à -dire s oit du type pointcolq ui s e ra conve rti im plicite m e nt e n un
type de bas e point, soit d'un type e ntie r q ui s e ra conve rti im plicite m e nt e n un type point par l'inte rm édiaire
du constructe ur), aucun problèm e ne s e pos e (c'e s t le cas de la troisiè m e instruction).

Si x e s t de type pointcole t y d'un type pouvant s e ram e ne r au type point, on e s t ram e né au cas précédent,
dans la m e s ure où la fonction m e m bre ope rator ==, h é rité e d e point pe ut toujours s'appliq ue r à un obje t de
type point (c'e s t le cas des instructions 1, 2 e t 4).

En re vanch e , si x est de type int, iln'e s t plus possible de lui appliq ue r une fonction m e m bre . C'e s t ce q ui s e
passe dans la derniè re instruction q ui s e ra donc re je té e à la com pilation.

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

class vect
{ int nelem ; // nombre d'éléments
int * adr ; // adresse zone dynamique contenant les éléments
public :
vect (int) ; // constructeur (précise la taille du vecteur)
~vect () ; // destructeur
int & operator [] (int) ; // accès à un élément par son "indice"
} ;

O n supppose q ue le constructe ur alloue e ffe ctive m e nt l'e m place m e nt né ce s s aire pour le nom bre d'entie rs
re çu e n argum e nt e t q ue l'opé rate ur [] pe ut ê tre utilisé indiffé re m m e nt dans une expression ou à gauch e
d'une affe ctation.

Cré e r une clas s e ve ctb, dérivé e d e ve ct, pe rm e ttant de m anipuler de s ve cte urs dynam iq ue s , dans les q ue ls on
pe ut fixe r les "borne s " des indices, les q ue lles s e ront fournie s au constructe ur de ve ctb. La clas s e ve ct
apparaî tra ainsi com m e un cas particulie r de ve ctb (un obje t de type ve ct é tant un obje t de type ve ctb dans
leq ue lla lim ite infé rie ure de l'indice e s t 0).

O n ne s e pré occupe ra pas, ici, de s problèm e s é ve ntue llem e nt pos é s par la re copie ou l'affe ctation d'obje ts de
type ve ctb.

___________________________________________________________________________

Sol
ution

Nous pré voirons, dans ve ctb, deux m e m bres donnée supplém e ntaire s (de but e t fin) pour cons e rve r les bornes
de l'indice (e n toute rigue ur, on pourrait s e conte nte r d'un m e m bre s upplém e ntaire conte nant la lim ite
infé rie ure , sach ant q ue la valeur supérieure pourrait s'e n déduire à partir de la connaissance de la taille du
ve cte ur ;toute fois, ce tte derniè re inform ation n'é tant pas publiq ue , nous re ncontre rions des problèm e s
d'accè s!).

M anife s te m e nt, ve ctb né ce s s ite un constructe ur à deux argum e nts e ntie rs corre s pondant aux bornes de
l'indice ;son en-tê te pourrait com m e nce r ainsi :
142 Exercices en langage C+ +

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'élém e nts souh aité s , c'e s t-à -dire ici f-
d+ 1. Voici l'e n-tê te com plet du constructe ur de ve ctb :

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 donnée de but e t fin.

Aucun de s tructe ur n'e s t né ce s s aire pour ve ctb, dans la m e s ure où son constructe ur n'alloue aucun autre
e m place m e nt dynam iq ue q ue ce lui alloué par ve ct.

En ce q ui conce rne l'opé rate ur [], on pe ut pe ns e r q ue ve ctb l'h é rite de ve ct e t q ue , par cons é q ue nt, iln'e s t
pas néce s s aire de le s urdé finir. Toute fois, la notation t[i] ne désigne plus forcé m e nt l'é lém e nt de rang i d'un
obje t de type ve ctb. O r, m anife s te m e nt, on souh aite ra q u'ile n aille toujours ainsi. Ilfaut donc redéfinir []
pour ve ctb, q uitte d'ailleurs à ré utiliser l'opé rate ur dé fini dans ve ct.

Voici ce q ue pourrait ê tre notre clas s e ve ctb (ici, on ne trouve q u'une définition, dans la m e s ure où les deux
fonctions m e m bre ont é té définie s "e n ligne ") :

#include "vect.h"
class vectb : public vect
{ int debut, fin ;
public :
vectb ( int d, int f) : vect (f-d+1)
{ debut = d ; fin = f ;
}
int & operator [] (int i)
{ return vect::operator [] (i-debut) ;
}
}

R e m arques :

1) Si le m e m bre donnée adr avait é té déclaré proté gé (prote cte d) dans la clas s e ve ct, nous aurions pu
redéfinir l'opé rate ur [] de ve ctb, sans faire appe là ce lui de ve ct :

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

/****** fichier int2d.h :déclaration 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) ; // accès à un élément, 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 , dérivé e d e int2d, pe rm e ttant de m anipuler de s tableaux dynam iq ue s , dans les q ue ls
on pe ut fixe r les "borne s " (valeur m inim ale e t valeur m axim ale) des deux indices ;les q uatre valeurs
corre s pondante s s e ront fournie s e n argum e nts du constructe ur de int2db.

O n ne s e pré occupe ra pas, ici, de s problèm e s é ve ntue llem e nt pos é s par la re copie ou l'affe ctation d'obje ts de
type int2db.

___________________________________________________________________________

Sol
ution

Ilsuffit, e n fait, de généraliser à la clas s e int2d, le travailré alisé dans l'e xe rcice pré cédent pour la clas s e
ve ct. Voici ce q ue pourrait ê tre notre clas s e int2db (ici, on ne trouve q u'une définition, dans la m e s ure où les
fonctions m e m bre de int2db ont é té fournie s "e n ligne ") :

#include "int2d.h"
class int2db : public int2d
{ int ligdeb, ligfin ; // bornes (mini, maxi) premier indice
int coldeb, colfin ; // bornes (mini, maxi) second indice
public : // constructeur
int2db (int ld, int lf, int cd, int cf) : int2d (lf-ld+1, cf-cd+1)
{ ligdeb = ld ; ligfin = lf ;
coldeb = cd ; colfin = cf ;
}
int & int2db::operator () (int i, int j) // rédéfinition de operator ()
{ return int2d::operator () (i-ligdeb, j-coldeb) ;
}
} ;

Note z q ue , là non plus, aucune prote ction d'indice s upplém e ntaire n'e s t à pré voir dans int2db, dè s lors
q u'e lle a déjà é té pré vue dans int2d.

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 déclaration
(fournie dans un fich ie r ve ct.h ) s e pré s e nte ainsi (note z la pré s e nce de m e m bre s "proté gé s ") :
144 Exercices en langage C+ +

class vect
{ protected : // en prévision d'une éventuelle classe dérivée
int nelem ; // nombre d'éléments
int * adr ; // adresse zone dynamique contenant les éléments
public :
vect (int) ; // constructeur
~vect () ; // destructeur
int & operator [] (int) ; // accès à un élément par son "indice"
} ;

O n suppose que le constructe ur alloue e ffe ctive m e nt l'e m place m e nt né ce s s aire e t q ue l'opé rate ur [] pe ut ê tre
utilisé indifé re m m e nt dans une expression ou à gauch e d'une affe ctation. En re vanch e , com m e on pe ut le
voir, ce tte clas s e n'a pas prévu de constructe ur par re copie e t e lle n'a pas surdé fini l'opé rate ur d'affe ctation.
L'affe ctation e t la transm ission par valeur d'obje ts de type ve ct pos e nt donc les "problèm e s h abitue ls".

Cré e r une clas s e ve ctok , dérivé e d e ve ct, de m aniè re à ce q ue l'affe ctation e t la transm ission par valeur
d'obje ts de type ve ctok se déroule conve nablem e nt. Pour facilite r l'utilisation de ce tte nouve lle clas s e ,
introduire une fonction m e m bre taille fournissant la dim e nsion d'un ve cte ur.

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 définir de nouve aux m e m bres donnée. Pour dé clare r de s
obje ts de type ve ctok , il faudra pouvoir e n pré cis e r la dim e nsion, ce q ui signifie q ue ve ctok devra
absolum e nt disposer d'un constructe ur approprié . Ce dernie r s e conte nte ra toute fois de re transm e ttre la
valeur re çue e n argum e nt au constructe ur de ve ct ;ilaura donc un corps vide. Note z q u'iln'e s t pas
né ce s s aire de pré voir un de s tructe ur (car ce lui de ve ct s e ra appe lé e n cas de destruction d'un obje t de type
ve ctok e t iln'y a rie n de plus à faire ).

Note z q ue pour gé re r conve nablem e nt la re copie ou l'affe ctation d'obje ts, nous nous conte nte rons de la
m é th ode déjà re ncontré e q ui consiste à dupliq ue r les obje ts conce rné s (e n e n faisant une "copie profonde ").

Pour satisfaire aux contraintes de l'é noncé , ilnous faut donc pré voir de définir, dans ve ctok , un constructe ur
par re copie . Ilfaut alors te nir com pte de ce q ue la re copie d'un obje t de type ve ctok (q ui fe ra donc appe là
ce constructe ur) e ntraî ne ra alors l'appe l du constructe ur de ve ct q ui s e ra indiq ué dans l'e n-tê te du
constructe ur par re copie de ve ctok , du m oins si une te lle inform ation e s t pré cis é e (dans le cas contraire , ily
aurait appe ld'un constructe ur sans argum e nt de ve ct, ce q ui n'e s t pas possible ici). Ici, nous disposons donc
de deux possibilité s :

- dem ande r l'appe ldu constructe ur par re copie de ve ct, ce q ui conduit à ce t e n-tê te :
vectok::vectok (vectok & v) : vect (v)

(rappe lons q ue l'argum e nt v, de type ve ctok , s e ra im plicite m e nt conve rti e n type ve ct, pour pouvoir ê tre
transm is au constructe ur ve ct).
Ce tte façon de faire conduit à la cré ation d'un obje t de type ve ct, obte nu par re copie (par dé faut) de v. Il
faudra alors com pléte r le travaile n cré ant un nouve le m place m e nt dynam iq ue pour le ve cte ur e t e n
adaptant corre cte m e nt la valeur de adr.
- dem ande r l'appe ldu constructe ur à un argum e nt de ve ct, ce q ui conduit à ce t e n-tê te :
vectok::vectok (vectok & v) : vect (v.nelem)
IX. La tech nique de l'h é ritage 145

Ce tte fois, ily aura cré ation d'un nouve lobje t de type ve ct, ave c son propre e m place m e nt dynam iq ue ,
dans leq ue l il faudra né anm oins re copie r les valeurs de v. C'e s t ce tte derniè re s olution q ue nous
ch oisirons ici.
Toujours pour satisfaire aux contraintes de l'é noncé , nous devons surdé finir l'affe ctation dans la clas s e
ve ctok . Ici, aucun ch oix ne s e pré s e nte . Nous utiliserons l'algorith m e pré s e nté dans l'e xe rcice VII.4.

Voici ce q ue pourraie nt ê tre la déclaration e t la définition de notre clas s e ve ctok :

/**** déclaration de la classe vectok ****/


#include "vect.h"
class vectok : public vect
{ // pas de nouveaux membres donnée
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 &); // surdéfinition de l'affectation de vectok
int taille ()
{ return nelem ;
}
} ;
/***** définition du constructeur par recopie de vectok *****/
// il doit obligatoirement prévoir 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] ;
}
/***** définition 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 définition du constructe ur par re copie de ve ctok , dans le cas où
l'on fe rait appe lau constructe ur par re copie (par dé faut) de ve ct :
vectok::vectok (vectok & v) : vect (v)
{ nelem = v.nelem ;
adr = new int [nelem] ;
int i ;
for (i=0 ; i<nelem ; i++)
adr[i] = v.adr[i] ;
}

Ici, ila fallu alloue r un nouve le m place m e nt pour un ve cte ur, ce q ui n'é tait pas le cas lors q ue l'on faisait
appe lau constructe ur à un argum e nt de ve ct (puis q ue ce dernie r faisait déjà une te lle allocation).

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 reçu par fct : " << "\n" ;
int i ;
for (i=0 ; i<v.taille() ; i++) cout << v[i] << " " ;
}
CH A PITRE X :
L'H ERITAGE M ULTIPLE

RAPPELS

D e puis la ve rsion 2.0, C+ + autoris e l'h é ritage m ultiple : une clas s e pe ut h é rite r de plusieurs autre s clas s e s ,
com m e dans ce t e xe m ple où la clas s e pointcolh é rite s im ultané m e nt des clas s e s point e t coul ;

class pointcol : public point, public coul // chaque dérivation, ici publique
// pourrait être privée
{ // définition des membres supplémentaires (données ou fonctions)
// ou redéfinition de membres existants déjà dans point ou coul
} ;

Ch acune des dérivations peut ê tre publiq ue ou privé e . Le s m odalités d'accè s aux m e m bres de ch acune des
classes de bas e re s te nt les m ê m e s q ue dans le cas d'une dérivation "sim ple". L'opé rate ur de ré s olution de
porté e (::) pe ut ê tre utilisé :

- soit lors q ue l'on ve ut accéder à un m e m bre d'une des classes de bas e , alors q u'ile s t redéfini dans la
classe dérivé e ,
- soit lors q ue deux classes de bas e possè dent un m e m bre de m ê m e nom e t q u'ilfaut alors préciser celui
q ui nous inté re s s e .

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 ntionnés dans la déclaration de la classe dérivé e (ici, point puis coulpuis q ue nous
avons é crit class pointcol : public point, public coul). Les destructe urs sont appe lés dans l'ordre inve rs e .

Le constructe ur de la classe dérivé e pe ut m e ntionne r, dans son e n-tê te , des inform ations à re transm e ttre à
ch acun de s constructe urs des classes de bas e (ce s e ra gé né ralem e nt indispensable, sauf si une classe de bas e
possè de un constructe ur sans argum e nt ou si elle ne dispose pas du tout de constructe ur). En voici un
e xe m ple :

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 dérivations succe s s ive s , ile s t tout à fait possible q u'une clas s e h é rite "deux fois" d'une m ê m e
clas s e . En voici un e xe m ple dans leq ue lD h é rite deux fois de A :

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

D ans ce cas, les m e m bres donnée de la clas s e e n q ue s tion (A dans notre e xe m ple) apparais s e nt deux fois
dans la classe dérivée de deuxiè m e nive au (ici D ). Nature llem e nt, il e s t né ce s s aire de faire appe l à
l'opé rate ur de ré s olution de porté e (::) pour leve r l'am biguïté . Si l'on souh aite q ue de te ls m e m bre s
n'apparais s e nt q u'une s e ule fois dans la classe dérivée de deuxiè m e nive au, ilfaut, dans les déclarations des
dérivé e s d e pre m ie r nive au (ici B e t C) déclare r ave c l'attribut virtualla classe dont on ve ut é vite r la
duplication (ici A).

Voici com m e nt on procéderait dans l'e xe m ple pré cédent (le m ot virtualpe ut ê tre indiffé re m m e nt placé avant
ou aprè s le m ot public ou le m ot private ) :

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 dérivé e s puis s e nt pré cis e r les inform ations à transm e ttre au constructe ur de ce tte clas s e virtue lle
(dans le cas "usuel" où l'on autoris e la duplication, ce problèm e ne s e pos e plus ;e n e ffe t, ch aq ue
constructe ur transm e t les inform ations aux clas s e s asce ndantes dont les constructe urs transm e tte nt, à leur
tour, les inform ations aux constructe urs de ch acune des "occurre nce s " de la clas s e e n q ue s tion - ce s
inform ations pouvant é ve ntue llem e nt ê tre diffé re nte s ). Dans ce cas, on le pré cise dans l'e n-tê te du
constructe ur de la classe dérivé e , e n plus des argum e nts destiné s aux constructe urs des classes du nive au
im m édiate m e nt supérieur, com m e dans ce t e xe m ple :

D ( .....) : 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 déclaré q ue A é tait "virtue lle") n'auront
plus à spécifie r d'inform ations pour un constructe ur de A

Enfin, le constructe ur d'une clas s e virtue lle e s t toujours appe lé avant les autre s .

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 déclaration de
la clas s e C, e t non par l'e n-tê te du constructe ur de C!). Le je u de la transm ission des argum e nts e t des
argum e nts par dé faut conduit au ré s ultat suivant :

** construction objet B : 1 0
** construction objet A : 1 1
** construction objet C : 3 3
** construction objet B : 1 5
** construction objet A : 10 1
156 Exercices en langage C+ +

** construction objet C : 12 21

Exe rcice X.2

___________________________________________________________________________

Enoncé

M ê m e q ue s tion q ue pré cédem m e nt, e n re m plaçant sim plem e nt l'e n-tê te du constructe ur de C par :

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 prévue s par dé faut. Voici le ré s ultat obte nu :

** construction objet B : 1 0
** construction objet A : 2 1
** construction objet C : 3 3
** construction objet B : 1 5
** construction objet A : 2 1
** construction objet C : 12 21

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é cédent, de m aniè re à ce q u'un obje t de type D ne contie nne q u'une s e ule fois
les m e m bres de A (q ui s e réduisent e n fait à l'e ntie r na). O n s'arrange ra pour q ue le constructe ur de A s oit
appe lé ave c la valeur 2*nn+ 1, dans laq ue lle nn désigne l'argum e nt du constructe ur de C.

___________________________________________________________________________

Sol
ution

D ans la déclaration 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 déclaration 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 façon 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é cédent ainsi transform é :

$$construction objet A 21
$$construction objet B 5
X. L'h é ritage m ultiple 159

$$construction objet C 10
$$construction objet D 20

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 déclaration de la clas s e liste s e pré s e nte ra ainsi :

struct element // structure d'un élément de liste


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

La fonction ajoute devra ajoute r, e n début de liste , un é lém e nt pointant sur l'inform ation dont l'adre s s e e s t
fournie e n argum e nt (void *). Pour "e xplore r" la liste , on a pré vu trois fonctions :

- pre m ie r, q ui fournira l'adresse de l'inform ation associé e au pre m ie r noe ud de la liste e t q ui, e n m ê m e
te m ps, pré pare ra le proce s s us de parcours de la liste ,
160 Exercices en langage C+ +

- proch ain, q ui fournira l'adresse de l'inform ation associé e au "proch ain noe ud" ;des appe ls succe s s ifs
de proch ain devront pe rm e ttre de parcourir la liste (sans q u'il soit né ce s s aire d'appe ler une autre
fonction),
- fini, q ui pe rm e ttra de s avoir si la fin de liste e s t atte inte ou non.
1) Com pléte r la déclaration pré cédente de la clas s e liste e t e n fournir la définition de m aniè re à ce q u'e lle
fonctionne com m e dem andé .

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 << "Coordonnées : " << x << " " << y << "\n" ; }
} ;

Cré e r une clas s e liste _points, dérivé e à la fois de liste e t de point, pour q u'e lle puis s e pe rm e ttre de
m anipuler de s liste s ch aîné e s d e points, c'e s t-à -dire des liste s com parables à ce lles pré s e nté e s ci-de s s us, e t
dans les q ue lles l'inform ation associé e e s t de type point. O n de vra pouvoir, notam m e nt :

- ajoute r un point e n début 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 é lém e nt courant". Ils e ra
m e m bre donnée de la clas s e liste .

Nous convie ndrons (classiquem e nt) q ue la fin de liste e s t m até rialisée par un noe ud com portant un pointe ur
"nul". La clas s e liste devra disposer d'un constructe ur dont le rôle s e lim ite ra à l'initialiser à une "liste
vide", ce q ui s'obtie ndra sim plem e nt e n plaçant un pointe ur nulcom m e adresse de début de liste (ce tte façon
de procéder sim plifie grande m e nt l'algorith m e d'ajout d'un é lém e nt e n début de liste , puis q u'e lle é vite
d'avoir à distingue r de s autre s le cas de la liste vide).

Com m e un obje t de type liste e s t am e né à cré e r diffé re nts e m place m e nts dynam iq ue s , ile s t né ce s s aire de
pré voir la libération de ce s e m place m e nts lors q ue l'obje t e s t détruit. Ilfaudra donc pré voir un de s tructe ur,
ch argé de détruire les diffé re nts noeuds de la liste . A ce propos, note z q u'iln'e s t pas possible ici de
dem ande r au de s tructe ur de détruire é galem e nt les inform ations associé e s ;e n e ffe t, d'une part, ce n'e s t pas
l'obje t de type liste q ui a alloué ce s e m place m e nts : ils sont sous la re s ponsabilité de l'utilisate ur de la clas s e
liste .

Voici ce q ue pourraî
t ê tre notre clas s e liste com plète :

struct element // structure d'un élément de liste


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

{ debut = NULL ;
courant = debut ; // par sécurité
}
~liste () ; // destructeur
void ajoute (void *) ; // ajoute un élément en début de liste
void * premier () // positionne sur premier élément
{ courant = debut ; return (courant->contenu) ; }
void * prochain () // positionne sur prochain élément
{ if (courant != NULL) courant = courant->suivant ;
return (courant->contenu) ;
}
int fini () { return (courant == NULL) ; }
} ;
liste::~liste ()
{ element * suiv ;
courant = debut ;
while (courant != NULL )
{ suiv = courant->suivant ; delete courant ; courant = suiv ; }
}
void liste::ajoute (void * chose)
{ element * adel = new element ;
adel->suivant = debut ;
adel->contenu = chose ;
debut = adel ;
}

2) Com m e nous le dem ande l'é noncé , nous allons donc cré e r une clas s e liste _points par :

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 donnée (x et y) n'ayant aucun inté rê t par la suite .

En re vanch e , la cré ation de s fonctions m e m bre dem andé e s d e vie nt e xtrê m e m e nt sim ple. En e ffe t, la fonction
d'insertion d'un point e n début de liste pe ut ê tre la fonction ajoute de la clas s e liste : nous n'aurons donc
m ê m e pas besoin de la surdéfinir. En ce q ui conce rne la fonction d'affich age de tous les points de la liste
(q ue nous nom m e rons é galem e nt affich e ), illui suffira de faire appe l:

- aux fonctions pre m ie r, proch ain e t fini de la clas s e liste pour le parcours de la liste de points,
- à la fonction affich e de la clas s e point pour l'affich age d'un point.
Nous aboutissons à ce ci :

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 :

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

RAPPELS

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 dérivé e pe rm e tte nt d'affe cte r à un pointe ur
sur une classe de bas e la valeur d'un pointe ur sur une classe dérivé e . Toute fois, par dé faut, le type des obje ts
pointé s e s t défini lors de la com pilation. Par e xe m ple, ave c :

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 problèm e s inh é re nts au typage s tatiq ue . Lors q u'une
fonction e s t déclaré e virtue lle (m ot clé virtual ) dans une clas s e , les appe ls à une te lle fonction ou à
n'im porte laq ue lle de ses redéfinitions dans des classes dérivé e s s ont "ré s olus" au m om e nt de l'e xé cution, e n
fonction du type de l'obje t conce rné . O n parle de typage dynam iq ue des obje ts (ou de ligature dynam iq ue
des fonctions). Par e xe m ple, ave c :

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 précisém e nt, ilne doit pas
accom pagne r les redéfinitions de ce tte fonction dans les classes dérivé e s ,
- une m é th ode déclaré e virtue lle dans une classe de bas e pe ut ne pas ê tre redéfinie dans s e s clas s e s
dérivé e s ,
- une fonction virtue lle pe ut ê tre s urdé finie (ch aq ue fonction surdéfinie pouvant ê tre ou ne pas ê tre
virtue lle),
- un constructe ur ne pe ut pas ê tre virtue l, un de s tructe ur pe ut l'ê tre .
- de par sa nature m ê m e , le m é canism e de ligature dynam iq ue e s t lim ité à une h ié rarch ie de clas s e s ;
souve nt, pour q u'ilpuis s e s 'appliq ue r à toute une biblioth è q ue de clas s e s , on s e ra am e né à faire h é rite r
toute s les classes de la biblioth è q ue d'une m ê m e classe de bas e .

Le s fonctions virtue l
le s pure s

Une "fonction virtue lle pure " se déclare ave c une initialisation à zé ro, com m e dans :

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 considérée com m e "abstraite ",
c'e s t-à -dire q u'iln'e s t plus possible de cré e r de s obje ts de son type .

Une fonction dé claré e virtue lle pure dans une classe de base doit obligatoire m e nt ê tre redéfinie dans une
classe dérivé e ou dé claré e à nouve au virtue lle pure (depuis la ve rsion 3.0 de C+ + , une fonction virtue lle
pure pe ut ne pas ê tre déclarée dans une classe dérivé e e t, dans ce cas, e lle e s t à nouve au im plicite m e nt
fonction virtue lle pure de ce tte classe dérivé e ).

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 coordonnées 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 coordonnées 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é déclaré e virtue lle), on aurait obte nu le m ê m e ré s ultat.

En re vanch e , dans les instructions 2, adp (de type point *) pointe m ainte nant sur un obje t de type pointcol.
Grâ ce au typage dynam iq ue , adp-> affich e () appe lle bien la fonction affich e du type pointcol.

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

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

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 coordonnées 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é déclaré e virtue lle). Lors q u'un obje t de type pointcolappe lle une fonction affich e , ce
s e ra bie n la fonction affich e de point q ui s e ra appe lée (puis q ue affich e n'e s t pas redéfinie dans pointcol).
M ais ce tte derniè re fe ra appe l, dans ce cas, à la fonction ide ntifie de pointcol. Bie n e nte ndu, lors q u'un obje t
XI. Les fonctions virtuelles 171

de type point appe lle affich e , ce tte derniè re fe ra toujours appe là la fonction ide ntifie de point (le m ê m e
ré s ultat s e rait obte nu sans ligature dynam iq ue ).

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

Je suis un point
Mes coordonnées sont : 3 4
Je suis un point coloré de couleur : 5
Mes coordonnées sont : 5 9
---------------
Je suis un point
Mes coordonnées sont : 3 4
Je suis un point coloré de couleur : 5
Mes coordonnées sont : 5 9
---------------
Je suis un point coloré de couleur : 5
Mes coordonnées sont : 5 9
Je suis un point coloré de couleur : 5
Mes coordonnées sont : 5 9

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
é lém e nts e s t non s e ulem e nt inconnu de la clas s e , m ais é galem e nt susce ptible de varie r d'un é lém e nt à un
autre . Pour q ue la ch os e s oit possible, on im pos e ra sim plem e nt la contrainte s uivante : tous les type s
conce rnés devront dérive r d'un m ê m e type de bas e nom m é base . Le type base s e ra supposé connu au
m om e nt où l'on dé finit la clas s e e ns_h e te r.

La clas s e base disposera au m oins d'une fonction virtue lle pure nom m é e affich e ;ce tte fonction de vra ê tre
redéfinie dans les classes dérivé e s pour affich e r les caracté ristiq ues de l'obje t conce rné .

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é lém e nt à l'e ns e m ble (e lle devra s'assure r q u'iln'e xiste pas déjà ),
- appartie nt pour te s te r l'apparte nance d'un élém e nt à l'e ns e m ble,
- cardinalq ui fournira le nom bre d'élém e nts de l'e ns e m ble.
D e plus, la classe devra ê tre m unie d'un "ité rate ur", c'e s t-à -dire d'un m é canism e pe rm e ttant de parcourir les
diffé re nts é lém e nts de l'e ns e m ble. O n pré voira 3 fonctions :

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


- suivant q ui fournira e n re tour le proch ain é lém e nt (obje t d'un type dérivé de base ),
- e xiste pour pré cis e r s'ile xiste e ncore un é lém 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 é lém e nts de l'e ns e m ble
(e lle fe ra, bie n sûr, 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 dérive r de base .
172 Exercices en langage C+ +

O n ne s e pré occupe ra pas des éve ntue ls problèm e s pos é s par l'affe ctation ou la transm ission par valeur
d'obje ts du type e ns_h e te r.

_______________________________________________________________

Sol
ution

M anife s te m e nt, la clas s e e ns_h e te r ne contie ndra pas les obje ts corre s pondants aux é lém e nts de l'e ns e m ble,
m ais s e ulem e nt des pointe urs sur ces diffé re nts é lém e nts. A m oins de fixe r le nom bre m axim ald'élém e nts a
priori, ile s t né ce s s aire de cons e rve r ce s pointe urs dans un em place m e nt alloué dynam iq ue m e nt par le
constructe ur ;ce dernie r com porte ra donc un argum e nt pe rm e ttant de préciser le nom bre m axim al
d'élém e nts re q uis pour l'e ns e m ble.

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

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


- le nom bre courant d'élém e nts (ne le m ),
- un e ntie r (courant) q ui s e rvira au m é canism e d'ité ration (il désignera 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 dérivé 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 déclaration de notre clas s e e ns_h e te r :

/* fichier ensheter.H */
/* déclaration de la classe ens_heter */
class base ;
class ens_heter
{ int nmax ; // nombre maximal d'éléments
int nelem ; // nombre courant d'éléments
base * * adel ; // adresse zone de pointeurs sur les objets éléments
int courant ; // numéro d'élément courant (pour l'itération)

public :
ens_heter (int=20) ; // constructeur
~ens_heter () ; // destructeur
void ajoute (base &) ; // ajout d'un élément
int appartient (base &) ; // appartenance d'un élément
int cardinal () ; // cardinal de l'ensemble
void init () ; // initialisation itération
base & suivant () ; // passage élément suivant
int existe () ; //
void liste () ; // affiche les "valeurs" des différents éléments
} ;

La clas s e base (déclaration e t définition) découle directe m e nt de l'é noncé :

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

} ;

Voici la définition de s fonctions m e m bre de e ns_h e te r (note z q ue leur com pilation né ce s s ite la déclaration
(définition) pré cédente de la clas s e base (e t non pas s e ulem e nt une s im ple indication de la form e class
base ) :

/* définition de la classe ens_heter */


#include "ensheter.h"
ens_heter::ens_heter (int dim)
{ nmax = dim ;
adel = new base * [dim] ;
nelem = 0 ;
courant = 0 ; // précaution
}
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 , dérivé e s d e base e t q ui cré e un pe tit
e ns e m ble h é té rogè ne (sa com pilation né ce s s ite la déclaration de la clas s e base ). A la suite figure le ré s ultat
de son exécution :

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

class point : public base


{ int x, y ;
public :
point (int abs=0, int ord=0)
{ x = abs ; y = ord ;
}
void affiche ()
{ cout << "Point de coordonnées : " << 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 réelle : " << 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'itérateur, on trouve : " << n << " éléments\n" ;
}
______________________________
cardinal de e : 0
contenu de e
cardinal de e : 1
contenu de e
Point de coordonnées : 1 3
cardinal de e : 2
contenu de e
Point de coordonnées : 1 3
Complexe - partie réelle : 0.5, partie imaginaire : 3
avec l'itérateur, on trouve : 2 éléments
XI. Les fonctions virtuelles 175

Re m arq ue :

Si l'on ch e rch ait à ré s oudre les problèm e s pos é s par l'affe ctation e t la transm ission par valeur d'obje ts de
type e ns_h e te r, on s e rait am e né à ch oisir entre deux m é th ode s :
- e ffe ctue r de s "copie s com plète s " (on dit souve nt "profonde s ") de l'obje t, c'e s t-à -dire e n te nant
com pte , non s e ulem e nt de ses m e m bre s , m ais aussi de ses parties dynam iq ue s ,
- utiliser un "com pte ur de ré fé re nce " associé à ch aq ue partie dynam iq ue de façon à e n é vite r la
duplication.
O r, la pre m iè re m é th ode conduirait e ffe ctive m e nt à re copie r la partie dynam iq ue de l'e ns e m ble, c'e s t-à -
dire le tableau de pointe urs d'adre s s e base *. M ais, q ue faudrait-ilfaire pour les é lém e nts e ux-m ê m e s
(pointé s par les diffé re nte s valeurs du dit tableau)? M ê m e s i l'on adm e t q u'il faut é galem e nt les
dupliq ue r, iln'e s t pas possible de le faire s ans connaî tre la "structure " des obje ts conce rné s , à m oins
d'im pos e r à ch aq ue obje t de disposer de fonctions approprié e s pe rm e ttant d'en réaliser une copie
profonde .
La deuxiè m e m é th ode pose des problèm e s voisins en ce qui concerne le traite m e nt à appliq ue r aux obje ts
q ui sont des élém e nts d'un ensem ble de type e ns_h e te r. Disposent-ils d'un com pte ur de ré fé re nce ?Si oui,
com m e nt le m e ttre à jour?
D 'une m aniè re gé né rale, vous voye z q ue ce t e xe m ple, par les q ue s tions q u'ilsoulève , plaide large m e nt
e n fave ur de la constitution de biblioth è q ues d'obje ts, dans les q ue lles s ont définie s , a priori, un ce rtain
nom bre de rè gles com m une s . Parm i ce s rè gles , on pourrait notam m e nt im pos e r à ch aq ue obje t de
posséder une fonction (de nom uniq ue ) e n assurant la copie profonde ;nature llem e nt, pour pouvoir
l'utiliser correcte m e nt, ilfaudrait q ue la ligature dynam iq ue s oit possible, c'e s t-à -dire q ue tous les obje ts
conce rnés dérive nt d'un m ê m e obje t de bas e .
CH A PITRE XII :
LES FLO TS D 'ENTREE ET D E SO RTIE

RAPPELS

Un flot e s t un "canalre ce vant (flot "d'entré e ") ou fournissant (flot de "sortie ") de l'inform ation. Ce canal
e s t associé à un pé riph é riq ue ou à un fich ie r. Un flot d'entré e e s t un obje t de type istre am tandis q u'un flot
de sortie e s t un obje t de type ostre am . Le flot cout e s t un flot de sortie prédéfini, conne cté à la sortie
standard stdout ;de m ê m e , le flot cin e s t un flot d'entré e prédéfini, conne cté à l'e ntré e s tandard stdin. Le s
inform ations fournie s ici sont valables à partir de la ve rsion 2.0.

La cl
as s e os tre am

Elle s urdé finit l'opé rate ur < < s ous la form e d'une fonction m e m bre :

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 ntrée d'une ch aîne de caractè re s , e t non d'une adre s s e ).

Le s "e s pace s _blancs" (e s pace , tabulation h orizontale \t ou ve rticale \v , fin de ligne \n, re tour ch ariot \r e t
ch ange m e nt de page \f) s e rve nt de "délim ite urs" (com m e dans scanf), y com pris pour les ch aî nes de
caractè re s .

Principal
es fonctions m em bres :

istream & get (ch ar & c) : e xtrait un caractè re du flot d'entré e e t le range dans c.
int get () : e xtrait un caractè re du flot d'entré e e t e n re nvoie la valeur (sous form e d'un entie r) ;fournit
EOF e n cas de fin de fich ie r.
istream & read (void *adr, int tail
le) lit taille caractè re s s ur le flot e t les range à partir de l'adre s s e
adr.

La cl
as s e ios tre am

Elle e s t dérivé e d e istre am e t ostre am . Elle pe rm e t de ré aliser des entré e s s ortie s "conve rsationne lles ".

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 dérive nt istre am e t ostre am ) définit les constante s s uivante s :

e ofbit : fin de fich ie r (le flot n'a plus de caractè res disponibles ),
failbit : la proch aine opé ration sur le flot ne pourra pas aboutir,
badbit : le flot e s t dans un état irré cupé rable,
goodbit (valant, e n fait 0) : aucune d e s e rrre urs précédente s .
Une opé ration sur le flot a ré ussi lors q ue l'un des bits goodbit ou e ofbit e s t activé . La proch aine opé ration
sur le flot ne pourra ré ussir que si goodbit e s t activé (m ais iln'e s t pas ce rtain q u'e lle ré ussisse !).

Lors q u'un flot e s t dans un état d'erreur, aucune opé ration ne pe ut aboutir tant q ue la condition d'e rre ur n'a
pas é té corrigé e e t q ue le bit d'erreur corre s pondant n'a pas é té re m is à zé ro (à l'aide de la fonction clear).

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

Surdéfinition 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 précédents 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 surdéfinira < < e t > > pour une clas s e q ue lconq ue , sous form e de fonctions am ie s , e n utilisant ce s
"cane vas" :

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


{
// Envoi sur le flot sortie des membres de objet en utilisant
// les possibilités 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 différents membres de objet
// en utilisant les possibilités classiques de >> pour les types de base
// c'est-à-dire des instructions de la forme :
// entree >> ..... ;
return entree ;
}

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 décimale

ios::oct conversion octale

ios::hex conversion hexadécimale

1 Ici, l
a trans m ission peut s e faire par val
e ur ou par ré fé re nce.
182 Exercices en langage C+ +

_____________________________________________________________________________________
ios::showbase affichage indicateur de base (en sortie)

ios::showpoint affichage point décimal (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 Entrée/Sortie Active le bit de conversion décimale

hex Entrée/Sortie Active le bit de conversion hexadécimale

oct Entrée/Sortie Active le bit de conversion octale

endl Sortie Insère un saut de ligne et vide le tampon

ends Sortie Insère un caractère de fin de chaîne (\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) Entrée/Sortie Définit la base de conversion

setprecision (int) Entrée/Sortie Définit la précision des nombres


flottants

setw (int) Entrée/Sortie


Définit le gabarit. Il retombe à 0
après chaque opération
_____________________________________________________________________________________

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 , dérivant 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 (déplace 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 , dérivant 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 données (écriture en fin de fichier)

ios::ate Se place en fin de fichier après 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 façons diffé re nte s :

- e n "point fixe ", ave c un nom bre de décim ales variant de 0 à 10,
- e n notation scie ntifiq ue , ave c un nom bre de décim ales variant de 0 à 10.
D ans tous les cas, on affich e ra les ré s ulats ave c ce tte pré s e ntation :

précision 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 déte rm iné par le m anipulate ur s e tw . Note z q u'ilfaut transm e ttre ce m anipulate ur
au flot conce rné , juste avant d'affich e r l'inform ation voulue .

#include <iostream.h>
#include <iomanip.h> //pour les "manipulateurs paramétriques"
main()
{ float val, carre ;
cout << "donnez un nombre réel : " ;
cin >> val ;
carre = val*val ;
cout << "Voici son carré : \n" ;
int i ;
cout << " en notation point fixe : \n" ;
cout.setf (ios::fixed, ios::floatfield) ; // met à 1 le bit ios::fixed
// du champ ios::floatfield
for (i=0 ; i<10 ; i++)
cout << " précision de " << setw (2) << i << " chiffres : "
<< setprecision (i) << setw (12) << carre << "\n" ;
cout << " en notation scientifique : \n" ;
cout.setf (ios::scientific, ios::floatfield) ;
for (i=0 ; i<10 ; i++)
cout << " précision de " << setw (2) << i << " chiffres : "
<< setprecision (i) << setw (12) << carre << "\n" ;
}
XII. Les flots d'entré e e tde sortie 185

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

___________________________________________________________________________

Sol
ution

Nous devons donc surdéfinir les opé rate urs < < e t > > pour q u'ils puissent re ce voir, e n de uxiè m e
opé rande , un argum e nt de type point. Ilne pourra s'agir q ue de fonctions am ie s , dont les prototype s s e
pré s e nte ront ainsi :

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 coordonnées du point, accom pagnées des sym boles < e t > .

En re vanch e , l'é criture de ope rator > > né ce s s ite un pe u plus d'atte ntion. En e ffe t, ilfaut s'assure r q ue
l'inform ation s e pré s e nte bien sous la form e re q uis e e t, si ce n'e s t pas le cas, pré voir de donner au flot
conce rné l'é tat bad, afin q ue l'utilisate ur puis s e s avoir q ue l'opé ration s'e s t m al déroulée (e n te s tant
"nature llem e nt" l'é tat du flot).

Voici ce q ue pourrait ê tre la déclaration de notre clas s e (nous l'avons sim plem e nt m unie d'un contructe ur) e t
la définition des deux fonctions am ie s voulue s :

#include <iostream.h>

class point
{ int x, y ;
public :
point (int abs=0, int ord=0)
{ x = abs ; y = ord ; }
int abscisse () { return x ; }
friend ostream & operator << (ostream &, point) ;
friend istream & operator >> (istream &, point &) ;
186 Exercices en langage C+ +

} ;

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'exécution :

main()
{ char ligne [121] ;
point a(2,3), b ;
cout << "point a : " << a << " point b : " << b << "\n" ;
do
{ cout << "donnez un point : " ;
if (cin >> a) cout << "merci pour le point : " << a << "\n" ;
else { cout << "** information incorrecte \n" ;
cin.clear () ;
cin.getline (ligne, 120, '\n') ;
}
}
while ( a.abscisse () ) ;
}
____________________________

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

___________________________________________________________________________

Sol
ution

Si nom fich désigne une ch aî


ne de caractè re s , la déclaration :

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 à créer : " ;
cin >> setw (LGMAX) >> nomfich ;
ofstream sortie (nomfich, ios::out) ;
if (!sortie) { cout << "création 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é cédent.

___________________________________________________________________________

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 << "Numéro 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 définir ce q u'on nom m e s ouve nt des
"fonctions génériques". Plus précisém e nt, à l'aide d'une uniq ue définition com portant des "param è tres de
type ", on dé crit toute une fam ille de fonctions ;le com pilate ur "fabriq ue " (on dit aussi instancie ) la ou les
fonctions néce s s aire s à la dem ande (on nom m e s ouve nt ce s instances des fonctions patron).

La ve rsion 3 lim ite les param è tres d'un patron de fonctions à des param è tres de type ;beaucoup
d'im plém e ntations toute fois acce pte nt é galem e nt des "param è tre s e xpre s s ion" e t ce tte possibilité s e ra
probablem e nt re connue de la future norm e ANSI de C+ + . Toute fois, pour plus de clarté , nous la
pré s e nte rons s é paré m e nt.

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é céder ch acun du m ot (re lative m e nt arbitraire ) class
sous la form e te m plate < class ..., class ..., ...> . La définition de la fonction e s t classique, h orm is le fait
q ue les param è tre s m ue ts de type pe uve nt ê tre e m ployé s n'im porte où un type e ffe ctif e s t pe rm is. Par
e xe m ple :

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 éléments 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 légale m ê m e s i T n'e s t pas un type clas s e ;dans ce dernie r cas, e lle e s t sim plem e nt é q uivalente à :
T x = 3 ;

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é cédent :

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 définition d'un patron de fonctions ne peut pas ê tre com pilée s e ule ;de toute façon, e lle doit ê tre
connue du com pilate ur pour q u'ilpuis s e instancie r la bonne fonction patron. En gé né ral, les définitions
de patrons de fonctions figure ront dans des fich ie rs d'exte nsion h , de façon à é vite r d'avoir à e n fournir
systé m atiq ue m e nt la liste .

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 ptées dans l
e s corre s pondance s
e xacte s pour l
e s fonctions s urdéfinie s ).
XIII. Les patrons de fonctions 19 7

Lors de l'instanciation d'une fonction patron, ile s t né ce s s aire de ré aliser une correspondance exacte s ur les
param è tre s e xpre s s ion (aucune conve rsion n'e s t possible, contraire m e nt à ce q ui s e produit dans le cas de
surdéfinition de fonction).

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


is ation de fonctions de patrons

O n pe ut définir plusieurs patrons de m ê m e nom , possédant des param è tre s (de type ou e xpre s s ion) diffé re nts.
La s e ule rè gle à re s pe cte r dans ce cas e s t q ue l'appe ld'une fonction de ce nom ne doit pas conduire à une
am biguïté : un s e ulpatron de fonctions doit pouvoir ê tre utilisé à ch aq ue fois.

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

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


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

min (n, p) // appelle la fonction instanciée par le patron général


// soit ici : int min (int, int)
min (adr1, adr2) // appelle la fonction spécialisée
// 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 surdéfinie , dans le cas où
ile xiste un ou plusieurs patrons de fonctions.

Lors d'un appe lde fonction, le com pilate ur re ch e rch e tout d'abord une corre s pondance e xacte ave c les
fonctions "ordinaire s ". S'ily a am biguïté , la re ch e rch e é ch oue (com m e à l'accoutum é e ). Si aucune fonction
"ordinaire " ne convie nt, on e xam ine alors tous les patrons ayant le nom voulu. Si une s e ule corre s pondance
e xacte e s t trouvé e , la fonction corre s pondante e s t instancié e 2 e t le problèm e e s t ré s olu. S'ily en a plusieurs,
la re ch e rch e é ch oue ..

Enfin, si aucun patron de fonction ne convie nt, on e xam ine à nouve au toute s les fonctions "ordinaire s " e n les
traitant ce tte fois com m e de sim ples fonctions surdé finie s (prom otions num é riq ue s , conve rsions
standards 3...).

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 déjà é té.
3 - R e voyez éve ntue l
le m e nt l
e paragraph e 5.3 du ch apitre 4.
19 8 Exercices en C+ +

___________________________________________________________________________

Sol
ution

Ici, notre patron ne com porte ra q u'un s e ulparam è tre de type (corre s pondant à la fois à l'uniq ue argum e nt e t
à la valeur de re tour de la fonction). Sa définition ne pos e pas de problèm e particulie r.

#include <iostream.h>
template <class T> T carre (T a)
{ return a * a ;
}
main()
{ int n = 5 ;
float x = 1.5 ;
cout << "carre de " << n << " = " << carre (n) << "\n" ;
cout << "carre de " << x << " = " << carre (x) << "\n" ;
}

Exe rcice X III.2

___________________________________________________________________________

Enoncé

Soit ce tte définition de patron de fonctions :

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


{ .....
}

Ave c les déclarations 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 considéré com m e une
corre s pondance e xacte pour un ch ar [20] ;il s e ra probablem e nt acce pté dans la norm e ANSI e t il
instancie ra alors la fonction :

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'é lém e nts de type q ue lconq ue ,
le nom bre d'élém e nts du tableau é tant fourni e n param è tre (on supposera q ue l'e nvironne m e nt utilisé acce pte
les "param è tre s e xpre s s ion"). Ecrire un pe tit program m e utilisant ce patron.

___________________________________________________________________________

Sol
ution

// définition 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é conçu, le patron som m e ne pe ut ê tre appliq ué q u'à un type T pour leq ue l:
- l'opé ration d'addition a un s e ns ;ce la signifie donc q u'ilne pe ut pas s'agir d'un type pointe ur ;il
pe ut s'agir d'un type clas s e , à condition q ue ce tte derniè re ait surdéfini l'opé rate ur d'addition,
- la déclaration T som e s t corre cte ;ce la signifie q ue s i T e s t un type clas s e , ile s t né ce s s aire q u'il
dispose d'un constructe ur sans argum e nt,
- l'affe ctation som =0 e s t corre cte ;ce la signifie q ue s i T e s t un type clas s e , ile s t né ce s s aire q u'ilait
surdéfini l'affe ctation.
200 Exercices en C+ +

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

Ce la e s t é q uivalent à T som =0 si T e s t un type prédéfini ;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 problèm 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 "spécialisant" 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 définitions 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 déclarations :

int n, p, q ;
float x, y ;
double z ;

Quels sont les appe ls corre cts e t, dans ce cas, q ue ls sont les patrons utilisés et les prototypes des fonctions
instancié e s ?

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 : ambigüité entre fct (T, U) et fct (T*, U)
VI) erreur : ambigüité 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 biguïté . Le patron II e s t donc, ici, parfaite m e nt inutile.
Note z q ue s i nous avions défini sim ultané m e nt les deux patrons :
template <class T, class U> void fct (T a, U b) ;
template <class T> void fct (T a, T b)

le m ê m e ph é nom è ne d'am biguïté (e ntre ces deux patrons) s e rait apparu lors d'appe ls te ls q ue fct (n,p) ou
fct(x,y).
R appe lons q ue l'am biguïté n'e s t déte cté e q ue lors q ue le com pilate ur doit instancie r une fonction e t non
sim plem e nt au vu des définitions de patrons e lles -m ê m e s : ces derniè re s re s te nt donc acce pté e s tant q ue
l'am biguïté n'e s t pas m ise en évidence par un appe lla ré vé lant.
CH A PITRE XIV
LES PA TRO NS D E CLASSES
(D e puis l
a ve rs ion 3 s e ul
e m e nt)

RAPPELS

Introduite par la ve rsion 3, la notion de patron de clas s e s pe rm e t de définir ce q ue l'on nom m e aussi des
"clas s e s gé né riq ue s ". Plus précisém e nt, à l'aide d'une seule définition com portant des param è tres de type e t
des param è tre s e xpre s s ion1, on dé crit toute une fam ille de clas s e s ;le com pilate ur fabriq ue (instancie ) la ou
les clas s e s né ce s s aire s à la dem ande (on nom m e s ouve nt ce s instances des clas s e s patron).

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é céder du m ot-clé class e t les param è tre s e xpre s s ion e n
m e ntionnant leur type dans une liste de param è tre s introduite par le m ot te m plate (com m e pour les patrons
de fonctions, ave c ce tte diffé re nce q u'ici, tous les param è tre s - type ou e xpre s s ion - apparais s e nt). Par
e xe m ple :

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


{ // ici, T désigne un type quelconque, n une valeur entière quelconque
} ;

Si une fonction m e m bre e s t définie (ce q ui e s t le cas usuel) à l'e xté rie ur de la définition du patron, ilfaut
rappe ler au com pilate ur la liste de param è tre s (te m plate ) e t pré fixe r l'e n-tê te de la fonction m e m bre du nom
du patron accom pagné de ses param è tre s 2. Par e xe m ple, pour un constructe ur de notre patron de clas s e s
pré cédent :

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


{ ..... }

1. Cette fois, ce s param è tres sont déja prévus 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, constatée, m ais non jus tifiée, 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é cédent 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 défini 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 définir de ux patrons de m ê m e nom ). En
re vanch e , on pe ut spécialiser un patron de classes de diffé re nte s m aniè re s :

- en spécial
isant une fonction m em bre :

Par e xe m ple, ave c ce patron :

template <class T, int n> class tableau { ..... } ;

Nous pourrons é crire une ve rsion spécialisée de constructe ur pour le cas où T e s t le type point e t où n vaut
10 e n procédant ainsi :

tableau <point, 10>:: tableau (...) { ..... }

- en spécialisant une cl asse (dans ce dernie r cas, on pe ut é ve ntue llem e nt spécialiser tout ou une partie des
fonctions m e m bre , m ais ce n'e s t pas néce s s aire ). Par e xe m ple, ave c ce patron :

3. Dans l
a norm e ANSI, les conversions triviales seront probablem ent acceptées.
XIV. Les patrons de cl
asses 205

template <class T> class point { ..... } ;

nous pouvons fournir une ve rsion spécialisée pour le cas où T e s t le type ch ar e n procédant ainsi :

class point <char>


{ // nouvelle définition de la classe point pour les caractères
} ;

Ide ntité de cl
as s e s patron

O n ne pe ut affe cte r e ntre e ux q ue deux obje ts de m ê m e type . Dans le cas d'obje ts d'un type clas s e patron, on
considè re q u'ily a ide ntité de type lors q ue leurs param è tres de type s s ont identiq ue s e t q ue les param è tre s
e xpre s s ion ont les m ê m e s valeurs.

Cl
as s e s patron e t h é ritage

O n pe ut "com bine r" de plusieurs façons l'h é ritage ave c la notion de patron de clas s e s :

- Cl asse "ordinaire" dérivé 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 dérive 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 dérivé 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 dérivé d 'un patron de cl
asses. Par e xe m ple, si A est une clas s e patron dé finie par
te m plate < class T> A, on pe ut :
*définir une nouve lle fam ille de fonctions dérivé e s par :
template <class T> class B : public A <T>

D ans ce cas, ile xiste autant de classes dérivé e s possibles q ue de classes de bas e possibles .
*définir une nouve lle fam ille de fonctions dérivé e s par :
template <class T, class U> class B : public A <T>

D ans ce cas, on pe ut dire q ue ch aq ue classe de bas e possible pe ut e nge ndre r une fam ille de clas s e s
dérivé e s (de param è tre de type U).

Exe rcice X IV.1

___________________________________________________________________________

Enoncé

Soit la définition 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 définition 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 définition pré cédente ,
- q ue le constructe ur re copie la valeur re çue e n argum e nt dans ch acun de s é lém e nts du tableau tab .
b) D isposant ainsi de la définition pré cédente du patron e s s ai, de son constructe ur e t de ces déclarations :

const int n = 3 ;
int p = 5 ;

Quelles s ont les instructions corre cte s e t les clas s e s instancié e s : on e n fournira (dans ch aq ue cas) une
définition é q uivalente s ous form e d'une "clas s e ordinaire ", c'e s t-à -dire dans laq ue lle la notion de param è tre
a disparu.

essai <int, 10> ei (3) ; // I


essai <float, n> ef (0.0) ; // II
essai <double, p> ed (2.5) ; // III
___________________________________________________________________________

Sol
ution

a) La définition du constructe ur e s t analogue à ce lle q ue l'on aurait é crite "e n ligne " ;ilfaut sim plem e nt
"pré fixe r" son en-tê te d'une liste de param è tre s introduite par te m plate . De plus, ilfaut pré fixe r l'e n-tê te de
la fonction m e m bre du nom du patron accom pagné de ses param è tre s (bie n q ue ce la soit redondant) :

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 possédant 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 définition 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 définition pré cédente figure dans
pointcol.h :

#include <iostream.h>
#include "pointcol.h"

main()
{ pointcol <int, short int > p1 (5, 5, 2) ; p1.affiche () ;
pointcol <float, int> p2 (4, 6, 2) ; p2.affiche () ;
pointcol <double, unsigned short> p3 ; p3.affiche () ;
208 Exercices en C+ +

b) Ilsuffit q ue le type clas s e e n q ue s tion ait conve nablem e nt surdéfini l'opé rate ur < < , afin d'assure r
conve nablem e nt l'affich age s ur cout des inform ations corre s pondante s .

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 définition de notre patron pour q ue les instructions précédente s affich e nt
bien :

Coordonnees : 60 65

___________________________________________________________________________

Sol
ution

a) O n obtie nt l'affich age des caractè res de code 60 e t 65 (c'e s t-à -dire dans une im plém e ntation utilisant le
code A SCII : < et A) e t non les nom bre s 60 e t 65.

b) Ilfaut spécialiser notre patron point pour le cas où le type T e s t le type ch ar. Pour ce faire , on pe ut :

- soit fournir une définition com plète de point< ch ar> , ave c s e s fonctions m e m bre ,
- soit, puis q u'ici s e ule la fonction affich e e s t conce rné e , s e conte nte r de s urdé finir la fonction
point< ch ar> ::affich e , ce q ui conduit à ce tte nouve lle définition de notre patron :
// définition generale du patron point
template <class T> class point
{ T x, y ; // coordonnees
public :
point (T abs, T ord) { x = abs ; y = ord ; }
void affiche () ;
} ;
template <class T> void point<T>::affiche ()
XIV. Les patrons de cl
asses 209

{ cout << "Coordonnees : " << x << " " << y << "\n" ;
}
// version specialisee de la fonction affiche pour le type char
void point<char>::affiche ()
{ cout << "Coordonnees : " << (int)x << " " << (int)y << "\n" ;
}

R e m arque :

Bie n q ue ce la ne s oit pas totalem e nt form alisé, beaucoup d'im plém e ntations n'acce pte nt pas q ue l'on
spécialise une fonction m e m bre définie e n ligne ;c'e s t d'ailleurs pour ce tte raison q ue , dans notre
é noncé , nous avions défini affich e sous form e d'une fonction indé pe ndante .

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 é lém e nts de ce s ve cte urs
puis s e nt ê tre de type q ue lconq ue .

O n surdéfinira conve nablem e nt l'opé rate ur [] pour q u'ilpe rm e tte l'accè s aux é lém e nts du ve cte ur (aussi bien
e n consultation q u'e n m odification) e t on s'arrange ra pour q u'iln'e xiste aucun ris q ue de "débordem e nt
d'indice ". En re vanch e , on ne ch e rch e ra pas à ré gler les problèm e s pos é s é ve ntue llem e nt par l'affe ctation ou
la transm ission par valeur d'obje ts du type conce rné .

___________________________________________________________________________

Sol
ution

En gé né ralisant ce q ui a é té fait dans l'e xe rcice VII.7 (m ais sans toute fois initialiser les é lém e nts du ve cte ur
lors de sa construction), nous aboutissons au patron de clas s e s s uivant4 :

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 définition 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 é lém e nt situé e n de h ors du
ve cte ur conduis e à accéder à l'é lém e nt de rang 0. D ans la pratiq ue , on aura inté rê t à utiliser des prote ctions
plus é laboré e s .

A titre indicatif, voici un pe tit program m e utilisant ce patron (dont on suppose que la définition figure dans
ve ctge n.h :

#include <iostream.h>
#include "vectgen.h"
main()
{ vect<int> vi (10) ;
vi[5] = 5 ; vi[2] = 2 ;
cout << vi[2] << " " << vi[5] << "\n" ;
vect<double> vd (3) ;
vd[0] = 0.0 ; vd[1] = 0.1 ; vd[2] = 0.2 ;
cout << vd[0] << " " << vd[1] << " " << vd[2] << "\n" ;
cout << vd[12] ; vd[12] = 1.2 ; cout << vd[12] << " " << vd[0] ;
}

R e m arque :

Notre patron ve ct pe rm e t d'instancie r de s ve cte urs dynam iq ues dans les q ue ls les é lém e nts sont de type
absolum e nt q ue lconq ue , e n particulie r de type clas s e (pourvu q ue ladite classe dispose d'un constructe ur
sans argum e nt). Iln'e n s e rait pas allé ainsi si nous avions initialisé les é lém e nts du tableau lors de leur
construction par :
int i ;
for (i=0 ; i<nelem ; i++) adr[i] = 0 ;

En e ffe t, dans ce cas, ce s instructions auraie nt conve nablem e nt fonctionné pour n'im porte q ue ltype de
bas e (par conve rsion de l'e ntie r 0 dans le type voulu). En re vanch e , pour ê tre applicable à des élém e nts
de type clas s e , ilaurait fallu, e n outre , q ue la clas s e conce rnée dispose d'une conve rsion d'un int dans ce
type clas s e , c'e s t-à -dire d'un constructe ur à un argum e nt de type num é riq ue .

Exe rcice X IV.5

___________________________________________________________________________

Enoncé

Com m e dans l'e xe rcice pré cédent, ré aliser un patron de clas s e s pe rm e ttant de m anipuler de s ve cte urs dont
les é lém e nts sont de type q ue lconq ue m ais pour les q ue ls la dim e nsion, supposée ê tre ce tte fois une
e xpre s s ion constante , apparaî
tra com m e un param è tre (e xpre s s ion) du patron. H orm is ce tte diffé re nce , les
"fonctionnalité s " du patron re s te ront les m ê m e s .

___________________________________________________________________________
XIV. Les patrons de cl
asses 211

Sol
ution

Iln'e s t donc plus néce s s aire d'alloue r un e m place m e nt dynam iq ue pour notre ve cte ur q ui pe ut donc figure r
dire cte m e nt dans les m e m bres donnée de notre patron. Le constructe ur n'e s t plus néce s s aire (voir toute fois la
re m arq ue ci-de s s ous), pas plus q ue le destructe ur. Voici ce q ue pourrait ê tre la définition de notre patron5 :

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'élém e nts un m e m bre donnée de nos clas s e s patron :
e n e ffe t, lors q u'on e n a be s oin, on l'obtie nt com m e é tant la valeur du second param è tre fourni lors de
l'instanciation. Si nous avions voulu cons e rve r ce nom bre d'élém e nts sous form e d'un m e m bre donnée, il
aurait é té né ce s s aire de pré voir un constructe ur, par e xe m ple :
template <class T, int n> class vect
{ int nelem ; // nombre d'elements
T v [n] ; // vecteur de n elements de type T
public :
vect () ;
T & operator [] (int) ; // operateur d'acces a un element
} ;
template <class T, int n> vect<T,n>::vect ()
{ nelem = n ;
}

Exe rcice X IV.6

5. La définition 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 redéfinira conve nablem e nt les fonctions
m e m bre e n ré utilisant les fonctions m e m bre de la classe de bas e .

b) M ê m e q ue s tion, m ais e n pré voyant q ue les coordonné e s e t la couleur puis s e nt ê tre de deux type s
diffé re nts.

c) Toujours par dé rivation, cré e r ce tte fois une "clas s e ordinaire " (c'e s t-à -dire une clas s e q ui ne s oit plus un
patron de clas s e s , autre m e nt dit q ui ne dépe nde plus de param è tre s ...) dans laq ue lle les coordonné e s s ont de
type int, tandis q ue la couleur e s t de type sh ort.

___________________________________________________________________________

Sol
ution

a) Aucun problèm e particulie r ne s e pos e ;ilsuffit de faire dérive r pointcol< T> de point< T> . Voici ce
q ue pe ut ê tre la définition de notre patron (ici, nous avons lais s é le constructe ur "e n ligne " m ais nous avons
défini affich e e n de h ors de la clas s e ) :

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 déclarations approprié e s ou l'incorporation de fich ie rs .h
corre s pondants) :

main()
{ pointcol <int> p1 (2, 5, 1) ; p1.affiche () ;
pointcol <float> p2 (2.5, 5.25, 4) ; p2.affiche () ;
}
XIV. Les patrons de cl
asses 213

b) Ce tte fois, la classe dérivé e d é pe nd de deux param è tre s (nom m é s ici T e t U). Voici ce q ue pourrait ê tre la
définition de notre patron (ave c, toujours, un constructe ur e n ligne e t une fonction affich e définie à
l'e xté rie ur de la clas s e ) :

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 déclarations approprié e s ) :

main()
{ pointcol <int, short> p1 (2, 5, 1) ; p1.affiche () ;
pointcol <float, int> p2 (2.5, 5.25, 4) ; p2.affiche () ;
}

c) Ce tte fois, pointcole s t une s im ple clas s e , ne dépe ndant plus d'aucun param è tre . Voici ce q ue pourrait ê tre
sa définition :

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é cédem 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 spécialisée de affich e pour le cas ou T e s t le type caractè re .

b) Com m e dans la q ue s tion a de l'e xe rcice pré cédent, cré e r un patron de clas s e s pointcolpe rm e ttant de
m anipuler de s "points coloré s " dans les q ue ls les coordonné e s e t la couleur sont de m ê m e type . O n redéfinira
conve nablem e nt les fonctions m e m bre e n ré utilisant les fonctions m e m bre de la classe de bas e e t l'on
pré voira une ve rsion spécialisée de affich e de pointcoldans le cas du type caractè re .

___________________________________________________________________________

Sol
ution

a) Pour dé finir une ve rsion spécialisée d'une fonction m e m bre , ile s t gé né ralem e nt né ce s s aire q ue ce tte
fonction m e m bre s oit définie e xté rie ure m e nt au patron de clas s e s (re voye z la re m arq ue de l'e xe rcice XIV.3).
Voici ce q ue pourrait ê tre la nouve lle définition (com plète ) de notre patron point :

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 précédem m e nt, la fonction affich e de pointcoldoit ê tre définie à l'e xté rie ur
du patron. Voici ce q ue pourrait ê tre ce tte définition :

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 dérivé e
dépendant de 2 param è tre s T e t U. Ilfaudrait alors pouvoir spécialiser une clas s e , non plus pour de s
valeurs données de tous les (deux) param è tre s , m ais pour une valeur donné e (ch ar) de l'un d'e ntre e ux ;
ce tte notion de fam ille de spé cialisation n'e s t pas possible, du m oins dans l'é tat actue lde la définition de
C+ + .

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 , définis par leur ce ntre (de
type point) e t un rayon. O n n'y pré voira, com m e fonctions m e m bre , q u'un constructe ur e t une fonction
affich e s e conte ntant d'affich e r les coordonnées du ce ntre e t la valeur du rayon.

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 dém arch e e s t analogue à ce lle de la q ue s tion a de l'e xe rcice XIV.6. O n a affaire à un patron dé pe ndant
de deux param è tre s (ici T e t U).

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 définition du constructe ur ce rcle, nous avons transm is les argum e nts abs e t ord à un
constructe ur de point pour le m e m bre c. Nous aurions pu utiliser la m ê m e notation pour r, bie n q ue ce
m e m bre s oit d'un type de bas e e t non d'un type clas s e ;ce la nous aurait conduit au constructe ur suivant (de
corps vide) :

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 désigne un élém e nt de type s e t_int e t n un e ntie r :

< < , te lq ue e < < n ajoute l'é lém 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'élém e nts de l'e ns e m ble. Enfin, on s'arrange ra pour q ue
l'affe ctation ou la transm ission par valeur d'obje ts de type s e t_int ne pos e aucun problèm e (on acce pte ra la
duplication com plète d'obje ts).

_______________________________________________________________

Sol
ution

Nature llem e nt, notre clas s e com porte ra, e n m e m bres donnée, le nom bre m axim al(nm ax) d'élém e nts de
l'e ns e m ble, le nom bre courant d'élém e nts (ne le m ) e t un pointe ur sur l'e m place m e nt conte nant les valeurs de
l'e ns e m ble.

Com pte te nu de ce q ue notre clas s e com porte une partie dynam iq ue , ile s t né ce s s aire , pour q ue l'affe ctation
e t la transm ission par valeur se déroulent conve nablem e nt, de surdéfinir l'opé rate ur d'affe ctation e t de m unir
notre classe d'un constructe ur par re copie . Les deux fonctions m e m bre (ope rator = e t s e t_int ) devront
pré voir une "copie profonde " des obje ts. Nous utiliserons pour cela une m é th ode q ue nous avons déjà
re ncontré e e t q ui consiste à considérer que deux obje ts diffé re nts disposent systé m atiq ue m e nt de deux partie s
dynam iq ues diffé re nte s , m ê m e s i e lles possè dent le m ê m e conte nu.

L'opé rate ur % doit ê tre s urdé fini obligatoire m e nt sous form e d'une fonction am ie , puis q ue s on pre m ie r
opé rande n'e s t pas de type clas s e . L'opé rate ur de s ortie dans un flot doit, lui aussi, ê tre s urdé fini sous form e
d'une fonction am ie , m ais pour une raison diffé re nte : son pre m ie r argum e nt e s t de type ostre am .
226 Exercices de C+ +

Voici la déclaration de notre clas s e s e t_int :

/* fichier SETINT.H */
/* déclaration de la classe set_int */
class set_int
{
int * adval ; // adresse du tableau des valeurs
int nmax ; // nombre maxi d'éléments
int nelem ; // nombre courant d'éléments
public :
set_int (int = 20) ; // constructeur
set_int (set_int &) ; // constructeur par recopie
set_int & operator = (set_int &) ; // opérateur d'affectation
~set_int () ; // destructeur
int cardinal () ; // cardinal de l'ensemble
set_int & operator << (int) ; // ajout d'un élément
friend int operator % (int, set_int &) ; // appartenance d'un élément
// envoi ensemble dans un flot
friend ostream & operator << (ostream &, set_int &) ;
} ;

Voici ce q ue pourrait ê tre la définition de notre clas s e (les points délicats sont com m e nté s au s e in m ê m e des
instructions) :

#include <iostream.h>
#include "setint.h"

/*************** 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 ; // libération 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] ;
}

/************ opérateur d'affectation ************/


set_int & set_int::operator = (set_int & e)
// surdéfinition de l'affectation - les commentaires correspondent à b = a
{ if (this != &e) // on ne fait rien pour a = a
{ delete adval ; // libération 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 ; // entièrement 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 ;
}

/************ opérateur d'ajout << ***************/


set_int & set_int::operator << (int nb)
{ // on examine si nb appartient déjà à l'ensemble
// en utilisant l'opérateur %
// 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) ;
}

/*********** opérateur d'appartenance % **********/


int operator % (int nb, set_int & e)
{ int i=0 ;
// on examine si nb appartient déjà à 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) ;
}

/****** opérateur << 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 é lém e nt (ope rator < < ). Ilsuffirait, e n e ffe t, q u'e lle pré voie , lors q ue la lim ite e s t atte inte , d'alloue r
un nouve le m place m e nt dynam iq ue , par e xe m ple d'une taille double de l'e m place m e nt e xistant, d'y re copie r
l'actue lconte nu e t de libérer l'ancie n e m place m e nt (e n actualisant conve nablem e nt les m e m bres donnée de
l'obje t).

Voici un e xe m ple de program m e utilisant la clas s e s e t_int, accom pagné du résultat fourni par son exécution :

/************* 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 différents\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 reçu par fct : " << e << "\n" ;
e << -1 << -2 << -3 ;
}

void fctref (set_int & e)


{ cout << "ensemble reçu 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 différents
qui forment l'ensemble : [ 3 5 1 8 7 ]
ensemble reçu 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 reçu 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 définie au m om e nt de leur cré ation (une te lle clas s e a déjà é té
partie llem e nt ré alisée dans l'e xe rcice VII.7). Ce tte classe devra disposer des opérate urs suivants :

[] pour l'accè s à une des com posantes du ve cte ur, e t ce la aussi bien au s e in d'une e xpre s s ion q u'à gauch e
d'une affe ctation (m ais ce tte derniè re s ituation ne devra pas ê tre autoris é e s ur de s "ve cte urs constants"),
==, te lq ue s i v1 e t v2 sont deux obje ts de type ve ct, v1==v2 pre nne la valeur 1 si v1 e t v2 sont de
m ê m e dim e nsion et ont les m ê m e s com posante s e t la valeur 0 dans le cas contraire ,
< < , te lq ue flot< < v e nvoie le ve cte ur v sur le flot indiq ué , sous la form e :
<entier1, entier2, ... , entiern>

D e plus, on s'arrange ra pour q ue l'affe ctation e t la transm ission par valeur d'obje ts de type ve ct ne pos e
aucun problèm e ;pour ce faire , on acce pte ra de dupliq ue r com plète m e nt les obje ts conce rné s .

_______________________________________________________________

Sol
ution

R appe lons q ue lors q ue l'on dé finit des obje ts constants, iln'e s t pas possible de leur appliq ue r une fonction
m e m bre publiq ue , sauf si ce tte derniè re a é té déclaré e ave c le q ualificatif const (auq ue lcas, une te lle
fonction pe ut indiffé re m m e nt ê tre utilisée ave c des obje ts constants ou non constants). Pour obte nir l'e ffe t
dem andé de l'opé rate ur [], lors q u'ile s t appliq ué à un ve cte ur constant, ile s t né ce s s aire d'en prévoir de ux
définitions dont l'une s 'appliq ue aux ve cte urs constants ;pour é vite r q u'on ne puis s e , dans ce cas, l'utiliser à
gauch e d'une affe ctation, ile s t né ce s s aire q u'e lle re nvoie s on ré s ultat par valeur (e t non par adre s s e com m e
le fe ra la fonction applicable aux ve cte urs non constants).

Voici la déclaration 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) ; // surdéfinition opérateur affectation
int & operator [] (int i) ; // surdef [] pour vect non constants
int operator [] (int i) const ; // surdef [] pour vect constants
} ;

Voici la définition 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) // surdéfinition opérateur 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) // surdéfinition opérateur []


{ return adr[i] ;
}

int vect::operator [] (int i) const // surdéfinition opérateur [] 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 problèm 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 donnée, on
trouve ra donc un pointe ur (ch ar *) sur un te le m place m e nt. O n pe ut se dem ande r s'ile s t né ce s s aire de
cons e rve r un caractè re de fin de ch aî ne dans l'e m place m e nt dynam iq ue e n q ue s tion. En fait, à partir du
m om e nt où l'on dé cide de cons e rve r la longue ur (courante ) d'une ch aî ne dans un m e m bre donnée, ce
caractè re de fin n'e s t plus indispensable ;né anm oins, sa pré s e nce facilite les traite m e nts à opé re r sur les
caractè res de la ch aî ne , dans la m e s ure où ilre s te alors possible de faire appe laux fonctions classiques du
langage C . 1

En ce q ui conce rne les constructe urs du type string, on pe ut nature llem e nt pré voir un constructe ur sans
argum e nt q ui initialise la ch aî
ne corre s pondante à une ch aî
ne vide. Ilfaut é galem e nt pré voir un constructe ur
par re copie .

M ais on pe ut é galem e nt ajoute r un constructe ur q ui fabriq ue une ch aî ne , à partir d'une ch aîne classique
(ch ar *) re çue e n argum e nt. En e ffe t, dans ce cas, on disposera alors d'une conve rsion ch ar *--> string q ui
pe rm e ttra de donner un sens à la concaté nation d'un obje t de type string e t d'une ch aî ne C classique.
Toute fois, ce la ne s e ra possible pour une e xpre s s ion de la form e "h e llo" + ch q ue s i l'opé rate ur + a é té
surdéfini sous form e d'une fonction am ie (son pre m ie r opé rande pourra alors ê tre s oum is à d'éve ntue lles
conve rsions) e t non pas sous form e d'une fonction m e m bre .

1 Ce q ui ne sera peut-ê tre pas l


e cas si l'on vise une grande efficacité en tem ps d'exécution.
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 déclaration de notre clas s e string :

/* fichier chaine.h : déclaration de la classe chaine */


#include <iostream.h>
#include <string.h>
class string
{ int lg ; // longueur actuelle de la chaîne
char * adr ; // adresse zone contenant la chaîne
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 définition des diffé re nte s fonctions :

/* définitions 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 chaîne 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 prévu de
} // vérification de la valeur de i

/* définition 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 résultat de son exécution :

/* programme d'essai */
main()
{ string a ;
cout << "chaine a : " << a << " de longueur " << a.length() << "\n" ;
string b("bonjour") ;
cout << "chaine b : " << b << "\n" ;
string c=b ;
cout << "chaine c : " << c << "\n" ;
string d("hello") ;
a = b = d ;
cout << "chaine b : " << b << " de longueur " << b.length() << "\n" ;
cout << "chaine a : " << a << "\n" ;
cout << "a == b : " << (a == b) << "\n" ;
string x("salut "), y("chère "), z("madame");
a = x + y + z ;
cout << "chaine a : " << a << "\n" ;
a = a ;
cout << "chaine a : " << a << "\n" ;
a = a + " - Comment allez-vous ?\n" ;
cout << "chaine a : " << a ;
a = "*****" + a ;
cout << "chaine a : " << a ;
// a = "bon" + "jour" ; // serait rejeté en compilation
string e("xxxxxxxxxx") ;
for (char cr='a', i=0 ; cr<'f' ; cr++, i++ ) e[i] = cr ;
cout << "chaine e : " << e << "\n" ;
}
___________________________
234 Exercices de C+ +

chaine a : de longueur 0
chaine b : bonjour
chaine c : bonjour
chaine b : hello de longueur 5
chaine a : hello
a == b : 1
chaine a : salut chère madame
chaine a : salut chère madame
chaine a : salut chère madame - Comment allez-vous ?
chaine a : *****salut chère madame - Comment allez-vous ?
chaine e : abcdexxxxx

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 é lém e nt ne pe ut pre ndre q ue l'une des deux valeurs 0 ou 1. La taille d'un
tableau (c'e s t-à -dire le nom bre de bits) s e ra dé finie lors de sa cré ation (par un argum e nt pas s é à son
constructe ur). O n pré voira les opé rate urs suivants :

+ =, 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
problèm e . De plus, les opé rate urs m odifiant le conte nu d'un tableau ne devront pas pouvoir ê tre appliq ué s à
des obje ts constants (attribut const).

_______________________________________________________________

Sol
ution

Si l'on ch e rch e à m inim iser l'e m place m e nt m é m oire utilisé pour les obje ts de type bit_array, il e s t
né ce s s aire de n'e m ploye r q u'un s e ulbit pour re pré s e nte r un "é lém e nt" d'un tableau. Ces bits devront donc
ê tre re groupé s , par e xe m ple à raison de CH AR _BIT (défini dans lim its.h ) bits par caractè re .

M anife s te m e nt, ilfaut pré voir q ue l'e m place m e nt destiné à ces diffé re nts bits soit alloué dynam iq ue m e nt e n
fonction de la valeur fournie au constructe ur : pour n bits, ilfaudra n/CH A R _BIT+ 1 caractè re s .

En m e m bre donnée, ilnous suffit de disposer d'un pointe ur sur l'e m place m e nt dynam iq ue e n q ue s tion, ainsi
q ue du nom bre de bits du tableau. Pour sim plifie r ce rtaines des fonctions m e m bre , nous prévoirons
é galem e nt de cons e rve r le nom bre de caractè re s corre s pondant.
XV. Exercices de synth è se 235

Le s opé rate urs + =, -=, + + e t -- pe uve nt ê tre définis indiffé re m m e nt sous form e d'une fonction m e m bre
ou d'une fonction am ie . Ici, nous avons ch oisi des fonctions m e m bre .

L'é noncé ne pré cis e rie n q uant au ré s ultat fourni par ce s 4 opé rate urs. En fait, on pourrait pré voir q u'ils
re s titue nt le tableau aprè s q u'ils y ont e ffe ctué l'opé ration voulue , m ais, e n pratiq ue , ce la s e m ble de pe u
d'inté rê t. Ici, nous avons donc sim plem e nt pré vu q ue ce s opé rate urs ne fourniraie nt aucun ré s ultat.

Pour q ue [] ne s oit pas utilisable dans une affe ctation de la form e t[i] = ..., ilsuffit de prévoir q u'il
fournis s e s on ré s ultat par valeur (e t non par ré fé re nce com m e on a gé né ralem e nt l'h abitude de le faire ).

Le s opé rate urs + =, -=, + + e t -- m odifie nt la valeur de leur pre m ie r opé rande . Ils ne doive nt donc pas
pouvoir agir sur un obje t constant. En re vanch e , les autre s , c'e s t-à -dire [] (ici) e t < < , pe uve nt agir sans
ris q ue s ur un obje t constant ;pour q u'ils puissent y ê tre autoris é s , nous leur donne rons l'attribut const.

Nature llem e nt, ici e ncore , l'é noncé nous im pose de surdé finir l'opé rate ur d'affe ctation e t de prévoir un
constructe ur par re copie .

Voici ce q ue pourrait ê tre la déclaration de notre clas s e bit_array :

/* fichier bitarray.h : déclaration de la classe bit_array */


#include <iostream.h>
class bit_array
{ int nbits ; // nombre courant de bits du tableau
int ncar ; // nombre de caractères nécessaires (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 opérateurs 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) ; // désactivation d'un bit
// envoi sur flot
friend ostream & operator << (ostream &, bit_array &) const ;
// les opérateurs unaires
void operator ++ () ; // mise à 1
void operator -- () ; // mise à 0
void operator ~ () ; // complément à 1
} ;

Voici la définition des diffé re nte s fonctions.

/* définition 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 considérant le bit
// de rang i % CHAR_BIT du caractère 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 résultat fourni par son exécution :

/* 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 aîne de caractè re s , corre s pondant
aux ch iffres d'un "grand e ntie r".

O n fe ra e n sorte q ue l'affe ctation e t la transm ission par valeur d'obje ts de type big_int ne pos e aucun
problèm e .

_______________________________________________________________

Sol
ution

Pour re pré s e nte r un "grand e ntie r", la dém arch e la plus nature lle (m ais pas la plus é conom iq ue e n place
m é m oire !) consiste à cons e rve r le nom bre s ous form e décim ale, à ch aq ue ch iffre é tant associé un caractè re .
Pour ce faire , on pe ut ch oisir de "coder" un te lch iffre par le caractè re corre s pondant ('0' pour 0, '1' pour 1,
...) ;on pe ut aussi ch oisir de place r une valeur é gale au ch iffre lui-m ê m e (0 pour 0, 1 pour 1, ...). La
derniè re s olution oblige à e ffe cte ur un "transcodage " lors q ue l'on doit pas s e r de la form e ch aî ne de caratè re s
à la form e big_int (dans le constructe ur corre s pondant, notam m e nt) ou, inve rs e m e nt, lors q ue l'on doit pas s e r
de la form e big_int à la form e s uite de caractè re s (pour l'affich age ). En re vanch e , e lle s im plifie q ue lque pe u
l'algorith m e d'addition, e t c'e s t e lle q ue nous avons ch oisie.

L'e m place m e nt pe rm e ttant de cons e rve r un grand e ntie r s e ra alloué dynam iq ue m e nt ;sa taille s e ra,
nature llem e nt, adapté e à la valeur du nom bre q ui s'y trouve ra. O n cons e rve ra é galem e nt le nom bre courant
de ch iffres de l'e ntie r ;on pourrait, e n toute rigue ur, s'e n pas s s e r m ais nous ve rrons q ue s a pré s e nce
sim plifie q ue lque pe u la program m ation. En ce q ui conce rne l'ordre de range m e nt des ch iffre s au s e in de
l'e m place m e nt corre s pondant, ily a m anife s te m e nt deux possibilité s . Ch acune possè de des avantage s e t des
inconvé nie nts ;nous avons ici ch oisi de ranger les ch iffres dans l'ordre inve rse de ce lui où on les é crit
(unité s , dizaine s , ce ntaine s ...).

Pour pouvoir acce pte r les e xpre s s ions m ixte s , on dispose de plusieurs solutions :

- soit surdéfinir l'opé rate ur + pour tous les cas possibles ,


- soit surdéfinir + 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'exécution.

Par ailleurs, pour q ue les conve rsions envisagé e s s 'appliq ue nt au pre m ie r opé rande de l'addition, ile s t
né ce s s aire de surdé finir l'opé rate ur + com m e une fonction am ie .

Voci la déclaration de notre clas s e big_int (la pré s e nce d'un constructe ur privé à deux argum e nts e ntie rs s e ra
justifié e un pe u plus loin) :

/* fichier bigint.h : déclaration de la classe big_int */


#define NCHIFMAX 32 // nombre maxi de chiffres d'un entier (dépend de
// l'implémentation
#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 chaîne
big_int (big_int &) ; // constructeur par recopie
big_int & operator = (big_int &) ; // affectation
friend big_int operator + (big_int &, big_int &) ; // opérateur +
XV. Exercices de synth è se 239

friend ostream & operator << (ostream &, big_int &) ; // opérateur <<
} ;

L'opé rate ur + com m e nce par cré e r un e m place m e nt te m poraire pouvant re ce voir un nom bre com portant un
ch iffre de plus q ue le plus grand de ses deux opé rande s (on ne s ait pas e ncore com bien de ch iffre s
com porte ra e xacte m e nt le ré s ultat). O n y calcule la som m e s uivant un algorith m e calqué s ur le proce s s us
m anue ld'addition.

Puis on cré e un obje t de type big_int e n utilisant un constructe ur particulie r : big_int (int, int). En fait, nous
avons besoin d'un constructe ur cré ant un big_int com portant un nom bre de ch iffres donné, ce dont nous ne
disposons pas dans les constructe urs publics. De plus, nous ne pouvons pas utiliser un constructe ur de la
form e big_int (int) car, alors, les additions m ixte s faisant inte rve nir de s e ntie rs ch e rch e raie nt à l'e m ploye r
pour e ffe ctue r une conve rsion! C'e s t pourq uoi nous avons prévu un constructe ur à deux argum e nts, le s e cond
é tant fictif ;de plus, nous l'avons re ndu privé , dans la m e s ure où iln'a nullem e nt besoin d'ê tre acce s s ible à
un utilisate ur de la clas s e .

Voici la définition de s fonctions de la clas s e big_int

/* définition 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 ; // précaution
adchif[nchif-i-1] = c ; // attention à l'ordre des chiffres !
}
}

big_int::big_int (unsigned long n)


{ // on crée 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 libère 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 ;
// préparation du résultat 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 résultat
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 résultat fourni par son
e xé cution :
XV. Exercices de synth è se 241

/* programme d'essai */
#include <iostream.h>
#include "bigint.h"
main()
{ big_int n1=12 ;
big_int n2(35) ;
big_int n3 ;
n3 = n1 + n2 ;
cout << n1 << " + " << n2 << " = " << n3 << "\n" ;
big_int n4 ("1234567890123456789"), n5("9876543210987654321"), n6 ;
n6 = n4 + n5 ;
cout << n4 << " + " << n5 << " = " << n6 << "\n" ;
cout << n6 << " + " << n1 << " = " << n6 + n1 << "\n" ;
}
_______________________

12 + 35 = 47
1234567890123456789 + 9876543210987654321 = 11111111101111111110
11111111101111111110 + 12 = 11111111101111111122

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 é lém e nts sont de type
q ue lconq ue . Ces dernie rs s e ront cons e rvés dans un em place m e nt alloué dynam iq ue m e nt e t dont la dim e nsion
s e ra fournie au constructe ur (ilne s 'agira donc pas d'un param è tre e xpre s s ion du patron). La classe devra
com porte r les opé rate urs suivants :

< < , te lq ue p<<n ajoute l'é lém e nt n à la pile p (si la pile e s t pleine , ilne s e pas s e ra rie n),
> > , te lq ue p> > n place dans n la valeur du h aut de la pile p, e n la supprim ant de la pile (si la pile e s t
vide, ilne s e pas s e ra rie n),
+ + , te lq ue + + p vale 1 si la pile p e s t pleine e t 0 dans le cas contraire ,
--, te lq ue --p vale 1 si la pile p e s t vide et 0 dans le cas contraire ,
< < , te lq ue , flot é tant un flot de sortie , flot < < p affich e le conte nu de la pile p
sur le flot sous la form e : // valeur_1 valeur_2... valeur_n //.
O n supposera q ue les obje ts de type stack ne s e ront jam ais soum is à des transm issions par valeur ou à des
affe ctations ;on ne ch e rch e ra donc pas à surdéfinir le constructe ur par re copie ou l'opé rate ur d'affe ctation.

___________________________________________________________________________

Sol
ution

En fait, on pe ut s'inspire r de ce q ui a é té fait dans l'e xe rcice VII.10 pour ré aliser une pile d'entie rs e n
faisant e n sorte q ue int soit re m placé par un param è tre de type .

Voici ce q ue pourrait ê tre la définition 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) ; // opérateur d'empilage
stack & operator >> (T &) ; // opérateur de dépilage (attention T &)
int operator ++ () ; // opérateur de test pile pleine
int operator -- () ; // opérateur de test pile vide
// opérateur << 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
définition 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 é lém e nts sont de type q ue lconq ue .

___________________________________________________________________________

Sol
ution

Voici la déclaration 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) ; // surdéfinition opérateur affectation
T & operator [] (int i) ; // surdef [] pour vect non constants
T operator [] (int i) const ; // surdef [] pour vect constants
} ;

Voici la définition 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
}

Vous aimerez peut-être aussi