close
一、Accustoming Yourself to C++

01、 View C++ as a federation of language.
將 C++ 視為一個由相關語言組成的聯邦語言,其主要的次語言有四種:
C、Object-Oriented C++、Template C++、STL。


02、 Prefer const, enum and inline to #define.
對於單純常數,最好以 const 物件或 enum 取代 #define。
對於類似 function 的 macro,最好改用 inline function 取代 #define。


03、 Use const whenever possible.
將某些東西宣告為 const,可以幫助 compiler 偵測出錯誤用法;const 可被施加於任何作用域內的 object、function parameter、function return type、member function 本體。
compiler 強制實施 bitwise constness ,但在編寫程式時,應該使用 conceptual constness。
兩個 member function,如果只有 constness 不同,仍然可以被 overload;而當 const 和 non-const member function 有著實質相等的實作時,令 non-const 版本呼叫 const 版本可避免程式碼重複。


04、 Make sure that objects are initialized before they're used.
為內建型別進行手工初始化,因為 C++ 不保證初始化它們。
constructor 最好使用 member initialization list,其排列次序應該和宣告次序相同。
為了避免「跨編譯單元之初始化次序」問題,以 local static objects 來取代 non-local static objects。



二、Constructors, Destructors, and Assignment Operators

05、 Know what functions C++ silently writes and calls.
compiler 可以暗自為 class 產生: default constructor、copy constructor、copy assignment operator 及 destructor;但只有在這些 function 被呼叫時,它們才會被 compiler 產生出來。
但如果 user 有自行宣告 constructor,default constructor 且只有 default constructor 不會被自動產生。


06、 Explicitly disallow the use of compiler-generated functions you do not want.
為駁回 compiler 自動提供的機能,可將相應的 member function 宣告為 private 並且不予實作;又或是繼承像 Uncopyable 這樣的 base class。


07、 Declare destructors virtual in polymorphic base classes.
polymorphic base classes 應該宣告一個 virtual destructor,如果 class 帶有任何 virtual function,也應該擁有一個 virtual destructor。
Classes 的設計目的如果不是為了作為 base class,或不是具備 polymorphism,就不應該宣告 virtual destructor。


08、 Prevent exceptions from leaving destructors.
在 destructor 中,絕對不要吐出 exception,如果其呼叫的 function 可能拋出 exception,則吞下它們或結束程式。
如果要讓使用者可以在執行期間對 function 所拋出的 exception 作處理,則 class 應該提供一個普通的 function 執行該操作,讓使用者在非解構式中使用。


09、 Never call virtual functions during constructor or destruction.
在 constructor 及 destructor 中不要呼叫 virtual function,因為這類的呼叫從不會下降至 derived class。


10、 Have assignment operators return a reference to *this.
令 assignment operator 傳回一個 reference to *this,以實現 chain of assignments。


11、 Handle assignment to self in operator=.
確保當 object 執行 self assignment 時,operator= 會具備「自我賦值安全性」及「exception safety」,其中技術包括:identity test、精心排列的述句順序以及 copy and swap。
確定操作一個以上的 object 的任何 function,在其中多個 object 是同一個物件時,也能正常執行。


12、 Copy all parts of an object.
Copying function (就是 copy constructor 及 copy assignment operator) 應該確保複製「object 的所有 data member」及「所有 base class 內的 data member」。
不要嘗試以某個 copying function 呼叫另一個 copying function,而是應該就共同機能放進第三個 function 中,並由兩個 copying function 呼叫。



三、Resource Management

13、 Use objects to manage resources
為了防止 memory leak,儘量使用 RAII(Resource Acquisition Is Initialization) 物件,它會在 constructor 中獲得資源,並在 destructor 中釋放資源。
兩個常被使用的 RAII class 是:tr1::shared_ptr 及 auto_ptr;但 tr1::shared_ptr 是較佳的選擇。


14、 Think carefully about copying behavior in resource-managing classes.
複製 RAII Object 必須一併處理它所管理的資源,而資源的 copying 行為將決定 RAII Object 的 copying 行為。
常見的 RAII class copying 行為是:抑制 copying、使用 reference counting。


15、 Provide access to raw resources in resource-managing classes.
APIs 往往要求存取原始資源,所以每個 RAII class 都應該提供一個「取得其管理之資源」的方式。
對原始資源的存取可能經由顯示轉換或隱式轉換,一般而言,顯示轉換較為安全。

