Je ne comprends pas les arguments contre la surcharge de l'opérateur [fermé]


82

Je viens de lire un des articles de Joel dans lequel il dit:

En général, je dois admettre que j'ai un peu peur des fonctionnalités du langage qui cachent des choses . Quand vous voyez le code

i = j * 5;

… En C, vous savez au moins que j est multiplié par cinq et que les résultats sont stockés dans i.

Mais si vous voyez le même extrait de code en C ++, vous ne savez rien. Rien. La seule façon de savoir ce qui se passe réellement en C ++ est de savoir ce que sont les types i et j, ce qui pourrait être déclaré ailleurs. C'est parce que j est peut-être d'un type operator*surchargé et qu'il fait quelque chose de terriblement spirituel lorsque vous essayez de le multiplier.

(C'est moi qui souligne.) Peur des fonctionnalités linguistiques qui cachent des choses? Comment pouvez-vous avoir peur de cela? Cacher des objets (également appelés abstraction ) n’est-il pas l’ une des idées clés de la programmation orientée objet? Chaque fois que vous appelez une méthode a.foo(b), vous n'avez aucune idée de ce que cela pourrait faire. Vous devez trouver quels types aet quels types bsont quelque chose qui pourrait être déclaré ailleurs. Alors devrions-nous nous débarrasser de la programmation orientée objet, car elle cache trop de choses au programmeur?

Et en quoi est-il j * 5différent de j.multiply(5)ce que vous pourriez avoir à écrire dans un langage qui ne prend pas en charge la surcharge des opérateurs? Encore une fois, il vous faudrait connaître le type de méthode jet jeter un coup d'œil à l'intérieur de la multiplyméthode, car bon à savoir, il jpourrait s'agir d'un type qui possède une multiplyméthode qui fait quelque chose de terriblement spirituel.

"Muahaha, je suis un mauvais programmeur qui nomme une méthode multiply, mais ce qu'elle fait est totalement obscure et non intuitive et n'a absolument rien à faire avec la multiplication des choses." Est-ce un scénario que nous devons prendre en compte lors de la conception d'un langage de programmation? Ensuite, nous devons abandonner les identifiants des langages de programmation sous prétexte qu’ils pourraient être trompeurs!

Si vous voulez savoir ce que fait une méthode, vous pouvez regarder la documentation ou jeter un coup d'œil à l'intérieur de la mise en œuvre. La surcharge d'opérateur n'est que du sucre syntaxique, et je ne vois pas du tout en quoi cela change le jeu.

S'il te plaît, éclaire-moi.


20
+1: Bien écrit, bien argumenté, sujet intéressant et très discutable. Un exemple brillant d'une question personnelle.
Allon Guralnek

19
+1: Les gens écoutent Joel Spolsky parce qu'il écrit bien et qu'il est bien connu. Mais cela ne lui donne pas raison 100% du temps. Je suis d'accord avec votre argument. Si nous suivions tous la logique de Joel ici, nous ne réussirions jamais.
Personne

5
Je dirais que soit i et j sont déclarés localement pour que vous puissiez voir leur type rapidement, soit ce sont des noms de variables loufoques qui doivent être renommés de manière appropriée.
Cameron MacFarland

5
+1, mais n'oubliez pas la meilleure partie de l'article de Joel: après avoir couru un marathon pour trouver la bonne réponse, il s'arrête sans raison apparente à une cinquantaine de mètres. Un mauvais code ne devrait pas simplement être faux; il ne devrait pas compiler.
Larry Coleman

3
@Larry: Vous pouvez empêcher la compilation du code incorrect en définissant les classes de manière appropriée. Ainsi, dans son exemple, vous pourriez utiliser SafeString et UnsafeString en C ++, ou RowIndex et ColumnIndex, mais vous devrez alors utiliser la surcharge d'opérateur pour les forcer à se comporter de manière intuitive.
David Thornley

Réponses:


32

L'abstraction "cache" le code afin que vous n'ayez pas à vous soucier du fonctionnement interne et que vous ne puissiez souvent pas le modifier, mais l'intention n'était pas de vous empêcher de le regarder. Nous faisons simplement des suppositions sur les opérateurs et, comme Joel l'a dit, cela pourrait être n'importe où. Avoir une fonction de programmation exigeant que tous les opérateurs surchargés soient établis dans un emplacement spécifique peut aider à la trouver, mais je ne suis pas sûr que cela en facilite l'utilisation.

Je ne vois pas comment faire quelque chose qui ne ressemble pas beaucoup à la multiplication, mieux qu'une fonction appelée Get_Some_Data qui supprime des données.


13
+1 Pour le bit "Je ne vois pas". Les fonctionnalités linguistiques sont là pour être utilisées, pas d’abus.
Michael K

5
Pourtant, nous avons un <<opérateur défini sur les flux qui n'a rien à voir avec le décalage au niveau du bit, directement dans la bibliothèque standard de C ++.
Malcolm

l'opérateur de "décalage au niveau du bit" n'est appelé cela que pour des raisons historiques. Lorsqu'il est appliqué à des types standard, il effectue un décalage au niveau du bit (de la même manière que l'opérateur + additionne des nombres lorsqu'il est appliqué à des types numériques), mais lorsqu'il est appliqué à un type complexe, il peut faire ce qu'il veut, tant qu'il sens pour ce type.
gbjbaanb

1
* est également utilisé pour le déréférencement (comme le font les pointeurs intelligents et les itérateurs); il n'est pas clair où mettre la limite entre bonne et mauvaise surcharge
martinkunev

Ce ne serait pas n'importe où, ce serait dans la définition de type de j.
Andy

19

IMHO, les fonctionnalités du langage telles que la surcharge des opérateurs donnent plus de puissance au programmeur. Et, comme nous le savons tous, un grand pouvoir entraîne de grandes responsabilités. Les fonctionnalités qui vous donnent plus de puissance vous donnent également plus de moyens de vous tirer une balle dans le pied et, évidemment, elles devraient être utilisées judicieusement.

Par exemple, il est parfaitement logique de surcharger le +ou l’ *opérateur pour class Matrixou class Complex. Tout le monde saura instantanément ce que cela signifie. Par contre, le fait que cela +signifie concaténation de chaînes de caractères n’est pas évident du tout, même si Java le fait en tant que partie du langage et STL le fait pour std::stringutiliser la surcharge d’opérateurs.

Un autre bon exemple de la surcharge d’opérateurs qui rend le code plus clair est les pointeurs intelligents en C ++. Vous voulez que les pointeurs intelligents se comportent autant que possible comme des pointeurs normaux. Il est donc parfaitement logique de surcharger le unaire *et les ->opérateurs.

En substance, la surcharge des opérateurs n’est rien d’autre qu’un autre moyen de nommer une fonction. Et il existe une règle pour nommer les fonctions: le nom doit être descriptif, rendant immédiatement évident le travail de la fonction. La même règle exacte s'applique à la surcharge de l'opérateur.


1
Vos deux dernières phrases vont au coeur de l’objection à la surcharge d’opérateurs: le désir que tout le code soit immédiatement évident.
Larry Coleman

2
N'est-ce pas évident ce que M * N signifie, où M et N sont de type Matrix?
Dima

2
@Fred: Nope. Il existe un type de multiplication matricielle. Vous pouvez multiplier une matrice mxn par une matrice nxk et obtenir une matrice mxk.
Dima

1
@FredOverflow: Il existe différentes manières de multiplier un vecteur tridimensionnel, l'un vous donnant un scalaire et l'autre un autre vecteur tridimensionnel. Par conséquent, une surcharge *pour ceux-ci peut être source de confusion. On pourrait dire que vous pourriez utiliser operator*()pour le produit scalaire et operator%()le produit croisé, mais je ne le ferais pas pour une bibliothèque à usage général.
David Thornley

2
@ Martin Beckett: Non. C ++ n'est pas autorisé à réorganiser A-Bcomme tel B-A, et tous les opérateurs suivent ce modèle. Il y a toujours une exception: lorsque le compilateur peut prouver que ce n'est pas grave, il est permis de tout réorganiser.
Sjoerd

9

En Haskell, "+", "-", "*", "/" etc ne sont que des fonctions (infixes).

Devriez-vous nommer une fonction infixe "plus" comme dans "4 plus 2"? Pourquoi pas, si addition est ce que votre fonction fait. Devriez-vous nommer votre fonction "plus" "+"? Pourquoi pas.

Je pense que le problème avec les soi-disant "opérateurs" est qu’ils ressemblent pour la plupart à des opérations mathématiques et qu’il n’ya pas beaucoup de façons de les interpréter. Il y a donc de grandes attentes quant à ce que fait une telle méthode / fonction / opérateur.

EDIT: fait mon point plus clair


Euh, à l'exception de ce qui est hérité de C, C ++ (et c'est ce que Fred demandait), fait à peu près la même chose. Maintenant, quelle est votre opinion sur le fait que cela soit bon ou mauvais?
sbi

