1,C++屬于一個語言聯(lián)邦 : C Object-Oriented C++ Template C++ STL
2,應(yīng)盡量以const,enum,inline替換#define
如果一個常量是class專屬常量又是static,且為整數(shù)類型(int,char,bool),則需特殊處理:只要不取它們的地址,可以聲明并使用它們而不須提供定義式。
但是如果取某個class專屬常量的地址,或縱使不取其地址而編譯器卻(不正確的)堅持要看到一個定義式,必須提供定義式
enum的行為某方面比較像#define而不像const:取一個const地址合法,但是取一個enum地址就不合法,而取#define也不合法,如果不想讓被人獲得一個pointer或reference指向你的某個整數(shù)常量,enum可以幫助你實現(xiàn)這個約束
#define和enum不會設(shè)定額外的空間,const在優(yōu)秀的編譯器中也許可能也是這樣,但是不夠優(yōu)秀的編譯器就必須設(shè)定額外的空間
3,盡可能使用const
const語法雖然變化多端,但并不莫測高深。如果關(guān)鍵字const出現(xiàn)在*左側(cè),表示被指物是常量;如果出現(xiàn)在*右邊,表示指針自身是常量;如果出現(xiàn)在*兩邊,表示被指物和指針都是常量
std::vector<int> vec;
const std::vector<int>::interator iter = vec.begin() T*const
std::vector<int>:const_iterator iter = vec.begin() const T*
將const實施于成員函數(shù)的目的,是為了確認(rèn)該成員函數(shù)可作用于const對象身上
const特性的兩個函數(shù)可以重載(必須在類中,不在類中會出現(xiàn)問題) const函數(shù)不可改變對象任何變量(注意區(qū)分指針的情況,只要指針不變,指向的值可變化)
注意操作符[]的重載要返回值的引用,否則不能對結(jié)果賦值(相當(dāng)于對值的一個拷貝賦值,沒有意義)
mutable可以在常函數(shù)中修改其值,但是一般作用是:1,用于緩存2,或必須在常函數(shù)中修改值
利用const Operator[]實現(xiàn)出non-const版本:
class TextBlock {
public:
const char & operator[](std::size_t position) const { ..... return text[position]; }
char & operator[](std::size_t position){
return const_cast<char &>( static_cast<const TextBlock&> (*this)[position]);
};
非const調(diào)用const是安全的
const調(diào)用非const是不安全的,(因為非const有可能改變)
總結(jié):
1>將某些東西聲明為const可幫助編譯器偵測出錯誤用法。const可被施加于任何作用域內(nèi)的對象、函數(shù)參數(shù)、函數(shù)返回類型、成員函數(shù)本體
2>編譯器強制實施bitwise constness,但你編寫程序時應(yīng)該使用“概念上的常量性”(conceptual constness)
3>當(dāng)const和non-const成員函數(shù)有著實質(zhì)等價的實現(xiàn)時,令non-const版本調(diào)用const版本可避免代碼重復(fù)
4,確定對象被使用之前已被初始化:
為了保證一定性,使用內(nèi)建數(shù)據(jù)類型一定要初始化,構(gòu)造對象要保證構(gòu)造函數(shù)初始化它的成員
C++ 對“定義在不同的編譯單元內(nèi)的non-local static對象”的初始化相對次序并無明確定義。因為決定它們初始化次序相當(dāng)困難,根本無解
最常用的形式,就是 多個編譯單元內(nèi)的non-local static對象經(jīng)由“模板隱式具體化,implicit template instantiations"形式(而后者自己可能也是經(jīng)由”模板隱式具現(xiàn)化“形成)
任何一種non-const static對象,不論它是local或non-local,在多線程環(huán)境下“等待某事發(fā)生”都會有麻煩。處理方法:在程序的單線程啟動階段手工調(diào)用所有的reference-returning函數(shù),這可消除與初始化有關(guān)的“競速形勢”
為了避免在對象初始化之前過早地使用它們,你需要做三件事:
第一,手工初始化內(nèi)置型non-member對象。
第二,使用成員初值列對付對象的所有成分
第三,在”初始化次序不缺定性“(這對不同編譯單元所定義的non-local static對象是一種折磨)氛圍下加強設(shè)計
總結(jié):
1>為內(nèi)置對象進行手工初始化,因為C++不保證初始化它們
2>構(gòu)造函數(shù)最好使用成員初始化列表,而不要在構(gòu)造函數(shù)體內(nèi)使用賦值操作。次序和變量聲明的順序一致
3>為免除“跨編譯單元之初始化次序”問題,請以local static對象替換non-local static對象
5,編譯器默認(rèn)提供四個public且inline函數(shù),賦值,拷貝,構(gòu)造,析構(gòu),但是它們是在需要的時候才被編譯器創(chuàng)建出來的
C++不允許讓引用改指向不同的對象
>如果打算在一個“內(nèi)含reference成員”的class內(nèi)支持賦值操作,必須自己定義copy assignment操作符
>如果某個base class將copy assignment操作符聲明為PRivate,編譯器將拒絕為其derived classes 生成copy assignment操作符
6,若不想使用編譯器自動生成的函數(shù),就該明確拒絕:將他們聲明為private,同時不要實現(xiàn)他們,這樣的話即使friend或者成員函數(shù)調(diào)用他們連接器會提示錯誤
另外一種方法是把他們聲明為private在一個單獨的類中,然后用這個類繼承它
7,為多態(tài)基類聲明virtual析構(gòu)函數(shù)
>帶有多態(tài)性質(zhì)的base classes應(yīng)該聲明一個virtual析構(gòu)函數(shù)。如果class帶有任何virtual函數(shù),他就應(yīng)該擁有一個virtual析構(gòu)函數(shù)
>classes的設(shè)計目的如果不是作為base classes使用,或不是為了具備多態(tài)性,就不該聲明virtual析構(gòu)函數(shù)
8,別讓異常逃離析構(gòu)函數(shù)
>析構(gòu)函數(shù)絕對不要吐出異常。如果一個被析構(gòu)函數(shù)調(diào)用的函數(shù)可能拋出異常,析構(gòu)函數(shù)應(yīng)該捕捉任何異常,然后吞下它們(不傳播)或結(jié)束程序
>如果客戶需要對某個操作函數(shù)運行期間拋出異常作出反應(yīng),那么class應(yīng)該提供一個普通函數(shù)(而非在析構(gòu)函數(shù)中)執(zhí)行該操作
9,絕不在構(gòu)造和析構(gòu)過程中調(diào)用virtual函數(shù)
>無法使用virtual函數(shù)從base classes向下調(diào)用,在構(gòu)造期間,可以藉由“令derived classes將必要的構(gòu)造信息向上傳遞到base class構(gòu)造函數(shù)”替換加以彌補
>在構(gòu)造和析構(gòu)期間不要調(diào)用virtual函數(shù),因為這類調(diào)用從不下降至derived class(比起當(dāng)前執(zhí)行構(gòu)造函數(shù)和析構(gòu)函數(shù)的那層)
10,令operator=返回一個 reference to *this
目的是:為了實現(xiàn)“連鎖賦值”
11,在operator=中處理“自我賦值”
自我賦值 發(fā)生在對象被賦值給自己時
>確保當(dāng)對象自我賦值時operator=有良好行為。其中技術(shù)包括比較“來源對象”和“目標(biāo)對象”的地址、精心周到的語句順序、以及copy-and-swap
>確定任何函數(shù)如果操作一個以上的對象,而其中多個對象是同一對象時,其行為仍然正確
12,復(fù)制對象時勿忘其每一個成分
自己編寫copying函數(shù),要確保:
1,復(fù)制所有l(wèi)ocal成員變量
2,調(diào)用所有base classes內(nèi)的適當(dāng)copying函數(shù)
>Copying函數(shù)應(yīng)該確保復(fù)制“對象內(nèi)的所有成員變量”及“所有base class成分”
>不要嘗試以某個copying函數(shù)實現(xiàn)另一個copying函數(shù)。應(yīng)該將共同機能放進第三個函數(shù)中,并由兩個copying函數(shù)共同調(diào)用
所謂資源就是:一旦用了它,將來必須還給系統(tǒng),否則會出現(xiàn)問題。C++最常用的資源就是動態(tài)內(nèi)存分配,當(dāng)然還有其他資源:文件描述扶,互斥鎖,圖形界面中的字型和筆刷、數(shù)據(jù)庫連接、以及網(wǎng)絡(luò)sockets
13,以對象管理資源
兩個關(guān)鍵想法:
>獲得資源后立刻放進管理對象內(nèi)
>管理對象運用析構(gòu)函數(shù)確保資源被釋放
由于auto_ptr被銷毀時會自動刪除它所指之物,所以一定要注意別讓多個auto_ptr同時指向同一對象。如果真是那樣,對象會被刪除一次以上,而那會使你的程序搭上駛向“未定義行為”的快速列車上。為了預(yù)防這個問題,auto_ptr有一個不尋常的性質(zhì):若通過copy構(gòu)造函數(shù)或copy assignment操作符復(fù)制它們,它們會變成null,而復(fù)制所得的指針將取的資源的唯一控制權(quán)!!!(該性質(zhì)有利也有弊)
auto_ptr的替代方案是“引用計數(shù)型智慧指針”(RCSP),RCSP也是個智能指針,持續(xù)追蹤共有多少對象指向某筆資源,并在無人指向它時自動刪除該資源。RCSP提供的行為類似垃圾回收,不同的是RCSP無法打破環(huán)狀引用:::tr1:shared_ptr就是一個RCSP
注意:auto_ptr和tr1::shared_ptr兩者都在析構(gòu)函數(shù)內(nèi)做delete而不是delete[]動作!!如果對數(shù)組指針調(diào)用則是餿主意
>為了防止資源泄漏,請使用RAII對象,它們在構(gòu)造函數(shù)中活得資源并在析構(gòu)函數(shù)中釋放資源 (Resource Acquisition Is Initialization,RAII)
>兩個常被使用的RAII classes分別是tr1::shared_ptr和auto_ptr。前者通常是較佳選擇,已經(jīng)其copy行為比較直觀。若選擇auto_ptr,復(fù)制動作會時它(被復(fù)制物)指向null
14,在資源管理類中小心copying行為
在RAII中當(dāng)一個對象被復(fù)制時,出現(xiàn)嚴(yán)重后果,一般有兩種方案:
>禁止復(fù)制(聲明為private)
>對底層資源祭出“引用計數(shù)法”(reference-count) 這種做法一般如果內(nèi)含有一個tr1::shared_ptr ,但是默認(rèn)事件是count為0時刪除,幸運的是我們可以自己指定為0時的事件
類的析構(gòu)函數(shù)會自動調(diào)用其non-static成員變量的析構(gòu)函數(shù)
復(fù)制底部資源 (深度拷貝)
轉(zhuǎn)移底部資源的擁有權(quán) (auto_ptr)
15,在資源管理類中提供對原始資源的訪問
tr1::shared_ptr和auto_ptr都提供了一個get函數(shù),用來執(zhí)行顯式轉(zhuǎn)換,也就是它返回智能指針內(nèi)部的原始指針,以便于直接訪問時可以通過
它們還重載了(->和*)操作符
>APIs往往要求訪問原始資源(raw resources),所以每一個RAII class應(yīng)該提供一個“取得其所管理之資源”的辦法
>對原始資源的訪問可能經(jīng)由顯式轉(zhuǎn)換或隱式轉(zhuǎn)換。一般而言顯式轉(zhuǎn)換比較安全,但隱式轉(zhuǎn)換對客戶比較方便
16,成對使用new和delete時要采用相同形式
最好不要用typedef,否則會造成語義不清:
typedef std::string AddressLines[4];
std::string *pal = new AddressLines;
delete pal; 錯誤!!!
delete[] pal ; 正確!!!
17,以獨立語句將newed對象置入智能指針
> 以獨立語句將newed對象存儲于(置入)智能指針內(nèi)。如果不這樣做,一旦異常被拋出,有可能導(dǎo)致難以覺察的資源泄露
18,讓接口容易被正確使用,不易被誤用
>好的接口很容易被正確的使用,不容易被誤用。應(yīng)該在所有的接口中努力達成這些性質(zhì)
>“促進正確使用”的辦法包括接口一致性,以及與內(nèi)置類型的行為兼容
>“組織誤用”的辦法包括建立新類型、限制類型上的操作,束縛對象值,以及消除客戶的資源管理責(zé)任
>tr1::shared_ptr支持定制型刪除器。這個防范DLL問題,可被用來自動解除互斥鎖等等
19,設(shè)計class猶如設(shè)計type
好的types是一項艱巨的工作。好的types有自然的語法,直觀的語義,以及一或多個高效的實現(xiàn)品
如何設(shè)計好的classes:
>新的type的對象應(yīng)該如何被創(chuàng)建和銷毀
>對象的初始化和對象的賦值該有什么差別
>新的type的對象如果被passed by value(以值傳遞),意味著什么
>什么是新type的“合法值”
>新的type需要配合某個繼承圖系么
>新的type需要什么樣的轉(zhuǎn)換
>什么樣的操作符和函數(shù)對此新type而言是合理的
>什么樣的標(biāo)準(zhǔn)函數(shù)應(yīng)該駁回
>誰該取用新type的成員
>什么是新type的“未聲明接口”
>新type有多么一般化
>真的需要一個新type么
20,寧以pass-by-reference-to-const替換pass-by-value
>盡量以pass-by-reference-to-const替換pass-by-value。前者通常比較高效,并可避免切割問題
>以上規(guī)則并不適用于內(nèi)置類型,以及STL的迭代器和函數(shù)對象。對它們而言,pass-by-value往往比較適當(dāng)
21,必須返回對象時,別妄想返回其reference
任何時候看到一個reference聲明式,都應(yīng)該立刻問自己,它的另一個名稱是什么?因為它一定是某物的另一個名稱
任何函數(shù)如果返回一個reference指向某個local對象,都將一敗糊涂
>絕不要返回point或reference指向一個local stack對象,或返回reference指向一個heap-allocated對象,或返回pointer或reference指向一個local static對象而有可能同時需要多個這樣的對象
22,將成員變量聲明為private
>切記將成員函數(shù)聲明為private。這可賦予客戶訪問數(shù)據(jù)的一致性、可細微劃分訪問控制、允許約束條件獲得保證,并提供class作者以充分的實現(xiàn)彈性
>protected并不比public更具封裝性
23,寧以non-menber、non-friend替換menber函數(shù)
這樣做可以增加封裝性、包裹彈性和機能擴充性
24,若所有參數(shù)皆需要類型轉(zhuǎn)換,請為此采用non-member函數(shù)
>如果需要為某個函數(shù)的所有參數(shù)(包括被this指針?biāo)傅哪莻€隱喻參數(shù))進行類型轉(zhuǎn)換,那么這個函數(shù)必須是個non-member
25,考慮一個不拋出異常的swap函數(shù)
C++只允許對class template偏特化,在function templates身上偏特化行不通,解決方案是:為它添加一個重載版本
>當(dāng)std::swap對你的類型效率不高時,提供一個swap成員函數(shù),并確定 這個函數(shù)不拋出異常
>如果你提供一個member swap,也該提供一個non-member swap用來調(diào)用前者。對于classes(而非templates),也請?zhí)鼗痵td::swap
>調(diào)用swap時應(yīng)針對std::swap使用using聲明式,然后調(diào)用swap并且不帶任何“命名空間資格修飾”
>為“用戶定義類型”進行std template全特化是好的,但千萬不要嘗試在std內(nèi)加入某些對std而言全新的東西
26,盡可能延后變量定義式的出現(xiàn)時間
>盡可能延后變量定義式的出現(xiàn)。這樣做可增加程序的清晰度并改善程序的效率。
27,盡量少做轉(zhuǎn)型動作
>const_cast通常被用來將對象的常量性轉(zhuǎn)除。它也是唯一有此能力的C++-style轉(zhuǎn)型操作符
>dynamic_cast主要用來執(zhí)行“安全向下轉(zhuǎn)型”,也就是用來決定某對象是否歸屬繼承體系中的某個對象。它是唯一無法由舊式語法執(zhí)行的動作,也是唯一可能耗費重大運行成本的轉(zhuǎn)型動作
>reinterpret_cast意圖執(zhí)行低級轉(zhuǎn)型,實際動作(及結(jié)果)可能取決于編譯器,這也就是表示它不可移植。例如將point to int轉(zhuǎn)型為int
>static_cast用來強迫飲食轉(zhuǎn)換。例如將non-const轉(zhuǎn)型const對象,或?qū)nt轉(zhuǎn)型double等等。但是無法將const轉(zhuǎn)型non-const
任何一個類型轉(zhuǎn)換(不論是通過轉(zhuǎn)型操作而進行的顯式轉(zhuǎn)換,或通過編譯器完成的隱式轉(zhuǎn)換)往往真的令編譯器編譯出運行期執(zhí)行的碼
單一對象(例如Derived對象)可能擁有一個以上的地址(例如“以base *指向它”時的地址和“以Derived *指向它”時的地址)行為
之所以需要dynamic_cast,通常是因為想在一個認(rèn)定為derived對象身上執(zhí)行derived class操作函數(shù),但手上卻只有一個"指向base”的pointer或reference,只能靠它們呢來處理對象
絕對必須避免的一件事是所謂的"連串 dynamic_casts"
>如果可以,盡量避免轉(zhuǎn)型,特別是在注重效率的代碼中避免dynamic_casts。如果有個設(shè)計需要轉(zhuǎn)型這個動作,試著發(fā)展無需轉(zhuǎn)型的替代品
>如果轉(zhuǎn)型是必要的,試著將它隱藏于某個函數(shù)背后。客戶隨后可以調(diào)用該函數(shù),而不需要將轉(zhuǎn)型放進他們自己的代碼內(nèi)
>寧可使用C++-style轉(zhuǎn)型,不要使用舊式轉(zhuǎn)型。前者容易辨識出來,而且也比較有著分門別類的職掌
28,避免返回handles指向?qū)ο髢?nèi)部成分
兩個教訓(xùn):
--->成員變量的封裝性最多只等于"返回其reference"的函數(shù)的訪問級別
--->如果const成員函數(shù)傳出一個reference,后者所指數(shù)據(jù)與對象自身關(guān)聯(lián),而它又被存儲于對象之外,那么這個函數(shù)的調(diào)用者可以修改那筆數(shù)據(jù)
handles: 指針,reference,迭代器
避免返回handles(reference,pointer,iterator)指向?qū)ο髢?nèi)部。遵守這個約定可增加封裝性,幫助const成員函數(shù)的行為像個const,并將發(fā)生”虛吊號碼牌“(dangling handles)的可能性降到最低
29,為"異常安全"而努力是值得的
當(dāng)異常被拋出時,帶有異常安全性的函數(shù)會:
=>不泄露任何資源
=>不允許數(shù)據(jù)敗壞
異常安全函數(shù)提供以下三個保證之一:
==>基本承諾
==>強烈保證
==>不拋擲保證
1,> 異常安全函數(shù)(Exception-safe functions)即使發(fā)生異常也不會泄露資源或允許任何數(shù)據(jù)結(jié)構(gòu)敗壞。這樣的函數(shù)區(qū)分三種可能的保證:基本型,強烈型,不拋異常型
2,>“強烈保證"往往能夠以copy-and-swap實現(xiàn)出來,但"強烈保證"并非對所有函數(shù)都可實現(xiàn)或具備實現(xiàn)意義
3,>函數(shù)提供的"異常安全保證"通常最高只等于其所調(diào)用之各個函數(shù)的"異常安全保證"中的最弱者
30,透徹了解inlining的里里外外
inline只是對編譯器的一個申請,不是強制命令
inline函數(shù)通常 一定 被置于頭文件內(nèi)
inline 在大多數(shù)C++程序中時編譯器行為,只有少數(shù)基于.NET CLI(Common Language Infrastructure,公共語言基礎(chǔ)設(shè)施)可以在運行期完成inlining
Template通常也被置于頭文件中,以為一旦被使用,編譯器為了將它具現(xiàn)化,需要知道它長什么樣子(像inline),(并不是同一準(zhǔn)則,某些建置環(huán)境可以在連接期執(zhí)行template具現(xiàn)化)
編譯器拒絕將過于復(fù)雜的函數(shù)(循環(huán)或遞歸)的函數(shù)inlining。而所有對virtual函數(shù)的調(diào)用(除非最平淡無奇的)也對會使inling落空,因為virtual是運行期確定
編譯器通常不對“通過函數(shù)指針而進行的調(diào)用”實施inlining,因為要確定地址
inline函數(shù)無法升級
很多調(diào)試器對inline函數(shù)束手無策
>將大多數(shù)inlining限制在小型、被頻繁調(diào)用的函數(shù)身上。這可使日后的調(diào)試過程和二進制升級更容易,也可使?jié)撛诘拇a膨脹問題最小化,使程序的速度提升機會最大化
>不要只因為function templates出現(xiàn)在頭文件,就將它們聲明為inline
31,將文件間的編譯依存關(guān)系降至最低
如果使用object references 或 object pointers可以完成任務(wù),就不要使用objects
如果能夠,盡量以class聲明替換class定義式
為聲明式和定義式提供不同的頭文件
>支持“編譯依存性最小化”的一般構(gòu)想是:相依于聲明式,不要 相依于定義式。基于此構(gòu)想的兩個手段是Handle classes和Interface classes
>程序庫頭文件應(yīng)該以“完全且僅有聲明式”的形式存在。這種做法不論是否涉及templates都適用
繼承與面向?qū)ο?/p>
32,確定public繼承塑模出is-a關(guān)系
以C++進行面向?qū)ο缶幊蹋钜囊粋€原則:公開繼承 意味 "is-a“的關(guān)系
企鵝是一種鳥,但企鵝不會飛 理論解決辦法:1,雙繼承 2,重新(一個錯誤的)飛功能 3,都不寫飛功能
>"public"繼承意味著is-a。適用于base classes身上的每一件事情一定也適用于derived classes身上,因為每一個derived class對象也都是一個
base class對象
33,避免遮掩繼承而來的名稱