// type conversion operator
ex: operator int () const { return f; } // implicit conversion function


16、 Use the same form in corresponding uses of new and delete.
如果在 new 算式中使用 [],必須也在相應的 delete 算式中使用 [],如果在 new 算式中不使用 [],一定也不要在相應的 delete 算式中使用 []。


17、 Store newed objects in smart pointers in standalone statements.
以獨立述句將 newed object 儲存於 smart pointer 內,以避免有 exception 被拋出,而造成資源洩漏。



四、Designs and Declarations

18、 Make interfaces easy to use correctly and hard to use incorrectly.
要在 interface 中達成容易被使用,不易被誤用的性質。
「促進正確使用」的辦法包括 interface 的一致性,以及與內建型別的行為相容。
「阻止誤用」的辦法包話建立新型別、限制型別上的操作、束縛物件值及消除使用者的資源管理責任。
tr1::shared_ptr 支援 custom deleter,可防範 "cross-DLL problem",也可被用來自動解鎖(mutex,詳見條款14)。


19、 Treat class design as type design.
Class 的設計就是 type 的設計,在定義一個新的 type 之前,需考慮本條款的所有主題。


20、 Prefer pass-by-reference-to-const to pass-by-value.
儘量以 pass-by-reference-to-const 取代 pass-by-value,前者通常較有效率,並可避免 slicing problem。
但對於內建型別及 STL 的迭代器及函式物件,pass-by-value 往往比較適當。


21、 Don't try to return a reference when you must return an object.
絕對不要回傳一個指向 local stack object 的 pointer 或 reference 。
也不要回傳一個指向 heap-allocated object 的 reference 。
也不要回傳一個指向可能同時被操作的 local static object 的 pointer 或 reference 。


22、 Declare data members private.
切記將成員變數宣告為 private,這將賦予存取資料的一致性、可細微劃分存取控制、並提供 class 的充分實作彈性。
protected 並不比 public 更具封裝性。


23、 Prefer non-member non-friend functions to member functions.
寧可拿 non-member non-friend function 取代 member function,這樣做可以增加封裝性、package flexibility 和機能擴充性。
namespace 和 class 不同,前者可跨越多個源碼檔,而後者不能。


24、 Declare non-member functions when type conversions should apply to all parameters.
如果需要為某個 function 作好其所有 parameters 都可能進行型別轉換的準備,那麼這個 function 必須是個 non-member function。


25、 Consider support for a non-throwing swap.
當 std::swap() 對自訂型別效率不高時,提供一個 swap() member function,並確保這個 function 不拋出 exception。
如果提供一個 member swap(),也該提供一個 non-member swap() 用來呼叫前者,對於 class ,也需要 specialize std::swap。
呼叫 swap() 時,應針對 std::swap 使用 using 宣告式,然後不帶任何 namespace 呼叫 swap()。
為了「使用者定義型別」而對 std templates 進行 totally specialize 是好的,但不要嘗試在 std 內加入對 std 而言是全新的東西。

total template specialization vs. partially template specialization



五、Implementations

26、 Postpone variable definitions as long as possible.
儘可能延後變數定義式的出現,可以增加程式的清晰度及程式效率。


27、 Minimize casting.
儘量避免轉型,特別是在注重效率的程式碼中避免 dynamic_cast;如果有個設計需要轉型,試著發展無需轉型的替代設計。
如果轉型是必要的,試著將它隱藏在某個函式背後,讓使用者可以隨時呼叫該函式,而不需將轉型放進他們的程式碼內。
寧可使用 C++-style 轉型,不要使用舊式轉型,前者容易辨識,且有著分門別類的職掌。


28、 Avoid returning "handles" to object internals.
避免傳回 handle 指向 object 內部,將可增加封裝性、幫助 const member 的行為像個 const、並將發生 dangling handle 的可能性降低。


29、 Strive for exception-safe code.
exception-safe function 即使發生 exception 也不會洩漏資源或允許任何資料結構敗壞,這樣的 function 分成三種保證:基本型、強烈型、nothrow 型。
「強烈保證」往往能以 copy and swap 實作出來,但並非對所有 function 都可實現或具備實現意義。
function 所提供的「exception safety 保證」最高只等於其所呼叫之各個 function 的「exception safety 保證」中的最弱者。