@sbi J'adore la surcharge des opérateurs ... En fait , même C a des opérateurs surchargées ... Vous pouvez les utiliser pour int, float, long longet que ce soit. Alors, de quoi s'agit-il?
FUZxxl

@FUZxxl: Il s’agit d’ opérateurs définis par l’ utilisateur qui surchargent les opérateurs intégrés.
sbi

1
@sbi Haskell n'a pas de distinction entre intégré et défini par l'utilisateur . Tous les opérateurs sont égaux. Vous pouvez même activer certaines extensions qui suppriment tous les éléments prédéfinis et vous permettent de tout écrire à partir de zéro, y compris les opérateurs.
FUZxxl

@FUZxxl: C'est peut-être bien le cas, mais les opérateurs surchargés qui s'opposent ne s'opposent généralement pas à l'utilisation de la fonction intégrée +pour différents types de numéros intégrés, mais à la création de surcharges définies par l'utilisateur. d'où mon commentaire.
sbi

7

Sur la base des autres réponses que j'ai vues, je ne peux que conclure que la véritable objection à la surcharge de l'opérateur est le désir de code immédiatement évident.

C'est tragique pour deux raisons:

  1. Porté à sa conclusion logique, le principe selon lequel le code devrait être immédiatement évident voudrait que nous codions tous encore en COBOL.
  2. Vous n'apprenez pas du code qui est immédiatement évident. Vous apprenez du code qu’il est logique de prendre une fois que vous prenez le temps de réfléchir à la façon dont cela fonctionne.

