欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Effective C++ 入門學(xué)習(xí)筆記

 更新時間:2023年08月10日 10:51:18   作者:Cloudox_  
這篇文章主要介紹了Effective C++ 入門學(xué)習(xí)筆記,需要的朋友可以參考下

讓自己習(xí)慣C++

條款01:視C++為一個語言聯(lián)邦

可以將C++看作四部分組成,因為每個部分的思路都不同,分而治之效果更佳:1.C語言。C++仍以C為基礎(chǔ)2.objected-oriented C++。面向?qū)ο缶幊蹋?、封裝、繼承、多態(tài)3.template C++。C++泛型編程、模板元編程的基礎(chǔ)4.STL。容器、迭代器、算法

通常,內(nèi)置類型通過值傳遞比引用效率更高;而自定義類型通過引用比值傳遞效率更高。

條款02:盡量以const、enum、inline替換 #define

#define 最好只用于復(fù)雜變量名替換。

條款03:盡可能使用const

通??梢詫onst理解為只讀權(quán)限。使用const有以下好處:1.讓編譯器參與幫助檢測變量的權(quán)限。當(dāng)使用了const聲明變量后,就不必?fù)?dān)心該變量被改變了,因為編譯器會幫你檢測;2.讓使用者明白。當(dāng)參數(shù)中使用了const后,使用者會很容易知道這個參數(shù)是只讀還是讀寫,這樣就可以判斷是入?yún)⑦€是出參了,如果不聲明const,恐怕使用者都會進去看一眼。

const可以批量設(shè)置變量屬性,如一個結(jié)構(gòu)體被聲明為const,這時結(jié)構(gòu)體內(nèi)成員都將成為只讀權(quán)限。如果此時結(jié)構(gòu)體內(nèi)某個成員需要可寫屬性,就可以使用 mutable 關(guān)鍵字來解除限制,使其永久可寫。

對于成員函數(shù)中使用后置const的行為,如 void printall() const ,主要是讓使用者更有信心,證明這個函數(shù)只讀不寫。

條款04:確定對象被使用前已先被初始化

對于內(nèi)置類型:全局變量如果初始化時沒被賦值,則會被初始化為默認(rèn)值;局部變量一定要被初始化,不然其內(nèi)部存放的是上一次存放過的值,然而該值是未知的。對于內(nèi)置類型以外的類型:初始化的責(zé)任落在構(gòu)造函數(shù)身上。初始化構(gòu)造函數(shù)時建議使用初始化列表而不是在其體內(nèi)使用賦值語句。

構(gòu)造/析構(gòu)/賦值運算

條款05:了解C++默默編寫并調(diào)用哪些函數(shù)

當(dāng)沒有聲明時,編譯器會自動為類創(chuàng)建默認(rèn)構(gòu)造函數(shù)、析構(gòu)函數(shù)、復(fù)制構(gòu)造函數(shù)和賦值運算符。

條款06:若不想使用編譯器自動生成的函數(shù),就該明確拒絕

若不想使用編譯器自動生成的函數(shù),可將相應(yīng)的成員函數(shù)申明為private并且不予實現(xiàn)。

條款07:為多態(tài)基類聲明虛析構(gòu)函數(shù)

如果一個類有任何虛函數(shù),那么它就應(yīng)該有虛析構(gòu)函數(shù)。

條款08:別讓異常逃離析構(gòu)函數(shù)

析構(gòu)函數(shù)不要拋出異常,如果析構(gòu)函數(shù)中調(diào)用的函數(shù)可能拋出異常,析構(gòu)函數(shù)應(yīng)該捕捉并記錄下來然后吞掉他(不傳播)或結(jié)束程序。同時最好提供一個普通函數(shù)用來供用戶執(zhí)行可能異常的該操作。

條款09:絕不在構(gòu)造和析構(gòu)過程中調(diào)用虛函數(shù)

在構(gòu)造函數(shù)和析構(gòu)函數(shù)中不要去調(diào)用虛函數(shù),因為子類在構(gòu)造/析構(gòu)時,會調(diào)用父類的構(gòu)造/析構(gòu)函數(shù),此時其中的虛函數(shù)是調(diào)用父類的實現(xiàn),但這是父類的虛函數(shù)可能是純虛函數(shù),即使不是,也可能不符合你想要的目的(是父類的結(jié)果不是子類的結(jié)果)。如果想調(diào)用父類的構(gòu)造函數(shù)來做一些事情,替換做法是:在子類調(diào)用父類構(gòu)造函數(shù)時,向上傳遞一個值給父類的構(gòu)造函數(shù)。

條款10:令 operator= 返回一個*this 引用

注意這只是個協(xié)議,但建議遵守。因為該形式被內(nèi)置類型和標(biāo)準(zhǔn)庫類型共同遵守。