30、 Understand the ins and outs of inlining.
如果 inline function 的本體很小,則 compiler 針對「function 本體」所產生的碼可能比針對「function call」所產生的碼更小。
inline function 無法隨著程式庫的升級而升級。
將大多數 inlining 限制在小型、被頻繁呼叫的 function 身上,這將使得日後的 debug 過程及 binary upgradability 更容易,也可使潛在的程式膨脹問題最小化,並使程式的速度提升機會最大化。


31、 Minimize compilation dependencies between files.
支持「compilation dependency 最小化」的一般構想是:相依於宣告式,不要相依於定義式。
奠基於上述構想的兩個手段是 Handle class 及 Interface class。
程式庫 header file 應該以「完全且僅有宣告式」(full and declaration-only forms) 的型式存在。



六、Inheritance and Object-Oriented Design

32、 Make sure public inheritance models "is-a."
「public inheritance」意味 "is-a",適用於 base class 身上的每件事情,也一定適用於 derived class 身上。


33、 Avoid hiding inherited names.
derived class 內的名稱會遮掩 base class 內的所有同名名稱。
而為了讓被遮掩的名稱可以被找到,可以使用 using 宣告式或 forwarding function。


34、 Differentiate between inheritance of interface and inheritance of implementation.
inheritance of interface 和 inheritance of implementation 是不同的;在 public inheritance 中,derived class 總是 inherit base class 的 interface。
pure virtual functions 只具體指定 inheritance of interface。
simple(impure) virtual functions 具體指定 inheritance of interface plus inheritance of a default implementation。
non-virtual functions 具體指定 inheritance of interface plus inheritance of a mandatory implementation。


35、 Consider alternatives to virtual functions.
virtual function 替代方案包括:
  1. 使用 non-virtual interface(NVI) 手法,這是 Template Method design pattern 的一種特殊形式,以 public non-virtual member function 包裏較低存取性的 virtual function。
  2. 將 virtual function 替換為「function pointer data members」,這是 Strategy design pattern 的一種分解表現形式。
  3. 將 virtual function 替換為 tr1::function data members,因而允許任何 callable entity 搭配一個相容於需求的 signature,這也是 Strategy design pattern 的一種形式。
  4. 將 virtual function 替換為另一個繼承體系內的 virtual function,這是 Strategy design pattern 的傳統實作手法。

將機能從 member function 移到 class 外部 function,所帶來的缺點之一是:非成員函式將無法存取 class 內的 non-public 成員。
tr1::function 物件的行為就像一般函式,這樣的物件可以接納「與給定之 target signature 相容」的所有 callable entry。


36、 Never redefine an inherited non-virtual function.
non-virtual functions are statically bound,但 virtual functions are dynamically bound。


37、 Never redefine a function's inherited default parameter value.
object 的所謂 static type,就是它在程式中被宣告時所採用的型別;而 object 的 dynamic type 則是指「目前所指 object 的型別」。
default values 都是 statically binding,而 virtual functions - 唯一該 override 的東西 - 卻是 dynamically binding。


38、 Model "has-a" or "is-implemented-in-terms-of" through composition.
當 composition 發生於 application domain,所表現出來的是 has-a 的關係。
而當 composition 發生於 implementation domain,則是表現出 is-implemented-in-terms-of 的關係。


39、 Use private inheritance judiciously.
private inheritance 意味 is-implemented-in-terms-of,它通常不如 composition 來得好,但是當 derived class 需要存取 protected base class 的 member,或需要重新定義 inherit 而來的 virtual function 時,這樣設計是合理的。
private inheritance 可能造成 empty base optimization,對致力於「物件尺寸最小化」的開發著而言,可能很重要。


40、 Use multiple inheritance judiciously.
multiple inheritance 較 single inheritance 為複雜,且可能導致 ambiguity 及對 virtual inheritance 的需要。
virtual inheritance 會增加大小、速度、初始化及賦值的複雜度等等,在 virtual base classes 不帶任何資料的情況,將是最具實用價值的情況。
multiple inheritance 具有合理應用的情況,其中之一是對一個 interface class 實作 public inheritance ,對另一個協助實作的 class 進行 private inheritance。



七、Templates and Generic Programming

41、 Understand implicit interfaces and compile-time polymorphism.
classes 和 templates 都支援 interfaces 和 polymorphism。
對 class 而言,interfaces 為 explicit,以 function signatures 為中心, polymorphism 則是透過 virtual functions 發生於 run-time。
對 template 而言,interfaces 為 implicit,奠基於 valid expressions, polymorphism 則是透過 template instantiation 和 function overloading resolution 發生於 compilation。


