青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

洛譯小筑

別來(lái)無(wú)恙,我的老友…
隨筆 - 45, 文章 - 0, 評(píng)論 - 172, 引用 - 0
數(shù)據(jù)加載中……

[ECPP讀書(shū)筆記 條目18] 要讓接口易于正確使用,而不易被誤用

C++中到處充滿了接口。函數(shù)接口、類接口、模板接口,等等。每個(gè)接口都是實(shí)現(xiàn)客戶與你的代碼相交互的一種手段。假設(shè)你的客戶都是完全理性的,他們致力于更優(yōu)秀的完成當(dāng)前項(xiàng)目,他們便會(huì)十分看重你的接口是否能夠正確使用。這樣一來(lái),如果你的接口中的任意一個(gè)被他們誤用了,那么這個(gè)接口便成了這一錯(cuò)誤的“罪魁禍?zhǔn)?#8221;。在理想狀態(tài)下,如果客戶嘗試使用一個(gè)接口,但是沒(méi)有達(dá)到預(yù)期的效果,那么代碼則不應(yīng)通過(guò)編譯。反之,如果代碼通過(guò)了編譯,則運(yùn)行結(jié)果必須要符合客戶的需求。

開(kāi)發(fā)中我們應(yīng)做到讓接口更易于正確使用而不易被誤用,這需要你考慮到客戶會(huì)犯的各種錯(cuò)誤。請(qǐng)參見(jiàn)下邊的示例,假設(shè)你正在設(shè)計(jì)一個(gè)表示日期時(shí)間的類的構(gòu)造函數(shù):

class Date {

public:

  Date(int month, int day, int year);

  ...

};

乍一看,這一接口設(shè)計(jì)得很合理(至少在美國(guó)很合理),但是當(dāng)客戶面對(duì)這樣的接口時(shí),很容易犯下兩種錯(cuò)誤。第一,他們可能會(huì)使用錯(cuò)誤的傳參順序:

Date d(30, 3, 1995);               // 啊哦,應(yīng)該是“3, 30”而不是“30, 3

第二,他們可能會(huì)傳進(jìn)一個(gè)無(wú)效的月份或日期:

Date d(2, 30, 1995);               // 啊哦,應(yīng)該是“3, 30”而不是2, 30

(這一示例看上去有些愚蠢,但是不要忘了,在鍵盤(pán)上2和3是緊挨著的。這種“擦肩而過(guò)”的錯(cuò)誤在現(xiàn)實(shí)中并不少見(jiàn))

客戶犯下的許多錯(cuò)誤是可以通過(guò)引入新類型來(lái)避免的。實(shí)際上,對(duì)于防止不合要求的代碼通過(guò)編譯,類型系統(tǒng)是你最得力的助手。在上述情況下,我們可以引入幾個(gè)簡(jiǎn)單的“包裝類型”來(lái)區(qū)分日期、月份、和年份,然后再在Date的構(gòu)造函數(shù)中使用這些類型:

struct Day {

  explicit Day(int d) : val(d) {}

  int val;

};

 

struct Month {

  explicit Month(int m) : val(m) {}

  int val;

};

 

struct Year {

  explicit Year(int y) : val(y) {}

  int val;

};

 

class Date {

public:

 Date(const Month& m, const Day& d, const Year& y);

 ...

};

 

Date d(30, 3, 1995);                    // 報(bào)錯(cuò)!類型錯(cuò)誤

Date d(Day(30), Month(3), Year(1995)); // 報(bào)錯(cuò)!類型錯(cuò)誤

Date d(Month(3), Day(30), Year(1995)); // OK,類型正確

我們可以改善上邊應(yīng)用結(jié)構(gòu)體的簡(jiǎn)單思路,讓DayMonthYear變得“羽翼豐滿”,從而可以提供完善的數(shù)據(jù)封裝性(參見(jiàn)條目22)。但是即使是結(jié)構(gòu)體也足以說(shuō)服我們:適時(shí)引入新的類型可以十分有效地防止接口誤用。

