157 votes

Si l’opérateur << être mis en œuvre comme ami ou comme fonction membre ?

C'est un peu la question, est-il une "bonne" façon de implemente l'opérateur<< ? La lecture de ce que je peux voir que quelque chose comme:

friend bool operator<<(obj const& lhs, obj const& rhs);

Est préférée à quelque chose comme

ostream& operator<<(obj const& rhs);

Mais je n'arrive pas à voir pourquoi devrais-je utiliser l'un ou l'autre.

Mon cas personnel est:

friend ostream & operator<<(ostream &os, const Paragraph& p) {
    return os << p.to_str();
}

Mais je pourrais probablement le faire:

ostream & operator<<(ostream &os) {
    return os << paragraph;
}

Ce raisonnement je devrais la base de cette décision?

Note:

 Paragraph::to_str = (return paragraph)

les cas où le paragraphe est une chaîne de caractères.

141voto

Loki Astari Points 116129

Ici, le problème est dans votre interprétation de l'article que vous lien.

Cet article est à propos de quelqu'un qui est d'avoir des problèmes de définir correctement le bool relation des opérateurs.

L'opérateur:

  • L'égalité == et !=
  • Relation < > <= >=

Ces opérateurs doivent retourner un booléen comme ils sont de la comparaison de deux objets de même type. Il est habituellement plus facile de définir ces opérateurs dans le cadre de la classe. C'est parce qu'une classe est automatiquement un ami de lui-même afin que les objets de type Point peut examiner les uns des autres (même les uns les autres membres privés).

Il y a un argument pour rendre ces debout libre fonctionne comme ceci permet à l'auto de conversion de convertir les deux parties si elles ne sont pas du même type, tandis que les fonctions de membre d'autoriser uniquement les rhs pour être automatiquement convertis. Je trouve que c'est un document de l'homme argument que vous n'avez pas vraiment envie de conversion automatique qui se passe en premier lieu (en général). Mais si c'est quelque chose que vous voulez (je ne le recommande pas) pour ensuite faire les comparateurs de standing libre peut être avantageux.

Le flux des opérateurs:

  • l'opérateur << de sortie
  • l'opérateur >> entrée

Lorsque vous utilisez ces flux opérateurs (plutôt que binaire maj) le premier paramètre est un flux. Puisque vous n'avez pas accès à l'objet de flux de données (ce n'est pas la vôtre à modifier) ceux-ci peuvent ne pas être membre des opérateurs qu'ils ont à l'extérieur de la classe. Par conséquent, ils doivent être amis de la classe ou avoir accès à une méthode publique qui fera le streaming pour vous.

Il est également d'usage de ces objets pour renvoyer une référence à un objet de flux de données de sorte que vous pouvez enchaîner les flux de l'ensemble des opérations.

#include <iostream>

class Paragraph
{
    public:
        explicit Paragraph(std::string const& init)
            :m_para(init)
        {}

        std::string const&  to_str() const
        {
            return m_para;
        }

        bool operator==(Paragraph const& rhs) const
        {
            return m_para == rhs.m_para;
        }
        bool operator!=(Paragraph const& rhs) const
        {
            // Define != operator in terms of the == operator
            return !(this->operator==(rhs));
        }
        bool operator<(Paragraph const& rhs) const
        {
            return  m_para < rhs.m_para;
        }
    private:
        friend std::ostream & operator<<(std::ostream &os, const Paragraph& p);
        std::string     m_para;
};

std::ostream & operator<<(std::ostream &os, const Paragraph& p)
{
    return os << p.to_str();
}


int main()
{
    Paragraph   p("Plop");
    Paragraph   q(p);

    std::cout << p << std::endl << (p == q) << std::endl;
}

58voto

Magnus Hoff Points 12052

Vous pouvez le faire pas comme fonction membre, parce que l’implicite paramètre est le côté gauche de la -opérateur. (Par conséquent, vous devez l’ajouter comme fonction membre de la `` -classe. Pas bon  :)

Vous pourriez le faire comme une fonction libre sans ing il ? C’est ce que je préfère, car il établit clairement qu’il s’agit d’une intégration avec et pas une fonctionnalité de base de votre classe.

32voto

paercebal Points 38526

Si possible, en tant que non-membre et non-amis fonctions de.

Comme décrit par Herb Sutter et Scott Meyers, préférez les non-ami non membre de fonctions de fonctions membres, pour aider à augmenter l'encapsulation.

Dans certains cas, comme en C++ flux, vous n'aurez pas le choix et doit utiliser des fonctions membres.

Mais encore, cela ne signifie pas que vous avez à faire ces fonctions les amis de vos classes: Ces fonctions peuvent toujours accéder à votre classe par le biais de votre classe accesseurs. Si vous réussissez dans l'écriture de ces fonctions de cette façon, alors vous avez gagné.

À propos de l'opérateur << et >> les prototypes

Je crois que les exemples que vous citez dans votre question sont mauvais. Par exemple;

ostream & operator<<(ostream &os) {
    return os << paragraph;
}

Je ne peux même pas commencer à penser à la façon dont cette méthode pourrait fonctionner dans un ruisseau.

Voici les deux façons de mettre en œuvre les << et >> les opérateurs.

Disons que vous voulez utiliser un flux-comme objet de type T.

Et que vous voulez extraire d'insertion/de/en T les données pertinentes de votre objet de type Point.

Générique de l'opérateur << et >> les prototypes de fonction

La première étant que les fonctions de:

// T << Paragraph
T & operator << (T & p_oOutputStream, const Paragraph & p_oParagraph)
{
   // do the insertion of p_oParagraph
   return p_oOutputStream ;
}

// T >> Paragraph
T & operator >> (T & p_oInputStream, const Paragraph & p_oParagraph)
{
   // do the extraction of p_oParagraph
   return p_oInputStream ;
}

Générique de l'opérateur << et >> méthode de prototypes

La seconde étant que les méthodes:

// T << Paragraph
T & T::operator << (const Paragraph & p_oParagraph)
{
   // do the insertion of p_oParagraph
   return *this ;
}

// T >> Paragraph
T & T::operator >> (const Paragraph & p_oParagraph)
{
   // do the extraction of p_oParagraph
   return *this ;
}

Notez que pour utiliser cette notation, vous devez étendre T de la déclaration de classe. Pour STL objets, ce n'est pas possible (vous n'êtes pas censé modifier...).

