Sunteți pe pagina 1din 12

Centre Informatique pour les Lettres

et les Sciences Humaines


Apprendre C++ avec Qt : Leon 11
Fonctions oprateur






1 - Gnralits.......................................................................................................................... 2
Quelles fonctions oprateur pouvons-nous dfinir ? ........................................................2
Immuabilit de la prcdence des oprateurs et de leur arit...........................................3
Particularits des fonctions oprateur .............................................................................3
Dans quels cas est-il judicieux de surcharger un oprateur ? ..........................................3
2 - Surcharger les oprateurs monadiques ............................................................................... 3
Les oprateurs prfixs....................................................................................................4
Les oprateurs ++et - - suffixs ......................................................................................4
L'oprateur - >.................................................................................................................5
3 - Surcharger les oprateurs dyadiques .................................................................................. 6
Fonction oprateur globale ..............................................................................................6
Fonction oprateur membre d'une classe.........................................................................7
4 - Un cas particulier : l'oprateur "parenthses"...................................................................... 7
Fonctionodes..................................................................................................................8
Oprateurs de transtypage ..............................................................................................8
5 - Oprateurs sur enum: un exemple ...................................................................................... 9
Conversion d'une valeur entire en valeur de type EJ our ...............................................10
Reprsentation crite de la valeur d'un EJ our ...............................................................10
Fonctions oper at or ++( ) .............................................................................................10
Utilisation du type EJ our ..............................................................................................11
6 - Bon, c'est gentil tout a, mais a fait dj 9 pages. Qu'est-ce que je dois vraiment en
retenir ?............................................................................................................................ 11
Document du 05/06/07 - Retrouvez la version la plus rcente sur http://www.up.univ-mrs.fr/wcpp
C++ - Leon 11 Fonctions oprateur 2/12
La syntaxe normale pour invoquer une fonction est de mentionner son nom et de faire suivre
celui-ci d'un couple de parenthses entourant les variables ou valeurs que la fonction doit
utiliser pour initialiser ses paramtres. Cette syntaxe n'est pas toujours la plus agrable que l'on
puisse imaginer, notamment lorsque la fonction ralise une opration qui est habituellement
dsigne par un symbole bien connu. Imaginons par exemple que nous avons dfini une classe
CTr uc pour laquelle la notion d'addition a un sens. Il est, bien entendu, possible de dfinir une
fonction membre nomme aj out e( ) , qui permettra d'crire

/ / pr emi er Tr uc, secondTr uc et t r ucSomme sont des i nst ances de CTr uc
t r ucSomme = pr emi er Tr uc. aj out e( secondTr uc) ;

Toutefois, notre code serait sans doute plus lisible si nous pouvions crire

t r ucSomme = pr emi er Tr uc + secondTr uc;

C'est justement cette possibilit que nous offre le langage C++ en nous autorisant dfinir les
fonctions qui seront excutes pour valuer les expressions comportant certains oprateurs.
1 - Gnralits
Le code dfinissant le traitement qui doit correspondre un oprateur va prendre place dans
ce que l'on appelle une "fonction oprateur". La dfinition d'une telle fonction est aussi souvent
qualifie de surcharge de l'oprateur en question. Cette surcharge est soumise quelques
restrictions, mais ne diffre finalement qu'assez peu de la dfinition d'une fonction "ordinaire".
Quelles fonctions oprateur pouvons-nous dfinir ?
En dpit de sa grande gnrosit, C++ ne permet pas de crer de nouveaux oprateurs, mais
seulement de dfinir la faon dont certains de ses oprateurs peuvent s'appliquer de
nouveaux types d'oprandes. Nous ne pourrons donc jamais crire des choses comme :

i f ( a <> b) / / i l n' exi st e pas d' opr at eur <> en C++ 1
c = a # b; / / i l n' exi st e pas d' opr at eur # en C++ 2
el se 3
c = a : b; / / i l n' exi st e pas d' opr at eur : en C++ 4

Qui plus est, parmi les oprateurs C++, cinq ne peuvent pas faire l'objet de fonctions
oprateur :

Symbole Nature Commentaire
si zeof Calcul de taille
Cet oprateur donne dj le seul rsultat raisonnable (la
taille en octet de son oprande), mme lorsqu'il est
appliqu une classe ou une instance. Le redfinir ne
donnerait certainement rien de bon.
?: if arithmtique
: : Rsolution de porte
. Slection d'un membre
. *
Drfrencement d'un
pointeur sur membre
Ces oprateurs touchent des mcanismes fondamentaux.
Il est difficile d'envisager de les redfinir sans
compromettre gravement la cohrence du langage.

Bien entendu, puisqu'il s'agit de confrer de nouvelles significations des symboles que le
langage utilise dj, il faut que le contexte permette de lever l'ambigut. Deux cas peuvent se
prsenter :

- Si la fonction oprateur est membre d'une classe, le type de l'objet au titre duquel elle est
invoque la dsigne de faon parfaitement univoque.

