manipuladores libreria ejemplos descargar c++ iostream ostream

ejemplos - libreria iostream c++ descargar



¿Cómo funcionan los manipuladores de flujo? (3)

Es bien sabido que el usuario puede definir manipuladores de flujo como este:

ostream& tab(ostream & output) { return output<< ''/t''; }

Y esto se puede usar en main () así:

cout<<''a''<<tab<<''b''<<''c''<<endl;

Por favor, explícame, ¿cómo funciona todo esto? Si el operador << asume como segundo parámetro un puntero a la función que toma y devuelve ostream & , entonces explique por qué es necesario? ¿Qué sería incorrecto si la función no toma y devuelve ostream & pero fue nula en lugar de ostream & ?

¿También es interesante el motivo por el cual los manipuladores "dec", "hex" entran en vigencia hasta que no los cambio, pero los manipuladores definidos por el usuario siempre deben usarse para que surta efecto en cada transmisión?


El estándar define el siguiente operator<< sobrecarga en la plantilla de clase basic_ostream :

basic_ostream<charT,traits>& operator<<( basic_ostream<charT,traits>& (*pf) (basic_ostream<charT,traits>&) );

Efectos: Ninguno. No se comporta como una función de salida formateada (como se describe en 27.6.2.5.1).

Devoluciones: pf(*this) .

El parámetro es un puntero a una función que toma y devuelve una referencia a un std::ostream .

Esto significa que puede "transmitir" una función con esta firma a un objeto ostream y tiene el efecto de llamar a esa función en el flujo. Si usa el nombre de una función en una expresión, entonces (generalmente) se convierte en un puntero a esa función.

std::hex es un manipulador std::ios_base definido de la siguiente manera.

ios_base& hex(ios_base& str);

Efectos: Llama a str.setf(ios_base::hex, ios_base::basefield) .

Devoluciones: str.

Esto significa que la transmisión de hex a un ostream establecerá las ostream formato de la base de salida para generar números en hexadecimal. El manipulador no produce nada por sí mismo.


No hay nada de malo en ello, excepto que no hay un operador << sobrecargado definido para ello. Las sobrecargas existentes para << están esperando un manipulador con la firma ostream & (* fp) (ostream &) .

Si le asignara un manipulador con el tipo ostream & (* fp) () , obtendría un error de compilación, ya que no tiene una definición para operator << (ostream &, ostream & (* fp) ()) . Si deseara esta funcionalidad, tendría que sobrecargar al operador << para aceptar manipuladores de este tipo.

Tendrías que escribir una definición para esto:
ostream & ostream :: operator << (ostream & (* m) ())

Ten en cuenta que aquí no está sucediendo nada mágico. Las bibliotecas de flujo dependen en gran medida de las características estándar de C ++: sobrecarga de operadores, clases y referencias.

Ahora que sabe cómo puede crear la funcionalidad que describió, he aquí por qué no:

Sin pasar una referencia al flujo que intentamos manipular, no podemos realizar modificaciones en el flujo conectado al dispositivo final (cin, out, err, fstream, etc.). La función (los modificadores son solo funciones con nombres sofisticados) tendría que devolver un nuevo ostream que no tuviera nada que ver con el que está a la izquierda del operador <<, o mediante algún mecanismo muy feo, averiguar qué ostream debería conecte con el resto, todo a la derecha del modificador no llegará al dispositivo final, sino que se enviará a cualquier ostream que devuelva la función / modificador.

Piense en corrientes como esta

cout << "something here" << tab << "something else"<< endl;

realmente significa

(((cout << "something here") << tab ) << "something else" ) << endl);

donde cada conjunto de paréntesis hace algo para cout (escribir, modificar, etc.) y luego devuelve cout para que el siguiente conjunto de paréntesis pueda trabajar en él.

Si su modificador / función de tabulación no tomaba una referencia a un ostream, tendría que adivinar de alguna manera qué era ostream a la izquierda del operador << para realizar su tarea. ¿Estabas trabajando con cour, cerr, algún flujo de archivos ...? Las partes internas de la función nunca sabrán a menos que se les entregue esa información de alguna manera, y por qué no que sea tan simple como una referencia a ella.

Ahora, para realmente llevar el punto a casa, veamos qué es realmente endl y qué versión sobrecargada del operador << que estamos usando:

Este operador se ve así:

ostream& ostream::operator<<(ostream& (*m)(ostream&)) { return (*m)(*this); }

endl se parece a esto:

ostream& endl(ostream& os) { os << ''/n''; os.flush(); return os; }

El propósito de endl es agregar una nueva línea y vaciar el flujo, asegurándose de que todo el contenido del búfer interno del flujo haya sido escrito en el dispositivo. Para hacer esto, primero debe escribir un ''/ n'' en esta secuencia. A continuación, tiene que decirle a la secuencia para vaciar. La única forma en que Endl puede saber en qué secuencia escribir y vaciar es que el operador pase esa información a la función Endl cuando la llame. Sería como si te dijera que laves mi auto, pero nunca te digo cuál es el mío en el estacionamiento completo. Nunca serías capaz de hacer tu trabajo. Necesitas que te dé mi coche o puedo lavarlo yo mismo.

Espero que eso aclare las cosas

PD: si accidentalmente encuentra mi auto, lávelo.


Normalmente, el manipulador de flujo establece algunas banderas (u otras configuraciones) en el objeto de flujo, de modo que la próxima vez que se use, actuará de acuerdo con los indicadores. Por lo tanto, el manipulador devuelve el mismo objeto al que fue pasado. La sobrecarga del operator<< que llamó al manipulador ya tiene este objeto, por supuesto, por lo que, como habrá notado, el valor de retorno no es estrictamente necesario para ese caso. Creo que esto cubre a todos los manipuladores estándar, todos devuelven sus comentarios.

Sin embargo, con el valor de retorno, el marco es lo suficientemente flexible como para que un manipulador de flujo personalizado pueda devolver un objeto diferente, presumiblemente una envoltura para el objeto dado. Este otro objeto sería devuelto desde la cout << ''a'' << tab , y podría hacer algo que las configuraciones de formato de ostream no admiten.

Sin embargo, no estoy seguro de cómo arreglarías la liberación de este otro objeto, así que no sé qué tan práctico es esto. Puede que tenga que ser algo peculiar, como un objeto proxy que es administrado por el propio ostream . Entonces, el manipulador solo funcionaría para las clases de flujo personalizadas que lo apoyan activamente, lo que no suele ser el punto de los manipuladores.