42、 Understand the two meanings of typename.
宣告 template 參數時,前綴關鍵字可以是 class 也可以是 typename。
nested dependent type names 必須使用關鍵字 typename 來標識 ,除非是在 base class lists 或 member initialization list 內。


43、 Know how to access names in templatized base classes.
可在 derived class template 內透過 "this->" 指涉 base class template 內的 member name,或利用 using declaration,又或藉由 explicit base class qualification,來達到使用 base classes 內的 member name。


44、 Factor parameter-independent code out of templates.
Template 會生成多個 classes 和 functions,所以任何 template 程式碼都不該與某個造成膨脹的 template 參數產生相依關係。
因非型別模板參數(non-type template parameters) 而造成的程式膨脹,往往可消除,作法是以 function parameters 或 class data members 取代 template parameters。
因型別參數(type parameters) 而造成的程式膨脹,往往可降低,作法是讓帶有完全相同二進制表述 (identical binary representation) 的具現型別 (instantiation type) 共享實作碼。


45、 Use member function templates to accept "all compatible types."
請使用 member function template 生成「可接受所有相同型別」的 function。
如果宣告 member template 用於「generalized copy constructor」或「generalized assignment operator」,還是得需要宣告正常的 copy constructor 及 copy assignment operator。


46、 Define non-member functions inside templates when type conversions are desired.
當在編寫一個 class template,而它所提供之「將此 template 相關的」function 支援「所有參數之隱式型別轉換」時,將那些 function 定義為「class template 內部的 friend function」。


47、 Use traits classes for information about types.
traits classes 使得「型別相關資訊」在 compile 期可用,以 template 和 partial template specialization 完成實作。
整合 overloading 後,traits classes 有可能在 compile 期間,就對型別執行 if ... else 測試。


48、 Be ware of template metaprogramming.
template metaprogramming(TMP, 模板超編程) 可將工作由 run-time 移往 compile-time,因而得以實現早期錯誤偵測和更高的執行效率。
TMP 可被用來生成「植基於政策選擇組合」(based on combinations of policy choices) 的客戶訂製碼,也可用來避免生成對某些特殊型別並不適合的碼。



八、Customizing new and delete

49、 Understand the behavior of the new-handler.
set_new_handler 允許使用者指定一個 function,在記憶體配置無法獲得滿足時被喚起。
nothrow new 是一個頗為侷限的工具,只適用於記憶體配置,後續的 constructor 呼叫還是可能拋出 exception。


50、 Understand when it makes sense to replace new and delete.
有許多理由需要寫個自定的 new 及 delete,包括改善效能、對 heap 運用錯誤進行除錯、蒐集 heap 使用資訊…等。


51、 Adhere to convention when writing new and delete.
operator new 應該內含一個無窮迴圈,並在其中嘗試 allocate memory,如果無法滿足需求,就該呼叫 new-handler;operator new 也應該有能力處理 0 byte 申請;若是 class 專屬版本則還應該處理「比正確 size 更大的申請」。
operator delete 應該在收到 null pointer 時,不作任何事;若是 class 專屬版本則還應該處理「比正確 size 更大的申請」。


52、 Write placement delete if you write placement new.
當寫一個 placement operator new,也須確定寫出對應的 placement operator delete,以免發生 memory leak。
如果宣告了 placement new 及 placement delete,需多加一些手法,避免遮掩了它們的正常版本。




九、Miscellany

53、 Pay attention to compiler warnings.
努力在 compiler 的最嚴苛警告層級下爭取「無任何 warning message」。
不要過度依賴 compiler 的 warning 能力,不同的 compiler 對待事情的態度並不相同。


54、 Familiarize yourself with the standard library, including TR1.
C++ standard library 主要機能由 STL, iostream, locale 組成,並包含 C99 standard library。
TR1 添加了 smart pointer, 一般化 function pointer, hash-based container, regular expression 及另外 10 個 components 的支援。
TR1 本身只是一份規格,為了得到 TR1 提供的好處,還需要一份實物,一個好的實物來源是 Boost。


55、 Familiarize yourself with Boost.
Boost 是一個社群,也是一個網站,致力於免費、源碼開放、同僚覆審的 C++ library development,Boost 在 C++ 標準化過程中扮演深具影響力的角色。
Boost 提供許多 TR1 組件實作品,以及其它許多函式庫。



arrow
arrow
    全站熱搜

    silverfoxkkk 發表在 痞客邦 留言(0) 人氣()