- Si la fonction oprateur est globale (c'est--dire n'est pas membre d'une classe), il s'agit d'un
cas de surcharge, et le type de ses arguments doit permettre de la distinguer de ses
homonymes.

Nous pouvons donc conclure qu'une fonction oprateur est toujours lie un type dfini par le
programmeur : soit parce qu'elle est membre d'une classe, soit parce qu'au moins un de ses
paramtres relve d'un type numr ou d'une classe.
J-L Pris - 05/06/07
C++ - Leon 11 Fonctions oprateur 3/12
Immuabilit de la prcdence des oprateurs et de leur arit
Si la dfinition d'une fonction oprateur permet de spcifier comment cet oprateur s'applique
de nouveaux types, il reste des caractristiques de l'oprateur que cette dfinition ne saurait
remettre en cause. C'est en particulier le cas de la prcdence (ensemble de rgles qui
dtermine dans quel ordre sont effectues les oprations lors de l'valuation d'une expression
impliquant plusieurs oprateurs) et de l'arit (nombre d'arguments ncessaires l'application
d'un oprateur). Ainsi, quel que soit le type des objets concerns,

x + y * z; / / l ' opr at eur * est pr i or i t ai r e par r appor t l ' opr at eur +

quivaut toujours x + ( y * z) et non ( x + y) * z, et il ne sera jamais possible d'crire

j = k &&; / / && est un opr at eur ncessi t ant deux opr andes !
Particularits des fonctions oprateur
Par rapport une fonction "ordinaire", une fonction oprateur ne se distingue gure que de
trois faons.

- La premire est assez anecdotique : le nom attribu la fonction lors de sa dclaration est
compos du mot oper at or , suivi du symbole qui sera utilis pour appeler la fonction.

- La deuxime particularit des fonctions oprateur dcoule de la syntaxe particulire utilise
pour les appeler : leurs paramtres ne peuvent pas tre dots de valeurs par dfaut.

- La dernire particularit mrite plus d'attention, car il s'agit de la faon dont la fonction a
accs aux oprandes soumis l'oprateur : cette faon varie selon le nombre d'objets
impliqus et selon si la fonction oprateur est globale ou membre d'une classe.
Dans quels cas est-il judicieux de surcharger un oprateur ?
Dans la plupart des cas, le recours une fonction oprateur ne relve que du souci
d'augmenter le confort des programmeurs et d'assurer une lisibilit maximale au texte source.
Ces objectifs ne seront atteints que dans la mesure o le symbole de l'oprateur surcharg
rvle sans ambigut la nature de l'opration qu'il effectue.

S'il peut y avoir le moindre doute sur le sens qu'aurait l'application d'un oprateur l'un de
vos types, n'utilisez pas un oprateur mais une fonction ordinaire portant un nom bien choisi.

Il existe toutefois des cas particuliers : l'utilisation de conteneurs (cf. Leon 8) exige parfois que
l'oprateur de comparaison ==puisse tre appliqu deux instances de la classe concerne, et
l'oprateur d'affectation doit parfois imprativement tre surcharg.

En effet, en l'absence d'une fonction oprateur le prenant explicitement en charge, l'oprateur
d'affectation est automatiquement dfini comme une copie membre membre de l'oprande de
droite vers l'oprande de gauche. Il s'agit l d'un traitement semblable celui opr par le
constructeur par copie fourni automatiquement par le langage (cf. Leon 10). Si ce traitement
convient parfaitement dans un trs grand nombre de cas, il s'avre parfois tout fait
inacceptable, ce qui entrane la fois la ncessit de dfinir explicitement un constructeur par
copie plus judicieux et celle de surcharger l'oprateur d'affectation.
2 - Surcharger les oprateurs monadiques
Oprateur Exemples
! bool n = ! t r ue;
- i nt a = - 2;
+ a = + 2;
& i nt * pt r = & a;
* * pt r = 4;
~ char c = ~ ' x' ;
++
++ a;
a ++;
- -
- - a;
a - - ;
Le langage C++ comporte neuf oprateurs qui
s'appliquent un oprande unique. Dans la plupart
des cas, la syntaxe exige que le symbole de l'oprateur
prcde la dsignation de son oprande (cf. tableau ci-
contre). Les oprateurs ++et - - admettent galement
une syntaxe suffixe, qui doit tre prise en charge par
une fonction spcifique.

L'oprateur - >, pour sa part, n'admet qu'un usage
suffix et exige sa droite la prsence d'un
identificateur de membre qui lui donne l'air d'avoir
deux oprandes.
- > t r uc- >f membr e( ) ;
J-L Pris - 05/06/07
C++ - Leon 11 Fonctions oprateur 4/12
Les oprateurs prfixs
La surcharge de ces oprateurs peut tre ralise de deux faons : soit par une fonction
membre de la classe vise, soit par une fonction globale.

S'il s'agit d'une fonction membre, elle doit tre dpourvue d'argument, car elle sera invoque au
titre de l'instance sur laquelle l'oprateur est appliqu. En d'autres termes, si une classe CTr uc
possde, par exemple, une fonction membre surchargeant l'oprateur ! , alors l'instruction

