C++编程准则(3)

41. 避免用预处理器。可以用常量来代替值,用内联函数代替宏。

42. 保持范围尽可能地小,这样我们的对象的可见性和生命周期也就尽可能地小。这就减少了错用对象和隐藏难以发现的错误的可能性。

43. 避免使用全局变量。尽可能把数据放在类中。全局函数的存在可能性要比全局变量大。一个全局函数作为一个类的静态成员更合适。

44. 如果我们需要声明一个来自库中的类或函数,应该包含一个头文件的方法。而不要用一个不完全类型指定的方法,如:创建一个函数来写道ostream中:

Class ostream。

应该是:#include <iostream>

当我们创建我们自己的类的时候,在只需用到指针的情况下,如果一个库很大,应该给用户提供一个头文件的简写形式,文件中包含又不完全的类型说明(就是类型名声明),它可以提高编译器的速度。

45. 当选择重载运算符的返回值类型时,要考虑表达式连成一串时可能出现的情况:当定义operator=时,应记住x=x。要对左值返回一个拷贝或一个引用,这样才能用在串连表达式中。

46. 当写一个函数时,我们的第一选择是用const引用来传递参数。只要我们不需要修改正在被传递进入的对象,这种方式是最好的。因为它有着传值方式的简单,但不需要费时的构造和析构来产生局部对象,而这在传值方式时是不可避免的。通常我们在设计和构建我们的系统时不用注意效率问题,但养成这种习惯仍是件好事。

47. 当心临时变量。当调整效率时,要注意临时创建的对象,尤其是用运算符重载时。如果我们的构造函数和析构函数很复杂,创建和销毁临时对象就很费时。当从一个函数返回一个值时,总是应在return语句中调用析构函数来“就地”产生一个对象。return MyType(i,j);这优于 MyTypex(i,j); return x;

48. 当产生构造函数时,要考虑到异常情况,在最好的情况下,构造函数只是抛出异常,其次是:类只从健壮的类被组合和继承,所以当抛出异常时它们会自动清除它们所作的一切。如果我们必须使用裸指针,我们应该负责捕获自己的异常,然后在我们的构造函数抛出异常以前释放所有指针指向的资源。如果一个构造函数无法避免失败,最好的方法是抛出异常。

49. 在我们的构造函数中只做一些最必要的事情,这不仅使构造函数的调用有较低的时间花费,而且我们的构造函数更少地抛出异常和引用问题。

50. 析构函数的作用是释放在对象的整个生命期内分配的所有资源,而不仅仅是在创建期间。

51. 使用异常层次,最好从标准C++异常层次中继承,并作为公共类嵌入能抛出异常的类中。捕获异常的人然后可以确定异常的类型。如果我们加上新的派生异常,已存在的客户代码还是通过基类来捕获这个异常。

52. 用值来抛出异常,用引用来捕获异常。让异常处理机制处理内存管理。如果我们抛出一个指向在堆上产生的异常的指针,则捕获者必须破坏这个异常,这事一种不利的耦合。如果我们用值来捕获异常,我们需要额外的构造和析构,更糟的是,我们的异常对象的派生部分可能在以值向上类型转换时被切片。

53. 除非确有必要,否则不要写自己的类模板。先查看一个标准模板库,然后查问创建特殊工具的开发商。当我们熟悉了这些产品后,我们就可大大提高我们的生产效率。

54. 当创建模板时,留心那些带类型的代码并把它们放在非模板的基类中,以防不必要的代码膨胀。用继承或组合,我们可以产生自己的模板,模板中包含的大量代码都应是必要的,类型相关的。

55. 不要用<cstdio>函数,例如printf()。学会用输入输出流来代替,他们是安全和可扩展类型,而且功能也更强。我们在这上面花费的时间肯定不会白费。一般情况下都要尽可能用C++中的库而不要用C库。

56. 不要用C的内部数据类型,虽然C++为了向后兼容仍然支持他们,但他们不像C++的类那样强壮,所以这会增加我们查找错误的时间。

57. 无论何时,如果我们用一个内部数据类型作为一个全局或自动变量,在我们可以初始化他们之前不要定义他们。每一行定义一个变量,并同时对它初始化。当定义指针时,把"*"紧靠在类型的名字一边。如果我们每个变量占一行,我们就可以很安全地定义他们。

