- 需要構造器嗎?
- 數據成員是private的嗎?它可以是const的嗎?
- 需要默認構造器嗎?
- 是不是每個構造器初始化了所有成員?
- 需要析構器嗎?它需要虛化嗎?
- 需要拷貝構造器嗎?
- 需要assigment operator嗎?它能正確自賦值嗎?
- 需要關系操作符嗎?
- 在函數形參上使用了const嗎?在成員函數之后呢?
- 刪除數組成員時用delete []嗎?
這個列表是在很久前自從看了《C++沉思錄》后,一直穩定的沿用至今,是要求作出高度復用的類設計之保障。希望能對別人有個拋磚引玉的作用。
上善若水,厚德載物
大道無親 大象無形 大音希聲 大巧若拙 大智若愚 大器晚成
生命的隱喻:自我意識產生于軀體,軀體產生于食物,食物源于物質,物質源于太陽輻射能。
http://cyberzei.wordpress.com |
迷時師度,悟了自度
Previous Section < Day Day Up > Next Section
Item 19: Treat class design as type design
設計 class 猶如設計 type
C++ 就像在其他 OOP(面向對象編程)語言一樣,當你定義一個新的 class,也就定義了一個新 type。身為 C++ 程序員,你的許多時間主要用來擴張你的類型系統(type system)。這意味你并不只是 class 設計者,還是 type 設計者。重載(overloading)函數和操作符、控制內在的分配和歸還、定義對象的初始化和終結……全都在你手上。因此你應該帶著和“語言設計者當初設計語言內置類型里時”一樣的謹慎來研討 class 的設計。
設計優秀的 classes 是一項艱巨的工作,因為設計好的 types 是一項艱巨的工作。好的 types 有自然的語法,直觀的語義,以及一或多個高效實現品。在 C++ 中,一個不良規劃下的 class 定義恐怕無法達到上述任何一個目標。甚至 class 的成員函數的效率都有可能受到它們“如何被聲明”的影響。
那么,如何設計高效的 classes 呢?首先你必須了解你面對的問題,幾乎每一個 class 都要求你面對以下提問,而你的回答往往導致你的設計規范:
新 type 的對象應該如何被創建和銷毀?? 這會影響到你的 class 的構造函數和析構函數以及內存分配函數和釋放函數(operator new, operator new[], operator delete 和 operator delete[] 見 第八章)的設計,當然前提是如果你打算撰寫它們。
對象的初始化和對象的賦值該有什么樣的差別? 這個答案決定你的構造函數和賦值(assignment)操作符的行為,以及其間的差異。很重要的是別混淆了“初始化”和“賦值”,因為它們對應于不同的函數調用(見條款四)。
新 type 的對象如果被 passed by value(以值傳遞),意味著什么? 記住, copy 構造函數用來定義一個 type 的 pass-by-value 該如何實現。
什么是新 type 的“合法值”? 對 class 的成員變量而言,通常只有某些數值集是有效的。那些數值集決定了你的 class 必須維護的約束條件(invariants),也就決定了你的成員函數(特別是構造函數、賦值操作符和所謂“setter”函數)必須進行的錯誤檢查工作。它也影響函數拋出異常、以及(極少被使用的)函數異常明細列(exception specifications)。
你的新 type 需要配合某個繼承圖系(inheritance graph)嗎 ? 如果你繼承自某些既有的 classes,你就受到那些 classes 的設計的束縛,特別是受到“它們的函數是 virtual 或 non-virtual”的影響(見條款三十四和條款三十六)如果你允許其他 classes 繼承你的 class,那會影響你所聲明的函數——尤其是析構函數——是否為 virtual(見 條款七)。
你的新 type 需要什么樣的轉換? 你的 type 生存于其他一海票 types 之前,因而彼此該有轉換行為嗎?如果你希望允許類型 T1 之物被隱式轉換為類型 T2 之物,就必須在 class T1 內寫一個類型轉換函數(operator T2)或在 class T2 內寫一個 non-explicit-one-argument(可被單一實參調用)的構造函數。如果你只允許 explicit 構造函數存在,就得寫出專門負責執行轉換的函數,且不得為類型轉換操作符(type conversion operators) 或 non-explicit-one-argument 構造函數。(條款十五有隱式和顯式轉換函數的范例。)
什么樣的操作符和函數對此新 type 而言是合理的? 這個問題的答案決定你將為你的 class 聲明哪些函數。其中某些該是 member 函數,某些則否見(條款二十三,條款二十四,條款四十六)
什么樣的標準函數應該駁回? 那些正是你必須聲明為 private 者(見條款六)。
誰該取用新 type 的成員? 這個提問可以幫助你決定哪個成員函數為 public,哪個為 protected,哪個為 private。它也幫助你決定哪一個 classes 和/或 function 應該是 friends,以及將它們嵌套于另一個之內是否合理。
什么是新 type 的“未聲明接口”(undeclared interface)? 它對效率、異常安全性(條款二十九)以及資源運用(例如多任務鎖定和動態內存)提供何種保證?你在這些方面提供的保證將為你的 class 實現代碼加上相應的約束條件。
你的新 type 有多么一般化? 或許你其實并非定義一個新 type,而是定義一整個 types 家族。果真如此你就不該定義一個新 class,而是應該定義一個新的 class template。
你真的需要一個新 type 嗎? 如果只是定義新的 derived class 以便既有的 class 添加機能,那么說不定單純定義一或多個 non-member 函數或 templates,更能夠達到目標。
這些問題不容易回答,所以定義出高效的 classes 是一種挑戰。然而如果能夠設計出至少像 C++ 內置類型一樣好的用戶自定義(user-defined)classes,一切汗水便都值得。
請記住
Class 的設計就是 type 的設計。在定義一個新 type 之前,請確定你已經考慮過條款覆蓋的所有討論主題。