只要你在恰當(dāng)?shù)牡胤绞褂昧饲‘?dāng)?shù)念愋停惚憧梢院侠淼叵拗七@些類型的值。比如說(shuō),一年有12個(gè)月,所以Month類型應(yīng)該能夠反映出這一點(diǎn)。一個(gè)途徑是使用枚舉類型來(lái)表示月份,但是枚舉類型并不總能達(dá)到我們對(duì)于類型安全的需求。比如說(shuō),枚舉類型可以像int一樣使用(參見(jiàn)條目2)。一個(gè)更安全的解決方法是:預(yù)先定義好所有有效Month值的集合:

class Month {

public:

  static Month Jan() { return Month(1); } // 用來(lái)返回所有有效月份值

  static Month Feb() { return Month(2); } // 的函數(shù);

  ...                                     // 下面你將看出為什么使用

  static Month Dec() { return Month(12); } // 函數(shù),而不是對(duì)象

 

  ...                                     // 其他成員函數(shù)

 

private:

  explicit Month(int m);                  // 防止創(chuàng)建新的月份值

  ...                                     // 與月份相關(guān)的數(shù)據(jù)

};

 

Date d(Month::Mar(), Day(30), Year(1995));

如果上面代碼中使用函數(shù)來(lái)代替具體月份的思路讓你感到奇怪,那么可能是由于你已經(jīng)忘記了聲明非局部靜態(tài)對(duì)象可能會(huì)帶來(lái)可靠性問(wèn)題。條目4可以喚醒你的記憶。

為防止客戶犯下類似的錯(cuò)誤,我們還可以采用另一個(gè)途徑,那就是嚴(yán)格限制一個(gè)類型可以做的事情。加強(qiáng)限制的一個(gè)常用的手段就是添加const屬性。比如說(shuō),條目3中曾解釋過(guò),const是如何通過(guò)限定operator*的返回值,從而防止客戶對(duì)用戶定義類型犯下以下的錯(cuò)誤的:

if (a * b = c) ...                 // 啊哦,本來(lái)是想進(jìn)行一次比較!

實(shí)際上,這僅僅是針對(duì)“讓接口易于正確使用,而不易被誤用”另一條一般性建議的一個(gè)表現(xiàn)形式,這條建議是:除非有更好的理由阻止你這樣做,否則你應(yīng)該保證你所創(chuàng)建類型的行為與內(nèi)建數(shù)據(jù)類型保持一致。因?yàn)榭蛻粢呀?jīng)清楚int的行為,所以只要是合情合理,你就應(yīng)該力求使你的類擁有與int一致的行為。比如說(shuō),如果abint類型,那么為a*b賦值就是不合法的。所以除非你有好的理由拒絕這一規(guī)定,否則你自己創(chuàng)建的類型也應(yīng)該將這一行為界定為不合法。當(dāng)你舉棋不定時(shí),就讓你的類型的行為與int保持一致。

設(shè)計(jì)接口時(shí)應(yīng)避免與內(nèi)建數(shù)據(jù)類型之間存在不必要的不兼容問(wèn)題,這樣做的真正目的是保持各類接口行為的一致性。很少有特征能像一致性這樣,可以讓接口如此易于正確使用;同時(shí),也很少有特征能像不一致性那樣,可以讓接口變得那般糟糕。STL容器的接口大體上(但并不完美)是一致的,這就使得它們更易于使用。比如說(shuō)每個(gè)STL容器都有一個(gè)名為size成員函數(shù),它可以告訴我們當(dāng)前這一容器中容納了多少對(duì)象。這一點(diǎn)與Java和.NET是不同的,Java中使用length屬性來(lái)表示數(shù)組的長(zhǎng)度,length方法來(lái)表示字符串的長(zhǎng)度,以及size方法來(lái)表示List的大小。而.NET中的Array擁有一個(gè)叫做Length的屬性,而ArrayList中功能相類似的屬性則叫做Count。一些開(kāi)發(fā)人員認(rèn)為,集成開(kāi)發(fā)環(huán)境(IDE)使得這類不一致性問(wèn)題變得不那么重要,但是實(shí)際上他們想錯(cuò)了。不一致性問(wèn)題會(huì)給開(kāi)發(fā)人員帶來(lái)無(wú)窮盡的煩惱,沒(méi)有哪個(gè)IDE是能夠完美解決這些問(wèn)題的。

