免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
楼主: CRLF
打印 上一主题 下一主题

[C++] 考考大家对C++输出流的熟悉程度(附录:google c++编程风格指南、c++解答) [复制链接]

论坛徽章:
0
21 [报告]
发表于 2009-02-09 13:41 |只看该作者

Google C++编程风格指南

流(Streams):只在记录日志时使用流。
  
定义
流是printf()和scanf()的替代。

优点
有了流,在输出时不需要关心对象的类型,不用担心格式化字符串与参数列表不匹配(虽然在gcc中使用printf也不存在这个问题),打开、关闭对应文件时,流可以自动构造、析构。

缺点
流使得pread()等功能函数很难执行,如果不使用printf之类的函数而是使用流很难对格式进行操作(尤其是常用的格式字符串%.*s),流不支持字符串操作符重新定序(%1s),而这一点对国际化很有用。

结论
不要使用流,除非是日志接口需要,使用printf之类的代替。使用流还有很多利弊,代码一致性胜过一切,不要在代码中使用流。

拓展讨论
对这一条规则存在一些争论,这儿给出深层次原因。回忆唯一性原则(Only One Way):我们希望在任何时候都只使用一种确定的I/O类型,使代码在所有I/O处保持一致。因此,我们不希望用户来决定是使用流还是printf + read/write,我们应该决定到底用哪一种方式。把日志作为例外是因为流非常适合这么做,也有一定的历史原因。

使用流易造成的错误
流的支持者们主张流是不二之选,但观点并不是那么清晰有力,他们所指出流的所有优势也正是其劣势所在。流最大的优势是在输出时不需要关心输出对象的类型,这是一个亮点,也是一个不足:很容易用错类型,而编译器不会报警。使用流时容易造成的一类错误是:

  1. cout << this; // Prints the address
  2. cout << *this; // Prints the contents
复制代码

编译器不会报错,因为<<被重载,就因为这一点我们反对使用操作符重载。

可读性比较
有人说printf的格式化丑陋不堪、易读性差,但流也好不到哪儿去。看看下面两段代码吧,哪个更加易读?

  1. cerr << "Error connecting to '" << foo->bar()->hostname.first
  2. << ":" << foo->bar()->hostname.second << ": " << strerror(errno);
复制代码

  1. fprintf(stderr, "Error connecting to '%s:%u: %s",
  2. foo->bar()->hostname.first, foo->bar()->hostname.second, strerror(errno));
复制代码

你可能会说,“把流封装一下就会比较好了”,这儿可以,其他地方呢?而且不要忘了,我们的目标是使语言尽可能小,而不是添加一些别人需要学习的新的内容。

总结
每一种方式都是各有利弊,“没有最好,只有更好”,简单化的教条告诫我们必须从中选择其一,最后的多数决定是printf + read/write。

[ 本帖最后由 CRLF 于 2009-2-9 13:46 编辑 ]

论坛徽章:
0
22 [报告]
发表于 2009-02-09 14:03 |只看该作者
原帖由 die 于 2009-2-9 02:00 发表
不过,记得某牛人说,某种语言或库里有一样东西,让大家觉得很难用,
这就是语言或库的一个设计BUG。

同意!

原帖由 reiase 于 2009-2-9 10:22 发表
C的printf被其他语言借鉴很多次,C++的流基本没见有语言借鉴过

老大真是观察入微啊!


  1. C版本的printf
  2. printf("%04d == 0123", 123)
复制代码


  1. C#版本的printf
  2. Console.Writeline("{0:0000} == 0123",   123);
复制代码


C#确实借鉴了C,两者的思想是一致的,只不过标记不一样。

[ 本帖最后由 CRLF 于 2009-2-9 14:11 编辑 ]

论坛徽章:
0
23 [报告]
发表于 2009-02-09 14:23 |只看该作者
C++是个很丑陋的语言

论坛徽章:
0
24 [报告]
发表于 2009-02-09 14:24 |只看该作者

