Team LiB
Previous Section Next Section

8.3. Managing the Output Buffer

8.3. 输出缓冲区的管理

Each IO object manages a buffer, which is used to hold the data that the program reads and writes. When we write

每个 IO 对象管理一个缓冲区,用于存储程序读写的数据。如有下面语句:

    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:

系统将字符串字面值存储在与流 os 关联的缓冲区中。下面几种情况将导致缓冲区的内容被刷新,即写入到真实的输出设备或者文件:

  1. The program completes normally. All output buffers are emptied as part of the return from main.

    程序正常结束。作为 main 返回工作的一部分,将清空所有输出缓冲区。

  2. At some indeterminate time, the buffer can become full, in which case it will be flushed before writing the next value.

    在一些不确定的时候,缓冲区可能已经满了,在这种情况下,缓冲区将会在写下一个值之前刷新。

  3. We can flush the buffer explicitly using a manipulator (Section 1.2.2, p. 7) such as endl.

    用操纵符(第 1.2.2 节)显式地刷新缓冲区,例如行结束符 endl

  4. We can use the unitbuf manipulator to set the stream's internal state to empty the buffer after each output operation.

    在每次输出操作执行完后,用 unitbuf 操作符设置流的内部状态,从而清空缓冲区。

  5. 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.

    可将输出流与输入流关联(tie)起来。在这种情况下,在读输入流时将刷新其关联的输出缓冲区。

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:

我们的程序已经使用过 endl 操纵符,用于输出一个换行符并刷新缓冲区。除此之外,C++ 语言还提供了另外两个类似的操纵符。第一个经常使用的 flush,用于刷新流,但不在输出中添加任何字符。第二个则是比较少用的 ends,这个操纵符在缓冲区中插入空字符 null,然后后刷新它:

    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

unitbuf 操纵符

If we want to flush every output, it is better to use the unitbuf manipulator. This manipulator flushes the stream after every write:

如果需要刷新所有输出,最好使用 unitbuf 操纵符。这个操纵符在每次执行完写操作后都刷新流:

    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.

nounitbuf 操纵符将流恢复为使用正常的、由系统管理的缓冲区刷新方式。

Caution: Buffers Are Not Flushed if the Program Crashes

警告:如果程序崩溃了,则不会刷新缓冲区

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.

如果需要使用最后的输出给程序错误定位,则必须确定所有要输出的都已经输出。为了确保用户看到程序实际上处理的所有输出,最好的方法是保证所有的输出操作都显式地调用了 flushendl

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.

如果仅因为缓冲区没有刷新,程序员将浪费大量的时间跟踪调试并没有执行的代码。基于这个原因,输出时应多使用 endl 而非 '\n'。使用 endl 则不必担心程序崩溃时输出是否悬而未决(即还留在缓冲区,未输出到设备中)。


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

当输入流与输出流绑在一起时,任何读输入流的尝试都将首先刷新其输出流关联的缓冲区。标准库将 coutcin 绑在一起,因此语句:

         cin >> ival;

causes the buffer associated with cout to be flushed.

导致 cout 关联的缓冲区被刷新。

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.

tie 函数可用 istreamostream 对象调用,使用一个指向 ostream 对象的指针形参。调用 tie 函数时,将实参流绑在调用该函数的对象上。如果一个流调用 tie 函数将其本身绑在传递给 tieostream 实参对象上,则该流上的任何 IO 操作都会刷新实参所关联的缓冲区。

    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.

一个 ostream 对象每次只能与一个 istream 对象绑在一起。如果在调用 tie 函数时传递实参 0,则打破该流上已存在的捆绑。

Team LiB
Previous Section Next Section