Vous êtes sur la page 1sur 6

Programmation en langage C++

Exemples
par Claude DELANNOY
Ingnieur de lENSEM (cole nationale suprieure dlectricit
et de mcanique) de Nancy
Ingnieur informaticien au CNRS (Centre national de la recherche scientifique)

Concepts ............................................................................................................. S 8 065


1. Exemple 1 : ensembles dentiers ......................................................... S 8 066 2
1.1 Premire bauche de solution.................................................................... 2
1.2 Surdfinition du constructeur par recopie ................................................ 2
1.3 Surdfinition de laffectation ...................................................................... 2
2. Exemple 2 : utilisation dun patron de classes................................ 4
3. Exemple 3 : liste htrogne ................................................................ 5

ous proposons quelques exemples de programmes illustrant la plupart des


N fonctionnalits de C++ que nous venons de prsenter dans larticle [S 8 065].

Toute reproduction sans autorisation du Centre franais dexploitation du droit de copie est strictement interdite.
Techniques de lIngnieur, trait Informatique industrielle S 8 066 1
PROGRAMMATION EN LANGAGE C++ ______________________________________________________________________________________________________

1. Exemple 1 : vera librer deux fois le mme emplacement. Les consquences


dpendent de limplmentation et peuvent tre catastrophiques.
ensembles dentiers Pour rgler ce problme, la solution la plus raisonnable consiste
dfinir son propre constructeur de recopie en faisant en sorte quil
recopie la partie dynamique de lobjet dans un emplacement quil
Nous allons raliser une classe nomme set_int permettant de crera. Rappelons que son en-tte doit tre de la forme :
manipuler des ensembles de nombres entiers. Nous prvoirons les
oprateurs suivants (e dsigne un lment de type set_int et n un set_int (set_int & e)
entier) :
<<, tel que e<<n ajoute l'lment n l'ensemble e ;
Remarques
%, tel que n % e vale 1 si n appartient e et 0 sinon ;
<<, tel que flot << e envoie le contenu de l'ensemble e sur le 1 Les mmes problmes se posent pour une fonction ren-
flot indiqu, sous la forme : voyant une valeur de type set_int. Ils se trouvent rgls conjointe-
ment aux prcdents par la dfinition du constructeur par recopie.
[entier_1, entier_2, ... entier_n]
2 Aucun problme ne se pose pour les arguments de type
La fonction membre cardinal fournira le nombre d'lments de set_int transmis par rfrence puisqualors la fonction appele
l'ensemble. travaille directement sur lobjet correspondant largument
Pour ne pas alourdir lexemple, le nombre maximal d'entiers que effectif de lappel.
pourra contenir l'ensemble sera prcis au constructeur qui allouera
dynamiquement l'espace ncessaire.

1.3 Surdfinition de laffectation


1.1 Premire bauche de solution
Naturellement, notre classe comportera, en membres donnes, le Laffectation pose des problmes voisins des prcdents. Considrons
nombre maximal (nmax) d'lments de l'ensemble, le nombre cou- deux objets e1 et e2 de type set_int et une simple instruction telle que :
rant d'lments (nelem) et un pointeur sur l'emplacement conte- e1 = e2 ;
nant les valeurs de l'ensemble.
Aprs son excution, les deux champs adval des deux objets
Loprateur << devra tre surdfini de manire disposer dun
pointeront sur la mme partie dynamique. Or, lorsque les destruc-
premier oprande de type set_int et dun second oprande entier.
teurs des objets e1 et e2 seront appels (quel quen soit lordre), on
Comme le premier oprande est du type de la classe, nous pouvons
se trouvera librer, l aussi, deux fois le mme emplacement. Quant
le dfinir sous forme dune fonction membre (de nom operator<<).
lancien emplacement associ e2, il se peut quil ne soit plus uti-
En revanche, l'oprateur % doit tre surdfini obligatoirement lis ; nanmoins, il ne sera jamais libr.
sous la forme d'une fonction amie, puisque son premier oprande
Pour rgler ces problmes, la solution la plus raisonnable
n'est pas de type classe. L'oprateur de sortie dans un flot doit, lui
consiste surdfinir convenablement loprateur daffectation de
aussi, tre surdfini sous la forme d'une fonction amie, mais pour
faon compatible avec la dmarche utilise pour le constructeur de
une raison diffrente : son premier argument est dun type classe
recopie. Ici, donc, nous ferons en sorte que chaque objet de type
(ostream) dj dfini et que lon ne peut pas modifier.
set_int dispose de sa propre partie dynamique.
Voici une premire bauche de ce que pourrait tre la dclaration
On notera que si le problme est voisin de celui de la construction
de notre classe set_int :
par recopie, il nen est pas pour autant identique. Quelques diffren-
#include <iostream> ces apparaissent :
using namespace std ; on peut se trouver en prsence d'une affectation d'un objet
class set_int lui-mme ;
{ int * adval ; // adresse du tableau des valeurs avant affectation, il existe deux objets complets (c'est--
int nmax ; // nombre maxi d'lments dire avec leur partie dynamique). Dans le cas de la construction par
int nelem ; // nombre courant d'lments recopie, il n'existait qu'un seul emplacement dynamique, le second
public : tant crer. On va donc se retrouver ici avec l'ancien emplacement
dynamique de b. Or, sil n'est plus rfrenc par b, est-on sr qu'il
set_int (int = 20) ; // constructeur
n'est pas rfrenc par ailleurs ?
~set_int () ; // destructeur
int cardinal () ; // cardinal de l'ensemble Voici donc comment nous pourrions traiter une affectation telle
set_int & operator << (int) ; // ajout d'un lment que e1 = e2, lorsque e1 est diffrent de e2 :
friend int operator % (int, set_int &) ; libration de l'emplacement point par e2 ;
// appartenance d'un lment cration dynamique d'un nouvel emplacement dans lequel on
// envoi ensemble dans un flot recopie les valeurs de l'emplacement point par e1 ;
mise en place des valeurs des membres donnes de e2.
friend ostream & operator << (ostream &, set_int &) ;
}; Supposons quavant affectation, la situation se prsente ainsi :

1.2 Surdfinition du constructeur par recopie 1


5
e1 8
Lorsquun argument de type set_int est transmis en argument 3
une fonction, il y appel du constructeur de recopie par dfaut. Ce der- 100 4
nier recopie les diffrents champs de lobjet dans un objet local la 5
fonction, y compris lorsque ceux-ci sont des pointeurs. Toutefois,
dans ce dernier cas, lemplacement point nest nullement recopi. 6
Dans ces conditions, on se trouve en prsence dun mme empla- 200 0
3 4
cement dynamique dsign par deux objets diffrents. Lors de la
destruction de ces deux objets (quel quen soit lordre), on se trou- e2

Toute reproduction sans autorisation du Centre franais dexploitation du droit de copie est strictement interdite.
S 8 066 2 Techniques de lIngnieur, trait Informatique industrielle
______________________________________________________________________________________________________ PROGRAMMATION EN LANGAGE C++