1 #include <iostream> 2 using namespace std; 3 4 class Base { 5 private: 6 int x; 7 public: 8 virtual void mf1() = 0; 9 virtual void mf1(int);10 virtual void mf2();11 void mf3();12 void mf3(double);13 };14 15 class Derived : public Base {16 public:17 virtual void mf1();18 void mf3();19 void mf4();20 };21 22 int main()23 {24 Derived d;25 int x;26 27 d.mf1(); //Derived::mf128 d.mf1(x); //ERROR !!! NO ARGS using Base::mf129 d.mf2(); //Base::mf230 d.mf3(); //Derived::mf331 d.mf3(x); //ERROR !!! NO ARGS using Base::mf332 33 return 0;34 }
上面的程序只是為了說明編譯時候的正確性,因此有些函數(shù)沒有實現(xiàn),鏈接肯定通不過,不過能說明問題
>derived classes內(nèi)的名稱會遮掩base classes內(nèi)的名稱。在public繼承下從來沒有人希望如此
>為了讓被遮掩的名稱再見天日,可使用using聲明式或轉(zhuǎn)交函數(shù)
34,區(qū)分接口繼承和實現(xiàn)繼承
聲明一個pure virtual函數(shù)的目的是為了讓derived classes只繼承函數(shù)接口
聲明簡樸的(非純)impure virtual函數(shù)的目的,是讓derived classes繼承該函數(shù)的接口和缺省實現(xiàn)
聲明non-virtual函數(shù)的目的是為了令derived classes繼承函數(shù)的接口及一份強制性實現(xiàn)
一個典型的程序有80%的執(zhí)行時間花費在%20的代碼身上
>接口繼承和實現(xiàn)繼承不同。在public繼承之下,derived classes總是繼承base class的接口
>pure virtual函數(shù)只具體指定接口繼承
>簡樸的(非純)impure virtual函數(shù)具體指定接口繼承及缺省實現(xiàn)繼承
>non-virtual函數(shù)具體指定接口繼承以及強制性實現(xiàn)繼承
35,考慮virutal 函數(shù)以外的其他選擇
>virtual函數(shù)的替代方案包括NVI手法及Stratrgy設(shè)計模式的多種形式。NVI手法自身是一個特殊形式的Template Method設(shè)計模式
>將機能從成員函數(shù)移到class外部函數(shù),帶來的一個缺點是,非成員函數(shù)無法訪問class的non-public成員
>tr1::function對象的行為就像一般函數(shù)指針。這樣的對象可接納“與給定值目標(biāo)簽名兼容”的所有可調(diào)用物
36,絕不重新定義繼承而來的non-virtual函數(shù)
>絕對不要重新定義繼承而來的non-virtual
37,絕不重新定義繼承而來的缺省參數(shù)值
virtual函數(shù)系動態(tài)綁定,而缺省參數(shù)值卻是靜態(tài)綁定
>絕對不要重新定義一個繼承而來的缺省參數(shù)值,因為缺省參數(shù)值都是靜態(tài)綁定,而virtual函數(shù)—你唯一應(yīng)該覆寫的東西——確實動態(tài)綁定
38,通過復(fù)合塑模出has-a或“根據(jù)某物實現(xiàn)出”
>復(fù)合的意義和public繼承完全不同
>在應(yīng)用域,復(fù)合意味has-a。在實現(xiàn)域,復(fù)合意味is-implemented-in-terms-of(根據(jù)某物實現(xiàn)出)
39,明智而審慎的使用private繼承
private繼承主要用于“當(dāng)一個意欲成為derived class者想訪問一個意欲成為base class者的protected成分,或為了重新定義一或多個virtual函數(shù)
EBO empty base optimization 空白基類最優(yōu)化,只適用于單一繼承
復(fù)合和private繼承都意味 is-implemented-in-terms-of
>Private 繼承意味is-implemented-in-terms-of(根據(jù)某物實現(xiàn)出)。它通常比復(fù)合的級別低。但是當(dāng)derived class需要訪問protected base class的成員,或需要重新定義繼承而來的virtual函數(shù)時,這么設(shè)計是合理的
>和復(fù)合不同,private繼承可以造成empty base最優(yōu)化。這對致力于“對象尺寸最小化”的程序庫開發(fā)者而言,可能很重要
40,明智而審慎的使用多重繼承
C++編譯器解析重載函數(shù)調(diào)用的規(guī)則:在看到是否有個函數(shù)可取用之前,C++首先確認(rèn)這個函數(shù)對此調(diào)用的最佳匹配。找出最佳匹配函數(shù)后才檢驗其可取用性
virtual base的初始化責(zé)任是由繼承體系中的最低層class 負(fù)責(zé)。這暗示:
1>classes若派生自virtual bases而需要初始化,必須認(rèn)知其virtual bases——不論那些bases距離多遠
2>當(dāng)一個新的derived class加入繼承體系中,它必須承擔(dān)起virtual bases(不論直接還是間接)的初始化責(zé)任
對待virtual base classes: 1,非必須不是有2,如果必須使用,盡可能避免在其中放置數(shù)據(jù)
>多重繼承比單一繼承復(fù)雜。它可能導(dǎo)致新的歧義性,以及對virtual繼承的需要
>virtual繼承會增加大小,速度,初始化(及賦值)復(fù)雜度等等成本。如果virtual base classes不帶任何數(shù)據(jù),將是最具實用價值的情況
>多重繼承的確有正當(dāng)用途。其中一個情節(jié)涉及"public繼承某個Interface class"和"private 繼承某個協(xié)助實現(xiàn)的class"的兩相組合
41,了解隱式接口和編譯期多態(tài)
Templates及泛型編程的世界,與面向?qū)ο笥懈镜牟煌T诖耸澜缰酗@式接口和編譯期多態(tài)依然存在,但重要性降低。反倒是隱式接口和編譯期多態(tài)移到前面了
>classes和templates都支持接口和多態(tài)
>對classes而言接口是顯式的,以函數(shù)簽名為中心。多態(tài)則是通過virtual函數(shù)發(fā)生于運行期
>對template參數(shù)而言,接口是隱式的,奠基于有效表達式。多態(tài)則是通過template具現(xiàn)化和函數(shù)重載解析發(fā)生于編譯期
42,了解typename的雙重意義
template內(nèi)出現(xiàn)的名稱如果相依于某個template參數(shù),稱之為從屬名稱
如果C++解析器在template中遭遇一個嵌套從屬名稱,它便假設(shè)這個名稱不是個類型,除非告訴它是(typename)
typename不可以出現(xiàn)在base classes list內(nèi)的嵌套從屬類型名稱之前,也不可以在member initialization list中作為base class修飾符
>聲明template參數(shù)時,前綴關(guān)鍵字class和typename可互換
>請使用關(guān)鍵字typename標(biāo)識嵌套從屬類型名稱;但不得在base class list(基類列)或member initialization list內(nèi)以它作為base class修飾符
43, 學(xué)習(xí)處理模板化基類內(nèi)的名稱
class 定義式最前面的"template<>"語法象征這既不是template也不是標(biāo)準(zhǔn)的class 。而是模板全特化
C++編譯期往往拒絕在templatized base classes(模板化基類)內(nèi)尋找繼承而來的名稱,因為有可能特化而擁有不同的接口!可這是個嚴(yán)重的問題,我們可以有三個辦法解決:
1,在調(diào)用base class函數(shù)動作之前加上this->
2, 使用using聲明式
3, 明白指出被調(diào)用函數(shù)位于base class內(nèi),但是如果是虛函數(shù)的話會導(dǎo)致關(guān)閉虛函數(shù)"virtual 綁定行為"
>可在derived class tempalte內(nèi)通過"this->"指涉base class templates內(nèi)的成員名稱,或藉由一個明白的"base class資格修飾符"完成
44, 將于參數(shù)無關(guān)的代碼抽離templates
>Template生成多個classses和多個函數(shù),所以任何template代碼都不該與某個構(gòu)造成膨脹的template參數(shù)產(chǎn)生相依關(guān)系
>因非類型模板參數(shù)而造成的代碼膨脹,往往可消除,做法是以函數(shù)參數(shù)或class成員變量替換template參數(shù)
>因類型參數(shù)而造成的代碼膨脹,往往可降低,做法是讓帶有完全相同二進制表述的具現(xiàn)類型共享實現(xiàn)碼
45,運用成員函數(shù)模板接受所有兼容類型
>請使用member function templates生成"可接受所有兼容類型"的函數(shù)
>如果你聲明member templates用于"泛化copy構(gòu)造"或"飯或assignment操作",你還是需要聲明正常的copy構(gòu)造函數(shù)和copy assignment操作符
46, 需要類型轉(zhuǎn)換時請為模板定義非成員函數(shù)
在template實參推導(dǎo)過程中從不將隱式類型轉(zhuǎn)換函數(shù)納入考慮
>當(dāng)我們編寫一個class template,而它所提供之"與此template相關(guān)的"函數(shù)支持"所有參數(shù)之隱式類型轉(zhuǎn)換"時,請將那些函數(shù)定義為"class template內(nèi)部的friend函數(shù)"
47, 請使用traits classes表現(xiàn)類型信息
>Traits classes使得"類型相關(guān)信息"在編譯期可用。它們以templates和"templates特化"完成實現(xiàn)
>整合重載技術(shù)后,traits classes有可能在編譯期對類型執(zhí)行if ... else 測試
48, 認(rèn)識template元編程

