当我注意到我正在使用C风格的操作来访问IO(printf
,fopen
等)时,我正在为一个小型业余爱好项目编写一些C ++。
在C ++项目中涉及C函数是否被视为“不良做法”?使用流而不是C风格的IO访问有什么好处?
答案 0 :(得分:39)
这是一个热门话题。
有些人更喜欢使用C ++ IO,因为它们是类型安全的(你不能在对象的类型和格式字符串中指定的类型之间存在分歧),并且使用C ++的其余部分更自然地流动编码方式。
但是,还有C IO功能的参数(我个人的最爱)。其中一些是:
printf
函数的一个实例,生成的代码更小(这在嵌入式环境中很重要)。就个人而言,我不认为在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)
<强>优点强>
printf
参数通过...
传递,如果它们与格式不匹配则导致未定义的行为。fclose
。<强>缺点强>
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,例如本地化问题,错误报告错误,代码膨胀以及某些实现中的性能问题。