TheClass& operator=(const TheClass& rhs) {
...
return *this;
}

條款11:在 operator= 中處理“自我賦值”

由于變量有別名的存在(多個指針或引用只想一個對象),所以可能出現(xiàn)自我賦值的情況。比如 a[i] = a[j] *px=*py ,可能是同一個對象賦值。一般的解決辦法是先檢測再賦值。

條款12:復(fù)制對象時勿忘其每一個成分

復(fù)制構(gòu)造函數(shù)和賦值構(gòu)造函數(shù)要確保復(fù)制了對象內(nèi)的所有成員變量和所有基類成分,這意味著你如果自定義以上構(gòu)造函數(shù),那么每增加成員變量,都要同步修改以上構(gòu)造函數(shù),且要調(diào)用基類的相應(yīng)構(gòu)造函數(shù)。

資源管理

條款13:以對象管理資源

為了確保一個對象在初始化后能夠最終有效被delete,最好使用shared_ptr和auto_ptr,而前者更好,因為是基于引用計數(shù)機制,可以在復(fù)制時保持兩個指針都指向同一對象,且只有兩個指針都銷毀時才delete。

這本書可能舊了,auto_ptr已經(jīng)被廢棄了。

條款14:在資源管理類中小心copying行為

如果對想要自行管理delete(或其他類似行為如上鎖/解鎖)的類處理復(fù)制問題,有以下方案,先創(chuàng)建自己的資源管理類,然后可選擇:

  • 禁止復(fù)制,使用條款6的方法
  • 對復(fù)制的資源做引用計數(shù)(聲明為shared_ptr),shared_ptr支持初始化時自定義刪除函數(shù)(auto_ptr不支持,總是執(zhí)行delete)
  • 做真正的深復(fù)制
  • 轉(zhuǎn)移資源的擁有權(quán),類似auto_ptr,只保持新對象擁有。

條款15:在資源管理類中提供對原始資源的訪問

封裝了資源管理類后,API有時候往往會要求直接使用其原始資源(作為參數(shù)的類型只能接受原始資源,不接受管理類指針),這時候就需要提供一個獲取其原始資源的方法。有顯式轉(zhuǎn)換方法(如指針的->和(*)操作,也比如自制一個getXXX()函數(shù)),還有隱式轉(zhuǎn)換方法(比如覆寫XXX()取值函數(shù))。顯式操作比較安全,隱式操作比較方便(但容易被誤用)。

不明覺厲。

條款16:成對使用new和delete時要采取相同形式

new 對應(yīng) delete。new a[4] 對應(yīng) delete [] a。兩者的使用必須對應(yīng)

條款17:以獨立語句將newed對象置入智能指針

如果有函數(shù)參數(shù)接收智能指針對象,那么該智能指針對象一定要在調(diào)用該函數(shù)前用獨立語句去創(chuàng)建,否則在創(chuàng)建所指對象和用該對象綁定智能指針兩個操作之間,可能插入一些操作(由于C++的獨特性),這時候如果出異常,那么會造成創(chuàng)建的對象還沒來得及用智能指針修飾,也就無法被自動回收了。

不明覺厲。

設(shè)計與聲明

條款18:讓接口容易被正確使用,不易被誤用

好的接口要容易被正確使用,不容易被誤用,符合客戶的直覺。

  • 促進正確使用的辦法包括保持接口的一致性,既包括自定義接口之間的一致性,也包括與內(nèi)置類型行為的相似一致性。
  • 阻止誤用的辦法包括建立新類型來限制該類型上的操作、束縛對象的值以及消除客戶管理資源的責(zé)任,以此來作為接口的參數(shù)與返回類型。
  • shared_ptr支持定制刪除函數(shù),所以可以很方便的實現(xiàn)上述問題,以及防范DLL問題。

條款19:設(shè)計class猶如設(shè)計type

在設(shè)計class時,要考慮一系列的問題,包括:

  • 對象的創(chuàng)建和銷毀(構(gòu)造、析構(gòu))
  • 對象的初始化與賦值(構(gòu)造、賦值操作符)
  • 復(fù)制操作(復(fù)制構(gòu)造)
  • 合法值(約束條件)
  • 繼承體系(注意虛函數(shù))
  • 支持的類型轉(zhuǎn)換(顯示轉(zhuǎn)換、類型轉(zhuǎn)換操作符)
  • 成員函數(shù)和成員變量的可見范圍(public/protected/private)
  • 是否用模板就能實現(xiàn)?

條款20:寧以傳遞const引用替換傳遞值

盡量用 常量引用類型 來作為函數(shù)的參數(shù)類型,這通常比較高效,也可以解決基類參數(shù)類型被賦值子類時引起的內(nèi)容切割問題。但對于內(nèi)置類型和STL的迭代器與函數(shù)對象,通常編譯器會對其專門優(yōu)化,直接傳值類型往往比較恰當(dāng)。

