如何寫出高效C++(實現)
26.儘可能延後變數定義式的出現時間
你不止應該延後變數的定義,直到非得使用該變數的前一刻為止,甚至應該嘗試延後這份定義直到能夠給它初值實參位置,好處是不僅能夠避免構造(和析構)非必要物件,還可以避免無意義的default構造行為。
對於迴圈而言有如下兩個計劃
A:定義變數於迴圈外,則一個建構函式一個解構函式和n個賦值操作
B:定義變數於迴圈內,則n個建構函式和n個解構函式
除非你知道
(1)賦值成本比"構造+析構"成本低
(2)你正在處理程式碼中效率高度敏感的部分
否則用B吧
27.儘量少做轉型動作
關於C語言的不再論述
C++提供的四種新式轉型:
const_cast<T>(expression)//將物件的常量性轉除
dynamic_cast<T>(expression)//安全向下轉除,用來決定某物件是否歸屬繼承體系中的某個型別。
reinterpret_cast<T>(expression)//低階轉型,很少用,實際動作和結果取決於編譯器,它不可移植,pointer to int轉換為int
static_cast<T>(expression)//強迫隱式轉換,比如non-const物件轉換為const物件,或者將int轉換為double物件等等。
如果可以,儘量避免轉型,尤其是在注重效率的程式碼中避免dynamic_casts,如果有個設計需要轉型動作,使者發展無需轉型的替代設計。
28.避免返回handles指向物件內部成分
handles(包括reference,指標,迭代器)指向物件內部。遵守這個條款可增加封裝性,幫助const成員函式的行為像個const,並將發生"虛吊號碼牌"的可能性降至最低。
29.為"異常安全"而努力是值得的
"異常安全"的兩個條件:
(1)不洩露任何資源
(2)不允許資料敗壞
異常安全函式會提供以下的三個保證之一:
(1)基本承諾:如果異常被丟擲,程式內的任何事物仍然保持在有效狀態下
(2)強烈保證:如果異常被丟擲,程式狀態不改變,如果函式成功,那就是完全成功,如果函式失敗,程式會回覆到"呼叫函式之前"的狀態。
(3)不拋擲保證:承諾絕不丟擲異常,因為它們總是能夠完成它們原先承諾的功能。
如何讓程式碼具有異常安全性:
首先“以物件管理資源”阻止資源洩露,然後是挑選三個"異常安全保證"中的一個實施於每一個函式身上。
強烈保證往往能夠以copy-and-swap實現出來,但是"強烈保證"並非對所有函式都可實現或具備現實意義。
30.透徹瞭解inlining的裡裡外外
inline函式,將“對此函式的每一個呼叫”都用函式本體替換之。
將函式定義於calss定義式內的話那就是隱喻的inline
如果friend函式也可被定義於class內,那麼它們也是被隱喻宣告為inline。
所謂的明確宣告方法,就是在定義式前加上關鍵字inline。
大部分編譯器拒絕太過複雜(例如帶有迴圈或者遞迴)的函式inlining,而所有對virtual函式的呼叫也都會使inlining落空
建構函式和解構函式是inlining的糟糕人選。
31.將檔案的編譯依存關係降到最低
支援"編譯依存性最小化"的一般構想是:相依於宣告式,不要相依於定義式,基於此構想的兩個手段是handle classer和interface classes
程式庫標頭檔案應該以"完全且僅有宣告式"的形式存在,這種做法不論是否涉及templates都適用。然後可以通過把這些函式轉交給相應的實現類並由後者完成實際工作來實現函式的使用。