Essa é basicamente a questão: existe uma maneira "correta" de implementar operator<<
? Lendo isso , vejo que algo como:
friend bool operator<<(obj const& lhs, obj const& rhs);
é preferível a algo como
ostream& operator<<(obj const& rhs);
Mas não consigo entender por que devo usar um ou outro.
Meu caso pessoal é:
friend ostream & operator<<(ostream &os, const Paragraph& p) {
return os << p.to_str();
}
Mas eu provavelmente poderia fazer:
ostream & operator<<(ostream &os) {
return os << paragraph;
}
Em que justificativa devo basear essa decisão?
Nota :
Paragraph::to_str = (return paragraph)
onde o parágrafo é uma string.
c++
operator-overloading
Federico Builes
fonte
fonte
Respostas:
O problema aqui está na sua interpretação do artigo que você vincula .
Igualdade
Este artigo é sobre alguém que está tendo problemas para definir corretamente os operadores de relacionamento bool.
O operador:
Esses operadores devem retornar um bool, pois estão comparando dois objetos do mesmo tipo. Geralmente é mais fácil definir esses operadores como parte da classe. Isso ocorre porque uma classe é automaticamente amiga de si mesma, portanto objetos do tipo Parágrafo podem se examinar (até mesmo os membros privados).
Há um argumento para tornar essas funções independentes, pois isso permite que a conversão automática converta ambos os lados se não forem do mesmo tipo, enquanto as funções de membro permitem apenas que os rhs sejam convertidos automaticamente. Acho isso um argumento do homem do papel, pois você realmente não quer que a conversão automática aconteça em primeiro lugar (geralmente). Mas se isso é algo que você deseja (eu não recomendo), tornar os comparadores independentes pode ser vantajoso.
Transmissão
Os operadores de fluxo:
Quando você os usa como operadores de fluxo (em vez de deslocamento binário), o primeiro parâmetro é um fluxo. Como você não tem acesso ao objeto de fluxo (não é seu para modificar), eles não podem ser operadores membros; eles precisam ser externos à classe. Portanto, eles devem ser amigos da classe ou ter acesso a um método público que fará o streaming para você.
Também é tradicional que esses objetos retornem uma referência a um objeto de fluxo para que você possa encadear operações de fluxo juntos.
fonte
operator<<
private:
?freiend
é uma maneira de estender a interface pública sem interromper o encapsulamento. Leia programmers.stackexchange.com/a/99595/12917Você não pode fazer isso como uma função de membro, porque o
this
parâmetro implícito é o lado esquerdo do<<
operador. (Portanto, você precisaria adicioná-lo como uma função de membro àostream
classe-. Não é bom :)Você poderia fazê-lo como uma função livre sem
friend
usá-lo? É isso que eu prefiro, porque deixa claro que essa é uma integraçãoostream
e não uma funcionalidade principal da sua classe.fonte
friend
função tem os mesmos direitos que uma função membro ( é isso quefriend
significa); portanto, como usuário da classe, eu teria que me perguntar por que precisaria disso. Esta é a distinção que estou tentando fazer com a expressão "funcionalidade principal".Se possível, como funções de não membro e não amigo.
Conforme descrito por Herb Sutter e Scott Meyers, preferem funções não pertencentes a não amigas a funções membro, para ajudar a aumentar o encapsulamento.
Em alguns casos, como fluxos C ++, você não terá a opção e deve usar funções que não são membros.
Mas, ainda assim, isso não significa que você precise tornar essas funções amigas de suas classes: Essas funções ainda podem acessar sua classe por meio de seus acessadores de classe. Se você conseguir escrever essas funções dessa maneira, você venceu.
Sobre os protótipos << e >> do operador
Eu acredito que os exemplos que você deu na sua pergunta estão errados. Por exemplo;
Não consigo nem pensar em como esse método poderia funcionar em um fluxo.
Aqui estão as duas maneiras de implementar os operadores << e >>.
Digamos que você queira usar um objeto semelhante a um fluxo do tipo T.
E que você deseja extrair / inserir de / em T os dados relevantes do seu objeto do tipo Parágrafo.
Operador genérico << e >> protótipos de função
O primeiro sendo como funções:
Operador genérico << e >> protótipos de método
O segundo sendo como métodos:
Observe que, para usar essa notação, você deve estender a declaração de classe de T. Para objetos STL, isso não é possível (você não deve modificá-los ...).
E se T for um fluxo C ++?
Aqui estão os protótipos dos mesmos operadores << e >> para fluxos C ++.
Para basic_istream e basic_ostream genéricos
Observe que é o caso de fluxos, como você não pode modificar o fluxo C ++, você deve implementar as funções. O que significa algo como:
Para char istream e ostream
O código a seguir funcionará apenas para fluxos baseados em char.
Rhys Ulerich comentou sobre o fato de o código baseado em char ser apenas uma "especialização" do código genérico acima dele. Obviamente, Rhys está certo: eu não recomendo o uso do exemplo baseado em char. É fornecido apenas aqui porque é mais simples de ler. Como só é viável se você trabalhar apenas com fluxos baseados em char, evite-o em plataformas onde o código wchar_t é comum (por exemplo, no Windows).
Espero que isso ajude.
fonte
Ele deve ser implementado como uma função gratuita e não amiga, especialmente se, como a maioria das coisas hoje em dia, a saída for usada principalmente para diagnóstico e registro. Adicione acessadores const para todas as coisas que precisam ser inseridas na saída e faça com que o emissor chame essas informações e faça a formatação.
Eu realmente comecei a coletar todas essas funções livres de saída do ostream em um cabeçalho "ostreamhelpers" e arquivo de implementação, mantém essa funcionalidade secundária longe do objetivo real das classes.
fonte
A assinatura:
Parece bastante suspeito, isso não se encaixa na
stream
convenção nem na convenção bit a bit; portanto, parece um caso de abuso de sobrecarga do operador,operator <
deve retornar,bool
masoperator <<
provavelmente deve retornar outra coisa.Se você quis dizer, diga:
Então, como você não pode adicionar funções,
ostream
por necessidade, a função deve ser uma função livre,friend
dependendo se depende ou não do que deve ser acessado (se não for necessário acessar membros privados ou protegidos, não há necessidade de fazê-lo). amigo).fonte
ostream
seria necessário ao usar oostream.operator<<(obj&)
pedido; daí a função livre. Caso contrário, o tipo de usuário precisará ser do tipo vapor para acomodar o acesso.Apenas para concluir, gostaria de acrescentar que você realmente pode criar um operador
ostream& operator << (ostream& os)
dentro de uma classe e ele pode funcionar. Pelo que sei, não é uma boa ideia usá-lo, porque é muito complicado e pouco intuitivo.Vamos supor que temos este código:
Então, para resumir - você pode fazê-lo, mas provavelmente não deveria :)
fonte
operador amigo = direitos iguais à classe
fonte
operator<<
implementado como uma função de amigo:Isso pode ser uma função de amigo apenas porque o objeto está no lado direito
operator<<
e o argumentocout
está no lado esquerdo. Portanto, essa não pode ser uma função de membro da classe, apenas uma função de amigo.fonte