Question

Existe-t-il un modèle canonique ou recommandé pour implémenter la surcharge d'opérateur arithmétique dans les classes de type numérique C ++?

Dans la FAQ C ++, nous avons un opérateur d'affectation sûr pour les exceptions qui évite la plupart des problèmes:

class NumberImpl;

class Number {
   NumberImpl *Impl;

   ...
};

Number& Number::operator=(const Number &rhs)
{
   NumberImpl* tmp = new NumberImpl(*rhs.Impl);
   delete Impl;
   Impl = tmp;
   return *this;
}

Mais pour les autres opérateurs (+, + =, etc.), très peu de conseils sont donnés si ce n'est de les faire se comporter comme les opérateurs des types intégrés.

Existe-t-il un moyen standard de les définir? C’est ce que j’ai trouvé - y a-t-il des pièges que je ne vois pas?

// Member operator
Number& Number::operator+= (const Number &rhs)
{
    Impl->Value += rhs.Impl->Value; // Obviously this is more complicated
    return *this;
}

// Non-member non-friend addition operator
Number operator+(Number lhs, const Number &rhs)
{
     return lhs += rhs;
}
Était-ce utile?

La solution

Dans le livre de Bjarne Stroustrup, " le langage de programmation C ++ " ;, au chapitre 11 (celui consacré à la surcharge de l'opérateur), il passe en revue une classe pour un type de nombre complexe (section 11.3).

Ce que je remarque dans cette section, c’est qu’il implémente des opérations de type mixte ... ceci est probablement attendu pour n’importe quelle classe numérique.

En général, ce que vous avez l'air bien.

Autres conseils

La chose importante à prendre en compte lors de l'écriture d'un opérateur est que les opérateurs membres ne subissent pas de conversions dans le paramètre de gauche:

struct example {
  example(int);
  example operator + (example);
};

void foo() {
  example e(3), f(6);
  e + 4; // okay: right operand is implicitly converted to example
  e + f; // okay: no conversions needed.
  6 + e; // BAD: no matching call.
}

Cela est dû au fait que la conversion ne s'applique jamais à this pour les fonctions membres, et cela s'étend aux opérateurs. Si l'opérateur était plutôt exemple opérateur + (exemple, exemple) dans l'espace de noms global, il serait compilé (ou si Pass-by-const-ref était utilisé).

Par conséquent, les opérateurs symétriques tels que + et - sont généralement implémentés en tant que membres non membres, alors que les opérateurs d'assignation composés tels que + = et - = sont implémentés en tant que membres (ils modifient également les données, ce qui signifie qu'ils doivent être membres). Et, comme vous voulez éviter la duplication de code, les opérateurs symétriques peuvent être implémentés en termes d'affectations composées (comme dans votre exemple de code, bien que la convention recommande de rendre le temporaire à l'intérieur de la fonction).

La convention est d'écrire sur opérateur + (const T & amp;) et opérateur- (const T & amp;) en termes de opérateur + = (const T & amp;) et opérateur - = (const T & amp;) . S'il est judicieux d'ajouter et de soustraire des types primitifs, vous devez écrire un constructeur qui construit l'objet à partir du type primitif. Ensuite, les opérateurs surchargés fonctionneront également pour les types primitifs, car le compilateur appellera implicitement le constructeur approprié.

Comme vous l'avez dit vous-même, évitez de donner des privilèges d'accès aux fonctions qui n'en ont pas besoin. Mais dans votre code ci-dessus pour opérateur + (Nombre, nombre Const et & amp;) , personnellement, je ferais les deux paramètres const références et utiliserais un temp. Je pense que ce n'est pas surprenant que le commentateur ci-dessous ait oublié votre question; Sauf si vous avez une bonne raison de ne pas le faire, évitez les surprises et les astuces et soyez aussi évident que possible.

Si vous souhaitez que votre code s'intègre à d'autres types numériques, dites std :: complex , faites attention aux conversions cycliques. En d'autres termes, ne fournissez pas l'opérateur OtherNumeric () dans Numeric si OtherNumeric fournit un constructeur qui prend un Numeric . paramètre.

Il est de tradition d'écrire l'opérateur X en fonction de l'opérateur = X
Il est également traditionnel que tous les paramètres des opérateurs standard soient const

// Member operator
// This was OK
Number& Number::operator+= (Number const& rhs) 
{
    Impl->Value += rhs.Impl->Value; // Obviously this is more complicated
    return *this;
}

// Non-member non-friend addition operator
Number operator+(Number const& lhs,Number const& rhs)
{
     // This I would set the lhs side to const.
     // Make a copy into result.
     // Then use += add the rhs
     Number result(lhs);
     return result += rhs;
}

Vous mentionnez l'opérateur d'affectation.
Mais vous n'avez pas mentionné le constructeur de copie. Étant donné que votre classe est propriétaire d'un pointeur RAW, je m'attendrais à ce que vous le définissiez également. L’opérateur Assignment est alors traditionnellement écrit en termes de constructeur de copie.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top