Effective C++ ——让自己习惯C++
生活随笔
收集整理的這篇文章主要介紹了
Effective C++ ——让自己习惯C++
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
條款一:視C++為一個(gè)語(yǔ)言聯(lián)邦
? ? ? ?為了理解C++,你必須認(rèn)識(shí)其主要的次語(yǔ)言。幸運(yùn)的是總共只有四個(gè):
- ?C:C++是由C語(yǔ)言繼承而來(lái)的,必然對(duì)C有很好的兼容性,這一部分主要包括C中的一些語(yǔ)言,庫(kù)函數(shù)等。但當(dāng)你以C++內(nèi)的C成分工作時(shí),高效編程守則照出C語(yǔ)言的局限:沒(méi)有模板、沒(méi)有異常、沒(méi)有重載。。。
- ?Object-Oriented C++:這部分也就是C with Class所訴說(shuō)的:classes(包括構(gòu)造函數(shù)和析構(gòu)函數(shù))、封裝、繼承、多態(tài)、virtual函數(shù)(動(dòng)態(tài)綁定)。。。。等等。
- ?Template C++:這一部分主要是泛型編程。
- ?STL:標(biāo)準(zhǔn)模板庫(kù),主要是以template C++為基礎(chǔ)實(shí)現(xiàn)的,里面提供了很多有用的類和對(duì)應(yīng)的算法,幫助我們很好的結(jié)果了C語(yǔ)言中要自己去解決的問(wèn)題,主要有容器(數(shù)組就是一個(gè)特殊的容器)、迭代器(智能指針,之所以用迭代器是為了通用性)、算法(包括容器特殊的算法和容器間通用的算法)、函數(shù)對(duì)象(能想函數(shù)一樣被調(diào)用的對(duì)象,通過(guò)重定義對(duì)象中的()操作符來(lái)完成的)、適配器(可以理解為修改了容器接口實(shí)現(xiàn)的一種容器),這一部分是c++強(qiáng)大的后盾,學(xué)習(xí)C++。不能不學(xué)STL,不僅會(huì)用最好能知道STL中成員的實(shí)現(xiàn)方式,這樣就能更加高效的使用!
條款二:盡量以const,enum,inline替換#define
? ? ? ?可以理解為能在編譯器期間做的事情不要放到預(yù)編譯器中處理,預(yù)編譯器是對(duì)程序編譯之前的一個(gè)預(yù)先的處理,不會(huì)檢查對(duì)代碼最任何的檢出,這樣如果在編譯期間發(fā)現(xiàn)問(wèn)題,如果問(wèn)題是我們自己直接造成的那可能會(huì)很快的定位,想反的如果問(wèn)題在預(yù)編譯期間做了一些處理,那樣在編譯中出現(xiàn)的問(wèn)題可能會(huì)顯示預(yù)編譯中的問(wèn)問(wèn)題,這樣對(duì)問(wèn)題定位不方便,例如:在程序中定義宏#define PI 3.1415,如果編譯過(guò)程中出現(xiàn)錯(cuò)誤,錯(cuò)誤顯示中會(huì)直接顯示3,1415,而不會(huì)對(duì)PI符號(hào)有所說(shuō)明,如果宏是自己定義的還好,如果是引用的其他頭文件中的定義,那真的不是很好查的!對(duì)于條款二,主要有以下兩點(diǎn)說(shuō)明:
1.盡量以const、enum定義來(lái)替換#define 定義。
? ? ? ?在頭文件中以#define定義的常量在預(yù)編譯期間直接對(duì)對(duì)應(yīng)的符號(hào)進(jìn)行替換,沒(méi)有內(nèi)存的申請(qǐng),這也是#define可以放到頭文件中的原因,對(duì)于const常量定義中,它將常量直接放入到符號(hào)表中,不會(huì)申請(qǐng)固定的內(nèi)存,除非對(duì)該常量有內(nèi)存的引用,這也就是為什么const定義的常量能像#define一樣放到頭文件中,在const常量的使用中主要常量重疊的出現(xiàn),相關(guān)知識(shí)自行查閱!
? ? ? ?為了將常量的作用域限制于class內(nèi),你必須讓它成為class的一個(gè)成員;而為確保此常量至多只有一份實(shí)體,你必須讓它成為一個(gè)static成員:
class GamePlayer{ private:static const int NumTurns = 5;//常量聲明式int score[NumTurns];//使用該常量..... }? ? ? ?上面中NumTurns是常量的聲明而不是定義式,在后面的使用中如果只是使用該常量并沒(méi)有用到該常量的地址,就可以不用再對(duì)該常量做定義,否組需要如下的定義:const int GamePlayer::NumTurns;? ? ? ?請(qǐng)把這個(gè)式子放進(jìn)一個(gè)實(shí)現(xiàn)文件而非頭文件。由于class常量已在聲明時(shí)獲得初值,因此定義時(shí)不可以再設(shè)初值。
? ? ? ?對(duì)應(yīng)的在有些編譯器中是不支持在聲明常量的時(shí)候直接賦值的,例如:class GamePlay{ private:static const int NumTurns;//static class常量聲明位于頭文件內(nèi)....... }后面接著定義:
const int GamePlay::NumTurns=5;//static class常量定義位于實(shí)現(xiàn)文件內(nèi)
enum
1 ?比較像#define而不像const。取一個(gè)enum的地址不合法,所以可以防止pointer或reference指向你的某個(gè)整數(shù)常量。
2 ?模板元編程的基礎(chǔ)技術(shù)
?Template Inline代替宏
#define CALL_WITH_MAX(a,b) f((a) > (b)) ? (a) :(b)) int a = 5,b = 0 CALL_WITH_MAX(++a,b); //a被累加兩次 CALL_WITH_MAX(++a,b+10);//a被累加一次? ?對(duì)應(yīng)的替代方案就是采用inline函數(shù)來(lái)替換:
template<typename T> inline void callWithMax(cosnt T &a, cosnt T &b) {f(a > b ? a : b); }? ? ? ?其中用到了template模板函數(shù),之所以用引用時(shí)為了自定義的類型,如果只是內(nèi)置類型,可以不需要引用!- 對(duì)于單純常量,最好以const對(duì)象或enums替換#defines;
- 對(duì)于形似函數(shù)的宏,最好改用inline函數(shù)替換#defines。
條款三:盡量的使用const
1.const對(duì)于基本內(nèi)置類型的約束,主要指的是指針類型。
const int * pi; int * const pi;
在STL中,迭代器也是一種指針,const可以對(duì)迭代器進(jìn)行修飾,對(duì)應(yīng)的也有兩種修飾的方式:
const std::vector<int>::iterator iter; std::vector<int>::const_iterator iter;
? ? ? ?此外在函數(shù)的應(yīng)用中,也常用到const關(guān)鍵字,主要是兩點(diǎn),一是對(duì)于函數(shù)的形式參數(shù)的修飾上,如果參數(shù)在函數(shù)內(nèi)部不被修改那么一般情況下都要用const將參數(shù)修飾下,這樣當(dāng)函數(shù)接口暴露出去的時(shí)候,別人能很容的看清楚,還有一種比較少見(jiàn)的用法就是對(duì)函數(shù)返回值得修飾上,這個(gè)主要是防止最函數(shù)返回值進(jìn)行賦值操作,例如下面:class Ration{}; const Ration operator*(const Ration& lhs,const Ration* rhs);
2.const成員函數(shù)
class Ration{ private:int n_; public:int getn(){return n;}int getn() const{return m;}void setn(int i) const{n = i;} };? ? ? ?在C++中有函數(shù)重載的概念,對(duì)同名的函數(shù),如果函數(shù)的參數(shù)類型或者個(gè)數(shù)不相同,就可以作為不同的函數(shù),這個(gè)主要是通過(guò)在編譯源代碼過(guò)程中對(duì)不同的函數(shù)重新命名來(lái)實(shí)現(xiàn),對(duì)于const修飾的函數(shù),不能對(duì)對(duì)象的任何成員進(jìn)行修改,并且const修飾的對(duì)象只能調(diào)用對(duì)象的const成員函數(shù),其中const對(duì)象主要是作為函數(shù)的參數(shù)進(jìn)行傳遞的!
class Ration{ private:int n_; public:int getn(){return n;}int getn() const{return m;}void setn(int i) const{n = i;} };? ? ? ?定義一個(gè)const Ration test,則test調(diào)用getn()函數(shù)的時(shí)候只能調(diào)用const的get函數(shù),不能調(diào)用普通的成員函數(shù),如果沒(méi)有const的成員函數(shù),將報(bào)錯(cuò),對(duì)應(yīng)的例子中的setn函數(shù)定義為const函數(shù),但是它卻對(duì)成員n進(jìn)行了賦值,因此是不允許的,編譯也不會(huì)通過(guò),如果想讓setn()函數(shù)編譯通過(guò),我們可以借助關(guān)鍵詞mutable,將n定義為mutable int n,這樣即使在const函數(shù)中也可以對(duì)n進(jìn)行修改!咱們一般的應(yīng)用中很少直接定義一個(gè)const的對(duì)象,一般const的對(duì)象是用在函數(shù)的形式參數(shù)中出現(xiàn)的!
? ? ? ?當(dāng)const成員函數(shù)與非const成員函數(shù)功能相同的時(shí)候,我們一般不會(huì)定義兩個(gè)成員函數(shù),其中一個(gè)只是比另一個(gè)多了一個(gè)const的修飾,我們的解決辦法是讓非const的成員函數(shù)調(diào)用const的成員函數(shù),其中const的成員函數(shù)正常定義,此時(shí)可能用到C++中的強(qiáng)制類型轉(zhuǎn)換例如static_cast/const_cast等!
條款四:確定對(duì)象在使用前已經(jīng)初始化
? ? ? ?對(duì)于內(nèi)置類型以外的任何其他東西,初始化責(zé)任落在構(gòu)造函數(shù)(constructors)身上。確保每一個(gè)構(gòu)造函數(shù)都將對(duì)象的每一個(gè)成員初始化。
? ? ? ?C++規(guī)定,對(duì)象的成員變量的初始化動(dòng)作發(fā)生在進(jìn)入構(gòu)造函數(shù)本體之前。
? ? ? ?初始化列表免于先調(diào)用 default構(gòu)造函數(shù)然后再調(diào)用賦值操作符,只需調(diào)用一次拷貝構(gòu)造函數(shù)。更高效。
? ? ? ?構(gòu)造函數(shù)的最佳寫(xiě)法是,使用 member initialization list(成員初始化表)如:
ABEntry::ABEntry(char&name,char& address,list &phones):theName(name),theAddress(address),thePhones(phones) { ……. }
編譯器會(huì)為用戶自定義類型(user-definedtypes)之成員變量自動(dòng)調(diào)用default構(gòu)造函數(shù)--- 如果那些成員變量在“成員初始化列表”中沒(méi)有被指定初值的話。
成員變量是 const 或references,它們就一定需要初值,不能被賦值。
C++有著十分固定的“成員初始化次序”。Base classes 更早于其derived classes 被初始化,而class的成員變量總是以其聲明次序被初始化。
Static 對(duì)象,其有效時(shí)間從被構(gòu)造出來(lái)直到程序結(jié)束為止,因此stack和heap-based對(duì)象被排除。
注意:
為內(nèi)置對(duì)象進(jìn)行手工初始化,因?yàn)镃++不保證初始化它們;
構(gòu)造函數(shù)最好使用成員初始化列表,而不要在構(gòu)造函數(shù)本體內(nèi)使用賦值操作。初始化列表列出的成員變量,其排列次序應(yīng)該和它們?cè)陬愔械穆暶鞔涡蛳嗤?#xff1b;
為免除“跨編譯單元之初始化次序”問(wèn)題,請(qǐng)以local static對(duì)象替換non-local static對(duì)象。
轉(zhuǎn)載于:https://www.cnblogs.com/wangfengju/p/6172478.html
總結(jié)
以上是生活随笔為你收集整理的Effective C++ ——让自己习惯C++的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 封面人物丨“有味道”的学校什么样?她用2
- 下一篇: 在VC中如何使用OCX控件 【来源:ht