在新的C ++代码中,我倾向于使用C ++ iostream库而不是C stdio库。
我注意到一些程序员似乎坚持stdio,坚持认为它更便携。
这是真的吗?什么是更好用?
答案 0 :(得分:39)
回答原来的问题:
使用stdio可以完成的任何操作都可以使用iostream库完成。
Disadvantages of iostreams: verbose
Advantages of iostreams: easy to extend for new non POD types.
C ++制作C ++的步骤是类型安全。
iostreams被设计为显式类型安全。因此,对象的赋值也明确地检查了所分配对象的类型(在编译器时)(如果需要,则生成编译时错误)。因此,防止运行时内存溢出或将浮点值写入char对象等。
scanf()/ printf()和family依赖程序员获取格式字符串正确并且没有类型检查(我相信gcc有一个帮助的扩展名)。因此,它是许多错误的根源(因为程序员在分析中不如编译器完善[不会说编译器比人类更好完美])。
只是为了澄清Colin Jensen的评论。
澄清Mikael Jansson的评论。
N.B。我同意iostream库有点冗长。但我愿意忍受冗长以确保运行时安全。但我们可以使用Boost Format Library来缓解详细程度。
#include <iostream>
#include <iomanip>
#include <boost/format.hpp>
struct X
{ // this structure reverse engineered from
// example provided by 'Mikael Jansson' in order to make this a running example
char* name;
double mean;
int sample_count;
};
int main()
{
X stats[] = {{"Plop",5.6,2}};
// nonsense output, just to exemplify
// stdio version
fprintf(stderr, "at %p/%s: mean value %.3f of %4d samples\n",
stats, stats->name, stats->mean, stats->sample_count);
// iostream
std::cerr << "at " << (void*)stats << "/" << stats->name
<< ": mean value " << std::fixed << std::setprecision(3) << stats->mean
<< " of " << std::setw(4) << std::setfill(' ') << stats->sample_count
<< " samples\n";
// iostream with boost::format
std::cerr << boost::format("at %p/%s: mean value %.3f of %4d samples\n")
% stats % stats->name % stats->mean % stats->sample_count;
}
答案 1 :(得分:16)
这太冗长了。
考虑使用iostream构造来执行以下操作(类似于scanf):
// nonsense output, just to examplify
fprintf(stderr, "at %p/%s: mean value %.3f of %4d samples\n",
stats, stats->name, stats->mean, stats->sample_count);
这需要以下内容:
std::cerr << "at " << static_cast<void*>(stats) << "/" << stats->name
<< ": mean value " << std::precision(3) << stats->mean
<< " of " << std::width(4) << std::fill(' ') << stats->sample_count
<< " samples " << std::endl;
字符串格式化是一种情况,面向对象可以而且应该回避有利于嵌入字符串的格式化DSL。考虑Lisp的format
,Python的printf样式格式,或PHP,Bash,Perl,Ruby及其字符串插值。
iostream
充其量是错误的。
答案 2 :(得分:14)
Boost Format Library为printf样式的字符串格式化提供了一种类型安全,面向对象的替代方案,并且是iostream的补充,由于巧妙地使用了operator%,因此不会出现通常的冗长问题。如果您不喜欢使用iostream的运算符&lt;&lt;
进行格式化,我建议您考虑使用普通的C打印。答案 3 :(得分:9)
回到过去的糟糕时期,C ++标准委员会一直在使用语言,而iostreams是一个不断变化的目标。如果您使用了iostream,那么您每年都有机会重写部分代码。因此,我总是使用自1989年以来没有显着变化的stdio。
如果我今天做的话,我会使用iostreams。
答案 4 :(得分:6)
如果像我一样,你在学习C ++之前学过C,那么stdio库看起来更自然。 iostream vs. stdio有利有弊,但在使用iostream时我确实错过了printf()。
答案 5 :(得分:5)
原则上我会使用iostreams,在实践中我做了太多的格式化小数等,这使得iostreams太难以理解,所以我使用了stdio。 Boost :: format是一种改进,但对我来说还不够激励。实际上,stdio几乎是类型安全的,因为大多数现代编译器都会进行参数检查。
这是一个我对任何解决方案都不满意的领域。
答案 6 :(得分:4)
对于二进制IO,我倾向于使用stdio的fread和fwrite。对于格式化的东西,我通常会使用IO Stream,虽然正如Mikael所说,非trival(非默认?)格式可以是PITA。
答案 7 :(得分:4)
我将比较C ++标准库中的两个主流库。
有几个原因可以阻止它们的使用:
不仅印刷品本身不好。软件变老并被重构和修改,并且可能从远程位置引入错误。假设你有
// foo.h
...
float foo;
...
某处...
// bar/frob/42/icetea.cpp
...
scanf ("%f", &foo);
...
三年后你发现foo应该是一些自定义类型......
// foo.h
...
FixedPoint foo;
...
但某处...
// bar/frob/42/icetea.cpp
...
scanf ("%f", &foo);
...
...那么你的旧printf / scanf仍然会编译,除了你现在得到随机段错误而你不记得原因。
如果您认为printf()不那么详细,那么您有可能不会使用其iostream的全部力量。例如:
printf ("My Matrix: %f %f %f %f\n"
" %f %f %f %f\n"
" %f %f %f %f\n"
" %f %f %f %f\n",
mat(0,0), mat(0,1), mat(0,2), mat(0,3),
mat(1,0), mat(1,1), mat(1,2), mat(1,3),
mat(2,0), mat(2,1), mat(2,2), mat(2,3),
mat(3,0), mat(3,1), mat(3,2), mat(3,3));
将其与使用iostreams进行比较:
cout << mat << '\n';
您必须为运营商定义适当的重载&lt;&lt;它大致具有printf-thingy的结构,但显着的区别在于你现在有一些可重复使用和类型安全的东西;当然你也可以为printf-like制作一些可重复使用的东西,但是你再次使用printf(如果你用新的FixedPoint
替换矩阵成员怎么办?),除了其他非平凡的东西,例如你必须传递FILE *手柄。
请注意,格式字符串通常被认为是国际化的救援,但在这方面它们并不比iostream好:
printf ("Guten Morgen, Sie sind %f Meter groß und haben %d Kinder",
someFloat, someInt);
printf ("Good morning, you have %d children and your height is %f meters",
someFloat, someInt); // Note: Position changed.
// ^^ not the best example, but different languages have generally different
// order of "variables"
即,旧式C格式字符串缺少与iostream相同的位置信息。
您可能需要考虑boost::format,它支持明确说明格式字符串中的位置。从他们的例子部分:
cout << format("%1% %2% %3% %2% %1% \n") % "11" % "22" % "333"; // 'simple' style.
一些printf实现提供位置参数,但它们是非标准的。
除了表现(如Jan Hudec所指出的),我没有理由。但请记住:
“我们应该忘记小的效率,大约97%的时间说:过早的优化是所有邪恶的根源。然而,我们不应该放弃那个关键的3%的机会。一个好的程序员不会因为这样的推理而自满,他会明智地仔细研究关键代码;但只有在识别出代码之后“ - Knuth
和
“瓶颈发生在令人惊讶的地方,所以不要试图再次猜测并加速攻击,直到你证明这是瓶颈所在。” - 派克
是的,printf实现通常比iostream通常比boost :: format更快(来自我编写的一个小而具体的基准测试,但它应该在很大程度上取决于具体情况:如果printf = 100%,那么iostream = 160%,并且boost :: format = 220%)
但是不要盲目地忽略它的思考:你真正花费多少时间进行文本处理?退出前你的程序运行了多长时间? 是否与C风格的字符串相关,松散的类型安全性,降低可重构性, 增加非常微妙的错误的可能性,这些错误可能会隐藏自己多年,并且可能只能正确显示自己 进入你最喜欢的顾客面对?
就个人而言,如果我不能获得超过20%的加速,我就不会退缩。但是因为我的申请 几乎所有的时间花在除了字符串处理之外的其他任务上,我从来没有这么做过。一些解析器 我写的几乎所有的时间花在字符串处理上,但它们的总运行时间非常小 它不值得进行测试和验证。
最后,我想预设一些谜语:
查找所有错误,因为编译器不会(他只能建议他是否好):
shared_ptr<float> f(new float);
fscanf (stdout, "%u %s %f", f)
如果没有别的,这个怎么了?
const char *output = "in total, the thing is 50%"
"feature complete";
printf (output);
答案 8 :(得分:3)
虽然C ++ iostreams API有很多好处,但一个重要的问题是i18n。问题是参数替换的顺序可以根据文化而变化。经典的例子是:
// i18n UNSAFE
std::cout << "Dear " << name.given << ' ' << name.family << std::endl;
虽然这适用于英语,但在中文中,姓氏是第一位的。
在为国外市场翻译代码时,翻译片段充满了危险,因此新的l10ns可能需要更改代码而不仅仅是不同的字符串。
boost :: format似乎结合了最好的stdio(单个格式字符串,可以使用不同顺序的参数然后出现)和iostreams(类型安全性,可扩展性)。
答案 9 :(得分:2)
我使用iostreams,主要是因为这样可以让以后更容易摆弄流(如果我需要的话)。例如,您可能会发现要在某个跟踪窗口中显示输出 - 这对cout和cerr来说相对容易。当然,你可以在unix上摆弄管道和东西,但这不是那么便携。
我喜欢类似printf的格式,所以我通常首先格式化一个字符串,然后将其发送到缓冲区。使用Qt,我经常使用QString::sprintf(尽管他们建议使用QString::arg代替)。我也看过boost.format,但是不能真正习惯语法(太多%了)。不过,我应该看看它。
答案 10 :(得分:2)
我想念的iolibraries是格式化的输入。
iostreams没有很好的方法来复制scanf(),甚至boost也没有输入所需的扩展名。
答案 11 :(得分:1)
stdio更适合读取二进制文件(例如将块释放到向量&lt; unsigned char&gt;并使用.resize()等)。有关示例,请参阅http://nuwen.net/libnuwen.html中file.hh中的read_rest函数。
当读取二进制文件导致虚假的eof时,C ++流可能会扼杀大量字节。
答案 12 :(得分:1)
由于iostream已成为标准,因此您应该使用它们,因为您知道您的代码可以使用较新版本的编译器。我想现在大多数编译器都非常了解iostreams,使用它们应该没有任何问题。
但是如果你想坚持使用* printf函数,我认为没有任何问题。