Aprs laffectation, on aboutit : Voici ce que pourrait tre la dfinition de notre classe (les points
dlicats sont comments au sein mme des instructions).
1
5
e1 8
3 Dfinition de la classe set_int
100 4 #include "setint.h"
5 #include <iostream>
using namespace std ;
1 /*************** constructeur ********************/
100 5 set_int::set_int (int dim)
5 8 { adval = new int [nmax = dim] ; // allocation tableau
e2 3 // de valeurs
4 nelem = 0 ;
}
/****************** destructeur ******************/
Il reste rgler le cas o e1 et e2 correspondent au mme objet.
set_int::~set_int ()
Or, la surdfinition de loprateur daffectation peut se faire indif- { delete adval ; // libration tableau
fremment avec lun des deux en-ttes suivants : // de valeurs
operator = (set_int & e) }
operator = (set_int e) /********** constructeur par recopie *************/
set_int::set_int (set_int & e)
Si la transmission de e1 loprateur daffectation a lieu par { adval = new int [nmax = e.nmax] ;// allocation nouveau tableau
valeur et si le constructeur par recopie a t redfini de faon appro- nelem = e.nelem ;
prie (par cration dun nouvel emplacement dynamique), lalgo- int i ;
rithme propos fonctionnera sans problme. for (i=0 ; i<nelem ; i++)// copie ancien tableau dans nouveau
En revanche, si la transmission de e1 a lieu par rfrence, on abor- adval[i] = e.adval[i] ;
dera lalgorithme avec cette situation : }
/************ oprateur d'affectation ************/
set_int & set_int::operator = (set_int & e) // commentaires faits
1 // pour b = a
5 { if (this != &e) // on ne fait rien pour a = a
8 { delete adval ; // libration partie dynamique de b
100
3 adval = new int [nmax = e.nmax] ; // allocation nouvel
5
4 // ensemble pour a
e1 et e2
nelem = e.nelem ; // dans lequel on recopie
int i ; // entirement l'ensemble b
Lemplacement dynamique associ b (donc aussi a) sera libr for (i=0 ; i<nelem ; i++) // avec sa partie dynamique
avant que lon tente de lutiliser pour le recopier dans un nouvel adval[i] = e.adval[i] ;
emplacement. La situation sera alors catastrophique. }
Enfin, il faut dcider de la valeur de retour fournie par loprateur. return * this ;
ce niveau, tout dpend de l'usage que nous souhaitons en faire : }
si nous nous contentons d'affectations simples (e2 = e1), nous /************ fonction membre cardinal ***********/
n'avons besoin d'aucune valeur de retour (void) ; int set_int::cardinal ()
en revanche, si nous souhaitons pouvoir traiter une affectation { return nelem ;
multiple ou, plus gnralement, faire en sorte que (comme on peut s'y }
/************ oprateur d'ajout << ***************/
attendre !) l'expression e2 = e1 ait une valeur (probablement celle de
set_int & set_int::operator << (int nb)
e2 !), il est ncessaire que loprateur fournisse une valeur de retour.
{ // on examine si nb appartient dj l'ensemble
Nous choisirons ici la seconde possibilit qui a le mrite dtre // en utilisant l'oprateur %
plus gnrale. // s'il n'y appartient pas, et s'il y a encore de la place
Voici la dclaration dfinitive de notre classe set_int (suppose // on l'ajoute l'ensemble
place ici dans un fichier nomm setint.h) : if ( ! (nb % *this) && nelem < nmax ) adval [nelem++] = nb ;
return (*this) ;
}
Dclaration de la classe set_in /*********** oprateur d'appartenance % **********/
/* fichier SETINT.H : dclaration de la classe set_int */ int operator % (int nb, set_int & e)
#include <iostream> { int i=0 ;
using namespace std ; // on examine si nb appartient dj l'ensemble
class set_int
{ int * adval ; // adresse du tableau // (dans ce cas i vaudra nele en fin de boucle)
// des valeurs while ( (i<e.nelem) && (e.adval[i] != nb) ) i++ ;
int nmax ; // nombre maxi d'lments return (i<e.nelem) ;
int nelem ; // nombre courant d'lments }
public :
set_int (int = 20) ; // constructeur /****** oprateur << pour sortie sur un flot *****/
set_int (set_int &) ; // constructeur par recopie ostream & operator << (ostream & sortie, set_int & e)
set_int & operator = (set_int &) ; // oprateur d'affectation { sortie << "[ " ;
~set_int () ; // destructeur int i ;
int cardinal () ; // cardinal de l'ensemble
set_int & operator << (int) ; // ajout d'un lment for (i=0 ; i<e.nelem ; i++)
friend int operator % (int, set_int &) ; sortie << e.adval[i] << " " ;
// appartenance d'un lment sortie << "]" ;
// envoi ensemble dans un flot return sortie ;
friend ostream & operator << (ostream &, set_int &) ;
}; }

Toute reproduction sans autorisation du Centre franais dexploitation du droit de copie est strictement interdite.
Techniques de lIngnieur, trait Informatique industrielle S 8 066 3
PROGRAMMATION EN LANGAGE C++ ______________________________________________________________________________________________________

Voici un exemple de programme utilisant la classe set_int, ac-