! monTr uc; / / monTr uc est une i nst ance de l a cl asse CTr uc

est quivalente

monTr uc. oper at or ! ( ) ; / / appel expl i ci t e de l a f onct i on oper at or ! ( )

La dfinition d'une fonction membre surchargeant un oprateur ne prsente qu'une seule
particularit : son nom complet comporte le mot oper at or prcdant le symbole de l'oprateur
concern. Le type de la valeur ventuellement renvoye par la fonction, tout comme la logique
des oprations qu'elle effectue, dpend bien entendu totalement de la smantique associe la
classe en gnral, et l'oprateur concern en particulier.

CTr uc CTr uc: : oper at or ! ( ) const / / cet opr at eur est sans ef f et 1
{ 2
CTr uc r esul t at = *t hi s; 3
/ / modi f i cat i on de r esul t at pour cal cul er l e r sul t at de l ' appl i cat i on de !
r et ur n r esul t at ; 4
} 5

L'oprateur ! tant habituellement dnu d'effet, il est vraisemblable que l'objet auquel il est
appliqu ne doit pas tre modifi (la fonction membre est donc constante et utilise une variable
locale pour calculer le r esul t at ). De mme, l'oprateur ! produit gnralement un r esul t at du
mme type que son oprande, ce qui explique que la fonction est de type CTr uc.

Si la fonction surchargeant l'oprateur est une fonction globale, elle doit tre munie d'un
paramtre unique, qui correspond l'oprande sur lequel l'oprateur est appliqu. Si
l'oprateur doit avoir un effet, ce paramtre sera de type "rfrence une instance de la classe
vise", ce qui permettra la fonction de modifier l'oprande. Si l'oprateur ne fait que produire
un rsultat, la fonction peut se contenter de recevoir une valeur ayant pour type la classe
concerne, et les manipulations qu'elle effectuera sur son paramtre resteront sans effet sur
l'objet utilis comme oprande. Un exemple d'une telle fonction globale pourrait tre :

CTr uc oper at or ! ( CTr uc par amet r e) / / cet opr at eur est sans ef f et 1
{ 2
/ / modi f i cat i ons de par amet r e pour cal cul er l e r sul t at de l ' appl i cat i on de ! 3
r et ur n par amet r e; / / r envoi e l a val eur obt enue 4
} 5

Bien que globale, la fonction oprateur va sans doute devoir accder des membres non publics
de l'objet concern. La classe de cet objet doit donc la dclarer amie.
Les oprateurs ++et - - suffixs
Les oprateurs d'incrmentation et de dcrmentation doivent correspondre des fonctions
oprateurs diffrentes selon s'ils sont placs avant ou aprs leur oprande. L'effet qu'ont ces
oprateurs sur leur oprande est a priori le mme dans les deux cas, mais la valeur renvoye
doit tre diffrente : il s'agit de la valeur de l'oprande avant que le traitement n'ait t effectu
lorsque l'oprateur est suffix, et de la valeur de l'oprande aprs application du traitement
lorsque l'oprateur est prfix.

Il serait trs fcheux qu'une fonction surchargeant l'oprateur ++ ou l'oprateur -- n'adhre pas
ces conventions, qui sont profondment ancres dans l'esprit de tous les programmeurs C++.

Si la cration des fonctions oprateurs correspondant l'usage prfix ne diffre en rien du cas
des autres oprateurs un seul argument, un problme se pose pour dfinir les fonctions
correspondant l'usage suffix. Leur nom de la fonction (oper at or ++ou oper at or - - ) est en
effet ncessairement le mme que pour la fonction correspondant l'usage prfix. Pour que
les fonctions prenant en charge les usages prfix et suffix d'un mme oprateur puissent tre
toutes deux dfinies, il faut donc que leurs signatures diffrent.

J-L Pris - 05/06/07
C++ - Leon 11 Fonctions oprateur 5/12
La liste des arguments ncessaires tant la mme dans les deux cas (aucun argument pour
une fonction membre, un seul argument, du type concern, pour une fonction globale), et la
constance de la fonction tant exclue par la nature de l'oprateur, les concepteurs du langage
C++ ont dcid d'introduire un argument inutile (de type i nt ) pour singulariser la signature de
la fonction oprateur devant tre invoque lorsque l'oprateur est utilis en position suffixe.

S'il s'agit de fonctions membre, les fonctions oprateur seront donc dfinies ainsi :

CTr uc & CTr uc: : oper at or ++( ) / / excut e l or sque ++ est PREFI XE 1
{ 2
/ / modi f i cat i on des var i abl es membr e pour r al i ser l ' ef f et associ ++ 3
r et ur n *t hi s; / / r envoi e l a val eur de l ' opr ande modi f i 4
} 5

const CTr uc CTr uc: : oper at or ++( i nt ) / / excut e l or sque ++ est POSTFI XE 1
{ 2
const CTr uc r esul t at ( *t hi s) ; / / met l a val eur i ni t i al e de ct 3
++ ( *t hi s) ; / / modi f i e l a val eur l ' ai de de l ' opr at eur pr f i x 4
r et ur n r esul t at ; / / r envoi e l a val eur i ni t i al e de l ' opr ande 5
} 6

