Esa es básicamente la pregunta, ¿hay una forma "correcta" de implementar operator<<
? Al leer esto , puedo ver que algo así como:
friend bool operator<<(obj const& lhs, obj const& rhs);
se prefiere a algo como
ostream& operator<<(obj const& rhs);
Pero no puedo entender por qué debería usar uno u otro.
Mi caso personal es:
friend ostream & operator<<(ostream &os, const Paragraph& p) {
return os << p.to_str();
}
Pero probablemente podría hacer:
ostream & operator<<(ostream &os) {
return os << paragraph;
}
¿En qué razón debo basar esta decisión?
Nota :
Paragraph::to_str = (return paragraph)
donde el párrafo es una cadena
c++
operator-overloading
Federico Builes
fuente
fuente
Respuestas:
El problema aquí está en su interpretación del artículo que vincula .
Igualdad
Este artículo trata sobre alguien que tiene problemas para definir correctamente los operadores de relación bool.
El operador:
Estos operadores deberían devolver un bool ya que están comparando dos objetos del mismo tipo. Por lo general, es más fácil definir estos operadores como parte de la clase. Esto se debe a que una clase es automáticamente amiga de sí misma, por lo que los objetos de tipo Párrafo pueden examinarse entre sí (incluso entre los miembros privados).
Existe un argumento para realizar estas funciones independientes, ya que esto permite que la conversión automática convierta ambos lados si no son del mismo tipo, mientras que las funciones miembro solo permiten que los rhs se conviertan automáticamente. Creo que este es un argumento de hombre de papel, ya que en realidad no desea que la conversión automática ocurra en primer lugar (generalmente). Pero si esto es algo que desea (no lo recomiendo), hacer que los comparadores estén libres puede ser ventajoso.
Transmisión
Los operadores de flujo:
Cuando los utiliza como operadores de flujo (en lugar de desplazamiento binario), el primer parámetro es un flujo. Como no tiene acceso al objeto de flujo (no es suyo modificarlo), estos no pueden ser operadores miembros, deben ser externos a la clase. Por lo tanto, deben ser amigos de la clase o tener acceso a un método público que haga la transmisión por usted.
También es tradicional que estos objetos devuelvan una referencia a un objeto de flujo para que pueda encadenar las operaciones de flujo juntas.
fuente
operator<<
private:
?freiend
es una forma de extender la interfaz pública sin romper la encapsulación. Lea programmers.stackexchange.com/a/99595/12917No puede hacerlo como una función miembro, porque el
this
parámetro implícito es el lado izquierdo del<<
operador. (Por lo tanto, necesitaría agregarlo como una función miembro a laostream
clase. No es bueno :)¿Podrías hacerlo como una función libre sin
friend
usarlo? Eso es lo que prefiero, porque deja en claro que se trata de una integraciónostream
y no una funcionalidad central de su clase.fuente
friend
función tiene los mismos derechos que una función miembro ( esto es lo quefriend
significa), por lo que como usuario de la clase, tendría que preguntarme por qué necesitaría eso. Esta es la distinción que estoy tratando de hacer con la frase "funcionalidad principal".Si es posible, como no miembro y no amigo funciones.
Como lo describieron Herb Sutter y Scott Meyers, prefieren las funciones no miembros no amigos a las funciones miembros, para ayudar a aumentar la encapsulación.
En algunos casos, como las transmisiones de C ++, no tendrá la opción y deberá usar funciones que no sean miembros.
Pero aún así, no significa que tenga que hacer que estas funciones sean amigas de sus clases: estas funciones aún pueden acceder a su clase a través de sus accesos de clase. Si logra escribir esas funciones de esta manera, entonces ganó.
Sobre el operador << y >> prototipos
Creo que los ejemplos que dio en su pregunta están equivocados. Por ejemplo;
Ni siquiera puedo empezar a pensar cómo podría funcionar este método en una secuencia.
Estas son las dos formas de implementar los operadores << y >>.
Digamos que desea utilizar un objeto similar a una secuencia del tipo T.
Y que desea extraer / insertar de / en T los datos relevantes de su objeto de tipo Párrafo.
Operador genérico << y >> prototipos de funciones
El primer ser como funciones:
Operador genérico << y >> prototipos de métodos
El segundo ser como métodos:
Tenga en cuenta que para usar esta notación, debe extender la declaración de clase de T. Para los objetos STL, esto no es posible (se supone que no debe modificarlos ...).
¿Y si T es un flujo de C ++?
Aquí están los prototipos de los mismos operadores << y >> para flujos C ++.
Para genérico basic_istream y basic_ostream
Tenga en cuenta que es el caso de las secuencias, ya que no puede modificar la secuencia de C ++, debe implementar las funciones. Lo que significa algo como:
Para char istream y ostream
El siguiente código funcionará solo para transmisiones basadas en caracteres.
Rhys Ulerich comentó sobre el hecho de que el código basado en caracteres no es más que una "especialización" del código genérico sobre él. Por supuesto, Rhys tiene razón: no recomiendo el uso del ejemplo basado en char. Solo se da aquí porque es más fácil de leer. Como solo es viable si solo trabaja con secuencias basadas en char, debe evitarlo en plataformas donde el código wchar_t es común (es decir, en Windows).
Espero que esto ayude.
fuente
Debe implementarse como una función gratuita y no amiga, especialmente si, como la mayoría de las cosas en estos días, la salida se utiliza principalmente para el diagnóstico y el registro. Agregue accesos constantes para todas las cosas que necesitan ir a la salida, y luego haga que el emisor solo llame a esos y realice el formateo.
De hecho, he decidido recopilar todas estas funciones libres de salida de ostream en un encabezado "ostreamhelpers" y un archivo de implementación, mantiene esa funcionalidad secundaria lejos del propósito real de las clases.
fuente
La firma:
Parece bastante sospechoso, esto no se ajusta a la
stream
convención ni a la convención bit a bit, por lo que parece un caso de abuso de sobrecarga del operador,operator <
debería regresarbool
perooperator <<
probablemente debería devolver algo más.Si quisiste decir eso, di:
Entonces, dado que no puede agregar funciones
ostream
necesariamente, la función debe ser una función libre, ya sea quefriend
dependa o no de a qué tiene acceso (si no necesita acceder a miembros privados o protegidos, no es necesario hacerlo) amigo).fuente
ostream
que se requeriría acceso para modificar cuando se utiliza elostream.operator<<(obj&)
pedido; De ahí la función libre. De lo contrario, el tipo de usuario debe ser un tipo de vapor para acomodar el acceso.Solo por completar, me gustaría agregar que de hecho puedes crear un operador
ostream& operator << (ostream& os)
dentro de una clase y puede funcionar. Por lo que sé, no es una buena idea usarlo, porque es muy complicado y poco intuitivo.Supongamos que tenemos este código:
En resumen, puedes hacerlo, pero probablemente no deberías :)
fuente
operador amigo = igualdad de derechos como clase
fuente
operator<<
implementado como una función amiga:Esta puede ser una función amiga solo porque el objeto está en el lado derecho
operator<<
y el argumentocout
está en el lado izquierdo. Entonces, esta no puede ser una función miembro de la clase, solo puede ser una función amiga.fuente