C++ std::flush 是如何工作的?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/14105650/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How does std::flush work?
提问by Dasaru
Can someone please explain (preferably using plain english) how std::flush
works?
有人可以解释一下(最好使用简单的英语)是如何std::flush
工作的吗?
- What is it?
- When would you flush a stream?
- Why is it important?
- 它是什么?
- 你什么时候会刷新一个流?
- 它为什么如此重要?
Thank you.
谢谢你。
回答by Dietmar Kühl
Since it wasn't answered whatstd::flush
happens to be, here is some detail on what it actually is. std::flush
is a manipulator, i.e., a function with a specific signature. To start off simple, you can think of std::flush
of having the signature
由于没有回答碰巧是什么std::flush
,这里有一些关于它实际是什么的细节。std::flush
是一个操纵器,即具有特定签名的函数。从简单开始,您可以考虑std::flush
拥有签名
std::ostream& std::flush(std::ostream&);
The reality is a bit more complex, though (if you are interested, it is explained below as well).
但是,实际情况要复杂一些(如果您有兴趣,下面也会解释)。
The stream class overload output operators taking operators of this form, i.e., there is a member function taking a manipulator as argument. The output operator calls the manipulator with the object itself:
流类重载输出运算符采用这种形式的运算符,即有一个成员函数以操纵符作为参数。输出运算符使用对象本身调用操纵器:
std::ostream& std::ostream::operator<< (std::ostream& (*manip)(std::ostream&)) {
(*manip)(*this);
return *this;
}
That is, when you "output" std::flush
with to an std::ostream
, it just calls the corresponding function, i.e., the following two statements are equivalent:
也就是说,当你“输出”std::flush
到 an 时std::ostream
,它只是调用了对应的函数,即下面两条语句是等价的:
std::cout << std::flush;
std::flush(std::cout);
Now, std::flush()
itself is fairly simple: All it does is to call std::ostream::flush()
, i.e., you can envision its implementation to look something like this:
现在,std::flush()
它本身相当简单:它所做的就是调用std::ostream::flush()
,即,您可以设想它的实现看起来像这样:
std::ostream& std::flush(std::ostream& out) {
out.flush();
return out;
}
The std::ostream::flush()
function technically calls std::streambuf::pubsync()
on the stream buffer (if any) which is associated with the stream: The stream buffer is responsible for buffering characters and sending characters to the external destination when the used buffer would overflow or when the internal representation should be synced with the external destination, i.e., when the data is to be flushed. On a sequential stream syncing with the external destination just means that any buffered characters are immediately sent. That is, using std::flush
causes the stream buffer to flush its output buffer. For example, when data is written to a console flushing causes the characters to appear at this point on the console.
该std::ostream::flush()
函数在技术上调用std::streambuf::pubsync()
与流关联的流缓冲区(如果有):流缓冲区负责缓冲字符并将字符发送到外部目标,当使用的缓冲区溢出或内部表示应与外部目的地,即数据何时被刷新。在与外部目标同步的顺序流上,任何缓冲的字符都会立即发送。也就是说, usingstd::flush
会导致流缓冲区刷新其输出缓冲区。例如,当数据写入控制台时,刷新会导致字符出现在控制台上的这一点。
This may raise the question: Why aren't characters immediately written? The simple answer is that writing characters is generally fairly slow. However, the amount of time it takes to write a reasonable amount of characters is essentially identical to writing just one where. The amount of characters depends on many characteristics of the operating system, file systems, etc. but often up to something like 4k characters are written in about the same time as just one character. Thus, buffering characters up before sending them using a buffer depending on the details of the external destination can be a huge performance improvement.
这可能会提出一个问题:为什么不立即写出字符?简单的答案是,写字符通常相当慢。但是,编写合理数量的字符所需的时间基本上与只编写一个字符所需的时间相同。字符的数量取决于操作系统、文件系统等的许多特性,但通常多达 4k 个字符与一个字符几乎同时写入。因此,在根据外部目标的详细信息使用缓冲区发送字符之前对其进行缓冲可以是一个巨大的性能改进。
The above should answer two of your three questions. The remaining question is: When would you flush a stream? The answer is: When the characters should be written to the external destination! This may be at the end of writing a file (closing a file implicitly flushes the buffer, though) or immediately before asking for user input (note that std::cout
is automatically flushed when reading from std::cin
as std::cout
is std::istream::tie()
'd to std::cin
). Although there may be a few occasions where you explicitly want to flush a stream, I find them to be fairly rare.
以上应该回答你的三个问题中的两个。剩下的问题是:你什么时候刷新流?答案是:什么时候应该将字符写入外部目的地!这可能是在写入文件结束时(尽管关闭文件会隐式刷新缓冲区)或在要求用户输入之前立即(请注意,std::cout
从std::cin
as std::cout
is std::istream::tie()
'd to读取时会自动刷新std::cin
)。尽管在某些情况下您可能明确想要刷新流,但我发现它们相当罕见。
Finally, I promised to give a full picture of what std::flush
actually is: The streams are class templates capable of dealing with different character types (in practice they work with char
and wchar_t
; making them work with another characters is quite involved although doable if you are really determined). To be able to use std::flush
with all instantiations of streams, it happens to be a function template with a signature like this:
最后,我承诺给出std::flush
实际情况的全貌:流是能够处理不同字符类型的类模板(实际上它们与char
和wchar_t
一起工作;尽管如果你真的下定决心,使它们与其他字符一起工作是相当复杂的,尽管这是可行的) )。为了能够std::flush
与流的所有实例一起使用,它恰好是一个具有如下签名的函数模板:
template <typename cT, typename Traits>
std::basic_ostream<cT, Traits>& std::flush(std::basic_ostream<cT, Traits>&);
When using std::flush
immediately with an instantiation of std::basic_ostream
it doesn't really matter: The compiler deduces the template arguments automatically. However, in cases where this function isn't mentioned together with something facilitating the template argument deduction, the compiler will fail to deduce the template arguments.
当std::flush
立即使用std::basic_ostream
它的实例化时并不重要:编译器会自动推导出模板参数。但是,如果此函数未与有助于模板参数推导的内容一起提及,编译器将无法推导模板参数。
回答by Kerrek SB
By default, std::cout
is buffered, and the actual output is only printed once the buffer is full or some other flushing situation occurs (e.g. a newline in the stream). Sometimes you want to make sure that the printing happens immediately, and you need to flush manually.
默认情况下,std::cout
是缓冲的,并且只有在缓冲区已满或发生其他一些刷新情况(例如流中的换行符)时才会打印实际输出。有时您想确保立即打印,您需要手动冲洗。
For example, suppose you want to report a progress report by printing a single dot:
例如,假设您想通过打印一个点来报告进度报告:
for (;;)
{
perform_expensive_operation();
std::cout << '.';
std::flush(std::cout);
}
Without the flushing, you wouldn't see the output for a very long time.
如果没有冲洗,您将在很长一段时间内看不到输出。
Note that std::endl
inserts a newline into a stream as well as causing it to flush. Since flushing is mildly expensive, std::endl
shouldn't be used excessively if the flushing isn't expressly desired.
请注意,std::endl
将换行符插入到流中并使其刷新。由于冲洗稍微昂贵,std::endl
如果冲洗不是明确需要的,则不应过度使用。
回答by Neil Anderson
Here's a short program that you can write to observe what flush is doing
这是一个简短的程序,您可以编写它来观察flush在做什么
#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 this program: you'll notice that it prints line 1, pauses, then prints line 2 and 3. Now remove the flush call and run the program again- you'll notice that the program pauses and then prints all 3 lines at the same time. The first line is buffered before the program pauses, but because the buffer is never flushed, line 1 is not outputted until the endl call from line 2.
运行这个程序:你会注意到它打印第 1 行,暂停,然后打印第 2 和 3 行。现在删除刷新调用并再次运行程序 - 你会注意到程序暂停,然后打印所有 3 行同时。第一行在程序暂停之前被缓冲,但是因为缓冲区从不刷新,所以直到第 2 行的 endl 调用才输出第 1 行。
回答by Lee Meador
A stream is connected to something. In the case of standard output, it could be the console/screen or it could be redirected to a pipe or a file. There is a lot of code between your program and, for example, the hard disk where the file is stored. For example, the operating system is doing stuff with any file or the disk drive itself might be buffering data to be able to write it in fixed size blocks or just to be more efficient.
流连接到某物。在标准输出的情况下,它可能是控制台/屏幕,也可能被重定向到管道或文件。在您的程序和例如存储文件的硬盘之间有很多代码。例如,操作系统正在处理任何文件或磁盘驱动器本身可能正在缓冲数据,以便能够将其写入固定大小的块或只是为了提高效率。
When you flush the stream, it tells the language libraries, the os and the hardware that you want to any characters that you have output so far to be forced all the way to storage. Theoretically, after a 'flush', you could kick the cord out of the wall and those characters would still be safely stored.
当您刷新流时,它会告诉语言库、操作系统和硬件,您希望将迄今为止输出的任何字符强制一直存储到存储中。从理论上讲,在“冲水”之后,您可以将电源线踢出墙壁,而这些字符仍会被安全存储。
I should mention that the people writing the os drivers or the people designing the disk drive might are free to use 'flush' as a suggestion and they might not really write the characters out. Even when the output is closed, they might wait a while to save them. (Remember that the os does all sorts of things at once and it might be more efficient to wait a second or two to handle your bytes.)
我应该提到的是,编写操作系统驱动程序的人或设计磁盘驱动器的人可能可以随意使用“flush”作为建议,他们可能不会真正写出字符。即使输出关闭,他们也可能会等待一段时间来保存它们。(请记住,操作系统同时执行各种操作,等待一两秒钟来处理您的字节可能更有效。)
So a flush is a sort of checkpoint.
所以刷新是一种检查点。
One more example: If the output is going to the console display, a flush will make sure the characters actually get all the way out to where the user can see them. This is an important thing to do when you are expecting keyboard input. If you think you have written a question to the console and its still stuck in some internal buffer somewhere, the user doesn't know what to type in answer. So, this is a case where the flush is important.
再举一个例子:如果输出到控制台显示,刷新将确保字符实际上一直到达用户可以看到的地方。当您需要键盘输入时,这是一件很重要的事情。如果您认为您已经向控制台写了一个问题,但它仍然停留在某个内部缓冲区中的某个地方,那么用户不知道该输入什么答案。因此,这是冲洗很重要的情况。