我应该切换到C ++ I / O流吗?

时间:2010-12-02 20:27:41

标签: c++ iostream

我从来没有太多使用C ++ I / O流,并且总是选择我所知道的。 即printf函数。

我知道使用I / O流有一些好处,但我正在寻找一些提示 来自stackoverflow社区帮助我(或说服我)切换。因为我还是 更喜欢printf,我认为printf样式更容易阅读,更快速地输入。

即使我仍然继续使用printf,我仍然希望熟悉它。


编辑。有趣的是,谷歌C ++编码风格禁止使用除日志记录之外的流。 请参阅:http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml

  

     

仅将流用于记录。定义:流是一个   替换printf()和scanf()。

     

优点:使用流,您无需知道对象的类型   正在打印。您没有格式字符串的问题   匹配参数列表。 (虽然使用gcc,你没有那个   printf的问题。)Streams有自动构造函数和   打开和关闭相关文件的析构函数。

     

缺点:Streams使得难以执行pread()等功能。一些   格式化(特别是常见格式字符串idiom%。* s)是   如果不是不可能有效地使用没有流的流,很难   使用类似printf的hacks。 Streams不支持运营商重新排序   (%1s指令),这有助于国际化。

     

决策:除日志记录要求外,不要使用流   接口。改为使用类似printf的例程。

     

使用流有各种各样的利弊,但在这种情况下,as   在许多其他情况下,一致性胜过辩论。不要使用流   在你的代码中。

     

扩展讨论

     

关于这个问题一直存在争议,所以这解释了其中的推理   更深入。回想一下唯一的指导原则:我们想要   确保每当我们执行某种类型的I / O时,代码都会显示出来   在所有这些地方都一样。因此,我们不想允许   用户在使用流或使用printf plus之间做出决定   读/写/等。相反,我们应该选择其中一个。我们创造   日志记录的一个例外,因为它是一个非常专业的   申请,并且由于历史原因。

     

溪流的支持者认为溪流是显而易见的选择   两个,但问题实际上并不那么清楚。每一个   他们指出的流的优势,有一个等价物   坏处。最大的优点是你不需要知道   要打印的对象的类型。这是一个公平的观点。但,   有一个缺点:你可以轻松使用错误的类型,和   编译器不会警告你。很容易犯这种错误   不知道何时使用流。

cout << this;  // Prints the address 
cout << *this;  // Prints the contents 
     

编译器不会生成错误,因为&lt;&lt;一直都是   超载。出于这个原因,我们不鼓励超载。

     

有人说printf格式很丑陋,很难读,但是流是   往往没有更好的。考虑以下两个片段,两者都有   同样的错字。哪个更容易发现?

cerr << "Error connecting to '" << foo->bar()->hostname.first
     << ":" << foo->bar()->hostname.second << ": " << strerror(errno);
fprintf(stderr, "Error connecting to '%s:%u: %s",
      foo->bar()->hostname.first, foo->bar()->hostname.second,
      strerror(errno)); 
     对于您可能提出的任何问题,等等等等。 (你可以争辩说,“事情会发生   做得更好   右包装,“但如果一个方案都是如此,那么它也不是真的   为了另一个?此外,请记住,目标是制作语言   更小,而不是添加更多需要学习的机器。)

     

这两种方式都会产生不同的优点和缺点,并且   没有一个明显优越的解决方案。简单主义   我们要求我们解决其中一个问题,并作出多数决定   是在printf +读/写。

8 个答案:

答案 0 :(得分:8)

使用boost :: format。它是两全其美的。

答案 1 :(得分:7)

iostream提供的巨大优势是安全性。 printf()本质上是不安全的函数。不只是这样,但重载&lt;&lt;&lt;&lt;&lt;&lt;对于你自己的类型,而实际上不可能扩展printf() - 这具有额外的优势,即即时重载输出到文件,以及连接到流的任何其他内容。这些组合使得printf()在高质量代码中无法使用。我也注意到完全没有性能差异,虽然我看到很多人发布他们的速度有多慢。

答案 2 :(得分:7)

我自己不是溪流的大用户,所以我只会列出我对它们的看法。这是非常主观的,如果我的答案被投票删除,我会理解。

  • 我喜欢:同质性

我可能有一个enum,一个class或其他任何内容,使我的用户定义类型可打印始终通过在我的类型旁边提供相同的operator<<来完成:

std::ostream &operator<<(std::ostream &, const MyType &);

您可能会问自己如果类型是可打印的,但从不 它是如何打印的。

  • 我喜欢:抽象

显然,向用户定义的类型提供“流媒体容量”非常容易。能够提供我们自己的流实现并使其透明地适合现有代码也是一件很棒的事。一旦适当地定义了operator<<,写入标准输出,内存缓冲区或文件就可以轻易改变。

  • 我不喜欢:格式化

我一直认为iomanip是一团糟。我讨厌写一些东西(我只是在这里抛出随机操纵器):

std::cout << std::left << std::fixed << std::setprecision(0) << f << std::endl;

我认为使用printf会更容易,但Boost.Format在这里很有帮助。

答案 3 :(得分:5)

@Matt,我和你在一起。我一直讨厌溪流。当然,我可以使用它们。让他们做我想做的任何事情。但我喜欢printf,因为我更喜欢语法。

我甚至编写了一个与strprintf完全相同的sprintf,除了返回std::string而不是写入char缓冲区。

但渐渐地,不情愿地,我几乎完全停止使用sprintf。因为,简单来说,我写了太多该死的错误,我厌倦了经历过同样的错误时间和错误。再一次。 stringstream类型的安全性使我免于自己。

我正在谈论的错误有两种形式,主要是:

  1. 我为输出缓冲区选择了错误的幻数。假设我想出char buf_[256]来格式化一些东西。好吧,就像比尔盖茨着名的评论“256KB的记忆应该对任何人都足够”,我的错误在低端足以引起我的注意。另一方面,我该怎么办? char buf_[1024*64]?极端,但你明白了。没有完美的幻数。你要么让自己暴露在更多的崩溃中,要么浪费你的记忆。

  2. sprintf - 编了一个字符串,但发送了一个浮点数。一直这样做。好吧,不是所有的时间。对于sprintf的每100个电话,我可能会这样做一次或两次。对于生产代码,这很多。

  3. 有了这些流,这些都不会发生。所以我现在使用流,我的代码永远不会崩溃。好吧......无论如何。

    有人会说流比sprintf慢。呃,也许吧。为了争论,我甚至会赞同它。虽然没关系。我在实时股票市场服务器上工作,这些服务器整天每天处理300万条消息。而且我从来没有对流的速度有任何问题。也许它有点慢,但我有更大的鱼要炒。

答案 4 :(得分:3)

您无法使用新格式说明符扩展printf来处理您自己的类型。

答案 5 :(得分:2)

使用C ++ I / O流可以更好地防止类型错误,但它们很慢。所以它主要取决于表现的重要性。

I / O流也是多态的,与C stdio函数不同,流的用户不需要知道它是否连接到控制台,文件或其他逻辑。但这对大多数应用来说并不重要。

答案 6 :(得分:1)

在我10年前学习C ++之后,我改用了iostreams。几年前,我确信这是一个糟糕的选择,所以我转回去了。我没有后悔切换。对于一个优秀的C ++程序员,您需要了解两者。

Google C ++样式指南更喜欢在iostream上使用printf / sprint / snprint。它说只使用iostream进行记录。

就iostreams的好处而言:

  • 类型安全。编译器(gcc)可以检测类型错误,以及所有静态分析工具。即使存在类型错误,也可以从打印输出中轻松发现错误。
  • 扩展即可。是的iostreams有超载但所有数据成员最终都会转到POD类型。
  • 没有缓冲区溢出。使用snprintf克服缓冲区大小问题。

现在获得sprintf的好处:

  • 更好的可读性。 "Record(%d): %s\n"os << "Record(" << i << ") " << msg << endl;
  • 更容易阅读
  • 性能。如果你正在做很多iostreams的东西,那么改变它们显着可以提高性能。我曾经在一个使用stringstream将int / double转换为字符串的库上工作。我用sprintf替换了性能并且改进了很多(转换例程有很多调用)。为了记录,boost :: format的性能更差。

我的结论是尽量使用iostreams。我用它来读取内存缓冲区,或偶尔写入内存缓冲区。对于其他工作,我使用普通的C函数。

答案 7 :(得分:0)

为了跟进诺亚的答案,boost的格式库允许使用几种不同的语法,包括类似printf的语法。它通过重载%运算符来工作,所以起初看起来有点奇怪。但是,我更喜欢它,它允许你重用参数。例如

cout << format("%1% %2% %1%") % 1 % 2 << endl;

将打印

1 2 1

它非常多才多艺,如果你能适应各地的百分号,那么两全其美。我同意,printf风格通常要容易得多。

http://beta.boost.org/doc/libs/1_43_0/libs/format/doc/format.html