为什么我们需要将std :: cin和std :: cout绑定在一起?

27

默认情况下,标准输入设备与标准输出设备以以下形式绑定在一起:std::cin.tie(&std::cout); 这保证了在调用输入之前刷新了输出缓冲区。因此,我尝试使用std::cin.tie(0)来解除它们的绑定,但似乎结果与绑定的情况没有任何区别。

#include<iostream>
using namespace std;

int main(int argc, char *argv[])
{
    char c;

    cin.tie(0)

    cout << "Please enter c:";
    cin >> c;
    cout << c ;

    return 0;
}

我是不是测试错了?为什么我们需要把它们绑在一起?它们共享同一个缓冲区吗?


2
领带(tie)的理念是在读取输入之前显示提示符(prompt)。 :-) - Bo Persson
3个回答

29

你的例子没有问题(除了在 cin.tie(0) 行后应添加分号), iostream 对象的工作方式也没有问题。

tie() 简单地保证在执行输入操作 cin 之前刷新 cout。这对于用户在被要求回答问题之前看到问题很有用。

但是,如果你将 cincout 解绑(un-tie()),就不能保证 cout 的缓冲区已经被刷新。但也不能保证缓冲区未被刷新。事实上,如果计算机有足够的资源,它会立即刷新 cout 缓冲区,所以这会在 cin 请求输入之前发生。这就是你的例子中的情况。

因此,一切都运行正常,只是在 cin.tie(0) 之后,不能保证刷新操作会发生。然而,在99%的情况下,刷新仍然会发生(但不再保证)。

理论上,如果被绑定,cincout 可以共享同一个缓冲区。但是,我认为没有实现这样做的原因之一是这两个可能会被解绑。


2
小问题:这个绑定只保证当(底层的streambufcin需要向环境请求更多输入时,cout才会刷新。如果cin的输入缓冲区包含足够的数据来满足读取请求,则不会进行刷新。 - user1084944
4
第二个小问题:样例代码中cout被刷新的原因并不是关于“资源”的,而是因为默认情况下启用了与stdio的同步,因此刷新是按照stdio的策略进行的。在(交互式)Windows环境中,默认情况下stdio输出流是完全未缓冲的,因此输出会立即发生。(Linux在这种情况下更喜欢行缓冲,所以除非缓冲区非常小且提示填满它,否则样例代码不会被刷新。) - user1084944

9

我认为之前的回答是错误的(我很惊讶它为什么会被投票和标记为正确,显然不是这样)。

要打破 happens-before 链接,您应该(仅针对标准输入/输出流)(1) 移除与stdio的同步 并且 (2) 解开流的绑定。

就像这样:

std::cin.tie (nullptr);
std::cout.sync_with_stdio(false);
std::cout << "Please enter c: ";
std::cin >> c;

那么您就可以确保没有解开的流。与stdio同步是一种特殊能力,以便对于C风格和C++风格的输入和输出具有适当的发生顺序,并且我强烈建议您在没有真正必要的情况下不要删除它。


执行您的代码后仍然会得到“请输入c:”的提示。 - Itachi Uchiwa
请检查您的 std::cout 是否已正确缓冲。如果未缓冲,则会立即打印任何字符。 - Konstantin Vladimirov
我该怎么做? - Itachi Uchiwa
基本上,rdbuf 应该返回非空值,并且 flags 中不应设置 unitbuf 标志。 - Konstantin Vladimirov

2
在C语言中,提示用户输入的模式是先发出一个打印提示消息的命令,然后再发出一个输入值的命令。
为了使这个过程正常工作,您需要确保提示消息实际上被显示出来。环境被配置成自动完成这个过程:
  • 在DOS(或Windows命令行)环境中,STDOUT是无缓冲的,因此打印命令会立即显示消息。
  • 在*NIX环境中,STDOUT是行缓冲的。因此,如果按照*NIX的风格在提示消息中包含换行符,以便用户在下一行输入,您的提示将在输入之前自动显示。
流绑定特性使得自动刷新的行为成为iostream库工作方式的一部分,而不是惯例的产物。(尽管要注意,只有当程序请求系统获取更多输入时,自动刷新才会发生,因此存在一些边缘情况,cin >> c可能不会真正刷新)。

然而!默认情况下,iostream库与stdio库是同步的。实际上,这意味着iostream根本不进行任何缓冲;它只是将数据转发到底层的C库。

因此,这意味着您看到的是如果编写类似C程序时通常会看到的内容。如果您在Windows命令行环境中,则输出不会被缓冲,因此在输入之前您会看到提示。

要查看本机C++行为,您需要通过在程序开头运行std::cout.sync_with_stdio(false);来关闭同步,如其他答案所示。

如果您这样做,那么cout语句将不会刷新输出缓冲区(除非缓冲区非常小)。由于您已经解除了绑定,因此cin语句也不会刷新它。因此,您将得到在看到提示之前必须输入用户输入的结果。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接