任何接口都需要客戶記憶一些易發(fā)生錯(cuò)誤的內(nèi)容,這是因?yàn)榭蛻艨赡軙?huì)把這些東西搞砸。比如說(shuō),條目13中曾引入一個(gè)工廠函數(shù)來(lái)返回一個(gè)指向Investment層中動(dòng)態(tài)分配對(duì)象的指針:

Investment* createInvestment();   // 來(lái)自條目13,省略參數(shù)表以簡(jiǎn)化代碼

為防止資源泄漏,由createInvestment返回的指針在最后必須被刪除,但是這將會(huì)給客戶留下至少兩個(gè)犯錯(cuò)誤的機(jī)會(huì):忘記刪除指針、多于一次刪除同一指針。

條目13中介紹了客戶如何將createInvestment的返回值保存在諸如auto_ptrtr1::shared_ptr這樣的智能指針中,然后讓智能指針擔(dān)負(fù)起調(diào)用delete的責(zé)任。但是如果客戶忘記了使用智能指針,這該怎么辦呢?通常情況下,更好的接口的設(shè)計(jì)方案是:讓工廠函數(shù)返回一個(gè)智能指針,在一開(kāi)始就不給問(wèn)題任何發(fā)生的機(jī)會(huì)。

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

這樣便可以從根本上強(qiáng)制客戶使用tr1::shared_ptr來(lái)存儲(chǔ)返回值,這一做法基本上可以排除“忘記刪除當(dāng)前不再有用的Investment對(duì)象”的可能。

