• Aucun résultat trouvé

EXEM PLE D E SURD ÉFINITIO N D E L'O PÉRATEUR =

Nous avons déjà e u l'occasion d'utiliser une classe ve ct, correspondant à des "ve cte urs dynam iq ues" :

class vect

{ int nelem ; // nombre d'éléments int * adr ; // adresse

public :

vect (int n) // constructeur ...

} ;

Nous avons vu (paragraph e 3.2 du ch apitre VII) q u'alors, si fct é tait une fonction à un argum e nt de type

ve ct, les instructions suivantes :

vect a(5) ;

9 - Il ne suffira pas d'avoir surdéfini l'addition d'un com plexe et d'un double (laquelle peut se faire par une fonction m em bre). En effet, com m e nous l'avons dit, aucune h ypoth è se n'est faite par C+ + sur l'opé rate ur surdéfini, e n particulie r sur sa com m utativité !

... fct (a) ;

posaie nt problèm e : l'appe l de fct conduisait à la cré ation, par re copie de a, d'un nouve l obje t (q ue nous nom m ions b) ; nous étions alors en présence de deux obje ts a e t b com portant un pointe ur (adr) ve rs le m ê m e e m place m e nt :

En particulie r, si la classe ve ct possédait (com m e c'est souh aitbale !) un destructe ur ch argé de libérer l'e m place m e nt m é m oire e n q uestion, on ris q uait d'aboutir à deux dem andes de libération du m ê m e e m place m e nt m é m oire .

Nous avons vu alors q u'une solution (parm i d'autres) consistait à définir un constructe ur de re copie ch argé d'effe ctue r, non seulem e nt la re copie de l'obje t lui-m ê m e , m ais égalem e nt ce lle de sa partie dynam iq ue dans un nouve l em place m e nt (une autre solution consiste à e m ploye r un "com pte ur de ré fé re nces" : nous vous la présente rons dans le paragraph e suivant).

O r, l'affe ctation d'obje ts de type ve ct pose les m ê m es problèm es. Ainsi, ave c ce tte déclaration :

vect a(5), b(3) ;

q ui correspond au sch é m a suivant :

L'affe ctation :

b = a ;

IX. La surdéfinition d'opérate urs 137

Le problèm e est e ffe ctive m e nt voisin de ce lui de la construction par re copie . Voisin, m ais non ide ntiq ue , car q ue lques nuances apparaissent, à savoir :

• on pe ut se trouve r e n présence d'une affe ctation d'un obje t à lui-m ê m e ,

