C'est essentiellement la question, y a-t-il une «bonne» façon de mettre en œuvre operator<<
? En lisant ceci, je peux voir que quelque chose comme:
friend bool operator<<(obj const& lhs, obj const& rhs);
est préféré à quelque chose comme
ostream& operator<<(obj const& rhs);
Mais je ne vois pas vraiment 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 faire:
ostream & operator<<(ostream &os) {
return os << paragraph;
}
Sur quelle justification dois-je fonder cette décision?
Remarque :
Paragraph::to_str = (return paragraph)
où paragraphe est une chaîne.
c++
operator-overloading
Federico Builes
la source
la source
Réponses:
Le problème ici est dans votre interprétation de l'article que vous liez .
Égalité
Cet article concerne une personne qui rencontre des problèmes pour définir correctement les opérateurs de relation booléenne.
L'opérateur:
Ces opérateurs doivent renvoyer un booléen car ils comparent deux objets du même type. Il est généralement plus simple de définir ces opérateurs dans le cadre de la classe. En effet, une classe est automatiquement un ami d'elle-même, de sorte que les objets de type Paragraph peuvent s'examiner les uns les autres (même les membres privés).
Il existe un argument en faveur de la création de ces fonctions autonomes car cela permet à la conversion automatique de convertir les deux côtés s'ils ne sont pas du même type, tandis que les fonctions membres autorisent uniquement la conversion automatique des rhs. Je trouve que c'est un argument d'homme de papier car vous ne voulez pas vraiment que la conversion automatique se produise en premier lieu (généralement). Mais si c'est quelque chose que vous voulez (je ne le recommande pas), rendre les comparateurs autonomes peut être avantageux.
Diffusion
Les opérateurs de flux:
Lorsque vous les utilisez comme opérateurs de flux (plutôt que comme décalage binaire), le premier paramètre est un flux. Puisque vous n'avez pas accès à l'objet de flux (ce n'est pas à vous de le modifier), ceux-ci ne peuvent pas être des opérateurs membres, ils doivent être externes à la classe. Ainsi, ils doivent soit être des amis de la classe, soit avoir accès à une méthode publique qui fera le streaming pour vous.
Il est également traditionnel que ces objets renvoient une référence à un objet de flux afin que vous puissiez enchaîner les opérations de flux.
la source
operator<<
private:
?freiend
est un moyen d'étendre l'interface publique sans interrompre l'encapsulation. Lire programmers.stackexchange.com/a/99595/12917Vous ne pouvez pas le faire en tant que fonction membre, car le
this
paramètre implicite est le côté gauche de l'<<
opérateur. (Par conséquent, vous devrez l'ajouter en tant que fonction membre à laostream
classe -class. Pas bon :)Pourriez-vous le faire en tant que fonction gratuite sans
friend
cela? C'est ce que je préfère, car cela montre clairement qu'il s'agit d'une intégration avecostream
, et non d'une fonctionnalité de base de votre classe.la source
friend
fonction a les mêmes droits en tant que fonction membre ( c'est ce que signifie), donc en tant qu'utilisateur de la classe, je me demande pourquoi il faudrait que. C'est la distinction que j'essaie de faire avec le libellé «fonctionnalité de base».friend
Si possible, en tant que fonctions non-membres et non-amis.
Comme décrit par Herb Sutter et Scott Meyers, préférez les fonctions non-membres non-amis aux fonctions membres, pour aider à augmenter l'encapsulation.
Dans certains cas, comme les flux C ++, vous n'aurez pas le choix et devez utiliser des fonctions non membres.
Mais encore, cela ne signifie pas que vous devez rendre ces fonctions amis de vos classes: ces fonctions peuvent toujours accéder à votre classe via vos accesseurs de classe. Si vous réussissez à écrire ces fonctions de cette façon, vous avez gagné.
À propos des prototypes d'opérateur << et >>
Je pense que les exemples que vous avez donnés dans votre question sont faux. Par exemple;
Je ne peux même pas commencer à penser comment cette méthode pourrait fonctionner dans un flux.
Voici les deux manières d'implémenter les opérateurs << et >>.
Supposons que vous souhaitiez utiliser un objet semblable à un flux de type T.
Et que vous souhaitez extraire / insérer de / dans T les données pertinentes de votre objet de type Paragraphe.
Prototypes de fonctions d'opérateurs génériques << et >>
Le premier étant en tant que fonctions:
Prototypes de méthodes d'opérateurs génériques << et >>
Le second étant comme méthodes:
Notez que pour utiliser cette notation, vous devez étendre la déclaration de classe de T. Pour les objets STL, ce n'est pas possible (vous n'êtes pas censé les modifier ...).
Et si T est un flux C ++?
Voici les prototypes des mêmes opérateurs << et >> pour les flux C ++.
Pour basic_istream et basic_ostream génériques
Notez que c'est le cas des flux, comme vous ne pouvez pas modifier le flux C ++, vous devez implémenter les fonctions. Ce qui veut dire quelque chose comme:
Pour char istream et ostream
Le code suivant fonctionnera uniquement pour les flux basés sur des caractères.
Rhys Ulerich a commenté le fait que le code à base de caractères n'est qu'une "spécialisation" du code générique au-dessus. Bien sûr, Rhys a raison: je ne recommande pas l'utilisation de l'exemple basé sur char. Il n'est donné ici que parce qu'il est plus simple à lire. Comme il n'est viable que si vous travaillez uniquement avec des flux basés sur char, vous devez l'éviter sur les plates-formes où le code wchar_t est courant (c'est-à-dire sous Windows).
J'espère que cela aidera.
la source
Il devrait être implémenté en tant que fonctions gratuites et non-amis, surtout si, comme la plupart des choses de nos jours, la sortie est principalement utilisée pour les diagnostics et la journalisation. Ajoutez des accesseurs const pour toutes les choses qui doivent entrer dans la sortie, puis demandez à l'émetteur de les appeler et de faire le formatage.
En fait, j'ai commencé à collecter toutes ces fonctions libres de sortie ostream dans un en-tête et un fichier d'implémentation "ostreamhelpers", cela éloigne cette fonctionnalité secondaire de l'objectif réel des classes.
la source
La signature:
Cela semble plutôt suspect, cela ne correspond pas à la
stream
convention ni à la convention au niveau du bit, donc cela ressemble à un cas d'abus de surcharge d'opérateur,operator <
devrait revenirbool
maisoperator <<
devrait probablement renvoyer autre chose.Si vous vouliez dire ainsi, dites:
Ensuite, comme vous ne pouvez pas ajouter de fonctions à
ostream
par nécessité, la fonction doit être une fonction libre, que celafriend
dépende ou non de ce à quoi elle doit accéder (si elle n'a pas besoin d'accéder à des membres privés ou protégés, il n'est pas nécessaire de la créer ami).la source
ostream
serait requis lors de l'utilisation de laostream.operator<<(obj&)
commande; d'où la fonction libre. Sinon, le type d'utilisateur doit être un type à vapeur pour permettre l'accès.Juste pour compléter, j'aimerais ajouter que vous pouvez en effet créer un opérateur à l'
ostream& operator << (ostream& os)
intérieur d'une classe et que cela peut fonctionner. D'après ce que je sais, ce n'est pas une bonne idée de l'utiliser, car il est très compliqué et peu intuitif.Supposons que nous ayons ce code:
Donc, pour résumer, vous pouvez le faire, mais vous ne devriez probablement pas :)
la source
opérateur ami = droits égaux en tant que classe
la source
operator<<
implémenté comme une fonction ami:Cela peut être une fonction d'ami uniquement parce que l'objet est sur le côté droit de
operator<<
et l'argumentcout
est sur le côté gauche. Cela ne peut donc pas être une fonction membre de la classe, cela ne peut être qu'une fonction amie.la source