事實(shí)上,返回tr1::shared_ptr讓接口設(shè)計(jì)人員能夠防止與資源釋放相關(guān)的客戶端錯(cuò)誤,這是因?yàn)樵趧?chuàng)建tr1::shared_ptr智能指針時(shí),允許存在一個(gè)與當(dāng)前智能指針相綁定的資源釋放函數(shù)(即一個(gè)“刪除器”),而auto_ptr沒(méi)有這一功能。(參見(jiàn)條目14

假設(shè)客戶從createInvestment中得到了一個(gè)Investment*指針,在進(jìn)行刪除操作時(shí),我們期望這一客戶將這個(gè)指針傳給一個(gè)名為getRidOfInvestment的函數(shù),而不是使用delete。在這里,如果客戶會(huì)使用錯(cuò)誤的資源析構(gòu)機(jī)制(也就是使用delete而不是getRidOfInvestment),那么這樣的接口就帶來(lái)了新的客戶端錯(cuò)誤。實(shí)現(xiàn)createInvestment的程序員可以通過(guò)返回一個(gè)綁定getRidOfInvestment作為“刪除器”的tr1::shared_ptr來(lái)預(yù)防此類錯(cuò)誤。

tr1::shared_ptr提供了一個(gè)擁有兩個(gè)參數(shù)的構(gòu)造函數(shù),這兩個(gè)參數(shù)即:需要管理的指針,以及當(dāng)引用計(jì)數(shù)值為零時(shí)需要調(diào)用的刪除器。這使得我們可以創(chuàng)建使用getRidOfInvestment作為“刪除器”的空tr1::shared_ptr,請(qǐng)看下面的做法:

std::tr1::shared_ptr<Investment> pInv(0, getRidOfInvestment);

                                   // 嘗試創(chuàng)建一個(gè)nullshared_ptr

                                   // 并且讓其包含一個(gè)自定義的刪除器;

                                   // 這樣的代碼無(wú)法通過(guò)編譯

然而,這并不是合法的C++語(yǔ)法。tr1::shared_ptr的構(gòu)造函數(shù)的第一個(gè)參數(shù)必須是一個(gè)指針,而0則不是,它是一個(gè)int值。的確,數(shù)字可以當(dāng)做指針使用,但是這種情況下該做法并不值得推薦,tr1::shared_ptr的第一個(gè)參數(shù)必須是一個(gè)實(shí)際的指針。通過(guò)一次轉(zhuǎn)型可以解決這一問(wèn)題:

std::tr1::shared_ptr<Investment>

  pInv(static_cast<Investment*>(0), getRidOfInvestment);

                                   // 創(chuàng)建一個(gè)nullshared_ptr

                                   // 并且讓其包含一個(gè)自定義的刪除器;

                                   // static_cast的更多信息參見(jiàn)條目27

上面的代碼意味著,在實(shí)現(xiàn)createInvestment時(shí),可讓其返回一個(gè)“綁定了getRidOfInvestment刪除器的tr1::shared_ptr”:

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

{

  std::tr1::shared_ptr<Investment>

    retVal(static_cast<Investment*>(0),  getRidOfInvestment);

 

  retVal = ... ;                   // retVal指向恰當(dāng)?shù)膶?duì)象

  return retVal;

}

當(dāng)然,如果在創(chuàng)建pInv之前就確定了其所管理的原始指針,那么,比起“將pInv初始化為空值然后對(duì)其賦值”而言,“將原始指針傳遞給pInv的構(gòu)造函數(shù)”的方法更理想些。這是為什么呢?詳情請(qǐng)參見(jiàn)條目26

tr1::shared_ptr可以自動(dòng)為每個(gè)指針預(yù)留一個(gè)刪除器,它們可以排除另一類潛在的客戶端錯(cuò)誤,即所謂的“跨DLL問(wèn)題”,這是tr1::shared_ptr的一項(xiàng)尤為顯著的優(yōu)點(diǎn)。如果一個(gè)動(dòng)態(tài)鏈接庫(kù)(DLL)中使用new創(chuàng)建了一個(gè)對(duì)象,而在另一個(gè)DLL中這個(gè)對(duì)象被delete語(yǔ)句刪除了,那么此時(shí)將會(huì)引發(fā)“跨DLL問(wèn)題”。在許多平臺(tái)上,此類跨DLL的“new/delete對(duì)”將導(dǎo)致運(yùn)行時(shí)錯(cuò)誤。tr1::shared_ptr可以防止此類問(wèn)題發(fā)生,因?yàn)槿绻麆?chuàng)建了一個(gè)tr1::shared_ptr,它的默認(rèn)刪除器將在同一個(gè)DLL中使用delete。舉例說(shuō),如果Stock繼承自Investment,同時(shí)createInvestment是這樣實(shí)現(xiàn)的:

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

{

  return std::tr1::shared_ptr<Investment>(new Stock);

}

那么返回的tr1::shared_ptr能夠在各DLL文件中自由穿梭,而不用考慮跨DLL問(wèn)題。這一指向Stocktr1::shared_ptr會(huì)始終追蹤這一事件:當(dāng)Stock的引用計(jì)數(shù)值為零時(shí),需要使用哪一個(gè)DLLdelete語(yǔ)句來(lái)刪除它。

本條目講解的主要內(nèi)容是如何讓接口更加易于正確使用,而不易被誤用,而不是tr1::shared_ptr,但是tr1::shared_ptr對(duì)于避免此類客戶端錯(cuò)誤卻是一個(gè)不可多得的好工具,學(xué)會(huì)使用它是值得的。tr1::shared_ptr最為通用的實(shí)現(xiàn)來(lái)自Boost(參見(jiàn)條目55)。Boost中的shared_ptr有兩個(gè)原始指針那么大,它在存儲(chǔ)計(jì)數(shù)信息和刪除器相關(guān)的數(shù)據(jù)時(shí)會(huì)使用動(dòng)態(tài)分配的內(nèi)存,在進(jìn)行刪除器調(diào)用時(shí)會(huì)使用虛函數(shù),對(duì)于其識(shí)別為多線程的應(yīng)用程序,在修改引用計(jì)數(shù)時(shí)會(huì)引入線程同步的開(kāi)銷。(你也可以通過(guò)定義一個(gè)預(yù)處理符號(hào)來(lái)禁用多線程。)簡(jiǎn)言之:它比原始指針的體積更大,執(zhí)行速度更慢,并且使用輔助動(dòng)態(tài)內(nèi)存。在許多應(yīng)用中,這些額外的運(yùn)行時(shí)開(kāi)銷是微不足道的,但是它可以顯著降低每個(gè)客戶出錯(cuò)的可能,這一點(diǎn)絕對(duì)是振奮人心的。