Apprendre du code n’est pas toujours l’objectif principal. Dans un cas comme "La fonctionnalité X est fictive, la personne qui l’a rédigée a quitté la société et vous devez la réparer le plus rapidement possible", je préférerais de beaucoup avoir un code immédiatement évident.
Errorsatz

5

Je suis un peu d'accord.

Si vous écrivez multiply(j,5), cela jpeut être de type scalaire ou matriciel, rendant multiply()plus ou moins complexe, selon ce qui jest. Toutefois, si vous abandonnez complètement l’idée de surcharger, la fonction doit être nommée multiply_scalar()ou multiply_matrix()indiquer clairement ce qui se passe en dessous.

Il y a un code où beaucoup d'entre nous préféreraient une méthode et un code où la plupart d'entre nous préférerions une méthode différente. La plupart du code, cependant, se situe entre les deux extrêmes. Ce que vous préférez là-bas dépend de vos antécédents et de vos préférences personnelles.


Bon point. Cependant, abandonner complètement la surcharge ne joue pas bien avec la programmation générique ...
fredoverflow

@FredO: Bien sûr que non. Mais la programmation générique consiste à utiliser le même algorithme pour des types très différents, de sorte que ceux qui préfèrent multiply_matrix()n’apprécieront pas non plus la programmation générique.
sbi

