有人可以解释(最好用简单的英语)std::flush
的工作原理吗?
谢谢。
答案 0 :(得分:100)
由于没有回答 std::flush
恰好是什么,这里有一些关于它实际是什么的细节。 std::flush
是操纵符,即具有特定签名的函数。首先,您可以考虑std::flush
具有签名
std::ostream& std::flush(std::ostream&);
现实情况稍微复杂一点(如果您感兴趣,也会在下面解释)。
流类重载输出运算符采用这种形式的运算符,即有一个成员函数将操纵符作为参数。输出操作符使用对象本身调用操纵器:
std::ostream& std::ostream::operator<< (std::ostream& (*manip)(std::ostream&)) {
(*manip)(*this);
return *this;
}
也就是说,当你将std::flush
输出到std::ostream
时,它只调用相应的函数,即以下两个语句是等价的:
std::cout << std::flush;
std::flush(std::cout);
现在,std::flush()
本身非常简单:它所做的就是调用std::ostream::flush()
,即你可以设想它的实现看起来像这样:
std::ostream& std::flush(std::ostream& out) {
out.flush();
return out;
}
std::ostream::flush()
函数在技术上调用与流关联的流缓冲区(如果有)上的std::streambuf::pubsync()
:流缓冲区负责缓冲字符并在使用时将字符发送到外部目标缓冲区将溢出或内部表示应与外部目标同步,即,当要刷新数据时。在与外部目标同步的顺序流上,意味着立即发送任何缓冲的字符。也就是说,使用std::flush
会导致流缓冲区刷新其输出缓冲区。例如,当数据写入控制台时,刷新会导致字符出现在控制台上的此位置。
这可能会引发一个问题:为什么不立即写字符?简单的答案是写字符通常很慢。但是,写入合理数量的字符所花费的时间与写入其中一个字符的时间基本相同。字符数量取决于操作系统,文件系统等的许多特征,但通常最多只有4k字符的内容与大约一个字符同时写入。因此,根据外部目标的详细信息,在使用缓冲区发送字符之前缓冲字符可以大大提高性能。
以上应该回答你的三个问题中的两个。剩下的问题是:你什么时候冲洗一条小溪?答案是:当字符应写入外部目的地时!这可能是在写文件结束时(关闭文件隐式刷新缓冲区)或者在请求用户输入之前(注意std::cout
从std::cin
读取std::cout
时自动刷新std::istream::tie()
1}} std::cin
'到std::flush
)。虽然可能有一些情况下您明确要刷新流,但我觉得它们相当罕见。
最后,我承诺全面了解char
实际上是什么:流是能够处理不同角色类型的类模板(实际上它们与wchar_t
和{{1}一起使用使它们与另一个角色一起工作是非常复杂的,尽管如果你真的确定可行的话。为了能够将std::flush
与流的所有实例一起使用,它恰好是一个带有如下签名的函数模板:
template <typename cT, typename Traits>
std::basic_ostream<cT, Traits>& std::flush(std::basic_ostream<cT, Traits>&);
当std::flush
立即使用std::basic_ostream
的实例化时,它并不重要:编译器会自动推断出模板参数。但是,如果没有提及此函数以及促进模板参数推导的内容,编译器将无法推导出模板参数。
答案 1 :(得分:25)
默认情况下,缓存std::cout
,并且只有在缓冲区已满或发生其他一些刷新情况时才打印实际输出(例如,流中的换行符)。有时您需要确保立即进行打印,并且需要手动刷新。
例如,假设您要通过打印单个点来报告进度报告:
for (;;)
{
perform_expensive_operation();
std::cout << '.';
std::flush(std::cout);
}
如果没有冲洗,你很长时间都不会看到输出。
请注意,std::endl
会在流中插入换行符并使其刷新。由于潮红是相当昂贵的,如果不明确需要冲洗,则不应过度使用std::endl
。
答案 2 :(得分:10)
这是一个简短的程序,你可以编写来观察正在做什么
#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;
}
运行此程序:您会注意到它打印第1行,暂停,然后打印第2行和第3行。现在删除刷新调用并再次运行程序 - 您将注意到程序暂停并且然后同时打印所有3行。在程序暂停之前缓冲第一行,但由于缓冲区从不刷新,因此直到第2行的endl调用才会输出第1行。
答案 3 :(得分:4)
流连接到某个东西。在标准输出的情况下,它可以是控制台/屏幕,也可以重定向到管道或文件。程序之间有很多代码,例如存储文件的硬盘。例如,操作系统正在处理任何文件,或者磁盘驱动器本身可能正在缓冲数据,以便能够以固定大小的块写入数据,或者只是为了提高效率。
当您刷新流时,它会告诉语言库,您希望将目前输出的任何字符的操作系统和硬件强制一直存储到存储中。从理论上讲,在“冲洗”之后,你可以将绳子踢出墙壁,这些角色仍然可以安全存放。
我应该提一下,编写操作系统驱动程序的人或设计磁盘驱动器的人可以免费使用'flush'作为建议,他们可能不会真正写出字符。即使输出关闭,它们也可能会等待一段时间来保存它们。 (请记住,os会同时执行各种操作,等待一两秒来处理字节可能会更有效。)
所以同花顺是一种检查点。
还有一个例子:如果输出进入控制台显示,则刷新将确保字符实际上完全到达用户可以看到的位置。当您期待键盘输入时,这是一件很重要的事情。如果您认为自己已经向控制台写了一个问题并且它仍然卡在某个内部缓冲区中,那么用户就不知道在答案中输入什么内容。因此,这是冲洗很重要的情况。