effective C++ 要點總結

effective C++ 要點總結

條款01

C++分為四個次語言,C,Object-oriented C++,Template ++,STL

對於內置類型pass-by-value 比pass-by-reference高效,用戶自定義類型pass-by-reference-to-const更好

STL中迭代器和函數對象都是在C指針的基礎上塑造出來的,所以舊式的C pass-by-value再次試用

條款02

對於單純常量最好使用const和enums代替defines

對於形似函數的宏,最好使用inline代替defines

條款03

如果const出現在星號左邊 const * p,表示被指物是常量,如果const出現在星號右邊 * const p,表示指針自身是常量

const iterator 表示iterator是一個常量,const_iterator表示iterator指向一個常量。

兩個成員函數如果只是constness(常量性)不同,可以被重載

physical constness 和 logical constness,physical constness,成員函數只有不改變對象任何成員時,才可以說是const;logical constness,一個成員函數可以修改對象中的某些bits,但只有在客戶端偵測不出來的情況下,

通過設置變量的mutable,釋放掉non-static成員變量的bitwise constness約束

將某些東西聲明為const可以幫助編譯器偵測出錯誤用法

當const和non-const的成員函數有著實際等價的實現時,用non-const調用用const函數,可以避免重複,不可以反過來調用

條款04

進入構造函數體之前,會調用成員變量的默認構造函數初始化變量,因此,如果在構造函數體內進行賦值,相當於是多先初始化,再賦值,所以調用成員初始化列是比較高效的做法,對於內置類型則無所謂。

成員變量是按照聲明的順序來進行初始化的,不會按照初始化列表中所寫的順序進行初始化

non-local static對象是指,定義在global,namespace中的對象(全局性質的), class和file中的static對象,壽命直到程序結束為止

local-static 對象,是指定義在函數中的static對象

問題,如果某個編譯單元中的non-local static對象初始化時,使用了另外一個編譯單元的non-local static對象,他用到的這個對象可能尚未被初始化。

解決方法,把每個non-local static對象放到自己專屬的函數內,該對象在函數中被聲明為static,該函數返回對象的reference。這種方法在多線程環境下會有麻煩,解決的辦法是單線程啟動階段調用所有類似的函數。

避免在對象初始化之前使用他們,一,手工初始化內置類型non-member對象,二,使用成員初始化列表初始化所有成員,三,初始化順序不確定時(non-local static),加強設計,使用local static 替換non-local static

條款05

編譯器會暗自為class創建default構造函數,除非用戶定義了自己的構造函數(有實參,無實參或者拷貝構造)

編譯器會暗自為class創建拷貝構造,拷貝賦值和析構函數,除非用戶自己定義了對應的函數

如果class中有成員是引用,或者const,或者基類的賦值操作符被聲明為private,則編譯器不會再生成拷貝賦值操作符,需要用戶自己定義

條款06

為了拒絕編譯器默認生成的copy構造和賦值操作符,可以將對應的函數聲明為private(member和friend還是能夠調用),並且不實現,或者從uncopyable的類繼承。

條款07

帶多態性質的base class應該聲明一個virtual 析構函數, 如果class帶有virtual函數,那麼應該同時具有virtual 析構函數

class的設計如果不是為了作為base class使用,或者不是用作多臺,那麼不應該聲明virtual 析構函數

條款08

析構函數不要拋出異常,如果被析構函數調用的函數會拋出異常,析構函數應該捕獲他,不傳播或者結束程序,多個對象析構時拋出異常,C++對多個異常不太好處理

如果客戶需要對某個函數被調用時拋出的異常做出反應,那麼class應該提供一個普通函數而非析構函數執行該操作

條款09

構造函數和析構函數中不能調用virtual函數,構造和析構時不會調用子類的函數

條款10

賦值操作符返回reference to *this

條款11

確保當對象自我賦值時operator=有良好行為,其中的技術包括,比較來源和目標地址,精心安排語句順序,及copy-and-swap

一個函數如果操作多個對象,而其中多個對象是同一對象時,其行為仍正確

條款12

任何時候,編寫派生類的copy構造函數時,都要小心的copy基類部分,應該調用基類的copy構造函數

別用copy構造函數調用copy複製操作符,也別用copy賦值操作符調用copy構造函數,給一個沒有初始化的對象賦值和對一個已經初始化的對象再次初始化都是不正確的。將共同的機能放到第三個函數中。

條款13

auto_ptr簡單封裝了指針,在析構時會刪除指針所指對象,禁止多個auto_ptr指向同一對象,auto_ptr的複製動作會把使他指向null

帶引用計數的智能指針無法打破環狀引用

tr1::shared_ptr可以解決上述問題,

auto_ptr和shared_ptr析構函數中只會調用delete,不會調用delete[],因此不應該把數組的指針傳遞給他們,boost::scoped_array和boost::shared_array支持

條款14

資源管理類的copy行為,常見的方法有,禁止copy行為,對底部資源進行引用技術,底部資源深度複製,轉移底部資源的所有權

條款15

條款16

不要對數組typedef,這樣定義的類型,申請時使用new, 釋放時需要delete[],不對稱,容易出錯

