繁体   English   中英

为什么C ++中的流?

[英]Why the streams in C++?

众所周知,有些库使用iostreamfstream等流。

我的问题是:

  • 为什么流? 为什么他们不坚持类似于printfgets等功能(例如)?

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

printf("Hello World!");

对我来说比我更具可读性和逻辑性

cout << "Hello World";

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

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

例如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"));

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

void outputFoo(std::ostream& os)
{
  os << "Foo!";
}

int main()
{
  outputFoo(std::cout);

  std::ofstream outputFile("foo.txt");
  outputFoo(outputFile);

  MyNetworkStream outputSocket;
  outputFoo(outputSocket);
}

同样对于输入流。

在输入和输出对象时,Streams也有优势。 如果你想用scanf读取一个对象会发生什么?

MyObject obj;
scanf("??", &obj); // What specifier to use? 

即使有适当的说明符, scanf如何知道如何填写对象的成员? 使用C ++流,您可以重载operator<<和write

MyObject obj;
std::cin >> obj;

它会奏效。 类似地,对于std::cout << obj ,您可以在一个地方编写对象序列化代码,而不必在其他任何地方担心它。

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

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

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

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

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

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

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

流可以链接在一起

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

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

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;

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

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

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

那些东西需要修复。 嘿,在早期,虚函数和运算符重载都很糟糕 看起来很酷。 当然,图书馆想要使用这些功能。

IOStreams库是以下之间的妥协:

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

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

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

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

VS

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

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

其实

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

对我来说似乎更直观

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

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

在任一情况下,

print "Test: " + test

(多国语言, 包括Python :()使得很多更有意义:)

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

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

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

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

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

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

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

Streams使用模板,而printf/scanf则不使用。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM