EffectiveC中文版第三版高清PDF總結(jié)_第1頁
EffectiveC中文版第三版高清PDF總結(jié)_第2頁
EffectiveC中文版第三版高清PDF總結(jié)_第3頁
EffectiveC中文版第三版高清PDF總結(jié)_第4頁
EffectiveC中文版第三版高清PDF總結(jié)_第5頁
已閱讀5頁,還剩45頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)

文檔簡介

1、EffectiveC+稿讀筆記EffectiveC+閱讀筆記1原則3:盡可能使用const4原則5:了解C+徽默編寫并調(diào)用哪些函數(shù)6原則6:若不想使用編譯器自動生成的函數(shù),就應(yīng)該明確拒絕8原則7:為多態(tài)基類聲明virtual析構(gòu)函數(shù)11原則8:別讓異常逃離析構(gòu)函數(shù)15原則9:絕不在構(gòu)造和析構(gòu)過程中調(diào)用virtual函數(shù)16原貝U10:令operator=返回一個referenceto*this19原則11:在operator=中處理自我賦值”19原則12:復(fù)制對象時勿忘其每一個成分21原則13:以對象管理資源22原則14:在資源管理類中小心COPYING亍為24原則15:在資源管理類中提供對原

2、始資源的訪問25原則16:成對使用new和delete時要采用相同形式27原則17:以獨(dú)立語句將newed對象置入智能指針28原則18:讓接口容易被正確使用,不易被誤用29原貝U19:設(shè)計class猶如設(shè)計type30原則20:寧以引用傳遞代替值傳遞31原則21:必須返回對象時,別妄想返回其引用32原則22:將成員變量聲明為private33原則23:寧以非member、非friend替換member函數(shù)34原則24:若所有參數(shù)皆需要類型轉(zhuǎn)換,請為此采用非member函數(shù)35原則25:考慮寫出一個不拋出異常的swap函數(shù)37原則26:盡可能延后變量定義式的出現(xiàn)時間39原則27:盡量少做類型轉(zhuǎn)換

3、動作40原則28:避免返回handles指向?qū)ο蟮膬?nèi)部成分43原則29:為異常安全”而努力是值得的45原則30:透徹了解inline(內(nèi)聯(lián))的里里外外48原則31:將文件間的變異依存關(guān)系降至最低50原則32:確定你的public繼承塑造出了IS-A關(guān)系53條款33:避免屏蔽繼承而來的名字54原則34:區(qū)分接口繼承和實現(xiàn)繼承55原則35:考慮virtual函數(shù)以外的其他選擇57原則36:決不能重新定義繼承而來的非virtual函數(shù)60原則37:絕不重新定義繼承而來的缺省參數(shù)值62原則38:通過復(fù)合塑造出HAS-A關(guān)系或者根據(jù)某物實現(xiàn)出來63原則39:明知而審慎地使用PRIVATE!承64原則40

4、:明智而審慎地使用多重繼承69原則41:了解隱式接口和編譯期多態(tài)71原貝U42:了解typename的雙重意義72原則43:學(xué)習(xí)處理模版化基類內(nèi)的名稱74原則44:將與參數(shù)無關(guān)的代碼抽離templates76原則45:運(yùn)用成員函數(shù)模版接受所有兼容類型78原則46:需要類型轉(zhuǎn)換時請為模版定義非成員函數(shù)80原則47:請使用traitsclasses表現(xiàn)類型信息82原貝U48:認(rèn)識template元編程85原貝U49:了解new-handler的行為87原則50:了解new和delete的合理替換時機(jī)90條款51:編寫new和delete時需固守常規(guī)92原貝U52:寫了placementnew也要寫

5、placementdelete94原則54:不要忽視編譯器的警告95原則54:讓自己熟悉包括TR1在內(nèi)的標(biāo)準(zhǔn)程序庫96原則55:讓自己熟悉Boost97本來是寫在百度空間的,但是不知道咋回事百度博客中圖片看不到了,所以百度博客的不穩(wěn)定性可見一斑。于是我決定將我的領(lǐng)會和感受寫在自己的云盤里面。雖好也弄個目錄啥的,最后再整車成PDF格式的。這個標(biāo)準(zhǔn)我就參考我研究生期間論文的格式吧。原則3:盡可能使用const«EffectiveC+里面第3條原則是盡量使用const。其原因是防止無意中更改而本來不應(yīng)該更改的變量。本條款也提到const成員函數(shù)的重要性,原因之一就是只有const函數(shù)才能用

6、來操縱const對象。而所謂const對象就像下圖所示的這樣:constTextBlockctb(tfWorld1T);有的時候會遇到在const函數(shù)中更改非const成員變量的情況,這個時候就要用到mutable關(guān)鍵字了。如果一個成員變量被mutable修飾,那么它在const函數(shù)中仍然可以被修改,但是前提是該成員變量是非const成員。還有一種情況就是為了防止代碼重復(fù),比如兩個函數(shù)實現(xiàn)了同樣的功能只是類型不同而已,這樣就會導(dǎo)致兩段幾乎相同的代碼段,這無疑會增加編譯時間、維護(hù)和代碼膨脹等風(fēng)險。在本原則的有關(guān)敘述中,作者采用了強(qiáng)制類型轉(zhuǎn)換來解決之,雖然作者本身在大多數(shù)情況下并不提倡做法。為了給

7、用戶一個一目了然的接口,一看就知道那些成員函數(shù)可以操縱const對象而哪些不能,作者建議在類中明確將那些不改變對象的成員函數(shù)聲明為const函數(shù),雖然const成員函數(shù)可以使用非const成員變量,但是遵守這一原則會給客戶帶來極大的便利。因為const成員函數(shù)不更改對象,這就防止了由于誤操作而帶來的問題,因為最好用非const成員函數(shù)去調(diào)用const的實現(xiàn),說白了就是直接return這個const成員函數(shù),只不過需要對作為這個return的表達(dá)式的const成員函數(shù)進(jìn)行一下強(qiáng)制類型轉(zhuǎn)換使其成為非const型的。所以,在這里不得不提一下純粹的C+的強(qiáng)制類型轉(zhuǎn)換。關(guān)鍵在static_cast<

