Résumé : Chap 5 – La surcharge d’opérateurs
Lorsque nous créons une classe, le compilateur connaît sa structure, mais il ne peut pas deviner ce que doivent signifier les opérateurs appliqués à ses objets. L’addition de deux nombres complexes est évidente pour le mathématicien, mais l’expression z1 + z2 n’a aucun sens pour le C++ tant que nous ne lui avons pas expliqué comment la réaliser. Nous pourrions employer une méthode comme additionner(), mais la surcharge permet de conserver la notation naturelle du problème.
Pour donner un sens à +, la classe Complexe définit une fonction operator+. Dans l’expression z1 + z2, l’objet z1 devient l’objet courant et z2 est reçu comme argument ; l’écriture équivaut donc à z1.operator+(z2). La fonction additionne les parties réelles et imaginaires, construit un nouveau nombre complexe et le retourne par valeur, sans modifier les deux opérandes.
Le même raisonnement permet de définir - et *. Ce ne sont pas les symboles eux-mêmes qui effectuent les calculs : ils offrent une syntaxe particulière pour appeler les fonctions que nous avons écrites. Les opérateurs peuvent ensuite être combinés dans des expressions ordinaires, tandis que leurs priorités restent celles du langage ; une multiplication demeure donc prioritaire sur une addition.
Tous les opérateurs n’ont cependant pas le même rôle. Les opérateurs +, - et * produisent naturellement un nouvel objet, alors que += doit modifier celui qui se trouve à gauche. Sa fonction retourne une référence vers l’objet courant avec return *this;, ce qui évite une copie et permet d’enchaîner les opérations.
L’affectation = suit une logique comparable : l’objet de gauche existe déjà et doit recevoir l’état de celui de droite. Pour une classe contenant seulement deux double, l’opérateur généré par le compilateur suffit. L’étude de sa surcharge prépare néanmoins les classes qui possèdent des ressources, pour lesquelles une simple copie membre par membre ne garantit pas toujours des objets indépendants.
Les comparaisons montrent qu’un opérateur ne retourne pas nécessairement un objet de la classe. operator== compare les deux composantes et retourne un booléen ; operator!= peut ensuite réutiliser ce résultat en l’inversant. Définir une opération à partir d’une autre évite de dupliquer la règle d’égalité et maintient leur cohérence.
L’affichage soulève un problème différent. Dans std::cout << z, l’objet placé à gauche est le flux std::cout, et non le nombre complexe. operator<< est donc défini comme une fonction extérieure recevant le flux et l’objet. Elle retourne le flux par référence afin que plusieurs insertions puissent être enchaînées.
Une fonction extérieure ne peut normalement consulter que l’interface publique de la classe. Le mot-clé friend permet à la classe de lui accorder un accès ciblé à ses membres privés, mais cette autorisation n’est pas automatique ni toujours nécessaire : si les accesseurs publics suffisent, l’encapsulation peut rester intacte.
La surcharge ne permet finalement ni d’inventer de nouveaux opérateurs ni de modifier leur priorité, leur associativité ou le nombre de leurs opérandes. Son intérêt n’est pas de rendre la syntaxe spectaculaire, mais de rapprocher le code du problème représenté. Elle est pertinente lorsque z1 + z2, z1 == z2 ou std::cout << z1 expriment plus clairement l’intention qu’un appel de fonction explicite.