web-dev-qa-db-fra.com

fonctions 'ami' et << surcharge d'opérateur: Quelle est la bonne façon de surcharger un opérateur pour une classe?

Dans un projet sur lequel je travaille, j'ai une classe Score, définie ci-dessous dans score.h. J'essaye de le surcharger donc, quand une opération << Est effectuée dessus, _points + " " + _name Est imprimé.

Voici ce que j'ai essayé de faire:

ostream & Score::operator<< (ostream & os, Score right)
{
    os << right.getPoints() << " " << right.scoreGetName();
    return os;
}

Voici les erreurs renvoyées:

score.h(30) : error C2804: binary 'operator <<' has too many parameters

(Cette erreur apparaît 4 fois, en fait)

J'ai réussi à le faire fonctionner en déclarant la surcharge comme une fonction amie:

friend ostream & operator<< (ostream & os, Score right);

Et supprimer le Score:: De la déclaration de fonction dans score.cpp (en fait, ne pas le déclarer en tant que membre).

Pourquoi cela fonctionne-t-il, mais pas l'ancien code?

Merci pour votre temps!

MODIFIER

J'ai supprimé toutes les mentions de la surcharge sur le fichier d'en-tête ... mais j'obtiens l'erreur suivante (et unique). binary '<<' : no operator found which takes a right-hand operand of type 'Score' (or there is no acceptable conversion) Comment se fait-il que mon test, dans main (), ne trouve pas la surcharge appropriée? (ce n'est pas les inclus, j'ai vérifié)

Ci-dessous la partition complète.

#ifndef SCORE_H_
#define SCORE_H_

#include <string>
#include <iostream>
#include <iostream>

using std::string;
using std::ostream;

class Score
{

public:
    Score(string name);
    Score();
    virtual ~Score();
    void addPoints(int n);
    string scoreGetName() const;
    int getPoints() const;
    void scoreSetName(string name);
    bool operator>(const Score right) const;

private:
    string _name;
    int _points;

};
#endif
25
F. P.

Remarque: Vous voudrez peut-être consulter la FAQ sur la surcharge de l'opérateur .


Les opérateurs binaires peuvent être membres de la classe de leur argument de gauche ou des fonctions libres. (Certains opérateurs, comme l'affectation, doivent être membres.) Étant donné que l'argument de gauche des opérateurs de flux est un flux, les opérateurs de flux doivent être membres de la classe de flux ou des fonctions libres. La manière canonique d'implémenter operator<< pour tout type:

std::ostream& operator<<(std::ostream& os, const T& obj)
{
   // stream obj's data into os
   return os;
}

Notez que c'est pas une fonction membre. Notez également qu'il faut l'objet à diffuser par const référence. C'est parce que vous ne voulez pas copier l'objet afin de le diffuser et que vous ne voulez pas non plus que le streaming le modifie.


Parfois, vous souhaitez diffuser des objets dont les éléments internes ne sont pas accessibles via l'interface publique de leur classe, afin que l'opérateur ne puisse pas y accéder. Ensuite, vous avez deux choix: soit mettre un membre public dans la classe qui fait le streaming

class T {
  public:
    void stream_to(std::ostream&) const {os << obj.data_;}
  private:
    int data_;
};

et appelez ça depuis l'opérateur:

inline std::ostream& operator<<(std::ostream& os, const T& obj)
{
   obj.stream_to(os);
   return os;
}

ou faites de l'opérateur un friend

class T {
  public:
    friend std::ostream& operator<<(std::ostream&, const T&);
  private:
    int data_;
};

afin qu'il puisse accéder aux parties privées de la classe:

inline std::ostream& operator<<(std::ostream& os, const T& obj)
{
   os << obj.data_;
   return os;
}
65
sbi

Supposons que vous vouliez écrire une surcharge d'opérateur pour + pour pouvoir ajouter deux objets Score et un autre pour ajouter un int à un Score et un troisième pour ajouter un Score en int. Celles où Score est le premier paramètre peuvent être des fonctions membres de Score. Mais celui où un int est le premier paramètre ne peut pas devenir des fonctions membres de int, non? Pour vous aider, vous pouvez les écrire en tant que fonctions libres. C'est ce qui se passe avec ce <<, vous ne pouvez pas ajouter de fonction membre à ostream, vous écrivez donc une fonction libre. Voilà ce que cela signifie lorsque vous enlevez le Score:: partie.

Maintenant, pourquoi faut-il que ce soit un friend? Ce n'est pas le cas. Vous appelez uniquement des méthodes publiques (getPoints et scoreGetName). Vous voyez beaucoup d'opérateurs amis car ils aiment parler directement aux variables privées. C'est ok pour moi de faire ça, car ils sont écrits et maintenus par la personne qui maintient la classe. Ne vous embrouillez pas avec la partie membre-fonction-vs-fonction libre.

9
Kate Gregory

Vous obtenez des erreurs de compilation lorsque operator<< est une fonction membre dans l'exemple, car vous créez un operator<< qui prend un Score comme premier paramètre (l'objet auquel la méthode est appelée), puis en lui donnant un paramètre supplémentaire à la fin.

Lorsque vous appelez un opérateur binaire déclaré en tant que fonction membre, le côté gauche de l'expression est l'objet auquel la méthode est appelée. par exemple. a + b pourrait fonctionner comme ceci:

A a;
B b

a.operator+(b)

Il est généralement préférable d'utiliser des opérateurs binaires non membres (et dans certains cas, par exemple operator<<pour ostream est la seule façon de le faire. Dans ce cas, a + b pourrait fonctionner comme ceci:

A a;
B b

operator+(a, b);

Voici un exemple complet montrant les deux façons de le faire; main () affichera '55' trois fois:

#include <iostream>

struct B
{
    B(int b) : value(b) {}
    int value;
};


struct A
{
    A(int a) : value(a) {}
    int value;

    int operator+(const B& b) 
    {
        return this->value + b.value;
    }
};

int operator+(const A& a, const B& b)
{
    return a.value + b.value;
}

int main(int argc, char** argv)
{
    A a(22);
    B b(33);

    std::cout << a + b << std::endl;
    std::cout << operator+(a, b) << std::endl;
    std::cout << a.operator+(b) << std::endl;

    return 0;
}
6
pkh