Each output stream manages a buffer, which it uses to hold the data that the program reads and writes. For example, when the following code is executed
os << "please enter a value: ";
the literal string might be printed immediately, or the operating system might store the data in a buffer to be printed later. Using a buffer allows the operating system to combine several output operations from our program into a single system-level write. Because writing to a device can be time-consuming, letting the operating system combine several output operations into a single write can provide an important performance boost.
There are several conditions that cause the buffer to be flushed—that is, to be written—to the actual output device or file:
• The program completes normally. All output buffers are flushed as part of the return
from main
.
• At some indeterminate time, the buffer can become full, in which case it will be flushed before writing the next value.
• We can flush the buffer explicitly using a manipulator such as endl
(§ 1.2, p. 7).
• We can use the unitbuf
manipulator to set the stream’s internal state to empty the buffer after each output operation. By default, unitbuf
is set for cerr
, so that writes to cerr
are flushed immediately.
• An output stream might be tied to another stream. In this case, the buffer of the tied stream is flushed whenever the tied stream is read or written. By default, cin
and cerr
are both tied to cout
. Hence, reading cin
or writing to cerr
flushes the buffer in cout
.
Our programs have already used the endl
manipulator, which ends the current line and flushes the buffer. There are two other similar manipulators: flush
and ends. flush
flushes the stream but adds no characters to the output; ends
inserts a null character into the buffer and then flushes it:
cout << "hi!" << endl; // writes hi and a newline, then flushes the buffer
cout << "hi!" << flush; // writes hi, then flushes the buffer; adds no data
cout << "hi!" << ends; // writes hi and a null, then flushes the buffer
unitbuf
ManipulatorIf we want to flush after every output, we can use the unitbuf
manipulator. This manipulator tells the stream to do a flush
after every subsequent write. The nounitbuf
manipulator restores the stream to use normal, system-managed buffer flushing:
cout << unitbuf; // all writes will be flushed immediately
// any output is flushed immediately, no buffering
cout << nounitbuf; // returns to normal buffering
When an input stream is tied to an output stream, any attempt to read the input stream will first flush the buffer associated with the output stream. The library ties cout
to cin
, so the statement
cin >> ival;
causes the buffer associated with cout
to be flushed.
Interactive systems usually should tie their input stream to their output stream. Doing so means that all output, which might include prompts to the user, will be written before attempting to read the input.
There are two overloaded (§ 6.4, p. 230) versions of tie:
One version takes no argument and returns a pointer to the output stream, if any, to which this object is currently tied. The function returns the null pointer if the stream is not tied.
The second version of tie
takes a pointer to an ostream
and ties itself to that ostream
. That is, x.tie(&o)
ties the stream x
to the output stream o
.
We can tie either an istream
or an ostream
object to another ostream
:
cin.tie(&cout); // illustration only: the library ties cin and cout for us
// old_tie points to the stream (if any) currently tied to cin
ostream *old_tie = cin.tie(nullptr); // cin is no longer tied
// ties cin and cerr; not a good idea because cin should be tied to cout
cin.tie(&cerr); // reading cin flushes cerr, not cout
cin.tie(old_tie); // reestablish normal tie between cin and cout
To tie a given stream to a new output stream, we pass tie
a pointer to the new stream. To untie the stream completely, we pass a null pointer. Each stream can be tied to at most one stream at a time. However, multiple streams can tie themselves to the same ostream
.