Partage
  • Partager sur Facebook
  • Partager sur Twitter

Surcharge d'opérateurs

Sujet résolu
    29 décembre 2022 à 16:29:01

    Bonjour ! Je suis actuellement en train d'essayer de créer une classe permettant de gérer des nombres rationnels (a/b) mais je rencontre un problème avec les opérateurs.

    Voici un extrait de la classe :

    template <typename T> class Ratio{
    
        private :
    
        int m_num;
        int m_den;
    
        public :
    
        Ratio(const int &num = 0, const int &den = 1) : m_num(num), m_den(den) {};
        ~Ratio() = default;
    
    
        //opérateurs
        
        Ratio operator+(const Ratio &rn) const;
        Ratio operator-(const Ratio &rn) const;
        Ratio operator*(const Ratio &rn) const;
        Ratio operator/(const Ratio &rn) const;
    
        Ratio operator/(const T &real) const;
        Ratio operator*(const T &real) const;
        Ratio operator-(const T &real) const;
        Ratio operator+(const T &real) const;
    
        Ratio operator++(); // marche qu'avec ++a mais pas avec a++
        Ratio operator--(); // marche qu'avec --a mais pas avec a--
    
        Ratio operator-(); // moins unaire
     
        // ... Le reste des méthodes ...
    }

     Exemple d'implémentation pour l'opérateur + :

    template <typename T> Ratio<T> Ratio<T>::operator+(const T &real) const {
        Ratio ratio;
        Ratio ratio2 = convert_float_to_ratio(real, 10);
        ratio.m_num = this->m_num*ratio2.m_den + this->m_den*ratio2.m_num;
        ratio.m_den = this->m_den*ratio2.m_den;
        ratio = ratio.make_irreductible();
        return ratio;
    }
    template <typename T> Ratio<T> Ratio<T>::operator+(const Ratio &rn) const {
        Ratio ratio;
        ratio.m_num = this->m_num*rn.m_den + this->m_den*rn.m_num;
        ratio.m_den = this->m_den*rn.m_den;
        ratio = ratio.make_irreductible();
        return ratio;
    }


    actuellement ces surcharges d'opérateurs me permettent de faire :
    a/b + c/d ;
    a/b + 2 ;

    mais pas :
    2 + a/b ;

    et c'est pareil avec les autres opérateurs... Comment je peux régler cela ?

    Merci d'avance pour votre aide !

    -
    Edité par ÉricTIB 29 décembre 2022 à 16:55:55

    • Partager sur Facebook
    • Partager sur Twitter
      29 décembre 2022 à 19:15:19

      Salut !

      Il n'y a aucune raison que ta classe soit en template : tes membres privés sont des int.

      Je vois que tu utilises des template pour des méthodes dont le nom s'appelle real, tu devrais virer ce template aussi. 

      Si  tu veux faire  2 + a/b, il faut faire une méthode

      friend ratio operator+(int,const ratio&) const

      • Partager sur Facebook
      • Partager sur Twitter

      Recueil de code C et C++  http://fvirtman.free.fr/recueil/index.html

        29 décembre 2022 à 23:03:39

        Salut,

        En ce qui concerne la définition d'opérateurs pour une classe, il y a deux -- et peut-être trois, dans certains cas -- grandes catégories de cas à envisager:

        • Les opérateurs qui modifient directement l'opérande de gauche;
        • les opérateurs qui créent une nouvelle instance de la classe;
        • (potentiellement) les opérateurs qui impliquent une conversion implicite de l'opérande de gauche

        Le opérateurs qui modifient directement l'opérateur de gauche

        Il s'agit le plus souvent des opérateurs à "double symbole" (comme +=, -=, *= ou /=), ceux qui te permettront d'écrire un code proche de

        Ratio r1;
        Ratio r2;
        /* ... */
        r1 += r2; // modifie l'état de r1 pour pour lui donner
                  // la valeur correspondant à r1 + r2

        Ces opérateurs sont, classiquement, implémentés sous la forme de fonctions membres / méthodes de la classe prenant un seul paramètre (déclaré comme étant une référence constante sur une instance de la classe (*) ) et renvoyant une référence non constante sur une instance de la classe.

        Le paramètre fourni correspondra à l'opérande de droite, celui de gauche étant fourni de manière implicite au travers du pointeur this.

        La référence renvoyée par cet opérateur correspond à l'instance de la classe pointée par this.

        Comme ces opérateurs vont forcément modifier l'état de l'instance de la classe représentée par le pointeur this, ils ne peuvent en aucun cas être déclarés comme des fonctions constantes sous peine d'obtenir (à juste titre) une erreur de compilation car cela impliquerait que l'opérateur s'engage formellement à ne pas modifier l'état de l'instance à partir de laquelle il est appelé.

        Les opérateurs qui créent une nouvelle instance de la classe

        Ce sont, généralement, les opérateur à "symbole unique" (comme +, -, * ou /), ceux qui te permettent d'écrire un code proche de

        Ratio r1;
        Ratio r2;
        auto result = r1 + r2; // result est une nouvelle instance
                               // dont l'état correspond au 
                               // résultat attendu pour r1 + 2

        Ces opérateurs seront, a priori, des fonctions libres (comprends: des fonctions définies en dehors de la classe) , qui pourront éventuellement être déclarés comme friend de la classe.

        Ces opérateurs seront "logiquement" définis sous la forme d'une fonction prenant deux paramètres dont le premier est une référence constante sur une instance de la classe et dont le second sera déclaré comme une référence constante sur une instance de la classe (*) et renvoyant une instance de la classe par valeur.

        Le premier paramètre correspond à l'opérande de gauche et le deuxième correspond à l'opérande de droite.

        Ces opérateurs travailleront le plus souvent en deux temps, à savoir:

        • créer une copie (non constante) de l'opérande de gauche (du premier paramètre de la fonction)
        • appeler l'opérateur "double symbole" correspondant à partir de cette copie en lui transmettant l'opérande de droite (le deuxième paramètre)

        afin de renvoyer par valeur la copie créée

        Les opérateur nécessitant une conversion de l'opérande de gauche

        Il s'agit des opérateurs à "symbole unique" (comme +, -, * ou /) utilisant un type primitif comme opérande de gauche, et qui te permettent un code proche de

        Ratio r1;
        auto result = 1 + r1; // result est une donnée de type
                              // Ratio dont l'état correspond
                              // au résultat escompté de l'opération
                              // 1 + r

        Ces opérateurs seront, a priori, des fonctions libres (comprends: des fonctions définies en dehors de la classe) nécessitant deux paramètre et renvoyant une instance de la classe par valeur

        Le premier paramètre (typiquement une donnée de type primitif) pourra être transmis sous forme de valeur, alors que le deuxième paramètre sera typiquement transmis sous la forme d'une référence constante sur une instance de la classe

        Ces opérateurs vont, le plus souvent, agir en deux temps, à savoir:

        • forcer la création d'une nouvelle instance de la classe par conversion du type primitif faisant office d'opérande de gauche
        • appeler l'opérateur "à double symbole" correspondant à partir de cette nouvelle instance

        Conclusion

        Au final, une classe pour laquelle il y a lieu de définir un opérateur nommé op prendra sans doute une forme proche de

        class MaClasse{
        public: 
            /* autorise un code proche de mc1 op= mc2 */
            MaClasse & operator op=( MaClasse const & operandeDroite);
        private:
            /* les données requises pour MaClasse */
        };
        MaClasse & MaClasse::operator op= (MaClasse const & operandeDroite){
            /* modification de l'instance pointée par this
             * pour obtenir le résultat escompté
             */
            return *this;
        }
        /* autorise un code proche de auto result = mc1 op mc2 */
        MaClasse operator op (MaClasse const & operandeGauche, MaClasse const & operandeDroite){
            /* je décris toute la logique, il y a moyen
             * de faire du code plus "compact" 
             */
            MaClasse copy{operandeGauche}; // création d'une copie
            copy op= operandeDroite; // modification de la copie
            return copy; // renvoi de la copie
        }
        MaClasse operator op (int i, MaClasse const & operandeDroite){
            /* je décris toute la logique, il y a moyen
             * de faire du code plus "compact" 
             */
            MaClasse result{i}; // nécessite le constructeur
                                // "qui va bien"
            result op= operandeDroite;
            return result;
        
        }

        REMARQUE IMPORTANTE:

        Toute règle "un peu trop générale" subira forcément quelques exceptions spécifiques en fonction du domaine auquel on essaye de les appliquer.  C'est bien évidemment le cas de celle-ci.

        En effet, il n'y a absolument aucun sens à vouloir absolument implémenter une notion (comme l'opérateur "à  double symbole" ) qui ne serait pas permise par le domaine concerné.

        Par exemple, si tu veux développer ta propre classe de vecteur mathématique, la multiplication de deux vecteur est sensée renvoyer une matrice, ce qui rend "logiquement" l'existence de l'opérateur *= totalement aberrante ;)

        (*) ou sur tout type de paramètre susceptible d'être utilisé comme deuxième opérande, en partant du principe que tout ce qui est "plus gros qu'un type primitif" mérite d'être passé sous la forme d'une référence constante

        -
        Edité par koala01 3 janvier 2023 à 22:11:02

        • Partager sur Facebook
        • Partager sur Twitter
        Ce qui se conçoit bien s'énonce clairement. Et les mots pour le dire viennent aisément.Mon nouveau livre : Coder efficacement - Bonnes pratiques et erreurs  à éviter (en C++)Avant de faire ce que tu ne pourras défaire, penses à tout ce que tu ne pourras plus faire une fois que tu l'auras fait
          29 décembre 2022 à 23:13:05

          Bonjour,

          et attention, tes operateurs ++ et -- sont mal définis, ils doivent modifier l'objet courant et retourner une référence sur celui-ci:

              Ratio&  operator++() {     // operateur pré++ retourne une référence
                  *this = *this + 1;
                  return  *this;
              }
              Ratio  operator++(int) {   // opérateur post++ retourne un objet
                  Ratio  result{*this};
                  ++*this;
                  return  result;
              }

          Et l'operator-() doit être const.

          • Partager sur Facebook
          • Partager sur Twitter

          En recherche d'emploi.

            31 décembre 2022 à 15:04:32

            Merci beaucoup pour vos réponses j'ai essayé tout ça et ça fonctionne merci !
            • Partager sur Facebook
            • Partager sur Twitter

            Surcharge d'opérateurs

            × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
            × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
            • Editeur
            • Markdown