《Effective C++》學(xué)習(xí)筆記(5)

4 設(shè)計與聲明

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

理想上击碗,如果客戶企圖使用某個接口而卻沒有獲得他所預(yù)期的行為把跨,這個代碼不該通過編譯丧没;如果代碼通過了編譯鹰椒,它的作為就該是客戶所想要的。欲開發(fā)一個“容易被正確使用呕童,不容易被誤用”的接口篡帕,首先必須考慮客戶可能做出什么樣的錯誤哈街。

  • 許多客戶端錯誤可以因為導(dǎo)入新類型而獲得預(yù)防窍荧。在防范“不值得擁有的代碼”上浸间,類型系統(tǒng)是你的主要同盟國套蒂。
struct Day     {         
explicit Day(int d)        //explicit 避免隱式的轉(zhuǎn)換钞支。
    :val(d) {}        
int val;      
};  
... ...
class Date  {
public:
    Date(const Month& m, const Day& d, const Year& y);
... ...
}   
// 使用
Date d(Month(3), Day(30), Year(1995));

對日期進行類似的類型封裝茫蛹,能有效地避免不恰當(dāng)?shù)娜掌谫x值。
對值的限制可通過枚舉方法實現(xiàn)烁挟。

  • “除非有好的理由婴洼,否則應(yīng)該盡量令你的類型(定義的類)的行為與內(nèi)置類型一致”。

  • 在資源管理方面撼嗓,讓函數(shù)返回一個資源的指針改為返回一個智能指針柬采,以把delete責(zé)任推給智能指針。
    例如:

std::tr1::shared_ptr<Investment> createInvestment();    

這便實質(zhì)上強迫客戶將返回值存儲于一個tr1::shared_ptr內(nèi)且警,幾乎消除了忘記刪除底部Investment對象的可能性粉捻。

  • tr1::shared_ptr有一個特別好的性質(zhì)是:它會自動使用它的“每個指針專屬的刪除器”,因而消除另一個潛在的客戶錯誤:所謂的“cross-DLL problem”斑芜。這個問題發(fā)生于“對象在動態(tài)鏈接程序庫(DLL)中被new創(chuàng)建肩刃,卻在另一個DLL內(nèi)被delete銷毀”。tr1::shared_ptr沒有這個問題杏头,因為它缺省的刪除器是來自“tr1::shared_ptr誕生所在的那個DLL”的delete盈包。

note:

  1. 好的接口很容易被正確使用,不容易被誤用醇王。你應(yīng)該在你的所有接口中努力達(dá)成這些性質(zhì)呢燥。
  2. “促進正確使用”的辦法包括接口的一致性,以及與內(nèi)置類型的行為兼容寓娩。
  3. “阻止誤用”的辦法包括建立新類型叛氨、限制類型上的操作,束縛對象值棘伴,以及消除客戶的資源管理責(zé)任寞埠。
  4. tr1::shared_ptr支持定制刪除器。這可防范DLL問題排嫌,可被用來自動解除互斥鎖(條款14)等等畸裳。

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

C++就像在其它面向?qū)ο缶幊陶Z言一樣,當(dāng)你定義一個新class淳地,也就定義了一個新type怖糊。這意味著你并不只是類的設(shè)計者,更是類型的設(shè)計者颇象。重載函數(shù)和操作符伍伤、控制內(nèi)存的分配和歸還、定義對象的初始化和終結(jié)......全部在你手上遣钳。

設(shè)計一個良好的類扰魂,或者稱作類型,需要考慮以下設(shè)計規(guī)范:

  • 新類型的對象應(yīng)該如何被創(chuàng)建和銷毀?
  • 對象的初始化和對象的賦值該有什么樣的差別劝评?
  • 新類型的對象如果被passed by value(值傳遞)姐直,意味著什么?
  • 什么是新類型的“合法值”蒋畜?
  • 你的新類型需要配合某個繼承圖系嗎声畏?
  • 你的新類型需要什么樣的轉(zhuǎn)換?
  • 什么樣的操作符和函數(shù)對此新類型而言是合理的姻成?
  • 什么樣的標(biāo)準(zhǔn)函數(shù)應(yīng)該駁回插龄?
  • 誰該取用新類型的成員?
  • 什么是新類型的“未聲明接口”科展?
  • 你的新類型有多少一般化均牢?
  • 你真的需要一個新類型嗎?