Et si T est un C++ stream?

Voici les prototypes de la même << et >> les opérateurs en C++ ruisseaux.

Pour générique basic_istream et basic_ostream

Notez que c'est le cas de cours d'eau, que vous ne pouvez pas modifier le C++ flux, vous devez implémenter les fonctions. Ce qui signifie quelque chose comme:

// OUTPUT << Paragraph
template <typename charT, typename traits>
std::basic_ostream<charT,traits> & operator << (std::basic_ostream<charT,traits> & p_oOutputStream, const Paragraph & p_oParagraph)
{
   // do the insertion of p_oParagraph
   return p_oOutputStream ;
}

// INPUT >> Paragraph
template <typename charT, typename traits>
std::basic_istream<charT,traits> & operator >> (std::basic_istream<charT,traits> & p_oInputStream, const CMyObject & p_oParagraph)
{
   // do the extract of p_oParagraph
   return p_oInputStream ;
}

Pour char istream et ostream

Le code suivant ne fonctionne que pour le char à base de flux.

// OUTPUT << A
std::ostream & operator << (std::ostream & p_oOutputStream, const Paragraph & p_oParagraph)
{
   // do the insertion of p_oParagraph
   return p_oOutputStream ;
}

// INPUT >> A
std::istream & operator >> (std::istream & p_oInputStream, const Paragraph & p_oParagraph)
{
   // do the extract of p_oParagraph
   return p_oInputStream ;
}

Rhys Ulerich a commenté sur le fait que le char à base de code mais une "spécialisation" de l'génériques de code ci-dessus. Bien sûr, Rhys est juste: je ne recommande pas l'utilisation de la char-base exemple. Il n'est donné ici parce que c'est plus simple à lire. Comme il n'est viable seulement si vous travaillez avec char à base de flux, vous devriez éviter sur les plates-formes où wchar_t code commun (c'est à dire sur Windows).

Espérons que cela aidera.

11voto

XPav Points 802

Il devrait être mis en œuvre en tant que libre, non-ami de fonctions, surtout si, comme la plupart des choses ces jours-ci, la sortie est principalement utilisé pour le diagnostic et la journalisation. Ajouter const accesseurs pour toutes les choses qui doivent aller dans la sortie, et alors le outputter suffit d'appeler ceux-ci et de faire la mise en forme.

En fait, j'ai prises pour la collecte de tous ces ostream sortie libre des fonctions dans une "ostreamhelpers" en-tête et de la mise en œuvre de fichier, il maintient que secondaire fonctionnalité loin de l'objectif réel de la classe.

9voto

Motti Points 32921

La signature:

bool operator<<(const obj&, const obj&);

Semble assez suspect, cela ne correspond pas à l' stream convention, ni le bit à bit de la convention, de sorte qu'il ressemble à un cas de surcharge d'opérateur abus, operator < doit renvoyer bool mais operator << devrait probablement revenir à quelque chose d'autre.

Si vous avez voulu dire:

ostream& operator<<(ostream&, const obj&);

Alors, puisque vous ne pouvez pas ajouter des fonctions à l' ostream par la nécessité de la fonction doit être une fonction libre, qu'il s' friend ou pas ne dépend pas de ce qu'il a à l'accès (si il n'est pas nécessaire pour accéder aux membres privés ou protégés il n'y a pas besoin d'en faire un ami).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X