Effective C++學(xué)習(xí)筆記
一、讓自己習(xí)慣C++
條款01:視C++為一個語言聯(lián)邦
條款02:盡量以const,enum,inline替換 #define
#define ASPECT_RATIO 1.653
const double AspectRatio = 1.653
string對象通常比char* 更好一點(diǎn) 對于class的專屬常量,為了限制作用域在class內(nèi),并且防止產(chǎn)生多個實(shí)體,最好使用static 如果你的編譯器支持在類內(nèi)對const static 整數(shù)類型聲明時(shí)獲初值,則使用 如果不支持,則在類內(nèi)定義,在對應(yīng)的實(shí)現(xiàn)文件中賦值 如果你需要在編譯器就使用一個class常量值,則應(yīng)最好改用枚舉類型enum,且枚舉不能用來取地址,不會為它分配額外的存儲空間 對于形似函數(shù)的宏,最好改用inline的模板函數(shù)
條款 03:盡可能使用const
const出現(xiàn)在星號左邊目標(biāo)是指物是常量,出現(xiàn)在星號右邊表示指針本身是常量,如果出現(xiàn)在兩邊,則指針和物都是常量 void f1(const Widget* pw)和void f2(Widget const* pw)兩種寫法意義相同,都表示被指物是常量對于STL迭代器來說,如果你希望迭代器所指的動科不可改動,你需要的是const_iterator 令函數(shù)返回一個常量值,往往可以降低因客戶錯誤而造成的意外(例如把一個值賦值給一個返回值) 將const實(shí)施與成員函數(shù)的目的是為了明確該成員函數(shù)可作用于const對象: 他們使class接口比較容易理解 他們使得可以操作const對象 const成員函數(shù)和no-const成員函數(shù)可重載,即可以同時(shí)出現(xiàn),在傳入不同的參數(shù)時(shí)候會調(diào)用不同的版本,但是有時(shí)我們需要這樣,但是又不想代碼重復(fù),我們可以在no-const成員調(diào)用const成員函數(shù)來處理這個代碼重復(fù)問題 例如: const_cast,經(jīng)過這樣里面 先安全轉(zhuǎn)型使得調(diào)用的是const版本,外面再去const轉(zhuǎn)型( static_cast (*this)[position]);
條款 04:確定對象被使用前已先被初始化
對于內(nèi)置類型要進(jìn)行手工初始化
構(gòu)造函數(shù)最好使用成員初值列表,不要在構(gòu)造函數(shù)中使用賦值操作來初始化,而且初值列表列出的成員變量次序應(yīng)該和在class中聲明的次序一樣,因?yàn)?strong style="color: black;">聲明次序就是C++保證的初始化次序
對于static對象,在跨編譯單元之間的初始化次序是不能確定的,因?yàn)镃++只保證在本文件內(nèi)使用之前一定被初始化了
舉例(使用如下方式可以解決這個問題即以loacl static對象替換non-local static對象):
class?FileSystem{...};
FileSystem&?tfs(){
????static?FileSystem?fs;
????return?fs;
}
二、構(gòu)造/析構(gòu)/賦值運(yùn)算
條款05:了解C++默默編寫并調(diào)用了哪些函數(shù)
如果你不定義,編譯器會自動幫你實(shí)習(xí)默認(rèn)的構(gòu)造函數(shù),析構(gòu)函數(shù),拷貝賦值運(yùn)算符和拷貝構(gòu)造函數(shù),但是如下幾種情況不會替你生成默認(rèn)的拷貝賦值運(yùn)算符
類中含有引用的成員變量
類中含有const的成員變量
類的基類中的拷貝賦值運(yùn)算符是私有成員函數(shù)
條款06:若不想使用編譯器自動生成的函數(shù),就應(yīng)該明確拒絕
當(dāng)我們不希望編譯器幫我們生成相應(yīng)的成員函數(shù)的時(shí)候,我們可以將其聲明為private并且不予以實(shí)現(xiàn)
條款07:為多態(tài)基類聲明virtual析構(gòu)函數(shù)
以下情況應(yīng)該為類聲明一個virtual析構(gòu)函數(shù): 用來作為帶有多態(tài)性質(zhì)的基類的類 一個類中帶有任何virtual函數(shù) 如果類的設(shè)計(jì)目的不是作為基類使用,那么就不應(yīng)該為它聲明virtual析構(gòu)函數(shù)
條款08:別讓異常逃離析構(gòu)函數(shù)
析構(gòu)函數(shù)不要吐出異常,如果實(shí)在要拋出異常,那么最好使用
std::abort();,放在catch中,把這個行為壓下去如果某個動作可能會拋出異常,那么最好把它放在普通函數(shù)中,而不是放在析構(gòu)函數(shù)里面,讓客戶來執(zhí)行這個函數(shù)并去處理
條款09:絕不再構(gòu)造和析構(gòu)函數(shù)中調(diào)用virtual函數(shù)
在構(gòu)造和析構(gòu)的時(shí)候,不要試圖調(diào)用或在調(diào)用的函數(shù)中調(diào)用virtual函數(shù),因?yàn)闀{(diào)用父類版本導(dǎo)致出現(xiàn)一些未定義的錯誤
解決辦法之一:
class?Transaction{
????publci:
?????explicit?Transaction(const?std::string&?logInfo);
?????void?logTransaction(const?std::string&?logIngo)?const;//把它變成這樣的non-virtual函數(shù)
?????...
};
Transaction::Transaction(const?std::string&?logInfo){
????...
????logTransaction(logInfo);//這樣調(diào)用
}
class?BuyTransaction:?public?Transaction{
?????BuyTransaction(?parameters?):Transaction(createLogString(?parameters?)){...}//將log信息傳給基類的構(gòu)造函數(shù)
????private:
?????static?std::string?createLogString(?parameters?);//注意此函數(shù)為static函數(shù)
}
條款10:令operator= 返回一個reference to *this
為了實(shí)現(xiàn)連鎖賦值如內(nèi)置類型
x= y = z =15由于=采用右結(jié)合律,所以等價(jià)于x = (y = (z = 15)),因此,為了使我們自定義類也實(shí)現(xiàn),所以*重載=,+=,-=,*=使其返回refercence to this
條款11:在operator= 中處理“自我賦值”
在賦值的時(shí)候會出現(xiàn)對自我進(jìn)行賦值的情況,這種情況下我們很容易寫出不安全的代碼
Widget::operator=(const?Widget&?rhs){
?delete?pb;?//把自己釋放了
?pb?=?new?Bitmap(*rhs.pb);//這就不安全了
?return?*this;
}
因此有三種推薦的做法
先驗(yàn)證是不是相同的,是不是自我賦值
Widget::operator=(const?Widget&?rhs){
if(this?==?&rhs)?return?*this;//驗(yàn)證是不是相同
?delete?pb;?
?pb?=?new?Bitmap(*rhs.pb);
?return?*this;
}
在復(fù)制pb所指的東西之前別刪除pb
Widget::operator=(const?Widget&?rhs){
?Bitmap*?pOrig?=?pb;
?pb?=?new?Bitmap(*rhs.pb);//讓pb指向*pb的一個副本
????delete?pOrig;?//刪除原先的pb
?return?*this;
}
使用交換數(shù)據(jù)的函數(shù)
class?Widget{
...
void?swap(Widget&?rhs);//交換*this和rhs的數(shù)據(jù)
...
};
Widget::operator=(const?Widget&?rhs){
?Widget?temp(rhs);//創(chuàng)建一個rhs副本
?swap(temp);//交換*this和上面的副本
?return?*this;
}
條款12:復(fù)制對象時(shí)勿忘其每一個成分
為了確保復(fù)制的時(shí)候復(fù)制對象內(nèi)的所有成員變量,我們應(yīng)該在字類的構(gòu)造和賦值函數(shù)中調(diào)用父類的構(gòu)造和賦值函數(shù)來完成各自的任務(wù)
不要嘗試在復(fù)制構(gòu)造函數(shù)和賦值函數(shù)中相互調(diào)用,如果想消除重復(fù)代碼,請建立一個新的成員函數(shù),并且最好將其設(shè)為私有且命名為init
三、資源管理
條款13:以對象管理資源
為了防止資源泄露,我們應(yīng)該在構(gòu)造函數(shù)中獲取資源,在析構(gòu)函數(shù)中釋放資源,這樣可以有效的避免資源泄露
使用智能指針是一個好的辦法,在C++11中auto_ptr已經(jīng)被棄用,有三個常用的是unique_ptr,share_ptr和weak_ptr
條款14:在資源管理類中心copying行為
我們在管理RAII(構(gòu)造函數(shù)中獲得,析構(gòu)函數(shù)中釋放)觀念的類時(shí),應(yīng)該對不同的情況,根據(jù)不同的目的進(jìn)行處理
當(dāng)我們處理不能同步擁有的資源的時(shí)候,可以才用禁止復(fù)制,如把copying操作聲明為private
當(dāng)我們希望共同擁有資源的時(shí)候,可以采用引用計(jì)數(shù)法,例如使用shared_ptr
當(dāng)我們需要拷貝的時(shí)候,可以采用深拷貝
或者某些時(shí)候我們可以采用轉(zhuǎn)移底部資源擁有權(quán)的方式
條款15:在資源管理類中提供對原始資源的訪問
有的api函數(shù)往往需要訪問類的原始資源,所以每一個RAII類應(yīng)該提供一個返回其管理的原始資源的方法
返回原始資源可以使用顯示轉(zhuǎn)換也可以使用隱式轉(zhuǎn)換,但是往往顯示轉(zhuǎn)換更加安全一點(diǎn),但是隱式轉(zhuǎn)換更加方便
class?Font{
?...
?FontHandle?get()?const?{return?f;}?//顯示轉(zhuǎn)換
?...
?operator?FontHandle()?const?{return?f;}?//隱式轉(zhuǎn)換函數(shù)
?....
?private:
??FontHandle?f;?//管理的原始資源
}
條款16:成對使用new和delete時(shí)要采用相同形式
不要對數(shù)組形式做typedef,因?yàn)檫@樣會導(dǎo)致delete的時(shí)候調(diào)用的是
delete ptr而不是delete [] ptr,對內(nèi)置類型會出現(xiàn)未定義或有害的,對類的類型會導(dǎo)致無法調(diào)用剩余的析構(gòu)函數(shù),導(dǎo)致類中管理的資源無法釋放,從而造成內(nèi)存泄漏在new 表達(dá)式中使用[ ] ,則在相應(yīng)的delete 表達(dá)式中也使用 [ ]
條款17:以獨(dú)立語句將newed對象置入智能指針
諸如這樣的語句 ? processWidget (std::tr1::shared_ptr(new Widget),priority()) 因此要以獨(dú)立語句將newd對象存儲于智能指針中,把它分離出來。如果不這樣,一旦異常被拋出,有可能導(dǎo)致難以察覺的資源泄漏 在先執(zhí)行 ? new Widget語句和調(diào)用 ?std::tr1::shared_ptr構(gòu)造函數(shù)之間不能確定 priority函數(shù)的執(zhí)行順序,可能在最前面,也可能在他們的中間
四、設(shè)計(jì)與聲明
條款18:讓接口容易被正確使用,不易被誤用
我們接口應(yīng)該替客戶著想,考慮周全,避免它們犯錯誤。例如在向函數(shù)傳遞日期的時(shí)候,把日期參數(shù)做成類的形式,并且用static成員函數(shù)來返回固定的月份,避免用戶參數(shù)寫錯
接口應(yīng)該和內(nèi)置接口保持一致,避免讓客戶感覺不舒服,這方面STL做的很好
tr1::shared_ptr支持定制型刪除器,使用它可以防范跨DLL構(gòu)建和刪除的問題,可以用它來自動解除互斥鎖
條款19:設(shè)計(jì)class猶如設(shè)計(jì)type
謹(jǐn)慎的設(shè)計(jì)一個類,應(yīng)該遵守以下規(guī)范
合理的構(gòu)建class的構(gòu)造函數(shù)、析構(gòu)函數(shù)和內(nèi)存分配函數(shù)以及釋放函數(shù)
不能把初始化和賦值搞混了
如果你的類需要被用來以值傳遞,復(fù)制構(gòu)造函數(shù)應(yīng)該設(shè)計(jì)一個通過值傳遞的版本
你應(yīng)該給你的成員變量加約束條件,保證他們是合法值,所以你的成員函數(shù)必須擔(dān)負(fù)起錯誤檢查工作
如果你是派生類,那么你應(yīng)該遵守基類的一些規(guī)范,如析構(gòu)函數(shù)是否為virtural
你是否允許你的class有轉(zhuǎn)換函數(shù),,是否允許隱式轉(zhuǎn)換。如果你只允許explicit構(gòu)造函數(shù)存在,就得寫出專門負(fù)責(zé)執(zhí)行轉(zhuǎn)換的函數(shù)
想清楚你的類應(yīng)該有哪些函數(shù)和成員
哪些應(yīng)該設(shè)計(jì)為私有
哪個應(yīng)該是你的friend,以及將他們嵌套與另一個是否合理
對效率,異常安全性以及資源運(yùn)用提供了哪些保證
如果你定義的不是一個新type,而是定義整個type家族,那么你應(yīng)該定義一個類模板
如果只是定義新的字類以便為已有的類添加機(jī)制,說不定單純定義一個或多個non-member函數(shù)或模板更好
條款20:寧以pass-by-reference-to-const替換pass-by-value
盡量以pass-by-reference-to-const替換pass-by-value,因?yàn)榍罢咄ǔ1容^高效,比如在含有類的傳遞時(shí),避免了多次構(gòu)造函數(shù)和多次析構(gòu)函數(shù)的調(diào)用,大大的提高了效率
但是對于某些,比如內(nèi)置類型,迭代器,函數(shù)調(diào)用等最好以值傳遞的形式
條款21:必須返回對象時(shí),別妄想返回其reference
絕對不能返回指針或者一個引用指向一個臨時(shí)變量,因?yàn)樗嬖?strong style="color: black;">棧中,一旦函數(shù)調(diào)用結(jié)束返回那么你得到的將是一個壞指針,也不能使用static變量來解決,你可以通過返回值 來解決
條款22:將成員變量聲明為private
成員變量要聲明為private protected并不比public更具封裝性 為了保證一致性 可以細(xì)微的劃分訪問和控制以及約束 內(nèi)部更改后不影響使用
條款23:寧以non-member、non-friend、替換member函數(shù)
我們可以用non-member、non-friend函數(shù)來替換某些成員函數(shù),可以增加類的封裝性,包裹彈性和擴(kuò)充性
條款24:若所有參數(shù)皆需要類型轉(zhuǎn)換,請為此采用non-member函數(shù)
如果你需要為某個函數(shù)的所有參數(shù)(包括被this指針?biāo)傅哪莻€隱喻參數(shù))進(jìn)行類型轉(zhuǎn)換,那么這個函數(shù)必須是個non-member
條款25:考慮寫出一個不拋出異常的swap函數(shù)
在你沒有定義swap函數(shù)的情況下,編譯器會為你調(diào)用通用的swap函數(shù),但是有的時(shí)候那并不是高效的,因?yàn)槟J(rèn)情況它在置換如指針的時(shí)候把整個內(nèi)存都置換
我們采取一種解決辦法
在類中提供一個 public swap成員函數(shù),并且這個函數(shù)不能拋出異常
在類的命名空間中提供一個non-member swap函數(shù),并令它調(diào)用類中的swap函數(shù)
如果你正在編寫一個類而不是模板類,為你的class特化std::swap函數(shù),并令它調(diào)用你的swap函數(shù)
請?jiān)陬愔?strong style="color: black;">聲明
using std::swap,讓其暴露,使得編譯器自行選擇更合適的版本
五、實(shí)現(xiàn)
條款26:盡可能延后變量定義式的出現(xiàn)時(shí)間
定義一個變量,那么你就得承受這個變量的構(gòu)造和析構(gòu)的成本時(shí)間,所以在定義一個變量的時(shí)候我們應(yīng)該盡可能的延后定義時(shí)間,在使用前定義,這樣避免我們定義了卻沒有使用它,造成浪費(fèi)
條款27:盡量少做轉(zhuǎn)型動作
舊式轉(zhuǎn)型是C風(fēng)格的轉(zhuǎn)型,C++中提供四種新式轉(zhuǎn)型: 舊式轉(zhuǎn)型使用的時(shí)機(jī)是,當(dāng)要調(diào)用一個explicit構(gòu)造函數(shù)對一個對象傳遞給一個函數(shù)時(shí),其他盡量用新式轉(zhuǎn)型 const_cast 通常被用來將對象的常量性轉(zhuǎn)除。它也是唯一有此能力的轉(zhuǎn)型操作符
dynamic_cast 主要用來執(zhí)行“安全向下轉(zhuǎn)型” ,也就是用來決定對某對象是否歸屬繼承體系中的某個類型。它是唯一無法由舊式語法執(zhí)行的動作,也是唯一可能耗費(fèi)重大運(yùn)行成本的轉(zhuǎn)型動作
reinterpret_cast 意圖執(zhí)行低級轉(zhuǎn)型,實(shí)際動作(及結(jié)果)可能取決于編譯器,這也就表示它不可移植。例如將一個pointer to int轉(zhuǎn)型為一個int。這一類轉(zhuǎn)型在低級代碼以外很少見。
static_cast 用來強(qiáng)迫隱式轉(zhuǎn)換,例如將non-const對象轉(zhuǎn)換為const對象,或?qū)nt轉(zhuǎn)為double等等,它也可以用來執(zhí)行上述多種轉(zhuǎn)換的反向轉(zhuǎn)換,例如將void* 指針轉(zhuǎn)為 type 指針,將pointer-to-base 轉(zhuǎn)為 pointer-ro-derived 。但它無法將 const 轉(zhuǎn)為 non-const ——這個只有const_cast才能辦到
請記住以下: 如果可以的話,避免dynamic_cast轉(zhuǎn)型,如果實(shí)在需要,則可以試著用別的無轉(zhuǎn)型方案代替 如果轉(zhuǎn)型是必要的,那么應(yīng)該把他隱藏于某個函數(shù)背后,客戶隨后可以調(diào)用該函數(shù),而不是需要將轉(zhuǎn)型放進(jìn)自己的代碼里 寧可要新型轉(zhuǎn)型,也不要使用舊式轉(zhuǎn)型
條款28:避免返回handles指向?qū)ο髢?nèi)部成分
避免返回handle(包括引用,指針和迭代器)指向?qū)ο髢?nèi)部。這樣可以增加封裝性,也能把出現(xiàn)空懸指針的可能性降低
條款29:為“異常安全”而努力是值得的
異常安全函數(shù)提供以下三個保證之一: 基本承諾:如果異常被拋出,程序內(nèi)的任何事物仍然保持在有效狀態(tài)下。沒有任何對象或數(shù)據(jù)會因此而敗壞,所有對象都處于一種內(nèi)部前后一致的狀態(tài)。然而程序的現(xiàn)實(shí)狀態(tài)恐怕不可預(yù)料 強(qiáng)烈保證:如果異常被拋出,程序狀態(tài)不改變。調(diào)用這樣的函數(shù)需要有這樣的認(rèn)知:如果函數(shù)成功,就是完全成功,如果函數(shù)失敗,程序會恢復(fù)到“調(diào)用之前”的狀態(tài) 不拋擲保證:承諾絕不拋出異常,因?yàn)樗鼈兛偸悄軌蛲瓿伤麄冊瘸兄Z的功能。作用于內(nèi)置類型身上所有操作都提供nothrow保證,這是異常安全碼中一個必不可少的關(guān)鍵基礎(chǔ)材料 這三種保證是遞增的關(guān)系,但是如果我們實(shí)在做不到,那么可以提供第一個基本承諾,我們在寫的時(shí)候應(yīng)該想如何讓它具備異常安全性 首先以對象管理資源可以阻止資源泄漏 在你能實(shí)現(xiàn)的情況下,盡量滿足以上的最高等級
條款30:透徹了解inlining 的里里外外
inline 聲明的兩種方式: 隱喻的inline申請,即把定義寫在class內(nèi)部 明確聲明,即在定義式前加上關(guān)鍵字inline 將大多數(shù)inlining限制在小型、被頻繁調(diào)用的函數(shù)身上。這可使日后調(diào)試和二進(jìn)制升級更容易,也可使得潛在的代碼膨脹問題最小化。 不要只因?yàn)閒unction templates出現(xiàn)在頭文件,就將他們聲明為inline
條款31:將文件間的編譯依存關(guān)系降至最低
支持“編譯依存性最小化”的思想是:相依于聲明式,不要相依于定義式
頭文件和實(shí)現(xiàn)相分離,頭文件完全且僅有聲明式
使用創(chuàng)建接口類
六、繼承與面向?qū)ο笤O(shè)計(jì)
條款32:確定你的public繼承塑模出is-a關(guān)系
public繼承意味著is-a的關(guān)系,即子類是父類的一種特殊化,適合基類的一定適合子類,每個派生類對象含有著父類對象的特點(diǎn)
條款33:避免遮掩繼承而來的名稱
在父類中的名稱會被字類的名稱覆蓋,尤其是在public繼承下,沒有人希望這樣的發(fā)生
為了避免被遮掩,可以使用using聲明式或轉(zhuǎn)交函數(shù),交給子類
條款34:區(qū)分接口繼承和接口實(shí)現(xiàn)
聲明純虛函數(shù)的目的就是為了讓派生類只繼承函數(shù)接口
聲明虛函數(shù)的目的是讓派生類繼承該函數(shù)的接口和缺省實(shí)現(xiàn)
聲明普通函數(shù)的目的就是讓派生類強(qiáng)制接受自己的代碼,不希望重新定義
條款35:考慮virtual函數(shù)以外的其他選擇
條款36:絕不重新定義繼承而來的non-virtual函數(shù)
任何情況下都不應(yīng)該重新定義一個繼承而來的non-virtual函數(shù)
條款37:絕不重新定義繼承而來的缺省參數(shù)值
絕對不要重新定義一個繼承而來的缺省參數(shù)值,因?yàn)?strong style="color: black;">缺省參數(shù)值都是靜態(tài)綁定的,而virtual函數(shù)——你唯一應(yīng)該覆寫的東西是動態(tài)綁定
條款38:通過復(fù)合塑模has-a或“根據(jù)某物實(shí)現(xiàn)出”
區(qū)分public繼承和復(fù)合
在應(yīng)用領(lǐng)域,復(fù)合意味著一個中含有另一個,即has-a關(guān)系;
在實(shí)現(xiàn)領(lǐng)域意味著根據(jù)某物實(shí)現(xiàn)出
條款39:明智而審慎地使用private繼承
當(dāng)需要復(fù)合時(shí),盡可能的使用復(fù)合,必要時(shí)才使用private:
當(dāng)protected成員或virtual函數(shù)牽扯進(jìn)來的時(shí)候
當(dāng)空間方面的利害關(guān)系,需要尺寸最小化
條款40:明智而審慎地使用多重繼承
多重繼承時(shí)候,如果其父類又繼承同一個父類,所以解決的方式就是使用virtual繼承,即其父類同時(shí)以virtual繼承那個父類,但是相應(yīng)的也會付出一些代價(jià),例如時(shí)間更慢,需要重新定義父類的初始化等,因此設(shè)計(jì)時(shí)最好不要讓這個父類有任何數(shù)據(jù)成員
當(dāng)單一繼承和多重繼承都可以,那么最好選擇單一繼承,多重繼承也有正當(dāng)的用途,可以實(shí)現(xiàn)同時(shí)public繼承和private繼承的組合
七、模板與泛型編程
條款41:了解隱式接口和編譯期多態(tài)
顯式接口:由函數(shù)的簽名式(也就是函數(shù)名稱、參數(shù)類型、返回類型)構(gòu)成
隱式接口:不基于函數(shù)簽名式,而是由有效表達(dá)式組成
面向?qū)ο蠛头盒途幊潭贾С纸涌诤投鄳B(tài),只不過一個以顯式為主,一個以隱式為主
兩種多態(tài)一個在運(yùn)行期一個在編譯期
條款42:了解typename的雙重意義
聲明模板參數(shù)的時(shí)候,class和typename是可以互換的,沒什么不一樣
但是標(biāo)識嵌套從屬類型名稱的時(shí)候必須用typename
不得在基類列(繼承的時(shí)候)或成員初值列(初始化列表)內(nèi)以它作為基類修飾符
templete<typename?T>
class?Derived:public?Base::Nested{?//基類列表中不可以加“typename”
public:
????explicit?Derived(int?x):?Base::Nested(x) {//mem.init.list中不允許“typename”
????????typename?Base::Nested?temp;?//這個是嵌套從屬類型名稱
????????...?//作為一個基類修飾符需要加上typename
????}
}???
條款43:學(xué)習(xí)處理模板化基類內(nèi)的名稱
模板化基類指的是當(dāng)派生類的基類是一個模板 但是當(dāng)有模板全特化的時(shí)候,確實(shí)使用的沒有這個函數(shù),那么依然會報(bào)錯 當(dāng)派生類中使用父類的函數(shù)時(shí)候編譯器會報(bào)錯,因?yàn)榫幾g器不能確定當(dāng)模板實(shí)例化的時(shí)候,是否真的有那個函數(shù),因此有三種方法 在基類函數(shù)調(diào)用之前加上 ? this->使用 using 聲明式 ?,告訴編譯器,請它假設(shè)這個函數(shù)存在 指出這個函數(shù)在基類中,使用 ? 基類 : : 函數(shù)? 的形式寫出來(不推薦這個,因?yàn)槿绻莢irtual函數(shù),則 會影響動態(tài)綁定)
條款44:將與參數(shù)無關(guān)的代碼抽離出來
模板生成多個類和多個函數(shù),所以任何模板代碼都不該和某個造成膨脹的模板參數(shù)產(chǎn)生相依關(guān)系
因非類型模板參數(shù)造成的代碼膨脹,往往可以消除,做法是以函數(shù)參數(shù)或類成員變量替換模板參數(shù)
因類型模板參數(shù)造成的代碼膨脹,往往可以降低,做法是讓帶有完全相同的二進(jìn)制表述 的具體類型共享實(shí)現(xiàn)碼
條款45:運(yùn)用成員函數(shù)模板接受所有兼容類型
使用成員函數(shù)模板可以生成接收所有兼容類型的函數(shù)
如果你聲明成員函數(shù)模板用來泛化拷貝構(gòu)造函數(shù)和賦值操作,那么你還需要聲明正常的拷貝構(gòu)造函數(shù)和賦值操作
條款46:需要類型轉(zhuǎn)換時(shí)請為模板定義非成員函數(shù)
當(dāng)我們編寫一個模板類,它提供的和這個模板祥光的函數(shù)支持所有參數(shù)的隱式類型轉(zhuǎn)換,請將哪些函數(shù)定義為模板類的內(nèi)部的friend函數(shù)
條款47:請使用traits class表現(xiàn)類型信息
條款48:認(rèn)識template元編程
八、定制new和delete
條款49:了解new—handler的行為
當(dāng)new分配失敗的時(shí)候,它會先調(diào)用一個客戶指定的錯誤處理函數(shù)(set_new_handler),一個所謂的new—handler
它是一個typedef定義出一個指針指向函數(shù),該函數(shù)沒有參數(shù)也不返回任何東西
set_new_handler的參數(shù)是個指針指向operator new 無法分配足夠內(nèi)存時(shí)該被調(diào)用的函數(shù)。其返回值也是個指針,指向set_new_handler 被調(diào)用前正在執(zhí)行(馬上就要被替換)的那個new—handler函數(shù)
一個良好設(shè)計(jì)的new—handler函數(shù)必須做以下事情:
**讓更多內(nèi)存可被使用。**此策略的一個做法是,程序一開始就分配一大塊內(nèi)存,而后當(dāng)其第一次被調(diào)用,將它釋還給程序使用
**安裝另一個new—handler。**可以設(shè)置讓其調(diào)用另一個new—handler來替換自己,用來做不同的事情,其做法是調(diào)用set_new_handler
卸載new—handler,也就是將null指針傳給set_new_handler,這樣new在分配不成功時(shí)拋出異常
拋出bad_alloc的異常。
不返回,調(diào)用abort或exit
C++并部支持類的專屬new—handler,但其實(shí)也不需要。你可以令每個類提供自己的set_new_handler和operator new即可
set_new_handler允許客戶指定一個函數(shù),在內(nèi)存分配無法獲得滿足時(shí)調(diào)用。
Nothrow new是一個頗為局限的工具,因?yàn)樗?strong style="color: black;">只適用于內(nèi)存分配:后繼的構(gòu)造函數(shù)調(diào)用還是可能拋出異常
條款50:了解new和delete的合理替換時(shí)機(jī)
替換operator new或operator delete的三個常見理由: 用來檢測運(yùn)用上的錯誤 為了收集使用上的統(tǒng)計(jì)數(shù)據(jù) 為了增加分配和歸還的速度 為了降低缺省內(nèi)存管理器帶來的空間額外開銷,也就是實(shí)現(xiàn)內(nèi)存池,可以節(jié)省空間 為了彌補(bǔ)缺省分配器中的非最佳齊位 為了將相關(guān)對象成簇集中 為了獲得非傳統(tǒng)行為 了解何時(shí)可在“全局性的”或“class專屬的”基礎(chǔ)上合理替換缺省的new和delete
條款51:編寫new和delete時(shí)需固守常規(guī)
operator new應(yīng)該內(nèi)含有一個無窮的循環(huán),并在其中嘗試分配內(nèi)存,如果它無法滿足內(nèi)存需求,就該調(diào)用new-handler。它也應(yīng)該有能力處理0 bytes申請,即將其按照1 byte分配。Class 專屬版本應(yīng)該處理“比正確大小更大的(錯誤)申請”,因?yàn)?strong style="color: black;">當(dāng)有字類繼承的時(shí)候,會出現(xiàn)傳入的大小和父類大小不同,所以要進(jìn)行判斷形如
if(size != sizeof(父類))operator delete應(yīng)該在收到NULL指針的時(shí)候什么也不做,必要時(shí)交給全局的operator new來處理。
條款52:寫了placement new也要寫placement delete
當(dāng)你寫一個placement operator new ,請確定也寫了對應(yīng)的placement operator delete版本。如果沒有這樣做,可能回發(fā)生隱微而時(shí)斷時(shí)續(xù)的內(nèi)存泄露
當(dāng)你聲明placement new 和placement delete,請確定不要無意識(非故意)地遮掩正常的全局版本,你如果想提供自定義形式,請內(nèi)含所有正常形式的new和delete或利用繼承機(jī)制及using聲明式
九、雜項(xiàng)討論
條款53:不要輕忽編譯器的警告
不同的編譯器有不同的警告標(biāo)準(zhǔn),要嚴(yán)肅對待編譯器發(fā)出的警告信息。努力在你的編譯器的最高警告級別下爭取“無任何警告”的榮譽(yù)
不要過度依賴編譯器的報(bào)警能力,因?yàn)椴煌木幾g器對待事情的態(tài)度并不相同。一旦移植到另一個編譯器上,你原本依賴的警告信息有可能消失
條款54:讓自己熟悉包括TR1在內(nèi)的標(biāo)準(zhǔn)程序庫
C++標(biāo)準(zhǔn)程序庫的主要機(jī)能由STL、iostream、locales組成。并包含C99標(biāo)準(zhǔn)程序庫。
TR1添加了智能指針(例如 tr1::shared_ptr)、一般化函數(shù)指針(tr1::function)、hash-based容器、正則表達(dá)式以及另外10個組件的支持
TR1自身知識一份規(guī)范。為了獲得TR1提供的好處,你需要一份實(shí)物。一個好的實(shí)物來源是Boost。
條款55:讓自己熟悉Boost
Boost是一個社群,也是一個網(wǎng)站。致力于免費(fèi)、源碼開放、同僚復(fù)審的C++程序庫開發(fā)。Boost在C++標(biāo)準(zhǔn)化過程中扮演具有影響力的角色
Boost提供許多TR1組件實(shí)現(xiàn)品,以及其他許多程序庫


▊《Effective C++:改善程序與設(shè)計(jì)的55個具體做法(第三版)中文版》
Scott Meyers 著
侯捷 譯
一本輕薄短小高密度的“專家經(jīng)驗(yàn)累積”
國際影響力波及了整個計(jì)算機(jī)技術(shù)出版領(lǐng)域
本書不是讀完一遍就可以束之高閣的快餐讀物,也不是用以解決手邊問題的參考手冊,而是需要您去反復(fù)閱讀體會的,C++是真正程序員的語言,背后后精神的思想與無以倫比的表達(dá)能力,這使得它具有類似宗教般的魅力。希望這本書能夠幫您跨越C++的重重險(xiǎn)阻,領(lǐng)略高處才有的壯美風(fēng)光,做一個成功而快樂的C++程序員。
(快快掃碼搶購吧?。?/span>
如果喜歡本文 歡迎?在看丨留言丨分享至朋友圈?三連 ?熱文推薦??