note:
class的設(shè)計就是type的設(shè)計才睹。在定義一個新type之前徘跪,請確定你已經(jīng)考慮過本條款覆蓋的所有討論主題。


條款20:寧以 pass-by-reference-to-const 替換 pass-by-value

缺省情況下C++以by value方式傳遞對象至(或來自)函數(shù)砂竖。除非你另外指定真椿,否則函數(shù)參數(shù)都是以實際實參的副本為初值,而調(diào)用端所獲得的亦是返回值的一個副本乎澄。

這些副本由對象的拷貝構(gòu)造函數(shù)產(chǎn)生突硝。所以在以對象為pass by value時,可能會調(diào)用相應(yīng)的構(gòu)造函數(shù)(成員對象的構(gòu)造置济、基類對象的構(gòu)造)解恰,然后調(diào)用對應(yīng)的析構(gòu)函數(shù)。所以以by value的形式傳遞開銷還是比較大的浙于。

如果我們用pass-by-reference-to-const护盈,例如:

bool validateStudent(const Student& s);     //const,希望別對傳入對象進行不恰當(dāng)?shù)男薷模?    

這種傳遞方式效率高得多:沒有任何構(gòu)造函數(shù)或析構(gòu)函數(shù)被調(diào)用羞酗,因為沒有任何新對象被創(chuàng)建腐宋。

以傳引用方式傳遞參數(shù)也可以避免對象切割問題:即當(dāng)一個派生類對象以傳值的方式傳遞并被視為一個基類對象,基類對象的拷貝構(gòu)造函數(shù)會被調(diào)用檀轨,而“造成此對象的行為像個派生類對象”的那些特化性質(zhì)全被切割掉了胸竞,僅僅留下了基類對象。這一般不是你想要的参萄。解決切割問題的辦法卫枝,就是以by-reference-to-const的方式傳遞。

所以我們一般的做法應(yīng)該是這樣:內(nèi)置對象和STL的迭代器和函數(shù)對象讹挎,我們一般以傳值的方式傳遞校赤,而其它的任何東西都以傳引用的方式傳遞吆玖。

note:

  1. 盡量以pass-by-reference-to-const替代pass-by-value。前者通常比較高效马篮,并可避免切割問題沾乘。
  2. 以上規(guī)則并不適用于內(nèi)置類型,以及STL的迭代器和函數(shù)對象积蔚。對它們而言意鲸,pass-by-value往往比較適當(dāng)烦周。

條款21:必須返回對象時尽爆,別妄想返回其reference

當(dāng)我們領(lǐng)悟條款20中傳值的開銷后,總是避免于少用傳值读慎,然而在返回對象時漱贱,要格外小心了,因為你可能:傳遞一些引用或指針指向其實已經(jīng)不存在的對象夭委。這可不是件好事幅狮。

任何時候看到一個reference聲明式,你都應(yīng)該立刻問自己株灸,它的另一個名稱是什么崇摄?

函數(shù)創(chuàng)建新對象的途徑有二:在棧空間和堆空間

  • 棧(stack)上:即在函數(shù)內(nèi)的局部變量慌烧。局部變量在函數(shù)返回后就沒有存在的意義逐抑,若還對它“念念不忘”,將帶來災(zāi)難性后果屹蚊。所以傳引用在棧上不可行厕氨。

  • 堆(heap)上:在堆上構(gòu)造一個對象,并返回汹粤∶看似可行,也埋下了資源泄漏的危險嘱兼。誰該對這對象實施delete呢国葬?別把這種對資源的管理寄托完全寄托于用戶。所以傳引用在堆上不可行芹壕。

  • 可能還有一種想法:把“讓返回的引用指向一個被定義于函數(shù)內(nèi)部的靜態(tài)對象”汇四。出于我們對多線程安全性的疑慮,以及當(dāng)線程中兩個函數(shù)對單份對象的處理也可能帶來不可測行為哪雕。所以靜態(tài)對象也是不可行的船殉。

