IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C++ Discussion :

Surcharge operateur C++


Sujet :

C++

  1. #1
    Membre averti Avatar de speedy_souris
    Profil pro
    Inscrit en
    Novembre 2009
    Messages
    47
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2009
    Messages : 47
    Par défaut Surcharge operateur C++
    Bonjour à toutes et tous,

    il y a quelques temps déjà, j'ai commencé a apprendre le c, puis maintenant me revoilà dans les méandres du c++,

    dans mon apprentissage du c++, mon projet est une algorithme pour manipuler les fractions d'entier.

    Mon code ne donne pas le résultat attendu

    j'ai du mal à comprendre mon erreur

    merci de m’orientè

    Fichier.ccp

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    #include <iostream>
    #include "Fraction.h"
     
    #include <iostream>
    #include "Fraction.h"
     
    using namespace std;
     
    //---------------CONSTRUCTEURS FRACTIONS---------------------------------------------------------------------------
     
    // FRACTION A 0 (DEFAULT)
    Fraction::Fraction(): m_numerateur(0), m_denominateur(1){}
     
    // FRACTION A 1 ENTIER
    Fraction::Fraction(int numerateur): m_numerateur(numerateur), m_denominateur(1){}
     
    // FRACTION
    Fraction::Fraction(int numerateur, int denominateur): m_numerateur(numerateur), m_denominateur(denominateur){}
     
    //----------------------------------------------------------------------------------------------------------------
     
     
    // REDEFINITION OPERATEUR POUR AFFICHER
    ostream& operator<<(ostream& flux, Fraction const& fraction){
     
            fraction.afficher(flux);
     
    return flux;
    }
     
    //--------------- OPERATEUR-------------------------------------------
     
    // OPERATEUR D'ADDITION
    Fraction operator+(Fraction const& a, Fraction const& b){
     
              Fraction copie(a);
              copie += b;
     
    return copie;
    }
     
    // OPERATEUR DE MULTIPLICATION
    Fraction operator*(Fraction const& a, Fraction const& b){
     
              Fraction copie(a);
              copie *= b;
     
    return copie;
    }
     
    //--------------------------------------------------------------------
     
     
    // METHODE D'AFFICHAGE
    void Fraction::afficher(ostream& flux) const{
     
         if(m_denominateur == 1){
     
            flux << m_numerateur;
     
         }else{
               flux << m_numerateur << '/' << m_denominateur;
              }
    }
     
    //----------------------OPERATEURS--------------------------------------------------------------
     
    // METHODE D'ADDITION
    Fraction& Fraction::operator+=(const Fraction &b){
     
               m_numerateur = b.m_denominateur * m_numerateur + m_denominateur * b.m_denominateur;
               m_denominateur *= b.m_denominateur;
     
    return *this;
    }
     
    // METHODE DE MULTIPLICATION
    Fraction& Fraction::operator*=(const Fraction &b){
     
               m_numerateur *= b.m_denominateur;
               m_denominateur *= b.m_denominateur;
     
    return *this;
    }
     
    //----------------------------------------------------------------------------------------------
    fichier.h

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    #ifndef FRACTION_H_INCLUDED
    #define FRACTION_H_INCLUDED
     
    #include <iostream>
     
    class Fraction{
     
    public:
     
    //-----------PROTOTYPES CONSTRUCTEURS FRACTIONS----------------------------
     
            // FRACTION A 0 (DEFAULT)
             Fraction();
     
             // FRACTION A 1 ENTIER
             Fraction(int numerateur);
     
     
                // FRACTION
             Fraction(int numerateur, int denominateur);
     
    //------------------------------------------------------------------------
     
              // METHODE D'AFFICHAGE
             void afficher(std::ostream& flux) const;
     
             // METHODE D'ADDITION
             Fraction& operator+=(Fraction const& b);
     
             // METHODE DE MULTIPLICATION
             Fraction& operator*=(Fraction const& b);
     
     
    private:
     
             int m_numerateur;
             int m_denominateur;
    };
     
    //-------------OPERATEURS-------------------------------------------------
     
    // REDEFINITION OPERATEUR POUR AFFICHER
    std::ostream& operator<<(std::ostream& flux, Fraction const& fraction);
     
    //OPERATEUR D'ADDITION
    Fraction operator+(Fraction const& a, Fraction const& b);
     
    // OPERATEUR DE MULTIPLICATION
    Fraction operator*(Fraction const& a, Fraction const& b);
     
    //------------------------------------------------------------------------
     
     
     
    #endif // FRACTION_H_INCLUDED
    fichier main.ccp

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    #include <iostream>
    #include "Fraction.h"
     
    using namespace std;
     
    int main(){
     
        Fraction a(4,5) ; //Déclare une fraction valant 4/5
        Fraction b(2);        //Déclare une fraction valant 2/1 (ce qui vaut 2)
        Fraction c,d;         //Déclare deux fractions valant 0
     
        c = a + b;
        cout << a << " + " << b << " = " << c << endl << endl;
     
        d = a * b;
        cout << a << " x " << b << " = " << d << endl;
     
        return 0;
    }
    résultat de l'addition : 9 / 5

    résultat de la multiplication : 4 / 5

  2. #2
    Expert éminent
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 392
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 392
    Par défaut
    Ce code est suspect:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    Fraction& Fraction::operator+=(const Fraction &b){
     
               m_numerateur = b.m_denominateur * m_numerateur + m_denominateur * b.m_denominateur;
               m_denominateur *= b.m_denominateur;
     
    return *this;
    }
    Pour commencer, il n'utilise pas b.m_numerateur...
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  3. #3
    Membre éclairé
    Homme Profil pro
    Inscrit en
    Février 2013
    Messages
    70
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Canada

    Informations forums :
    Inscription : Février 2013
    Messages : 70
    Par défaut
    Tout le monde s'attend à ce que a=a+5 et a+=5 donne le même résultat. C'est certain pour les types prédéfinis comme int et unsigned mais C++ n'offre aucune garantie pour les types définis par l'utilisateur. Les opérateurs =. + et += sont trois fonctions distinctes qui pourraient avoir été implémentées d'une manière telle que cette relation naturelle entre + et += ne soit pas respectée.

    Afin d'éviter ce problème, on doit toujours implémenter l’opérateur + en se servant de l’opérateur +=

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    const Fraction operator +(const Fraction& lhs, const Fraction& rhs) //lhs left hand side, rhs , righr hand side, ce qui est à gauche et à droite du signe +
    {
        return Fraction(lhs)+=rhs;
    }
    En fait, on peut faire beaucoup mieux, on peut régler le problème une fois pour toute pour tous les types.


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    template <typename T> inline const T operator+(const T& lhs, const T& rhs)
    {
       return T(lhs)+=rhs;
    }
    Ce qui signifie que pour tous les types T, l’opérateur + sera généré automatiquement, lorsque cela sera nécessaire, en utilisant l'opérateur +=.

  4. #4
    Membre chevronné Avatar de fenkys
    Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Octobre 2007
    Messages
    376
    Détails du profil
    Informations personnelles :
    Âge : 57
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Produits et services télécom et Internet

    Informations forums :
    Inscription : Octobre 2007
    Messages : 376
    Par défaut
    @pierre : c'est exactement ce qu'il a fait, rien à dire ce côté.

    @speedy_souris : tu t'es allègrement mélangé les pinceaux entre les numérateurs et dénominateur dans la redéfinition des opérateur *= et +=. Medinoc a évoqué l'erreur dans +=, il y en a aussi une dans *=.

  5. #5
    Membre averti Avatar de speedy_souris
    Profil pro
    Inscrit en
    Novembre 2009
    Messages
    47
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2009
    Messages : 47
    Par défaut Relecture et rerelecture à tête reposer !
    Bonjour Médinoc, Pierre le Grand, fenkys,

    merci pour vos réponses rapides

    @Médinoc, avec un autre regard et un peu de recul les erreurs sont évidentes, quand on est dans le code , il est très difficile de voir ce type d'erreur surtout en tant que débutant

    je vais relire le code à tête reposé pour bien comprendre mes erreurs algorithmiques

    bien à vous,

    speedy_souris

  6. #6
    Expert éminent

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 202
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 202
    Par défaut
    D'ailleurs, pour les opérateurs relationnels comme >, <=, >= et !=, il existe std::rel_ops, dont on peut hériter si on dispose juste de == et <

    Il y a aussi Boost.operators

  7. #7
    Membre averti Avatar de speedy_souris
    Profil pro
    Inscrit en
    Novembre 2009
    Messages
    47
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2009
    Messages : 47
    Par défaut Tout finis par arriver !
    Re,

    hey voila mon code une fois mes erreurs corriger et la cerise sur le gateau j'ai le résultat attendu

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    // METHODE D'ADDITION
    Fraction& Fraction::operator+=(const Fraction &b){
     
               m_numerateur = m_numerateur * b.m_denominateur + b.m_numerateur * m_denominateur;
               m_denominateur *= b.m_denominateur;
     
    return *this;
    }
     
    // METHODE DE MULTIPLICATION
    Fraction& Fraction::operator*=(const Fraction &b){
     
               m_numerateur *= b.m_numerateur;
               m_denominateur *= b.m_denominateur;
     
    return *this;
    }

  8. #8
    Expert confirmé
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    Juillet 2013
    Messages
    4 750
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Analyste/ Programmeur

    Informations forums :
    Inscription : Juillet 2013
    Messages : 4 750
    Par défaut
    Pour la culture générale, et parce que tu travailles avec des fractions, tu as d'autres opérateurs

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    Fraction::operator int() { return (int) std::floor(m_numerateur / m_denominateur); }
     
    Fraction::operator double() { return (double) (m_numerateur / m_denominateur); }

    Édit: white_tentacle m'a repris , mais c'est à titre d'exemple: on peut prendre en compte d'autres PODs que int et double, et faire d'autres conversions.

  9. #9
    Membre Expert
    Avatar de white_tentacle
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    1 505
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 1 505
    Par défaut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    Fraction::operator int() { return m_numerateur / m_denominateur; }
     
    Fraction::operator double() { return ((double)m_numerateur / m_denominateur); }
    Pour la conversion en int, ça revient à effectuer la division entière (enfin, ça dépend comment on traite les négatifs -1/2 doit donner zéro ou un ?).

    Pour la conversion en double, il faut caster en double avant la division, sinon c’est une division entière qui sera effectuée.

  10. #10
    Membre éclairé
    Homme Profil pro
    Inscrit en
    Février 2013
    Messages
    70
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Canada

    Informations forums :
    Inscription : Février 2013
    Messages : 70
    Par défaut
    Citation Envoyé par white_tentacle Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    Fraction::operator int() { return m_numerateur / m_denominateur; }
     
    Fraction::operator double() { return ((double)m_numerateur / m_denominateur); }
    Il est dangereux de définir une fonction de conversion implicite de cette manière. Supposons que le concepteur de la classe Fraction ait oublié de surcharger << mais qu'il ait défini uniquement la fonction de conversion en double. Ce qui suit va imprimer 0.5


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    Fraction f(1,2)
    std::cout<<f<<end;
    Lorsque le gentil compilateur découvre l'absence de l'opérateur << pour la classe Fraction, il fait un grand effort pour que le code soit légal. Il cherche un moyen de convertir l'instance de Fraction vers un autre type qui est imprimable. Comme la conversion en double existe, elle est utilisée. Le problème est que cela n'est pas notre décision consciente. C'est le compilateur qui décide quand ce type de fonction sera appelée en utilisant des règles complexes.

    Il est préférable de coder dans le style de Lazarus/Delphi en utilisant une fonction.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    double Fraction::AsDouble() const
    { 
       return ((double)m_numerateur / m_denominateur); 
     
    }
    Cela a le grand avantage de permettre de décider quand cette fonction sera appelée au lieu de laisser le compilateur décider. Ce n'est pas un hasard si std::string utilise la fonction c_str() au lieu d'une conversion directe en char*. Cette conversion serait trop dangereuse.


    Le code original comporte aussi deux constructeurs de trop, on peut tout regrouper en un seul constructeur.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    // FRACTION
    explicit Fraction::Fraction(int numerateur=0, int denominateur=1): m_numerateur(numerateur), m_denominateur(denominateur){}
    Tant qu'à parler de conversion, le explicit a pour but de bloquer une autre conversion implicite problématique. Tout constructeur qui peut être appelé avec un seul argument définit automatiquement une conversion implicite vers le type de cet argument. Ce code serait donc légal si le mot explicit avait été omis.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Fraction f=2; //effectue une conversion implicite des entiers vers les fractions avec le constructeur Fraction(2)
    Cela peut sembler pratique jusqu'à ce que quelqu'un essaye.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    Fraction f=0.2;
    assert(f==Fraction(2,10); //Échec
    Le compilateur, ne découvrant pas de conversion des réels vers les fractions, va effectuer un gros effort pour rendre la déclaration légale. Il va tout d'abord convertir 0.5 en entier, ce qui donne 0 et ensuite convertir 0 en fraction. Ce n'est pas ce que l'on désirait. Ceci démontre que ces conversions implicites par constructeur sont dangereuses. Encore une fois, c'est le compilateur qui décide quand les utiliser. Avec le mot explicit, il faut appeler le constructeur explicitement et la déclaration

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Fraction f=0.2; //Erreur de compilation
    est illégale.

  11. #11
    Membre Expert
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2012
    Messages
    1 711
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2012
    Messages : 1 711
    Par défaut
    Citation Envoyé par Pierre le Grand Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    double Fraction::AsDouble() const
    { 
       return ((double)m_numerateur / m_denominateur); 
     
    }
    Cela a le grand avantage de permettre de décider quand cette fonction sera appelée au lieu de laisser le compilateur décider. Ce n'est pas un hasard si std::string utilise la fonction c_str() au lieu d'une conversion directe en char*. Cette conversion serait trop dangereuse.
    Ou mieux : un cast explicite.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    struct Foo {
    	explicit operator int() const {
    		return 42;
    	}
    };
     
    void bar(int) { }
     
    Foo f;
    bar(f); // error C2664: 'void bar(int)'*: impossible de convertir l'argument 1 de 'Foo' en 'int'
    bar(int(f)); // ok
    Mais tant qu'on en abuse pas, c'est très bien les casts implicites. Çà évite une syntaxe lourde pour rien.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. surcharge operateur delete et héritage
    Par Hervé dans le forum C++
    Réponses: 5
    Dernier message: 29/03/2006, 13h59
  2. Surcharge operateur =
    Par rulianf dans le forum C++
    Réponses: 9
    Dernier message: 23/02/2006, 00h32
  3. [debutante] surcharge operateur <<
    Par norkius dans le forum Débuter
    Réponses: 3
    Dernier message: 24/10/2005, 12h20
  4. [Surcharge]Operateur<< avec une classe maison
    Par KeNnEdY dans le forum C++
    Réponses: 6
    Dernier message: 14/09/2005, 15h51
  5. surcharge operateur && pointeurs
    Par le y@m's dans le forum C++
    Réponses: 6
    Dernier message: 10/05/2005, 15h57

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo