gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-12-01 23:22:11 24 4
gpt4 key购买 nike

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

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

从文字:

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.

最佳答案

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

关于I/O刷新,是指将当前缓冲的字节写入其目的地-实际上这意味着什么。对于C++ IOStreams,刷新流等于调用成员函数std::ostream::flush(),后者又在关联的流缓冲区上调用std::streambuf::pubsync()(这忽略了该流实际上是类模板的细节,例如std::basic_ostream<cT, traits>;出于此讨论的目的,这无关紧要它们是类模板):基类std::streambuf是C++关于如何处理特定流的抽象。它在概念上由输入和输出缓冲区以及分别负责读取或写入缓冲区的虚函数组成。函数std::streambuf::pubsync()调用虚拟函数std::streambuf::sync(),该函数应为每个可能缓冲字符的流缓冲区重写。也就是说,刷新的实际含义取决于该虚拟功能的实现方式。
sync()的覆盖是否实际起作用以及它的作用显然取决于流缓冲区表示的内容。例如,对于负责读写文件的std::filebufsync()写入缓冲区的当前内容,并从缓冲区中删除刷新的字符。鉴于文件可能并不真正代表物理文件,但例如与不同进程进行通信的命名管道,这是合理的行为。另一方面,刷新std::stringbuf,它是用于实现对使用的std::string进行写入的流缓冲区。 by std::ostringstream实际上不执行任何操作:字符串完全在程序中,并且表示其值的std::string是在调用std::stringbuf::str()成员函数时构造的。对于用户定义的流缓冲区,有许多不同的行为。常见的一类流缓冲区是在将输出传递到另一个流缓冲区之前以某种方式过滤输出(例如,日志记录缓冲区可能会在每个换行符之后添加时间戳)。这些通常只调用下一个流缓冲区的std::streambuf::pubsync()函数。

因此,对实际行为的描述通常含糊不清,因为尚不清楚确切会发生什么。从概念上讲,刷新流或在流缓冲区上调用pubsync()应该更新字符的外部目标以匹配当前内部状态。通常,这相当于转发当前缓冲的字符并将其从内部缓冲区中删除。在这一点上,值得注意的是,缓冲区通常在刚满时也会被写入。它何时变满,再次取决于特定的流缓冲区。好的std::filebuf实现实质上将填充与基础页面(或其倍数)大小匹配的字节缓冲区,然后写入完整的页面,从而最大程度地减少所需的I/O操作数量(这实际上相对棘手,因为缓冲区大小在不同的文件系统之间是不同的,并且取决于写入时产生的字节数无法轻易估算出的编码方式)。

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

  • std::cerr设置为自动刷新在调用每个输出操作后产生的任何输出。这是默认设置格式标志std::ios_base::unitbuf的结果。要关闭此功能,您可以使用std::cerr << std::nounitbuf,也可以只使用std::clog,它会写入同一目标位置,但不会执行此刷新操作。
  • std::istream读取时,将刷新“绑定(bind)的” std::ostream(如果有)。默认情况下,std::coutstd::cin绑定(bind)在一起。如果您想自己设置绑定(bind)的std::ostream,则可以使用例如in.tie(&out),或者,如果您想删除绑定(bind)的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来处理的。当使用其他I/O机制时,可能需要显式 tie()相关流。如果程序在调试期间“崩溃”或断言,则缓冲区有时会成为问题,因为流的某些输出可能已完成但尚未发送。解决方法是使用 std::unitbuf

    关于c++ - C++ cout和cin缓冲区,以及一般的缓冲区,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9274057/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com