C ++ Streams与C风格的IO?

时间:2011-03-16 17:01:11

标签: c++ io printf iostream

当我注意到我正在使用C风格的操作来访问IO(printffopen等)时,我正在为一个小型业余爱好项目编写一些C ++。

在C ++项目中涉及C函数是否被视为“不良做法”?使用流而不是C风格的IO访问有什么好处?

10 个答案:

答案 0 :(得分:39)

这是一个热门话题。

有些人更喜欢使用C ++ IO,因为它们是类型安全的(你不能在对象的类型和格式字符串中指定的类型之间存在分歧),并且使用C ++的其余部分更自然地流动编码方式。

但是,还有C IO功能的参数(我个人的最爱)。其中一些是:

  • 它们更容易与本地化集成,因为整个字符串本地化不会在较小的字符串中分解,并且通过一些实现,本地化程序可以重新排序插入值的顺序,将它们移动到字符串中,...
  • 您可以直接看到要写入的文本的格式(对于流操作符来说这可能非常困难)。
  • 由于没有内联,只有printf函数的一个实例,生成的代码更小(这在嵌入式环境中很重要)。
  • 在某些实现中比C ++函数更快。

就个人而言,我不认为在C ++代码中使用C流是不好的做法。 Some organisations甚至建议在C ++流上使用它们。我认为糟糕的风格是在同一个项目中使用两者。一致性是我认为的关键。


正如其他人所说,在一个相对较大的项目中,你可能不会直接使用它们,但你会使用一组最适合你的编码标准和你的需求的包装函数(或类)(本地化,类型安全,...)。您可以使用一个或另一个IO接口来实现此更高级别的接口,但您可能只使用一个。


编辑:添加一些有关printf格式化函数系列与本地化相关的优势的信息。请注意,这些信息仅适用于某些实施。

您可以使用%m$代替%来引用索引参数,而不是按顺序引用它们。这可用于重新排序格式化字符串中的值。以下程序将在标准输出上写Hello World!

#include <stdio.h>
int main() {
    printf("%2$s %1$s\n", "World!", "Hello");
    return 0;
}

考虑翻译这个C ++代码:

if (nb_files_deleted == 1)
    stream << "One file ";
else
    stream << nb_file_deleted << " files ";
stream << removed from directory \"" << directory << "\"\n";

这可能真的很难。使用printf(以及像gettext这样的库来处理本地化),代码不会与字符串混合。因此,我们可以将字符串传递给本地化团队,如果在某种语言中存在特殊情况,则不必更新代码(在某种语言中,如果对象的数量为0,则使用复数形式,在其他语言中,有三种形式,一种是单数形式,一种是有两种物体,一种是复数形式,......)。

printf (ngettext ("One file removed from directory \"%2$s\"",
                  "%1$d files removed from directory \"%2$s\"",
                  n),
        n, dir);

答案 1 :(得分:8)

printf和朋友相比<iostream>显然不安全,无法延长,当然加fopen而且朋友没有RAII,这意味着除非你用分析器,你肯定需要性能差异(你已证明存在于你的平台和代码中),你必须是printf的白痴。

编辑:本地化是一件有趣的事情,我没有考虑过。我从未对任何代码进行本地化,也无法评论printf<iostream>的相对本地化能力

答案 2 :(得分:6)

对于一个小型的爱好项目,我可能会使用更安全的C ++ io流。

有趣的是,我从来没有见过使用其中任何一个的非平凡的现实生活项目。在所有情况下,我们都使用了一些基于本机OS API构建的抽象来实现IO。

答案 3 :(得分:5)

如果它有明确的目的,那么什么都不能被认为是不好的做法。我的意思是,如果IO是程序的瓶颈,那么是的,C风格的IO比C ++ IO工作得更快。但如果不是,我会采用C ++流方法。因为它是可爱的:)

答案 4 :(得分:5)

<强>优点

  • 类型安全:在编译时检查C ++流操作的参数类型,而printf参数通过...传递,如果它们与格式不匹配则导致未定义的行为。
  • 资源管理:C ++流对象有析构函数来关闭文件句柄,空闲缓冲区以及你有什么。 C流要求您记得致电fclose

<强>缺点

  • 性能:当然,这取决于实现,但我发现使用C ++流的格式要比等效的printf格式慢得多。

答案 5 :(得分:4)

恕我直言,一个真正的C ++程序员试图用惯用的C ++方式做事; C转换程序员试图坚持旧的做事方式。它与可读性和一致性有关。

答案 6 :(得分:3)

首先,您不必首先将C ++对象(尤其是string s)转换为C兼容表单。

答案 7 :(得分:3)

  
    

在C ++项目中涉及C函数是否被视为“不良做法”?

  

通常,文件IO代码应封装在类或函数实现中。我不认为你在封装实现中做出的任何选择都是“不好的做法”,我保留这个术语来影响你的库或代码的用户(即界面)。如果您在接口中公开文件IO机制,那么,IMO,使用IO流或C风格的IO函数是不好的做法。

我宁愿说C风格的IO功能(可能总是)是更糟糕的选择。

  
    

使用流而不是C风格的IO访问有什么好处?

  

首先,它们更好地与标准C ++构造集成,例如std::string。它们与STL <algorithms>很好地整合。它们允许您创建封装的自定义读/写运算符(&lt;&lt;&gt;&gt;),这样在执行IO操作时,您的自定义类几乎看起来像原始类型。

最后,C ++中的IO流可以使用异常机制来报告流中的错误或读/写操作。这些使得文件IO代码更好,避免了错误代码机制的糟糕外观(if语句的序列和丑陋的while循环,在每次操作后检查错误代码)。

答案 8 :(得分:3)

作为一般规则,您应该更喜欢C ++运算符,它们是:

- 输入安全。你不冒险将格式传递给double     要求int。

- 可扩展。你可以编写自己的插入器和     extracters,并使用它们。

- 可扩展。你可以定义自己的操纵器(用     应用程序特定的逻辑含义),并使用它们。如果你     想要改变所有WidgitNumber的格式     (在内部,一个int)输出中,你改变了     手;你不必找到所有的格式     %d是WidgitNumber的语句。

- 可扩展。您可以编写自己的接收器和源,以及     这些可以转发到其他接收器和源,过滤或     根据需要扩展输入或输出。

(FWIW:我认为我从来没有写过一个应用程序 没有使用自定义&gt;&gt;和&lt;&lt;操作员,自定义操纵器和 自定义streambuf。)

答案 9 :(得分:3)

在C ++项目中涉及C函数是否被视为“不良做法”?

没有。 C函数通常用于C ++项目中。例如,关于流,Google C++ Style Guide建议仅在有限的情况下使用它们,例如“ad-hoc,本地,人类可读,并针对其他开发人员而非最终用户”。

使用流而不是C风格的IO访问有什么好处?

主要优点是类型安全性和可扩展性。但是,C ++流存在严重缺陷,请参阅the answers to this question,例如本地化问题,错误报告错误,代码膨胀以及某些实现中的性能问题。