Si les fonctions oprateur sont globales, elles prendront la forme suivante :

CTr uc & oper at or ++( CTr uc & oper ande) / / excut e l or sque ++ est PREFI XE 1
{ 2
/ / modi f i cat i on de oper ande pour r al i ser l ' ef f et associ ++ 3
r et ur n oper ande; / / r envoi e l a val eur de l ' opr ande modi f i 4
} 5

const CTr uc oper at or ++( CTr uc &oper ande, i nt ) / / excut e l or sque ++ est POSTFI XE 1
{ 2
const CTr uc r esul t at ( oper ande) ; / / met l a val eur i ni t i al e de ct 3
++ oper ande; / / modi f i e l a val eur l ' ai de de l ' opr at eur pr f i x 4
r et ur n r esul t at ; / / r envoi e l a val eur i ni t i al e de l ' opr ande 5
} 6

Il est prfrable que les fonctions prenant en charge l'usage prfix d'un oprateur ++ ou - -
renvoient une rfrence l'objet sur lequel elles sont appliques, et non simplement la nouvelle
valeur de celui-ci. Les expressions utilisant l'oprateur prfix deviennent ainsi des l val ue, ce
qui autorise des choses comme ++unObj et . f onct i onMembr e( ) (incrmente l'objet, puis excute
la f onct i onMembr e). On prfrera aussi que les oprateurs postfixs renvoient une constante, de
faon interdire la compilation d'expressions pathologiques telles que unObj et ++++ (qui de
toute faon, n'incrmenterait pas deux fois l'objet, puisque le second ++ serait appliqu la
valeur que l'objet avait avant la premire incrmentation)
L'oprateur - >
L'oprateur - > s'interpose habituellement entre deux identificateurs et ne peut tre utilis
comme un oprateur monadique habituel, que ce soit en position prfixe ou suffixe :

obj et - >l eMembr e; / / OK 1
- >l eMembr e; / / ?? a ne veut r i en di r e ! 2
obj et - >; / / ?? a ne veut r i en di r e ! 3

Il n'en demeure pas moins que, si l'on appelle explicitement l'oprateur - >, la ligne 1 devient

( obj et . oper at or - > ( ) ) - >l eMembr e; / / qui vaut obj et - >l eMembr e;

L'examen de cette expression conduit deux conclusions :

- L'oprateur - >est bien monadique (comme nous le verrons bientt, un second oprande lui
aurait t transmis par le passage d'un paramtre dont l'absence est ici vidente).

- La fonction oprateur prenant en charge - >doit ncessairement renvoyer l'adresse d'un objet
ayant un membre nomm l eMembr e (puisque l'oprateur standard de slection indirecte est
appliqu sur le rsultat produit par l'appel explicite de notre oprateur - >).

J-L Pris - 05/06/07
C++ - Leon 11 Fonctions oprateur 6/12
La surcharge de l'oprateur - >ne peut tre ralise que par une fonction membre et permet de
crer des classes dont les instances se comportent comme des pointeurs.
3 - Surcharger les oprateurs dyadiques
Le langage C++ permet de surcharger trente deux oprateurs produisant un rsultat partir de
deux oprandes placs de part et d'autre de leur symbole
1
. Trente de ces oprateurs peuvent
faire l'objet soit d'une fonction globale, soit d'une fonction membre d'une classe, alors que les
oprateurs =et [ ] ne peuvent tre pris en charge que par des fonctions membre.

Un oprateur admettant deux oprandes de types diffrents jouant des rles symtriques doit
gnralement tre surcharg par deux fonctions : l'une prendra en charge les expressions
adoptant l'ordre t ypeA # t ypeB, et l'autre traitera les cas t ypeB # t ypeA. Si cette prcaution
n'est pas prise, les utilisateurs devront se faire l'ide qu'ils peuvent par exemple crire
monTr uc + 4;
mais pas
4 + monTr uc;
Fonction oprateur globale
Les oprateurs dyadiques susceptibles d'tre surchargs par une fonction globale sont :

+ / * % == > < << >> & | ^ && ,
+= = / = *= %= ! = >= <= <<= >>= &= | = ^= | | >*

Remarquez que les symboles +, - , * et & correspondent chacun deux oprateurs : un oprateur
monadique et un oprateur dyadique. C'est donc le contexte (c'est dire, ici, la prsence d'un ou
de deux oprandes) qui dtermine la signification de chacune des occurrences de l'un de ces
symboles. Les caractres * et & sont aussi utiliss pour dclarer respectivement des pointeurs et
des rfrences. Ils ne dsignent aucun oprateur lorsqu'ils apparaissent dans ce contexte.

Tous ces oprateurs utilisent une notation infixe : l'un des oprandes est gauche de
l'oprateur, alors que l'autre est droite. Cette rgularit permet d'exprimer simplement la
correspondance entre les oprandes et les arguments d'une fonction globale surchargeant un
oprateur dyadique : si #reprsente l'un des 30 symboles mentionns ci-dessus

