C ++ cout和cin缓冲区,以及一般的缓冲区

时间:2012-02-14 08:57:56

标签: c++ flush buffer

有人可以更明确地解释缓冲区的概念吗?我知道缓冲区是存储字符的数据结构,以及从中读取数据的位置。冲洗缓冲区的想法是什么?

当刷新缓冲区时,这是指写入存储在其中的字符的行为吗?

来自文字:

To avoid the overhead of writing in response to each output request, the library uses the 
buffer to accumulate the characters to be written, and flushes the buffer, by writing its
contents to the output device, only when necessary. By doing so, it can combine several 
output operations into a single write.

当提到“刷新”时,几乎使它听起来好像缓冲区正在写入但同时也被擦除。只是猜测。

那么,为了在屏幕上写入视图需要缓冲区刷新?

When our program writes its prompt to cout, that output goes into the buffer associated
with the standard output stream. Next, we attempt to read from cin. This read flushes
the cout buffer, so we are assured that our user will see the prompt.

在这里,听起来好像通过在结尾处使用'endl'它告诉系统它需要立即写入(暗示否则不会?)什么是endl未使用?

Writing the value of std::endl ends the line of 
output, and then flushes the buffer, which forces the system to write to the output 
stream immediately.

2 个答案:

答案 0 :(得分:23)

缓冲的基本思想是将操作组合成更大的块:而不是读取少量字节,读取整个页面并按要求使其可用;而不是写入少量字节,缓冲它们并写入整个页面或明确请求写入时。从本质上讲,这是一项重要的性能优化。对于I / O操作来说非常明确,但通常也适用于其他用途:一次处理多个单元通常比处理单个单元更快。

关于I / O 刷新是指将当前缓冲的字节写入其目的地 - 无论这在实践中意味着什么。对于C ++,IOStreams刷新流量相当于调用成员函数std::ostream::flush(),而成员函数std::streambuf::pubsync()依次调用关联流缓冲区上的std::basic_ostream<cT, traits>(这忽略了流实际为类模板的细节,例如std::streambuf ;出于本讨论的目的,它们是类模板并不重要:基类std::streambuf::pubsync()是C ++关于如何处理某个流的抽象。它在概念上分别由输入和输出缓冲器以及负责读取或写入缓冲器的虚函数组成。函数std::streambuf::sync()调用虚函数sync(),该函数应该为每个可能缓冲字符的流缓冲区重写。也就是说,刷新实际上意味着什么取决于虚拟功能的实现方式。

覆盖std::filebuf是否实际上做了什么以及它做了什么显然取决于流缓冲区代表什么。例如,对于负责读取或写入文件的sync()std::stringbuf写入缓冲区的当前内容并从缓冲区中删除刷新的字符。鉴于文件可能并不真正代表物理文件,例如一个命名管道与不同的进程通信,这是合理的行为。另一方面,刷新std::string,它是用于实现写入std::ostringstream的流缓冲器,例如使用的std::stringstd::stringbuf::str()实际上并没有做任何事情:字符串完全在程序中,并且在调用std::streambuf::pubsync()成员函数时构造表示其值的pubsync()。对于用户定义的流缓冲区,有许多不同的行为。一种常见的流缓冲区类是在将输出传递给另一个流缓冲区之前以某种方式过滤输出(例如,日志缓冲区可以在每个换行符之后添加时间戳)。这些通常只调用下一个流缓冲区的std::filebuf函数。

因此,对实际行为的描述通常保持相当模糊,因为它并不是很清楚究竟发生了什么。从概念上讲,刷新流或在流缓冲区上调用std::cerr应该更新字符的外部目标以匹配当前的内部状态。通常,这相当于转发当前缓冲的字符并将其从内部缓冲区中删除。在这一点上值得注意的是,缓冲区通常也会在它刚刚满时写入。当它变满时再次依赖于特定的流缓冲区。 std::ios_base::unitbuf的良好实现基本上将填充与底层页面大小(或其倍数)匹配的字节缓冲区,然后编写完整页面,最大限度地减少所需的I / O操作数量(这实际上相对棘手)要做的是因为不同文件系统之间的缓冲区大小不同,并且根据编写生成的字节数时使用的编码不容易估计。

标准C ++库通常不需要显式刷新:

  • std::cerr << std::nounitbuf设置为自动刷新在调用每个输出操作后产生的任何输出。这是默认设置格式标志std::clog的结果。要关闭此功能,您可以使用std::istream,也可以使用std::ostream写入同一目的地,但不会执行此操作。
  • std::cout中读取&#34;并列#34;如果有std::cin,则会被刷新。默认情况下,std::ostreamin.tie(&out)绑定。如果您想自己设置绑定std::ostream,可以使用例如std::cin.tie(0)或者,如果您要删除绑定std::ostream,可以使用例如std::ostream
  • std::ostream被销毁时(或者当std::streambuf::overflow()是标准流之一时,它会被正常销毁),sync()将被刷新。
  • 当流的缓冲区溢出时,调用虚函数std::ostream,该函数通常将当前缓冲区的缓冲区(加上传递的字符,如果有的话)写入其目的地。这通常是通过调用std::ostream::flush()来清除当前缓冲区来完成的,但再次完成的工作取决于具体的流缓冲区。

您还可以明确请求刷新std::cout.flush()

  • 您可以调用成员函数std::streambuf::pubsync(),例如std::cout.rdbuf()->pubsync()
  • 您可以调用成员函数std::ostream::flush(),例如std::flush(假设有一个流缓冲区设置;如果没有流缓冲区,则调用std::cout << std::flush将无效。)
  • 您可以使用操纵器std::endl,例如std::cout << std::endl
  • 您可以使用操纵器std::endl,例如std::endl首先写一个换行符然后刷新流。请注意,当您真正想要刷新输出时,应该使用std::cin 。当你真正想要创建一个行尾时,不要使用std::cout!只为后者写一个换行符!

在任何情况下,如果你只写了几个不会导致流缓冲区缓冲区溢出的字符,那么在它们被隐式或显式刷新之前不会发生任何事情。一般来说,这不是问题,因为缓存输出需要变得可用的正常情况,即从tie()读取时,由std::cin处理tie() d到{{1 }}。当使用其他I / O机制时,可能需要明确std::unitbuf个相关流。如果程序&#34;崩溃&#34;缓冲区有时会出现问题。或者在调试期间断言,因为某些流输出可能已经发出但尚未发送。解决此问题的方法是使用{{1}}。

答案 1 :(得分:2)

想想如果每次“写入”一个字节到磁盘文件,你的程序实际上去了磁盘,在当前扇区/簇/块中读取,更改了单个字节然后将其写回来会发生什么到物理磁盘。

我认为你的软件在性能方面最好被描述为“冰川”: - )

缓冲是一种对读取和写入进行批处理的方法,以提高它们的效率。

例如,如果您正在写入磁盘文件,它可能会等到您有一个完整的4K块然后再将其写入磁盘。

阅读时,即使你只要求十个字节,它也可能会得到一个4K块,假设你可能会在短期内要求其余的。

一旦缓存已满,就会隐式进行刷新,如果请求则显式进行(使用刷新调用或关闭文件时)。

请注意,此文件缓冲只是一种类缓冲,该概念可以在任何可以通过“分块”读取和写入来提高效率的地方使用。