Pergunta

Isso é basicamente a pergunta: existe uma maneira "certa" para implementar operator<<? Leitura este eu posso ver que algo como:

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

é o preferido para algo como

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

Mas eu não posso ver bem por que eu deveria usar um ou o outro.

O 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;
}

O raciocínio que eu deveria basear essa decisão em?

Nota :

 Paragraph::to_str = (return paragraph) 

onde parágrafo é uma string.

Foi útil?

Solução

O problema aqui está na sua interpretação do artigo que você ligação .

Este artigo é sobre alguém que está tendo problemas corretamente definir os operadores de relacionamento bool.

O operador:

  • A igualdade == e! =
  • Relacionamento <> <=> =

Estes operadores devem retornar um bool como eles estão comparando dois objetos do mesmo tipo. Geralmente é mais fácil de definir esses operadores, como parte da classe. Isso ocorre porque uma classe é automaticamente um amigo de si mesmo para que os objetos do tipo Parágrafo pode examinar o outro (mesmo uns dos outros membros privados).

Há um argumento para fazer essas funções de pé livre, pois isso permite a conversão automática converter ambos os lados se eles não são do mesmo tipo, enquanto que as funções de membro só permitem que os RHS para ser auto convertido. Acho esse argumento um homem de papel que você realmente não quer que conversão automática acontecer, em primeiro lugar (geralmente). Mas se isso é algo que você quer (eu não recomendo), em seguida, fazer a comparadores de pé livre pode ser vantajosa.

Os operadores de fluxo:

  • operador << saída
  • operador >> entrada

Quando você usá-los como operadores de fluxo (ao invés de mudança de binário) o primeiro parâmetro é um córrego. Desde que você não tem acesso ao objeto de fluxo (seus não o seu para modificar) estes não podem ser operadores membros que têm que ser externo à classe. Assim, eles devem ser ou amigos da classe ou ter acesso a um método público que irá fazer o streaming de para você.

Também é tradicional para esses objetos para retornar uma referência a um objeto de fluxo para que você possa operações de fluxo cadeia juntos.

#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;
}

Outras dicas

Você não pode fazê-lo como uma função membro, porque o parâmetro implícito this é o lado esquerdo da <<-operador. (Daí, você precisa adicioná-lo como uma função de membro para o ostream-class Não é bom:).

Você poderia fazê-lo como uma função livre sem friending isso? Isso é o que eu prefiro, porque torna claro que esta é uma integração com ostream, e não uma funcionalidade do núcleo de sua classe.

Se possível, como funções de terceiros e não-amigo.

Como descrito por Herb Sutter e Scott Meyers, preferem funções terceiros não-amigo para funções de membro, para ajudar a aumentar o encapsulamento.

Em alguns casos, como fluxos de C ++, você não terá a escolha e deve utilizar as funções de terceiros.

Mas ainda assim, isso não significa que você tem que fazer essas funções amigos de suas classes: Estas funções podem ainda acesso a sua classe através de seus assessores de classe. Se você conseguir escrevendo essas funções desta forma, então você ganhou.

Sobre operador << e >> protótipos

Eu acredito que os exemplos que você deu na sua pergunta está errado. Por exemplo;

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

Eu não posso nem começar a pensar como esse método poderia funcionar em um córrego.

Aqui estão as duas maneiras de implementar as << >> e operadores.

Digamos que você queira usar um fluxo semelhante objeto do tipo T.

E que deseja extrair / inserção de / em T os dados relevantes de seu objeto do tipo Parágrafo.

operador genérico << e >> função protótipos

O primeiro é como funções:

// 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 ;
}

operador genérico << e >> método de protótipos

O segundo ser como métodos:

// 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 ;
}

Note que para usar esta notação, você deve estender declaração de classe do T. Para objetos STL, isso não é possível (você não deve modificá-los ...).

E o que se T é um fluxo de C ++?

Aqui estão os protótipos do mesmo << e >> operadores de fluxos C ++.

