現代程序設計語言中的絕大部分功能,都在程序的函數(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標註不改變數據的函數。