现代程序设计语言中的绝大部分功能,都在程序的函数(Function,Method)中实现,关于函数最重要的原则是:只做一件事,但是要做好。
二、 错误处理
80%的程序代码,都是对各种已经发生和可能发生的错误的处理。如果错误会发生,那就让程序死的地方离错误产生的地方越近越好。
在DeBug版本中,所有的参数都要验证其正确性。在正式版本中,从外部(用户或别的模块)传递过来的参数要验证其正确性。
2.断言
如何验证正确性?那就要用Assert(断言)。断言和错误处理是什么关系?
当你觉得某事肯定如何,你可以用断言。
Assert (p != NULL);
然后可以直接使用变量p;
如果你认为某事可能会发生,这时就要用错误处理。如:
p = AllocateNewSpace(); // could fail if (p == NULL){ // error handling. }else{ // use p to do something }
三、 如何处理C++中的类
注意,除了关于异常(Exception)的部分,大部分其他原则对C#也适用。
1.类
(1)使用类来封装面向对象的概念和多态(Polymorphism)。
(2)避免传递类型实体的值,应该用指针传递。换句话说,对于简单的数据类型,没有必要用类来实现。
(3)对于有显式的构造和析构函数,不要建立全局的实体,因为你不知道它们在何时创建和消除。
(4)只有在必要的时候,才使用“类”。
2.Class vs. Struct
如果只是数据的封装,用Struct即可。
3.公共/保护/私有成员Public、Private和Protected
按照这样的次序来说明类中的成员:public、protected、private
4.数据成员
(1)数据类型的成员用m_name说明。
(2)不要使用公共的数据成员,要用inline访问函数,这样可同时兼顾封装和效率。
5.虚函数Virtual Functions
(1)使用虚函数来实现多态(Polymorphism)。
(2)只有在非常必要的时候,才使用虚函数。
(3)如果一个类型要实现多态,在基类(Base Class)中的析构函数应该是虚函数。
6.构造函数Constructors
(1)不要在构造函数中做复杂的操作,简单初始化所有数据成员即可。
(2)构造函数不应该返回错误(事实上也无法返回)。把可能出错的操作放到HrInit()或FInit()中。
class Foo{ public: Foo(int cLines) { m_hwnd = NULL; m_cLines = cLines} virtual ~Foo(); HRESULT HrInit(); void DoSomething(); private: HWND m_hwnd; int m_cLines; };7.析构函数 (1)把所有的清理工作都放在析构函数中。如果有些资源在析构函数之前就释放了,记住要重置这些成员为0或NULL。 (2)析构函数也不应该出错。 8.New和Delete (1)如果可能,实现自己的New/Delete,这样可以方便地加上自己的跟踪和管理机制。自己的New/Delete可以包装系统提供的New/Delete。 (2)检查New的返回值。New不一定都成功。 (3)释放指针时不用检查NULL。 9.运算符(Operators) (1)在理想状态下,我们定义的类不需要自定义操作符。只有当操作符的确需要时。 (2)运算符不要做标准语义之外的任何动作。例如,“==”的判断不能改变被比较实体的状态。 (3)运算符的实现必须非常有效率,如果有复杂的操作,应定义一个单独的函数。 (4)当你拿不定主意的时候,用成员函数,不要用运算符。 10.异常(Exceptions) (1)异常是在“异乎寻常”的情况下出现的,它的设置和处理都要花费“异乎寻常”的开销,所以不要用异常作为逻辑控制来处理程序的主要流程。 (2)了解异常及处理异常的花销,在C++语言中,这是不可忽视的开销。 (3)当使用异常时,要注意在什么地方清理数据。 (4)异常不能跨过DLL或进程的边界来传递信息,所以异常不是万能的。 11.类型继承(Class Inheritance) (1)当有必要的时候,才使用类型继承。 (2)用Const标注只读的参数(参数指向的数据是只读的,而不是参数本身)。 (3)用Const标注不改变数据的函数。