2
Vous êtes plutôt optimiste quant aux noms, n'est-ce pas? En fonction de certains endroits où j'ai travaillé, je m'attendrais à des noms tels que 'multiply () `et' multiplym ()` ou peut real_multiply()- être à peu près . Les développeurs ne sont souvent pas doués pour les noms, et operator*()au moins vont être cohérents.
David Thornley

@ David: Oui, j'ai sauté sur le fait que les noms pourraient être mauvais. Mais alors, nous pourrions tout aussi bien supposer que nous operator*()pourrions faire quelque chose de stupide, jest une macro évaluant des expressions impliquant cinq appels de fonction, et tout le reste. Vous ne pouvez alors plus comparer les deux approches. Mais, oui, il est difficile de nommer les choses, même si cela en vaut la peine.
sbi

5
@ David: Et comme il est difficile de nommer les choses, les noms doivent être bannis des langages de programmation, non? C'est trop facile de se tromper! ;-)
fredoverflow

4

Je vois deux problèmes avec la surcharge de l'opérateur.

  1. La surcharge modifie la sémantique de l'opérateur, même si cela n'est pas prévu par le programmeur. Par exemple, lorsque vous surchargez &&, ||ou ,, vous perdez les points de séquence impliqués par les variantes intégrées de ces opérateurs (ainsi que le comportement de court-circuit des opérateurs logiques). Pour cette raison, il est préférable de ne pas surcharger ces opérateurs, même si le langage le permet.
  2. Certaines personnes considèrent la surcharge d'opérateur comme une fonctionnalité intéressante. Elles commencent à l'utiliser partout, même si ce n'est pas la solution appropriée. Cela amène d'autres personnes à réagir de manière excessive dans l'autre sens et à mettre en garde contre l'utilisation de la surcharge par l'opérateur. Je ne suis pas d’accord avec l’un ou l’autre groupe, mais prenons le juste milieu: la surcharge de l’opérateur doit être utilisée avec parcimonie et uniquement lorsque
    • l'opérateur surchargé a une signification naturelle pour les experts de domaine et les experts en logiciel. Si ces deux groupes ne s'accordent pas sur le sens naturel pour l'opérateur, ne le surchargez pas.
    • pour le ou les types impliqués, il n'y a pas de signification naturelle pour l'opérateur et le contexte immédiat (de préférence la même expression, mais pas plus de quelques lignes) indique toujours clairement la signification de l'opérateur. Un exemple de cette catégorie serait operator<<pour les flux.

1
+1 de moi, mais le deuxième argument peut également s'appliquer à l'héritage. Beaucoup de gens n'ont aucune idée de l'héritage et essaient de l'appliquer à tout. Je pense que la plupart des programmeurs conviendraient qu'il est possible d'abuser du patrimoine. Cela signifie-t-il que l'héritage est "pervers" et doit être abandonné des langages de programmation? Ou devrions-nous le laisser parce que cela peut aussi être utile?
fredoverflow

@FredOverflow Le deuxième argument peut être appliqué à tout ce qui est "nouveau et à chaud". Je ne le présente pas comme un argument pour supprimer la surcharge d’opérateurs d’une langue, mais comme une raison pour laquelle les gens s’y opposent. En ce qui me concerne, la surcharge des opérateurs est utile mais doit être utilisée avec précaution.
Bart van Ingen Schenau