compagn du rsultat fourni par son excution. 2. Exemple 2 : utilisation
Exemple : utilisation de la classe set_int
dun patron de classes
/************* test de la classe set_int *********/ Dans le prcdent exemple, nous avons volontairement vit de
#include "setint.h" recourir aux composants standards.
#include <iostream>
using namespace std ; Or, si l'on ne s'intresse qu'aux seules fonctionnalits de la classe,
main() en dehors de la sortie sur un flot, celles-ci sont fournies intgrale-
{ void fct (set_int) ; ment par le composant standard set<int> (son utilisation ncessite
void fctref (set_int &) ; linclusion du fichier en-tte set). En ce qui concerne la sortie sur un
set_int ens ; flot, on dispose de plusieurs dmarches. On peut bien sr la pro-
cout << "donnez 10 entiers \n" ; grammer au fur et mesure des besoins, en crivant chaque fois
int i, n ; les quelques instructions de parcours de l'ensemble :
for (i=0 ; i<10 ; i++) set<int> ens ;
{ cin >> n ; set<int>::iterator ie ;
ens << n ; .....
} for (ie=ens.begin() ; ie!=ens.end() ; ie++) cout << *ie << " " ;
cout << "il y a : " << ens.cardinal () << " entiers diffrents\n" ;
cout << "qui forment l\'ensemble : " << ens << "\n" ; On peut aussi en faire une fonction ordinaire, comme dans cet
fct (ens) ; exemple, analogue l'exemple d'utilisation prcdent.
cout << "au retour de fct, il y en a " << ens.cardinal () << "\n" ; Premier exemple dutilisation de la classe set<int>
cout << "qui forment l\'ensemble : " << ens << "\n" ; #include <iostream>
fctref (ens) ; #include <set> // pour set<int>
cout << "au retour de fctref, il y en a " << ens.cardinal () << "\n" ; using namespace std ;
void affiche (set<int>) ;
cout << "qui forment l\'ensemble : " << ens << "\n" ; main()
cout << "appartenance de -1 : " << -1 % ens << "\n" ; { void fct (set<int>) ;
cout << "appartenance de 500 : " << 500 % ens << "\n" ; void fctref (set<int> &) ;
set_int ensa, ensb ; set<int> ens ;
ensa = ensb = ens ; cout << "donnez 10 entiers \n" ;
int i, n ;
cout << "ensemble a : " << ensa << "\n" ; for (i=0 ; i<10 ; i++)
cout << "ensemble b : " << ensb << "\n" ; { cin >> n ;
} ens.insert(n) ;
}
void fct (set_int e) cout << "il y a : " << ens.size() << " entiers differents\n" ;
{ cout << "ensemble reu par fct : " << e << "\n" ; cout << "qui forment l\'ensemble : " ; affiche(ens) ;
e << -1 << -2 << -3 ; fct (ens) ;
cout << "au retour de fct, il y en a " << ens.size() << "\n" ;
} cout << "qui forment l\'ensemble : " ; affiche(ens) ;
fctref (ens) ;
void fctref (set_int & e) cout << "au retour de fctref, il y en a " << ens.size() << "\n" ;
{ cout << "ensemble reu par fctref : " << e << "\n" ; cout << "qui forment l\'ensemble : " ; affiche(ens) ;
e << -1 << -2 << -3 ; cout << "appartenance de -1 : " << ens.count(-1) << "\n" ;
} cout << "appartenance de 500 : " << ens.count(500) << "\n" ;
set<int> ensa, ensb ;
ensa = ensb = ens ;
____________________ cout << "ensemble a : " ; affiche(ensa) ;
donnez 10 entiers cout << "ensemble b : " ; affiche(ensb) ;
3531851773 }
il y a : 5 entiers diffrents void fct (set<int> e)
qui forment l'ensemble : [ 3 5 1 8 7 ] { cout << "ensemble reu par fct : " ; affiche(e) ;
e.insert(-1) ; e.insert(-2) ; e.insert(-3) ;
ensemble reu par fct : [ 3 5 1 8 7 ] }
au retour de fct, il y en a 5
void fctref (set<int> & e)
qui forment l'ensemble : [ 3 5 1 8 7 ] { cout << "ensemble recu par fctref : " ; affiche(e) ;
ensemble reu par fctref : [ 3 5 1 8 7 ] e.insert(-1) ; e.insert(-2) ; e.insert(-3) ;
au retour de fctref, il y en a 8 }
qui forment l'ensemble : [ 3 5 1 8 7 -1 -2 -3 ] void affiche (set<int> e)
appartenance de -1 : 1 {set<int>::iterator ie ;
appartenance de 500 : 0 cout << "[ " ;
for (ie=e.begin() ; ie!=e.end() ; ie++)
ensemble a : [ 3 5 1 8 7 -1 -2 -3 ] cout << *ie << " ";
ensemble b : [ 3 5 1 8 7 -1 -2 -3 ] cout <<"] \n" ;
}
_______________________
donnez 10 entiers
Remarque 3531851773
Ici, il n'est pas possible d'agrandir l'ensemble au-del de la il y a : 5 entiers differents
qui forment l'ensemble : [ 1 3 5 7 8 ]
limite qui lui a t impartie lors de sa construction. Il serait assez ensemble recu par fct : [ 1 3 5 7 8 ]
facile de remdier cette lacune en modifiant sensiblement la au retour de fct, il y en a 5
fonction d'ajout d'un lment (operator << ). Il suffirait, en effet, qui forment l'ensemble : [ 1 3 5 7 8 ]
qu'elle prvoit, lorsque la limite est atteinte, d'allouer un nouvel ensemble recu par fctref : [ 1 3 5 7 8 ]
au retour de fctref, il y en a 8
emplacement dynamique, par exemple d'une taille double de qui forment l'ensemble : [ -3 -2 -1 1 3 5 7 8 ]
l'emplacement existant, d'y recopier l'actuel contenu et de lib- appartenance de -1 : 1
rer l'ancien emplacement (en actualisant convenablement les appartenance de 500 : 0
membres donnes de l'objet). ensemble a : [ -3 -2 -1 1 3 5 7 8 ]
ensemble b : [ -3 -2 -1 1 3 5 7 8 ]

