Vous êtes sur la page 1sur 11

Ecole Nationale de Techniques Avancées

Simulation Numérique
Chapitre 4

Surcharge des opérateurs


Eric Lunéville

Surcharge des opérateurs


Le C++ utilise des opérateurs prédéfinis tels que + - * / ... sur les types
primaires (int, float...)
Il autorise la redéfinition de la plupart de ces opérateurs sur des objets
définis par l'utilisateur. On parle de surcharge des opérateurs
Redéfinition liée à la classe, implémentée par l'utilisateur
Le mécanisme de compilation (analyse syntaxique) recherche s'il y a des
implémentations correspondantes
Dans le contexte du calcul scientifique, leur utilisation augmente la lisibilité
des codes :
int main ( ) int main ( )
{ m a t r i c e A, B,C ; { m a t r i c e A, B,C ;
... ...
m a t r i c e D=p l u s (A, B ) ;D=p l u s (D,C ) ; m a t r i c e D=A+B+C;
} }

développement de la classe plus complexe et plus rigoureux !


Sim. Numérique – chapitre 4 - 1
Plan

Syntaxe de la surcharge d'opérateur


Surcharge interne et externe
Modèle de programmation
Principaux opérateurs
Opérateur = et opérateur de conversion
Opérateurs de flux
Exemples
Résumé

Sim. Numérique – chapitre 4 - 2

Syntaxe de la surcharge
On utilise le mot réservé operator :
arg_retour operator nom_operateur (args_entrée)
nombre d'arguments d'entrée lié au type d'opérateur (unaire, binaire)
argument de sortie dépend des objectifs fixés

Exemple : multiplication d'un point par un réel


c l a s s POINT
{ public : i n t main ( )
i n t dim ; {POINT P ( 3 , 1 ) ;
double ∗ pCor ; POINT Q=P ∗ 2 . ;
POINT( i n t d , double v = 0) ; };
POINT( const POINT & P ) ;
POINT operator ∗ ( double d ) ;
};
sémantiquement : P*2. ∼ P.(operator *)(2.)
POINT POINT : : operator ∗ ( double d ) opérateur unaire
{POINT Q( dim ) ;
f o r ( i n t i =0; i <dim ; i ++)
Q. pCor [ i ]=pCor [ i ] ∗ d ;
2*P n’est pas possible ! (op binaire)
return Q;
}
Sim. Numérique – chapitre 4- 3
Surcharge externe
Déclaration dans une classe : surcharge interne
ne fonctionne pas pour l'opération : double * POINT

Déclaration à l’extérieur d’une classe : surcharge externe


opérateur binaire (à 2 arguments)
POINT operator ∗ ( double d , const POINT & P) i n t main ( )
{POINT Q( dim ) ; {
f o r ( i n t i =0; i <dim ; i ++) POINT M( 3 , 1 ) ;
Q. pCor [ i ]=P . pCor [ i ] ∗ d ; POINT P=M∗ 2 ; // ∗ i n t e r n e
return Q; POINT Q=3∗M; // ∗ e x t e r n e
} }

Pas d'ambigüité : arguments d'entrée distincts entre les deux versions

Dans l'écriture M*2 ou 3*M, 2 et 3 sont des entiers,


il y a conversion automatique en double

Sim. Numérique – chapitre 4- 4

Surcharge interne ou externe ?


On peut également n'utiliser que des surcharges externes
POINT * double (externe) double*POINT (externe)
POINT operator ∗ ( const POINT &P , POINT operator ∗ ( double d ,
double d ) const POINT &P)
{POINT Q( dim ) ; {POINT Q( dim ) ;
f o r ( i n t i =0; i <dim ; i ++) f o r ( i n t i =0; i <dim ; i ++)
Q. pCor [ i ]=P . pCor [ i ] ∗ d ; Q. pCor [ i ]=P . pCor [ i ] ∗ d ;
return Q; return Q;
} }

pCor doit être public ici

Aucune règle absolue pour le choix externe ou interne !


