C++ std::flush是如何工作的?

C++ std::flush是如何工作的?,c++,stream,C++,Stream,有人能解释一下(最好用简单的英语)std::flush的工作原理吗 这是什么 你什么时候冲洗一条小溪 为什么它很重要 谢谢。默认情况下,std::cout是缓冲的,只有在缓冲区已满或出现其他刷新情况(例如流中的换行符)时,才会打印实际输出。有时,您需要确保打印立即进行,并且需要手动刷新 例如,假设要通过打印单个点来报告进度报告: for (;;) { perform_expensive_operation(); std::cout << '.'; std:

有人能解释一下(最好用简单的英语)std::flush的工作原理吗

  • 这是什么
  • 你什么时候冲洗一条小溪
  • 为什么它很重要

谢谢。

默认情况下,
std::cout
是缓冲的,只有在缓冲区已满或出现其他刷新情况(例如流中的换行符)时,才会打印实际输出。有时,您需要确保打印立即进行,并且需要手动刷新

例如,假设要通过打印单个点来报告进度报告:

for (;;)
{
    perform_expensive_operation();
    std::cout << '.';
    std::flush(std::cout);
}
(;;)的

{
执行_昂贵的_操作();

std::cout流连接到某个对象。在标准输出的情况下,它可以是控制台/屏幕,也可以重定向到管道或文件。程序和存储文件的硬盘之间有很多代码。例如,操作系统正在处理任何文件或磁盘驱动器本身可能是缓冲数据,以便能够在固定大小的块中写入数据,或者只是为了提高效率

当您刷新流时,它会告诉语言库、操作系统和硬件,您希望将迄今为止输出的所有字符强制存储到存储器中。理论上,在“刷新”后,您可以将电线从墙上拔出,这些字符仍然可以安全存储

我应该提到的是,编写操作系统驱动程序或设计磁盘驱动器的人可能可以自由地使用“刷新”作为建议,他们可能不会真正写出字符。即使输出关闭,他们也可能会等待一段时间来保存字符。(请记住,操作系统可以同时执行各种操作,而等待一两秒钟来处理字节可能会更有效。)

所以刷新是一种检查点

再举一个例子:如果输出将进入控制台显示,刷新将确保字符真正到达用户可以看到它们的地方。当您需要键盘输入时,这是一件重要的事情。如果您认为您已经向控制台写了一个问题,但它仍然卡在某个内部缓冲区中,用户不知道在回答中键入什么。因此,这是一个刷新很重要的情况。

由于没有回答std::flush碰巧是什么,下面是一些关于它实际是什么的详细信息。
std::flush
是一个操纵器,即具有特定签名的函数。简单来说,您可以想到
std::具有签名的刷新

std::ostream& std::flush(std::ostream&);
不过,现实情况要复杂一些(如果你感兴趣,下面也会解释)

stream类重载输出运算符采用这种形式的运算符,即有一个成员函数采用操纵器作为参数。输出运算符使用对象本身调用操纵器:

std::ostream& std::ostream::operator<< (std::ostream& (*manip)(std::ostream&)) {
    (*manip)(*this);
    return *this;
}
现在,
std::flush()
本身相当简单:它只需调用
std::ostream::flush()
,也就是说,您可以设想它的实现如下所示:

std::ostream& std::flush(std::ostream& out) {
    out.flush();
    return out;
}
template <typename cT, typename Traits>
std::basic_ostream<cT, Traits>& std::flush(std::basic_ostream<cT, Traits>&);
std::ostream::flush()
函数从技术上调用流缓冲区(如果有)上的
std::streambuf::pubsync()
与流关联:流缓冲区负责缓冲字符,并在使用的缓冲区溢出或内部表示应与外部目标同步时(即,当数据要刷新时)将字符发送到外部目标。在与exte同步的顺序流上rnal destination只意味着立即发送任何缓冲字符。也就是说,使用
std::flush
会导致流缓冲区刷新其输出缓冲区。例如,当数据写入控制台时,刷新会导致字符出现在控制台的这一点上

这可能会引发一个问题:为什么不立即书写字符?简单的答案是,书写字符通常相当缓慢。然而,书写合理数量的字符所需的时间基本上与只写一个字符所需的时间相同。字符的数量取决于歌剧的许多特征ng系统、文件系统等,但通常多达4k字符与一个字符同时写入。因此,根据外部目标的详细信息,在发送字符之前使用缓冲区对字符进行缓冲可以极大地提高性能

上面应该回答三个问题中的两个。剩下的问题是:何时刷新流?答案是:何时将字符写入外部目标!这可能是在写入文件结束时(关闭文件会隐式刷新缓冲区),也可能是在请求用户输入之前(请注意,
std::cout
在读取
std::cin
时会自动刷新,因为
std::cout
std::istream::tie()
'd to
std::cin
)。虽然可能有一些情况下您明确希望刷新流,但我发现它们非常罕见

最后,我承诺全面介绍
std::flush
实际上是什么:流是能够处理不同字符类型的类模板(实际上,它们与
char
wchar\u t
一起工作;让它们与另一个字符一起工作是相当复杂的,尽管如果你真的确定的话是可行的)。为了能够对流的所有实例化使用
std::flush
,它恰好是一个具有以下签名的函数模板:

std::ostream& std::flush(std::ostream& out) {
    out.flush();
    return out;
}
template <typename cT, typename Traits>
std::basic_ostream<cT, Traits>& std::flush(std::basic_ostream<cT, Traits>&);
模板
std::basic_ostream&std::flush(std::basic_ostream&);
当使用
std::flush
并实例化
std::basic_ostream
时,其实并不重要:编译器会自动推断模板参数