std :: flush如何工作?

Das*_*aru 68 c++ stream

有人可以解释(最好用简单的英语)如何std::flush工作?

  • 它是什么?
  • 你什么时候冲洗一条小溪?
  • 它为什么如此重要?

谢谢.

Die*_*ühl 112

既然没有回答过什么 std::flush,那么这里有一些关于实际情况的详细信息.std::flush是一个操纵者,即具有特定签名的函数.从简单开始,您可以想到std::flush签名

std::ostream& std::flush(std::ostream&);
Run Code Online (Sandbox Code Playgroud)

但现实情况有点复杂(如果您有兴趣,下面也会对此进行说明).

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

std::ostream& std::ostream::operator<< (std::ostream& (*manip)(std::ostream&)) {
    (*manip)(*this);
    return *this;
}
Run Code Online (Sandbox Code Playgroud)

也就是说,当你"输出" std::flush到a时std::ostream,它只调用相应的函数,即以下两个语句是等价的:

std::cout << std::flush;
std::flush(std::cout);
Run Code Online (Sandbox Code Playgroud)

现在,std::flush()它本身就相当简单:它所做的只是调用std::ostream::flush(),即你可以设想它的实现看起来像这样:

std::ostream& std::flush(std::ostream& out) {
    out.flush();
    return out;
}
Run Code Online (Sandbox Code Playgroud)

std::ostream::flush()功能在技术上要求std::streambuf::pubsync()对数据流缓存器(如果有的话),其与所述流相关联:所述数据流缓存器是负责缓冲字符和发送字符到外部目的地时使用的缓冲将溢出,或当内部表示应与进行同步外部目的地,即要刷新数据的时间.在与外部目标同步的顺序流上,意味着立即发送任何缓冲的字符.也就是说,使用std::flush导致流缓冲区刷新其输出缓冲区.例如,当数据写入控制台时,刷新会导致字符出现在控制台上的此点.

这可能会引发一个问题:为什么不立即写字符?简单的答案是写字符通常很慢.但是,写入合理数量的字符所花费的时间与写入其中一个字符的时间基本相同.字符的数量取决于操作系统,文件系统等的许多特征,但通常最多只有4k字符的内容与大约一个字符同时写入.因此,在根据外部目的地的细节使用缓冲器发送字符之前缓冲字符可以是巨大的性能改进.

以上应该回答你的三个问题中的两个.剩下的问题是:你什么时候冲洗一条小溪?答案是:当字符应写入外部目的地时!这可能是在写一个文件(关闭文件隐含地刷新缓冲区,虽然)或立即的端要求用户输入之前(请注意,std::cout从读取时自动刷新std::cinstd::coutstd::istream::tie()"d到std::cin).虽然可能有一些情况下您明确要刷新流,但我觉得它们相当罕见.

最后,我答应给一个什么样的全貌std::flush居然是:流是能够与不同性格类型的交易(在他们工作的实践类模板charwchar_t;使他们与其他字符是相当复杂,虽然是可行的工作,如果你真的有决心).为了能够std::flush与流的所有实例一起使用,它碰巧是一个带有如下签名的函数模板:

template <typename cT, typename Traits>
std::basic_ostream<cT, Traits>& std::flush(std::basic_ostream<cT, Traits>&);
Run Code Online (Sandbox Code Playgroud)

std::flush立即使用它实例化std::basic_ostream它并不重要:编译器自动推断模板参数.但是,如果没有提及此函数以及促进模板参数推导的内容,编译器将无法推导出模板参数.

  • 很棒的答案.无法找到关于其他地方如何工作的质量信息. (3认同)

Ker*_* SB 26

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

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

for (;;)
{
    perform_expensive_operation();
    std::cout << '.';
    std::flush(std::cout);
}
Run Code Online (Sandbox Code Playgroud)

没有冲洗,你很长时间都看不到输出.

请注意,std::endl将换行符插入流中并使其刷新.由于冲洗是温和的,std::endl如果不明确需要冲洗,则不应过度使用.

  • 对读者来说只是另外一个注意事项:`cout`并不是C++中唯一缓存的东西.`ostream`s通常默认是缓冲的,其中还包括`fstream`s等. (6认同)

小智 13

这是一个简短的程序,您可以编写它来观察正在进行的刷新

#include <iostream>
#include <unistd.h>

using namespace std;

int main() {

    cout << "Line 1..." << flush;

    usleep(500000);

    cout << "\nLine 2" << endl;

    cout << "Line 3" << endl ;

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

运行此程序:你会注意到它打印第1行,暂停,然后打印第2行和第3行.现在删除刷新调用并再次运行程序 - 你会注意到程序暂停,然后打印所有3行同时.在程序暂停之前缓冲第一行,但由于缓冲区永远不会被刷新,因此直到第2行的endl调用才会输出第1行.


Lee*_*dor 7

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

当您刷新流时,它会告诉语言库、操作系统和硬件,您希望将迄今为止输出的任何字符强制一直存储到存储中。从理论上讲,在“冲水”之后,您可以将电源线踢出墙壁,而这些字符仍会被安全存储。

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

所以刷新是一种检查点。

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