Une suggestion : opération externe si l'opération n'induit pas de modification
des données membre de l’objet en entrée
meilleure visibilité
plus de cohérence
Sim. Numérique – chapitre 4- 5
Surcharge interne conseillée
Pour les opérateurs unaires « auto-écrasants » += -= *= /= …
surcharge interne (données membre modifiées)

Exemple POINT *=double


c l a s s POINT
{ public : On retourne une référence
... sur l'objet lui-même (*this),
POINT & operator ∗= ( double d ) ;
}; autorise :
POINT & POINT : : operator ∗= ( double d )
{ f o r ( i n t i =0; i <dim ; i ++)
M=(P*=2)
pCor [ i ]∗=d ;
return ∗ t h i s ;
}

Sim. Numérique – chapitre 4 - 6

Modèle de programmation
c l a s s POINT
{ public :
...
POINT & operator ∗= ( double d ) ;
};
*= surcharge interne

POINT & POINT : : operator ∗= ( double d )


{ f o r ( i n t i =0; i <dim ; i ++) pCor [ i ]∗=d ;
return ∗ t h i s ;
}

// d o u b l e ∗P e t P∗ d o u b l e e x t e r n e s * surcharge externe
POINT operator ∗ ( double d , const POINT & P)
{ return POINT(P∗=d ) ; }
POINT operator ∗ ( const POINT & P , double d )
{ return POINT(P∗=d ) ; }

l’opérateur * appelle l’opérateur *=


plus robuste, une seule implémentation

rmq : P*=d retourne une référence transmise au constructeur par copie


Sim. Numérique – chapitre 4 - 7
Quoi retourner ?
La plupart des opérations retourne un objet, comment le retourner :
par valeur (classe), par référence (classe &), par pointeur (classe *) ?

Jamais par pointeur


interdit l’enchainement des opérations A + (B+C) (A+D) + (B+C)
objet pointeur pointeur pointeur

Par référence pour les opérations unaires (surcharge interne)


opération sur l’objet courant A+=B équivalent à A.(operator +=)(B)

Par valeur pour les opérations binaires (surcharge externe)


création d’un nouvel objet résultat A +B C
on ne doit pas retourner une référence dans ce cas !
D=(A+B) + C
référence objet

sur un objet temporaire alloué dynamiquement, ne sera pas détruit !

Sim. Numérique – chapitre 4 - 8

Opérateurs surchargeables
Principaux opérateurs

+ - * / % ^ & | ~ =
+= -= *= /= %= ^= &= |= ++ --
! == != < > <= >= && ||
<< >> () []  *
Opérateurs unaires ou binaires suivant les cas

new et delete sont également surchargeables

Sim. Numérique – chapitre 4 - 9


Surcharge rationnelle
Utiliser le sens usuel des opérateurs
Eviter des surcharges fantaisistes
// d i v i s i o n de 2 p o i n t s ! ! !
POINT operator /( const POINT &,const POINT &);

Eviter toutes confusions possibles, par exemple :


Opérateur d’accès à un élément d’un tableau ou vecteur :
operator ( )(int i ) : i=1 à n , notation mathématique
operator [ ](int i ) : i=0 à n-1 , notation informatique

class vect
{ public :
double ∗ v a l ; // t a b l e a u de v a l e u r s
...
double & operator ( ) ( i nt i ) { return v a l [ i + 1 ] ; }
double & operator [ ] ( i nt i ) { return v a l [ i ] ; }
}
Sim. Numérique – chapitre 4 -10

Opérateur =
L’opérateur = est défini par défaut pour tout type :
recopie des données membre pour une classe
Si la construction induit des op. d’allocation dynamique
il est impératif de le surcharger (idem constructeur par copie)

Exemple :
c l a s s POINT POINT& POINT : : operator=(const POINT &P)
{ public : { i f ( t h i s==&P) return ∗ t h i s ; //P=P
i n t dim ; i f ( dim!=P . dim ) // r é a l l o c a t i o n
double ∗ pCor ; { d el et e [ ] pCor ;
POINT( i n t d , double v = 0) ; pCor=new double [ P . dim ] ;
POINT( const POINT & P ) ; }
POINT& operator = ( const POINT & ); dim=P . dim ;
}; f o r ( i n t i =0; i <dim ; i ++)
pCor [ i ]=P . pCor [ i ] ;
return ∗ t h i s ;
}