oper andeGauche # oper andeDr oi t ; / / not at i on i nf i xe

est quivalent l'appel explicite de la fonction ralis par

oper at or #( oper andeGauche, oper andeDr oi t ) ;

Comme nous l'avons signal dans la Leon 7, il est souvent intressant de rendre les classes
capables de se dcrire elles-mmes sous formes textuelles. Il devient alors possible d'envoyer
dans un fichier le contenu de toutes les variables membres d'un objet, en une seule opration :

/ / f i chi er est un QText St r eampr t r ecevoi r des donnes
f i chi er << unTr uc; / / unTr uc est une i nst ance de l a cl asse CTr uc

Il faut pour cela dfinir une fonction prenant en charge l'oprateur d'insertion lorsque son
oprande de droite est de type CTr uc :

QText St r eam& oper at or << ( QText St r eam&out , CTr uc l eTr uc) 1
{ 2
/ / i l suf f i t i ci d' envoyer chacun des membr es dans l e f l ux out l ' ai de
/ / d' i nst r uct i ons du t ype :
out << l eTr uc. l eMembr e << " \ n" ; 3
/ / et c
r et ur n out ; 4
} 5


Bien entendu, cette fonction globale ne sera autorise accder aux membres de son second
paramtre que si elle bnficie d'une relation d'amiti avec la classe CTr uc.


1
Dans le cas de l'oprateur [ ] , l'oprande de droite prend place entre les deux crochets et, en toute rigueur, les
oprandes ne sont donc pas parfaitement "de part et d'autre" du symbole de l'oprateur.
J-L Pris - 05/06/07
C++ - Leon 11 Fonctions oprateur 7/12
Remarquez que la fonction oper at or <<ne se contente pas d'insrer du texte dans le flux qui
lui est dsign par son premier paramtre, mais qu'elle prend aussi la peine de renvoyer la
rfrence ce flux qui lui a t transmise. En d'autres termes, la fonction ne se contente pas de
raliser l'effet souhait, elle confre de plus une valeur de type "rfrence un flux"
l'expression qui l'a appele. Pourquoi est-il intressant qu'une expression telle que
f i chi er << unTr uc;
soit de type "rfrence un flux" ? Parce que, de cette faon, elle dsigne un flux, ce qui permet
d'utiliser l'expression en question comme oprande de gauche d'un autre oprateur d'insertion :
f i chi er << unTr uc << " Fi n des val eur s cont enues dans unTr uc\ n"
Fonction oprateur membre d'une classe
Lorsque la fonction oprateur est membre d'une classe, la notation infixe usuelle

oper andeGauche # oper andeDr oi t ; / / not at i on i nf i xe

est quivalente l'appel explicite de la fonction ralis par

oper andeGauche. oper at or #( oper andeDr oi t ) ;

Cette quivalence syntaxique rvle immdiatement une contrainte importante :

Il n'est possible de surcharger un oprateur dyadique l'aide d'une fonction membre d'une
classe que lorsque l'oprande de gauche est une instance de cette classe.

En contrepartie, outre les trente autres oprateurs dyadiques surchargeables,

Les oprateurs [ ] et =peuvent tre surchargs par des fonctions membre.

Les fonctions dfinissant l'oprateur d'affectation renvoient habituellement une rfrence
l'objet au titre duquel elles sont excutes, ce qui permet de chaner les affectations :
t r ucUn = t r ucDeux = t r ucTr oi s;

Si la classe CTr uc comporte deux variables membres nommes m_a et m_b, il peut tre
intressant de dfinir un oprateur de test d'galit entre instances qui renvoie t r ue si et
seulement si les deux membres ont des valeurs identiques dans ses deux oprandes :

bool CTr uc: : oper at or == ( CTr uc unAut r e) 1
{ 2
r et ur n ( m_a == unAut r e. m_a && m_b == unAut r e. m_b) ; 3
} 4

En tant que fonction membre de CTr uc, notre fonction oprateur dispose de privilges lui
autorisant l'accs aux membres de son paramtre (qui est galement de classe CTr uc), mme si
ceux-ci sont protgs (ce qui est souhaitable).

Une fois cet oprateur dfini, il devient possible de comparer deux instances de CTr uc :

i f ( pr emi er Tr uc == secondTr uc)
/ / t r ai t er l e cas d' gal i t

Rappel : aucune version par dfaut de l'oprateur de comparaison n'est fournie par le langage. Si
des comparaisons entre instances sont ncessaires, il faut crire une fonction les effectuant. Il
n'est pas indispensable de surcharger l'oprateur ==, puisqu'une fonction membre nomme
est Egal A( ) et utilisant un argument du type concern permettra d'obtenir le mme effet :
i f ( pr emi er Tr uc. est Egal A( secondTr uc) )
/ / t r ai t er l e cas d' gal i t
La lisibilit de cette version est un peu moindre et, comme nous le savons, certaines
fonctionnalits des conteneurs seront rendues disponibles par une fonction oper at or ==( ) ,
mais pas par une fonction est Egal A( ) .
4 - Un cas particulier : l'oprateur "parenthses"
Les fonctions oper at or ( ) prsentent deux caractristiques remarquables, ayant trait l'une
au nombre de leurs paramtres et l'autre leur ventuelle excution la suite d'un appel
implicite. Ces caractristiques sont prfrentiellement exploites pour rpondre deux types de
J-L Pris - 05/06/07
C++ - Leon 11 Fonctions oprateur 8/12
proccupations bien diffrents, et la seconde est obtenue au prix d'une syntaxe tout fait
particulire. Une rgle fondamentale s'applique cependant dans tous les cas :