條款21:必須返回對象時,別妄想返回其引用

雖然函數(shù)參數(shù)最好用引用值,但函數(shù)返回值卻不要隨便去用引用,這回造成很多問題,比如引用的對象在函數(shù)結(jié)束后即被銷毀,或是需要付出很多成本和代碼來保證其不被銷毀且不重復(fù),這大概率沒有必要,就返回一個值/對象就好了。

條款22:將成員變量聲明為private

切記將成員變量聲明為private,這可以保證客戶訪問數(shù)據(jù)的一致性、可以細(xì)微劃分訪問控制、允許約束條件獲得保證,并提供類作者充分的實現(xiàn)彈性來修改對其的處理,因為這保證了“封裝性”,作者可以改變實現(xiàn)和對成員變量的操作,而不改變客戶的調(diào)用方式。protected并不比public更加具有封裝性,因為protected修飾的成員變量一旦修改,也會造成子類的大量修改。

條款23:寧以非成員、非友元替換成員函數(shù)

寧可拿非成員非友元函數(shù)來替換成員函數(shù)。因為這種函數(shù)位于函數(shù)之外,不能訪問類的private成員變量和函數(shù),保證了封裝性(沒有增加可以看到內(nèi)部數(shù)據(jù)的函數(shù)量),此外,這些函數(shù)只要位于同一個命名空間內(nèi),就可以被拆分為多個不同的頭文件,客戶可以按需引入頭文件來獲得這些函數(shù),而類是無法拆分的(子類繼承與此需求不同),因此這種做法有更好的擴充性。

條款24:若所有參數(shù)皆需類型轉(zhuǎn)換,請為此采用非成員函數(shù)

如果你要為某個函數(shù)的所有參數(shù)(包括this所指對象本身)進行類型轉(zhuǎn)換,那么該函數(shù)必須是個非成員函數(shù)。舉個例子,你想為一個有理數(shù)類實現(xiàn)乘法函數(shù),支持與int類型的乘積,可以,因為傳參int進去后會調(diào)用構(gòu)造函數(shù)隱式轉(zhuǎn)換為有理數(shù)類型,同時你想滿足交換律,這時就會報錯,因為int類型并沒有一個函數(shù)用來支持你的有理數(shù)類做參數(shù)的乘法運算。解決方案是將該乘法運算函數(shù)作為一個非成員函數(shù),傳兩個參數(shù)進去,這樣不管你的int放在前面還是后面,都能作為參數(shù)被轉(zhuǎn)換類型了。但是,非成員函數(shù)不代表就一定成為友元函數(shù),能夠通過public函數(shù)調(diào)用完成功能的,就不該設(shè)為友元函數(shù),避免權(quán)力過大造成麻煩。

條款25:考慮寫出一個不拋異常的swap函數(shù)

由于swap函數(shù)如此重要,需要特別對他做出一些優(yōu)化。常規(guī)的swap是簡單全復(fù)制三次對象進行交換(包括temp對象),如果效率足夠就用常規(guī)版。如果效率不夠,那么給你的類提供一個成員函數(shù)swap,用來對那些復(fù)制效率低的成員變量(通常是指針)做交換。然后,提供一個非成員函數(shù)的swap來調(diào)用這個成員函數(shù),供別人調(diào)用置換。對于類(非模板),為標(biāo)準(zhǔn)std::swap提供一個特定版本(swap是模板函數(shù),可以特化)。在使用swap時,記得 using std::swap,讓編譯器可以獲取到標(biāo)準(zhǔn)swap或特化版本。編譯器會自行從所有可能性中選擇最優(yōu)版本。

實現(xiàn)

條款26:盡可能延后變量定義式的出現(xiàn)時間

直到使用它時才進行定義。但對于循環(huán),定義在循環(huán)體前還是定義在循環(huán)體內(nèi),哪一個比較好呢?方法A:定義在循環(huán)體外

Widget w;
for(int i=0;i<n;i++)
{
w=...
}

方法A:定義在循環(huán)體內(nèi)

for(int i=0;i<n;i++)
{
Widget w(...);
}

做法A:1個構(gòu)造函數(shù)+1個析構(gòu)函數(shù)+n個賦值操作;做法B:n個構(gòu)造函數(shù)+n個析構(gòu)函數(shù)。

除非(1)你知道賦值成本比“構(gòu)造+析構(gòu)”成本低,(2)你正在處理代碼中效率高度敏感的部分,否則你應(yīng)該使用做法B。

條款27:盡量少做轉(zhuǎn)型操作