一個“必須返回新對象”的函數(shù)的正確寫法是:就讓那個函數(shù)返回一個新對象。

編譯器實現(xiàn)者實行最優(yōu)化斯嚎,用以改善產(chǎn)出碼的效率卻不改變其觀察的行為利虫。所以我們還是老老實實的返回一個對象吧挨厚。

note:
絕不要返回pointer或reference指向一個local stack對象,或返回reference指向一個heap-allocated對象糠惫,或返回pointer或reference指向一個local static對象而有可能同時需要多個這樣的對象疫剃。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市硼讽,隨后出現(xiàn)的幾起案子巢价,更是在濱河造成了極大的恐慌,老刑警劉巖固阁,帶你破解...
    沈念sama閱讀 211,348評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件壤躲,死亡現(xiàn)場離奇詭異,居然都是意外死亡备燃,警方通過查閱死者的電腦和手機碉克,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,122評論 2 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來并齐,“玉大人漏麦,你說我怎么就攤上這事】鐾剩” “怎么了撕贞?”我有些...
    開封第一講書人閱讀 156,936評論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長测垛。 經(jīng)常有香客問我捏膨,道長,這世上最難降的妖魔是什么赐纱? 我笑而不...
    開封第一講書人閱讀 56,427評論 1 283
  • 正文 為了忘掉前任脊奋,我火速辦了婚禮,結(jié)果婚禮上疙描,老公的妹妹穿的比我還像新娘诚隙。我一直安慰自己,他們只是感情好起胰,可當(dāng)我...
    茶點故事閱讀 65,467評論 6 385
  • 文/花漫 我一把揭開白布久又。 她就那樣靜靜地躺著,像睡著了一般效五。 火紅的嫁衣襯著肌膚如雪地消。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,785評論 1 290
  • 那天畏妖,我揣著相機與錄音脉执,去河邊找鬼。 笑死戒劫,一個胖子當(dāng)著我的面吹牛半夷,可吹牛的內(nèi)容都是我干的婆廊。 我是一名探鬼主播,決...
    沈念sama閱讀 38,931評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼巫橄,長吁一口氣:“原來是場噩夢啊……” “哼淘邻!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起湘换,我...
    開封第一講書人閱讀 37,696評論 0 266
  • 序言:老撾萬榮一對情侶失蹤宾舅,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后彩倚,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體筹我,經(jīng)...
    沈念sama閱讀 44,141評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,483評論 2 327
  • 正文 我和宋清朗相戀三年署恍,在試婚紗的時候發(fā)現(xiàn)自己被綠了崎溃。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,625評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡盯质,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出概而,到底是詐尸還是另有隱情呼巷,我是刑警寧澤,帶...
    沈念sama閱讀 34,291評論 4 329
  • 正文 年R本政府宣布赎瑰,位于F島的核電站王悍,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏餐曼。R本人自食惡果不足惜压储,卻給世界環(huán)境...
    茶點故事閱讀 39,892評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望源譬。 院中可真熱鬧集惋,春花似錦、人聲如沸踩娘。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽养渴。三九已至雷绢,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間理卑,已是汗流浹背翘紊。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留藐唠,地道東北人帆疟。 一個月前我還...
    沈念sama閱讀 46,324評論 2 360
  • 正文 我出身青樓孵滞,卻偏偏與公主長得像,于是被迫代替她去往敵國和親鸯匹。 傳聞我的和親對象是個殘疾皇子坊饶,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,492評論 2 348

推薦閱讀更多精彩內(nèi)容