Fer*_*cio 33 c++ iostream stdio
在新的C++代码中,我倾向于使用C++ iostream库而不是C stdio库.
我注意到一些程序员似乎坚持stdio,坚持认为它更便携.
这是真的吗?有什么好用的?
Mar*_*ork 39
回答原始问题:
可以使用iostream库完成使用stdio完成的任何操作.
Disadvantages of iostreams: verbose
Advantages of iostreams: easy to extend for new non POD types.
Run Code Online (Sandbox Code Playgroud)
通过C制作的C++的前进是类型安全.
iostreams被设计为明确类型安全.因此,对象的赋值也明确地检查了所分配对象的类型(在编译器时)(如果需要,则生成编译时错误).从而防止运行时内存溢出或将浮点值写入char对象等.
另一方面,scanf()/ printf()和family依赖于程序员正确的格式字符串,并且没有类型检查(我相信gcc有一个帮助的扩展).因此,它是许多错误的根源(因为程序员的分析不如编译器完美[不会说编译器完美只比人类更好]).
只是为了澄清Colin Jensen的评论.
澄清Mikael Jansson的评论.
NB我同意iostream库有点冗长.但我愿意忍受冗长以确保运行时安全.但是我们可以通过使用Boost格式库来减轻冗长.
#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;
}
Run Code Online (Sandbox Code Playgroud)
Mik*_*son 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);
Run Code Online (Sandbox Code Playgroud)
这将需要类似的东西:
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;
Run Code Online (Sandbox Code Playgroud)
字符串格式化是一种情况,面向对象可以而且应该回避有利于嵌入字符串的格式化DSL.考虑Lisp format
,Python的printf样式格式,或PHP,Bash,Perl,Ruby及其字符串插值.
iostream
因为这个用例充其量是错误的.
回到过去的糟糕时期,C++标准委员会一直在使用该语言,而iostreams则是一个不断变化的目标.如果您使用了iostream,那么您每年都有机会重写部分代码.因此,我总是使用自1989年以来没有显着变化的stdio.
如果我今天做的事情,我会使用iostreams.
原则上我会使用iostreams,在实践中我做了太多格式化的小数等,使得iostreams太难以理解,所以我使用了stdio.Boost :: format是一种改进,但对我来说还不够激励.在实践中,stdio几乎是类型安全的,因为大多数现代编译器无论如何都会进行参数检查.
这是一个我仍然不满意任何解决方案的领域.
我将比较 C++ 标准库中的两个主流库。
\n\n限制使用它们有几个原因:
\n\n不仅仅是 printf 本身不好。软件会变旧并被重构和修改,并且错误可能会从远程位置引入。假设你有
\n\n。
\n\n// foo.h\n...\nfloat foo;\n...\n
Run Code Online (Sandbox Code Playgroud)\n\n和某个地方......
\n\n// bar/frob/42/icetea.cpp\n...\nscanf ("%f", &foo);\n...\n
Run Code Online (Sandbox Code Playgroud)\n\n三年后,您发现 foo 应该是某种自定义类型......
\n\n// foo.h\n...\nFixedPoint foo;\n...\n
Run Code Online (Sandbox Code Playgroud)\n\n但在某个地方...
\n\n// bar/frob/42/icetea.cpp\n...\nscanf ("%f", &foo);\n...\n
Run Code Online (Sandbox Code Playgroud)\n\n...那么你的旧 printf/scanf 仍然会编译,除了你现在得到随机段错误并且你不记得为什么。
\n\n如果您认为 printf() 不太冗长,那么您很可能没有充分利用其 iostream。例子:
\n\n printf ("My Matrix: %f %f %f %f\\n"\n " %f %f %f %f\\n"\n " %f %f %f %f\\n"\n " %f %f %f %f\\n",\n mat(0,0), mat(0,1), mat(0,2), mat(0,3), \n mat(1,0), mat(1,1), mat(1,2), mat(1,3), \n mat(2,0), mat(2,1), mat(2,2), mat(2,3), \n mat(3,0), mat(3,1), mat(3,2), mat(3,3));\n
Run Code Online (Sandbox Code Playgroud)\n\n将其与正确使用 iostream 进行比较:
\n\ncout << mat << \'\\n\';\n
Run Code Online (Sandbox Code Playgroud)\n\n您必须为operator<< 定义适当的重载,它大致具有printf-thingy 的结构,但显着的区别是您现在拥有可重用且类型安全的东西;当然,你也可以为 printf 之类的东西重用,但是你又拥有了 printf (如果你用新的替换矩阵成员怎么办FixedPoint
?),除了其他重要的事情,例如你必须传递 FILE* 句柄。
请注意,格式字符串通常被认为是国际化的救援,但在这方面它们并不比 iostream 更好:
\n\nprintf ("Guten Morgen, Sie sind %f Meter gro\xc3\x9f und haben %d Kinder", \n someFloat, someInt);\n\nprintf ("Good morning, you have %d children and your height is %f meters",\n someFloat, someInt); // Note: Position changed.\n\n// ^^ not the best example, but different languages have generally different\n// order of "variables"\n
Run Code Online (Sandbox Code Playgroud)\n\n即,旧式 C 格式字符串与 iostream 一样缺乏位置信息。
\n\n您可能需要考虑boost::format,它支持显式声明格式字符串中的位置。从他们的示例部分:
\n\ncout << format("%1% %2% %3% %2% %1% \\n") % "11" % "22" % "333"; // \'simple\' style.\n
Run Code Online (Sandbox Code Playgroud)\n\n一些 printf 实现提供位置参数,但它们是非标准的。
\n\n除了性能(正如扬·邬达克所指出的)之外,我看不出有什么原因。但请记住:
\n\n\n\n\n\xe2\x80\x9c我们应该忘记小的效率,大约 97% 的时间:过早的优化是万恶之源。然而,我们不应该放弃这关键的 3% 的机会。一个好的程序员不会因为这样的推理而沾沾自喜,他会明智地仔细查看关键代码;但只有在该代码被识别之后\xe2\x80\x9d - Knuth
\n
和
\n\n\n\n\n\xe2\x80\x9c瓶颈出现在令人惊讶的地方,因此在证明瓶颈所在之前,不要尝试再次猜测并进行速度黑客攻击。\xe2\x80\x9d - Pike
\n
是的, printf 实现通常比 iostream 更快,通常比 boost::format 更快(来自我编写的一个小型且具体的基准测试,但这在很大程度上应该取决于具体情况:如果 printf=100%,则 iostream=160% ,并且 boost::format=220%)
\n\n但不要盲目地忽略这一点:你真正花了多少时间在文本处理上?你的程序在退出之前运行了多长时间?\n是否与回退到 C 样式格式字符串、松散类型安全性、降低可重构性、\n增加非常微妙的错误的可能性有关,这些错误可能会隐藏多年,并且可能只会暴露自己对\n您最喜欢的客户面孔吗?
\n\n就我个人而言,如果我不能获得超过 20% 的加速,我就不会退缩。但因为我的应用程序几乎将所有时间都花在字符串处理之外的其他任务上,所以我从来不需要这样做。我编写的一些解析器几乎将所有时间都花在字符串处理上,但它们的总运行时间是如此之小,以至于不值得进行测试和验证工作。
\n\n最后,我想预设一些谜语:
\n\n找到所有错误,因为编译器不会(他只能在他友善的情况下提出建议):
\n\nshared_ptr<float> f(new float);\nfscanf (stdout, "%u %s %f", f)\n
Run Code Online (Sandbox Code Playgroud)\n\n如果不出意外的话,这个有什么问题吗?
\n\nconst char *output = "in total, the thing is 50%"\n "feature complete";\nprintf (output);\n
Run Code Online (Sandbox Code Playgroud)\n