盡量避免使用轉(zhuǎn)型cast(包括C的類型轉(zhuǎn)換和C++的四個新式轉(zhuǎn)換函數(shù)),特別是注重效率的代碼中避免用dynamic_casts。如果一定要用,試著考慮無需轉(zhuǎn)型的替代設(shè)計,例如為基類添加一個什么也不做的衍生類使用的函數(shù),避免在使用時需要將基類指針轉(zhuǎn)型為子類指針。如果一定要轉(zhuǎn)型,試著將其隱藏于某個函數(shù)后,客戶調(diào)用該函數(shù)而無需自己用轉(zhuǎn)型。寧可使用C++新式轉(zhuǎn)型,也不用用C的舊式,因為新式的更容易被注意到,而且各自用途專一。

條款28:避免返回handles指向?qū)ο髢?nèi)部成分

避免讓外部可見的成員函數(shù)返回handles(包括引用、指針、迭代器)指向?qū)ο髢?nèi)部(更隱私的成員變量或函數(shù)),即使返回const修飾也有風(fēng)險。這一方面降低了封裝性,另一方面可能導(dǎo)致其指向的對象內(nèi)部元素被修改或銷毀。

條款29:為異常安全而努力是值得的

異常安全函數(shù)是指即使發(fā)生異常也不會泄露資源或者導(dǎo)致數(shù)據(jù)結(jié)構(gòu)破壞,分三種保證程度:基本保證、強烈保證和不拋異常型。只有基本類型才確保了不拋異常型。對于我們自己設(shè)計的函數(shù),往往想要提供強烈保證,即一旦發(fā)生異常,程序的整個狀態(tài)會回到執(zhí)行函數(shù)前的狀態(tài),實現(xiàn)方法一般用復(fù)制一個副本然后執(zhí)行操作,全部成功后再替換原對象的方式來實現(xiàn)。但這一操作有時對時間和空間的消耗較大,適用性不強。這種情況下可以提供基本保證。函數(shù)提供的保證程度通常最高只等于其所調(diào)用的各個函數(shù)中的保證的最弱者——木桶理論。

條款30:透徹了解inline的里里外外

inline還真的和宏很像,用一個名稱替換一段代碼。類聲明中的帶有實現(xiàn)的成員函數(shù)就是內(nèi)聯(lián)函數(shù)。一般用于返回私有值。

只將inline用在小型、被頻繁調(diào)用的函數(shù)身上。inline會帶來體積增大的問題,此外,不要對構(gòu)造函數(shù)、析構(gòu)函數(shù)等使用inline,即使你自己在其中寫的代碼可能很少,編譯器卻會為他添加很多代碼。不要只因為模板函數(shù)出現(xiàn)在頭文件,就將它們聲明為inline,模板函數(shù)和inline并不是必須結(jié)對出現(xiàn)的。

條款31:將文件間的編譯依存關(guān)系降至最低

為了增加編譯速度,應(yīng)該減少類文件之間的相互依存性(include),但是類內(nèi)又常常使用到其他類,不得不相互依存,解決方案是:將類的聲明和定義分開(不同的頭文件),聲明相互依存,而定義不相依存,這樣當(dāng)定義需要變更時,編譯時不需要再因為依賴而全部編譯?;诖藰?gòu)想的兩個手段是Handle classes和Interface classes。Handle classes是一個聲明類,一個imp實現(xiàn)類,聲明類中不涉及具體的定義,只有接口聲明,在定義類中include聲明類,而不是繼承。而Interface classes是在接口類中提供純虛函數(shù),作為一個抽象基類,定義類作為其子類來實現(xiàn)具體的定義。

繼承與面向?qū)ο笤O(shè)計

條款32:確定你的public繼承是is-a關(guān)系

public繼承意味著 is-a 關(guān)系,也就是要求,適用于基類身上的每一件事情,是每一件,也一定適用于衍生類身上。有時候,直覺上滿足這一條件的繼承關(guān)系,可能并不一定,比如,企鵝是鳥,但并不會飛。

條款33:避免遮掩繼承而來的名稱

就如函數(shù)作用域內(nèi)的變量會掩蓋函數(shù)作用域外的同名變量一樣。衍生類中如果聲明了與基類中同名的函數(shù)(無論是虛、非虛,還是其他形式),都會掩蓋掉基類中的所有同名函數(shù),注意,是所有,包括參數(shù)不同的重載函數(shù),都會不再可見。此時再通過子類使用其基類中的重載函數(shù)(子類沒有聲明接收該參數(shù)的重載函數(shù)時),都會報錯。解決方案一是使用using聲明式來在子類中聲明父類的同名函數(shù)(重載函數(shù)不需要聲明多個),此時父類的各重載函數(shù)就是子類可見的了。二是使用轉(zhuǎn)交函數(shù),即在子類函數(shù)的聲明時進行定義,調(diào)用父類的某個具體的重載函數(shù)(此時由于在聲明時定義,成為inline函數(shù)),此舉可以只讓需要的部分父類重載函數(shù)于子類可見。