條款17

以獨立語句把newed對象放入智能指針中,不過不這樣,一旦異常拋出可能會導致內存洩漏

條款18

cross-DLL problem 在一個DLL中new,在另外一個DLL中delete會導致運行時錯誤,使用tr1::shared_ptr可以解決該問題,因為它使用的刪除器來自產生shared_ptr的DLL

促進正確使用的方法,接口一致性,以及與內置類型的行為兼容

阻止誤用的方法有建立新類型,限制類型上的操作,束縛對象值,消除客戶的資源管理責任

條款19

新type的對象如何創建和釋放,初始化和賦值的區別,拷貝構造的實現,新type的合法值檢查,繼承關係中父類的約束,有子類時要提供虛析構函數,是否需要隱式類型轉換,提供顯示類型轉換的函數,成員函數public,private,protected劃分,成員變量的劃分,效率,異常,資源使用方面的考慮,能否抽象為模版類,如果定義派生類只是為已有類型添加機制,說不定定義non-member函數或者template可以達到相同目的。

條款20

pass-by-valid僅適用於傳遞內置類型,STL迭代器和函數對象,其他情況儘量使用pass-by-reference-to-const

條款21

不要返回pointer或者reference to local-stack對象

條款22

將變量聲明為private,可以賦予客戶訪問數據的一致性(都通過函數訪問),可以進行訪問控制,可以允諾約束條件一直得以維護(只會通過函數來訪問變量),提供class作者實現的彈性

條款23

拿non-member non-frined函數替換member函數,這樣做可以增加封裝性(可以訪問成員變量的函數更少了),將一個類的所有便利函數放到多個頭文件中但隸屬於一個名字空間,可以減少編譯的依賴性,也方便功能的擴展(新增頭文件)

條款24

只有當參數放在參數列表中時,才能滿足隱式類型轉換的基本條件,函數的調用者不可以被轉換,因此如果需要進行類型轉換應該使用non-member函數,儘量通過public函數訪問成員,而不是使non-member函數稱為friend

條款25

C++不允許偏特化class templates,當想嘗試給一個function tempates偏特化時,進行函數重載可以達到相同的目標

當std:swap函數對你的對象效率不高時,提供一個swap成員函數,這個函數不能拋出異常

提供成員函數的同時,也需要提供一個non-member swap函數,體用成員函數,當對象的類型不是template時,特化std:swap版本,當類是template時,無法偏特化std:swap函數,因此在自己的命名空間中定一個重載的函數(無法給std空間中加入任何東西)

使用swap時,使用using std::swap 然後調用swap函數,不能在前面加任何名字空間修飾,這樣編譯器會選擇最合適的swap函數

條款26

延後定義變量,直到使用時

條款27

一個單一對象可能擁有一個以上的地址,以base*指向和以dervied*指向在某些場景下是不同的(所有多繼承和有的單繼承)

轉型過後會產生臨時變量,使用時應該慎重,轉型之後的變量並不是原始的變量

避免使用dynamic_cast,兩種方式,一種是直接使用dervied類的對象,另外一種是在base類中增加virtual函數

條款28

成員變量的封裝性取決於返回該成員reference的成員函數的訪問級別。

const成員函數返回reference,而後者指向的對象在本對象之外,而他的指針在本對象內,那麼函數的調用者可以修改

避免返回成員變量的reference 指針和迭代器,可以增加封裝性,並降低出現dangling handles的可能性

條款29

異常章節(略過了)

條款30

inline某個函數,編譯器會優化他

inline只是對編譯器的申請,不是強制指令

定義在類中成員函數,或者friend函數是隱式inline的

Template和inline無關,如果所有的具現化版本都是inlined,那麼在template前面加上inline,否則不加

一個表面上看起來是inline是否真的inline取決於編譯器,(循環,遞歸 ,virtual)

編譯器通常不會對通過函數指針調用實施inline

構造函數和析構函數其實並沒有看起來那樣有很少的執行過程,因此是否作為inline需要視情況而定

程序庫中的inline函數無法隨著程序庫的升級而升級,客戶程序必須重新編譯,如果是非inline的直接連接即可

調試器對inline函數束手無策,無法設立斷點

條款31

儘量使用object reference和object pointer

聲明一個函數,縱使他用到了某個類,也不需要該class的定義

程序庫作者應該提供兩個頭文件,一個聲明頭文件,一個定義頭文件,而不是手動寫前置聲明

export關鍵字只有部分編譯器支持

handle class和 interface class

class A

{

AImpl* pimpl;

}

class interface{

使用handleclass和interface class可以在修改實現代碼的時候,對用戶代碼的影響最小,但是由於增加了virtual或者新增指針,在使用時需要考慮平衡。

條款32

public繼承主張能夠施行與base的行為,都能夠施行在dervied上

如果在派生類中對基類中的重載函數進行重載或者重寫,會遮掩基類中其他的重載函數(在派生類中不可見,調用會出錯),為了解決這一問題, 需要引入using,這個規則同時適應與virtual和non-virtual

或者直接顯示的輸入(基類類型::函數名)

using聲明會使所有同名函數都可見


分享到:


相關文章: