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

洛譯小筑

別來無恙,我的老友…
隨筆 - 45, 文章 - 0, 評論 - 172, 引用 - 0
數據加載中……

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

C++中到處充滿了接口。函數接口、類接口、模板接口,等等。每個接口都是實現客戶與你的代碼相交互的一種手段。假設你的客戶都是完全理性的,他們致力于更優秀的完成當前項目,他們便會十分看重你的接口是否能夠正確使用。這樣一來,如果你的接口中的任意一個被他們誤用了,那么這個接口便成了這一錯誤的“罪魁禍首”。在理想狀態下,如果客戶嘗試使用一個接口,但是沒有達到預期的效果,那么代碼則不應通過編譯。反之,如果代碼通過了編譯,則運行結果必須要符合客戶的需求。

開發中我們應做到讓接口更易于正確使用而不易被誤用,這需要你考慮到客戶會犯的各種錯誤。請參見下邊的示例,假設你正在設計一個表示日期時間的類的構造函數:

class Date {

public:

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

  ...

};

乍一看,這一接口設計得很合理(至少在美國很合理),但是當客戶面對這樣的接口時,很容易犯下兩種錯誤。第一,他們可能會使用錯誤的傳參順序:

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

第二,他們可能會傳進一個無效的月份或日期:

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

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

客戶犯下的許多錯誤是可以通過引入新類型來避免的。實際上,對于防止不合要求的代碼通過編譯,類型系統是你最得力的助手。在上述情況下,我們可以引入幾個簡單的“包裝類型”來區分日期、月份、和年份,然后再在Date的構造函數中使用這些類型:

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);                    // 報錯!類型錯誤

Date d(Day(30), Month(3), Year(1995)); // 報錯!類型錯誤

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

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

只要你在恰當的地方使用了恰當的類型,你便可以合理地限制這些類型的值。比如說,一年有12個月,所以Month類型應該能夠反映出這一點。一個途徑是使用枚舉類型來表示月份,但是枚舉類型并不總能達到我們對于類型安全的需求。比如說,枚舉類型可以像int一樣使用(參見條目2)。一個更安全的解決方法是:預先定義好所有有效Month值的集合:

class Month {

public:

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

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

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

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

 

  ...                                     // 其他成員函數

 

private:

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

  ...                                     // 與月份相關的數據

};

 

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

如果上面代碼中使用函數來代替具體月份的思路讓你感到奇怪,那么可能是由于你已經忘記了聲明非局部靜態對象可能會帶來可靠性問題。條目4可以喚醒你的記憶。

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

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

實際上,這僅僅是針對“讓接口易于正確使用,而不易被誤用”另一條一般性建議的一個表現形式,這條建議是:除非有更好的理由阻止你這樣做,否則你應該保證你所創建類型的行為與內建數據類型保持一致。因為客戶已經清楚int的行為,所以只要是合情合理,你就應該力求使你的類擁有與int一致的行為。比如說,如果abint類型,那么為a*b賦值就是不合法的。所以除非你有好的理由拒絕這一規定,否則你自己創建的類型也應該將這一行為界定為不合法。當你舉棋不定時,就讓你的類型的行為與int保持一致。

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

任何接口都需要客戶記憶一些易發生錯誤的內容,這是因為客戶可能會把這些東西搞砸。比如說,條目13中曾引入一個工廠函數來返回一個指向Investment層中動態分配對象的指針:

Investment* createInvestment();   // 來自條目13,省略參數表以簡化代碼

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

條目13中介紹了客戶如何將createInvestment的返回值保存在諸如auto_ptrtr1::shared_ptr這樣的智能指針中,然后讓智能指針擔負起調用delete的責任。但是如果客戶忘記了使用智能指針,這該怎么辦呢?通常情況下,更好的接口的設計方案是:讓工廠函數返回一個智能指針,在一開始就不給問題任何發生的機會。

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

這樣便可以從根本上強制客戶使用tr1::shared_ptr來存儲返回值,這一做法基本上可以排除“忘記刪除當前不再有用的Investment對象”的可能。

