Chapitre 5 – Surcharge d’opérateur
Document réservé
Vous consultez actuellement la présentation publique de ce chapitre.
Les documents PDF complets, comprenant les développements théoriques, les exemples détaillés et les exercices, sont disponibles sur demande.
Pour obtenir un accès, contactez-moi via la page Contact en indiquant les domaines qui vous intéressent (C++, ESP-IDF, électronique, etc.).
5 Surcharge d’opérateur
5.1 Pourquoi surcharger un opérateur ? . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.1.1 Et pour nos propres classes ? . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.1.2 Une méthode classique serait possible . . . . . . . . . . . . . . . . . . . . . .
5.1.3 Surcharger ne signifie pas créer un nouvel opérateur . . . . . . . . . . . . . .
5.2 Une classe pour représenter les nombres complexes . . . . . . . . . . . . . . . . . . .
5.2.1 Implémentation de la classe . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.4 Que signifie réellement l’expression z1 + z2 ? . . . . . . . . . . . . . . . . . . . . . .
5.4.1 L’objet de gauche et l’objet de droite . . . . . . . . . . . . . . . . . . . . . . .
5.5 Surcharger les opérateurs – et * . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.5.1 Surcharger l’opérateur – . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.5.2 Surcharger l’opérateur * . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Combiner les opérateurs . . . . . . . . . . . . . . . . . . . . . . . .
5.6 Surcharger les opérateurs == et != . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.8 Retour sur l’opérateur d’affectation = . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.8.1 Surcharger l’opérateur d’affectation . . . . . . . . . . . . . . . . . . . . . . . .
5.8.2 Retourner l’objet qui vient d’être modifié . . . . . . . . . . . . . . . . . . . .
5.8.3 Les affectations en chaîne . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.8.4 Une surcharge ici essentiellement pédagogique . . . . . . . . . . . . . . . . .
5.9 Surcharger l’opérateur « . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.9.1 L’objet situé à gauche . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.9.2 Une fonction opérateur extérieure à la classe . . . . . . . . . . . . . . . . . .
5.9.5 Une autorisation accordée par la classe . . . . . . . . . . . . . . . . . . . . . .
5.9.6 friend est-il indispensable ? . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.10 Quels opérateurs peut-on surcharger ? . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.10.1 Les opérateurs doivent déjà exister . . . . . . . . . . . . . . . . . . . . . . . .
5.10.2 Quelques opérateurs ne peuvent pas être surchargés . . . . . . . . . . . . . .
5.10.3 Les règles syntaxiques restent celles du C++ . . . . . . . . . . . . . . . . . .
5.10.3.1 Conserver une signification naturelle . . . . . . . . . . . . . . . . .
La surcharge d’opérateur
AccueilSurcharge d’opérateur Pourquoi surcharger un opérateur ? Les opérateurs du C++ permettent d’effectuer des opérations sur les types fondamentaux du langage. Nous pouvons par exemple écrire : L’opérateur + possède ici une signification parfaitement définie : il effectue l’addition de deux nombres Il en va de même pour d’autres types fondamentaux : Le C++ sait donc naturellement ce que signifie l’expression lorsque les objets manipulés appartiennent à des types pour lesquels cette opération est définie. Et pour nos propres classes ? Considérons maintenant une classe représentant un nombre complexe. Un nombre complexe peut s’écrire sous la forme où a représente la partie réelle et b la partie imaginaire. Nous pourrions construire une classe contenant ces deux valeurs : double partieReelle; double partieImaginaire; La surcharge d’opérateur Nous pouvons alors créer deux nombres complexes : Complexe z1(2.0, 3.0); Complexe z2(4.0, 5.0); Mathématiquement, leur somme est parfaitement définie : (2 + 3i ) + (4 + 5i ) = 6 + 8i . Nous aimerions donc naturellement pouvoir écrire : Complexe z3 = z1 + z2; Mais le compilateur ne peut pas deviner ce que l’opérateur + doit faire avec les objets que nous avons nous-mêmes définis. La classe Complexe est un nouveau type créé par le programmeur. Nous devons donc indiquer au compilateur quelle signification doit prendre l’opérateur + lorsqu’il est appliqué à deux objets de ce type. Cette opération porte le nom de surcharge d’opérateur. Une méthode classique serait possible Nous pourrions parfaitement définir une méthode : Complexe additionner(const Complexe& autre) const; Complexe z3 = z1.additionner(z2); Cette solution serait correcte. Cependant, dans le cas d’objets possédant une opération mathématique naturelle, l’écriture Complexe z3 = z1 + z2; est beaucoup plus proche de la notation habituelle : La surcharge d’opérateur permet donc notamment de donner aux objets créés par le programmeur une syntaxe similaire à celle des types fondamentaux. Surcharger ne signifie pas créer un nouvel opérateur Le C++ permet de redéfinir le comportement de nombreux opérateurs existants pour les objets appartenant à nos propres classes. Nous pouvons par exemple donner une signification à
AccueilLa surcharge d’opérateur Cependant, la surcharge d’opérateur ne permet pas de créer de nouveaux opérateurs. Nous ne pouvons pas inventer, par exemple, un opérateur si cette séquence ne constitue pas un opérateur défini par le langage. De même, la surcharge ne permet pas de modifier les règles syntaxiques associées à un opérateur existant. Sa priorité et son associativité restent celles définies par le langage. Ainsi, l’expression reste interprétée comme même si + et ∗ ont été surchargés pour les classes des objets A, B et C. Nous pouvons donc modifier la signification d’une opération pour nos objets, mais pas les règles grammaticales de l’opérateur lui-même. • La surcharge d’opérateur permet de définir le comportement d’un opérateur existant pour des objets appartenant à des classes créées par le programmeur. • Une expression telle que peut ainsi être utilisée avec des objets de type Complexe. • La surcharge d’opérateur n’est pas indispensable : une méthode classique peut généralement réaliser la même opération. Elle permet surtout d’obtenir une notation plus naturelle lorsque l’opérateur possède une signification logique pour les objets concernés. • Il n’est pas possible de créer de nouveaux opérateurs. • La surcharge ne modifie ni la priorité ni l’associativité des opérateurs existants. Une classe pour représenter les nombres complexes Pour étudier la surcharge d’opérateur, nous allons utiliser une classe représentant les nombres complexes. Un nombre complexe s’écrit sous la forme où a représente sa partie réelle et b sa partie imaginaire. Nous allons représenter ces deux valeurs par deux membres de type double :
AccueilLa surcharge d’opérateur double partieReelle; double partieImaginaire; double getPartieReelle() const; double getPartieImaginaire() const; void afficher() const; Cette classe ne contient pour l’instant aucune surcharge d’opérateur. Elle utilise uniquement des mécanismes déjà étudiés. Implémentation de la classe Le constructeur initialise les parties réelle et imaginaire à l’aide d’une liste d’initialisation : : partieReelle(reelle), partieImaginaire(imaginaire) Les deux accesseurs permettent de consulter les composantes du nombre complexe : double Complexe::getPartieReelle() const return partieReelle; double Complexe::getPartieImaginaire() const return partieImaginaire; Pour faciliter nos premiers essais, ajoutons une méthode d’affichage : void Complexe::afficher() const std::cout << partieReelle Nous pouvons maintenant écrire :
AccueilComplexe z1(2.0, 3.0); Complexe z2(4.0, 5.0); La surcharge d’opérateur Le programme affiche : Nous disposons maintenant de deux objets : Complexe z1(2.0, 3.0); Complexe z2(4.0, 5.0); Nous savons mathématiquement calculer Nous aimerions donc pouvoir écrire directement : Complexe z3 = z1 + z2; Il reste à expliquer au compilateur ce que signifie l’opérateur + lorsqu’il est placé entre deux objets Surcharger l’opérateur + Nous souhaitons pouvoir additionner deux objets de type Complexe à l’aide de la notation naturelle : Complexe z3 = z1 + z2; Mathématiquement, si z 1 + z 2 = (a + c) + i (b + d ). Il suffit donc d’additionner séparément les parties réelles et les parties imaginaires. Nous allons définir dans la classe une fonction particulière appelée operator+ : const Complexe& autre La classe devient donc :
AccueilLa surcharge d’opérateur double partieReelle; double partieImaginaire; double getPartieReelle() const; double getPartieImaginaire() const; const Complexe& autre void afficher() const; L’implémentation de l’opérateur est très proche de celle d’une méthode classique : Complexe Complexe::operator+( const Complexe& autre + autre.partieReelle, + autre.partieImaginaire Dans cette méthode, deux objets interviennent. L’objet placé à gauche de l’opérateur est l’objet sur lequel la méthode est appelée. L’objet placé à droite est reçu dans le paramètre const Complexe& autre Ainsi, dans l’expression z1 correspond à l’objet courant, tandis que z2 correspond au paramètre autre. peut donc être comprise comme un appel équivalent à : L’opérateur + constitue ainsi une syntaxe particulière permettant d’appeler la fonction operator+().
AccueilLa surcharge d’opérateur Complexe z1(2.0, 3.0); Complexe z2(4.0, 5.0); Complexe z3 = z1 + z2; Le programme affiche : ce qui correspond bien à (2 + 3i ) + (4 + 5i ) = 6 + 8i . • Une surcharge d’opérateur est réalisée à l’aide d’une fonction dont le nom commence const Complexe& autre définit le comportement de l’opérateur + pour les objets de type Complexe. • Dans l’expression l’objet z1 est l’objet courant et z2 est transmis comme argument à operator+(). • Cette expression peut être comprise comme : • La fonction retourne ici un nouvel objet Complexe contenant le résultat de l’addition. Que signifie réellement l’expression z1 + z2 ? Complexe z3 = z1 + z2; peut donner l’impression que l’opérateur + possède un mécanisme particulier entièrement différent d’un appel de méthode classique. Ce n’est pas le cas. Lorsque operator+() est défini comme méthode membre de la classe Complexe, l’expression peut être comprise comme : Nous retrouvons donc exactement la structure d’un appel de méthode.
AccueilLa surcharge d’opérateur L’objet de gauche et l’objet de droite l’objet z1 est l’objet courant. À l’intérieur de la méthode, il est donc accessible implicitement à travers le pointeur this. L’objet z2, placé à droite de l’opérateur, est reçu dans le paramètre : const Complexe& autre Nous pouvons donc écrire conceptuellement : Complexe Complexe::operator+( const Complexe& autre + autre.partieReelle, this->partieImaginaire + autre.partieImaginaire Comme dans les autres méthodes membres, l’utilisation explicite de this−> n’est pas nécessaire. Les désignent ici le même membre de l’objet courant.Comme dans les autres méthodes membres, l’utilisation explicite de this−> n’est pas nécessaire. désignent ici le même membre de l’objet courant. Création du résultat L’opération ne modifie ni z1 ni z2. Elle construit un nouvel objet contenant la somme : partieReelle + autre.partieReelle, partieImaginaire + autre.partieImaginaire Le constructeur de Complexe est donc appelé pour créer l’objet résultat.
AccueilLa surcharge d’opérateur La fonction operator+() retourne un objet de type Complexe par valeur : const Complexe& autre Ce choix est naturel : le résultat de l’addition est un nouvel objet qui doit continuer à exister après la fin de l’appel de la méthode. Complexe z3 = z1 + z2; utilise donc l’objet retourné pour initialiser z3. est une syntaxe particulière correspondant ici à un appel de méthode : • L’objet de gauche est l’objet courant ; l’objet de droite est transmis comme argument. • L’opérateur ne modifie pas nécessairement ses opérandes. Dans notre cas, il construit un nouvel objet contenant le résultat. • Le mot-clé const placé après la méthode garantit que l’objet situé à gauche de l’opérateur n’est pas modifié. Surcharger les opérateurs – et * Une fois la surcharge de l’opérateur + comprise, les autres opérations arithmétiques se construisent de la même manière. Pour les nombres complexes, la soustraction et la multiplication possèdent toutes deux une signification mathématique naturelle. Nous allons donc permettre les écritures : Complexe z3 = z1 – z2; Complexe z4 = z1 * z2; Surcharger l’opérateur –
AccueilLa surcharge d’opérateur z 1 − z 2 = (a − c) + i (b − d ). Nous ajoutons donc à la classe Complexe : const Complexe& autre Son implémentation est : Complexe Complexe::operator-( const Complexe& autre – autre.partieReelle, – autre.partieImaginaire peut être comprise comme : L’objet z1 est l’objet courant et z2 est transmis à la méthode comme argument. Surcharger l’opérateur * La multiplication de deux nombres complexes demande un calcul légèrement plus élaboré. z 1 z 2 = (a + i b)(c + i d ). z 1 z 2 = ac + i ad + i bc + i 2 bd . z 1 z 2 = (ac − bd ) + i (ad + bc). Nous déclarons dans la classe :
AccueilLa surcharge d’opérateur const Complexe& autre puis nous définissons : Complexe Complexe::operator*( const Complexe& autre partieReelle * autre.partieReelle – partieImaginaire * autre.partieImaginaire; partieReelle * autre.partieImaginaire + partieImaginaire * autre.partieReelle; return Complexe(reelle, imaginaire); Nous pouvons maintenant écrire : Complexe z1(2.0, 3.0); Complexe z2(4.0, 5.0); Complexe somme = z1 + z2; Complexe difference = z1 – z2; Complexe produit = z1 * z2; difference.afficher(); 10 produit.afficher(); (2 + 3i )(4 + 5i ) = 8 + 10i + 12i + 15i 2 = −7 + 22i . Combiner les opérateurs Puisque les opérateurs ont maintenant été définis pour notre classe, ils peuvent être combinés dans des expressions plus complexes. Nous pouvons par exemple écrire : Complexe resultat = z1 + z2 * z3; Les règles habituelles de priorité des opérateurs restent applicables. L’opérateur ∗ étant prioritaire sur l’opérateur +, cette expression est interprétée comme :
AccueilLa surcharge d’opérateur Complexe resultat = z1 + (z2 * z3); La surcharge modifie donc le comportement des opérateurs pour nos objets, mais elle ne modifie pas les règles de priorité du langage. Nous pouvons même comprendre cette expression comme une succession d’appels de méthodes : z1.operator+(z2.operator*(z3)) La notation utilisant les opérateurs est évidemment beaucoup plus naturelle : • Les opérateurs − et ∗ se surchargent selon le même principe que l’opérateur +. peuvent être comprises respectivement comme : • Chaque opérateur doit recevoir une signification cohérente avec les objets manipulés. • La surcharge d’un opérateur ne modifie pas sa priorité. Ainsi, ∗ reste prioritaire sur + et • Les opérateurs surchargés peuvent être combinés pour former des expressions complexes utilisant une notation naturelle. Surcharger les opérateurs == et != Les opérateurs de comparaison peuvent également être surchargés. Pour deux nombres complexes nous considérerons qu’ils sont égaux si leurs parties réelles et imaginaires sont respectivement égales : Nous souhaitons donc pouvoir écrire :
AccueilLa surcharge d’opérateur L’opérateur != peut être défini à partir de l’opérateur == déjà disponible : bool Complexe::operator!=( const Complexe& autre return !(*this == autre); utilise l’opérateur == que nous venons de surcharger. L’opérateur logique ! inverse ensuite le résultat. • Les opérateurs de comparaison peuvent eux aussi être surchargés. • L’opérateur == retourne ici une valeur de type bool. peut être comprise comme • L’opérateur != peut être défini à partir de == afin d’éviter de répéter la même logique. • Une surcharge d’opérateur peut donc retourner un type différent de celui des objets Surcharger l’opérateur += Jusqu’à présent, les opérateurs +, − et ∗ construisent un nouvel objet contenant le résultat de l’opération. Complexe z3 = z1 + z2; ne modifie ni z1 ni z2. L’opérateur += possède un comportement différent. L’instruction doit modifier directement l’objet z1. L’opérateur retourne une référence vers l’objet courant : La valeur retournée est :
AccueilLa surcharge d’opérateur c’est-à-dire l’objet sur lequel l’opérateur a été appliqué. • L’opérateur += modifie l’objet placé à gauche. • Contrairement à +, il ne construit pas nécessairement un nouvel objet contenant le • La surcharge retourne généralement une référence vers l’objet courant : Complexe& operator+=( const Complexe& autre retourne l’objet courant lui-même. • Ce choix permet notamment les opérations en chaîne. Retour sur l’opérateur d’affectation = L’opérateur d’affectation = peut lui aussi être surchargé. Considérons deux objets de notre classe Complexe z1(2.0, 3.0); Complexe z2(5.0, 7.0); affecte les valeurs contenues dans z2 à l’objet z1. Comme pour les autres opérateurs étudiés dans ce chapitre, cette écriture peut être comprise comme un appel de méthode : Surcharger l’opérateur d’affectation Nous pouvons déclarer l’opérateur d’affectation dans la classe Complexe : Complexe& operator=( const Complexe& autre Son implémentation peut être écrite : Complexe& Complexe::operator=( const Complexe& autre partieReelle = autre.partieReelle; partieImaginaire = autre.partieImaginaire;
AccueilLa surcharge d’opérateur z1 est l’objet courant. contient donc l’adresse de z1. L’objet z2 est transmis à la méthode par l’intermédiaire du paramètre : const Complexe& autre Les deux affectations partieReelle = autre.partieReelle; partieImaginaire = autre.partieImaginaire; modifient ainsi les membres de l’objet courant. Retourner l’objet qui vient d’être modifié La méthode retourne : Elle retourne donc une référence vers un objet de type Complexe, et non une copie de cet objet. déréférence le pointeur this et désigne ainsi l’objet courant lui-même. Dans notre exemple, ∗this désigne donc z1. L’opérateur retourne ainsi une référence vers l’objet qui vient d’être modifié. ne crée pas un nouvel objet Complexe. Il retourne une référence vers l’objet courant. Les affectations en chaîne Le retour d’une référence vers l’objet courant permet notamment d’écrire : L’opération est effectuée en commençant par : Cette opération modifie z2 puis retourne une référence vers z2. Cette référence peut alors être utilisée pour effectuer :
AccueilLa surcharge d’opérateur Les trois objets contiennent finalement les mêmes valeurs. Une surcharge ici essentiellement pédagogique Dans notre classe Complexe, les deux membres sont de type double : double partieReelle; double partieImaginaire; Le compilateur est parfaitement capable de réaliser lui-même leur affectation. Même si nous n’écrivions aucun operator=(), l’instruction fonctionnerait donc correctement. Nous avons ici écrit explicitement cet opérateur afin d’en comprendre le fonctionnement. Pour certaines classes gérant directement des ressources, une définition particulière de l’opérateur d’affectation peut en revanche devenir nécessaire. Nous avons déjà rencontré cette situation avec une classe gérant une zone de mémoire allouée dynamiquement : copier uniquement la valeur d’un pointeur ne suffit pas lorsque nous voulons obtenir une véritable copie indépendante des données. La surcharge de l’opérateur d’affectation permet alors de définir précisément le comportement souhaité. • L’opérateur d’affectation = peut être surchargé comme les autres opérateurs. peut être comprise comme : • L’opérateur d’affectation modifie l’objet situé à gauche. • Il retourne généralement une référence vers cet objet : • Le type de retour Complexe& évite de retourner une copie de l’objet. • Pour une classe simple comme Complexe, l’opérateur d’affectation fourni automatiquement par le compilateur est suffisant. Surcharger l’opérateur « Notre classe Complexe possède actuellement une méthode afficher() permettant d’écrire :
AccueilComplexe z(2.0, 3.0); La surcharge d’opérateur Nous aimerions cependant utiliser la syntaxe habituelle du C++ : et obtenir par exemple : Pour y parvenir, nous devons surcharger l’opérateur <<. L’objet situé à gauche Jusqu’à présent, les opérateurs surchargés étaient définis comme des méthodes de la classe Complexe. pouvait être compris comme : L’objet situé à gauche de l’opérateur était donc un objet de type Complexe. Considérons maintenant : L’objet situé à gauche de l’opérateur n’est plus z, mais std::cout. Il n’est donc pas possible de traiter cette expression comme un appel d’une méthode operator<<() de notre classe Complexe. Une fonction opérateur extérieure à la classe Un opérateur surchargé ne doit pas nécessairement être une méthode membre d’une classe. Nous pouvons définir une fonction extérieure : std::ostream& operator<<( std::ostream& sortie, Cette fois, les deux opérandes apparaissent explicitement comme paramètres de la fonction.
AccueilLa surcharge d’opérateur • Un opérateur surchargé peut être implémenté par une méthode membre ou par une fonction extérieure à la classe. • Pour une méthode membre, l’opérande de gauche correspond à l’objet courant. • Dans l’expression l’opérande de gauche est std::cout et non l’objet z. • Nous pouvons donc définir une fonction extérieure : std::ostream& operator<<( std::ostream& sortie, • Les deux opérandes apparaissent alors explicitement comme paramètres de la fonction. Nous avons défini l’opérateur << comme une fonction extérieure à la classe Complexe : std::ostream& operator<<( std::ostream& sortie, sortie << z.getPartieReelle() << z.getPartieImaginaire(); Cette fonction peut utiliser les méthodes publiques de la classe, mais elle ne possède aucun accès particulier à ses membres privés. Par exemple, l’écriture suivante est normalement interdite : cout << z.partieReelle; car partieReelle est un membre privé de Complexe. Une classe peut autoriser explicitement une fonction extérieure à accéder à ses membres privés. Cette autorisation est donnée à l’aide du mot-clé friend : friend std::ostream& operator<<( std::ostream& sortie, La fonction operator<<() reste une fonction extérieure à la classe. Elle ne devient pas une méthode de Complexe. Elle reçoit cependant l’autorisation d’accéder aux membres privés de ses objets.
AccueilLa surcharge d’opérateur Une autorisation accordée par la classe Le mot-clé friend apparaît dans la déclaration de la classe qui accorde l’accès. La fonction extérieure ne peut donc pas décider elle-même qu’elle possède un accès aux membres privés d’une classe. C’est la classe qui désigne explicitement les fonctions auxquelles elle accorde ce privilège. friend est-il indispensable ? Dans notre exemple, l’utilisation de friend n’est pas indispensable. Puisque la classe possède des accesseurs publics, nous pouvons parfaitement écrire : sortie << z.getPartieReelle() << z.getPartieImaginaire(); L’utilisation de friend permet simplement à la fonction operator<<() d’accéder directement à la représentation interne de l’objet sans rendre ses membres publics. Elle doit donc correspondre à un choix volontaire de conception. • Une fonction extérieure ne peut normalement pas accéder aux membres privés d’une • Une classe peut accorder cette autorisation à une fonction en la déclarant avec le mot-clé • Une fonction friend reste une fonction extérieure : elle ne devient pas une méthode membre de la classe. • Dans notre exemple, la déclaration friend std::ostream& operator<<( std::ostream& sortie, permet à operator<<() d’accéder directement aux membres privés de Complexe. • L’amitié ne rend pas les membres privés publics. Elle accorde un accès particulier uniquement à la fonction déclarée amie. • L’utilisation de friend n’est pas obligatoire lorsque l’interface publique de la classe fournit déjà les opérations nécessaires. Quels opérateurs peut-on surcharger ? Le C++ permet de surcharger un grand nombre de ses opérateurs. Parmi ceux-ci, nous trouvons
AccueilLa surcharge d’opérateur Cette possibilité ne signifie cependant pas que le programmeur puisse modifier librement la syntaxe du langage. La surcharge d’opérateur est soumise à plusieurs règles. Les opérateurs doivent déjà exister La surcharge permet de donner une signification particulière à un opérateur existant lorsqu’il est utilisé avec des objets appartenant à nos propres types. Elle ne permet pas de créer de nouveaux opérateurs. Nous ne pouvons donc pas inventer, par exemple, si cette combinaison ne correspond pas à un opérateur défini par le langage. Quelques opérateurs ne peuvent pas être surchargés Certains opérateurs et constructions du langage ne peuvent pas être surchargés. C’est notamment le cas de : Le programmeur ne peut donc pas modifier leur comportement pour ses propres classes. Les règles syntaxiques restent celles du C++ La surcharge d’un opérateur ne permet pas de modifier sa priorité. reste interprété comme : même si les opérateurs + et ∗ ont été surchargés. La surcharge ne permet pas non plus de modifier l’associativité d’un opérateur. Les règles syntaxiques du langage restent donc indépendantes du comportement que nous donnons à l’opérateur. Conserver une signification naturelle Le langage permet parfois de donner à un opérateur un comportement qui n’a aucun rapport avec sa signification habituelle. Il serait par exemple techniquement possible de définir l’opérateur + de manière à effectuer une soustraction. Une telle surcharge serait cependant extrêmement difficile à comprendre pour le lecteur du programme. Une surcharge d’opérateur doit donc conserver une signification cohérente et naturelle pour les objets manipulés. Pour notre classe Complexe, les expressions
AccueilLa surcharge d’opérateur possèdent une interprétation mathématique immédiate. La surcharge améliore alors la lisibilité du programme au lieu de la dégrader. • De nombreux opérateurs du C++ peuvent être surchargés, mais pas tous. • Il n’est pas possible d’inventer de nouveaux opérateurs. • La surcharge ne modifie ni la priorité ni l’associativité d’un opérateur. • Une surcharge doit conserver une signification naturelle et prévisible pour les objets auxquels elle s’applique. • Le fait qu’une surcharge soit techniquement possible ne signifie pas qu’elle soit souhaitable. La surcharge d’opérateur permet aux classes créées par le programmeur d’utiliser certains opérateurs du C++ avec une syntaxe proche de celle des types fondamentaux. Notre classe Complexe nous a ainsi permis d’écrire : Complexe z3 = z1 + z2; Complexe z4 = z1 − z2; Complexe z5 = z1 ∗ z2; Ces expressions utilisent toutes des fonctions opérateurs que nous avons définies pour notre propre classe. Une expression utilisant un opérateur surchargé correspond à l’appel d’une fonction. Lorsque l’opérateur est défini comme méthode membre, une expression telle que peut être comprise comme : L’objet situé à gauche est alors l’objet courant et l’objet situé à droite est transmis comme argument. Un opérateur peut également être défini par une fonction extérieure. Nous avons rencontré cette L’opérande de gauche étant ici std::cout, nous avons défini une fonction extérieure : std::ostream& operator<<(
AccueilLa surcharge d’opérateur std::ostream& sortie, Les deux opérandes apparaissent alors explicitement comme paramètres de la fonction. Nous avons enfin vu qu’une classe peut accorder à une fonction extérieure un accès à ses membres privés en la déclarant friend. Cette fonction reste extérieure à la classe : elle reçoit simplement une autorisation particulière d’accès à sa représentation interne. • La surcharge d’opérateur permet de définir le comportement d’un opérateur existant pour les objets d’une classe. • Un opérateur surchargé est fondamentalement une fonction possédant un nom particulier • Une surcharge peut être définie comme méthode membre ou, lorsque cela est approprié, comme fonction extérieure. • Les opérateurs tels que + construisent généralement un nouvel objet résultat, tandis que des opérateurs tels que += ou = modifient l’objet situé à gauche. • Une fonction qui modifie l’objet courant peut retourner une référence vers celui-ci avec : • Le choix d’une surcharge est effectué à partir des types des opérandes. Il ne dépend pas des valeurs qu’ils contiennent. • Le mot-clé friend permet à une classe d’accorder à une fonction extérieure l’accès à ses membres privés. • La surcharge d’opérateur doit être utilisée lorsqu’elle rend l’écriture du programme plus naturelle et plus lisible. La surcharge d’opérateur ne crée donc pas une nouvelle forme de calcul : elle permet de donner une syntaxe naturelle à des appels de fonctions définis pour nos propres types.
AccueilP2-Chap-05- La surcharge d’opérateur
AccueilTermes à ajouter au glossaire
Codes et programmes contenus dans le chapitre
Complexe
Classe Complexe et surcharge d’opérations sur les nombres complexes
README.pdf
Explications sur l’utilisation des codes