为什么C ++中的流?

时间:2010-04-30 16:58:20

标签: c++ stream

众所周知,有些图书馆使用iostreamfstream等流。

我的问题是:

  • 为什么要流?为什么他们不坚持使用与printfgets等相似的功能(例如)?

他们需要自己的运算符<<>>,但他们所做的一切都可以用上面的简单函数实现,也可以用函数

printf("Hello World!");

对我来说比

更具可读性和逻辑性
cout << "Hello World";

我还认为C ++中的所有字符串抽象都编译为二进制的(效率较低的)标准函数调用。

14 个答案:

答案 0 :(得分:24)

Streams具有更好的类型安全性。

例如printf("%s", a);如果a是整数,则可能会出现严重错误。 cout << a;没有这个问题。

另一个问题是流更好地符合面向对象的设计方法。

例如,您有一个简单的应用程序写入一些输出,然后您希望输出转到文件而不是控制台。使用C来电,您必须将对printf的所有来电替换为对fprintf的来电,并注意保持FILE*。使用流只需更改您正在使用的流的具体类,就是这样,大多数代码都保持相同:

void doSomething(ostream& output)
{
   output << "this and that" << a;
}

doSomething(cout);
doSomething(ofstream("c:\file.txt"));

答案 1 :(得分:9)

首先,它允许您利用C ++对象模型来创建不关心它们是写入标准输出,文件还是网络套接字的函数(如果您有一个派生自{的网络套接字{1}})。 E.g。

ostream

类似于输入流。

Streams在输入和输出对象方面也有优势。如果您想使用void outputFoo(std::ostream& os) { os << "Foo!"; } int main() { outputFoo(std::cout); std::ofstream outputFile("foo.txt"); outputFoo(outputFile); MyNetworkStream outputSocket; outputFoo(outputSocket); } 读取对象会发生什么?

scanf

即使有适当的说明符,MyObject obj; scanf("??", &obj); // What specifier to use? 如何知道如何填写对象的成员?使用C ++流,您可以重载scanf并写入

operator<<

它会起作用。类似地,对于MyObject obj; std::cin >> obj; ,您可以在一个地方编写对象序列化代码,而不必在其他任何地方担心它。

答案 2 :(得分:7)

C ++流是类型安全的。在C中,如果你说:

double d = 1.23;
printf( "%d", d );

即使转换不正确,也不能保证收到错误消息。

您似乎在问非常基本的C ++问题。你使用哪种C ++教科书不包括它们?

答案 3 :(得分:5)

printf对一个人来说不是类型安全的。 cout界面也更通用,这允许printf版本无法使用很多东西。一个很好的例子是你的类型的流操作符,如果你正确的话,将std :: ostream称为流,而不是cout。因此,您只需使用不同的流即可更改输出的目标。要使用printf执行此操作,您必须执行大量依赖于平台的输出句柄覆盖。

我还认为C ++中的所有字符串抽象都编译为二进制的(效率较低的)标准函数调用。

想想你想要的一切。在你真正测试你的假设之前,你的意见不会太重。此外,您需要考虑抽象的丢失,这在现代软件开发中通常更为重要。

答案 4 :(得分:3)

除了类型安全和多态之外,流更便携。在某些系统上,带有long的printf需要“%d”,而在某些系统上则需要“%ld”。

答案 5 :(得分:2)

Streams可以链接在一起

cout << "hello" << " " << "world"

答案 6 :(得分:2)

流的另一个好处是它们可以扩展。使用流,您可以让用户定义的类像内置类型一样工作:

class foo { ... };

ostream &operator<<(ostream &ostr, const foo &f)
{
    ostr << ... how you want to print a foo ...;
    return ostr;
}

现在你可以像其他任何东西一样打印foo:

int n = ...
foo f = ...

cout << n << ": " << f << endl;

答案 7 :(得分:2)

C ++ IOStreams 非常低效(在我所知的大多数实现中)。通常,这不是一个问题,但当它时,库基本上是无用的。这也是一个很好的观点,语法不直观(而且非常非常冗长)。图书馆很复杂,不必要地难以扩展。它也不是很灵活。与STL之类的东西形成鲜明对比的是,IOStreams真的看起来像个糟糕的梦。但它就在这里,我们坚持下去。

它在这里的原因,以及它看起来的原因,是它是在C ++成熟语言之前开发的。在我们有数十年的经验告诉我们什么是和不是好的图书馆设计之前。在任何人真正知道选项是什么之前。

C ++需要一个比C更好的I / O库。在某些重要的方面,C ++ IOStreams 更好。正如其他人所提到的,它们是类型安全的和可扩展的。通过实现单个运算符,我可以打印出用户定义的类。 printf无法做到这一点。我也不必担心格式说明符错误,并且由于缺乏类型安全性而打印出垃圾。

需要修复所需的。嘿,在早期,虚函数和运算符重载是 the shit 。看起来很酷。当然,图书馆想要使用这些功能。

IOStreams库是以下之间的妥协:

  • 比C stdio.h
  • 更安全,更具扩展性
  • 高效的东西
  • 精心设计和直观的东西
  • 在C ++标准化时实际存在的库。 (他们必须添加某些东西,所以他们必须在当时实际存在的候选人之间做出选择。)

图书馆没有实现所有这些,我相信今天,凭借我们几十年的语言经验,我们可以设计出更好的图书馆。但是在90年代中期,当他们正在寻找要添加的I / O库时,这是他们能找到的最好的。

答案 8 :(得分:1)

带有operator<<operator>>的流式语法允许很好的连接。

printf("%d%d%d%d%d", a, b, c, d, e);

VS

cout << a << b << c << d << e;

此外,在第一种方法中,必须始终考虑类型,而在流方法中,不得指定类型。

答案 9 :(得分:1)

实际上

cout << "Test: " << test << endl;

对我来说似乎比

更直观
printf("Test: %d\n", test);

如果你以前从未像现在这样,那么你就无法知道那是什么了。

在任何一种情况下,

print "Test: " + test

(在几种语言中,包括Python :()使更多更有意义:)

答案 10 :(得分:1)

C ++允许你使用printf。所以如果你喜欢它,请继续使用它。

Streams比将输出写入控制台要多得多。它们是一种通用数据缓冲解决方案,可应用于从屏幕输出到文件处理,网络流量和输入设备接口等各种应用。你的东西可以写入(或读取)流,而无需关心数据实际发生的位置。

假设您有一个复杂的对象,您希望能够将其写入输出控制台,日志文件以及调试窗口弹出窗口。你打算写三个几乎完全相同的函数,或者你要编写一个传递(y)输出流的函数?

答案 11 :(得分:0)

我仍然使用printf,主要是因为它很容易控制输出格式。

对我来说,类型安全并不是一个主要的好处,因为它可以很容易地被测试捕获,因为基于控制台的程序比基于UI或基于Web的应用程序更容易测试。如果你不做测试,更严重的错误可以通过任何方式检查编译时间。

我也不同意索赔流因互换性而更灵活的另一个原因。它相当于建议使用fprintf(fout,...)进行互换。如果需要重定向输出,请使用管道。如果你在一个库中,为什么不只是返回一个字符串并让调用者决定它去哪里?

答案 12 :(得分:0)

stringstreamsnprintf / sscanf更安全,因为它完全避免了缓冲区溢出的可能性(即使是“优雅的失败”)。

答案 13 :(得分:0)

Streams使用模板,而printf/scanf没有。