1 #include <iostream> 2 3 template<unsigned n> 4 struct Factorial 5 { 6 enum { value = n * Factorial<n-1>::value}; 7 }; 8 9 template<>10 struct Factorial<0> {11 enum { value = 1 };12 };13 14 15 int main(int argc,char *argv[])16 {17 std::cout << Factorial<5>::value << std::endl;18 std::cout << Factorial<10>::value << std::endl;19 20 return 0;21 }
好處:1,確保量度單位正確 2,優(yōu)化矩陣 3, 可以生成客戶定制之設(shè)計模式
>Template metaprograming可將工作由運行期移往編譯期,因而得以實現(xiàn)早期錯誤偵測和更高的執(zhí)行效率
>TMP可被用來生成"基于政策選擇組合"的客戶定制代碼,也可用來避免生成對某些特殊類型不適合的代碼
49,了解new-handler的行為
new-handler函數(shù)必須做以下事情:
1= 讓更多內(nèi)存可被使用
2= 安裝另一個new-handler
3= 卸除new-handler
4= 拋出bad_alloc(或派生自bad_alloc)的異常
5= 不返回
operator new要做以下事情:
1= 調(diào)用set_new_handler,告知Widget的錯誤處理函數(shù)。這會將class的new-handler安裝為global new-handler
2= 調(diào)用global operator new,執(zhí)行實際之內(nèi)存分配。如果分配失敗,global operator new會調(diào)用class的new-handler,因為那個函數(shù)才剛被安裝為global new-handler。如果global operator new最終無法分配足夠內(nèi)存,會拋出一個bad_alloc異常。在此情況下class的operator new必須恢復(fù)原來的global new-handler,然后再傳播該異常。為確保原來的new-handler總是能夠被重新安裝回去,class將global new-handler視為資源并遵守忠告,運用資源管理對象防止資源泄漏
3= 如果global operator new能夠分配足夠一個class對象所用的內(nèi)存,class的operator new會返回一個指針,指向分配所得。class析構(gòu)函數(shù)會管理golbal new-handler,它會自動將class ‘s operator new被調(diào)用前的那個global new-handler恢復(fù)回來
50,了解new和delete的合理替換機制
51,編寫new和delete時需固守常規(guī)
52,寫placement new 也要寫 placement delete
53,不要輕易忽略編譯期警告
54,讓自己熟悉包括TR1在內(nèi)的標(biāo)準(zhǔn)程序庫
55,讓自己熟悉Boost
有一種落差是,你配不上自己的野心,也辜負(fù)了所受的苦難
新聞熱點
疑難解答