8、;typename>(value)l純粹的C+強(qiáng)制類型轉(zhuǎn)換的關(guān)鍵詞和用法,它的使用頻率是最高的。3 -ivoidmain()4 (51 inta=13;cout<<static_cast<float>(a)<<endl;52 cin.get():53 )const_cast<typename>(valu吼用來消除const屬性時用的。不過它不能用于基本類型。reinterpret_cast<typename>(value它用于無關(guān)類型之間的轉(zhuǎn)換。dynamic_cast<typename>(value)1于父子類指針

9、之間的轉(zhuǎn)換。在C+語言中只有這4中強(qiáng)制類型轉(zhuǎn)換。原則5:了解C+破默編寫并調(diào)用哪些函數(shù)這一篇博客是EffectiveC+中第5個條款。但現(xiàn)在感覺我還沒太理解它到底說了什么,所以想寫寫博客,萬一寫著寫著就明白了呢。首先在這里敘述一個機(jī)制,那就是空類,在默認(rèn)的情況下,編譯器會給它自動生成默認(rèn)的構(gòu)造函數(shù)、拷貝構(gòu)造函數(shù)、拷貝賦值操作符=和析構(gòu)函數(shù)。并且他們都是public的和inline的。F3rllOlElclassEmpty111213;14它與下面這個類是一樣的10111213141516171分-classBudtvpublic:Empty0(;Empty(constEmptyfirhs);E

10、mpty();Emptyftoperatori(constEmptrhs):;;至于這些成員函數(shù)和操作符是干啥用的,我在前邊的博文中闡述過了其中,默認(rèn)的構(gòu)造函數(shù)負(fù)責(zé)調(diào)用父類和非static的構(gòu)造函數(shù)和析構(gòu)函數(shù)。如上圖可見編譯器自動生成的析構(gòu)函數(shù)是非virtual的,如果父類中本身存在virtual的析構(gòu)函數(shù),編譯器就不會自動產(chǎn)生非virtual的析構(gòu)函數(shù)了。而默認(rèn)的copy構(gòu)造函數(shù)和copy賦值操作符只是copy非static成員到目標(biāo)對象。不過,如果你手動寫了它們中的一些,編譯器就只會自動生成你沒寫的。比如你只寫了構(gòu)造函數(shù),那么其他的東西編譯器負(fù)責(zé)給你自動生成。至于說copy構(gòu)造函數(shù)和cop

11、y賦值操作符的用法我以前的博文有提到過。而copy構(gòu)造函數(shù)總是層層調(diào)用底層的copy構(gòu)造函數(shù)來進(jìn)行賦值,比如說copy構(gòu)造函數(shù)要copy一個string類型的變量,那么它就會調(diào)用string的copy構(gòu)造函數(shù),實在沒辦法了,它再自己進(jìn)行賦值操作。其實本原則著重討論的是在什么情況下編譯器不會自動生成這些東東。對于默認(rèn)的構(gòu)造函數(shù)而言,當(dāng)你手動寫了一個構(gòu)造函數(shù)的話,編譯器就不會再費(fèi)那個勁了。而對于copy賦值操作符呢也是有自動生成條件的,那就是這個copy賦值操作符確實有存在的意義,并且它能在使用場合能正確工作,否則除非你自己手動寫一個,要不然編譯器是不會給你生成這些東東的。而在書中作者舉了2個例子

12、1個是引用,另一個是const常量,這兩者所指的對象都是不能更改的,那你非要給它們賦值,那肯定會導(dǎo)致copy賦值操作符的失敗。書中還舉個1個例子,一般情況下父類中如果有copy賦值操作符,在子類中編譯器是不會再給自動生成copy賦值操作符,直接使用父類的就好了,因為編譯器認(rèn)為子類的copy賦值操作符是要能夠處理父類的賦值操作的。所以如果你此時把父類的copy賦值操作符設(shè)置為private的,那么你就沒有copy賦值操作符可用了,除非你自己在子類中寫一個。原則6:若不想使用編譯器自動生成的函數(shù),就應(yīng)該明確拒絕這是EffectiveC+中第6個原則,在某些情況下你不想讓某些類的對象被拷貝,那么在這

13、種情況下即使你不寫copy構(gòu)造函數(shù)和copy賦值操作符編譯器也會為你生成,那么你不得不自己寫它們倆。而你又不希望別人調(diào)用它們,所以這時你要將它們聲明為private類型。一且你寫了,編譯器就不會自動調(diào)用父類的copy構(gòu)造函數(shù)和copy賦值操作符。即使這樣本類內(nèi)部成員函數(shù)和友元函數(shù)還是可以調(diào)用它們,該如何是好?辦法就是你只聲明這些函數(shù)而不去實現(xiàn),沒有實現(xiàn)就自然沒有功能了,而既然實際上沒用,你甚至連形參都可以省略,只在形參列表中寫個形參類型即可,就像下圖類的定義所示的這樣:101112131415161718192021-classEmptypublic:Emptv(string4stra,int

14、aa);voidEmptyFnnction0:Empty(constEmpty&);零copf構(gòu)造函數(shù)n字En.ptoperator=(constEinptyS;);"copy賦值操作符。*public;stringstr;inta:其中的幾個函數(shù)實現(xiàn)如下所示:34567891011L2bvoidEmptv:EruptvFunction()donew<<endl;.Emptv:Emptv(stringistraintaa):strChelloworld")>a(0)str-stra;)從上圖可見,copy構(gòu)造函數(shù)和copy賦值操作符都沒有實現(xiàn)。在主

15、程序中是如下調(diào)用的:3Rintmain0"IoEmptyw(3tatij匕區(qū)總t<£tring)(*哇哈哈*)*101);6 cout<<a.str<<endL;7 Emptyb(a);cout<<b.str<end1;9cin-get():0return0;1運(yùn)行結(jié)果如下所示:):生成->,勺案動生成:項目:CtutsoleApilicationl,酉R置:Debugfin32eriOESOld.無法解析的外省符號public:thiscallEinply.BuplyEnplycoitst&)"(?i

16、OEflipLySCQAEaABVO06Z)?該符名在函*soleApplicatioTilVDebugVCoiiEoleApplicationl.exe:fatilerror11120:1個無去解析的外部命令成:成功0個,關(guān)敗1個,最斯口個,跳過。個出現(xiàn)了錯誤提示,說copy構(gòu)造函數(shù)無法解析?,F(xiàn)在我把copy構(gòu)造函數(shù)和copy賦值操作符都注釋掉。17110 E-classEmpty11 12|public:13lEmpty(stringftstra,intaa);14|voidEmptyFunctionO;15 -/Empty(constEmptY&);/*匕。鯉構(gòu)造函數(shù)二小16 /

17、EmptySoperator=(constEmpty&)賦值操作符*17|18 public:191 stringstr;inta;211:在運(yùn)行得如下結(jié)果:哇哈哈而本思想只在闡述如果你不想讓編譯器為你自動生成函數(shù),你就要自己手寫原則7:為多態(tài)基類聲明virtual析構(gòu)函數(shù)這是EffectiveC+中第7條原則,其內(nèi)容是在具有多態(tài)用途的父類中應(yīng)該使用virtual析構(gòu)函數(shù)。首先要知道啥是多態(tài)。我就好說直白的,顯得沒有深度的東西。多態(tài)的一種體現(xiàn)就是通過父類的指針指向不同的子類來實現(xiàn)不同的功能,從而達(dá)到接口重用的目的。在這種情況下用作多態(tài)的父類往往具有至少一個virtual成員函數(shù)留給子類

18、來實現(xiàn)好了,現(xiàn)在鋪墊完畢了,來說正題,為啥要有一個virtual析構(gòu)函數(shù)呢?那是因為如果沒有這樣一個virtual析構(gòu)函數(shù)的話,子類的析構(gòu)函數(shù)就不會被調(diào)用,那么對象的子類部分不會被析構(gòu),那么就會造成資源的泄露?,F(xiàn)在來看下面的例93456子:tclassbase(public:base()cout<<tfbasew<<endl;()cout<<wbase<<endl:)-Cla&Sderived:pub1icbasepublic:derived0cout<<derived.*<<endl;'derived()

19、caut<<*'derived*«endl;:derived繼承了base,并且base中并沒有virtual析構(gòu)函數(shù),那么調(diào)用過程如下所示:3sintmain()base*a=newderived。;deletea;cim&et();return0;運(yùn)行結(jié)果如下所示:從這個結(jié)果可以看到父類的析構(gòu)函數(shù)執(zhí)行了,說明對象的父類部分所占資源已經(jīng)被釋放,但是子類的析構(gòu)函數(shù)并未調(diào)用這說明對象中子類部分所占資源并未得到釋放。但是如果在父類中加上一個virtual析構(gòu)函數(shù)的話就不一樣了。同樣的調(diào)用過程,運(yùn)行結(jié)果如下所示:riseerivcdderived1bm玉e這說明

20、對象的子類部分所占資源也被釋放掉了。在這里再說點別的,一般來講作為要被繼承的父類的類中至少含有一個virtual的成員函數(shù)留給子類去實現(xiàn)。而如果某類中一個virtual成員函數(shù)都沒有的話,在很大程度上說明了該類不會被作為父類而存在,在這種情況下不應(yīng)該把其析構(gòu)函數(shù)設(shè)為virtual的。為啥呢?這與C+中virtual本身的實現(xiàn)機(jī)制有關(guān),因為這樣的類的對象必須要攜帶一個表,這個表叫vtbl,所以本來沒必要多帶這么個表,但是你非要多出一個來占個空間,這就是占個茅坑不拉屎的表現(xiàn)啊。所以不準(zhǔn)備被繼承的類是沒有必要設(shè)置virtual析構(gòu)函數(shù)的。在這里在介紹一種情況,當(dāng)你希望在virtual類中把析構(gòu)函數(shù)設(shè)

21、為virtual的時候,應(yīng)該吧析構(gòu)函數(shù)設(shè)為純virtual函數(shù),并且給與空的實現(xiàn),如下圖所示:12l-classbase13(111public:151base();16lvirtualbase()=0;n|):3卜base:base0,ilI(516為啥要這樣做呢?因為析構(gòu)函數(shù)調(diào)用順序是從沒有子類的子類那里的析構(gòu)函數(shù)逐層調(diào)用父類的析構(gòu)函數(shù),所以如果這個純virtual函數(shù)沒有實現(xiàn)的話,編譯器就會報錯。這個原則簡而言之就是,只有作為多態(tài)用途的父類才有必要使用virtual析構(gòu)函數(shù),其他的就是畫蛇添足。另外,處于繼承機(jī)制的類對象包含了它所能涉及到的最低層次及其以上的所有層次的成分。原則8:別讓異

22、常逃離析構(gòu)函數(shù)這是EffectiveC+的第八條原則。主要說的是程序出現(xiàn)的異常不要從析構(gòu)函數(shù)這里漏掉,也就是說析構(gòu)函數(shù)應(yīng)該承擔(dān)起攔截異常的責(zé)任才行。如果異常越過了析構(gòu)函數(shù)這一關(guān),流竄到其他地方去,那么就會造成程序提早結(jié)束或者未知的風(fēng)險,這個后果就很嚴(yán)重了。對付這種情況通常有兩種簡單粗暴的手段:1、在析構(gòu)函數(shù)內(nèi)發(fā)現(xiàn)異常,立刻捕捉到并且結(jié)束整個程序;2、在析構(gòu)函數(shù)中發(fā)現(xiàn)異常,立刻捕捉到并將其扼殺,掩人耳目,繼續(xù)執(zhí)行程序。其中第一種手段比第二種手段要好,這是為啥呢?因為方法1直接結(jié)束程序,其結(jié)果是可預(yù)料的,不會造成太大破壞。而方法2你這個異常是終止了,但是程序中其他部分與這個功能相關(guān)的勢必會造成影響

23、,也許還會因此帶來其他異常的連鎖反應(yīng),這個就不好辦了。不過以上這兩種方法都沒能去正面處理出現(xiàn)的異常,所以這兩種方法都不提倡。書中給出的解決方案是,再創(chuàng)建一個類用來處理異常,在這個類中有一個成員函數(shù)專門用來處理原來的類中的異常。而這個成員函數(shù)是調(diào)用原類中的異常處理來完成的,這實際上就是變相的讓原類自己處理異常,這是第一道關(guān)卡。然后異常處理類的析構(gòu)函數(shù)中也有一份處理異常的代碼,這部分是異常處理類自己的,這是第二道關(guān)卡。這個就是雙保險,如果說在第二道關(guān)卡仍然不能有效處理異常,那沒辦法了,只能強(qiáng)行關(guān)閉程序了。再總結(jié)一下本原則就是無論如何也不能讓異常突破析構(gòu)函數(shù)這一關(guān)。原貝U9:絕不在構(gòu)造和析構(gòu)過程中調(diào)

24、用virtual函數(shù)這是EffectiveC+中的第9條原則。簡單的來說,如果你在父類的構(gòu)造函數(shù)中調(diào)用了虛擬函數(shù),那么子類的成員就會始終處于未初始化的狀態(tài),這樣對象的子類成分就會出現(xiàn)不可預(yù)知的行為,這是非常危險的。那么這是為什么呢?在繼承體系當(dāng)中,你聲明了一個子類對象,那么這個子類對象其實是很復(fù)雜的,它包含了子類所有父類的成分。而這些成分是要一層一層的進(jìn)行初始化的,具順序是按照類的繼承層次從上而下進(jìn)行的,即從最遠(yuǎn)的那個父類到最近的那個父類,然后是本類的初始化。而C+的機(jī)制又是只有在父類成分初始化完畢以后才去處理子類成分的初始化工作,換句話說,如果父類的成分沒有初始化完畢,它壓根就不會去管子類的

25、初始化工作。因為你在父類的構(gòu)造函數(shù)中調(diào)用了虛擬函數(shù),而這個虛擬函數(shù)一般在父類中是不進(jìn)行實現(xiàn)的。鄙人以為,一個函數(shù)之所以被調(diào)用肯定是因為這個函數(shù)是有一定的功能實現(xiàn)的,要不你調(diào)用它干嗎?我想C+的構(gòu)造函數(shù)也是這么想的。但是,你現(xiàn)在非要在本來用于初始化的構(gòu)造函數(shù)中去調(diào)用一個沒法初始化virtual函數(shù),C+就認(rèn)為這個對象的父類成分還沒有初始化完畢,現(xiàn)在不能去初始化子類成分。所以,即使你現(xiàn)在聲明了一個子類對象,子類中的成分還是沒有得到初始化,所以就會出現(xiàn)不可預(yù)知的后果。C+對未定位的成員變量是采取無視的態(tài)度的,因為還沒輪到你,你給我一邊涼快去。在構(gòu)造函數(shù)期間無視,在析構(gòu)函數(shù)期間也是無視。而析構(gòu)的順序又

26、是先子類再父類,因為對象的子類成分被無視,只有父類成分被析構(gòu),所以那些未定義的成員變量自始至終都是未定義的,你也不知道它們最終會怎樣。為了證實這一點請看下面的例子:esabo456classchiId:publicbasepub1ic:base()display():coiit«'Thisisbase,"<<endl:):virtualvoiddisplay();virtualvoiddisplayL0;virtualdisplaylO;cont<<1111sisbase.w<<endl;private:;public:child

27、OistiinCOK*)cout«J*Thisischild."<<endl;:voidchilddisplay0;cout<strin<<endl:childO(coLLt«ffThisischild,«endl;private:stringstrin;;3child*a=newchild();|a->childdisplay1);deletea;8 cin_get():9 return0;0運(yùn)行結(jié)果是這樣的:r1a>>J士在生成代招.MAZK.wbj.iurIJDC2D19-iWfflff的外部特號jj

28、ubljc.vir-ruid_thiictUb&st:.displi-y(vaid)*(?diispl4也上T般LAEHliZ),謖哥號在慳盟ahj'tmrLR3QO1B王:主解析的蚱部存號、晶1;嶗:virtMlvoidtkasedLlb*.?*-Hiwpltyl(vci4.«漆符號UI如,;At«ipICoasoleXpplicstiBtbuKCcnscleArpltcatiwilesefatalerrorUNKZ.1ZJZT無法解忻的外部命令>#-eft-iInnA.Jl冊nA-口HA4.*tJ4ft4-看到這個結(jié)果,我不得不說現(xiàn)在的編譯器已經(jīng)很

29、智能了,它直接把它攔下來To在介紹與本原則有關(guān)的內(nèi)容時,作者舉了一個應(yīng)用場景。那就是當(dāng)類中有多個不同版本的構(gòu)造函數(shù),它們的共同的初始化代碼都統(tǒng)一放到一個初始化成員函數(shù)里面了,并且這個初始化成員函數(shù)調(diào)用了一個virtual成員函數(shù),并且這個virtual成員函數(shù)會有一定的實現(xiàn)代碼。當(dāng)你建立子類對象時卻調(diào)用了錯誤的virtual成員函數(shù)。在此作者并沒有詳細(xì)地解釋是為什么,但他給出了一種解決辦法。那就是在子類的構(gòu)造函數(shù)的初始化成員列表中調(diào)用父類的構(gòu)造函數(shù)去初始化對象中父類的部分,當(dāng)然了,這時父類中的那個virtual函數(shù)你要改成非virtual函數(shù)了。在這里作者使用了一個技巧,他不是在成員初始化列表

30、直接把父類成分所需的東西直接給它,而是通過一個輔助函數(shù)返回一個值給父類進(jìn)行初始化,這樣寫比較方便也比較可讀。而且,這個輔助函數(shù)的是一個static類型。static類型的成員函數(shù)是靜態(tài)成員函數(shù),它的類的對象實例化之前就已經(jīng)被實例化,換句話說它跟類中其他的成員不發(fā)生關(guān)系。另外,子類對象的父類成分是在子類成分之前先被實例化,而在子類對象實例化的中間也就是父類成分正在實例化,還沒輪到子類成分實例化之前,子類中的成員函數(shù)啥的是未定義的,也就工作不了。而此時static成員函數(shù)卻能工作,這有助于對象中父類成分的實例化,所以把此輔助函數(shù)設(shè)置為static類型。原貝U10:令operator3回一個refe

31、renceto*this原貝U11:在operatoE1處理“自我賦值”在這篇博文里面我打算寫兩個EffectiveC+中的原則,因為第一個原則太短了?,F(xiàn)在介紹第一個原則:條款10,此條款旨在說明在你自己編寫的賦值操作符=一定要返回該左值的引用。具體來說就是返回*this。這很好解釋,因為this是指向本對象的指針,那么*this就是該對象的本身實體了。而你現(xiàn)在返回的只不過是該實體的一個代表符號而已?,F(xiàn)在一般的賦值操作符都采用這個原則,雖然不是強(qiáng)制的,但是大家都遵守。下面來對條款11做一些介紹。條款11的內(nèi)容很簡單,就是一定要妥善處理賦值操作符=的自我賦值問題,就是自己給自己賦值的情況。那么這

32、又是為什么呢?因為在某些時候你要編寫用來管理資源的類,那你知道一個資源不用了就要釋放掉,以便留給下一個需要該資源的對象。不過,這是很合理的。但是,假設(shè)當(dāng)前占用該資源的對恰好是用來賦值的右值,也就是它倆其實是一個東東。如果還是按照上面處理的話,就會出現(xiàn)被用來賦值的右值實際上已經(jīng)啥實質(zhì)內(nèi)容都沒有了,this指針指向了NULL,那就會發(fā)生錯誤。那么怎樣處理這種情況呢?第一種方法很簡單,那就是在賦值操作符的實現(xiàn)中最先判斷一下賦值的對象和被賦值的對象是不是一個,即if(this=&obj)。不過這種方法不好,因為這需要額外寫出一條語句,這無疑會增加運(yùn)行時間。所以實際上采用的辦法就是所謂的COPY

33、andSWAP方法。什么意思呢?首先賦值一份右值,生成一個COPY,然后用*this與這個COPY進(jìn)行SWAP,那么就可以完美解決自我賦值的問題。因為既然是COPY那么原來那個右值就沒有改變,而this原本是空的,它和那個COPY交換以后,那個COPY就變成了NULL,而this的內(nèi)容就成了COPY,也就是原來的那個右值的值了。還有一種方法就是直接利用賦值操作符重載函數(shù)的傳參機(jī)制是傳值這一特性,直接傳進(jìn)來一個COPYo該方法可行,但是作者不提倡,它說這樣做的話清晰性變差了,我的這個清晰性大概就是可讀性吧。不過,我倒覺得沒啥。他又說這種方法有時候可能更高效。原則12:復(fù)制對象時勿忘其每一個成分這

34、個原則是EffectiveC+中第12個原則,這原則主要涉及到兩個方面:1、自定義的COPY構(gòu)造函數(shù)和賦值操作符一定要卻把本類中的所有成員,包括新增加的成員和父類的所有成員都要復(fù)制過來。2、不要嘗試COPY構(gòu)造函數(shù)和賦值操作符進(jìn)行互相調(diào)用。第1點沒啥好討論的,現(xiàn)在來著重討論第2點。咱們只討論一種情況,就是copy賦值操作符去掉用copy構(gòu)造函數(shù)的情況。因為copy構(gòu)造函數(shù)本身就已經(jīng)把復(fù)制了一個副本,換句話說它自身已經(jīng)生成了一個嶄新的對象。而copy賦值操作符是把傳進(jìn)來的對象賦給這個嶄新的對象,那不就是試圖在構(gòu)造一個已經(jīng)存在的對象了嗎,這很荒謬。同理copy構(gòu)造函數(shù)去掉用copy賦值操作符同樣荒

35、謬。所以作者建議方法是把它們共同的代碼放到第3個函數(shù)中,通常這個函數(shù)被命名為init。原則13:以對象管理資源這是«EffectiveC+中提到的第13個原則。資源的管理這一主題從宏觀上來講就是你申請了資源,就一定要釋放。你不釋放會造成內(nèi)存泄露,資源泄露,你釋放多了有可能導(dǎo)致程序行為異常。所以簡而言之就是你申請了多少個資源就釋放多少個資源就行了??墒悄阍诰幊痰倪^程難免會忘掉或者沒有處理好資源釋放的過程,那么本原則就是告訴你如何去控制資源的釋放的。作者的經(jīng)驗之談就是以對象作為資源的載體進(jìn)行傳遞以代替用單個語句去實現(xiàn)資源的管理過程。因為在這個過程中,程序流說不定就可能被return拐走,

36、被作為異常拋出,被continue或者break跳過,當(dāng)然還有那幾乎被摒棄的goto語句等等,而沒有到達(dá)delete。因為對象本身有構(gòu)造函數(shù)和析構(gòu)函數(shù),而且它會在對象的生存期末尾自動調(diào)用析構(gòu)函數(shù)來釋放資源,從而不會造成資源泄露的情況。其實,我感覺此條款著重強(qiáng)調(diào)的是釋放資源的重要性,但是它也強(qiáng)調(diào)在取得資源的時候馬上就進(jìn)行初始化。而對于操縱對象作者又極力推薦了兩種智能指針:auto_ptr,shared_ptr)這兩個指針都會在資源使用結(jié)束后自動銷毀它們,而不用你管。auto_ptr的簡單用法如下:Base*bobj=newBase;xuto_pt巳ptb(bobj);它有個特性,那就是一旦用這種

37、指針指向某資源,那就不能有多個auto_ptr再去指向它了。如果已經(jīng)有一個指針指向了某資源,你再用新指針指向這個指針的話,就指針就被自動設(shè)為NULL。shared_ptr叫“引用計數(shù)型指針”,它與auto_ptr的不同之處在于它能記錄到底有多少個對象指向某資源,但是它無法解決環(huán)狀引用,就是兩個沒用的指針互指。不過,一般情況下,智能指針里面裝的都是一個函數(shù),這個函數(shù)返回一個對象的引用,并完成該對象的初始化工作。tr1:shared_ptr<Base>ptb(factory();其中factory。函數(shù)負(fù)責(zé)初始化并返回管理資源的對象的引用。原則14:在資源管理類中小心COPYING行為

38、這是EffectiveC+中第14個原則。本原則闡述了資源管理類往往遵循RAR原則,就是“資源在構(gòu)造期間獲得,在析構(gòu)期間釋放”。因為是要用對象來承載資源的,而本原則考慮的是如果對這種對像進(jìn)行復(fù)制要怎樣處理。因為這種管理資源的對象在復(fù)制的過程中很少COPY所謂的“同步化基礎(chǔ)器物”,據(jù)我的理解就是構(gòu)造和析構(gòu)函數(shù)這里的問題,當(dāng)然我的理解可能不對。所以可能出現(xiàn)COPY過來的資源不能及時釋放掉。作者給出的4個解決上述問題的辦法:1、壓根就不復(fù)制資源管理對象,這就不會有問題了嘛;2、采用“引用計數(shù)法”,即要達(dá)到COPY多少對象就釋放多少對象。這往往要用到shared_ptr;3、COPY要拷貝的全面,即在

39、該類的所有繼承體系中的類的成分都COPY過來;4、保持資源的獨(dú)一性,即它不會有多分COPY,而這往往要用到auto_ptr。原則15:在資源管理類中提供對原始資源的訪問這是EffectiveC+中第15條原則,我感覺非常抽象,理解起來很費(fèi)勁,那我就邊理解邊寫博客吧。首先你要明白啥叫原始資源,其實確切的概念我也說不準(zhǔn),但是你可以簡單地理解為被資源管理類管理的資源。管理資源要使用資源管理類,通常這個類被稱為RAR類,在理想的情況下,你總是試圖使用RAR類來進(jìn)行資源管理,但是世事無常,總有一些API(ApplicationProgrammingInterface)會直接調(diào)用原始資源,它們會繞過RAI

40、I類,而這不符合你的原則,而你又不得不去用。那么本原則會叫你處理這種情況的一些方法。作者舉了兩個例子:1、通過傳遞資源管理類的對象的某些方法間接傳遞一份原始資源的COPY,這樣真正的原始資源不會得到改變。那就需要RAH類提供一個接口使對象能夠暴露出其內(nèi)所含的原始資源。而這通常是通過顯式轉(zhuǎn)換和隱式轉(zhuǎn)換來實現(xiàn)的。書中仍然是以智能指針auto_ptr和shared_ptr為例加以說明,它們提供接口get來顯式獲取原始資源指針,另外還通過重載,和.來隱式獲取原始資源。2、作者又舉了一個字體調(diào)用的例子。字體本身是一種原始資源,我們創(chuàng)建了一個類用來管理字體。因為這種原始資源比較特殊應(yīng)用場合也很多,所以存在

41、讓資源管理類提供一個向外界開放的接口的必要性,外界通過調(diào)用這個接口從而使用字體這種原始資源。又因為最終是要使用這種原始資源的,所以必然會調(diào)用字體的類型,從而這就存在一個類型轉(zhuǎn)換的過程。同理,作者有提供了顯式和隱式轉(zhuǎn)換兩種轉(zhuǎn)換手段。顯式轉(zhuǎn)換自不必提,其實也是get,它極大地減少了資源泄露的可能性。而隱式轉(zhuǎn)換可以自動轉(zhuǎn)換為原始資源類型,但是這存在一個問題。那便是如果用戶現(xiàn)在就是想使用一個資源管理類RAR的對象,那沒辦法他現(xiàn)在必須轉(zhuǎn)換為原始資源類型才能使用。而這隱含的兇兆就是如果你不經(jīng)意間刪除了RAR對象,那也就意外地刪除了原始資源的對象,那么你轉(zhuǎn)換過來的也就沒了。總結(jié)一下,作者強(qiáng)調(diào)無論是顯示還是隱

42、式轉(zhuǎn)換都是要視情況而定的,沒有完全的絕對。另外,RAR是的職責(zé)是資源管理重在資源釋放,雖然訪問原始資源突破了類的封裝特性,但是這不是RAR的首要存在意義。原貝U16:成對使用new和delete時要采用相同形式這個原則太簡單了。當(dāng)你new一個數(shù)組的時候你要使用delete釋放,當(dāng)你new一個指針的時候,你要使用delete釋放。如果搭配錯了,后果都是未定義的。這其中的原理,只要你懂得new和delete是操作符,并且把內(nèi)存分配看成對象來處理,并會調(diào)用構(gòu)造和析構(gòu)函數(shù)就會明白了。原則17:以獨(dú)立語句將newed對象置入智能指針這是EffectiveC+中第17個原則,作者以一個示例形象地說明了這一

43、點。有一個資源處理函數(shù)A,這個函數(shù)中接收兩個參數(shù),它們分別是shared_ptr類型的指針和一個整形參數(shù)。但是,因為用對象來管理資源的原則,所以在這里首先有了一個資源管理類的對象,并且想把它作為A的第一個參數(shù)傳進(jìn)去,而A的第二個參數(shù)用一個能返回整形參數(shù)的成員函數(shù)B作為實參傳進(jìn)來。程序員為了圖省事,他直接在A的第一個參數(shù)的位置上new了一個對象C,這個對象當(dāng)然就是資源管理類的類型了,但是A接受的是智能指針類型,所以他還在此基礎(chǔ)上進(jìn)行強(qiáng)制類型轉(zhuǎn)換到智能指針類型。這里還要介紹一個機(jī)制,那就是編譯器在產(chǎn)生函數(shù)調(diào)用碼之前,首先要對實參進(jìn)行核算。那么在核算期問,上述內(nèi)容就可以分成3步:1、new對象C;2

44、、調(diào)用B;3、強(qiáng)制把C轉(zhuǎn)換成shared_ptr類型。在上述三個步驟中,1和3的順序是確定的,那就是1在先3在后。但是2卻不一定了,這是根據(jù)語言和編譯器的不同而異的,所以它們的順序可能是213、123、132。但是在123的情況下,如果調(diào)用B的過程發(fā)生了異常,導(dǎo)致程序終止,而newC返回的指針會丟失。又因為shared_ptr是用來防止資源泄露的,那么我們的目的沒有達(dá)到,new出來的C還是泄露了。所以作者在此原則中想著重強(qiáng)調(diào)的是,你最好不要在調(diào)用函數(shù)的過程中直接在參數(shù)列表里面進(jìn)行new啊,類型轉(zhuǎn)換之類的操作,一旦發(fā)生資源泄露難以察覺,所以你最好把這些都放在函數(shù)調(diào)用之前的單獨(dú)語句里面。原則18:

45、讓接口容易被正確使用,不易被誤用這是«EffectiveC+中的第18條原則。1、作者在本原則中舉了一個函數(shù)接口的例子,在一般的情況下,用戶可能錯誤地輸入了參數(shù),而導(dǎo)致程序運(yùn)行不正確。針對這種情況作者推薦采用導(dǎo)入新類型來解決此問題。而這些類型,你可以使用結(jié)構(gòu)體、枚舉類型和帶有特定返回值的成員函數(shù)等來實現(xiàn)。而帶有特定返回值的成員函數(shù)一般來講是以函數(shù)體帶對象。2、再有就是,作為接口設(shè)計者,你要限制用戶能做什么不能做什么。比如說不讓用戶去染指資源管理的任務(wù)。3、讓你接口提供的行為與內(nèi)置類型的一般性行為一致。因為用戶總喜歡對他們熟悉的東西反復(fù)用,并且喜歡套用在新的東西上。所以這樣做不僅可以讓

46、你的接口更快被用戶接受,還不容易犯錯。在這里作者舉了泛型算法的例子。4、讓接口對客戶提出最少的要求。許多接口總是要求用戶注意這注意那,所以一定要讓接口被要求一多,用戶就容易頭暈,這樣使用接口就更容易出錯傻瓜式地使用。在這里作者又舉了智能指針的例子。原貝U19:設(shè)計class猶如設(shè)計type英文是:Treatclassdesignastypedesig這是EffectiveC+中第19個原則。對類的設(shè)計歸根結(jié)底可以歸結(jié)為類型的設(shè)計,因為類也是一種類型的存在。該原則是若干個原則的集合,而這些原則是設(shè)計一個類需要遵循的。這些原則具體如下:1、類對象的創(chuàng)建和銷毀如何進(jìn)行;2、類對象賦值和初始化的區(qū)別是

47、什么;3、類對象在何時進(jìn)行值傳遞;4、類對象所能接受的值,不能接受哪些值,如何讓用戶容易使用,不容易犯錯;5、類對象需要考慮的繼承體系;6、類對象的類型轉(zhuǎn)換該如何實現(xiàn)。比如資源處理類的對象;7、對類對象來說那些操作符是必要的;8、什么樣的編譯器自動生成的或者已經(jīng)存在的成員函數(shù)你應(yīng)該自己重寫并取而代之;9、該類中哪些成員能提供給外部;10、哪些是新類的未聲明接口;(這個目前我不太懂)11、你是否要定義很多類型?如果是,你還不去寫個泛型類。否則,你只寫一個type就好了;12、你真的有必要定義一個新類型嗎?原則20:寧以引用傳遞代替值傳遞這是EffectiveC+中第20個原則。對于類對象而言,采

48、用值傳遞是非常不明智的,因為它會涉及到COPY構(gòu)造函數(shù)和析構(gòu)函數(shù)的調(diào)用,如果你COPY的那個對象還包含了其他類的對象,那就會涉及到更多的函數(shù)調(diào)用,而且這是呈指數(shù)級增長的。而采用const&不僅極大地提高了效率而且還沒有任何構(gòu)造函數(shù)和析構(gòu)函數(shù)的調(diào)用。而之所以采用const則是由于先前的原則3.另外采用const&可以避免對象切割問題,雖然這個問題我還沒認(rèn)識到那么深刻。當(dāng)一個子類對象采用值傳遞方式被調(diào)用,但是被調(diào)用時的類型要求是父類類型時,那么這時父類的COPY構(gòu)造函數(shù)會被調(diào)用,不要以為這種情況不能發(fā)生,記住這正是多態(tài)的體現(xiàn),而父類類型不過是個接口。這樣做的話,該對象本身的子類特性

49、就會被無視。引用的底層是用指針來實現(xiàn)的,所以你會發(fā)現(xiàn)引用和指針的行為有些相仿。所以對于內(nèi)置類型,如果你以值彳遞的話效率會高些,比如說VS中指針占4個字節(jié),而一個char占1個字節(jié)。另外對于STL迭代器和函數(shù),都被設(shè)計為采用以值傳遞,這是合理的,為什么呢,答案在原則1。但是不能因為對象小就采用值傳遞,這是因為一方面對象雖小但牽連廣泛,它所涉及的COPY構(gòu)造函數(shù)和析構(gòu)函數(shù)也不可小覷。另一方面,小對象也不排除將來因為需求的改變而變大的傾向。原則21:必須返回對象時,別妄想返回其引用EffectiveC+中第21個原則,因為引用是要指向某已存在的對象的,但如果該對象某一瞬間突然消失了,這個引用被架空了

50、,那就出錯了。為了證實這一點作者舉了一個有理數(shù)相乘的例子。有這個一個有理數(shù)類,其中有一個有理數(shù)相乘的成員函數(shù),該成員函數(shù)返回該有理數(shù)類的對象。在此例中該對象是一個本地對象,什么叫本地對象呢?就是一個普通的,局部的對象,它隨著作用域的結(jié)束而被自動銷毀。因為具備這一性質(zhì),一旦你把這個函數(shù)的返回值賦給某一變量,然后該函數(shù)使命完成被自動銷毀,那么它所返回的對象也就被自動銷毀了,那么被賦值的變量的行為就未定義了。然后作者又舉了動態(tài)分配對象的例子。因為是new一個對象出來,所以它肯定是要調(diào)用構(gòu)造函數(shù)進(jìn)行初始化工作的,但是你往往找不到一個合理的時機(jī)進(jìn)行析構(gòu)工作,從而導(dǎo)致資源泄露。因為上述兩個例子都是因為為本

51、地對象調(diào)用構(gòu)造函數(shù)而導(dǎo)致的,那么如果把本地變量寫成static的不就避免了構(gòu)造函數(shù)和析構(gòu)函數(shù)的問題了么。作者的回答是no。因為static對象是靜態(tài)分配,它在內(nèi)存中的位置是固定的,這樣多個操作對static對象進(jìn)行修改,static對象最后的內(nèi)容是最后修改的那個內(nèi)容,基于此種性質(zhì)。如果你的業(yè)務(wù)邏輯是多個對象之間才存在的,那么這樣做的后果肯定不是你想要的。最后作者給出了自己的建議一一你既然要返回一個對象,那就直接返回一個對象。原貝U22:將成員變量聲明為private這是EffectiveC+中第22個原則,原話是Declaredatamembersprivate即把數(shù)據(jù)成員稱名為私有的訪問權(quán)限

52、。為什么要這樣做?先說public,public是提供用戶的接口,它根本不具備封裝特性。從實際來看,被聲明為public的都是成員函數(shù),用戶通過操作成員函數(shù)來操作類內(nèi)的成員,而至于說這個接口的內(nèi)部細(xì)節(jié)對于用戶來說是不可見的,其中一個典型的例子就是getter和setter的運(yùn)用。再有就是通過把public成員函數(shù)作為接口提供給用戶,那么這個接口的實現(xiàn)會有若干種可能以應(yīng)對不同的需求。還是因為它是接口,是給用戶使用的,所以它不能變來變?nèi)サ?,否則你叫用戶怎么用。所以一旦某個成員函數(shù)被聲明為public的,那么一般情況下它就不能再有任何變化了,當(dāng)然這個變化是從用戶的角度來看。從而推出越是廣泛使用的類,

53、public成員聲明就越不能改變。在這里有個普遍的準(zhǔn)則,那就是封裝的越好,改變成員時所破壞的代碼量就越少。protected并不比public封裝性更好,因為你改變public只是改變用戶代碼,而你改變protected則可能導(dǎo)致所有子類的代碼的破壞,它倆的代碼破壞量都不可估量,后者更甚。所以成員一旦聲明為protected和public那就意味著它們應(yīng)該是永遠(yuǎn)不變的。所以從封裝的角度講只有兩中訪問權(quán)限可言,那就是private封裝和其他不封裝。原則23:寧以非member、非friend替換member函數(shù)這是EffectiveC+中第23條原則的內(nèi)容,我感到很奇怪,成員函數(shù)調(diào)用本類的成員怎

54、么了?難道這還不夠封裝嗎?看下面的解釋吧。類中可能存在一系列函數(shù)用來處理一系列操作,而這一系列函數(shù)可以放在類中某一成員函數(shù)中一起執(zhí)行,但是也可以放在一個非成員函數(shù)中一起執(zhí)行。那么從封裝性上來講哪一個好呢?答案是非成員函數(shù),這是因為成員函數(shù)還可以訪問類中的私有成員,但是非成員函數(shù)連私有成員都訪問不了,所以非成員函數(shù)的封裝性更好。要知道越多東西被封裝,就會有更大的余地在不為人知的情況下更改被封裝的數(shù)據(jù),在這里友元函數(shù)和成員函數(shù)一樣,所以本原則推崇的是非友元函數(shù)和非成員函數(shù)。這對于那些純面向?qū)ο笳Z言,像JAVA,而言很不幸,因為它們沒有獨(dú)立的函數(shù)存在。所以在這種情況下可以考慮寫一個工具類,在此類中添

55、加函數(shù),這樣這些函數(shù)就不是成員函數(shù)了。而C+的做法是把這些獨(dú)立的函數(shù)和被操作的類放在同一命名空間下。本原則這樣做的另一個原因是出于可擴(kuò)展原則。因為完成某一特定功能可能需要一系列的函數(shù),但是你可能需要完成很多功能,而這些功能都屬于同一個工程。這個時候你可以為這個工程明明一個名字空間,然后把每個功能寫在一個獨(dú)立的頭文件里面,因為它們共同隸屬于同一個命名空間,所以在某個頭文件中你就可以把操作那一系列成員函數(shù)的非成員非友元函數(shù)也寫在同一頭文件同一命名空間下。這樣做既降低了編譯相依性也提高程序擴(kuò)展性。原則24:若所有參數(shù)皆需要類型轉(zhuǎn)換,請為此采用非member函數(shù)這是EffectiveC+中第24個原則

56、,即非成員函數(shù)能夠完美解決題目中所敘述的情景。作者以一個有理數(shù)類的例子來詮釋本原則所述內(nèi)容。這個例子大致是這樣的,這個有理數(shù)類有一個帶有默認(rèn)值參數(shù)的構(gòu)造函數(shù),并且也有一個重載的乘法操作符,并且這個重載操作符函數(shù)只接受一個有理數(shù)類的對象?,F(xiàn)在在它參數(shù)的位置上放上一個整數(shù),因為構(gòu)造函數(shù)并非顯示,所以它允許將這個整數(shù)隱式轉(zhuǎn)換成該類的類型而參與運(yùn)算。但是奇怪的現(xiàn)象來了,因為這個重載操作符是單目操作符,這時出現(xiàn)了一個賦值語句,resulthoneHalf*2;這個*就是這個單目操作符,oneHalf是一個有理數(shù)類對象,2是整數(shù),2可以被隱式轉(zhuǎn)換成有理數(shù)類型。但是下面這個表達(dá)式result=2*oneHa

57、lf;從邏輯上將實現(xiàn)的功能是一樣的,但是它確報錯,這是為啥呢?那是因為這個*的函數(shù)原型如下所示:constRationaloperator*(constRationalsrhs)const;因為2并不在參數(shù)列表中,根本不存在類型轉(zhuǎn)換,而又因為它要求的是兩個有理數(shù)類型參與運(yùn)算,因此2壓根不符合類型要求,所以會報錯。而這就是所謂的只有被列于參數(shù)列表內(nèi),這個參數(shù)才是隱式轉(zhuǎn)換的合格參與者。那這里你一定會冒出一個疑問,既然參數(shù)列表里面只有一個參數(shù),那你就改寫一下這個重載操作符的函數(shù)唄,這樣兩個參數(shù)不就都能進(jìn)行隱式轉(zhuǎn)換了嗎?嗯,這個想法是好的,不過類中的*重載操作符不支持兩個參數(shù)的形式,請看下面的圖示:constRatioraloperator*(constRational&am

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論