時(shí)刻牢記

優(yōu)秀的接口應(yīng)該易于正確使用,而不易誤用。你應(yīng)該力爭(zhēng)讓你所有的接口都具備這一特征。

增加易用性的方法包括:讓接口保持一致性,讓代碼與內(nèi)建數(shù)據(jù)類型保持行為上的兼容性。

防止錯(cuò)誤發(fā)生的方法包括:創(chuàng)建新的數(shù)據(jù)類型,嚴(yán)格限定類型的操作,約束對(duì)象的值,主動(dòng)管理資源以消除客戶的資源管理職責(zé)。

tr1::shared_ptr支持自定義的刪除功能。可以防止DLL問(wèn)題,可以用于自動(dòng)解開(kāi)互斥鎖(參見(jiàn)條目14)。

posted on 2007-05-18 23:30 ★ROY★ 閱讀(885) 評(píng)論(0)  編輯 收藏 引用 所屬分類: Effective C++

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            一区二区三区国产| 久久国产精品久久久| 欧美日韩亚洲综合一区| 欧美成人黄色小视频| 久久欧美中文字幕| 欧美在线观看日本一区| 亚洲综合日本| 亚洲欧美日韩国产| 亚洲女人小视频在线观看| 亚洲专区欧美专区| 亚洲欧美日产图| 欧美一区二区性| 欧美一二区视频| 久久青草久久| 欧美日韩精品系列| 国产丝袜美腿一区二区三区| 精品动漫一区| 一本一本久久| 久久久噜噜噜久久中文字免| 欧美激情国产精品| 亚洲精选成人| 亚洲小说欧美另类社区| 久久久亚洲午夜电影| 欧美精品免费在线| 国产一区二区三区高清| 91久久午夜| 欧美在线观看一二区| 美日韩精品视频| 日韩视频免费| 精品成人一区| 欧美日韩一区在线播放| 国产日产高清欧美一区二区三区| 国产欧美一区二区三区国产幕精品 | 亚洲自拍偷拍麻豆| 乱人伦精品视频在线观看| 欧美激情精品久久久久久蜜臀| 国产精品成人观看视频免费 | 欧美在线看片| 久久久久国产精品厨房| 久久一区二区三区四区| 国产精品视频免费观看www| 在线成人av| 欧美在线啊v一区| 一区二区三区你懂的| 欧美sm极限捆绑bd| 好吊色欧美一区二区三区视频| 亚洲色图在线视频| 亚洲精品女av网站| 亚洲一区二区黄色| 亚洲国产视频a| 久久精品中文字幕一区| 欧美日韩国产首页在线观看| 韩国在线视频一区| 亚洲欧美日韩在线一区| 欧美福利在线观看| 久久久久国产一区二区三区| 国产欧美日本一区视频| 亚洲午夜激情网站| 欧美chengren| 欧美一区二区网站| 国产亚洲欧美日韩精品| 在线中文字幕不卡| 欧美成人免费在线| 玖玖在线精品| 国产欧美精品日韩区二区麻豆天美| 亚洲美女少妇无套啪啪呻吟| 欧美激情一区二区三区蜜桃视频| 欧美在线免费视屏| 激情成人综合| 欧美激情视频给我| 欧美激情视频在线免费观看 欧美视频免费一| 国产精品有限公司| 久久婷婷久久| 久久亚洲不卡| 亚洲欧洲日产国产网站| 亚洲国产高清一区| 久久精品道一区二区三区| 国产精品人人爽人人做我的可爱| 亚洲一区二区三区四区五区午夜| 日韩天堂av| 国产精品尤物| 欧美v亚洲v综合ⅴ国产v| 美女网站久久| 午夜久久久久久| 久久精品91| 欧美日韩的一区二区| 一区二区三区精品视频在线观看 | 性色一区二区三区| 久久国产欧美精品| 亚洲精品一区二| 亚洲特级毛片| 一区二区视频免费完整版观看| 亚洲国产精品第一区二区三区| 欧美视频在线播放| 久久三级视频| 欧美日韩成人在线| 欧美一区午夜视频在线观看| 久久一区激情| 午夜天堂精品久久久久 | 亚洲男人的天堂在线观看| 国产亚洲午夜| 亚洲高清视频在线| 欧美日韩成人激情| 久久精品在线观看| 欧美日韩成人综合| 午夜精品福利在线观看| 玖玖玖国产精品| 欧美在线91| 欧美久久电影| 免费观看在线综合色| 国产精品电影网站| 久久综合中文字幕| 国产精品亚洲综合色区韩国| 亚洲第一免费播放区| 国产一区二区三区不卡在线观看 | 一本综合久久| 久久久久久午夜| 午夜精品视频网站| 欧美精品在线极品| 美女主播一区| 国产主播精品在线| 亚洲天堂免费在线观看视频| 亚洲精品1234| 麻豆精品网站| 久久美女性网| 欧美视频在线观看免费| 欧美成人精品在线观看| 国产亚洲激情在线| 亚洲性线免费观看视频成熟| 9l视频自拍蝌蚪9l视频成人| 久久久精品视频成人| 久久免费的精品国产v∧| 欧美日韩三区| 日韩五码在线| 亚洲视频一起| 欧美视频1区| 在线综合亚洲欧美在线视频| 亚洲一区二区在线免费观看视频| 欧美日韩免费一区二区三区视频| 亚洲精品日韩欧美| 99精品国产在热久久| 欧美精品久久天天躁| 99精品视频免费观看| 国产精品一区二区在线| 中文高清一区| 欧美午夜精品一区| 亚洲亚洲精品在线观看| 久久福利精品| 伊人天天综合| 免费成人在线观看视频| 亚洲激情专区| 亚洲桃花岛网站| 国产精品久久二区二区| 欧美亚洲一区三区| 欧美成人精品三级在线观看| 亚洲精品免费网站| 欧美色精品在线视频| 午夜视频一区二区| 农村妇女精品| 一区二区三区久久| 国产欧美一级| 狼人社综合社区| 一卡二卡3卡四卡高清精品视频| 午夜国产一区| 亚洲高清视频在线| 国产精品盗摄久久久| 久久精品国产亚洲精品| 亚洲国产另类精品专区| 亚洲欧美日韩国产中文| 国产综合在线视频| 美女亚洲精品| 午夜精品理论片| 亚洲福利视频三区| 性欧美xxxx视频在线观看| 黑人一区二区三区四区五区| 美日韩免费视频| 亚洲欧美日韩国产中文| 欧美激情偷拍| 久久精品一区二区三区不卡牛牛| 亚洲精品永久免费| 国产欧美一区二区精品忘忧草 | 国产一区91| 欧美日本国产一区| 久久精品91久久香蕉加勒比| 亚洲精品激情| 免费视频亚洲| 久久av最新网址| 一本不卡影院| 亚洲精品乱码久久久久久按摩观| 国产精品美腿一区在线看| 免费不卡在线观看| 欧美一区二区在线免费观看| 99国产精品久久久| 六月天综合网| 午夜免费在线观看精品视频| 亚洲国产日韩欧美在线动漫| 国产精品中文字幕欧美| 欧美日韩精品在线观看| 久久久久久久999| 亚洲免费视频网站|