事實上,返回tr1::shared_ptr讓接口設計人員能夠防止與資源釋放相關的客戶端錯誤,這是因為在創建tr1::shared_ptr智能指針時,允許存在一個與當前智能指針相綁定的資源釋放函數(即一個“刪除器”),而auto_ptr沒有這一功能。(參見條目14

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

tr1::shared_ptr提供了一個擁有兩個參數的構造函數,這兩個參數即:需要管理的指針,以及當引用計數值為零時需要調用的刪除器。這使得我們可以創建使用getRidOfInvestment作為“刪除器”的空tr1::shared_ptr,請看下面的做法:

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

                                   // 嘗試創建一個nullshared_ptr

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

                                   // 這樣的代碼無法通過編譯

然而,這并不是合法的C++語法。tr1::shared_ptr的構造函數的第一個參數必須是一個指針,而0則不是,它是一個int值。的確,數字可以當做指針使用,但是這種情況下該做法并不值得推薦,tr1::shared_ptr的第一個參數必須是一個實際的指針。通過一次轉型可以解決這一問題:

std::tr1::shared_ptr<Investment>

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

                                   // 創建一個nullshared_ptr

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

                                   // static_cast的更多信息參見條目27

上面的代碼意味著,在實現createInvestment時,可讓其返回一個“綁定了getRidOfInvestment刪除器的tr1::shared_ptr”:

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

{

  std::tr1::shared_ptr<Investment>

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

 

  retVal = ... ;                   // retVal指向恰當的對象

  return retVal;

}

當然,如果在創建pInv之前就確定了其所管理的原始指針,那么,比起“將pInv初始化為空值然后對其賦值”而言,“將原始指針傳遞給pInv的構造函數”的方法更理想些。這是為什么呢?詳情請參見條目26

tr1::shared_ptr可以自動為每個指針預留一個刪除器,它們可以排除另一類潛在的客戶端錯誤,即所謂的“跨DLL問題”,這是tr1::shared_ptr的一項尤為顯著的優點。如果一個動態鏈接庫(DLL)中使用new創建了一個對象,而在另一個DLL中這個對象被delete語句刪除了,那么此時將會引發“跨DLL問題”。在許多平臺上,此類跨DLL的“new/delete對”將導致運行時錯誤。tr1::shared_ptr可以防止此類問題發生,因為如果創建了一個tr1::shared_ptr,它的默認刪除器將在同一個DLL中使用delete。舉例說,如果Stock繼承自Investment,同時createInvestment是這樣實現的:

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

{

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

}

那么返回的tr1::shared_ptr能夠在各DLL文件中自由穿梭,而不用考慮跨DLL問題。這一指向Stocktr1::shared_ptr會始終追蹤這一事件:當Stock的引用計數值為零時,需要使用哪一個DLLdelete語句來刪除它。

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

時刻牢記

優秀的接口應該易于正確使用,而不易誤用。你應該力爭讓你所有的接口都具備這一特征。

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

防止錯誤發生的方法包括:創建新的數據類型,嚴格限定類型的操作,約束對象的值,主動管理資源以消除客戶的資源管理職責。

tr1::shared_ptr支持自定義的刪除功能。可以防止DLL問題,可以用于自動解開互斥鎖(參見條目14)。

posted on 2007-05-18 23:30 ★ROY★ 閱讀(879) 評論(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>
            欧美激情按摩在线| 亚洲欧洲一区二区天堂久久 | 亚洲一区欧美激情| 一区二区三区高清在线观看| 亚洲深夜影院| 欧美亚洲综合在线| 久久久久久久网站| 久久夜色精品| 欧美金8天国| 欧美日韩在线免费| 国产精品热久久久久夜色精品三区 | 亚洲精品久久7777| 日韩视频在线一区二区| 亚洲一区欧美激情| 久久综合九色综合久99| 欧美美女bb生活片| 国产精品毛片一区二区三区| 国产亚洲精品自拍| 亚洲日韩视频| 欧美一区在线直播| 亚洲第一页在线| 亚洲中无吗在线| 久久乐国产精品| 欧美激情第三页| 国产精品毛片在线看| 红桃视频成人| 亚洲视频视频在线| 久久精品综合网| 亚洲人精品午夜在线观看| 亚洲一区二区伦理| 久热精品视频在线观看| 欧美午夜不卡| 日韩视频不卡中文| 老司机aⅴ在线精品导航| 这里只有精品视频在线| 麻豆国产精品va在线观看不卡 | 国产麻豆精品视频| 99精品视频网| 欧美91视频| 久久精品国产一区二区三区免费看| 欧美日韩国产综合视频在线| 亚洲国产mv| 久热国产精品| 香蕉久久一区二区不卡无毒影院| 欧美日韩国产一区二区| 亚洲精品国产无天堂网2021| 久久精品在线播放| 欧美影院午夜播放| 国产欧美日韩视频一区二区| 亚洲一区二区视频在线| 亚洲欧洲一区二区三区在线观看| 久久免费黄色| 1024成人| 欧美激情欧美狂野欧美精品| 久久精彩免费视频| 一区在线观看视频| 久久蜜桃资源一区二区老牛 | 99热在线精品观看| 欧美激情网友自拍| 欧美凹凸一区二区三区视频| 亚洲国产女人aaa毛片在线| 美女图片一区二区| 久久综合九色99| 亚洲精品日韩久久| 亚洲精品日韩久久| 国产精品v日韩精品| 午夜精品久久久久久久久久久久| 亚洲自拍电影| 国产日韩欧美自拍| 老鸭窝亚洲一区二区三区| 久久久久久久久久码影片| 在线免费不卡视频| 亚洲经典三级| 国产精品日韩久久久| 久久久久久伊人| 男同欧美伦乱| 亚洲一区二区三区高清| 亚洲影视综合| 精品不卡视频| 91久久极品少妇xxxxⅹ软件| 欧美新色视频| 久久综合九色欧美综合狠狠| 欧美aⅴ一区二区三区视频| 中文欧美在线视频| 欧美一区视频| 99视频有精品| 亚洲欧美日韩人成在线播放| 亚洲第一在线| 一区二区三区四区五区在线| 国产一级一区二区| 亚洲人成欧美中文字幕| 国产午夜一区二区三区| 亚洲国产成人午夜在线一区| 欧美日韩综合一区| 久久综合综合久久综合| 欧美性开放视频| 嫩草伊人久久精品少妇av杨幂| 欧美激情成人在线| 久久久久久久久久久一区 | 亚洲欧美国产精品专区久久| 久久精品女人| 亚洲男人的天堂在线| 鲁鲁狠狠狠7777一区二区| 亚洲一区欧美| 免费观看国产成人| 欧美在线视频免费观看| 欧美精品免费视频| 美女诱惑黄网站一区| 国产精品入口66mio| 亚洲经典三级| 亚洲国产日韩在线| 欧美一级一区| 亚洲一区二区三区久久| 欧美顶级艳妇交换群宴| 久热精品在线视频| 国产麻豆成人精品| 亚洲一区免费在线观看| 亚洲一区不卡| 欧美精品一区三区在线观看| 欧美激情在线狂野欧美精品| 红桃视频成人| 久久久精品国产免费观看同学| 欧美在线二区| 国产欧美精品在线播放| 亚洲视频综合在线| 亚洲欧美一区二区视频| 国产精品99免费看 | 亚洲毛片在线免费观看| 久久久www免费人成黑人精品 | 久久网站免费| 韩国精品主播一区二区在线观看| 亚洲一区二区精品视频| 午夜免费久久久久| 国产欧美日韩另类一区| 99国产精品99久久久久久粉嫩| 母乳一区在线观看| 亚洲国产欧美一区二区三区同亚洲 | 在线电影院国产精品| 亚洲免费在线视频| 亚洲欧美日韩在线播放| 国产精品久久久久久久久借妻| 夜夜嗨av一区二区三区免费区| 亚洲天堂第二页| 国产精品国产馆在线真实露脸 | 欧美中文字幕| 久久久久这里只有精品| 激情欧美一区二区三区| 久久久久国产精品一区| 免费亚洲一区二区| 亚洲伦理一区| 国产精品老牛| 久久精品av麻豆的观看方式 | 亚洲电影专区| 一二三四社区欧美黄| 欧美午夜三级| 久久精品99无色码中文字幕| 亚洲第一主播视频| 宅男在线国产精品| 国产欧美一区二区精品婷婷| 久久久五月天| 亚洲乱码一区二区| 欧美在线视频全部完| 亚洲国产欧美日韩另类综合| 欧美另类videos死尸| 亚洲一区二区三区色| 麻豆国产精品va在线观看不卡| 亚洲毛片网站| 国内成人在线| 欧美日韩亚洲高清| 久久不射2019中文字幕| 亚洲国产婷婷综合在线精品 | 欧美精品1区| 香蕉久久国产| 亚洲区在线播放| 久久精品免费电影| 亚洲精品影视| 国产午夜精品全部视频在线播放| 欧美国产极速在线| 西西裸体人体做爰大胆久久久| 亚洲国产精品热久久| 欧美在线观看一区二区三区| 亚洲国产一区二区a毛片| 国产精品女主播在线观看| 久久综合国产精品| 亚洲欧美一区二区原创| 亚洲精品一区二区网址 | 久久成人免费日本黄色| 亚洲精品一区久久久久久| 久久综合给合久久狠狠色 | 亚洲另类一区二区| 欧美成人a视频| 久久全球大尺度高清视频| 亚洲丝袜av一区| 亚洲精品一二区| 亚洲成色999久久网站| 国产欧美一区二区精品仙草咪| 欧美日韩午夜| 欧美日韩一区高清| 欧美片第一页| 欧美久久久久|