回复 #1 CRLF 的帖子

C++是个很丑陋的语言,建议不要浪费时间去研究它了。

论坛徽章:
0
25 [报告]
发表于 2009-02-09 18:33 |只看该作者
c++的确有些不对称,  可也不能以偏概全.

论坛徽章:
0
26 [报告]
发表于 2009-02-09 19:12 |只看该作者
原帖由 CRLF 于 2009-2-9 00:57 发表
如果有人熟悉C++的格式化输出,请把相同功能的c++也贴出来吧(谢谢),我刚学c++不久,还不是很了解。

  1. #include <iostream>
  2. #include <iomanip>
  3. using namespace std;

  4. int main()
  5. {
  6.     for (int i = 0; i < 4; ++i)
  7.         cout << setw(4) << i << '|';
  8.     cout << '\n';

  9.     cout << left;
  10.     for (int i = 0; i < 4; ++i)
  11.         cout << setw(4) << i << '|';
  12.     cout << '\n';
  13.     cout << right;

  14.     cout << setfill('0');
  15.     for (int i = 0; i < 4; ++i)
  16.         cout << setw(4) << i << '|';
  17.     cout << '\n';
  18.     cout << setfill(' ');

  19.     cout << '\n';

  20.     const float f = 3.14f;

  21.     cout << fixed;

  22.     cout << setprecision(4);
  23.     for (int i = 0; i < 4; ++i)
  24.         cout << f << '|';
  25.     cout << '\n';

  26.     for (int i = 0; i < 4; ++i)
  27.         cout << setw(8) << f << '|';
  28.     cout << '\n';

  29.     cout << left;
  30.     for (int i = 0; i < 4; ++i)
  31.         cout << setw(8) << f << '|';
  32.     cout << '\n';
  33.     cout << right;

  34.     cout.unsetf(ios_base::floatfield);
  35. }
复制代码

论坛徽章:
0
27 [报告]
发表于 2009-02-09 20:27 |只看该作者
先谢谢tyc的解答!

为了代码简洁,tyc使用了using namespace std导入了全部符号,但是有些标识符(比如left、right等)实在是太容易和程序自己定义的局部变量名冲突了,(是否使用全限定又是另外一个话题了)。所以,实际中要是使用cout完成这样的任务,还要加上std::的限定符,如下:

  1. int main()
  2. {
  3.     for (int i = 0; i < 4; ++i)
  4.         std::cout << std::setw(4) << i << '|';
  5.     std::cout << '\n';
复制代码

这样一来代码看上去要比tyc的解答还要繁嗦些。

而且c++的using也不是很实用,比如说在这个例子中,需要:

  1. using std::cout;
  2. using std::endl;
  3. using std::setw;
  4. using std::setprecision;
  5. using std::left;
  6. using std::right;
  7. using std::setfill;
复制代码

论坛徽章:
0
28 [报告]
发表于 2009-02-09 20:37 |只看该作者
看了tyc的解答后,我就感到后悔了,应该这么出的:

  1. printf("%04d, %8.3f", 123,  3.14);
复制代码

论坛徽章:
0
29 [报告]
发表于 2009-02-09 21:11 |只看该作者

回复 #27 CRLF 的帖子

1. 避免与标准库中的标识符冲突应该是程序员的责任
2. 需要时,可以对某些标识符带上限定符来解决命名冲突

论坛徽章:
0
30 [报告]
发表于 2009-02-09 21:20 |只看该作者
原帖由 tyc611 于 2009-2-9 21:11 发表
1. 避免与标准库中的标识符冲突应该是程序员的责任
2. 需要时,可以对某些标识符带上限定符来解决命名冲突

个人感觉std库把一些常常用于局部变量的名字都给占掉了,比如说:vector、list、left、right、find、count,因此在实际的工作中用"using namespace std"这样的方式有很大的几率引起命名冲突,而用using std::cout这样一个个的导入更不现实(要导入的实在太多了),所以只好使用全限定了。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP