编写高质量代码:改善C++程序的150个建议(十二)

建议23:尽量使用C++标准的iostream

  IO是我们最基本的需求之一。比如当我们进入C++世界时所接触的第一个程序HelloWorld,采用printf()或operator<<都可以。所以,我们会有如下的版本:

  1. //Version 1  
  2. #include < stdio.h > 
  3. int main()  
  4. {  
  5.      printf("Hello World");  
  6.      return 0;  
  7. }  
  8. //Version 2  
  9. #include < cstdio > 
  10. int main()  
  11. {  
  12.      std::printf("Hello World");  
  13.      return 0;  
  14. }  
  15. //Version 3  
  16. #include < iostream.h > 
  17. int main()  
  18. {  
  19.      cout<<"Hello World";  
  20.      return 0;  
  21. }  
  22. //Version 4  
  23. #include < iostream > 
  24. using namespace std;  
  25. int main()  
  26. {  
  27.      cout<<"Hello World";  
  28.      return 0;  
  29. }

  stdio.h、cstdio两个头文件中都有printf()的定义,而iostream.h和iostream中也都有operator<<的定义。是stdio.h 还是cstdio?是iostream.h还是iostream?是printf() 还是operator<<?这些都值得思考。

  关于File.h和File,这还得从C++标准库说起。C++标准程序库涵盖范围相当大,包含了许多好用的功能,所以,标准库与第三方提供程序库中的类型名称和函数名称发生名称冲突的可能性大大增加。为了避免这个问题的发生,标准委员会决定让标准库中的内容都披上std的外衣,放在std名空间中。但是这么做同时又带来了一个新的兼容性问题:很多C++程序代码依赖的都是没有用std包装的C++“准”标准库,例如iostream.h等,如果将原有的iostream.h贸然代替掉,那肯定会引起众多程序员的抗议。

  标准化委员会最后决定设计一种新的头文件名来解决这个问题。于是,他们把C++头文件File.h中的.h去掉,将File这样没有后缀的头文件名分配给那些用std包装过的组件使用;而旧有的File.h保持不变,仅仅在标准中声明不再支持它,顺势把问题丢给了广大厂商,堵住了那些老程序员抗议的嘴。同样,对C的头文件也做了相同的处理,在前面加上了一个字母c以示区分。因为C++标准还要遵守“对C兼容”这个契约,备受“歧视”的旧有的C头文件“侥幸存活”了下来。虽然标准化委员会选择抛弃那些旧有的C++头文件,但是各大厂商为了各自的商业利益,却依然选择了对旧有C++头文件的支持。

  因此就出现了类似stdio.h 和cstdio、iostream.h和iostream这样的双胞胎:

  1. // 标准化以前C++中的C标准库,标准C的头文件继续获得支持,这类文件的内容并  
  2. // 未放在std中  
  3. #include<stdio.h> 
  4. // 标准化后经过改造的C的标准库,C的标准库对应的新式C++版本,这类头文件的  
  5. // 内容也有幸穿上了std的外衣  
  6. #include<cstdio> 
  7. // 标准化以前的头文件,这些头文件的内容将不处于namespace std中  
  8. #include<iostream.h> 
  9. // 标准化以后的标准头文件,它提供了和旧有的头文件相同的功能,但它的内容都并  
  10. // 入了namespace std中,从而有效避免了名称污染的问题  
  11. #include<iostream>

  其实标准化以后标准程序库的改动并不只有这些,很多标准化的组件都被模板化了。

 接下来再说说printf() 和operator<<的问题。首先通过上面的讲解我们可以知道printf()函数继承自C标准库,而operator<<是标准C++所独享的。对于printf()函数,大家肯定很熟悉,它是可移植的、高效的,而且是灵活的;但是正如建议15中所说的那样,因为printf()、scanf()函数不具备类型安全检查,也不能扩充,所以并不完美;而C语言遗留的问题在C++的operator<<中得到了很好的解决,换句话说,printf()的缺点正是operator<<的长处。现在再去回顾建议15中提到的关于Student类型对象打印的问题,用operator<<就可以完美解决了:

  1. class Student  
  2. {  
  3. public:  
  4.      Student(string& name, int age, int scoer);  
  5.      ~ Student();  
  6. private:  
  7.    string m_name;  
  8.      int    m_age;  
  9.      int    m_scoer;  
  10. friend ostream& operator<<( ostream& s, const Student& p );  
  11. };  
  12.  
  13. ostream& operator<<( ostream& s, const Student& p )  
  14. {  
  15.      s<<p. m_name<<" "<<p.m_age<<" "<<p. m_scoer;  
  16.      return s;  
  17. }  
  18. // 调用operator<< 
  19. Student XiaoLi("LiLei", 23, 97);  
  20. cout<< XiaoLi;

  当然了,相比C++ iostream程序库中的类,C中的stream函数也并不是一无是处的:

  (1)一般认为C stream函数生成的可执行文件更小,有着更高的效率;Scott Meyers在《More Effective C++》的条款23中的测试也很好地证明了这一点;

  (2)C++ iostream程序库中的类会涉及对象构造、析构的问题,而C stream函数没有这些,所以不会像前者那样因为构造函数带来不必要的麻烦。

  (3)C stream函数有着更强的可移植能力。

  对于一般应用程序而言,这三条优点还不足以打动他们,抛弃C++ iostream程序库,转而投向C stream函数。

  请记住:

  C++ iostream程序库中的类与C stream函数虽然各有优点,但是一般推荐使用前者,因为类型安全与可扩充性对于我们更有吸引力,所以,建议使用#include< iostream >,而不是#include< stdio.h >、#include< cstdio >、#include< iostream.h >。

  建议24:尽量采用C++风格的强制转型

  在建议11中,我们详细讲述了强制转型存在的一些问题,并建议在代码编写过程中尽量避免使用这个招人讨厌的东西。然而,正如哲学中所讲的一样:存在的即是合理的。强制转型肯定具有它存在的意义。在某些情形下我们必须求助于这个“讨厌鬼”,以帮助我们更好地完成程序设计。

  比如,const属性的去除(请不要纠结于下面示例函数的“不良”设计):

  1. class CStudent{};  
  2. const CStudent* GetCertainStudent(const std::string& name)  
  3. {  
  4.      CStudent* p = new CStudent(name);  
  5.      return p;  
  6. }  
  7.  
  8. CStudent* p = GetCertainStudent("Li Lei");

 在VC++下编译,编译器会报错:

error C2440: "初始化": 无法从"const CStudent *"转换为"CStudent *"

  此时我们就只能求助于const_cast了:

CStudent* p = const_cast<CStudent*>(GetCertainStudent("Li Lei"));

  这里需要提醒的是,不要随意去除变量的const属性,除非是经过深思熟虑后不得不这样做。

  在C/C++编程中,新旧两种风格的强制转型同时存在。当强制转型已成为不可避免的定局时,安全性相对高的C++风格的强制转型更为可取。

  首先,新风格的强制转型不再像C风格的强制转型那样简单粗暴,在代码中它们更容易识别,更容易找到这些类型系统破坏者的藏匿之处。

  其次,新风格的强制转型针对性更强,它针对特定的目的进行了特别的设计。如果对这些特别设计的理解不是很清晰,请返回去看看建议11。这样能让程序员更清晰地了解强制转型的目的,同时使利用编译器诊断使用错误成为可能。

  请记住:

  如果实在不能避免,建议采用安全性较高的C++风格的强制转型形式。新风格更容易被注意,而且具有一定的针对性。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值