L'oprateur ( ) ne peut tre dfini que par une fonction membre.
Fonctionodes
Les fonctions surchargeant l'oprateur "parenthses" peuvent recevoir plusieurs paramtres.
En effet, bien que cet oprateur soit dyadique, son oprande de droite est une liste, ce qui
permet de fournir des valeurs permettant d'initialiser un nombre quelconque de paramtres. Si
la classe CTr uc possde une fonction oprateur du genre

voi d CTr uc: : oper at or ( ) ( i nt a, i nt b, i nt c, doubl e x) {}

il devient possible d'crire des choses comme

CTr uc unTr uc; 1
i nt a = 7; 2
unTr uc( 2, a, a+2, 2. 4) ; 3

Comme le montre le fragment de code ci-dessus, l'utilisation normale de l'oprateur ( )
ressemble l'appel d'une fonction membre dpourvue de nom : le nom de l'instance concerne
est directement suivi du couple de parenthses encadrant les valeurs transmises. Il reste bien
entendu possible d'appeler explicitement la fonction en utilisant son nom :

unTr uc. oper at or ( ) ( 2, a, a+2, 2. 4) ;

Si l'oprateur "parenthses" est parfois appel "oprateur d'appel de fonction", on voit donc
bien ce que cette expression a d'abusif : surcharger l'oprateur "parenthses" revient
simplement simuler l'existence d'une ou plusieurs fonction(s) membre anonyme(s), et ceci n'a
aucun effet sur le processus d'appel des autres fonctions. Une instance d'une classe
surchargeant ainsi l'oprateur parenthses se comporte donc comme une fonction, et les objets
de ce genre sont souvent appels "foncteurs", ou mme "fonctionodes".
Oprateurs de transtypage
Il arrive trs frquemment que tout ou partie de l'information contenue dans une variable d'un
certain type doive tre transmise une fonction disposant d'un paramtre d'un autre type.
Lorsque le type attendu est une classe, on peut rendre possible la cration automatique d'une
valeur adquate en dotant cette classe d'un constructeur de transtypage (cf. Leon 10).

L'oprateur ( ) permet d'obtenir un effet analogue mais, comme il est dfini dans la classe
source, il permet de traiter les cas o il n'est pas envisageable d'ajouter un constructeur de
transtypage au type attendu (parce que ce n'est pas une classe, ou parce que c'est une classe
faisant partie d'une librairie que l'on ne peut pas modifier, par exemple).

Imaginons, par exemple, une classe quelconque :

cl ass CDat a 1
{ 2
publ i c: 3
/ / i nt er f ace de l a cl asse
pr ot ect ed: 4
i nt a; 5
doubl e x; 6
}; 7

Si nous souhaitons disposer facilement d'une reprsentation textuelle de l'tat d'une instance
de cette classe, nous ne pouvons pas crer un constructeur de QSt r i ng prenant pour
argument une valeur de type CDat a (sauf, bien sr, si nous sommes prts recompiler la
librairie Qt et vivre avec notre propre version de la classe QSt r i ng). L'instruction

/ / i l exi st e un obj et de t ype CDat a nomm uneDat a
QSt r i ng t ext e( uneDat a) ;

peut nanmoins tre rendue acceptable en ajoutant un oprateur de transtypage CDat a.

J-L Pris - 05/06/07
C++ - Leon 11 Fonctions oprateur 9/12
La dclaration d'une telle fonction prend une forme inhabituelle, car le type de la valeur
renvoye (QSt r i ng, dans notre exemple) abandonne sa place en tte de dclaration pour venir
s'insrer entre le mot oper at or et les parenthses compltant le nom de la fonction :

cl ass CDat a 1
{ 2
publ i c: 3
/ / i nt er f ace de l a cl asse
oper at or QSt r i ng ( ) ; / / comme si c' t ai t une f onct i on nomme QSt r i ng( ) 4
pr ot ect ed: 5
i nt a; 6
doubl e x; 7
}; 8

La dfinition de l'oprateur n'a, pour sa part, rien de bien original :

CDat a: : oper at or QSt r i ng( ) 1
{ 2
QSt r i ng t ext e = " a: %1, x: %2" ; 3
r et ur n t ext e. ar g( a) . ar g( x) ; 4
} 5

Cette fonction peut tre invoque de diffrentes faons :

/ / i l exi st e un obj et de t ype CDat a nomm uneDat a
QString texte(uneData); / / appel i mpl i ci t e de CDat a: : oper at or QSt r i ng( ) 1
t ext e = uneDat a. oper at or QSt r i ng ( ) ; / / appel expl i ci t e 2
t ext e = ( QSt r i ng) uneDat a; / / t r anst ypage expl i ci t e ( synt axe hr i t e du C) 3
t ext e = ge expl i ci t e ( synt axe C++ pr i mi t i ve) QSt r i ng ( uneDat a) ; / / t r anst ypa 4
t ext e = static_cast<QString> (uneData); / / t r anst ypage expl i ci t e ( C++ act uel ) 5

Si le contexte d'utilisation n'est pas propice au transtypage implicite, c'est incontestablement la
forme 5 qui doit tre prfre.

Les formes 3 et 4 ne sont destines qu' assurer la compatibilit des textes sources antrieurs
la norme ISO du C++. La forme 2 est admissible, mais trs inhabituelle (comme tous les appels
explicites de fonctions oprateur).

La mme classe peut se trouver munie de plusieurs oprateurs de transtypage permettant
d'obtenir des valeurs de divers types. Il arrive alors parfois qu'il soit ncessaire de choisir
explicitement l'un des transtypages disponibles.

Supposons, par exemple, qu'une classe C dispose d'oprateurs de transtypage vers les types T1
et T2. Supposons galement qu'il existe deux fonctions homonymes dont les signatures ne
diffrent que parce que l'une attend une valeur de type T1 et l'autre une valeur de type T2. Si
l'on tente de passer une valeur de type C l'une de ces fonctions, le compilateur se trouve face
un dilemme : il peut soit gnrer une valeur de type T1 et appeler la premire fonction, soit
gnrer une valeur de type T2 et appeler la seconde. Cette ambigut ne peut tre rsolue qu'en
invoquant explicitement l'un des oprateurs c: : oper at or ( ) .

La disponibilit de plusieurs oprateurs de transtypages diffrents dans une mme classe cre
une situation tout fait exceptionnelle en C++, puisqu'il s'agit de fonctions homonymes ayant
la mme signature. C'est le seul cas o le langage admet des fonctions diffrentes ne se
distinguant que par le type de la valeur qu'elles renvoient.

C'est sans doute pour souligner ce privilge exceptionnel confr au type de la fonction que la
syntaxe lui fait prendre une place inhabituelle dans la dclaration de celle-ci.
5 - Oprateurs sur enum: un exemple
La possibilit de surcharger les oprateurs n'est pas rserve aux cas o ceux-ci sont appliqus
des instances de classes. Il est tout fait possible de crer des fonctions oprateur globales
dont l'un des paramtres est d'un type numr, ce qui permet d'accrotre considrablement
l'intrt de ce genre d'objets.

J-L Pris - 05/06/07
C++ - Leon 11 Fonctions oprateur 10/12
Pour bien comprendre l'exemple qui va suivre, il est ncessaire de savoir qu'il existe une
correspondance entre les valeurs numres et les valeurs entires. Ainsi, si le type EJ our est
dfini par

enumEJ our {LUNDI , MARDI , MERCREDI , J EUDI , VENDREDI , SAMEDI , DI MANCHE};

la valeur LUNDI correspond 0, MARDI 1, et ainsi de suite jusqu' DI MANCHE, qui correspond
6. Le langage opre automatiquement, en cas de besoin, la conversion du type numr vers
le type i nt , comme l'illustre le fait que

i nt x = J EUDI ;

initialise la variable x avec la valeur 3. La conversion inverse n'est en revanche pas effectue
automatiquement, et l'instruction

EJ our unJ our = 3; / / ERREUR !

ne provoquera pas l'initialisation de la variable unJ our avec la valeur J EUDI , mais une pure et
simple erreur de compilation.
Conversion d'une valeur entire en valeur de type EJ our
La premire chose faire est donc de se doter d'une fonction permettant de convertir un entier
en un EJ our de valeur quivalente. Au passage, cette fonction peut en profiter pour ramener
dans la plage de valeurs admissible les valeurs dpassant celle correspondant DI MANCHE. Les
ventuelles valeurs ngatives peuvent aussi tre ramenes dans la plage admissible, au moyen
d'une simple addition. Un transtypage explicite permet alors de convertir la valeur entire (dont
nous sommes maintenant certains qu'elle est positive et strictement infrieure 7) en une
valeur de type EJ our .

EJ our j our ( i nt val ) / / f onct i on de t r anst ypage d' i nt ver s EJ our 1
{ 2
r et ur n st at i c_cast <EJ our > ( ( ( val %=7) < 0) ? val +7 : val ) ; 3
} 4

Le type EJ our n'tant pas une classe, il ne peut pas disposer de fonctions membre, ce qui nous
empche de le doter d'un vritable oprateur de transtypage. Cette restriction ne nous prive
toutefois que de la possibilit de transtypage implicite, l'appel explicite de la fonction j our ( )
assurant exactement le traitement requis.
Reprsentation crite de la valeur d'un EJ our
Le transtypage automatique d'une valeur de type EJ our en la valeur entire quivalente ne
permet pas toujours d'obtenir l'effet souhait. Un problme frquemment rencontr est celui de
l'insertion d'un EJ our dans un flux : l'apparition de valeurs numriques dans celui-ci ne
correspond sans doute pas l'attente du programmeur qui utilise des objets de type EJ our . Ce
problme peut tre rsolu en surchargeant l'oprateur d'insertion dans les flux, de faon ce
que ceux-ci reoivent une chane de caractres plus vocatrice de la signification de la valeur.

