8.3. Managing the Output Buffer
Each IO object manages a buffer, which is used to hold the data that the program reads and writes. When we write
os << "please enter a value: ";
the literal string is stored in the buffer associated with the stream os. There are several conditions that cause the buffer to be flushedthat is, writtento the actual output device or file:
The program completes normally. All output buffers are emptied 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 (Section 1.2.2, p. 7) such as endl. We can use the unitbuf manipulator to set the stream's internal state to empty the buffer after each output operation. We can tie the output stream to an input stream, in which case the output buffer is flushed whenever the associated input stream is read.
Exercise 8.3: | Write a function that takes and returns an istream&. The function should read the stream until it hits end-of-file. The function should print what it reads to the standard output. Reset the stream so that it is valid and return the stream.
| Exercise 8.4: | Test your function by calling it passing cin as an argument.
| Exercise 8.5: | What causes the following while to terminate?
while (cin >> i) /* . . . */
|
|
Flushing the Output Buffer
Our programs have already used the endl manipulator, which writes a newline and flushes the buffer. There are two other similar manipulators. The first, flush, is used quite frequently. It flushes the stream but adds no characters to the output. The second, ends, is used much less often. It inserts a null character into the buffer and then flushes it:
cout << "hi!" << flush; // flushes the buffer; adds no data
cout << "hi!" << ends; // inserts a null, then flushes the buffer
cout << "hi!" << endl; // inserts a newline, then flushes the buffer
The unitbuf Manipulator
If we want to flush every output, it is better to use the unitbuf manipulator. This manipulator flushes the stream after every write:
cout << unitbuf << "first" << " second" << nounitbuf;
is equivalent to writing
cout << "first" << flush << " second" << flush;
The nounitbuf manipulator restores the stream to use normal, system-managed buffer flushing.
Output buffers are not flushed if the program terminates abnormally. When attempting to debug a program that has crashed, we often use the last output to help isolate the region of program in which the bug might occur. If the crash is after a particular print statement, then we know that the crash happened after that point in the program.
When debugging a program, it is essential to make sure that any output you think should have been written was actually flushed. Because the system does not automatically flush the buffers when the program crashes, it is likely that there is output that the program wrote but that has not shown up on the standard output. It is still sitting in an output buffer waiting to be printed.
If you use the last output to help locate the bug, you need to be certain that all the output really did get printed. Making sure that all output operations include an explicit flush or call to endl is the best way to ensure that you are seeing all the output that the program actually processed.
Countless hours of programmer time have been wasted tracking through code that appeared not to have executed when in fact the buffer simply had not been flushed. For this reason, we tend to use endl rather than \n when writing output. Using endl means we do not have to wonder whether output is pending when a program crashes.
|
Tying Input and Output Streams Together
When an input stream is tied to an output stream, any attempt to read the input stream will first flush the buffer associated 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 be sure that their input and output streams are tied. Doing so means that we are guaranteed that any output, which might include prompts to the user, has been written before attempting to read. |
The tie function can be called on either istream or an ostream. It takes a pointer to an ostream and ties the argument stream to the object on which tie was called. When a stream ties itself to an ostream, then any IO operation on the stream that called tie flushes the buffer associated with the argument it passed to tie.
cin.tie(&cout); // illustration only: the library ties cin and cout for us
ostream *old_tie = cin.tie();
cin.tie(0); // break tie to cout, cout no longer flushed when cin is read
cin.tie(&cerr); // ties cin and cerr, not necessarily a good idea!
// ...
cin.tie(0); // break tie between cin and cerr
cin.tie(old_tie); // restablish normal tie between cin and cout
An ostream object can be tied to only one istream object at a time. To break an existing tie, we pass in an argument of 0.
 |