编程准则

  1. 不要用C++主动重写我们已有的C代码,除非我们需要对它的功能做较大的调整。用C++重新编译是很有价值的,可以发现隐藏的错误。把一段运行得很好的C代码用C++重写可能实在浪费时间,除非C++的版本以类的形式提供许多重用的机会。
  2. 要区别类的创建者和类的使用者。类的使用者才是“顾客”,他们并不需要或许也不想知道类的内部是怎样运作的。类的创建者必须是设计类和编写类的专家,以使得被创建的类可以被最没有经验的程序员使用,而且在应用程序中工作良好。库只是在透明的情况下才会容易使用。
  3. 当创建一个类时,要尽可能用有意义的名字来命名类。我们的目标应该是使用户接口要领简单。可以用函数重载和缺省参数来创建一个清楚,易用的接口。
  4. 数据隐藏允许类的创建者将来在不破坏用户代码的情况下随心所欲地修改代码。为实现这一点,应把对象的成员尽可能定义为private,而只让接口部分为public,而且总是使用函数而不是数据。只有在迫不得已时才让数据为public。如果类的使用者不需要调用某个函数,就让这个函数称为private。如果类的一部分要让派生类可见,就定义为protected,并提供一个函数接口而不是直接暴露数据,这样,实现部分的改变将对派生类产生最小的影响。
  5. 不要陷入分析瘫痪之中。有些东西只有在编程时,才能学到并使各种系统正常。C++有内建的防火墙,让它们为我们服务。在类或一组类中的错误不会破坏整个系统的完整性。
  6. 分析和设计至少要在系统中创建类,它们的公共接口,它们与其他类的关系,特殊的基类。如果我们的方法产生的东西比这些更多,就应当问问自己,是不是所有的成分在程序的整个生命期中都是由价值的,如果不是,将会增加对它们的维护开销。
  7. 记住软件工程的基本原则:所有的问题都可以通过引进一个额外的间接层来简化。这是抽象方法的基础,而抽象是面向对象编程的首要特征。
  8. 使类尽可能地原子化。也就是每个类有一个单一,清楚的目的。如果类或者设计的系统过于复杂,就应当将所有复杂的类分解成多个简单的类。
  9. 从设计的角度,寻找并区分哪些变化和不变的成分。也就是在系统中寻找那些修改时不需要重新设计的成分,把它们封装到一个类中。
  10. 注意不同点。两个语义上不同的对象可能有同样的操作或反应,自然就会试着把一个作为另一个的子类以便利用继承性的好处。这就叫差异,但并没有充分的理由来强制这种并不存在的父子关系。一个好的解决办法是产生一个共同的父类:它包含两个子类——这可能要躲占一点空间,但可以从继承中获益,并且可能对这种自然语言的解有一个重要发现。
  11. 注意在继承过程中的限制。最清晰的设计是被继承者加入新的功能,而如果在继承过程中删除了原有功能,而不是加入新功能,那这个设计就值得怀疑了。但这也不是绝对的,如果正在和一个老的类库打交道,对已有的类在子类中进行限制可能更有效,而不必重建一套类层次来使新类适应新的应用。
  12. 不要用子类去扩展基类的功能。如果一个接口部分很关键的话,应当把它放在基类中,而不是在继承中加入。如果我们正在用继承来添加成员函数,应该重新重新考虑设计。
  13. 一个类一开始时接口部分应尽可能小而精。在类使用过程中,会发现需要扩展类的接口。然而一个类一旦投入使用,要想介绍接口部分,就会影响那些使用了该类的代码,但如果我们需要增加函数则不会有影响,只需要重新编译一下即可。但即使用新的成员函数取代了原来的功能,也不要去改正原有接口(如果愿意的话,可以在底层将两个函数合并)。如果需要对一个已有的函数增加参数,可以让原来的参数保持不变,把所有新参数作为缺省参数,这样不会妨碍对该函数已有的调用。
  14. 大声朗读类,确保它们是合理的。读基类时用“is-a”,读成员对象时用“has-a”。
  15. 在决定是用继承还是用组合时,问问自己是不是需要向上映射到基类。如果不需要,就用组合(成员对象)而不用继承。这样可以减少多继承的可能。如果我们选择继承,用户会认为他们被假设向上映射。
  16. 有时为了访问基类中的protected成员而采用继承。这可能导致一个可察觉的对多重继承的需求。如果不需要向上映射,首先导出一个新类来完成保护成员的访问,然后把这个新类作为一个成员对象,放在需要用到它的所有对象中去。
  17. 一个典型的基类仅仅是它的派生类的一个接口。当创建一个基类时,缺省情况下让成员函数都成员纯虚函数。析构函数也可以是纯虚函数(强制派生类对它重新定义),但记住要给析构函数一个函数体,因为继承关系中所有的析构函数总是被调用。
  18. 当在类中放一个虚函数时,让这个类的所有函数都成为虚函数,并在类中定义一个虚析构函数。当要求高效时再把virtual关键词去掉,这种方法防止了接口的行为出格。
  19. 用数据成员表示值的变化,用虚函数表示行为的变化。如果发现一个类中有几个状态变量和几个成员函数,而成员函数在这些变量的作用下改变行为,就可能要重新设计它,用子类和虚函数来区别这种不同的作用。
  20. 如果必须做一些不可移植的事,对这种服务做一个抽象并将它定位在一个类的内部,这个额外的间接层可防止这种不可移植性影响我们的整个程序。
  21. 尽量不用多重继承。
  22. 不要用私有继承。虽然C++中可以有私有继承,而且似乎在某些场合下很有用,但它和运行时类型识别一起使用时,常常引起语义的模棱两可。可以用一个私有成员对象来代替私有继承。
  23. 运算符重载仅仅是“语法糖”:另一种函数调用方法。如果重载一个运算符不会使类的接口更清楚,更易于使用,就不要重载它。一个类只创建一个自动类型转换运算符,一般情况下,重载运算符要遵循相应原则和格式。
  24. 首先保证程序能运行,然后再考虑优化。特别是,不要急于写内联函数,使一些函数为非虚函数或者紧缩代码以提高效率。这些在开始构建系统时都不用考虑。开始的目标应该是证明设计的正确性,除非设计要求一定的效率/
  25. 不要让编译器来为我们产生构造函数,析构函数或“=”运算符。这些是训练我们的机会。类的设计者应该明确地说出类应该做什么,并完全控制这个类。如果不想要拷贝构造函数或“=”运算符,就把他们声明为私有的。记住,只要产生了任何构造函数,就防止了缺省构造函数被生成。
  26. 如果类中包含指针,必须产生拷贝构造函数,“=”运算符和析构函数,以使类运行正常。
  27. 为了减少大项目开发过程中的重复编译,应使用类句柄/Cheshire cat技术,只有需要提高运行效率时才把它去掉。
  28. 避免用预处理器。可以用常量来代替值,用内联函数代替宏。
  29. 保持范围尽可能的小,这样对象的可见性和生命期也就尽可能的小。这就减少了错用对象和隐藏难以发现的错误的可能性。比如说,假设有一个包容器和一段扫描这个包容器的代码,如果我们拷贝这些代码来用一个新的包容器,可能无意间用原来的包容器的大小作为新包容器的边界。如果原来的这个包容器超过了这个范围,就会引起一个编译错误。
  30. 避免使用全局变量。尽可能把数据放在类中。全局函数存在的可能性要比全局变量大,虽然后来发现一个全局函数作为一个类的静态成员更合适。
  31. 如果需要声明一个来自库中的类或函数,应该用包含一个头文件的方法。比如,如果想创建一个函数来写到ostream中,不要用一个不完全类型指定的方法自己来声明ostream,会使代码变得脆弱。可以用头文件的形式,当创建我们自己的类时,如果一个库很大,应提供给用户一个头文件的简写形式,文件中包含有不完全的类型说明(类型名声明),这是对于只需要用到指针的情况(提高编译速度)。
  32. 当选择重载运算符的返回值类型时候,要一起考虑串联表达式:当定义运算符“=”时应记住x=x。对于左值返回一个拷贝或一个引用(返回*this),所以它可以用在串联表达式(A=B=C)中。
  33. 当写一个函数时,第一选择是用const引用来传递参数。只要不需要修改正在被传递进入的对象,这种方式是最好的。因为它有着传值方式的简单,但不需要费时的构造和析构来产生局部变量,而这在传值方式时时不可避免的。通常在设计和构建系统时不用注意效率问题,但养成这种习惯仍旧是好事。
  34. 当心临时变量。当调整完成时,要注意临时创建的对象,尤其是用运算符重载时。如果构造函数和析构函数很复杂,创建和销毁临时对象就很费时。当从一个函数返回一个值时,总是应在return语句中调用构造函数来“就地”产生一个对象。
  35. 当产生构造函数时,要考虑到异常,在最好的情况下,构造函数不需要引起一个异常,另一种较好的情况:类将只从健壮的类被组合和继承,所以当一个异常产生时它会自动清除它自己。如果必须使用一个裸指针,应该负责捕获自己的异常,然后在构造函数中释放所有异常出现之前指针指向的资源。如果一个构造函数无法避免失败,最好的方法是抛出一个异常。
  36. 在构造函数中只做一些最必要的事情,这不仅使构造函数的调用有较低的时间花费,而且更少地抛出异常和引起的问题。
  37. 待续
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值