Toute reproduction sans autorisation du Centre franais dexploitation du droit de copie est strictement interdite.
S 8 066 4 Techniques de lIngnieur, trait Informatique industrielle
______________________________________________________________________________________________________ PROGRAMMATION EN LANGAGE C++

On peut galement crer artificiellement une classe set_int, dri-


ve de set<int>, dans laquelle on surdfinit l'oprateur <<, comme
3. Exemple 3 :
dans cet exemple qui fournit les mmes rsultats que le prcdent. liste htrogne
Second exemple dutilisation de la classe set<int> Nous allons crer une classe permettant de grer une liste cha-
#include <iostream> ne dobjets de types diffrents et disposant des fonctionnalits sui-
#include <set> vantes :
using namespace std ; ajout dun nouvel lment ;
affichage des valeurs de tous les lments de la liste ;
/************* dclaration de la classe set_int *********/ mcanisme de parcours de la liste.
class set_int : public set<int>
{ public : Rappelons que, dans une liste chane, chaque lment comporte
// envoi ensemble dans un flot un pointeur sur llment suivant. En outre, un pointeur dsigne le
friend ostream & operator << (ostream &, set_int &) ; premier lment de la liste. Cela correspond ce schma :
};
/************* dfinition de la classe set_int *********/
ostream & operator << (ostream & sortie, set_int & e)
{ sortie << "[ " ;
Informations Informations Informations
set<int>::iterator ie ; 3
1 2
for (ie=e.begin() ; ie!=e.end() ; ie++)
sortie << *ie << " " ;
sortie << "]" ; Dbut
return sortie ;
}
Mais ici lon souhaite que les diffrentes informations puissent
/************* test de la classe set_int *********/
tre de types diffrents. Aussi chercherons nous isoler dans une
main()
classe (nomme liste) toutes les fonctionnalits de gestion de la liste
{
elle-mme sans entrer dans les dtails spcifiques aux objets
void fct (set_int) ; concerns. Nous appliquerons alors ce schma :
void fctref (set_int &) ;
set_int ens ;
cout << "donnez 10 entiers \n" ;
int i, n ;
for (i=0 ; i<10 ; i++)
{ cin >> n ;
ens.insert(n) ;
}
Dbut Objet Objet Objet
cout << "il y a : " << ens.size() << " entiers differents\n" ;
1 2 3
cout << "qui forment l\'ensemble : " << ens << "\n" ;
fct (ens) ;
cout << "au retour de fct, il y en a " << ens.size() << "\n" ;
cout << "qui forment l\'ensemble : " << ens << "\n" ;
fctref (ens) ;
cout << "au retour de fctref, il y en a " << ens.size() << "\n" ; La classe liste elle-mme se contentera donc de grer des l-
cout << "qui forment l\'ensemble : " << ens << "\n" ; ments simples rduits chacun :
cout << "appartenance de -1 : " << ens.count(-1) << "\n" ; un pointeur sur l'lment suivant ;
cout << "appartenance de 500 : " << ens.count(500) << "\n" ; un pointeur sur l'information associe (en fait, ici, un objet).
set_int ensa, ensb ;
On voit donc que la classe va possder au moins :
ensa = ensb = ens ;
cout << "ensemble a : " << ensa << "\n" ; un membre donne : pointeur sur le premier lment (debut,
cout << "ensemble b : " << ensb << "\n" ; dans notre schma) ;
} une fonction membre destine insrer dans la liste un objet
dont on lui fournira ladresse (nous choisirons linsertion en dbut
void fct (set_int e) de liste, par souci de simplification).
{ cout << "ensemble reu par fct : " << e << "\n" ;
e.insert(-1) ; e.insert(-2) ; e.insert(-3) ; Laffichage des lments de la liste se fera en appelant une
} mthode affiche, spcifique lobjet concern. Cela implique la
mise en oeuvre de la ligature dynamique par le biais des fonctions
void fctref (set_int & e) virtuelles. La fonction affiche sera dfinie dans un premier type
{ cout << "ensemble reu par fctref : " << e << "\n" ; dobjet (nomm ici mere) et redfinie dans chacune de ses descen-
e.insert(-1) ; e.insert(-2) ; e.insert(-3) ; dantes.
}
En dfinitive, on pourra grer une liste d'objets de types diffrents
sous rserve que les classes correspondantes soient toutes drives
Remarque d'une mme classe de base. Cela peut sembler quelque peu restric-
tif. En fait, cette famille de classes peut toujours tre obtenue par
Ici, il n'est pas ncessaire de redfinir l'affectation ou le construc- la cration d'une classe abstraite (rduite au minimum, ventuelle-
teur par recopie. En effet, compte tenu des rgles relatives l'hri- ment une fonction affiche vide ou virtuelle pure) destine simple-
tage, les fonctions par dfaut appellent bien les fonctions voulues ment donner naissance aux classes concernes. Bien entendu,
dans la classe de base ; cela suffit ici puisque la classe drive cela n'est concevable que si les classes en question ne sont pas dj
n'introduit aucune partie dynamique supplmentaire. figes (car il faut qu'elles hritent de cette classe abstraite).

