文章目錄
條款26.儘可能延後變量定義式出現的時間
不到迫不得已時,不定義變量。
上述代碼的問題:如果上述函數體內部拋出了異常,那麼定義的字符串變量沒有任何的作用,白白消耗構造和析構的時間改進版本:注意定義變量的位置,以及在定義變量的同時進行初始化(以避免默認構造函數的調用,具體見:條款4)
條款27.儘量少做轉型動作
舊式轉型:
新式轉型:
總結:
- 儘量避免轉型,特別是在注重效率的代碼中避免使用dynamic_cast,如果需要轉型,儘量使用無需轉型的方式來代替
- 如果轉型必要,設計相關函數,客戶直接調用該函數完成轉型,而不是在自己的代碼中轉型
- 儘量使用新式的轉型方式
條款28.避免返回handles(引用、指針、迭代器)指向對象內部成分
避免返回handles(包括引用、指針、迭代器)指向對象內部,這樣可以增加封裝性,幫助const成員函數的行爲像個const,並將發生“虛吊號碼牌”的可能性降到最低。
分析如下調用:
雖然upperLeft()
是常數函數,在其內部不能改變成員變量的值,但是可以通過該函數返回的引用改變內部變量的值,發生此狀況的原因在於返回了其內部成員的引用
導致空懸的號碼牌(handles)
定義如下類:
調用該函數:
分析一下調用上述函數會帶來什麼問題:
首先通過調用boundingBox()
返回一個Rectangle
對象,我們稱之爲temp
,然後調用upperLeft()
返回一個Point()
對象的引用,然後pUpperLeft
指向該對象。這裏看似一切正確,但是當該語句執行完後,temp
對象會釋放掉(析構),因爲它是一個臨時對象,最終導致pUpperLeft
指向一個不存在的對象,成爲空懸指針
條款29.爲“異常安全”而努力是值得的
異常安全函數:即使發生異常也不會泄漏資源或允許任何數據結構敗壞
異常安全保證:
- 基本保證:如果拋出異常,程序內任何對象或其數據結構不會被破壞,所有的對象都處於一種內部前後一致的狀態(但是我們並不知道處於哪種狀態,只要這種狀態合法就行,除非調用相關函數查看對象的狀態)。
- 強烈保證:如果異常拋出程序狀態不會改變。如果函數調用成功,就是完全成功;如果函數失敗,程序會恢復到“調用函數之前”的狀態。
- 不拋擲保證:承諾絕不拋出異常。作用於內置類型的所有操作都提供nothrow保證。
總結:
- 強烈保證往往能夠以copy-and-swap實現出來,但強烈保證並非對所有函數都可實現或具備現實意義
- 函數提供的“異常安全保證”通常最高只等於其所調用的各個函數的“異常安全保證”中最弱的
條款30.透徹理解inlining的裏裏外外
使用內聯函數的目的是爲了提高程序的執行效率,解決一些頻繁調用的小函數大量消耗棧內存空間的問題,但很多情況下並非如此。
總結:
- 大多數內聯函數限制在小型、被頻繁調用的函數身上。這可以使得最小化代碼膨脹問題,使程序的速度提升機會最大化
- inline只適合函數體內代碼簡單的函數使用,不能包含複雜的結構控制語句(switch,while,for)或者函數體內的代碼比較長,並且內聯函數本身不能是直接遞歸函數
- inline僅是對編譯器的一個建議,最後是否真正內聯,看編譯器的意思。編譯器如果認爲函數不復雜,能在調用點展開,就會真正內聯,並不是說聲明瞭內聯就一定會內聯
- 建議inline函數定義放在頭文件內。因爲內聯函數在調用點進行展開,所以編譯器必須隨處可見內聯函數的定義,每個調用內聯函數的文件都出現了該內聯函數的定義,因此,將其放在頭文件中最合適
- 定義在類中的成員函數,缺省情況下都是內聯的。但是成員函數的定義放在類聲明之中雖然不是不可以,但不是一種良好的編程風格,最好在類內進行聲明,在類外進行定義。要特別注意:構造函數和析構函數的定義最好不要放在函數體內,它們可能會隱藏一些行爲,例如執行了基類或者成員變量的構造函數或者析構函數,這些函數會產生大量的副本。
- inline是一種用於實現的關鍵字。關鍵字inline必須與函數定義體放在一起才能使函數成爲內聯函數,如果放在函數聲明的前面,其不起任何作用
條款31.將文件間的編譯依存關係降至最低
編譯依存:
有如下類的聲明:
其包含的頭文件:
分析:
如果頭文件中有任何一個被改變或者這些頭文件所依賴的其他頭文件被改變,那麼每一個包含Person
類的頭文件都需要重新編譯,這樣的連串編譯依存關係會對許多項目造成難以形容的災難
解決方法:將
Person
分成兩個類,一個是接口類PersonImpl
,一個是實現類Person
分析:這樣設計,Person
的客戶就與Dates,Addresses
以及Person
的實現分離了,那些類的任何修改都不需要Person
客戶端重新編譯
總結:
- 支持“編譯依存最小化的一般構想”是:相依於聲明式,不要相依於定義式。基於此構想的兩個手段:Handle classes 和 Interface classes
- 程序庫的頭文件應該以“完全且僅有聲明式”的形式存在。這種做法不論是否涉及templates都適用。