IMHO, autoriser les surcharges de &&et ||sans impliquer de séquençage était une grosse erreur (IMHO, si C ++ devait autoriser la surcharge de ceux-ci, il aurait dû utiliser un format spécial "à deux fonctions", la première fonction étant requise pour renvoyer un type implicitement convertible en un entier, la deuxième fonction peut prendre deux ou trois arguments, l'argument "extra" de la deuxième fonction étant le type de retour du premier. Le compilateur appelle alors la première fonction. si elle renvoie non nulle, évalue le deuxième opérande et appelle la deuxième fonction.)
Supercat

Bien sûr, ce n’est pas aussi étrange que de permettre à l’opérateur de virgule d’être surchargé. Incidemment, une chose surchargée que je n’ai pas vraiment vue, mais que j’aimerais aimer, serait un moyen d’accéder de manière très contraignante aux membres, permettant ainsi à une expression comme celle-ci foo.bar[3].Xd’être gérée par foola classe, plutôt que food’exposer un membre qui pourrait supporter la souscription et ensuite exposer un membre X. Si on voulait forcer l'évaluation via l'accès réel des membres, on écrirait ((foo.bar)[3]).X.
Supercat

3

D'après mon expérience personnelle, la façon dont Java permet d'utiliser plusieurs méthodes, sans surcharger l'opérateur, signifie que chaque fois que vous voyez un opérateur, vous savez exactement ce qu'il fait.

Vous n'avez pas à savoir si vous *appelez du code étrange, mais sachez qu'il s'agit d'une multiplication et que son comportement est identique à celui défini par la spécification du langage Java. Cela signifie que vous pouvez vous concentrer sur le comportement réel au lieu de découvrir tous les éléments de guichet définis par le programmeur.

En d'autres termes, interdire la surcharge de l'opérateur est un avantage pour le lecteur , et non pour l' écrivain , et facilite donc la maintenance des programmes!


+1, avec une mise en garde: C ++ vous donne assez de corde pour vous pendre. Mais si je veux implémenter une liste chaînée en C ++, je voudrais pouvoir utiliser [] pour accéder au nième élément. Il est logique d'utiliser les opérateurs pour les données pour lesquelles (mathématiquement) ils sont valables.
Michael K

@ Michael, vous ne pouvez pas vivre avec la list.get(n)syntaxe?

@ Thorbjørn: C'est bien aussi, peut-être un mauvais exemple. Le temps peut être meilleur - une surcharge +, - aurait du sens plutôt que du temps.add (une autre fois).
Michael K

4
@ Michael: À propos des listes chaînées, std::listne surcharge pas operator[](ni ne donne aucun autre moyen d'indexation dans la liste), car une telle opération serait O (n), et une interface de liste ne devrait pas exposer une telle fonction si vous vous souciez de l'efficacité. Les clients peuvent être tentés de parcourir des listes chaînées avec des index, rendant O (n) algorithmes inutilement O (n ^ 2). Vous le voyez assez souvent dans le code Java, surtout si les gens travaillent avec l' Listinterface qui vise à faire abstraction de la complexité.
fredoverflow

5
@Thor: "Mais pour être certain, vous devez vérifier :)" ... Encore une fois, cela n'est pas lié à la surcharge de l'opérateur . Si vous voyez time.add(anotherTime), vous devrez également vérifier si le programmeur de la bibliothèque a implémenté l'opération d'ajout "correctement" (peu importe ce que cela signifie).
fredoverflow

3

Une différence entre la surcharge a * bet l’appel multiply(a,b)est que cette dernière peut facilement être recherchée. Si la multiplyfonction n'est pas surchargée pour différents types, vous pouvez savoir exactement ce que la fonction va faire, sans avoir à suivre les types de aet b.

Linus Torvalds a un argument intéressant à propos de la surcharge des opérateurs. Dans quelque chose comme le développement du noyau Linux, où la plupart des modifications sont envoyées via des correctifs par courrier électronique, il est important que les responsables puissent comprendre le fonctionnement d'un correctif avec seulement quelques lignes de contexte autour de chaque modification. Si les fonctions et les opérateurs ne sont pas surchargés, le correctif peut être plus facilement lu de manière indépendante du contexte, car vous n'avez pas à parcourir le fichier modifié pour déterminer quels sont tous les types et pour rechercher les opérateurs surchargés.


Le noyau Linux n'est-il pas développé en C pur? Pourquoi discuter de la surcharge (de l'opérateur) du tout dans ce contexte?
fredoverflow

Les préoccupations sont les mêmes pour tout projet avec un processus de développement similaire, quelle que soit la langue. Une surcharge excessive peut rendre difficile la compréhension de l'impact des modifications si vous ne devez continuer que quelques lignes à partir d'un fichier de correctif.
Scott Wales

@FredOverflow: le noyau Linux est vraiment dans GCC C. Il utilise toutes sortes d’extensions qui donnent parfois à son C une impression de C ++. Je pense à certaines manipulations fantaisistes.
Zan Lynx

2
@Scott: Il est inutile de discuter du "maléfice" de la surcharge par rapport aux projets programmés en C, car C n'a pas la capacité de surcharger des fonctions.
fredoverflow

3
Linus Torvalds me semble avoir un point de vue étroit. Il critique parfois des choses qui ne sont pas vraiment utiles pour la programmation du noyau Linux, comme si cela les rendrait impropres à un usage général. Subversion est un exemple. C'est un bon VCS, mais le développement du noyau Linux nécessite vraiment un VCS distribué, aussi Linus a-t-il critiqué le SVN en général.
David Thornley

2

Je suppose que cela a quelque chose à voir avec le dépassement des attentes. Je suis habitué au C ++, au comportement de l'opérateur qui n'est pas entièrement dicté par le langage, et vous ne serez pas surpris qu'un opérateur fasse quelque chose de bizarre. Si vous êtes habitué à des langages qui ne possèdent pas cette fonctionnalité et que vous voyez ensuite du code C ++, vous apportez les attentes de ces autres langages et vous risquez d'être très surpris lorsque vous découvrez qu'un opérateur surchargé fait quelque chose de génial.

Personnellement, je pense qu'il y a une différence. Lorsque vous pouvez modifier le comportement de la syntaxe intégrée du langage, il devient plus opaque de raisonner. Les langages qui n'autorisent pas la méta-programmation sont syntaxiquement moins puissants, mais conceptuellement plus simples à comprendre.


Les opérateurs surchargés ne devraient jamais faire "quelque chose d’étrange". C'est bien si cela fait quelque chose de complexe, bien sûr. Mais ne surchargez que s’il ya une signification évidente.
Sjoerd

2

Je pense que la surcharge des opérateurs mathématiques n’est pas le véritable problème de la surcharge des opérateurs en C ++. Je pense que surcharger les opérateurs qui ne devraient pas s'appuyer sur le contexte de l'expression (c'est-à-dire du type) est "pervers". Par exemple, la surcharge , [ ] ( ) -> ->* new deleteou même le unaire *. Vous avez certaines attentes de la part de ces opérateurs qui ne devraient jamais changer.


+1 Ne faites pas [] l'équivalent de ++.
Michael K

3
Voulez - vous dire que nous ne devrions pas être en mesure de surcharger les opérateurs dont vous avez parlé du tout ? Ou êtes-vous simplement en train de dire que nous devrions les surcharger uniquement à des fins saines? Parce que je détesterais voir des conteneurs sans operator[], des foncteurs sans operator(), des pointeurs intelligents sans operator->et ainsi de suite.
fredoverflow

Je dis que le problème potentiel de surcharge d'opérateurs avec des opérations mathématiques est petit comparé à ces opérateurs. Faire quelque chose d'intelligent ou de fou avec des opérateurs de mathématiques peut être gênant, mais les opérateurs que j'ai énumérés, que les gens ne considèrent généralement pas comme des opérateurs, mais plutôt comme des éléments de base du langage, doivent toujours répondre aux attentes définies par le langage. []devrait toujours être un accesseur semblable à un tableau, et ->devrait toujours signifier accéder à un membre. Peu importe qu'il s'agisse d'un tableau ou d'un conteneur différent, ou d'un pointeur intelligent ou non.
Allon Guralnek

2

Je comprends parfaitement que vous n'aimiez pas l'argument de Joël à propos de la dissimulation. Moi non plus. Il est en effet bien préférable d’utiliser le signe "+" pour les types tels que les types numériques intégrés ou pour vos propres types comme, par exemple, la matrice. J'admets que c'est soigné et élégant de pouvoir multiplier deux matrices avec le '*' au lieu de '.multiply ()'. Et après tout, nous avons le même genre d'abstraction dans les deux cas.

Ce qui fait mal ici, c'est la lisibilité de votre code. Dans la vie réelle, pas dans l'exemple académique de la multiplication matricielle. Surtout si votre langue permet de définir des opérateurs qui ne sont pas initialement présents dans le noyau de la langue, par exemple =:=. Beaucoup de questions supplémentaires se posent à ce stade. De quoi parle ce maudit opérateur? Je veux dire quelle est la préséance de cette chose? Qu'est-ce que l'associativité? Dans quel ordre le a =:= b =:= cvraiment exécuté?

C'est déjà un argument contre la surcharge de l'opérateur. Toujours pas convaincu? Vérifier les règles de priorité ne vous a pas pris plus de 10 secondes? Ok, allons plus loin.

Si vous commencez à utiliser un langage qui autorise la surcharge d'opérateurs, par exemple le langage populaire dont le nom commence par «S», vous apprendrez rapidement que les concepteurs de bibliothèques adorent remplacer les opérateurs. Bien sûr, ils sont bien éduqués, ils suivent les meilleures pratiques (pas de cynisme ici) et toutes leurs API sont parfaitement logiques quand on les regarde séparément.

Maintenant, imaginez que vous deviez utiliser quelques API qui font un usage intensif des opérateurs surchargeant ensemble dans un code unique. Ou mieux encore, vous devez lire un code hérité comme celui-là. C'est à ce moment que la surcharge de l'opérateur est vraiment nulle. En gros, s’il ya beaucoup d’opérateurs surchargés à un endroit donné, ils commenceront bientôt à se mêler aux autres caractères non alphanumériques de votre code de programme. Ils se mêleront à des caractères non alphanumériques qui ne sont pas réellement des opérateurs, mais plutôt des éléments de grammaire linguistique plus fondamentaux qui définissent des éléments tels que des blocs et des portées, des instructions de contrôle de flux de formes ou des méta-choses. Vous devrez mettre les lunettes et rapprocher vos yeux de 10 cm de l'écran LCD pour comprendre ce désordre visuel.


1
Surcharger les opérateurs existants et inventer de nouveaux opérateurs ne sont pas la même chose, mais +1 de moi.
fredoverflow

1

En général, j'évite d'utiliser la surcharge de l'opérateur de manière non intuitive. Autrement dit, si j'ai une classe numérique, la surcharge * est acceptable (et encouragée). Cependant, si j'ai un employé de classe, que ferait une surcharge *? En d’autres termes, surchargez les opérateurs de manière intuitive qui facilitent la lecture et la compréhension.

Acceptable / Encouragé:

class Complex
{
public:
    double r;
    double i;

    Complex operator*(const Compex& rhs)
    {
        Complex result;
        result.r = (r * rhs.r) - (i * rhs.i);
        result.i = (r * rhs.i) + (i * rhs.r);
        return result;
    }
};

Pas acceptable:

class Employee
{
public:
    std::string name;
    std::string address;
    std::string phone_number;

    Employee operator* (const Employee& e)
    {
        // what the hell do I do here??
    }
};

1
Multiplier les employés? C’est sûrement une infraction passible de sac, s’ils le font à la table du conseil.
gbjbaanb

1

Outre ce qui a déjà été dit ici, il existe un autre argument contre la surcharge des opérateurs. En effet, si vous écrivez +, il est évident que vous entendez ajouter quelque chose à quelque chose. Mais ce n'est pas toujours le cas.

C ++ lui-même fournit un excellent exemple d'un tel cas. Comment est stream << 1censé être lu? flux décalé à gauche de 1? Ce n'est pas évident du tout, sauf si vous savez explicitement que << en C ++ écrit également dans le flux. Cependant, si cette opération était implémentée en tant que méthode, aucun développeur sensé n'écrirait o.leftShift(1), ce serait quelque chose comme o.write(1).

L'essentiel est qu'en rendant la surcharge d'opérateur indisponible, le langage incite les programmeurs à réfléchir aux noms des opérations. Même si le nom choisi n'est pas parfait, il est toujours plus difficile d'interpréter mal un nom qu'un signe.


1

Par rapport aux méthodes expliquées, les opérateurs sont plus courts, mais ils n’ont pas besoin de parenthèses. Les parenthèses sont relativement peu pratiques à taper. Et vous devez les équilibrer. Au total, tout appel de méthode nécessite trois caractères de bruit en clair par rapport à un opérateur. Cela rend l'utilisation des opérateurs très, très tentante.
Pourquoi d'autre voudrait-on à ceci cout << "Hello world":?

Le problème avec la surcharge est que la plupart des programmeurs sont incroyablement paresseux et que la plupart des programmeurs ne peuvent pas se permettre de l'être.

Ce qui pousse les programmeurs C ++ à l’abus de la surcharge d’opérateurs n’est pas sa présence, mais l’absence d’un moyen plus rationnel d’exécuter des appels de méthodes. Et les gens n'ont pas simplement peur de la surcharge des opérateurs parce que c'est possible, mais parce que c'est fait.
Notez que, par exemple, dans Ruby et Scala, personne n’a peur de la surcharge de l’opérateur. Outre le fait que l'utilisation des opérateurs n'est pas vraiment plus courte que les méthodes, une autre raison est que Ruby limite la surcharge des opérateurs à un minimum raisonnable, alors que Scala vous permet de déclarer vos propres opérateurs , rendant ainsi éviter les collisions triviales.


ou, en C #, pour utiliser + = pour lier un événement à un délégué. Je ne pense pas que blâmer les fonctionnalités du langage pour la stupidité des programmeurs est une manière constructive d’avancer.
gbjbaanb

0

La raison pour laquelle la surcharge d'opérateur est effrayante, c'est parce qu'il y a un grand nombre de programmeurs qui ne penseraient jamais que *cela ne signifie pas simplement "multiplier", alors qu'une méthode comme foo.multiply(bar)au moins instantanément indique à ce programmeur que quelqu'un a écrit une méthode de multiplication personnalisée . À quel moment ils se demanderaient pourquoi et partiraient à la recherche.

J'ai travaillé avec de "bons programmeurs" qui occupaient des positions élevées pour créer des méthodes appelées "CompareValues" qui prendraient 2 arguments, appliqueraient les valeurs de l'une à l'autre et renverraient un booléen. Ou une méthode appelée "LoadTheValues" qui irait dans la base de données pour 3 autres objets, obtenir des valeurs, faire des calculs, la modifier thiset la sauvegarder dans la base de données.

Si je travaille dans une équipe avec ces types de programmeurs, je sais instantanément qu’il faut enquêter sur les choses sur lesquelles ils ont travaillé. S'ils surchargent un opérateur, je n'ai absolument aucun moyen de savoir qu'ils l'ont fait, sauf de supposer qu'ils l'ont fait et d'aller les chercher.

Dans un monde parfait, ou avec une équipe de programmeurs parfaits, la surcharge d'opérateurs est probablement un outil fantastique. Je n'ai pas encore travaillé sur une équipe de programmeurs parfaits, c'est pourquoi c'est effrayant.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.