Toute reproduction sans autorisation du Centre franais dexploitation du droit de copie est strictement interdite.
Techniques de lIngnieur, trait Informatique industrielle S 8 066 5
PROGRAMMATION EN LANGAGE C++ ______________________________________________________________________________________________________

D'o une premire bauche de la classe liste : moins pratique car il faudrait obligatoirement agir sur le pointeur de
liste avant de savoir si l'on est la fin).
struct element // structure d'un lment de liste
{ element * suivant ; // pointeur sur l'lment suivant En dfinitive, nous introduisons trois nouvelles fonctions mem-
mere * contenu ; // pointeur sur un objet quelconque bres :
};
class liste void * premier () ;
{ element * debut ; // pointeur sur premier lment void * prochain () ;
public : int fini () ;
liste () ; // constructeur
~liste () ; // destructeur Comme dans le premier exemple, il nous faut nous proccuper
void ajoute (mere *) ; // ajoute un lment en dbut de liste des problmes daffectation et de copie dobjets de type liste. Ici,
void affiche () ; nous nous contenterons dinterdire ces oprations lutilisateur en
..... levant une exception lorsque lune dentre elles sera tente. Pour ce
}; faire, nous crons (un peu artificiellement) deux classes exc_affec et
exc_copie (en fait vides). Pour faciliter la gestion des exceptions cor-
Pour mettre en uvre le parcours de la liste, nous prvoyons des respondantes, nous les faisons toutes deux driver dune troisime
fonctions lmentaires pour : nomme exc_liste.
initialiser le parcours ; Voici la liste complte des diffrentes classes voulues. Notez
avancer d'un lment. quici, nous avons regroup dclaration et dfinition de classes. De
Celles-ci ncessitent un pointeur sur un lment courant . Il plus, au sein des dclarations, nous avons exploit la possibilit
sera membre donne de notre classe liste ; nous le nommerons cou- quoffre C++ de dfinir directement certaines fonctions que lon qua-
rant. Par ailleurs, les deux fonctions membres voques doivent lifie alors de fonctions en ligne . Cette faon de procder simplifie
fournir en retour une information concernant l'objet courant. ce les dfinitions courtes. En outre, elle offre au compilateur la possibi-
niveau, on peut choisir entre : lit (pas lobligation) dintroduire le code objet correspondant cha-
ladresse de l'lment courant ; que appel, en vitant les changements de contexte qui seraient
ladresse de l'objet courant (cest--dire lobjet point par ll- induits par un appel classique (sauvegarde de registres, conserva-
ment courant) ; tion dune adresse de retour...).
la valeur de l'lment courant. Enfin, nous avons ralis un petit programme dessai de la classe
La deuxime solution semble la plus naturelle. Il faut simplement liste, en dfinissant deux classes point et complexe (lesquelles nont
fournir lutilisateur un moyen de dtecter la fin de liste. Nous pr- pas besoin de driver lune de lautre), drives de la classe abs-
voirons donc une fonction supplmentaire permettant de savoir si la traite mere et dotes chacune dune fonction affiche approprie.
fin de liste est atteinte (en toute rigueur, nous aurions aussi pu four- Nous y avons tent une affectation entre objets de type liste et nous
nir un pointeur nul comme adresse de l'objet courant ; mais ce serait interceptons convenablement lexception exc_liste.

Dclaration, dfinition et utilisation dune liste htrogne adel->suivant = debut ;


adel->contenu = chose ;
debut = adel ;
#include <iostream> }
#include <cstddef> // pour la dfinition de NULL
using namespace std ; void liste::affiche_liste ()
{ mere * ptr ;
// ************* classes exceptions ************* premier() ;
class exc_liste {} ; while ( ! fini() )
class exc_affec : public exc_liste {} ; { ptr = (mere *) prochain() ;
class exc_copie : public exc_liste {} ; ptr->affiche () ;
// ************* classe mere ************* }
class mere }
{ public : // ************* classe point *************
virtual void affiche () = 0 ; // fonction virtuelle pure class point : public mere
}; { int x, y ;
// ************* classe liste ************* public :
struct element // structure d'un lment de liste point (int abs=0, int ord=0) { x=abs ; y=ord ; }
{ element * suivant ; // pointeur sur l'lment suivant void affiche ()
mere * contenu ; // pointeur sur un objet quelconque { cout << "Point de coordonnees : " << x << " " << y << "\n" ; }
}; };
class liste // ************* classe complexe *************
{ element * debut ; // pointeur sur premier lment class complexe : public mere
element * courant ; // pointeur sur lment courant { double reel, imag ;
public : public :
liste () // constructeur
{ debut = NULL ; courant = debut ; } complexe (double r=0, double i=0) { reel=r ; imag=i ; }
~liste () ; // destructeur void affiche ()
void ajoute (mere *) ; // ajoute un lment { cout << "Complexe : " << reel << " + " << imag << "i\n" ; }
void premier () // positionne sur premier lment };
{ courant = debut ; // ************* programme dessai *************
} main()
mere * prochain () // fournit ladresse de l'lment { try
// courant (0 si fin) { liste l1 ;
// et positionne sur prochain point a(2,3), b(5,9) ;
// lment (rien si fin) complexe x(4.5,2.7), y(2.35,4.86) ;
{mere * adsuiv = NULL ; l1.ajoute (&a) ; l1.ajoute (&x) ; l1.affiche_liste () ;
if (courant != NULL) { adsuiv = courant -> contenu ; cout << "--------\n" ;
courant = courant -> suivant ; l1.ajoute (&y) ; l1.ajoute (&b) ; l1.affiche_liste () ;
} liste l2 ;
return adsuiv ; l2 = l1 ; // provoque une exception exc_affec ;
}
void affiche_liste () ; // affiche tous les lments }
// de la liste catch (exc_liste)
int fini () { return (courant == NULL) ; } { cout << "tentative de copie ou daffectation de liste" ;
liste & operator = (liste & l) { throw exc_affec() ; } }
liste (liste &) { throw exc_copie() ; } }
}; ____________________________
liste::~liste () Complexe : 4.5 + 2.7i
{ element * suiv ;
courant = debut ; Point de coordonnees : 2 3
while (courant != NULL ) --------
{ suiv = courant->suivant ; delete courant ; courant = suiv ; } Point de coordonnees : 5 9
Complexe : 2.35 + 4.86i
} Complexe : 4.5 + 2.7i
void liste::ajoute (mere * chose) Point de coordonnees : 2 3
{ element * adel = new element ; tentative de copie ou affectation de liste

Toute reproduction sans autorisation du Centre franais dexploitation du droit de copie est strictement interdite.
S 8 066 6 Techniques de lIngnieur, trait Informatique industrielle

Vous aimerez peut-être aussi