條款34:區(qū)分接口繼承和實現(xiàn)繼承

Public繼承由兩部分組成:接口(interface)繼承和實現(xiàn)(implementation)繼承.

class Shape{
public:
virtual void draw() const = 0;//純虛函數(shù)
virtual void error(const std::string& msg);//非純虛函數(shù)
int ObjectID() const;//非虛函數(shù)
....
};
class Rectangle:public Shape{...};
class Ellipse:public Shape{...};

上述代碼中,類Shape是一個抽象類,因為draw()是純虛函數(shù),所以客戶不能創(chuàng)建Shape的實體。純虛函數(shù)的作用是:讓派生類只繼承函數(shù)接口,而派生類必須提供實現(xiàn);非純虛函數(shù)的作用是:讓派生類繼承函數(shù)接口,并提供給派生類默認(rèn)的實現(xiàn)方法(實現(xiàn)繼承),當(dāng)派生類不打算復(fù)寫方法時,將應(yīng)用基類提供的默認(rèn)方法;非虛函數(shù)的作用是:派生類不能復(fù)寫該函數(shù)。

條款35:考慮虛函數(shù)以外的其他選擇

虛函數(shù)(本質(zhì)是希望子類的實現(xiàn)不同)的替代方案:

  • 用public的非虛函數(shù)來調(diào)用private的虛函數(shù)具體實現(xiàn),非虛函數(shù)必須為子類繼承且不得更改,所以它決定了何時調(diào)用以及調(diào)用前后的處理;虛函數(shù)實現(xiàn)可以在子類中覆寫,從而實現(xiàn)多態(tài)。
  • 將虛函數(shù)替換為函數(shù)指針成員變量,這樣可以對同一種子類對象賦予不同的函數(shù)實現(xiàn),或者在運行時更改某對象對應(yīng)的函數(shù)實現(xiàn)(添加一個set函數(shù))。
  • 用tr1::function成員變量替換虛函數(shù),從而允許包括函數(shù)指針在內(nèi)的任何可調(diào)用物搭配一個兼容于需求的簽名式。
  • 將虛函數(shù)也做成另一個繼承體系類,然后在調(diào)用其的類中添加一個指針來指向其對象。

本條款的啟示為:為避免陷入面向?qū)ο笤O(shè)計路上因常規(guī)而形成的凹洞中,偶爾我們需要對著車輪猛推一把。這個世界還有其他許多道路,值得我們花時間加以研究。

條款36:絕不重新定義繼承而來的非虛函數(shù)

不要重新定義繼承而來的非虛函數(shù),理論上,非虛函數(shù)的意義就在于父類和子類在該函數(shù)上保持一致的實現(xiàn)。

條款37:絕不重新定義繼承而來的缺省參數(shù)值

不要重新定義一個繼承而來的函數(shù)(虛函數(shù))的缺省參數(shù)的值(參數(shù)默認(rèn)值),因為函數(shù)是動態(tài)綁定(調(diào)用指針指向的對象的函數(shù)實現(xiàn)),但參數(shù)默認(rèn)值卻是靜態(tài)綁定(指針聲明時的類型所設(shè)定的默認(rèn)參數(shù),比如基類設(shè)定的)。這會導(dǎo)致兩者不對應(yīng),比如: Base *p = new SubClass();

條款38:通過復(fù)合表示 has-a 或者“根據(jù)某物實現(xiàn)出”的關(guān)系

注意 has-a 和 is-a 的區(qū)分。如果是 is-a 的關(guān)系,可以用繼承,但如果是 has-a 的關(guān)系,應(yīng)該將一個類作為另一個類的成員變量來使用,以利用該類的能力,而不是去以繼承它的方式使用。

條款39:明智而審慎地使用private繼承

Private繼承意味著“根據(jù)某物實現(xiàn)出”,而不是 is-a 的關(guān)系。與上面的復(fù)合(has-a)很像,但比復(fù)合的級別低。當(dāng)衍生類需要訪問 protected 基類的成員,或需要重新定義繼承而來的虛函數(shù)時,可以這么設(shè)計。此外,private繼承可以讓空基類的空間最優(yōu)化。

條款40:明智而審慎地使用多重繼承

多重繼承確實有正當(dāng)使用場景,比如public繼承某個接口類的接口(其接口依然是public的),private繼承某個類的實現(xiàn)來協(xié)助實現(xiàn)(繼承來的實現(xiàn)為private,只供自己用)。虛繼承會增加大小、速度、初始化(及賦值)復(fù)雜度等成本,如果虛基類不帶任何數(shù)據(jù),將是最具使用價值的情況。

模板與泛型編程

條款41:了解隱式接口和編譯期多態(tài)

類和模板都支持接口和多態(tài)。類的接口是顯式定義的——函數(shù)簽名。多態(tài)是通過虛函數(shù)在運行期體現(xiàn)的。模板的接口是隱式的(由模板函數(shù)的實現(xiàn)代碼所決定其模板對象需要支持哪些接口),多態(tài)通過模板具現(xiàn)化和函數(shù)重載解析在編譯期體現(xiàn),也就是編譯期就可以賦予不同的對象于模板函數(shù)。

條款42:了解typename的雙重意義

聲明模板的參數(shù)時,前綴關(guān)鍵字 class 和 typename 可互換,功能相同。對于嵌套從屬類型名稱(即依賴于模板參數(shù)類型的一個子類型,例如迭代器),必須用typename來修飾,但不能在模板類的基類列和初始化列表中修飾基類。

條款43:學(xué)習(xí)處理模板化基類內(nèi)的名稱

如果基類是模板類,那么衍生類直接調(diào)用基類的成員函數(shù)無法通過編譯器,因為可能會有特化版的模板類針對某個類不聲明該接口函數(shù)。解決方法有:

條款44:將與參數(shù)無關(guān)的代碼抽離templates

任何模板代碼都不該與某個造成膨脹的參數(shù)產(chǎn)生相依關(guān)系:

條款45:運用成員函數(shù)模板接受所有兼容類型

真實指針允許父類指針指向子類對象,如果想要讓自制的智能指針也支持這種對象轉(zhuǎn)換,那就需要特殊操作,因為一般的模板類(智能指針能指向多種對象,必然是模板類)只能以自身模板聲明的類型來構(gòu)造。做法是聲明一個泛化構(gòu)造函數(shù),也就是定義一個模板構(gòu)造函數(shù),接收模板參數(shù),聲明一個指向的真實對象指針,聲明一個獲取該對象指針的get函數(shù),用該get函數(shù)放在初始化列表中來構(gòu)造模板類。這樣就能使用一種類型特化出的自制智能指針來構(gòu)造另一種類型特化出的自制智能指針了。同時,在初始化列表中編譯器會為你檢查是否允許該類型轉(zhuǎn)換(比如只允許子類往父類的轉(zhuǎn)換,不能相反)。雖然這種模板構(gòu)造函數(shù)也能作為復(fù)制構(gòu)造函數(shù)使用(用相同類型來構(gòu)造即可),但編譯器還是會當(dāng)做你沒有聲明復(fù)制構(gòu)造函數(shù),從而為你創(chuàng)建一個,因此如果想要徹底控制行為,你還是需要自行聲明你的復(fù)制構(gòu)造函數(shù)和賦值構(gòu)造函數(shù)。

條款46:需要類型轉(zhuǎn)換時請為模板定義非成員函數(shù)

模板類中的模板函數(shù)不支持隱式類型轉(zhuǎn)換,如果你在調(diào)用時傳了一個其他類型的變量,編譯器無法幫你做類型轉(zhuǎn)換,從而報錯。解決方案是將該模板函數(shù)定義為模板類內(nèi)的友元模板函數(shù),從而支持了參數(shù)的隱式轉(zhuǎn)換。如果函數(shù)的功能比較簡單,可以直接inline,如果比較復(fù)雜,可以調(diào)用一個類外的定義好的模板函數(shù)(此時,友元函數(shù)已經(jīng)給參數(shù)做了類型轉(zhuǎn)換,因此可以調(diào)用模板函數(shù)了)。

條款47:請使用traits classes表現(xiàn)類型信息

對于模板函數(shù),可能對于接收參數(shù)的不同類型,有不同的實現(xiàn)。此時,可以提供一個traits class,其中包含了某一系列類型的類型信息(通常以枚舉區(qū)分具體類型),然后,在該類中實現(xiàn)接收多種traits參數(shù)的重載工具函數(shù),用來根據(jù)標(biāo)識的不同類進行不同的具體函數(shù)操作。這使得該行為能在編譯期就被區(qū)分。

條款48:認(rèn)識模板元編程(TMP)

TMP可將工作由運行期移往編譯器,因而得以實現(xiàn)早期錯誤偵測和更高的執(zhí)行效率。實現(xiàn)方式以模板為基礎(chǔ),因為模板會在編譯時確定,上一條款的traits classes就是一種TMP,依靠模板函數(shù)參數(shù)不同的重載來在編譯器模擬if else(其在運行期才會判斷)。另一個例子是用模板來在編譯器實現(xiàn)階乘:

template<unsigned n>
struct Factorial {
enum { value = n * Factorial<n-1>::value };
};
template<>
struct Factorial<0> {
enum { value = 1 };
}

用模板來實現(xiàn)遞歸從而在編譯器實現(xiàn)階乘運算,用參數(shù)為0的特異化來做遞歸的終結(jié)。