QText St r eam& oper at or << ( QText St r eam& dest i nat i on, EJ our unJ our ) 1
{ 2
swi t ch ( unJ our ) 3
{ 4
case LUNDI : dest i nat i on << " Lundi " ; br eak; 5
case MARDI : dest i nat i on << " Mar di " ; br eak; 6
case MERCREDI : dest i nat i on << " Mer cr edi " ; br eak; 7
case J EUDI : dest i nat i on << " J eudi " ; br eak; 8
case VENDREDI : dest i nat i on out << " Vendr edi " ; br eak; 9
case SAMEDI : dest i nat i on << " Samedi " ; br eak; 10
case DI MANCHE : dest i nat i on << " Di manche" ; br eak; 11
} 12
r et ur n dest i nat i on; 13
} 14
Fonctions oper at or ++( )
J-L Pris - 05/06/07
C++ - Leon 11 Fonctions oprateur 11/12
Passer d'un jour l'autre est une opration que l'on peut incontestablement qualifier de
quotidienne. L'expression de ce phnomne dans un programme sera simplifie si l'oprateur
d'incrmentation est capable d'agir sur les EJ our . La dfinition des fonctions oprateur
rendant ceci possible est simplifie par la disponibilit de la fonction j our ( ) .

EJ our & oper at or ++ ( EJ our &unJ our ) / / ++ pr f i x 1
{ 2
unJ our = j our ( unJ our + 1) ; 3
r et ur n unJ our ; 4
} 5

EJ our oper at or ++ ( EJ our &unJ our , i nt ) / / post f i xed ++ 1
{ 2
EJ our avant = unJ our ; 3
++unJ our ; / / ut i l i sat i on de l ' opr at eur pr f i x df i ni ci - dessus ! 4
r et ur n avant ; 5
} 6

La conversion automatique est mise contribution : pour valuer le rsultat de l'addition, la
valeur de unJ our doit tre convertie en une valeur entire (puisque nous n'avons pas dfini
d'oprateur +prenant en charge les EJ our ). Ce rsultat est ensuite reconverti en une valeur de
type EJ our , qui peut ensuite tre affecte l'objet cible. L'effet de l'oprateur tant obtenu, il
reste fournir son rsultat, ce qui est simplement ralis en renvoyant la rfrence reue
comme paramtre.
Utilisation du type EJ our
Une fois ces quelques fonctions dfinies, il est possible d'crire du code ressemblant ceci :

/ / f i chi er est un QText St r eampr t r ecevoi r des donnes
EJ our auj our dhui = MERCREDI ; 1
i nt n; 2
f or ( n = 0 ; n < 9 ; n++) 3
f i chi er << auj our dhui ++ << " , " ; 4

L'excution d'une telle boucle insrerait dans f i chi er le texte suivant :

Mer cr edi , J eudi , Vendr edi , Samedi , Di manche, Lundi , Mar di , Mer cr edi , J eudi

Selon les besoins, on peut bien entendu envisager de surcharger d'autres oprateurs (- - , +=, -
=, +, - et l'extraction depuis un flux sont les premiers qui viennent l'esprit), de faon
augmenter encore les possibilits d'utilisation des variables de type EJ our .
6 - Bon, c'est gentil tout a, mais a fait dj 9 pages. Qu'est-ce que
je dois vraiment en retenir ?
1) Les fonctions oprateur permettent de dfinir la faon dont certains oprateurs s'appliquent
aux types dfinis par l'utilisateur, qu'il s'agisse de classes ou de types numrs.

2) Une fonction surchargeant un oprateur est soit globale soit membre de la classe dont
l'oprande de gauche est une instance.

3) Du point de vue de la mise au point du code qui la dfinit, une fonction oprateur n'est en
rien diffrente d'une fonction "ordinaire" qui ferait le mme travail.

4) Les oprateurs =, < et == sont les seuls dont la surcharge est parfois rellement
indispensable.

5) La disponibilit d'oprateurs surchargs peut accrotre considrablement l'agrment
d'utilisation des types dfinis par l'utilisateur, condition que l'usage fait des oprateurs
reste suffisamment proche de la signification usuelle de ceux-ci.

6) L'utilisation d'oprateurs surchargs ralisant des traitements qui s'loignent trop du sens
habituel du symbole utilis peut rendre les programmes parfaitement incomprhensibles.

J-L Pris - 05/06/07
C++ - Leon 11 Fonctions oprateur 12/12
7) Doter une classe d'un oprateur ( ) peut ouvrir la possibilit de transtypages automatiques
gnrant une valeur d'un type prdfini partir de la valeur d'une instance de cette classe.


J-L Pris - 05/06/07

S-ar putea să vă placă și