Effective C中文版第三版 高清PDF總結(jié)[共97頁(yè)]
《Effective C中文版第三版 高清PDF總結(jié)[共97頁(yè)]》由會(huì)員分享,可在線閱讀,更多相關(guān)《Effective C中文版第三版 高清PDF總結(jié)[共97頁(yè)](97頁(yè)珍藏版)》請(qǐng)?jiān)谘b配圖網(wǎng)上搜索。
1、Effective C++閱讀筆記 Effective C++閱讀筆記 1 原則3:盡可能使用const 4 原則5:了解C++默默編寫(xiě)并調(diào)用哪些函數(shù) 6 原則6:若不想使用編譯器自動(dòng)生成的函數(shù),就應(yīng)該明確拒絕 8 原則7:為多態(tài)基類(lèi)聲明virtual析構(gòu)函數(shù) 11 原則8:別讓異常逃離析構(gòu)函數(shù) 15 原則9:絕不在構(gòu)造和析構(gòu)過(guò)程中調(diào)用virtual函數(shù) 16 原則10:令operator=返回一個(gè)reference to *this 19 原則11:在operator=中處理“自我賦值” 19 原則12:復(fù)制對(duì)象時(shí)勿忘其每一個(gè)成分 21 原則13:以對(duì)象管理資源 22
2、 原則14:在資源管理類(lèi)中小心COPYING行為 24 原則15:在資源管理類(lèi)中提供對(duì)原始資源的訪問(wèn) 25 原則16:成對(duì)使用new和delete時(shí)要采用相同形式 27 原則17:以獨(dú)立語(yǔ)句將newed對(duì)象置入智能指針 28 原則18:讓接口容易被正確使用,不易被誤用 29 原則19:設(shè)計(jì)class猶如設(shè)計(jì)type 30 原則20:寧以引用傳遞代替值傳遞 31 原則21:必須返回對(duì)象時(shí),別妄想返回其引用 32 原則22:將成員變量聲明為private 33 原則23:寧以非member、非friend替換member函數(shù) 34 原則24:若所有參數(shù)皆需要類(lèi)型轉(zhuǎn)換,請(qǐng)為此采用
3、非member函數(shù) 35 原則25:考慮寫(xiě)出一個(gè)不拋出異常的swap函數(shù) 37 原則26:盡可能延后變量定義式的出現(xiàn)時(shí)間 39 原則27:盡量少做類(lèi)型轉(zhuǎn)換動(dòng)作 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:避免屏蔽繼承而來(lái)的名字 54 原則34:區(qū)分接口繼承和實(shí)現(xiàn)繼承 55 原則35:考慮virtual函數(shù)以外的其他選擇 57 原則3
4、6:決不能重新定義繼承而來(lái)的非virtual函數(shù) 60 原則37:絕不重新定義繼承而來(lái)的缺省參數(shù)值 62 原則38:通過(guò)復(fù)合塑造出HAS-A關(guān)系或者根據(jù)某物實(shí)現(xiàn)出來(lái) 63 原則39:明知而審慎地使用PRIVATE繼承 64 原則40:明智而審慎地使用多重繼承 69 原則41:了解隱式接口和編譯期多態(tài) 71 原則42:了解typename的雙重意義 72 原則43:學(xué)習(xí)處理模版化基類(lèi)內(nèi)的名稱(chēng) 74 原則44:將與參數(shù)無(wú)關(guān)的代碼抽離templates 76 原則45:運(yùn)用成員函數(shù)模版接受所有兼容類(lèi)型 78 原則46:需要類(lèi)型轉(zhuǎn)換時(shí)請(qǐng)為模版定義非成員函數(shù) 80 原則47:請(qǐng)使用
5、traits classes表現(xiàn)類(lèi)型信息 82 原則48:認(rèn)識(shí)template元編程 85 原則49:了解new-handler的行為 87 原則50:了解new和delete的合理替換時(shí)機(jī) 90 條款51:編寫(xiě)new和delete時(shí)需固守常規(guī) 92 原則52:寫(xiě)了placement new也要寫(xiě)placement delete 94 原則54:不要忽視編譯器的警告 95 原則54:讓自己熟悉包括TR1在內(nèi)的標(biāo)準(zhǔn)程序庫(kù) 96 原則55:讓自己熟悉Boost 97 本來(lái)是寫(xiě)在百度空間的,但是不知道咋回事百度博客中圖片看不到了,所以百度博客的不穩(wěn)定性可見(jiàn)一斑。于是我決定
6、將我的領(lǐng)會(huì)和感受寫(xiě)在自己的云盤(pán)里面。雖好也弄個(gè)目錄啥的,最后再整車(chē)成PDF格式的。這個(gè)標(biāo)準(zhǔn)我就參考我研究生期間論文的格式吧。 原則3:盡可能使用const 《Effective C++》里面第3條原則是盡量使用const。 其原因是防止無(wú)意中更改而本來(lái)不應(yīng)該更改的變量。 本條款也提到const成員函數(shù)的重要性,原因之一就是只有const函數(shù)才能用來(lái)操縱const對(duì)象。 而所謂const對(duì)象就像下圖所示的這樣: 有的時(shí)候會(huì)遇到在const函數(shù)中更改非const成員變量的情況,這個(gè)時(shí)候就要用到mutable關(guān)鍵字了。如果一個(gè)成員變量被mutable修飾,那么它在const函數(shù)中仍
7、然可以被修改,但是前提是該成員變量是非const成員。 還有一種情況就是為了防止代碼重復(fù),比如兩個(gè)函數(shù)實(shí)現(xiàn)了同樣的功能只是類(lèi)型不同而已,這樣就會(huì)導(dǎo)致兩段幾乎相同的代碼段,這無(wú)疑會(huì)增加編譯時(shí)間、維護(hù)和代碼膨脹等風(fēng)險(xiǎn)。在本原則的有關(guān)敘述中,作者采用了強(qiáng)制類(lèi)型轉(zhuǎn)換來(lái)解決之,雖然作者本身在大多數(shù)情況下并不提倡做法。 為了給用戶一個(gè)一目了然的接口,一看就知道那些成員函數(shù)可以操縱const對(duì)象而哪些不能,作者建議在類(lèi)中明確將那些不改變對(duì)象的成員函數(shù)聲明為const函數(shù),雖然const成員函數(shù)可以使用非const成員變量,但是遵守這一原則會(huì)給客戶帶來(lái)極大的便利。 因?yàn)閏onst成員函數(shù)不更改對(duì)象,這就
8、防止了由于誤操作而帶來(lái)的問(wèn)題,因?yàn)樽詈糜梅莄onst成員函數(shù)去調(diào)用const的實(shí)現(xiàn),說(shuō)白了就是直接return這個(gè)const成員函數(shù),只不過(guò)需要對(duì)作為這個(gè)return的表達(dá)式的const成員函數(shù)進(jìn)行一下強(qiáng)制類(lèi)型轉(zhuǎn)換使其成為非const型的。所以,在這里不得不提一下純粹的C++的強(qiáng)制類(lèi)型轉(zhuǎn)換。
關(guān)鍵在static_cast
9、alue)它用于無(wú)關(guān)類(lèi)型之間的轉(zhuǎn)換。
dynamic_cast
10、的,我在前邊的博文中闡述過(guò)了。 其中,默認(rèn)的構(gòu)造函數(shù)負(fù)責(zé)調(diào)用父類(lèi)和非static的構(gòu)造函數(shù)和析構(gòu)函數(shù)。如上圖可見(jiàn)編譯器自動(dòng)生成的析構(gòu)函數(shù)是非virtual的,如果父類(lèi)中本身存在virtual的析構(gòu)函數(shù),編譯器就不會(huì)自動(dòng)產(chǎn)生非virtual的析構(gòu)函數(shù)了。而默認(rèn)的copy構(gòu)造函數(shù)和copy賦值操作符只是copy非static成員到目標(biāo)對(duì)象。 不過(guò),如果你手動(dòng)寫(xiě)了它們中的一些,編譯器就只會(huì)自動(dòng)生成你沒(méi)寫(xiě)的。比如你只寫(xiě)了構(gòu)造函數(shù),那么其他的東西編譯器負(fù)責(zé)給你自動(dòng)生成。至于說(shuō)copy構(gòu)造函數(shù)和copy賦值操作符的用法我以前的博文有提到過(guò)。 而copy構(gòu)造函數(shù)總是層層調(diào)用底層的copy構(gòu)造函數(shù)來(lái)進(jìn)行
11、賦值,比如說(shuō)copy構(gòu)造函數(shù)要copy一個(gè)string類(lèi)型的變量,那么它就會(huì)調(diào)用string的copy構(gòu)造函數(shù),實(shí)在沒(méi)辦法了,它再自己進(jìn)行賦值操作。 其實(shí)本原則著重討論的是在什么情況下編譯器不會(huì)自動(dòng)生成這些東東。 對(duì)于默認(rèn)的構(gòu)造函數(shù)而言,當(dāng)你手動(dòng)寫(xiě)了一個(gè)構(gòu)造函數(shù)的話,編譯器就不會(huì)再費(fèi)那個(gè)勁了。 而對(duì)于copy賦值操作符呢也是有自動(dòng)生成條件的,那就是這個(gè)copy賦值操作符確實(shí)有存在的意義,并且它能在使用場(chǎng)合能正確工作,否則除非你自己手動(dòng)寫(xiě)一個(gè),要不然編譯器是不會(huì)給你生成這些東東的。而在書(shū)中作者舉了2個(gè)例子1個(gè)是引用,另一個(gè)是const常量,這兩者所指的對(duì)象都是不能更改的,那你非要給它們賦值
12、,那肯定會(huì)導(dǎo)致copy賦值操作符的失敗。 書(shū)中還舉個(gè)1個(gè)例子,一般情況下父類(lèi)中如果有copy賦值操作符,在子類(lèi)中編譯器是不會(huì)再給自動(dòng)生成copy賦值操作符,直接使用父類(lèi)的就好了,因?yàn)榫幾g器認(rèn)為子類(lèi)的copy賦值操作符是要能夠處理父類(lèi)的賦值操作的。所以如果你此時(shí)把父類(lèi)的copy賦值操作符設(shè)置為private的,那么你就沒(méi)有copy賦值操作符可用了,除非你自己在子類(lèi)中寫(xiě)一個(gè)。 原則6:若不想使用編譯器自動(dòng)生成的函數(shù),就應(yīng)該明確拒絕 這是《Effective C++》中第6個(gè)原則,在某些情況下你不想讓某些類(lèi)的對(duì)象被拷貝,那么在這種情況下即使你不寫(xiě)copy構(gòu)造函數(shù)和copy賦值操作符編譯器
13、也會(huì)為你生成,那么你不得不自己寫(xiě)它們倆。 而你又不希望別人調(diào)用它們,所以這時(shí)你要將它們聲明為private類(lèi)型。一旦你寫(xiě)了,編譯器就不會(huì)自動(dòng)調(diào)用父類(lèi)的copy構(gòu)造函數(shù)和copy賦值操作符。 即便這樣本類(lèi)內(nèi)部成員函數(shù)和友元函數(shù)還是可以調(diào)用它們,該如何是好?辦法就是你只聲明這些函數(shù)而不去實(shí)現(xiàn),沒(méi)有實(shí)現(xiàn)就自然沒(méi)有功能了,而既然實(shí)際上沒(méi)用,你甚至連形參都可以省略,只在形參列表中寫(xiě)個(gè)形參類(lèi)型即可,就像下圖類(lèi)的定義所示的這樣: 其中的幾個(gè)函數(shù)實(shí)現(xiàn)如下所示: 從上圖可見(jiàn),copy構(gòu)造函數(shù)和copy賦值操作符都沒(méi)有實(shí)現(xiàn)。在主程序中是如下調(diào)用的: 運(yùn)行結(jié)果如下所示: 出現(xiàn)了錯(cuò)誤提示,
14、說(shuō)copy構(gòu)造函數(shù)無(wú)法解析。 現(xiàn)在我把copy構(gòu)造函數(shù)和copy賦值操作符都注釋掉。 在運(yùn)行得如下結(jié)果: 而本思想只在闡述如果你不想讓編譯器為你自動(dòng)生成函數(shù),你就要自己手寫(xiě)。 原則7:為多態(tài)基類(lèi)聲明virtual析構(gòu)函數(shù) 這是《Effective C++》中第7條原則,其內(nèi)容是在具有多態(tài)用途的父類(lèi)中應(yīng)該使用virtual析構(gòu)函數(shù)。 首先要知道啥是多態(tài)。我就好說(shuō)直白的,顯得沒(méi)有深度的東西。多態(tài)的一種體現(xiàn)就是通過(guò)父類(lèi)的指針指向不同的子類(lèi)來(lái)實(shí)現(xiàn)不同的功能,從而達(dá)到接口重用的目的。在這種情況下用作多態(tài)的父類(lèi)往往具有至少一個(gè)virtual成員函數(shù)留給子類(lèi)來(lái)實(shí)現(xiàn)。 好了,現(xiàn)在鋪
15、墊完畢了,來(lái)說(shuō)正題,為啥要有一個(gè)virtual析構(gòu)函數(shù)呢?那是因?yàn)槿绻麤](méi)有這樣一個(gè)virtual析構(gòu)函數(shù)的話,子類(lèi)的析構(gòu)函數(shù)就不會(huì)被調(diào)用,那么對(duì)象的子類(lèi)部分不會(huì)被析構(gòu),那么就會(huì)造成資源的泄露?,F(xiàn)在來(lái)看下面的例子: derived繼承了base,并且base中并沒(méi)有virtual析構(gòu)函數(shù),那么調(diào)用過(guò)程如下所示: 運(yùn)行結(jié)果如下所示: 從這個(gè)結(jié)果可以看到父類(lèi)的析構(gòu)函數(shù)執(zhí)行了,說(shuō)明對(duì)象的父類(lèi)部分所占資源已經(jīng)被釋放,但是子類(lèi)的析構(gòu)函數(shù)并未調(diào)用這說(shuō)明對(duì)象中子類(lèi)部分所占資源并未得到釋放。但是如果在父類(lèi)中加上一個(gè)virtual析構(gòu)函數(shù)的話就不一樣了。 同樣的調(diào)用過(guò)程,運(yùn)行結(jié)果如下所示:
16、 這說(shuō)明對(duì)象的子類(lèi)部分所占資源也被釋放掉了。 在這里再說(shuō)點(diǎn)別的,一般來(lái)講作為要被繼承的父類(lèi)的類(lèi)中至少含有一個(gè)virtual的成員函數(shù)留給子類(lèi)去實(shí)現(xiàn)。而如果某類(lèi)中一個(gè)virtual成員函數(shù)都沒(méi)有的話,在很大程度上說(shuō)明了該類(lèi)不會(huì)被作為父類(lèi)而存在,在這種情況下不應(yīng)該把其析構(gòu)函數(shù)設(shè)為virtual的。為啥呢?這與C++中virtual本身的實(shí)現(xiàn)機(jī)制有關(guān),因?yàn)檫@樣的類(lèi)的對(duì)象必須要攜帶一個(gè)表,這個(gè)表叫vtbl,所以本來(lái)沒(méi)必要多帶這么個(gè)表,但是你非要多出一個(gè)來(lái)占個(gè)空間,這就是占個(gè)茅坑不拉屎的表現(xiàn)啊。所以不準(zhǔn)備被繼承的類(lèi)是沒(méi)有必要設(shè)置virtual析構(gòu)函數(shù)的。 在這里在介紹一種情況,當(dāng)你希望在vir
17、tual類(lèi)中把析構(gòu)函數(shù)設(shè)為virtual的時(shí)候,應(yīng)該吧析構(gòu)函數(shù)設(shè)為純virtual函數(shù),并且給與空的實(shí)現(xiàn),如下圖所示: 為啥要這樣做呢?因?yàn)槲鰳?gòu)函數(shù)調(diào)用順序是從沒(méi)有子類(lèi)的子類(lèi)那里的析構(gòu)函數(shù)逐層調(diào)用父類(lèi)的析構(gòu)函數(shù),所以如果這個(gè)純virtual函數(shù)沒(méi)有實(shí)現(xiàn)的話,編譯器就會(huì)報(bào)錯(cuò)。 這個(gè)原則簡(jiǎn)而言之就是,只有作為多態(tài)用途的父類(lèi)才有必要使用virtual析構(gòu)函數(shù),其他的就是畫(huà)蛇添足。 另外,處于繼承機(jī)制的類(lèi)對(duì)象包含了它所能涉及到的最低層次及其以上的所有層次的成分。 原則8:別讓異常逃離析構(gòu)函數(shù) 這是《Effective C++》的第八條原則。主要說(shuō)的是程序出現(xiàn)的異常不要從析構(gòu)函數(shù)
18、這里漏掉,也就是說(shuō)析構(gòu)函數(shù)應(yīng)該承擔(dān)起攔截異常的責(zé)任才行。如果異常越過(guò)了析構(gòu)函數(shù)這一關(guān),流竄到其他地方去,那么就會(huì)造成程序提早結(jié)束或者未知的風(fēng)險(xiǎn),這個(gè)后果就很?chē)?yán)重了。 對(duì)付這種情況通常有兩種簡(jiǎn)單粗暴的手段:1、在析構(gòu)函數(shù)內(nèi)發(fā)現(xiàn)異常,立刻捕捉到并且結(jié)束整個(gè)程序;2、在析構(gòu)函數(shù)中發(fā)現(xiàn)異常,立刻捕捉到并將其扼殺,掩人耳目,繼續(xù)執(zhí)行程序。 其中第一種手段比第二種手段要好,這是為啥呢?因?yàn)榉椒?直接結(jié)束程序,其結(jié)果是可預(yù)料的,不會(huì)造成太大破壞。而方法2你這個(gè)異常是終止了,但是程序中其他部分與這個(gè)功能相關(guān)的勢(shì)必會(huì)造成影響,也許還會(huì)因此帶來(lái)其他異常的連鎖反應(yīng),這個(gè)就不好辦了。 不過(guò)以上這兩種方法都沒(méi)能去
19、正面處理出現(xiàn)的異常,所以這兩種方法都不提倡。 書(shū)中給出的解決方案是,再創(chuàng)建一個(gè)類(lèi)用來(lái)處理異常,在這個(gè)類(lèi)中有一個(gè)成員函數(shù)專(zhuān)門(mén)用來(lái)處理原來(lái)的類(lèi)中的異常。而這個(gè)成員函數(shù)是調(diào)用原類(lèi)中的異常處理來(lái)完成的,這實(shí)際上就是變相的讓原類(lèi)自己處理異常,這是第一道關(guān)卡。然后異常處理類(lèi)的析構(gòu)函數(shù)中也有一份處理異常的代碼,這部分是異常處理類(lèi)自己的,這是第二道關(guān)卡。這個(gè)就是雙保險(xiǎn),如果說(shuō)在第二道關(guān)卡仍然不能有效處理異常,那沒(méi)辦法了,只能強(qiáng)行關(guān)閉程序了。 再總結(jié)一下本原則就是無(wú)論如何也不能讓異常突破析構(gòu)函數(shù)這一關(guān)。 原則9:絕不在構(gòu)造和析構(gòu)過(guò)程中調(diào)用virtual函數(shù) 這是《Effective C++》中的第
20、9條原則。 簡(jiǎn)單的來(lái)說(shuō),如果你在父類(lèi)的構(gòu)造函數(shù)中調(diào)用了虛擬函數(shù),那么子類(lèi)的成員就會(huì)始終處于未初始化的狀態(tài),這樣對(duì)象的子類(lèi)成分就會(huì)出現(xiàn)不可預(yù)知的行為,這是非常危險(xiǎn)的。 那么這是為什么呢?在繼承體系當(dāng)中,你聲明了一個(gè)子類(lèi)對(duì)象,那么這個(gè)子類(lèi)對(duì)象其實(shí)是很復(fù)雜的,它包含了子類(lèi)所有父類(lèi)的成分。而這些成分是要一層一層的進(jìn)行初始化的,其順序是按照類(lèi)的繼承層次從上而下進(jìn)行的,即從最遠(yuǎn)的那個(gè)父類(lèi)到最近的那個(gè)父類(lèi),然后是本類(lèi)的初始化。而C++的機(jī)制又是只有在父類(lèi)成分初始化完畢以后才去處理子類(lèi)成分的初始化工作,換句話說(shuō),如果父類(lèi)的成分沒(méi)有初始化完畢,它壓根就不會(huì)去管子類(lèi)的初始化工作。因?yàn)槟阍诟割?lèi)的構(gòu)造函數(shù)中調(diào)用了
21、虛擬函數(shù),而這個(gè)虛擬函數(shù)一般在父類(lèi)中是不進(jìn)行實(shí)現(xiàn)的。鄙人以為,一個(gè)函數(shù)之所以被調(diào)用肯定是因?yàn)檫@個(gè)函數(shù)是有一定的功能實(shí)現(xiàn)的,要不你調(diào)用它干嗎?我想C++的構(gòu)造函數(shù)也是這么想的。但是,你現(xiàn)在非要在本來(lái)用于初始化的構(gòu)造函數(shù)中去調(diào)用一個(gè)沒(méi)法初始化virtual函數(shù),C++就認(rèn)為這個(gè)對(duì)象的父類(lèi)成分還沒(méi)有初始化完畢,現(xiàn)在不能去初始化子類(lèi)成分。所以,即使你現(xiàn)在聲明了一個(gè)子類(lèi)對(duì)象,子類(lèi)中的成分還是沒(méi)有得到初始化,所以就會(huì)出現(xiàn)不可預(yù)知的后果。 C++對(duì)未定位的成員變量是采取無(wú)視的態(tài)度的,因?yàn)檫€沒(méi)輪到你,你給我一邊涼快去。在構(gòu)造函數(shù)期間無(wú)視,在析構(gòu)函數(shù)期間也是無(wú)視。而析構(gòu)的順序又是先子類(lèi)再父類(lèi),因?yàn)閷?duì)象的子類(lèi)成
22、分被無(wú)視,只有父類(lèi)成分被析構(gòu),所以那些未定義的成員變量自始至終都是未定義的,你也不知道它們最終會(huì)怎樣。 為了證實(shí)這一點(diǎn)請(qǐng)看下面的例子: 運(yùn)行結(jié)果是這樣的: 看到這個(gè)結(jié)果,我不得不說(shuō)現(xiàn)在的編譯器已經(jīng)很智能了,它直接把它攔下來(lái)了。 在介紹與本原則有關(guān)的內(nèi)容時(shí),作者舉了一個(gè)應(yīng)用場(chǎng)景。那就是當(dāng)類(lèi)中有多個(gè)不同版本的構(gòu)造函數(shù),它們的共同的初始化代碼都統(tǒng)一放到一個(gè)初始化成員函數(shù)里面了,并且這個(gè)初始化成員函數(shù)調(diào)用了一個(gè)virtual成員函數(shù),并且這個(gè)virtual成員函數(shù)會(huì)有一定的實(shí)現(xiàn)代碼。當(dāng)你建立子類(lèi)對(duì)象時(shí)卻調(diào)用了錯(cuò)誤的virtual成員函數(shù)。在此作者并沒(méi)有詳細(xì)地解釋是為什么,但他給出了
23、一種解決辦法。那就是在子類(lèi)的構(gòu)造函數(shù)的初始化成員列表中調(diào)用父類(lèi)的構(gòu)造函數(shù)去初始化對(duì)象中父類(lèi)的部分,當(dāng)然了,這時(shí)父類(lèi)中的那個(gè)virtual函數(shù)你要改成非virtual函數(shù)了。在這里作者使用了一個(gè)技巧,他不是在成員初始化列表直接把父類(lèi)成分所需的東西直接給它,而是通過(guò)一個(gè)輔助函數(shù)返回一個(gè)值給父類(lèi)進(jìn)行初始化,這樣寫(xiě)比較方便也比較可讀。而且,這個(gè)輔助函數(shù)的是一個(gè)static類(lèi)型。static類(lèi)型的成員函數(shù)是靜態(tài)成員函數(shù),它的類(lèi)的對(duì)象實(shí)例化之前就已經(jīng)被實(shí)例化,換句話說(shuō)它跟類(lèi)中其他的成員不發(fā)生關(guān)系。另外,子類(lèi)對(duì)象的父類(lèi)成分是在子類(lèi)成分之前先被實(shí)例化,而在子類(lèi)對(duì)象實(shí)例化的中間也就是父類(lèi)成分正在實(shí)例化,還沒(méi)輪到
24、子類(lèi)成分實(shí)例化之前,子類(lèi)中的成員函數(shù)啥的是未定義的,也就工作不了。而此時(shí)static成員函數(shù)卻能工作,這有助于對(duì)象中父類(lèi)成分的實(shí)例化,所以把此輔助函數(shù)設(shè)置為static類(lèi)型。 原則10:令operator=返回一個(gè)reference to *this 原則11:在operator=中處理“自我賦值” 在這篇博文里面我打算寫(xiě)兩個(gè)《Effective C++》中的原則,因?yàn)榈谝粋€(gè)原則太短了。 現(xiàn)在介紹第一個(gè)原則:條款10,此條款旨在說(shuō)明在你自己編寫(xiě)的賦值操作符=一定要返回該左值的引用。具體來(lái)說(shuō)就是返回*this。這很好解釋?zhuān)驗(yàn)閠his是指向本對(duì)象的指針,那么*this就是該對(duì)象的本
25、身實(shí)體了。而你現(xiàn)在返回的只不過(guò)是該實(shí)體的一個(gè)代表符號(hào)而已。現(xiàn)在一般的賦值操作符都采用這個(gè)原則,雖然不是強(qiáng)制的,但是大家都遵守。 下面來(lái)對(duì)條款11做一些介紹。 條款11的內(nèi)容很簡(jiǎn)單,就是一定要妥善處理賦值操作符=的自我賦值問(wèn)題,就是自己給自己賦值的情況。那么這又是為什么呢?因?yàn)樵谀承r(shí)候你要編寫(xiě)用來(lái)管理資源的類(lèi),那你知道一個(gè)資源不用了就要釋放掉,以便留給下一個(gè)需要該資源的對(duì)象。不過(guò),這是很合理的。但是,假設(shè)當(dāng)前占用該資源的對(duì)恰好是用來(lái)賦值的右值,也就是它倆其實(shí)是一個(gè)東東。如果還是按照上面處理的話,就會(huì)出現(xiàn)被用來(lái)賦值的右值實(shí)際上已經(jīng)啥實(shí)質(zhì)內(nèi)容都沒(méi)有了,this指針指向了NULL,那就會(huì)發(fā)生錯(cuò)誤
26、。那么怎樣處理這種情況呢? 第一種方法很簡(jiǎn)單,那就是在賦值操作符的實(shí)現(xiàn)中最先判斷一下賦值的對(duì)象和被賦值的對(duì)象是不是一個(gè),即if(this==&obj)。不過(guò)這種方法不好,因?yàn)檫@需要額外寫(xiě)出一條語(yǔ)句,這無(wú)疑會(huì)增加運(yùn)行時(shí)間。 所以實(shí)際上采用的辦法就是所謂的COPY and SWAP方法。什么意思呢?首先賦值一份右值,生成一個(gè)COPY,然后用*this與這個(gè)COPY進(jìn)行SWAP,那么就可以完美解決自我賦值的問(wèn)題。因?yàn)榧热皇荂OPY那么原來(lái)那個(gè)右值就沒(méi)有改變,而this原本是空的,它和那個(gè)COPY交換以后,那個(gè)COPY就變成了NULL,而this的內(nèi)容就成了COPY,也就是原來(lái)的那個(gè)右值的值了。
27、 還有一種方法就是直接利用賦值操作符重載函數(shù)的傳參機(jī)制是傳值這一特性,直接傳進(jìn)來(lái)一個(gè)COPY。該方法可行,但是作者不提倡,它說(shuō)這樣做的話清晰性變差了,我的這個(gè)清晰性大概就是可讀性吧。不過(guò),我倒覺(jué)得沒(méi)啥。他又說(shuō)這種方法有時(shí)候可能更高效。 原則12:復(fù)制對(duì)象時(shí)勿忘其每一個(gè)成分 這個(gè)原則是《Effective C++》中第12個(gè)原則,這原則主要涉及到兩個(gè)方面:1、自定義的COPY構(gòu)造函數(shù)和賦值操作符一定要卻把本類(lèi)中的所有成員,包括新增加的成員和父類(lèi)的所有成員都要復(fù)制過(guò)來(lái)。2、不要嘗試COPY構(gòu)造函數(shù)和賦值操作符進(jìn)行互相調(diào)用。 第1點(diǎn)沒(méi)啥好討論的,現(xiàn)在來(lái)著重討論第2點(diǎn)。 咱們只討論一種情
28、況,就是copy賦值操作符去掉用copy構(gòu)造函數(shù)的情況。因?yàn)閏opy構(gòu)造函數(shù)本身就已經(jīng)把復(fù)制了一個(gè)副本,換句話說(shuō)它自身已經(jīng)生成了一個(gè)嶄新的對(duì)象。而copy賦值操作符是把傳進(jìn)來(lái)的對(duì)象賦給這個(gè)嶄新的對(duì)象,那不就是試圖在構(gòu)造一個(gè)已經(jīng)存在的對(duì)象了嗎,這很荒謬。同理copy構(gòu)造函數(shù)去掉用copy賦值操作符同樣荒謬。所以作者建議方法是把它們共同的代碼放到第3個(gè)函數(shù)中,通常這個(gè)函數(shù)被命名為init。 原則13:以對(duì)象管理資源 這是《Effective C++》中提到的第13個(gè)原則。 資源的管理這一主題從宏觀上來(lái)講就是你申請(qǐng)了資源,就一定要釋放。你不釋放會(huì)造成內(nèi)存泄露,資源泄露,你釋放多了有可能導(dǎo)
29、致程序行為異常。所以簡(jiǎn)而言之就是你申請(qǐng)了多少個(gè)資源就釋放多少個(gè)資源就行了??墒悄阍诰幊痰倪^(guò)程難免會(huì)忘掉或者沒(méi)有處理好資源釋放的過(guò)程,那么本原則就是告訴你如何去控制資源的釋放的。 作者的經(jīng)驗(yàn)之談就是以對(duì)象作為資源的載體進(jìn)行傳遞以代替用單個(gè)語(yǔ)句去實(shí)現(xiàn)資源的管理過(guò)程。因?yàn)樵谶@個(gè)過(guò)程中,程序流說(shuō)不定就可能被return拐走,被作為異常拋出,被continue或者break跳過(guò),當(dāng)然還有那幾乎被摒棄的goto語(yǔ)句等等,而沒(méi)有到達(dá)delete。因?yàn)閷?duì)象本身有構(gòu)造函數(shù)和析構(gòu)函數(shù),而且它會(huì)在對(duì)象的生存期末尾自動(dòng)調(diào)用析構(gòu)函數(shù)來(lái)釋放資源,從而不會(huì)造成資源泄露的情況。其實(shí),我感覺(jué)此條款著重強(qiáng)調(diào)的是釋放資源的重要性
30、,但是它也強(qiáng)調(diào)在取得資源的時(shí)候馬上就進(jìn)行初始化。 而對(duì)于操縱對(duì)象作者又極力推薦了兩種智能指針:auto_ptr,shared_ptr。這兩個(gè)指針都會(huì)在資源使用結(jié)束后自動(dòng)銷(xiāo)毀它們,而不用你管。 auto_ptr的簡(jiǎn)單用法如下: 它有個(gè)特性,那就是一旦用這種指針指向某資源,那就不能有多個(gè)auto_ptr再去指向它了。如果已經(jīng)有一個(gè)指針指向了某資源,你再用新指針指向這個(gè)指針的話,就指針就被自動(dòng)設(shè)為NULL。 shared_ptr叫“引用計(jì)數(shù)型指針”,它與auto_ptr的不同之處在于它能記錄到底有多少個(gè)對(duì)象指向某資源,但是它無(wú)法解決環(huán)狀引用,就是兩個(gè)沒(méi)用的指針互指。 不過(guò),一般情況下,
31、智能指針里面裝的都是一個(gè)函數(shù),這個(gè)函數(shù)返回一個(gè)對(duì)象的引用,并完成該對(duì)象的初始化工作。
tr1::shared_ptr
32、和析構(gòu)函數(shù)這里的問(wèn)題,當(dāng)然我的理解可能不對(duì)。所以可能出現(xiàn)COPY過(guò)來(lái)的資源不能及時(shí)釋放掉。 作者給出的4個(gè)解決上述問(wèn)題的辦法:1、壓根就不復(fù)制資源管理對(duì)象,這就不會(huì)有問(wèn)題了嘛;2、采用“引用計(jì)數(shù)法”,即要達(dá)到COPY多少對(duì)象就釋放多少對(duì)象。這往往要用到shared_ptr;3、COPY要拷貝的全面,即在該類(lèi)的所有繼承體系中的類(lèi)的成分都COPY過(guò)來(lái);4、保持資源的獨(dú)一性,即它不會(huì)有多分COPY,而這往往要用到auto_ptr。 原則15:在資源管理類(lèi)中提供對(duì)原始資源的訪問(wèn) 這是《Effective C++》中第15條原則,我感覺(jué)非常抽象,理解起來(lái)很費(fèi)勁,那我就邊理解邊寫(xiě)博客吧。 首
33、先你要明白啥叫原始資源,其實(shí)確切的概念我也說(shuō)不準(zhǔn),但是你可以簡(jiǎn)單地理解為被資源管理類(lèi)管理的資源。 管理資源要使用資源管理類(lèi),通常這個(gè)類(lèi)被稱(chēng)為RAⅡ類(lèi),在理想的情況下,你總是試圖使用RAⅡ類(lèi)來(lái)進(jìn)行資源管理,但是世事無(wú)常,總有一些API(Application Programming Interface)會(huì)直接調(diào)用原始資源,它們會(huì)繞過(guò)RAⅡ類(lèi),而這不符合你的原則,而你又不得不去用。 那么本原則會(huì)叫你處理這種情況的一些方法。 作者舉了兩個(gè)例子: 1、通過(guò)傳遞資源管理類(lèi)的對(duì)象的某些方法間接傳遞一份原始資源的COPY,這樣真正的原始資源不會(huì)得到改變。那就需要RAⅡ類(lèi)提供一個(gè)接口使對(duì)象能夠暴露出其
34、內(nèi)所含的原始資源。而這通常是通過(guò)顯式轉(zhuǎn)換和隱式轉(zhuǎn)換來(lái)實(shí)現(xiàn)的。書(shū)中仍然是以智能指針auto_ptr和shared_ptr為例加以說(shuō)明,它們提供接口get來(lái)顯式獲取原始資源指針,另外還通過(guò)重載->和.來(lái)隱式獲取原始資源。 2、作者又舉了一個(gè)字體調(diào)用的例子。字體本身是一種原始資源,我們創(chuàng)建了一個(gè)類(lèi)用來(lái)管理字體。因?yàn)檫@種原始資源比較特殊應(yīng)用場(chǎng)合也很多,所以存在讓資源管理類(lèi)提供一個(gè)向外界開(kāi)放的接口的必要性,外界通過(guò)調(diào)用這個(gè)接口從而使用字體這種原始資源。又因?yàn)樽罱K是要使用這種原始資源的,所以必然會(huì)調(diào)用字體的類(lèi)型,從而這就存在一個(gè)類(lèi)型轉(zhuǎn)換的過(guò)程。同理,作者有提供了顯式和隱式轉(zhuǎn)換兩種轉(zhuǎn)換手段。 顯式轉(zhuǎn)換自
35、不必提,其實(shí)也是get,它極大地減少了資源泄露的可能性。 而隱式轉(zhuǎn)換可以自動(dòng)轉(zhuǎn)換為原始資源類(lèi)型,但是這存在一個(gè)問(wèn)題。那便是如果用戶現(xiàn)在就是想使用一個(gè)資源管理類(lèi)RAⅡ的對(duì)象,那沒(méi)辦法他現(xiàn)在必須轉(zhuǎn)換為原始資源類(lèi)型才能使用。而這隱含的兇兆就是如果你不經(jīng)意間刪除了RAⅡ?qū)ο?,那也就意外地刪除了原始資源的對(duì)象,那么你轉(zhuǎn)換過(guò)來(lái)的也就沒(méi)了。 總結(jié)一下,作者強(qiáng)調(diào)無(wú)論是顯示還是隱式轉(zhuǎn)換都是要視情況而定的,沒(méi)有完全的絕對(duì)。另外,RAⅡ是的職責(zé)是資源管理重在資源釋放,雖然訪問(wèn)原始資源突破了類(lèi)的封裝特性,但是這不是RAⅡ的首要存在意義。 原則16:成對(duì)使用new和delete時(shí)要采用相同形式 這個(gè)原則太
36、簡(jiǎn)單了。 當(dāng)你new一個(gè)數(shù)組的時(shí)候你要使用delete []釋放,當(dāng)你new一個(gè)指針的時(shí)候,你要使用delete釋放。如果搭配錯(cuò)了,后果都是未定義的。 這其中的原理,只要你懂得new和delete是操作符,并且把內(nèi)存分配看成對(duì)象來(lái)處理,并會(huì)調(diào)用構(gòu)造和析構(gòu)函數(shù)就會(huì)明白了。 原則17:以獨(dú)立語(yǔ)句將newed對(duì)象置入智能指針 這是《Effective C++》中第17個(gè)原則,作者以一個(gè)示例形象地說(shuō)明了這一點(diǎn)。 有一個(gè)資源處理函數(shù)A,這個(gè)函數(shù)中接收兩個(gè)參數(shù),它們分別是shared_ptr類(lèi)型的指針和一個(gè)整形參數(shù)。但是,因?yàn)橛脤?duì)象來(lái)管理資源的原則,所以在這里首先有了一個(gè)資源管理類(lèi)的對(duì)象,
37、并且想把它作為A的第一個(gè)參數(shù)傳進(jìn)去,而A的第二個(gè)參數(shù)用一個(gè)能返回整形參數(shù)的成員函數(shù)B作為實(shí)參傳進(jìn)來(lái)。程序員為了圖省事,他直接在A的第一個(gè)參數(shù)的位置上new了一個(gè)對(duì)象C,這個(gè)對(duì)象當(dāng)然就是資源管理類(lèi)的類(lèi)型了,但是A接受的是智能指針類(lèi)型,所以他還在此基礎(chǔ)上進(jìn)行強(qiáng)制類(lèi)型轉(zhuǎn)換到智能指針類(lèi)型。 這里還要介紹一個(gè)機(jī)制,那就是編譯器在產(chǎn)生函數(shù)調(diào)用碼之前,首先要對(duì)實(shí)參進(jìn)行核算。那么在核算期間,上述內(nèi)容就可以分成3步:1、new 對(duì)象C;2、調(diào)用B;3、強(qiáng)制把C轉(zhuǎn)換成shared_ptr類(lèi)型。 在上述三個(gè)步驟中,1和3的順序是確定的,那就是1在先3在后。但是2卻不一定了,這是根據(jù)語(yǔ)言和編譯器的不同而異的,所以
38、它們的順序可能是213、123、132。但是在123的情況下,如果調(diào)用B的過(guò)程發(fā)生了異常,導(dǎo)致程序終止,而new C返回的指針會(huì)丟失。又因?yàn)閟hared_ptr是用來(lái)防止資源泄露的,那么我們的目的沒(méi)有達(dá)到,new出來(lái)的C還是泄露了。 所以作者在此原則中想著重強(qiáng)調(diào)的是,你最好不要在調(diào)用函數(shù)的過(guò)程中直接在參數(shù)列表里面進(jìn)行new啊,類(lèi)型轉(zhuǎn)換之類(lèi)的操作,一旦發(fā)生資源泄露難以察覺(jué),所以你最好把這些都放在函數(shù)調(diào)用之前的單獨(dú)語(yǔ)句里面。 原則18:讓接口容易被正確使用,不易被誤用 這是《Effective C++》中的第18條原則。 1、作者在本原則中舉了一個(gè)函數(shù)接口的例子,在一般的情況下,用戶
39、可能錯(cuò)誤地輸入了參數(shù),而導(dǎo)致程序運(yùn)行不正確。針對(duì)這種情況作者推薦采用導(dǎo)入新類(lèi)型來(lái)解決此問(wèn)題。而這些類(lèi)型,你可以使用結(jié)構(gòu)體、枚舉類(lèi)型和帶有特定返回值的成員函數(shù)等來(lái)實(shí)現(xiàn)。而帶有特定返回值的成員函數(shù)一般來(lái)講是以函數(shù)體帶對(duì)象。 2、再有就是,作為接口設(shè)計(jì)者,你要限制用戶能做什么不能做什么。比如說(shuō)不讓用戶去染指資源管理的任務(wù)。 3、讓你接口提供的行為與內(nèi)置類(lèi)型的一般性行為一致。因?yàn)橛脩艨傁矚g對(duì)他們熟悉的東西反復(fù)用,并且喜歡套用在新的東西上。所以這樣做不僅可以讓你的接口更快被用戶接受,還不容易犯錯(cuò)。在這里作者舉了泛型算法的例子。 4、讓接口對(duì)客戶提出最少的要求。許多接口總是要求用戶注意這注意那,要求
40、一多,用戶就容易頭暈,這樣使用接口就更容易出錯(cuò)。所以一定要讓接口被傻瓜式地使用。在這里作者又舉了智能指針的例子。 原則19:設(shè)計(jì)class猶如設(shè)計(jì)type 英文是:Treat class design as type design。這是Effective C++中第19個(gè)原則。對(duì)類(lèi)的設(shè)計(jì)歸根結(jié)底可以歸結(jié)為類(lèi)型的設(shè)計(jì),因?yàn)轭?lèi)也是一種類(lèi)型的存在。該原則是若干個(gè)原則的集合,而這些原則是設(shè)計(jì)一個(gè)類(lèi)需要遵循的。這些原則具體如下: 1、類(lèi)對(duì)象的創(chuàng)建和銷(xiāo)毀如何進(jìn)行; 2、類(lèi)對(duì)象賦值和初始化的區(qū)別是什么; 3、類(lèi)對(duì)象在何時(shí)進(jìn)行值傳遞; 4、類(lèi)對(duì)象所能接受的值,不能接受哪些值,如何讓用戶容易使
41、用,不容易犯錯(cuò); 5、類(lèi)對(duì)象需要考慮的繼承體系; 6、類(lèi)對(duì)象的類(lèi)型轉(zhuǎn)換該如何實(shí)現(xiàn)。比如資源處理類(lèi)的對(duì)象; 7、對(duì)類(lèi)對(duì)象來(lái)說(shuō)那些操作符是必要的; 8、什么樣的編譯器自動(dòng)生成的或者已經(jīng)存在的成員函數(shù)你應(yīng)該自己重寫(xiě)并取而代之; 9、該類(lèi)中哪些成員能提供給外部; 10、哪些是新類(lèi)的未聲明接口;(這個(gè)目前我不太懂) 11、你是否要定義很多類(lèi)型?如果是,你還不去寫(xiě)個(gè)泛型類(lèi)。否則,你只寫(xiě)一個(gè)type就好了; 12、你真的有必要定義一個(gè)新類(lèi)型嗎? 原則20:寧以引用傳遞代替值傳遞 這是Effective C++中第20個(gè)原則。 對(duì)于類(lèi)對(duì)象而言,采用值傳遞是非常不明智的,因?yàn)樗鼤?huì)涉及
42、到COPY構(gòu)造函數(shù)和析構(gòu)函數(shù)的調(diào)用,如果你COPY的那個(gè)對(duì)象還包含了其他類(lèi)的對(duì)象,那就會(huì)涉及到更多的函數(shù)調(diào)用,而且這是呈指數(shù)級(jí)增長(zhǎng)的。而采用const&不僅極大地提高了效率而且還沒(méi)有任何構(gòu)造函數(shù)和析構(gòu)函數(shù)的調(diào)用。而之所以采用const則是由于先前的原則3. 另外采用const&可以避免對(duì)象切割問(wèn)題,雖然這個(gè)問(wèn)題我還沒(méi)認(rèn)識(shí)到那么深刻。當(dāng)一個(gè)子類(lèi)對(duì)象采用值傳遞方式被調(diào)用,但是被調(diào)用時(shí)的類(lèi)型要求是父類(lèi)類(lèi)型時(shí),那么這時(shí)父類(lèi)的COPY構(gòu)造函數(shù)會(huì)被調(diào)用,不要以為這種情況不能發(fā)生,記住這正是多態(tài)的體現(xiàn),而父類(lèi)類(lèi)型不過(guò)是個(gè)接口。這樣做的話,該對(duì)象本身的子類(lèi)特性就會(huì)被無(wú)視。 引用的底層是用指針來(lái)實(shí)現(xiàn)的,所以
43、你會(huì)發(fā)現(xiàn)引用和指針的行為有些相仿。所以對(duì)于內(nèi)置類(lèi)型,如果你以值傳遞的話效率會(huì)高些,比如說(shuō)VS中指針占4個(gè)字節(jié),而一個(gè)char占1個(gè)字節(jié)。另外對(duì)于STL迭代器和函數(shù),都被設(shè)計(jì)為采用以值傳遞,這是合理的,為什么呢,答案在原則1。 但是不能因?yàn)閷?duì)象小就采用值傳遞,這是因?yàn)橐环矫鎸?duì)象雖小但牽連廣泛,它所涉及的COPY構(gòu)造函數(shù)和析構(gòu)函數(shù)也不可小覷。另一方面,小對(duì)象也不排除將來(lái)因?yàn)樾枨蟮母淖兌兇蟮膬A向。 原則21:必須返回對(duì)象時(shí),別妄想返回其引用 Effective C++中第21個(gè)原則,因?yàn)橐檬且赶蚰骋汛嬖诘膶?duì)象的,但如果該對(duì)象某一瞬間突然消失了,這個(gè)引用被架空了,那就出錯(cuò)了。 為了
44、證實(shí)這一點(diǎn)作者舉了一個(gè)有理數(shù)相乘的例子。有這個(gè)一個(gè)有理數(shù)類(lèi),其中有一個(gè)有理數(shù)相乘的成員函數(shù),該成員函數(shù)返回該有理數(shù)類(lèi)的對(duì)象。在此例中該對(duì)象是一個(gè)本地對(duì)象,什么叫本地對(duì)象呢?就是一個(gè)普通的,局部的對(duì)象,它隨著作用域的結(jié)束而被自動(dòng)銷(xiāo)毀。因?yàn)榫邆溥@一性質(zhì),一旦你把這個(gè)函數(shù)的返回值賦給某一變量,然后該函數(shù)使命完成被自動(dòng)銷(xiāo)毀,那么它所返回的對(duì)象也就被自動(dòng)銷(xiāo)毀了,那么被賦值的變量的行為就未定義了。 然后作者又舉了動(dòng)態(tài)分配對(duì)象的例子。因?yàn)槭莕ew一個(gè)對(duì)象出來(lái),所以它肯定是要調(diào)用構(gòu)造函數(shù)進(jìn)行初始化工作的,但是你往往找不到一個(gè)合理的時(shí)機(jī)進(jìn)行析構(gòu)工作,從而導(dǎo)致資源泄露。 因?yàn)樯鲜鰞蓚€(gè)例子都是因?yàn)闉楸镜貙?duì)象調(diào)用
45、構(gòu)造函數(shù)而導(dǎo)致的,那么如果把本地變量寫(xiě)成static的不就避免了構(gòu)造函數(shù)和析構(gòu)函數(shù)的問(wèn)題了么。作者的回答是no。因?yàn)閟tatic對(duì)象是靜態(tài)分配,它在內(nèi)存中的位置是固定的,這樣多個(gè)操作對(duì)static對(duì)象進(jìn)行修改,static對(duì)象最后的內(nèi)容是最后修改的那個(gè)內(nèi)容,基于此種性質(zhì)。如果你的業(yè)務(wù)邏輯是多個(gè)對(duì)象之間才存在的,那么這樣做的后果肯定不是你想要的。 最后作者給出了自己的建議——你既然要返回一個(gè)對(duì)象,那就直接返回一個(gè)對(duì)象。 原則22:將成員變量聲明為private 這是Effective C++中第22個(gè)原則,原話是Declare data members private,即把數(shù)據(jù)成員稱(chēng)
46、名為私有的訪問(wèn)權(quán)限。 為什么要這樣做? 先說(shuō)public,public是提供用戶的接口,它根本不具備封裝特性。從實(shí)際來(lái)看,被聲明為public的都是成員函數(shù),用戶通過(guò)操作成員函數(shù)來(lái)操作類(lèi)內(nèi)的成員,而至于說(shuō)這個(gè)接口的內(nèi)部細(xì)節(jié)對(duì)于用戶來(lái)說(shuō)是不可見(jiàn)的,其中一個(gè)典型的例子就是getter和setter的運(yùn)用。 再有就是通過(guò)把public成員函數(shù)作為接口提供給用戶,那么這個(gè)接口的實(shí)現(xiàn)會(huì)有若干種可能以應(yīng)對(duì)不同的需求。還是因?yàn)樗墙涌?,是給用戶使用的,所以它不能變來(lái)變?nèi)サ?,否則你叫用戶怎么用。所以一旦某個(gè)成員函數(shù)被聲明為public的,那么一般情況下它就不能再有任何變化了,當(dāng)然這個(gè)變化是從用戶的角度來(lái)
47、看。從而推出越是廣泛使用的類(lèi),public成員聲明就越不能改變。 在這里有個(gè)普遍的準(zhǔn)則,那就是封裝的越好,改變成員時(shí)所破壞的代碼量就越少。 protected并不比public封裝性更好,因?yàn)槟愀淖僷ublic只是改變用戶代碼,而你改變protected則可能導(dǎo)致所有子類(lèi)的代碼的破壞,它倆的代碼破壞量都不可估量,后者更甚。 所以成員一旦聲明為protected和public那就意味著它們應(yīng)該是永遠(yuǎn)不變的。 所以從封裝的角度講只有兩中訪問(wèn)權(quán)限可言,那就是private封裝和其他不封裝。 原則23:寧以非member、非friend替換member函數(shù) 這是Effective C
48、++中第23條原則的內(nèi)容,我感到很奇怪,成員函數(shù)調(diào)用本類(lèi)的成員怎么了?難道這還不夠封裝嗎?看下面的解釋吧。 類(lèi)中可能存在一系列函數(shù)用來(lái)處理一系列操作,而這一系列函數(shù)可以放在類(lèi)中某一成員函數(shù)中一起執(zhí)行,但是也可以放在一個(gè)非成員函數(shù)中一起執(zhí)行。那么從封裝性上來(lái)講哪一個(gè)好呢?答案是非成員函數(shù),這是因?yàn)槌蓡T函數(shù)還可以訪問(wèn)類(lèi)中的私有成員,但是非成員函數(shù)連私有成員都訪問(wèn)不了,所以非成員函數(shù)的封裝性更好。 要知道越多東西被封裝,就會(huì)有更大的余地在不為人知的情況下更改被封裝的數(shù)據(jù),在這里友元函數(shù)和成員函數(shù)一樣,所以本原則推崇的是非友元函數(shù)和非成員函數(shù)。 這對(duì)于那些純面向?qū)ο笳Z(yǔ)言,像JAVA,而言很不幸,
49、因?yàn)樗鼈儧](méi)有獨(dú)立的函數(shù)存在。所以在這種情況下可以考慮寫(xiě)一個(gè)工具類(lèi),在此類(lèi)中添加函數(shù),這樣這些函數(shù)就不是成員函數(shù)了。而C++的做法是把這些獨(dú)立的函數(shù)和被操作的類(lèi)放在同一命名空間下。 本原則這樣做的另一個(gè)原因是出于可擴(kuò)展原則。因?yàn)橥瓿赡骋惶囟üδ芸赡苄枰幌盗械暮瘮?shù),但是你可能需要完成很多功能,而這些功能都屬于同一個(gè)工程。這個(gè)時(shí)候你可以為這個(gè)工程明明一個(gè)名字空間,然后把每個(gè)功能寫(xiě)在一個(gè)獨(dú)立的頭文件里面,因?yàn)樗鼈児餐`屬于同一個(gè)命名空間,所以在某個(gè)頭文件中你就可以把操作那一系列成員函數(shù)的非成員非友元函數(shù)也寫(xiě)在同一頭文件同一命名空間下。這樣做既降低了編譯相依性也提高程序擴(kuò)展性。 原則24:若
50、所有參數(shù)皆需要類(lèi)型轉(zhuǎn)換,請(qǐng)為此采用非member函數(shù) 這是Effective C++中第24個(gè)原則,即非成員函數(shù)能夠完美解決題目中所敘述的情景。 作者以一個(gè)有理數(shù)類(lèi)的例子來(lái)詮釋本原則所述內(nèi)容。這個(gè)例子大致是這樣的,這個(gè)有理數(shù)類(lèi)有一個(gè)帶有默認(rèn)值參數(shù)的構(gòu)造函數(shù),并且也有一個(gè)重載的乘法操作符,并且這個(gè)重載操作符函數(shù)只接受一個(gè)有理數(shù)類(lèi)的對(duì)象?,F(xiàn)在在它參數(shù)的位置上放上一個(gè)整數(shù),因?yàn)闃?gòu)造函數(shù)并非顯示,所以它允許將這個(gè)整數(shù)隱式轉(zhuǎn)換成該類(lèi)的類(lèi)型而參與運(yùn)算。 但是奇怪的現(xiàn)象來(lái)了,因?yàn)檫@個(gè)重載操作符是單目操作符,這時(shí)出現(xiàn)了一個(gè)賦值語(yǔ)句, 這個(gè)*就是這個(gè)單目操作符,oneHalf是一個(gè)有理數(shù)類(lèi)對(duì)象,2是
51、整數(shù),2可以被隱式轉(zhuǎn)換成有理數(shù)類(lèi)型。但是下面這個(gè)表達(dá)式 從邏輯上將實(shí)現(xiàn)的功能是一樣的,但是它確報(bào)錯(cuò),這是為啥呢?那是因?yàn)檫@個(gè)*的函數(shù)原型如下所示: 因?yàn)?并不在參數(shù)列表中,根本不存在類(lèi)型轉(zhuǎn)換,而又因?yàn)樗蟮氖莾蓚€(gè)有理數(shù)類(lèi)型參與運(yùn)算,因此2壓根不符合類(lèi)型要求,所以會(huì)報(bào)錯(cuò)。而這就是所謂的只有被列于參數(shù)列表內(nèi),這個(gè)參數(shù)才是隱式轉(zhuǎn)換的合格參與者。 那這里你一定會(huì)冒出一個(gè)疑問(wèn),既然參數(shù)列表里面只有一個(gè)參數(shù),那你就改寫(xiě)一下這個(gè)重載操作符的函數(shù)唄,這樣兩個(gè)參數(shù)不就都能進(jìn)行隱式轉(zhuǎn)換了嗎?嗯,這個(gè)想法是好的,不過(guò)類(lèi)中的*重載操作符不支持兩個(gè)參數(shù)的形式,請(qǐng)看下面的圖示: 但是非成員函數(shù)的*重
52、載操作符函數(shù)卻能支持兩個(gè)參數(shù)的形式,這樣兩個(gè)參數(shù)都能進(jìn)行隱式轉(zhuǎn)換了。 那么為什么不把非成員函數(shù)設(shè)成有原函數(shù)呢?那是因?yàn)橛性瘮?shù)的存在極大地破壞了封裝特性,不符合面向?qū)ο蟮乃枷?,亂用的話會(huì)帶來(lái)很大麻煩,所以能不用就不用。 原則25:考慮寫(xiě)出一個(gè)不拋出異常的swap函數(shù) Effective C++中第25個(gè)原則,頗為復(fù)雜,作者用了很長(zhǎng)的篇幅來(lái)講這個(gè)原則,我在理解這個(gè)原則上也花了不少功夫。 我還是先敘述一下這個(gè)作者所述的例子吧。 話說(shuō)在STL有這么一個(gè)泛型算法名叫SWAP,其功能如其名,就是用來(lái)交換,它能交換兩個(gè)類(lèi)型的對(duì)象,那更不用說(shuō)內(nèi)置類(lèi)型,因?yàn)閮?nèi)置類(lèi)型也可以說(shuō)是某種類(lèi)型的對(duì)象吧,
53、只要被交換類(lèi)型支持復(fù)制COPY操作就行。但是這個(gè)泛型算法的思路還是讓一個(gè)臨時(shí)變量tmp先接收一個(gè)對(duì)象a,然后把另一個(gè)對(duì)象b賦給這個(gè)對(duì)象a,之后再把這個(gè)臨時(shí)變量tmp的值再賦給a。思路很簡(jiǎn)單,也很正常,然后在某些情況下這種做法帶來(lái)的卻是效率的低下。 作者舉了一個(gè)常見(jiàn)的場(chǎng)景,那就是用指針指向一個(gè)對(duì)象的時(shí)候(pimpl,pointer to implementaion)這種做法具體就是一個(gè)類(lèi)A用來(lái)操作指針,這個(gè)指針指向類(lèi)B,另一個(gè)類(lèi)B中的內(nèi)容才是實(shí)質(zhì)的內(nèi)容。指針很小,但是對(duì)象很龐大,這個(gè)時(shí)候你再用復(fù)制的方法來(lái)交換那花費(fèi)的時(shí)間就不是一般兩般的長(zhǎng)了,占用的臨時(shí)空間也非常大。如果用泛型算法SWAP來(lái)交換
54、兩個(gè)A對(duì)象的話,因?yàn)锳對(duì)象中有指針已經(jīng)指向了B的對(duì)象,再加上SWAP中間的臨時(shí)變量tmp,那么SWAP不僅僅賦值了3個(gè)A對(duì)象,而且還復(fù)制了3個(gè)B對(duì)象,而后者并不是我們想要的,其效率之低下由此可見(jiàn)一斑。而我們想要的只是進(jìn)行到A的指針就可以了。 那這個(gè)時(shí)候該怎么辦呢?那我們就私人定制一個(gè)A類(lèi)專(zhuān)屬的SWAP好了,這就是所謂的SWAP針對(duì)A類(lèi)的特化。具體的做法就是弄一個(gè)全特化的SWAP版本,用它來(lái)交換給定對(duì)象中所包含的指針,然而這一版本并不能成功因?yàn)檫@個(gè)指針是私有的。解決這個(gè)問(wèn)題的辦法你可以考慮使用友元函數(shù),但是基于友元函數(shù)對(duì)于封裝性的破壞,我們能不用就不用。所以最好的做法就是聲明一個(gè)A類(lèi)的公有成員
55、函數(shù)SWAP,然后在這個(gè)公有成員的SWAP函數(shù)里面調(diào)用泛型算法只用來(lái)交換指針,這樣它就能訪問(wèn)A的私有成員指針了。同時(shí),在同一命名空間下再聲明一個(gè)非成員函數(shù)的SWAP,它的參數(shù)被特化為A類(lèi)型,這樣它直接調(diào)用實(shí)參對(duì)象,也就是A類(lèi)型對(duì)象的公有接口SWAP就可以完成交換了。而這種做法也恰恰符合了STL的風(fēng)格。 不過(guò),上面這種做法并不適合泛型函數(shù)級(jí)別上進(jìn)行偏特化,因?yàn)镃++只允許對(duì)泛型類(lèi)級(jí)別上進(jìn)行。那一般而言應(yīng)對(duì)這種情況的做法就是寫(xiě)一個(gè)重載函數(shù),這個(gè)重載函數(shù)只有參數(shù)是特化的。不過(guò),不要往std里面添加重載的泛型函數(shù),也可以說(shuō)成是你不要往std里面添加新東西,因?yàn)檫@些都是標(biāo)準(zhǔn)。所以,你還是自己弄個(gè)命名空
56、間然后把這些放到里面去。 有的時(shí)候純粹是為了方便,你希望你的特化SWAP被最大化使用,你還是應(yīng)該在你自定義的命名空間內(nèi)std的特化版本SWAP和本類(lèi)的專(zhuān)版,這樣編譯器就會(huì)根據(jù)實(shí)參的具體類(lèi)型自動(dòng)選擇最合適的版本了。 另外,SWAP作為成員函數(shù)決不能拋出異常,因?yàn)樗菐椭?lèi)提供一個(gè)異常安全性的保障。不過(guò)這一點(diǎn)不適用于非成員函數(shù)的SWAP,因?yàn)樗腔诳截悩?gòu)造函數(shù)和拷貝賦值操作符函數(shù),而這兩者是可能拋出異常的。一個(gè)規(guī)律就是你的SWAP效率越高,它的異常安全性越好,一個(gè)特例就是當(dāng)SWAP操作內(nèi)置類(lèi)型時(shí)SWAP根本不可能拋出異常。 什么是全特化?就是凡事涉及到泛型類(lèi)型的地方都用特定類(lèi)型代替之。
57、什么是偏特化?就是非全特化。 通過(guò)上述內(nèi)容作者想表達(dá)3個(gè)觀點(diǎn): 1、當(dāng)那個(gè)泛型SWAP效率實(shí)在低下時(shí),你自己寫(xiě)一個(gè),但是要確保不拋出異常; 2、如果你的SWAP是成員函數(shù),那你一定要用一個(gè)非成員函數(shù)來(lái)調(diào)用這個(gè)成員SWAP,并且特化一個(gè)std::SWAP; 3、不要往std里添加全新的東西。 原則26:盡可能延后變量定義式的出現(xiàn)時(shí)間 Effective C++中第26條原則,總的來(lái)說(shuō)就是這個(gè)變量你定義早了,你很難一眼看到這個(gè)變量在哪用了;如果你定義早了,這個(gè)變量你可能壓根沒(méi)用到過(guò),那你就是白白浪費(fèi)了空間。如果你定義的變量是個(gè)對(duì)象,那你還要花費(fèi)構(gòu)造函數(shù)和析構(gòu)函數(shù)的代價(jià)。如果在你定
58、義變量之后程序由于異常而中斷了,這個(gè)變量你根本沒(méi)用著,那你賠的更多了。 再有就是在你定義對(duì)象時(shí)最好直接給它賦值,因?yàn)槿绻悴贿@樣做,它是首先調(diào)用默認(rèn)構(gòu)造函數(shù),然后再調(diào)用拷貝賦值函數(shù),但是如果你直接拷貝那就不會(huì)調(diào)用默認(rèn)構(gòu)造函數(shù)了,所以這在效率上是一個(gè)提升。 本原則還講到一個(gè)用于循環(huán)的變量是定義在循環(huán)內(nèi)還是外?如果在內(nèi),那么你每次都要要調(diào)用拷貝構(gòu)造函數(shù)和析構(gòu)函數(shù),你循環(huán)多少次就調(diào)用多少次。如果在外,你只調(diào)用一次構(gòu)造和析構(gòu)函數(shù),循環(huán)多少次就調(diào)用多少次拷貝賦值操作函數(shù)多少次。 作者說(shuō)這是有條件的,這取決于拷貝構(gòu)造函數(shù)的代價(jià),如果一次拷貝的代價(jià)小于一次構(gòu)造函數(shù)+析構(gòu)函數(shù)的代價(jià),那么在外比較好,尤其
59、是在循環(huán)次數(shù)比較多的情況下,反之在內(nèi)比較好。不過(guò),從代碼的可讀性和可維護(hù)性方面來(lái)講,作者比較傾向于在內(nèi)的做法。 原則27:盡量少做類(lèi)型轉(zhuǎn)換動(dòng)作 這是Effective C++中第27個(gè)原則,作者花了很長(zhǎng)的篇幅來(lái)介紹這一原則。 總而言之一句話,因?yàn)轭?lèi)型轉(zhuǎn)換會(huì)導(dǎo)致破壞類(lèi)型系統(tǒng),從而帶來(lái)明顯的和不明顯麻煩,而且這是C++獨(dú)有的特性。 C++提供了四種新型的類(lèi)型轉(zhuǎn)換,就是下面這四種: const_cast是把const類(lèi)型轉(zhuǎn)換成非const類(lèi)型; dynamic_cast是類(lèi)體系中進(jìn)行轉(zhuǎn)換; reinterpret_cast是一個(gè)很強(qiáng)大的類(lèi)型轉(zhuǎn)換,最常用的是指針轉(zhuǎn)換為指針,而且
60、是兩個(gè)毫不相關(guān)的指針之間的轉(zhuǎn)換,它傳遞的是比特位。換句話說(shuō)這些比特位是不變的,但是它能按照另一種方式來(lái)解釋它。 static_cast就是平常的類(lèi)型轉(zhuǎn)換,用的最廣泛。 須知,在C++中,類(lèi)型轉(zhuǎn)換往往能夠令編譯器編譯出運(yùn)行時(shí)代碼,可想而知,這個(gè)代碼并不是你寫(xiě)的。在這里作者舉了一個(gè)多態(tài)的例子,當(dāng)你用父類(lèi)的指針指向子類(lèi)的對(duì)象時(shí),父類(lèi)的指針指向的地址和子類(lèi)對(duì)象所在的地址并不是一個(gè),它倆之間有時(shí)候是有個(gè)偏移量的。 作者又舉了一個(gè)例子?,F(xiàn)有一父類(lèi)和其子類(lèi),兩類(lèi)有兩個(gè)同名的虛函數(shù),現(xiàn)要求子類(lèi)虛函數(shù)中首先調(diào)用父類(lèi)虛函數(shù),這是子類(lèi)虛函數(shù)的代碼是這樣子的。 通過(guò)子類(lèi)的this指針強(qiáng)制類(lèi)型轉(zhuǎn)換成父類(lèi)類(lèi)型
61、,然后調(diào)用同名函數(shù)。但是次轉(zhuǎn)換的過(guò)程并不是你所想象的那樣簡(jiǎn)單,這個(gè)強(qiáng)制類(lèi)型轉(zhuǎn)換會(huì)創(chuàng)造出一個(gè)被轉(zhuǎn)型對(duì)象的副本,它是在這個(gè)副本身上執(zhí)行父類(lèi)的同名函數(shù),而在該對(duì)象身上執(zhí)行本類(lèi)專(zhuān)屬的同名函數(shù),兩者本應(yīng)作用于同一個(gè)對(duì)象而實(shí)際上卻沒(méi)有。而解決這個(gè)問(wèn)題的辦法就是不用強(qiáng)制類(lèi)型轉(zhuǎn)換,你該用父類(lèi)的成員函數(shù)就用就行了。 而dynamic_cast這個(gè)轉(zhuǎn)換你最好不要用,因?yàn)樗莿?dòng)態(tài)轉(zhuǎn)換,它會(huì)在整個(gè)類(lèi)層次上進(jìn)行尋找,而且每轉(zhuǎn)換一次就尋找一次,并且它是按照類(lèi)名進(jìn)行字符串匹配的。 通常你使用dynamic_cast進(jìn)行強(qiáng)制類(lèi)型轉(zhuǎn)換的情景是用一個(gè)父類(lèi)的指針指向子類(lèi)對(duì)象,這種情況你應(yīng)該使用的是子類(lèi)類(lèi)型的只能指針,作者使用的
62、是一個(gè)裝有智能指針的vector。如果子類(lèi)很多,并且你非要用父類(lèi)類(lèi)型的話,那你可以在父類(lèi)中提供一個(gè)同名空虛函數(shù),并在各個(gè)子類(lèi)中去實(shí)現(xiàn)它,然后你再用vector去容納裝有父類(lèi)類(lèi)型的智能指針,然后去操作。為啥要使用vector呢?因?yàn)樗穷?lèi)型安全容器。 在這里一定要記住不要在程序中多次使用dynamic_cast做沒(méi)有必要的類(lèi)型轉(zhuǎn)換,因?yàn)檫@樣做不僅代碼多,而且運(yùn)行慢,因?yàn)閐ynamic_cast需要查找嘛。 最后作者衷心地告訴大家盡量不要使用強(qiáng)制類(lèi)型轉(zhuǎn)換,尤其是除static_cast之外的另外三種;強(qiáng)制類(lèi)型轉(zhuǎn)換如果必要,那也要把它包裹在一個(gè)函數(shù)里面,不要讓用戶接觸到;推薦使用C++強(qiáng)制類(lèi)型轉(zhuǎn)
63、換,它不僅容易分辨,而且各司其職。 原則28:避免返回handles指向?qū)ο蟮膬?nèi)部成分 在這里首先要明確啥是handles,這里所說(shuō)的handles就是通常所說(shuō)的引用、指針或者迭代器等具有指代性質(zhì)的標(biāo)簽。那么題目所說(shuō)的意思就是你返回的那個(gè)handles不要涉及對(duì)象內(nèi)部的東西。 在敘述此原則的過(guò)程中作者舉了一個(gè)矩形Rectangular的例子,說(shuō)舉行的四個(gè)頂點(diǎn)存儲(chǔ)在一個(gè)結(jié)構(gòu)體中,Rectangular提供了兩個(gè)公有接口upperleft和lowerright它們返回的是該矩形左上角和右下角的頂點(diǎn)的坐標(biāo)結(jié)構(gòu)體,并且是以引用的形式返回的。因?yàn)橛脩糁恍柚肋@些頂點(diǎn)的坐標(biāo)而無(wú)需對(duì)這些頂點(diǎn)進(jìn)行
64、操作,因此這倆接口都是常函數(shù)。但是矛盾出現(xiàn)了,常函數(shù)的作用是不允許用戶去修改,但是這兩個(gè)常函數(shù)卻返回了Rectangular的私有成員。另外常函數(shù)只是說(shuō)在常函數(shù)體內(nèi)不進(jìn)行更改數(shù)據(jù)成員的操作,它返回的東西并不一定是常量。既然如此用戶就很有可能通過(guò)這倆接口去改變Rectangular類(lèi)原本私有成員,這是極其不符合該程序的初衷和封裝性原則的。 所以作者在這里說(shuō)“成員變量的封裝性最多等于其返回引用的訪問(wèn)級(jí)別”是很有道理的。作者還說(shuō)非公有的成員函數(shù)也是內(nèi)部數(shù)據(jù),這不是廢話嘛,我早就知道啊。作者想表達(dá)是啥呢,就是從訪問(wèn)級(jí)別上來(lái)講,不要讓那些訪問(wèn)級(jí)別高的成員函數(shù)返回指向訪問(wèn)級(jí)別低的成員函數(shù)的handles
65、,不過(guò)在我看來(lái),具體來(lái)講就是不要讓公有接口返回任何非公有的成員的handles。 那么這原則中所提及的問(wèn)題是怎么解決的呢?正如我所說(shuō)的,常函數(shù)只是在函數(shù)體內(nèi)不能對(duì)數(shù)據(jù)成員更改,但是它返回的東西并不一定不能被更改,所以呢,那么就把返回值也設(shè)成const就OK了。 盡管如此呢還是有個(gè)問(wèn)題存在,那就是函數(shù)雖然返回了const成員不能被更改,但是作為右值的語(yǔ)句結(jié)束以后,它的生命期就結(jié)束了,那右值的東西被析構(gòu)掉左值不就成了空吊子。 所以返回只想對(duì)象內(nèi)部成分的handles總是危險(xiǎn)的, 作者最后總結(jié)道:能不用handles只想對(duì)象內(nèi)部就不用,這樣可以增加封裝性,最好在const函數(shù)的返回類(lèi)型上也加
66、個(gè)const,也盡量避免空吊的出現(xiàn)。 原則29:為“異常安全”而努力是值得的 我怎么發(fā)現(xiàn)最近記錄的這幾條原則的敘述內(nèi)容都很冗長(zhǎng)無(wú)法一眼兩眼就能看懂。 作者首先從一個(gè)類(lèi)似于從MFC取材的例子,就是更換背景色的一個(gè)功能,并且它是在多線程環(huán)境下工作的。這個(gè)功能的具體實(shí)現(xiàn)首先加上互斥鎖,刪除舊背景,記錄圖像更改次數(shù),生成最新的背景圖片,去掉互斥鎖。然而,這種步驟并不符合異常安全性。 因?yàn)樽髡哒f(shuō)這種實(shí)現(xiàn)不符合異常安全性,而所謂的異常安全性,具體來(lái)講包括2個(gè)方面,它們是: 1、不泄露任何資源:上例很難做到,因?yàn)檎ι尚碌谋尘皥D片這一步,如果出現(xiàn)異常,程序就不會(huì)繼續(xù)往下執(zhí)行,那么互斥鎖就永遠(yuǎn)不會(huì)解鎖,那么占用的資源就會(huì)被釋放,那么就會(huì)造成資源泄露。 怎樣做到不泄露呢?條款14說(shuō)過(guò)就是用資源管理類(lèi)。這一點(diǎn)有點(diǎn)區(qū)別,在不使用資源管理類(lèi)時(shí)使用互斥鎖下圖這樣的: 而使用資源管理類(lèi)機(jī)制之后是如下的情形: Ml是Lock的一個(gè)對(duì)象,這樣它有自己的構(gòu)造函數(shù)和析構(gòu)函數(shù),這就避免了資源泄露,而且這不僅使程序較短而且還免去了寫(xiě)unlock,降低了程序的復(fù)雜性也就有效地降低了出錯(cuò)的可能性。
- 溫馨提示:
1: 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
2: 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
3.本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
5. 裝配圖網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 6.煤礦安全生產(chǎn)科普知識(shí)競(jìng)賽題含答案
- 2.煤礦爆破工技能鑒定試題含答案
- 3.爆破工培訓(xùn)考試試題含答案
- 2.煤礦安全監(jiān)察人員模擬考試題庫(kù)試卷含答案
- 3.金屬非金屬礦山安全管理人員(地下礦山)安全生產(chǎn)模擬考試題庫(kù)試卷含答案
- 4.煤礦特種作業(yè)人員井下電鉗工模擬考試題庫(kù)試卷含答案
- 1 煤礦安全生產(chǎn)及管理知識(shí)測(cè)試題庫(kù)及答案
- 2 各種煤礦安全考試試題含答案
- 1 煤礦安全檢查考試題
- 1 井下放炮員練習(xí)題含答案
- 2煤礦安全監(jiān)測(cè)工種技術(shù)比武題庫(kù)含解析
- 1 礦山應(yīng)急救援安全知識(shí)競(jìng)賽試題
- 1 礦井泵工考試練習(xí)題含答案
- 2煤礦爆破工考試復(fù)習(xí)題含答案
- 1 各種煤礦安全考試試題含答案