定制 new 和 delete

條款49:了解 new-handler 的行為

在對象new操作分配內(nèi)存時,如果分配失敗,默認(rèn)會返回null(老編譯器)或拋出bad_alloc 異常(新編譯器)。如果想要自定義分配失敗的操作,可以調(diào)用 set_new_handler 函數(shù)來設(shè)置new_handler。如果想要讓類在構(gòu)造時自動調(diào)用自定義的new_handler,并在構(gòu)造結(jié)束后回到系統(tǒng)默認(rèn)的new_handler 。可以繼承一個聲明了set_new_handler函數(shù)接口和包含設(shè)置與回歸new_handler的new函數(shù)的模板類,然后讓你的自定義類繼承自你的類名所特化的該模板類,從而能夠為每一個你的類做一個特化的new_handler函數(shù)。

條款50:了解new和delete的合理替換時機

有很多理由讓你想要寫個自定的new和delete,比如改善定制版的效能、對heap運用錯誤進行調(diào)試、收集heap使用信息等。也有許多商業(yè)或開源的內(nèi)存分配器供你使用。

條款51:編寫new和delete時需固守常規(guī)

自定義的new應(yīng)該內(nèi)含一個無窮循環(huán),在其中嘗試分配內(nèi)存,如果失敗,就該調(diào)用new-handler以退出循環(huán)。同時它應(yīng)該有能力處理0 bytes的申請(可以簡單判斷并改為1bytes)。Class專屬版本還要處理衍生類的申請,不要直接調(diào)用基類的(大小不同),可以判斷并轉(zhuǎn)調(diào)普通的new函數(shù)。自定義的delete應(yīng)該可在收到null指針時不做任何事,Class專屬版本還應(yīng)該處理衍生類的申請,不要直接調(diào)用基類的(大小不同),可以判斷并轉(zhuǎn)調(diào)普通的delete函數(shù)。

條款52:寫了 placement new 也要寫 placement delete

如果你的new接收的參數(shù)除了必定有的size_t外還有其他,就是個placement new。delete類似。當(dāng)創(chuàng)建對象時,會先進行new,然后調(diào)用構(gòu)造函數(shù),如果構(gòu)造出現(xiàn)異常,就需要delete,否則內(nèi)存泄漏。如果用了placement new,那么編譯器會尋找含有同樣參數(shù)的placement delete,否則不會delete,因此必須成對寫接收同樣參數(shù)的placement new和placement delete。同時,為了讓用戶主動使用delete時能進行正確操作,你需要同時定義一個普通形式的delete,來執(zhí)行和placement delete同樣的特殊實現(xiàn)。你在類中聲明placement new后,會掩蓋C++提供的new函數(shù),因此除非你確實不想用戶使用默認(rèn)的new,否則你需要確保它們還可用(條款33)。

雜項討論

條款53:不要輕忽編譯器的警告

對于編譯器編譯時給出的警告信息,最好立即修復(fù),避免后續(xù)調(diào)試半天來尋找編譯器早就告知你的問題。

條款54:讓自己熟悉包括TR1在內(nèi)的標(biāo)準(zhǔn)程序庫

C++98的標(biāo)準(zhǔn)程序庫有:

而TR1是新的一系列組件,在std內(nèi)的tr1命名空間中,比如:std::tr1::shared_ptr。它包含:

條款55:讓自己熟悉Boost

