2010-05-14 14 views
2

Quelle est la différence entre ces deux façons de surcharger l'opérateur! = Ci-dessous. Ce qui est considéré mieux?(C++) Quelle est la différence entre ces fonctions d'opérateur surchargées?

Class Test 
{ 
...// 
private: 
int iTest 

public: 
BOOL operator==(const &Test test) const; 
BOOL operator!=(const &Test test) const; 
} 

BOOL operator==(const &Test test) const 
{ 
    return (iTest == test.iTest); 
}  

//overload function 1 
BOOL Test::operator!=(const &Test test) const 
{ 
    return !operator==(test); 
} 

//overload function 2 
BOOL Test::operator!=(const &Test test) const 
{ 
    return (iTest != test.iTest); 
} 

Je viens récemment vu la syntaxe de la fonction 1 pour appeler une fonction d'opérateur de frères et soeurs et je me demande si l'écriture de cette façon offre des avantages.

+0

Si vous indentez votre code de 4 espaces (ou l'envelopper dans < pre > et < code > tags), il en sera de formater afin que nous puissions le lire plus facilement. – keithjgrant

+0

Merci pour le conseil. :-) – cv3000

+0

Il y a une définition ';' manquante après la classe '(http://www.reddit.com/r/programming/comments/b8ws6/) et qu'est-ce que 'BOOL'? – sbi

Répondre

2

Ils compileront presque certainement au même code machine.

Je préfère le choix 2, juste parce que je trouve difficile de dire "opérateur ==". Mais vous auriez pu utiliser

return ! (*this == test) 

Et à mon humble avis, c'est aussi clair et facile à comprendre.

+0

bon point. J'ai oublié de comparer juste la classe. Merci et très apprécié. – cv3000

7

Votre première surcharge assure qu'appeler != sur votre type fournira toujours le contraire d'appeler == même si votre implémentation de == changerait.

Votre deuxième fonction surchargée non, car il est possible de fournir une implémentation pour == et modifier l'implémentation existante à l'avenir.

Si vous voulez vous assurer que != sera toujours le contraire de ==, rendez-vous avec le premier (au prix d'un appel de fonction supplémentaire qui peut très bien devenir inline de toute façon).

Voici un bon exemple. Supposons que vous avez une classe Point2D avec les champs x et y. Si vous voulez implémenter ==, vous devrez comparer sur le champ x et sur le champ y. Si vous implémentez != en appelant operator==, vous avez un code plus court, et vous aurez une fonction moins à changer si vous avez déjà changé de représentation polaire.

Les tests d'égalité et les comparaisons sont toujours sensibles aux erreurs de maintenance lorsque les champs de classe changent. Minimiser le nombre de méthodes qui accèdent directement à l'état peut réduire le risque d'erreurs.

+0

C'est un excellent point. Je n'y ai jamais pensé comme ça. Merci. – cv3000

+0

Il y a aussi le fait que si vous êtes arrivé à changer la fonction 2 pour faire 'return (* this! = Test);' (puisque 'return! (* This == test);' a été suggéré pour la fonction 1), avoir un problème parce que la fonction 'operator! =' surchargée s'appellerait elle-même. En d'autres termes, l'utilisation de la fonction 1 présente l'avantage supplémentaire d'éviter un tel casse de débogage. – Dustin

1

Version 1, tandis que IMHO syntaxiquement moche, vous permet de modifier la logique d'égalité dans un seul endroit (dans la surcharge == opérateur). Il garantit que les deux surcharges sont toujours synchronisées.

2

Je peux penser à de nombreuses raisons (ou peut-être des aspects de la même raison) pour l'écrire de cette façon. Qu'est-ce qu'ils se résument à: c'est DRY.

  • Il assure que deux objets sont toujours soit == ou !=

  • Si vous décidez de changer ce qui est dans la classe, ou ce qui est utilisé pour les tests de l'égalité, il suffit de le changer en un seul endroit

Je pense que sur le plan conceptuel, vous avez vraiment deux choses différentes que vous définissez ici:

  • Une définition d ' « égalité » pour le test de classe

  • Une interface utile par laquelle les personnes qui utilisent cette classe peuvent déterminer l'égalité de leurs instances

Avec la méthode 2, que vous faites à la fois Ad- hoc. Avec la méthode 1, vous définissez l'égalité en operator== et en fournissant le reste de l'interface via operator!=.

Certaines langues/bibliothèques prennent encore plus loin, par exemple, en Ruby, vous pouvez définir juste<=> pour comparer des objets commandés et mélanger en Comparable et obtenir l'égalité, les inégalités et between?.

+2

Boost a la bibliothèque Opérateurs qui implémente pour vous de nombreux opérateurs arithmétiques/de comparaison en termes de quelques-uns: http://www.boost.org/doc/libs/1_43_0/libs/utility/operators.htm –

+0

explication impressionnante et grandement appréciée . – cv3000

1

En général, la déclaration suivante:
return !(*this == object);
vous permet de définir != en termes d'une fonction. Dans le monde de l'héritage, les objets enfant seulement besoin de définir operator== afin d'utiliser la classe de base operator!=:

struct Base 
{ 
    virtual bool isEqual(const Base& other) const = 0; 
    bool operator==(const Base& other) const 
    { 
    return isEqual(other); 
    } 
    bool operator!=(const Base& other) const 
    { 
    return !(*this == other); // Uses Base::operator== 
    } 
}; 

Avec la classe de base ci-dessus, la définition operator!= utilisant != nécessiterait descendants de mettre en œuvre plusieurs méthodes.

En outre, !(*this == other) permet de définir une fonction globale, générique pour !=:

template <typename T> 
bool operator!=(const T& a, const T& b) 
{ 
    return !(a == b); 
} 

Bien que ce modèle ne fournit pas beaucoup pour == et !=, les différences sont plus grandes lorsque vous utilisez les opérateurs relationnels: <, <=, >, >= .

0

En général, il est toujours préférable d'implémenter des fonctionnalités connexes les unes par rapport aux autres. Dans le cas des opérateurs, il y a toujours des groupes. Par exemple, != peut être implémenté en termes de ==; le post-incrément peut être mis en œuvre en termes de pré-incrémentation; Si quelque chose à propos de la classe a besoin d'être modifié, il suffit de modifier les opérateurs de base, et tout le reste sera automatiquement mis à jour pour refléter le nouveau comportement de < (voir std::rel_ops). .

La mise en œuvre générale de tous les opérateurs "secondaires" est toujours la même, et il existe même une bibliothèque qui fournit automatiquement des opérateurs définis en fonction de quelques opérateurs fournis.Voir Boost.Operators

Votre exemple:

#include <boost/operators.hpp> 

class Test : boost::equality_comparable<Test, Test> 
{ 
private: 
    int iTest; 

public: 
    bool operator==(const Test& test) const; 
    //look, no operator != 
}; 

int main() 
{ 
    Test a, b; 
    a != b; //still works 
}