Para basic_istream genéricos e basic_ostream

Note que é caso de fluxos, como você não pode modificar o fluxo de C ++, você deve implementar as funções. O que significa algo como:

// 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 ;
}

Para Char istream e ostream

O código a seguir irá funcionar apenas para fluxos à base de carvão de combustão.

// 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 comentou sobre o fato do código-base char é mas uma "especialização" do código genérico acima dela. Claro, Rhys é certa: eu não recomendo o uso do exemplo à base de carvão. Ele só é dado aqui, porque é mais fácil de ler. Como só é viável se você só trabalha com fluxos à base de carvão de combustão, você deve evitá-lo em plataformas onde o código wchar_t é comum (ou seja, no Windows).

Esperamos que isto ajude.

Deve ser implementado como um livre de funções, não amigo, especialmente se, como a maioria das coisas nos dias de hoje, a saída é usado principalmente para diagnóstico e registro. Adicionar acessores const por todas as coisas que precisam ir para a saída, e depois ter a outputter apenas chamar aqueles e não de formatação.

Eu realmente levado para coletar todas essas funções livres saída ostream em um arquivo "ostreamhelpers" cabeçalho e implementação, ele mantém essa funcionalidade secundário longe do verdadeiro propósito das classes.

A assinatura:

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

parece bastante suspeito, isso não cabe a convenção stream nem a convenção bit a bit de modo que parece um caso de abuso operador sobrecarga, operator < deve retornar bool mas operator << provavelmente deve retornar algo mais.

Se você significou dizer:

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

Em seguida, desde que você não pode adicionar funções ao ostream por necessidade a função deve ser uma função livre, quer se trate de um friend ou não depende do que ela tem para o acesso (se ele não precisa de acesso privado ou protegido membros não está não há necessidade de torná-lo amigo).

Apenas por causa de conclusão, gostaria de acrescentar que você de fato pode criar um ostream& operator << (ostream& os) operador dentro de uma classe e pode funcionar. Pelo que eu sei que não é uma boa idéia para usá-lo, porque é muito complicado e intuitiva.

Vamos supor que tenho este código:

#include <iostream>
#include <string>

using namespace std;

struct Widget
{
    string name;

    Widget(string _name) : name(_name) {}

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

int main()
{
    Widget w1("w1");
    Widget w2("w2");

    // These two won't work
    {
        // Error: operand types are std::ostream << std::ostream
        // cout << w1.operator<<(cout) << '\n';

        // Error: operand types are std::ostream << Widget
        // cout << w1 << '\n';
    }

    // However these two work
    {
        w1 << cout << '\n';

        // Call to w1.operator<<(cout) returns a reference to ostream&
        w2 << w1.operator<<(cout) << '\n';
    }

    return 0;
}

Assim, para resumir - você pode fazê-lo, mas você provavelmente não deve:)

operator<< implementado como uma função amigo:

#include <iostream>
#include <string>
using namespace std;

class Samp
{
public:
    int ID;
    string strName; 
    friend std::ostream& operator<<(std::ostream &os, const Samp& obj);
};
 std::ostream& operator<<(std::ostream &os, const Samp& obj)
    {
        os << obj.ID<< “ ” << obj.strName;
        return os;
    }

int main()
{
   Samp obj, obj1;
    obj.ID = 100;
    obj.strName = "Hello";
    obj1=obj;
    cout << obj <<endl<< obj1;

} 

SAÍDA: 100 Olá 100 Olá Pressione qualquer tecla para continuar ...

Esta pode ser uma função de amigo só porque o objeto está no lado direito da operator<< e argumento cout está no lado esquerdo. Portanto, este não pode ser uma função de membro para a classe, só pode ser uma função amigo.

amigo operador = direitos iguais como classe

friend std::ostream& operator<<(std::ostream& os, const Object& object) {
    os << object._atribute1 << " " << object._atribute2 << " " << atribute._atribute3 << std::endl;
    return os;
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top