Boost是一個程序庫,其由C++標(biāo)準(zhǔn)委員會成員創(chuàng)設(shè),可視為一個“可被加入標(biāo)準(zhǔn)C++的各種功能”的測試場,涵蓋眾多經(jīng)過多輪復(fù)核的優(yōu)質(zhì)程序,如果想知道當(dāng)前C++最高技術(shù)水平、想一瞥未來C++的可能長相?看看Boost[https://boost.org]吧。

  • 在調(diào)用動作前加上“this->”
  • 使用using聲明式來在子類中聲明基類的該接口
  • 明確指出被調(diào)用的函數(shù)位于基類:Base::xxx();
  • 以上做法都是承諾被調(diào)用的函數(shù)一定會在各種特化基類中均聲明。如果沒有聲明,還是會在運行期報錯。
    • 因非類型模板參數(shù)造成的代碼膨脹(比如用尺寸做模板參數(shù)導(dǎo)致為不同尺寸的同一對象生成多份相同代碼),往往可消除,做法是將該參數(shù)改為函數(shù)參數(shù)或者類成員變量,而不要放到模板的參數(shù)中。
    • 因類型參數(shù)造成的代碼膨脹(比如int和long有相同的二進制表述,但作為模板參數(shù)會產(chǎn)生兩份代碼),往往可降低,做法是讓帶有完全相同二進制表述的具體類型共享實現(xiàn)碼——使用唯一一份底層實現(xiàn)。
    • STL
    • Iostreams,包括cin、cout、cerr、clog等
    • 國際化支持
    • 數(shù)值處理
    • 異常階層體系
    • C89標(biāo)準(zhǔn)程序庫
    • 智能指針,包括shared_ptr和weak_ptr。
    • function:支持以函數(shù)簽名(出參類型+入?yún)㈩愋停┳鳛槟0?/li>
    • bind:綁定器
    • 無序hash表,用以實現(xiàn)無序的set、multiset、map、multimap
    • 正則表達(dá)式
    • tuples:擴充pair,能持有任意個數(shù)的對象,類似python中的tuples。
    • array:STL化的數(shù)組,支持begin和end,不過其大小固定,不適用動態(tài)內(nèi)存。
    • mem_fn
    • reference_wrapper:讓引用的行為更像對象,可以被容器持有。
    • 隨機數(shù)生成工具:大大超越rand
    • 數(shù)學(xué)特殊函數(shù):多種數(shù)學(xué)函數(shù)
    • C99兼容擴充。
    • type traits,使用見條款47,提供類型的編譯期信息。
    • result_of:是個模板,用來推到函數(shù)調(diào)用的返回類型。

相關(guān)文章

  • C語言實現(xiàn)串的順序存儲表示與基本操作

    C語言實現(xiàn)串的順序存儲表示與基本操作

    這篇文章主要為大家詳細(xì)介紹了C語言實現(xiàn)串的順序存儲表示與基本操作,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-09-09
  • C++中的關(guān)鍵字volatile詳解

    C++中的關(guān)鍵字volatile詳解

    這篇文章主要介紹了C++中的關(guān)鍵字volatile使用方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2025-03-03
  • C++實現(xiàn)一個簡單的SOAP客戶端

    C++實現(xiàn)一個簡單的SOAP客戶端

    這篇文章主要介紹了C++實現(xiàn)一個簡單的SOAP客戶端,在C++中,一般使用gSOAP來實現(xiàn)客戶端、服務(wù)端,下面一起進入文章了解具體內(nèi)容,需要的朋友可以參考一下
    2021-11-11
  • C++基礎(chǔ)概念講述

    C++基礎(chǔ)概念講述

    這篇文章主要介紹了C++基礎(chǔ)概念,??本次為C++的一個開篇,重點是更好的理解C++相對于其他編程語言的一個特性,之后會持續(xù)更新,本次專欄計劃是掌握C++的基礎(chǔ)語法以及常用特性,并且從細(xì)節(jié)上去理解,需要的朋友可以參考一下
    2021-12-12
  • 帶你了解C++中vector的用法

    帶你了解C++中vector的用法

    大家好,本篇文章主要講的是帶你了解C++中vector的用法,感興趣的同學(xué)趕快來看一看吧,對你有幫助的話記得收藏一下
    2022-01-01
  • C語言超詳細(xì)文件操作基礎(chǔ)下篇

    C語言超詳細(xì)文件操作基礎(chǔ)下篇

    這篇文章主要為大家詳細(xì)介紹了C語言的文件操作,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-03-03
  • C語言實現(xiàn)帶頭雙向循環(huán)鏈表的接口

    C語言實現(xiàn)帶頭雙向循環(huán)鏈表的接口

    這篇文章主要為大家詳細(xì)介紹了C語言實現(xiàn)帶頭雙向循環(huán)鏈表的接口,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-10-10
  • C語言實現(xiàn)繪制貝塞爾曲線的函數(shù)

    C語言實現(xiàn)繪制貝塞爾曲線的函數(shù)

    貝塞爾曲線,又稱貝茲曲線或貝濟埃曲線,是應(yīng)用于二維圖形應(yīng)用程序的數(shù)學(xué)曲線。本文將利用C語言實現(xiàn)繪制貝塞爾曲線的函數(shù),需要的可以參考一下
    2022-12-12
  • c++制作的時間函數(shù)類

    c++制作的時間函數(shù)類

    本文給大家分享的是一個個人使用C++編寫的時間函數(shù)類,主要是實現(xiàn)了類的定義和調(diào)用,相比較來說還算比較復(fù)雜的時間類了,推薦給小伙伴們,有需要的朋友可以參考下。
    2015-03-03
  • C++實現(xiàn)基于reactor的百萬級并發(fā)服務(wù)器

    C++實現(xiàn)基于reactor的百萬級并發(fā)服務(wù)器

    本文介紹了基于Reactor模式的百萬級并發(fā)服務(wù)器,使用epoll進行高效I/O多路復(fù)用,支持多個端口的監(jiān)聽,并通過回調(diào)機制處理每個連接的接收和發(fā)送操作,需要的朋友可以參考下
    2025-02-02

最新評論