std :: flush如何工作?
有人可以请解释(最好使用纯英文)如何std::flush
作品?
- 它是什么?
- 你什么时候冲一个小溪?
- 它为什么如此重要?
谢谢。
由于没有回答什么 std::flush
发生,这里是一些细节,它是什么。 std::flush
是一个操作符,即具有特定签名的函数。 从简单的开始,你可以想到有签名的std::flush
std::ostream& std::flush(std::ostream&);
但是现实情况稍微复杂一点(如果你感兴趣,下面也会有解释)。
stream类重载输出操作符是以这种forms的操作符,即有一个以操作符为参数的成员函数。 输出操作符用对象本身调用操纵器:
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()
函数技术上在与stream关联的stream缓冲区(如果有)上调用std::streambuf::pubsync()
:stream缓冲区负责缓冲字符并将字符发送到外部目的地,当使用的缓冲区溢出或内部表示应与外部目的地同步,即数据将被刷新时。 在与外部目标同步的顺序stream中,仅意味着立即发送任何缓冲的字符。 也就是说,使用std::flush
会导致stream缓冲区刷新其输出缓冲区。 例如,将数据写入控制台时,刷新会导致字符出现在控制台上的此处。
这可能会提出一个问题:为什么不立即写入字符? 简单的答案是写字符一般相当慢。 但是,写出合理数量的字符所需的时间与写入一个字符的时间基本相同。 字符的数量取决于操作系统,文件系统等的许多特性,但是通常高达像4k字符的东西几乎与一个字符同时写入。 因此,在根据外部目的地的细节使用缓冲区发送之前缓冲字符可以是巨大的性能改进。
上面应该回答三个问题中的两个。 剩下的问题是:你什么时候冲一个stream? 答案是:当字符应该被写入到外部目的地! 虽然这可能是在编写文件(closures一个文件隐式刷新缓冲区)或立即要求用户input(注意std::cout
从std::cin
作为std::cout
读取时自动刷新是std::istream::tie()
'd到std::cin
)。 虽然可能有几次您明确想要刷新一个stream,但我发现它们相当罕见。
最后,我答应给出std::flush
实际上是什么的全貌:这些stream是类模板,能够处理不同的字符types(实际上它们使用char
和wchar_t
;使它们与另一个字符一起工作是相当复杂的,尽pipe如果你真的确定了可行的话)。 为了能够对所有的stream实例使用std::flush
,它恰好是一个带有如下签名的函数模板:
template <typename cT, typename Traits> std::basic_ostream<cT, Traits>& std::flush(std::basic_ostream<cT, Traits>&);
当使用std::flush
std::basic_ostream
的实例立即使用std::flush
,它并不重要:编译器会自动推导出模板参数。 但是,如果没有提及此函数以及有助于模板参数推导的情况,编译器将无法推导出模板参数。
默认情况下, std::cout
被caching,并且只有在缓冲区已满或其他某种冲突情况(例如stream中的换行符)出现时才会打印实际的输出。 有时你想确保打印立即发生,而且你需要手动冲洗。
例如,假设您想通过打印一个点来报告进度报告:
for (;;) { perform_expensive_operation(); std::cout << '.'; std::flush(std::cout); }
如果没有冲洗,很长一段时间你不会看到输出。
请注意, std::endl
插入一个换行符,并使其刷新。 由于冲洗是轻度昂贵, std::endl
不应该过度使用,如果没有明确的冲洗。
这里有一个简短的程序,你可以写出来观察什么刷新正在做
#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; }
运行这个程序:你会注意到它打印第一行,暂停,然后打印第二行和第三行。现在删除flush调用并再次运行程序 – 你会注意到程序暂停,然后打印所有3行同时。 第一行在程序暂停之前被缓冲,但是由于缓冲区永远不会被刷新,第1行不会被输出,直到第2行的endl呼叫。
stream连接到某个东西。 在标准输出的情况下,它可能是控制台/屏幕,或者可能被redirect到pipe道或文件。 程序和例如存储文件的硬盘之间有很多代码。 例如,操作系统正在做任何文件的东西,或者磁盘驱动器本身可能会缓冲数据,以便能够将其写入固定大小的块中,或者只是为了更高效。
当你刷新stream时,它会告诉语言库,操作系统和硬件,你到目前为止输出的任何字符都被强制存储。 理论上讲,在“冲水”之后,你可以将电源线从墙上踢出,这些angular色仍然可以安全地存储。
我应该提到,编写os驱动程序的人或者devise磁盘驱动器的人可以自由地使用“flush”作为一个build议,他们可能不会真正写出字符。 即使输出closures,他们也许会等一段时间来保存它们。 (请记住,os会一次处理所有的事情,等一两秒来处理你的字节可能更有效率。)
所以冲洗是一种检查点。
再举一个例子:如果输出到控制台显示,一个刷新将确保字符实际上完全出来到用户可以看到他们的地方。 当您期待键盘input时,这是一个重要的事情。 如果你认为你已经写了一个问题的控制台,它仍然卡在某个内部缓冲区,用户不知道该input什么答案。 所以,这是一个冲洗很重要的情况。