• ici, avant affe ctation, il existe deux obje ts "com plets" (c'est-à -dire ave c leur partie dynam iq ue ). Dans le cas de la construction par re copie , il n'e xistait q u'un seul em place m e nt dynam iq ue , le second é tant à cré e r. O n va donc, ici, se re trouve r ave c l'ancie n e m place m e nt dynam iq ue de b ;il n'est plus ré fé re ncé par b, m ais est-on sûr q u'il n'est pas ré fé re ncé par ailleurs ?

En fait, nous pouvons ré gler les diffé re nts points en surdéfinissant l'opé rate ur d'affe ctation, de m aniè re q ue ch aq ue obje t de type ve ct com porte son propre e m place m e nt dynam iq ue . Dans ce cas, on est sûr q u'il n'est ré fé re ncé q u'une seule fois et son éve ntue lle libération pe ut se faire sans problèm e . Note z ce pe ndant q ue ce tte dém arch e ne convie nt totalem e nt q ue si e lle est associé e à la définition conjointe du constructe ur de re copie .

Voici donc com m e nt nous pourrions traite r une affe ctation te lle q ue b = a, dans le cas (pour l'instant) où a est diffé re nt de b :

• libération de l'e m place m e nt pointé par b,

• cré ation dynam iq ue d'un nouve l em place m e nt dans leq ue l on re copie les valeurs de l'e m place m e nt pointé par a,

• m ise en place des valeurs des m e m bres donnée de b. Voici un sch é m a illustrant la situation à laq ue lle on aboutit :

Par ailleurs, il nous faut pré voir de ne rie n faire lors q ue a e t b désignent le m ê m e obje t. Dans le cas contraire , l'algorith m e proposé ne serait pas satisfaisant. En e ffe t, e n supposant q ue a est transm is par valeur, il conduirait à ce tte situation :

Ce lle-ci ne serait pas catastroph iq ue , m ais l'ancie n e m place m e nt pointé par a ne pourrait pas ê tre libéré. En re vanch e , si a é tait transm is par ré fé re nce , on aboutirait à une double libération d'un m ê m e e m place m e nt m é m oire .

Enfin, il nous faut "décide r" de la valeur de re tour fournie par notre opé rate ur. A ce nive au, tout dépend de l'usage q ue nous souh aitons en faire :

• si nous nous conte ntons d'affe ctations sim ples (b = a), nous n'avons besoin d'aucune valeur de re tour (void),

• si, en revanch e , nous souh aitons pouvoir traite r une affe ctation m ultiple ou, plus généralem e nt faire e n sorte q ue (com m e on pe ut s'y atte ndre !) l'e xpression b = a ait une valeur (probablem e nt ce lle de b !), il est né cessaire q ue notre opé rate ur fournisse une valeur de re tour.

Nous ch oisissons ici la seconde possibilité q ui a le m é rite d'ê tre plus générale10. Voici finalem e nt ce q ue pourrait ê tre la définition de notre opé rate ur = (C+ + nous im pose de le définir com m e une fonction m e m bre ) : par rapport à nos précédentes explications, b devie nt le pre m ie r opé rande 150 – ici th is – e t a devie nt le second opé rande - ici v. De plus, nous prévoyons de transm e ttre le second opé rande par ré fé re nce .

vect vect::operator = (const vect & v) // notez const { if (this != &v)

{ delete adr ;

adr = new int [nelem = v.nelem] ;

for (int i=0 ; i<nelem ; i++) adr[i] = v.adr[i] ; }

return * this ; }

Note z q ue , com pte te nu de sa transm ission par ré fé re nce , il est né cessaire d'introduire le q ualificatif const pour l'uniq ue argum e nt de notre fonction m e m bre ope rator=, afin de traite r conve nablem e nt le cas de l'affe ctation d'un ve cte ur constant à un ve cte ur q ue lconq ue (n'oublie z pas q u'ainsi notre opé rate ur pourra indiffé re m m e nt s'appliq ue r à des obje ts constants ou non ;dans le cas contraire , il n'aurait pu s'appliq ue r q u'à des obje ts non constants).

Nous vous proposons d'inté gre r ce tte définition dans un program m e com plet servant à illustre r le fonctionne m e nt de l'opé rate ur. Pour ce faire , nous ajoutons, com m e d'h abitude , un ce rtain nom bre d'instructions d'affich age (e n particulie r, nous suivons les adresses des obje ts et des em place m e nts dynam iq ue s q ui leur sont associés). Par contre , pour q ue le program m e ne soit pas trop long, nous avons réduit la classe ve ct au strict m inim um ; e n particulie r, nous n'y avons pas prévu de constructeur de

recopie ;or celui-ci deviendrait naturellem ent indispensable dans une application réelle.

Qui plus est, m ê m e ici, alors q ue notre fonction m ain se lim ite à l'e m ploi de l'opé rate ur =, nous avons dû pré voir une transm ission par ré férence pour l'argum e nt e t la valeur de re tour de ope rator=. En e ffe t, si

10 - Bien entendu, C+ + vous laisse libre de faire ce que vous voulez, y com pris de renvoyer une valeur autre que celle de b (avec tous les ris q ues de m anq ue de lisibilité q ue ce la com porte !).

IX. La surdéfinition d'opérate urs 139

nous ne l'avions pas fait, l'appe l de ce t opé rate ur, traité , rappe lons-le, com m e une fonction, aurait e ntraîné un appe l de constructe ur de re copie (a = b est é q uivalent ici à : a.ope rator = (b)) ;il se serait alors agi du constructe ur de re copie par dé faut, ce q ui aurait e ntraîné les problèm es déjà é voq ués de double libération d'un e m place m e nt11.

_______________________________________________________________________________ ______

#include <iostream.h> class vect

{ int nelem ; // nombre d'éléments

int * adr ; // pointeur sur ces éléments public :

vect (int n) // constructeur { adr = new int [nelem = n] ;

for (int i=0 ; i<nelem ; i++) adr[i] = 0 ;

cout << "++ obj taille " << nelem << " en " << this << " - v. dyn en " << adr << "\n" ;

}

~vect () // destructeur { cout << "-- obj taille " << nelem << " en " << this << " - v. dyn en " << adr << "\n" ; delete adr ;

}

vect & operator = (const vect &) ; // surdéfinition opérateur = } ;

vect & vect::operator = (const vect & v)

{ cout << "== appel opérateur = avec adresses " << this << " " << &v << "\n" ; if (this != &v)

{ cout << " effacement vecteur dynamique en " << adr << "\n" ; delete adr ;

adr = new int [nelem = v.nelem] ;

cout << " nouveau vecteur dynamique en " << adr << "\n" ; for (int i=0 ; i<nelem ; i++) adr[i] = v.adr[i] ;

}

else cout << " on ne fait rien \n" ; return * this ;

} main()

{ vect a(5), b(3), c(4) ;

cout << "** affectation a=b \n" ; a = b ;

cout << "** affectation c=c \n" ; c = c ;

cout << "** affectation a=b=c \n" ; a = b = c ;

} ________________________________ ++ obj taille 5 en 0x41140ffa - v. dyn en 0x42c60004 ++ obj taille 3 en 0x41140ff4 - v. dyn en 0x42c70004 ++ obj taille 4 en 0x41140fee - v. dyn en 0x42c80004 ** affectation a=b

== appel opérateur = avec adresses 0x41140ffa 0x41140ff4 effacement vecteur dynamique en 0x42c60004

nouveau vecteur dynamique en 0x42c60004 ** affectation c=c

== appel opérateur = avec adresses 0x41140fee 0x41140fee on ne fait rien

** affectation a=b=c

== appel opérateur = avec adresses 0x41140ff4 0x41140fee effacement vecteur dynamique en 0x42c70004

nouveau vecteur dynamique en 0x42c70004

== appel opérateur = avec adresses 0x41140ffa 0x41140ff4 effacement vecteur dynamique en 0x42c60004

nouveau vecteur dynamique en 0x42c60004 -- obj taille 4 en 0x41140fee - v. dyn en 0x42c80004 -- obj taille 4 en 0x41140ff4 - v. dyn en 0x42c70004 -- obj taille 4 en 0x41140ffa - v. dyn en 0x42c60004

_______________________________________________________________________________ ____

Exe m ple d'utilisation d'une clas s e ve ct ave c un opérate ur d'affe ctation surdéfini