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 :

Problème avec la surcharge d'opérateur de flux


Sujet :

C++

  1. #1
    Membre régulier
    Homme Profil pro
    Etudiant CNAM (DIE20)
    Inscrit en
    Janvier 2010
    Messages
    151
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Etudiant CNAM (DIE20)

    Informations forums :
    Inscription : Janvier 2010
    Messages : 151
    Points : 97
    Points
    97
    Par défaut Problème avec la surcharge d'opérateur de flux
    Bonjour,

    Je débute en C++ ainsi qu'en POO depuis quelques jours et j'apprécierai grandement votre aide.
    J'aborde actuellement les classes, et en particulier l'utilisation d'opérateurs.

    Je me suis imaginé un exercice dans lequel j'ai une classe nommée Monnaie qui a comme variables membres 3 valeurs numériques (livre, shilling, penny) et une string utilisable par un accesseur permettant d'afficher la valeur en texte.

    Le soucis vient de l'impossibilité de passer un objet Monnaie issue d'une opération arithmétique directement dans un cout malgré la surcharge de l'opérateur << de ostream. En effet, tout fonctionne dans le programme, sauf la dernière ligne de mon main qui provoque une erreur lors de la compilation (no match for ‘operator<<’ in ‘std::operator<<):

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    cout << objetMonnaie1 << " + 35 pence = " << objetMonnaie1 + 35 << endl;
    Pourtant, la surcharge de operator<< fonctionne sachant que ceci passe à la compilation et au lancement :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    cout << objetMonnaie1 << endl;
    C'est donc mon opérateur + qui met apparemment le boxon.
    Dans la logique du programme, objetMonnaie1 + 35 renvoie pourtant bien un objet de type Monnaie, comme objetMonnaie1...

    Voici le code source complet, également fourni en pièce jointe zippée :
    (NB: les remarques sur l'amélioration du code pour le reste du programme sont bienvenues)

    main.cpp
    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
    #include <iostream>
    #include "include/Monnaie.h"
     
    using namespace std;
     
    int main()
    {
        Monnaie monArgent(3,45,15);
        cout << "3 livres 45 shillings 15 pence = " << monArgent.str() << endl;
        monArgent.vaut(6,19,13);
        cout << "6 livres 19 shillings 13 pence = " << monArgent.str() << endl;
        cout << endl;
     
        Monnaie tonArgent(4,1);
        cout << "Ton argent : " << tonArgent.str() << endl;
        tonArgent.vaut(1);
        cout << "En fait, ton argent vaut " << tonArgent.str() << endl;
        cout << endl;
     
        Monnaie gain(20,20,20);
        Monnaie finDeMois(monArgent + gain);
        cout << "Si je travaillais, je pourrais gagner " << gain.str();
        cout << " et avoir un total de " << finDeMois.str() << endl;
     
        monArgent += tonArgent;
        cout << "C'est plus facile de te piquer ton argent, et j'ai maintenant " << monArgent.str();
        cout << " en poche" << endl;
     
        monArgent /= 2;
        cout << "Comme je dois partager à part égale avec ma femme, il ne me reste que ";
        cout << monArgent.str() << "..." << endl << endl;
     
        // utilisation de la surcharge sur l'opérateur +=
        Monnaie objetMonnaie1(1,1,1), objetMonnaie2(2,13,11);
        // - ajout d'un objet Monnaie à un autre
        cout << objetMonnaie1.str() << " + " << objetMonnaie2.str() << " = ";
        objetMonnaie1 += objetMonnaie2;
        cout << objetMonnaie1.str() << endl;
        // - ajout de 35 pence à un objet Monnaie
        cout << objetMonnaie1.str() << " + 35 pence = ";
        objetMonnaie1 += 35;
        cout << objetMonnaie1.str() << endl;
     
        // utilisation de l'opérateur de flux sortant qui réduit à 1 ligne les 3 lignes précédentes
        cout << objetMonnaie1 << " + 35 pence = " << objetMonnaie1 + 35 << endl;
     
        return 0;
    }
    Monnaie.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
    #ifndef MONNAIE_H
    #define MONNAIE_H
     
    #include <string>
     
    class Monnaie
    {
        public:
            Monnaie(int livre = 0, int shilling = 0, int penny = 0);
            ~Monnaie();
            void vaut(int livre = 0, int shilling = 0, int penny = 0);
            std::string str();
            Monnaie &operator+=(Monnaie const &autreMonnaie);
            Monnaie &operator+=(int const pennyEnPlus);
            Monnaie &operator/=(double const &diviseur);
     
        private:
            int m_livre, m_shilling, m_penny;
            std::string m_string;
    };
     
    Monnaie operator+(Monnaie const &monnaie1, Monnaie const &monnaie2);
    Monnaie operator+(Monnaie const &monnaie, int const pennyEnPlus);
    /*
    * À noter que operator+ n'intervient pas sur un objet déjà existant,
    * n'appartient pas à l'espace de nom Monnaie,
    * et est déclaré en dehors de la classe comme une simple fonction
    * basée sur operator+= qui lui appartient à la classe.
    */
    std::ostream& operator<<(std::ostream &fluxSortant, Monnaie &monnaie);
     
    #endif // MONNAIE_H
    Monnaie.cpp
    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
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    #include "../include/Monnaie.h"
    #include <sstream>
    #include <iostream>
     
    void format(int &livre, int &shilling, int &penny, std::string &chaine)
    /*
    * Conversion livre - shilling - penny
    * 1 livre = 20 shilling
    * 1 shilling = 12 penny
    *
    * Affectation à une string nommée chaine de
    * "xx livre(s) xx shilling(s) xx penny/pence"
    * représentant la valeur de Monnaie
    */
    {
        // formatage numérique
        if (penny > 11)
        {
            shilling += penny/12;
            penny %= 12;
        }
        if (shilling > 19)
        {
            livre += shilling/20;
            shilling %= 20;
        }
     
        // formatage string
        // - flux de sortie pour string
        std::ostringstream fluxSortantString;
        // - écriture dans le flux
        if (livre != 0)
        {
            fluxSortantString << livre << " livre";
            if (livre > 1) {fluxSortantString << "s";}
            if (shilling != 0 || penny != 0) {fluxSortantString << " ";}
        }
        if (shilling != 0)
        {
            fluxSortantString << shilling << " shilling";
            if (shilling > 1) {fluxSortantString << "s";}
            if (penny != 0) {fluxSortantString << " ";}
        }
        if (penny != 0)
        {
            fluxSortantString << penny;
            if (penny > 1) {fluxSortantString << " pence";}
            else {fluxSortantString << " penny";}
        }
        // - récupération de la string
        chaine = fluxSortantString.str();
    }
     
    Monnaie::Monnaie(int livre, int shilling, int penny) : m_livre(livre), m_shilling(shilling), m_penny(penny)
    {
        //constructeur
        format(m_livre, m_shilling, m_penny, m_string);
    }
     
    Monnaie::~Monnaie()
    {
        //destructeur vide
    }
     
    void Monnaie::vaut(int livre, int shilling, int penny)
    {
        m_livre = livre;
        m_shilling = shilling;
        m_penny = penny;
        format(m_livre, m_shilling, m_penny, m_string);
     
    }
     
    std::string Monnaie::str()
    {
        return m_string;
    }
     
    Monnaie& Monnaie::operator+=(Monnaie const &autreMonnaie)
    {
        // ajout des valeurs de autreMonnaie
        m_livre += autreMonnaie.m_livre;
        m_shilling += autreMonnaie.m_shilling;
        m_penny += autreMonnaie.m_penny;
        // formatage et retour
        format(m_livre, m_shilling, m_penny, m_string);
        return *this;
    }
     
    Monnaie& Monnaie::operator+=(int const pennyEnPlus)
    {
        // ajout des pennyEnPlus
        m_penny += pennyEnPlus;
        // formatage et retour
        format(m_livre, m_shilling, m_penny, m_string);
        return *this;
    }
     
    Monnaie operator+(Monnaie const &monnaie1, Monnaie const &monnaie2)
    /*
    * operator+ n'intervient pas sur un objet déjà existant et n'appartient pas à l'espace de nom Monnaie
    */
    {
        // affectation des valeurs de monnaie1 à une copie
        Monnaie copieMonnaie(monnaie1);
        // ajout des valeurs de monnaie2
        copieMonnaie += monnaie2;
        // retour (formatage effectué par l'opérateur +=)
        return copieMonnaie;
    }
     
    Monnaie operator+(Monnaie const &monnaie, int const pennyEnPlus)
    /*
    * operator+ n'intervient pas sur un objet déjà existant et n'appartient pas à l'espace de nom Monnaie
    */
    {
        // affectation des valeurs de monnaie1 à une copie
        Monnaie copieMonnaie(monnaie);
        // ajout des valeurs de monnaie2
        copieMonnaie += pennyEnPlus;
        // retour (formatage effectué par l'opérateur +=)
        return copieMonnaie;
    }
     
    Monnaie& Monnaie::operator/=(double const &diviseur)
    {
        // division des valeurs de Monnaie
        m_livre /= diviseur;
        m_shilling /= diviseur;
        m_penny /= diviseur;
        // formatage et retour
        format(m_livre, m_shilling, m_penny, m_string);
        return *this;
    }
     
    std::ostream& operator<<(std::ostream &fluxSortant, Monnaie &monnaie)
    /*
    * cout est passé en référence dans fluxSortant
    * l'objet de type Monnaie est passé en référence dans la variable monnaie
    */
    {
        fluxSortant << monnaie.str(); // passage dans le flux de monnaie.m_string grace à son accesseur
        return fluxSortant;
    }

  2. #2
    Invité
    Invité(e)
    Par défaut
    C'est bien la surcharge de ton operateur << qui est mal faite : elle n'accepte pas d'objet Monnaie temporaire (en l'occurence, celui renvoyé par l'opérateur+). Si tu en voir le coeur net essaie ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    cout << Monnaie(3, 45, 15) << endl;
    L'erreur vient du fait que son second paramètre est une rvalue-reference non constante (i.e. qui ne peut référer à un objet temporaire ); pour avoir une sémantique correcte il faut qu'elle soit constante.
    Cette modification entrainera une seconde erreur de compilation : il faut indiquer que ta méthode str() ne modifie pas l'objet en la qualifiant elle aussi de constante

  3. #3
    Membre régulier
    Homme Profil pro
    Etudiant CNAM (DIE20)
    Inscrit en
    Janvier 2010
    Messages
    151
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Etudiant CNAM (DIE20)

    Informations forums :
    Inscription : Janvier 2010
    Messages : 151
    Points : 97
    Points
    97
    Par défaut
    donc prototype de l'opérateur << modifié ainsi :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    std::ostream& operator<<(std::ostream &fluxSortant, Monnaie const &monnaie);
    et celui de la fonction str() comme cela :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    std::string str() const;
    Tout est en string const et peut être envoyé dans le flux de sortie cout.

    Que du bonheur, ça fonctionne ! Merci beaucoup pour l'explication.

    En plus je viens de tilter qu'on pouvait mettre const à la fin d'une fonction... Du coup, j'imagine que par principe, une fonction accesseur est toujours mise en const puisque qu'elle sert simplement à renvoyer la valeur d'une variable membre sans qu'on puisse la modifier ?

  4. #4
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Salut,
    Citation Envoyé par spirzouf Voir le message
    Du coup, j'imagine que par principe, une fonction accesseur est toujours mise en const puisque qu'elle sert simplement à renvoyer la valeur d'une variable membre sans qu'on puisse la modifier ?
    Par principe, les accesseurs sont bannis car ils violent l'encapsulation

    Sinon, effectivement, il faut respecter la constance de tes fonctions au même titre que de tes variables. Cela est indispensable pour un design correct. Son nom respect est souvent source de bug et de calvitie (longues heures d'arrachage de cheveux).

  5. #5
    Membre régulier
    Homme Profil pro
    Etudiant CNAM (DIE20)
    Inscrit en
    Janvier 2010
    Messages
    151
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Etudiant CNAM (DIE20)

    Informations forums :
    Inscription : Janvier 2010
    Messages : 151
    Points : 97
    Points
    97
    Par défaut
    Citation Envoyé par 3DArchi Voir le message
    Salut,
    Par principe, les accesseurs sont bannis car ils violent l'encapsulation ;.
    Bonjour,

    Je croyais justement que l'accesseur permettait de respecter l'encapsulation car si il donnait effectivement "en lecture" l'accès à une caractéristique de l'objet, il empêchait toute modification

    Dans mon exemple, il faudrait donc par principe bannir l'utilisation publique de ma fonction Monnaie.str() qui renvoie la valeur de Monnaie.m_string, et la mettre en private ? Mais dans ce cas,je vais être ennuyé par "std::ostream& operator<<" qui est déclaré hors classe Monnaie et fais appel à la fonction Monnaie.str() pour intégrer la représentation string de mon objet dans le flux cout ?

    Qu'ont prévus les dieux du C++ pour me délivrer de cette impasse ?? Mon petit doigt me dit que je devrais reprendre le code de ma fonction format qui prend les variables de Monnaie pour renvoyer une chaîne directement au flux, sans une fonction accesseur... Je vais creuser l'idée, merci de me dire si je suis dans le vrai !

  6. #6
    Membre régulier
    Homme Profil pro
    Etudiant CNAM (DIE20)
    Inscrit en
    Janvier 2010
    Messages
    151
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Etudiant CNAM (DIE20)

    Informations forums :
    Inscription : Janvier 2010
    Messages : 151
    Points : 97
    Points
    97
    Par défaut
    Désolé, je sèche complètement...

    Ce que j'ai fait :

    1. J'ai supprimé l'accesseur str() de la classe Monnaie

    2. J'ai créé une fonction de la classe Monnaie nommée sortieVersFluxOstream, de type void et constante, qui prend en argument un flux ostream passé en référence et injecte dans ce flux les données à afficher.

    3. J'ai modifié le contenu de la fonction surchargeant l'opérateur<< d'ostream et qui se limite désormais à l'appel à la fonction sortieVersFluxOstream sus-décrite et au return.

    Et maintenant, j'ai la même erreur que dans mon post d'origine.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    undefined reference to `operator<<(std::basic_ostream<char, std::char_traits<char> >&, Monnaie const&)'
    Pourtant, je crois avoir bien fait attention aux const ?
    Quelle erreur ai-je commis ?

    Voici les sources (aussi en pièce jointe) :

    main.cpp
    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
    #include <iostream>
    #include "include/Monnaie.h"
     
    using namespace std;
     
    int main()
    {
        // utilisation de l'opérateur de flux << d'ostream pour afficher un objet Monnaie
     
        Monnaie monArgent(3,45,15);
        cout << "3 livres 45 shillings 15 pence = " << monArgent << endl;
        monArgent.vaut(6,19,13);
        cout << "6 livres 19 shillings 13 pence = " << monArgent << endl;
        cout << endl;
     
        Monnaie tonArgent(4,1);
        cout << "Ton argent : " << tonArgent << endl;
        tonArgent.vaut(1);
        cout << "En fait, ton argent vaut " << tonArgent << endl;
        cout << endl;
     
        Monnaie gain(20,20,20);
        Monnaie finDeMois(monArgent + gain);
        cout << "Si je travaillais, je pourrais gagner " << gain;
        cout << " et avoir un total de " << finDeMois << endl;
     
        monArgent += tonArgent;
        cout << "C'est plus facile de te piquer ton argent, et j'ai maintenant " << monArgent;
        cout << " en poche" << endl;
     
        monArgent /= 2;
        cout << "Comme je dois partager à part égale avec ma femme, il ne me reste que ";
        cout << monArgent << "..." << endl << endl;
     
        // utilisation de la surcharge sur l'opérateur += de Monnaie
        Monnaie objetMonnaie1(1,1,1), objetMonnaie2(2,13,11);
        // - ajout d'un objet Monnaie à un autre
        cout << objetMonnaie1 << " + " << objetMonnaie2 << " = ";
        objetMonnaie1 += objetMonnaie2;
        cout << objetMonnaie1 << endl;
        // - ajout de 35 pence à un objet Monnaie
        cout << objetMonnaie1 << " + 35 pence = " << objetMonnaie1 + 35 << endl;
     
        return 0;
    }
    Monnaie.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
    #ifndef MONNAIE_H
    #define MONNAIE_H
     
    #include <iostream>
     
    class Monnaie
    {
        public:
            Monnaie(int livre = 0, int shilling = 0, int penny = 0);
            ~Monnaie();
            void vaut(int livre = 0, int shilling = 0, int penny = 0);
            Monnaie &operator+=(Monnaie const &autreMonnaie);
            Monnaie &operator+=(int const pennyEnPlus);
            Monnaie &operator/=(double const &diviseur);
            void sortieVersFluxOstream(std::ostream &fluxSortant) const;
     
        private:
            int m_livre, m_shilling, m_penny;
    };
     
    Monnaie operator+(Monnaie const &monnaie1, Monnaie const &monnaie2);
    Monnaie operator+(Monnaie const &monnaie, int const pennyEnPlus);
    /*
    * À noter que operator+ n'intervient pas sur un objet déjà existant,
    * n'appartient pas à l'espace de nom Monnaie,
    * et est déclaré en dehors de la classe comme une simple fonction
    * de surcharge de operator+ et est basée sur Monnaie::operator+=
    * qui elle appartient à la classe Monnaie
    */
     
    std::ostream& operator<<(std::ostream &fluxSortant, Monnaie const &monnaie);
     
    #endif // MONNAIE_H
    Monnaie.cpp
    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
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    #include "../include/Monnaie.h"
    #include <iostream>
     
     
    void format(int &livre, int &shilling, int &penny)
    /*
    * Conversion livre - shilling - penny
    * 1 livre = 20 shilling
    * 1 shilling = 12 penny
    */
    {
        // formatage numérique
        if (penny > 11)
        {
            shilling += penny/12;
            penny %= 12;
        }
        if (shilling > 19)
        {
            livre += shilling/20;
            shilling %= 20;
        }
    }
     
    Monnaie::Monnaie(int livre, int shilling, int penny) : m_livre(livre), m_shilling(shilling), m_penny(penny)
    {
        //constructeur
        format(m_livre, m_shilling, m_penny);
    }
     
    Monnaie::~Monnaie()
    {
        //destructeur vide
    }
     
    void Monnaie::vaut(int livre, int shilling, int penny)
    {
        m_livre = livre;
        m_shilling = shilling;
        m_penny = penny;
        format(m_livre, m_shilling, m_penny);
     
    }
     
    void Monnaie::sortieVersFluxOstream(std::ostream &fluxSortant) const
    {
        // fonction publique ayant accès aux variablesm_livre, m_shilling et m_penny,
        // permettant le passage dans fluxSortant d'une représentation string de l'objet monnaie
        if (m_livre != 0)
        {
            fluxSortant << m_livre << " livre";
            if (m_livre > 1) {fluxSortant << "s";}
            if (m_shilling != 0 || m_penny != 0) {fluxSortant << " ";}
        }
        if (m_shilling != 0)
        {
            fluxSortant << m_shilling << " shilling";
            if (m_shilling > 1) {fluxSortant << "s";}
            if (m_penny != 0) {fluxSortant << " ";}
        }
        if (m_penny != 0)
        {
            fluxSortant << m_penny;
            if (m_penny > 1) {fluxSortant << " pence";}
            else {fluxSortant << " penny";}
        }
    }
     
    Monnaie& Monnaie::operator+=(Monnaie const &autreMonnaie)
    {
        // ajout des valeurs de autreMonnaie
        m_livre += autreMonnaie.m_livre;
        m_shilling += autreMonnaie.m_shilling;
        m_penny += autreMonnaie.m_penny;
        // formatage et retour
        format(m_livre, m_shilling, m_penny);
        return *this;
    }
     
    Monnaie& Monnaie::operator+=(int const pennyEnPlus)
    {
        // ajout des pennyEnPlus
        m_penny += pennyEnPlus;
        // formatage et retour
        format(m_livre, m_shilling, m_penny);
        return *this;
    }
     
    Monnaie operator+(Monnaie const &monnaie1, Monnaie const &monnaie2)
    /*
    * operator+ n'intervient pas sur un objet déjà existant et n'appartient pas à l'espace de nom Monnaie
    */
    {
        // affectation des valeurs de monnaie1 à une copie
        Monnaie copieMonnaie(monnaie1);
        // ajout des valeurs de monnaie2
        copieMonnaie += monnaie2;
        // retour (formatage effectué par l'opérateur +=)
        return copieMonnaie;
    }
     
    Monnaie operator+(Monnaie const &monnaie, int const pennyEnPlus)
    /*
    * operator+ n'intervient pas sur un objet déjà existant et n'appartient pas à l'espace de nom Monnaie
    */
    {
        // affectation des valeurs de monnaie1 à une copie
        Monnaie copieMonnaie(monnaie);
        // ajout des valeurs de monnaie2
        copieMonnaie += pennyEnPlus;
        // retour (formatage effectué par l'opérateur +=)
        return copieMonnaie;
    }
     
    Monnaie& Monnaie::operator/=(double const &diviseur)
    {
        // division des valeurs de Monnaie
        m_livre /= diviseur;
        m_shilling /= diviseur;
        m_penny /= diviseur;
        // formatage et retour
        format(m_livre, m_shilling, m_penny);
        return *this;
    }
     
    std::ostream& operator<<(std::ostream &fluxSortant, Monnaie &monnaie)
    /*
    * cout est passé en référence dans fluxSortant
    * l'objet de type Monnaie est passé en référence dans la variable monnaie
    */
    {
        // Passage dans fluxSortant d'une représentation string de l'objet monnaie
        // via la fonction publique sortieVersFluxOstream() qui a accès aux variables
        // m_livre, m_shilling et m_penny
        monnaie.sortieVersFluxOstream(fluxSortant);
        // retour obligatoire d'un flux cf prototype de cette fonction de surcharge d'opérateur
        // pour un objet ostream
        return fluxSortant;
    }
    Fichiers attachés Fichiers attachés

  7. #7
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Salut,

    Citation Envoyé par spirzouf Voir le message
    Et maintenant, j'ai la même erreur que dans mon post d'origine.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    undefined reference to `operator<<(std::basic_ostream<char, std::char_traits<char> >&, Monnaie const&)'
    Pourtant, je crois avoir bien fait attention aux const ?
    Quelle erreur ai-je commis ?
    Voici ton .h :
    Citation Envoyé par spirzouf Voir le message
    Monnaie.h
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    std::ostream& operator<<(std::ostream &fluxSortant, Monnaie const &monnaie);
    Et voici ton .cpp
    Citation Envoyé par spirzouf Voir le message
    Monnaie.cpp
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    std::ostream& operator<<(std::ostream &fluxSortant, Monnaie &monnaie)
    Il manquerait pas quelque chose dans la défintion dans le .cpp


    Citation Envoyé par spirzouf Voir le message
    Bonjour,

    Je croyais justement que l'accesseur permettait de respecter l'encapsulation car si il donnait effectivement "en lecture" l'accès à une caractéristique de l'objet, il empêchait toute modification
    C'est une réponse qu'on envoie souvent surtout vers les débutants car un premier (mauvais) réflexe lorsqu'on débute est d'ajouter systématiquement des accesseurs et des mutateurs vers les attributs d'une classe. Or il faut voir la chose sous deux aspects : les attributs reflètent l'état interne d'une classe et relèvent du 'détail' d'implémentation. Mettre une fonction qui y accède ou qui les modifie affaiblie fortement l'encapsulation de cet état interne dans la partie privée de la classe. Il peut y avoir des propriétés auxquelles ont peut accéder avec des fonctions ressemblant à GetName ou SetName, mais il n'y a pas (forcément) de correspondance 1 pour 1 entre une telle fonction et un membre interne de la classe. Je te laisse lire cette entrée de la F.A.Q. qui explique très bien la chose : Quand et comment faut-il utiliser des accesseurs / mutateurs ?. Pour aller plus loin, une classe est définie d'abord par les services attendus par ceux qui vont l'utiliser. Et c'est à partir de là que l'on détermine son interface, c'est à dire les fonctions publiques intéressantes. Il résulte souvent que les fonctions qu'on expose ont alors un niveau d'abstraction plus élevé et que les get/set deviennent naturellement inutiles. Là encore, je te renvoi à l'exemple de la F.A.Q. : La conception d'une classe doit-elle se faire plutôt par l'extérieur ou par l'intérieur ?

    La sortie d'un objet vers un flux est un cas particulier (qu'on appelle sérialisation). Il existe plusieurs façon de faire mais c'est un cas qui se prête assez bien à l'amitié : l'amitié ne brise pas l'encapsulation en permettant de choisir avec précision qui peut avoir accès aux détails internes de la classe et en limitant donc cet accès à des fonctions/classes particulières et identifiées. Je te propose de bien regarder cette entrée de la F.A.Q concernant la sérialisation et son code : Comment utiliser les flux pour afficher ou saisir mes objets ?. En fait, on se rend compte que la façon dont un objet est affiché à l'écran ou écrit dans un fichier intéresse assez peu l'objet (l'affichage sera peut être différent selon la langue, selon des options de configurations, selon la valeur, etc...). Il est donc assez naturel de déléguer cette affichage dans une fonction dédiée mais qui nécessite d'accéder à des détails d'implémentation de la classe pour réaliser correctement la tâche. D'où une amitié bien positionnée.

    Deux petites remarques :
    ton destructeur est inutile. Un destructeur qui ne fait rien est souvent un indice qu'il ne sert à rien. Le compilateur en implémente un par défaut qui suffit souvent dans ce genre de classe assez simple. L'exception étant les classes gérant des ressources qui doivent alors les libérer (mais tu verras ça plus tard dans ton apprentissage) et les classes servant de base à un héritage pour lesquels le destructeur doit être virtuel. Pour les classes comme Monnaie, le destructeur par défaut proposer par le compilateur suffit. Cf F.A.Q : Quand dois-je définir un destructeur ?

    Monnaie(int livre = 0, int shilling = 0, int penny = 0); devrait être explicite : explicit Monnaie(int livre = 0, int shilling = 0, int penny = 0);. Cela évite des conversions implicites qui pourrait surprendre Monnaie m = 42; // Quelle signification ?

  8. #8
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Yvelines (Île de France)

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

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Points : 16 213
    Points
    16 213
    Par défaut
    Citation Envoyé par 3DArchi Voir le message
    Il est donc assez naturel de déléguer cette affichage dans une fonction dédiée mais qui nécessite d'accéder à des détails d'implémentation de la classe pour réaliser correctement la tâche.
    Le problème est que l'opérateur << fait logiquement partie de la classe, et peut donc accéder à des données privées, mais que pour des raisons techniques de syntaxe de surcharge d'opérateurs, on est obligé de le définir en dehors de la classe, d'où problème.

    L'utilisation de friend en l’occurrence est une bonne solution, mais j'ai tendance à en utiliser une autre qui ne demande pas plus de travail, et qui a l'avantage de ne pas utiliser friend (avantage qui n'apporte rien sur le plan technique, mais permet d'éviter de longues discussions avec des gens persuadés que friend casse l'encapsulation alors qu'il fait le contraire).

    Je défini simplement une fonction publique display, qui prend un paramètre un ostream&, et j'utilise cette fonction publique dans mon opérateur << qui n'a alors plus besoin d'être friend.

    Cette solution possède l'autre avantage, technique celui-là, que cette fonction display peut être virtuelle, chose que ne peut pas l'opérateur<<.

    Comment surcharger correctement l'opérateur << pour afficher des objets polymorphes ?
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

  9. #9
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Citation Envoyé par JolyLoic Voir le message
    Le problème est que l'opérateur << fait logiquement partie de la classe
    Personnellement, je suis embêté de définir des éléments d'affichage dans une classe. Que cela soit à l'extérieur et utiliser l'amitié a souvent ma préférence. La fonction display permet effectivement l'utilisation avec de l'héritage comme le dit la F.A.Q. mais personnellement je ne trouve pas cela très satisfaisant. C'est un aspect indépendant de la classe. J'ai du mal à concevoir qu'une classe ait aussi la responsabilité de son affichage. Mais ce n'est que mon point de vue.

  10. #10
    Membre régulier
    Homme Profil pro
    Etudiant CNAM (DIE20)
    Inscrit en
    Janvier 2010
    Messages
    151
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Etudiant CNAM (DIE20)

    Informations forums :
    Inscription : Janvier 2010
    Messages : 151
    Points : 97
    Points
    97
    Par défaut
    Citation Envoyé par 3DArchi Voir le message
    Il manquerait pas quelque chose dans la défintion dans le .cpp
    Oups... Dire que j'ai retourné le truc dans tous les sens pendant une heure en étant certain d'avoir fait gaffe à tous les const
    C'est une réponse qu'on envoie souvent surtout vers les débutants car un premier (mauvais) réflexe lorsqu'on débute est d'ajouter systématiquement des accesseurs et des mutateurs vers les attributs d'une classe.
    J'utilisais un équivalent get(), et il est vrai que la 2nde solution adoptée ici me plait largement plus, car élégante à utiliser dans le main, en écrivant simplement le nom de mon objet après l'opérateur de flux sans recourir à ma méthode .str()

    La sortie d'un objet vers un flux est un cas particulier (qu'on appelle sérialisation). Il existe plusieurs façon de faire mais c'est un cas qui se prête assez bien à l'amitié :
    Oui, j'ai entendu parler de cette "amitié" dans un tuto, mais pas encore vu ce que ça représente. Je garde cette remarque en mémoire pour le moment.

    Deux petites remarques :
    ton destructeur est inutile. Un destructeur qui ne fait rien est souvent un indice qu'il ne sert à rien.
    C'est juste qu'il a été automatiquement créé par code::blocks et je n'ai pas pris la peine de l'enlever. Ça peut poser problème de le laisser tel quel, ou c'est simplement que ça encombre le code pour rien ?

    Monnaie(int livre = 0, int shilling = 0, int penny = 0); devrait être explicite : explicit Monnaie(int livre = 0, int shilling = 0, int penny = 0);. Cela évite des conversions implicites qui pourrait surprendre Monnaie m = 42; // Quelle signification ?
    Je vais creuser ça, c'est totalement nouveau pour moi...


    En tout cas, un grand pour cette superbe réponse détaillée et instructive.
    Je passe en "résolu".
    Bonne soirée !

  11. #11
    Membre régulier
    Homme Profil pro
    Etudiant CNAM (DIE20)
    Inscrit en
    Janvier 2010
    Messages
    151
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Etudiant CNAM (DIE20)

    Informations forums :
    Inscription : Janvier 2010
    Messages : 151
    Points : 97
    Points
    97
    Par défaut
    Citation Envoyé par JolyLoic Voir le message
    Je défini simplement une fonction publique display, qui prend un paramètre un ostream&, et j'utilise cette fonction publique dans mon opérateur << qui n'a alors plus besoin d'être friend.

    Cette solution possède l'autre avantage, technique celui-là, que cette fonction display peut être virtuelle, chose que ne peut pas l'opérateur<<.

    http://cpp.developpez.com/faq/cpp/?page=SL#SL_operateur_affichage_polymorphique
    Bonjour et merci pour ta réponse que je garde au chaud dans un coin avant de maitriser un peu mieux le sujet

    EDIT : maintenant, j'ai compris

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

Discussions similaires

  1. Problème avec la surcharge de l'opérateur >>
    Par phenix1988 dans le forum C++
    Réponses: 1
    Dernier message: 09/12/2011, 18h03
  2. [Débutant] probléme avec les surcharges de méthodes
    Par lila23 dans le forum C#
    Réponses: 5
    Dernier message: 30/06/2011, 15h44
  3. Réponses: 4
    Dernier message: 03/03/2008, 16h57
  4. Réponses: 6
    Dernier message: 12/07/2006, 15h34
  5. [Custom Tags] Problème avec une surcharge de méthode
    Par Strab dans le forum Taglibs
    Réponses: 19
    Dernier message: 26/08/2005, 16h34

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