Sim. Numérique – chapitre 4 - 11


Opérateur de conversion
Cas particulier de l’ « opérateur » de conversion/transtypage
Syntaxe générale : operator type_sortie () (type_entrée)

Exemple : operator POINT ( ) ( const v e c t & V) ;

v e c t V( 3 , 1 . ) ;
POINT P=POINT(V) ;

Pas d’argument de retour, s’apparente à un constructeur


Est utilisé implicitement par le compilateur s'il y a un transtypage
par exemple : fonction attendant un POINT et recevant un vect
Attention d'une utilisation délicate, augmente les ambiguïtés;

Il est plus simple d'utiliser un constructeur de la classe POINT :


POINT( const v e c t &);
Sim. Numérique – chapitre 4 - 12

Opérateurs de flux
Flux (stream) : canal par lequel transitent des informations
succeptibles d’être transformées

traitement

Opérations basiques :
ouvrir (open) un flux flux .open();
fermer (close) un flux flux .close();
envoyer une information : opérateur << flux << information
extraire une information : opérateur >> flux >> information

Flux standards : std::cout : flux d’écriture (écran) classe ostream


std::cin : flux de lecture (clavier) classe istream

Flux de fichiers : classes ofstream, ifstream et fstream


Sim. Numérique – chapitre 4 - 13
Opérateurs de flux
Surcharge des opérateurs << et >> pour un flux d’écriture
ou de lecture
c l a s s POINT { . . . } ;

ostream & operator <<(ostream &out , const POINT &P)


{ out<<” Poi nt : ” ;
for ( in t i =0; i <P . dim ; i ++) out<<P( i )<<” ” ;
out<<e n d l ;
return out ; }

i s t r e a m & operator >>( i s t r e a m &in , const POINT &P)


{ for ( in t i =0; i <P . dim ; i ++) in>>P( i ) ;
return i n ; }

flux non const en entrée


on retourne le flux (référence!)
Sim. Numérique – chapitre 4 - 14

Flux : insertion dans une liste


Insertion dans une liste de points à l’aide de <<
c l a s s l i s t e P OI N T
{ public :
POINT ∗∗ l i s t e ; // t a b l e a u de p o i n t e u r s
unsigned i n t n b p o i n t s ; // nb de p o i n t s
l i s t e P O I N T ( unsigned i n t l =1 0) ; // c o n s t r u c t e u r
˜ l i s t e P OI N T ( ) ; // d e s t r u c t e u r
void a l l o n g e ( ) ; // augmente l a t a i l l e du t a b l e a u de 10
POINT & operator ( const i n t & ) ; // a c c e s au i eme p o i n t
add ( const POINT &); // a j o u t d ’ un p o i n t
};

l i s t e P O I N T & operator <<(l i s t e P O I N T &L , const POINT &P)


{L . add (P ) ; return L ; }

POINT P ( 3 , 1 ) ;
liste POINT l i s t e ( ) ;
l i s t e <<P ;

Sim. Numérique – chapitre 4 - 15


Exemple "complet" de surcharge
Surcharge des opérateurs de la classe POINT
c l a s s POINT
{ public :
...
double& operator ( ) ( i n t i ) ;
POINT & operator = ( const POINT &) ;
POINT & operator += ( const POINT &) ;
POINT & operator −= ( const POINT &) ;
POINT & operator ∗= ( const double &) ;
POINT & operator /= ( const double &) ;
bool operator==(const POINT &) ;
bool operator !=( const POINT &) ;
};

POINT operator+(const POINT & ); + et – (opposé) unaire