58. 保证在所有代码前面初始化。在构造函数初始化表中完成所有成员的初始化,甚至包括内部数据类型(也是使用伪构造函数)。在初始化子对象时用构造函数初始化表常常更有效;否则调用了默认构造函数,而不再调用使初始化正确的其他成员函数(如:operator=)。

59. 不要用“MyType a = b”的形式来定义一个对象。这是常常引起混乱的原因。因为它调用构造函数来代替operator=。为了清除起见,可以用“MyType a(b)”来代替。这个语句结果是一样的,但不会引起混乱。(虽然,这两种都是调用拷贝构造函数来完成新建对象的)

60. 使用C++显示类型转换。类型转换重载了正常的类型系统,它往往是潜在的错误点。通过把C中一个类型转换负责一切的情况改为多种表达清楚的转换,任何人来调试和维护这些代码时,都可以很容易发现这些最容易发生逻辑错误的地方。

61. 为了使一个程序更健壮,每个组件都必须是很强壮的。在我们创建的类中运用C++中提供的所有工具:隐藏实现、异常、常量更正(避免用预处理器,用常量代替值)、类型检查等等。用这些方法我们可以再构造系统时安全地转移到下一个抽象层次。

62. 建立常量更正。这允许编译器指出一些非常细微且难以发现的错误。这需要一定训练,而且要在类中协调使用,但这是完全值得的。

63. 充分利用编译器的错误检查功能,用完全警告方式编译我们的全部代码,修改我们代码,直到消除所有的警告为止。在我们的代码中宁可犯编译错误也不要犯运行错误(比如不要用变参数列表,这会使所有类型检查无效)。用assert来调试,对运行时错误要进行异常处理。

64. 宁可犯编译错误也不要犯运行错误。处理错误的代码离出错点越近越好。尽量就地处理错误而不要抛出异常。用最近的异常处理器处理所有的异常,这里它有足够的信息处理它们。在当前层次上处理我们能解决的异常,如果解决不了,重新抛出这个异常。

65. 如果一个析构函数调用了任何函数,这些函数都可能抛出异常。一个析构函数不能抛出异常(这会导致terminate()调用,它指出一个程序设计错误)。所以,任何调用了其他函数的析构函数都应该捕获和管理它自己的异常。

66. 不要自己创建私有数据成员名字“修饰”,除非我们有许多已在的全局值,否则让类和名字空间来为我们做这些事。

67. 注意重载,一个函数不应该用某一个参数的值来决定执行哪段代码,如果遇到这种情况,应该产生两个或多个重载函数来代替。

68. 把指针隐藏在容器中。只有当我们要对它们执行一个立即可以完成的操作时才把他们带出来。指针已成为出错的一大来源,当用new运算符时,应该试着把结果指针放在一个容器中。让容器拥有它的指针,这样它就会负责清楚它们。更好的办法是把指针打包进来类中。如果我们仍想让它看起来想一个指针,就重载operator->和operator*,如果我们必须有一个游离状态的指针,记住初始化它,最好是指向一个对象的地址,必要时让它等于0。当我们删除它时把它置为0,以防意外的多次删除。

69. 不要重载全局new和delete,可以在类的基础上去重载它们。重载全局new和delete会影响整个客户程序员的项目,有些事只能由项目的创建者来控制。档位类重载new和delete时,不要假定我们知道对象的大小,有些人可能是从我们的类中继承的。用提供的参数的方法。如果我们做任何特殊的事,要考虑到他可能对继承者产生的影响。

70. 防止对象切片。实际上以值向上类型转换到一个对象毫无意义。为了防止这样,在我们的基类中放入一些纯虚函数。

 71、如果我们用异常说明,用set_unexpected()函数安装我们自己的unexpected()函数。我们的unexpected()应该记录这个错误并重新抛出当前这个异常。这样的话,如果一个已存在的函数被重写并且开始引起异常时,我们可以获得记录,从而修改调用代码处理异常。

72、建立一个用户定义的terminate()函数(指出一个程序员的错误)来记录引起异常的错误,然后释放系统资源,并退出程序。

73、有时简单的集中会很管用。一个航空公司的“旅客舒适系统”有一系列相互五官的因素组成:座位、空调、电视等,而我们需要在一家飞机上创建许多这样的东西。我们要创建私有成员并建立一个全部的接口吗?不,在这种情况下组建本身也是公开接口的一部分,所以我们应该创建公共成员对象。这些对象有它们自己的私有实现,所以,也是很安全的。注意简单的集合不是常用的方法,但也会用到。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值