C++ 流与 C 风格的 IO?
Posted
技术标签:
【中文标题】C++ 流与 C 风格的 IO?【英文标题】:C++ Streams vs. C-style IO? 【发布时间】:2011-07-16 18:41:00 【问题描述】:当我注意到我正在使用 C 风格的操作来访问 IO(printf
、fopen
等)时,我正在为一个小型业余项目编写一些 C++。
在 C++ 项目中涉及 C 函数是否被认为是“不好的做法”?与 C 风格的 IO 访问相比,使用流有什么优势?
【问题讨论】:
【参考方案1】:这是一个热门话题。
有些人更喜欢使用 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 用于本地化项目符号。但是如何重新排序 printf 中的值? 我更新了我的帖子来解释如何重新排列printf
中的值(提示:%m$
)。
在我手头的任何合理的官方来源中,我没有找到任何关于在printf
中重新安排订单的参考。你从哪里得到这个%2$
的想法?
这可能是一个 GNU 扩展,我在我的 linux 机器上的 printf
的联机帮助页中找到了它。不过,这似乎确实适用于 Mac OS X。我不知道这是否是 C 标准所要求的。
@Sylvain Defresne:C 标准不需要它。我不认为它实际上是 C 标准允许的,尽管我不会费心查找它。它可能是一个 Gnu 扩展(Mac OSX 的 XCode 使用 gcc,尽管它们可能正在逐渐消失),但问题标题、正文或使用特定编译器的标签中没有任何提示。【参考方案2】:
printf
和朋友们与<iostream>
相比是非常不安全的,并且不能扩展,当然fopen
和朋友们没有 RAII,这意味着除非你用分析器证明你肯定需要性能差异(您已经证明存在于您的平台和代码中),您必须是printf
的白痴。
编辑:本地化是我没有考虑过的一件有趣的事情。我从未本地化过任何代码,也无法评论printf
和<iostream>
的相对本地化能力
【讨论】:
AFAIK,基本本地化是通过从程序中提取字符串文字(被翻译函数调用包围)并让人们翻译这些字符串来完成的。想象一下,必须从使用 cout 获得的句子片段中翻译和构建有意义的句子。 - 不过,Boost.Format 可以帮助您解决这个问题。【参考方案3】:对于一个小型业余项目,我可能会选择类型更安全的 C++ io 流。
有趣的是,我从未见过使用其中任何一个的重要现实项目。在所有情况下,我们都使用了一些构建在本机 OS API 之上的 IO 抽象。
【讨论】:
【参考方案4】:优势
类型安全:在编译时检查 C++ 流操作的参数类型,而printf
参数通过 ...
传递,如果它们与格式不匹配,则会导致未定义的行为。
资源管理:C++ 流对象有析构函数来关闭文件句柄、释放缓冲区以及你拥有的东西。 C 流要求您记得调用 fclose
。
缺点
性能:当然,这取决于实现,但我发现使用 C++ 流进行格式化比等效的printf
格式化要慢得多。
【讨论】:
【参考方案5】:如果有明确的目的,任何事情都不能被视为不好的做法。我的意思是,如果 IO 是程序的瓶颈,那么是的,C 风格的 IO 比 C++ IO 运行得更快。但如果不是,我会使用 C++ 流方法。因为它更可爱:)
【讨论】:
【参考方案6】:恕我直言,一个真正的 C++ 程序员试图以惯用的 C++ 方式做事;转换为 C 的程序员试图坚持旧的做事方式。它与可读性和一致性有关。
【讨论】:
【参考方案7】:首先,您不必先将 C++ 对象(尤其是 string
s)转换为与 C 兼容的形式。
【讨论】:
【参考方案8】:在 C++ 项目中涉及 C 函数是否被认为是“不好的做法”?
通常,文件IO代码应该封装在一个类或者函数实现中。我不会认为您在封装的实现中做出的任何选择都是“不好的做法”,我保留该术语来表示影响您的库或代码(即接口)用户的因素。如果您在接口中公开文件 IO 机制,那么,IMO,无论您使用 IO 流还是 C 风格的 IO 函数,都是不好的做法。
我宁愿说 C 风格的 IO 函数(可能总是)是更糟糕的选择。
与 C 风格的 IO 访问相比,使用流有哪些优势?
首先,它们可以更好地与标准 C++ 结构(例如 std::string
)集成。它们与 STL <algorithms>
集成得相当好。它们允许您创建封装的自定义读/写运算符(>),这样您的自定义类在执行 IO 操作时几乎看起来像原始类型。
最后,C++ 中的 IO 流可以使用异常机制来报告流中的错误或读/写操作。通过避免错误代码机制的可怕外观(if 语句序列和丑陋的 while 循环,在每次操作后检查错误代码),这些使文件 IO 代码变得更好。
【讨论】:
在这种情况下我不必担心,因为将 IO 代码封装到一个类中正是我所做的。 =)【参考方案9】:作为一般规则,您应该更喜欢 C++ 运算符,它们是:
-- 键入安全。您不会冒险通过双精度格式 需要一个 int。
-- 可扩展。您可以编写自己的插入器和 提取器,并使用它们。
-- 可扩展。您可以定义自己的操纵器(使用 应用程序特定的逻辑含义),并使用它们。如果你 想改变所有的 WidgitNumber 的格式 (内部,一个 int)在你的输出中,你改变 机械手;您不必查找所有格式 %d 是 WidgitNumber 的语句。
-- 可扩展。您可以编写自己的接收器和源,并且 这些可以转发到其他接收器和源,过滤或 根据需要扩展输入或输出。
(FWIW:我不认为我曾经写过一个应用程序 没有使用自定义 >> 和
【讨论】:
【参考方案10】:在 C++ 项目中涉及 C 函数是否被认为是“不好的做法”?
没有。 C 函数经常用于 C++ 项目中。例如,关于流,Google C++ Style Guide 建议仅在有限的情况下使用它们,例如“临时的、本地的、人类可读的以及针对其他开发人员而不是最终用户”。
与 C 风格的 IO 访问相比,使用流有哪些优势?
主要优点是类型安全和可扩展性。但是 C++ 流存在严重缺陷,请参阅 the answers to this question,例如本地化问题、错误报告差、代码膨胀和某些实现中的性能问题。
【讨论】:
实际上,Google C++ 风格指南不推荐使用 C 风格 I/O 而不是 C++ 风格 I/O。它说最好将替代库用于更复杂的用途,但在必要时使用流。它甚至从未提及 C I/O 系统 他们一定改变了这一点。我已经更新了答案,谢谢!以上是关于C++ 流与 C 风格的 IO?的主要内容,如果未能解决你的问题,请参考以下文章