POINT operator −(const POINT & ); (+ pour la compatibilité)
POINT operator+(const POINT &, const POINT &) ;
POINT operator −(const POINT &, const POINT &) ;
POINT operator ∗ ( const POINT &, const double &);
POINT operator / ( const POINT &, const double &);
POINT operator ∗ ( const double &, const POINT &);
POINT operator / ( const double &, const POINT &);

o s t re am & operator <<(o st r ea m &, const POINT &) ;


i s t r e a m & operator >>( i s t r e a m &, const POINT &) ;
Sim. Numérique – chapitre 4 - 16

Surcharge externe et données privées


Un opérateur défini en surcharge externe ne peut pas accéder
aux données privées de la classe !
utiliser l’attribut friend pour qu’il y ait accès :
c l a s s POINT
{ private
in t dim ;
double ∗pCor ;
public
...
friend ostream& operator <<(ostream &, const POINT &);
};

ostream & operator<<(ostream & out , const POINT & P)


{ f or ( int i =0; i <P . dim ; i ++) out<<P . pCor [ i ]<<<<” ” ;
return out ;
}

Sim. Numérique – chapitre 4 - 17


Un exemple plus complexe
Supposons que l'on dispose d'une classe de matrices (matrice)
On souhaite faire une combinaison linéaire de plusieurs grosses matrices.

Calcul de M=2*A+4*D-5*C :
T1=5*C , T2=4*D , T3=T1-T2 , T4=2*A et enfin T5=T4+T3
soit 5 générations de matrices temporaires avec recopie !

Solution : utiliser une classe intermédiaire (combinaison) décrivant la combinaison


linéaire et n'effectuer le calcul qu'au moment du = :

5*C C1 , 4*D  C2 , C3=C1-C2 , 2*A  C4 C4+C3  M

une seule recopie finale !

Sim. Numérique – chapitre 4 - 18

Un exemple plus complexe


class matrice { . . . } ; // c l a s s e ma t r i c e

cl a ss combinaison // p o i n t e u r s m a tr i c e e t c o e f f s
{ public :
int nb matrice ;
m a t r i c e ∗∗ l i s t e m a t r i c e ;
double ∗ liste coefficients ;
...
}

c o m b i n a i s o n operator ∗ ( double , const m a t r i c e &) ;


c o m b i n a i s o n operator ∗ ( const m a t r i c e &,double ) ;
c o m b i n a i s o n operator+(const m a t r i c e &, const m a t r i c e &) ;
c o m b i n a i s o n operator −(const m a t r i c e &, const m a t r i c e &) ;
c o m b i n a i s o n& operator+(const m a t r i c e &, c o m b i n a i s o n &);
c o m b i n a i s o n& operator+( c o m b i n a i s o n &, const m a t r i c e &);
c o m b i n a i s o n& operator −(const m a t r i c e &, c o m b i n a i s o n &);
c o m b i n a i s o n& operator −( c o m b i n a i s o n &, const m a t r i c e &);
c o m b i n a i s o n& operator+( c o m b i n a i s o n &, const c o m b i n a i s o n& ) ;
c o m b i n a i s o n& operator −( c o m b i n a i s o n &, const c o m b i n a i s o n& ) ;
c o m b i n a i s o n& operator ∗ ( c o m b i n a i s o n &,double ) ;
c o m b i n a i s o n& operator ∗ ( double , c o m b i n a i s o n &);
m a t r i c e m a t r i c e : : operator=(const c o m b i n a i s o n &) ;

Sim. Numérique – chapitre 4 - 19


Résumé
Ne jamais surcharger un opérateur avec un autre sens que le sens commun

Limiter le nombre d'implémentations pour des opérations similaires (+ et +=)

ne pas renvoyer de pointeurs, résultat inutilisable dans une autre opération

renvoyer des références pour des opérateurs en surcharge interne

pour une classe personnelle ne surcharger que l'essentiel

pour une classe générale, prévoir toutes les surcharges possibles (complétude)

la surcharge des opérateurs n'est pas obligatoire


elle apporte un confort et augmente la lisibilité pour l’utilisateur

Sim. Numérique – chapitre 4 - 20