亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

C++箴言:使接口易于正確使用難錯誤使用

2019-11-17 05:24:22
字體:
來源:轉載
供稿:網友

  C++ 被沉沒于接口中。函數接口、類接口、模板接口。每一個接口都意味著客戶的代碼和你的代碼互相影響。假設你在和通情達理的人打交道,那些客戶也想做好工作。他們想要正確使用你的接口。在這種情況下,假如他們犯了一個錯誤,就說明你的接口至少有部分是不完善的。在理想情況下,假如一個接口的一種嘗試的用法不符合客戶的預期,代碼將無法編譯,反過來,假如代碼可以編譯,那么它做的就是客戶想要的。

  開發易于正確使用,而難以錯誤使用的接口需要你考慮客戶可能造成的各種錯誤。例如,假設你正在設計一個代表時間的類的構造函數:

class Date {
public:
Date(int month, int day, int year);
...
};
  匆匆一看,這個接口似乎是合乎情理的(至少在美國),但是客戶可能很輕易地造成兩種錯誤。首先,他們可能會以錯誤的順序傳遞參數:

Date d(30, 3, 1995); // Oops! Should be "3, 30" , not "30, 3"
  第二,他們可能傳遞一個非法的代表月或日的數字:

Date d(2, 20, 1995); // Oops! Should be "3, 30" , not "2, 20"
 ?。ê竺孢@個例子看上去似乎沒什么,但是想想鍵盤上,2 就在 3 的旁邊,這種 "off by one" 類型的錯誤并不罕見。)

  很多客戶錯誤都可以通過引入新的類型來預防。確實,類型系統是你阻止那些不合適的代碼通過編譯的主要支持者。在當前情況下,我們可以引入簡單的包裝類型來區別日,月和年,并將這些類型用于 Data 的構造函數。

strUCt Day { struct Month { struct Year {
eXPlicit Day(int d) explicit Month(int m) explicit Year(int y)
:val(d) {} :val(m) {} :val(y){}

int val; int val; int val;
}; }; };


class Date {
public:
Date(const Month& m, const Day& d, const Year& y);
...
};
Date d(30, 3, 1995); // error! wrong types

Date d(Day(30), Month(3), Year(1995)); // error! wrong types

Date d(Month(3), Day(30), Year(1995)); // okay, types are correct
  將日,月和年做成封裝數據的羽翼豐滿的類比上面的簡單地使用 struct 更好,但是即使是 struct 也足夠證實明智地引入新類型在阻止接口的錯誤使用方面能工作得非常出色。

  只要放置了正確的類型,它往往能合理地限制那些類型的值。例如,月僅有 12 個合法值,所以 Month 類型應該反映這一點。做到這一點的一種方法是用一個枚舉來表現月,但是枚舉不像我們希望的那樣是類型安全(type-safe)的。例如,枚舉能被作為整數使用。一個安全的解決方案是預先確定合法的 Month 的集合:

class Month {
public:
static Month Jan() { return Month(1); } // functions returning all valid
static Month Feb() { return Month(2); } // Month values; see below for
... // why these are functions, not
static Month Dec() { return Month(12); } // objects

... // other member functions

PRivate:
explicit Month(int m); // prevent creation of new
// Month values

... // month-specific data
};
Date d(Month::Mar(), Day(30), Year(1995));
  假如用函數代替對象來表現月的主意讓你感到驚異,那可能是因為你忘了非局部靜態對象(non-local static objects)的初始化的可靠性是值得懷疑的。Item 4 能喚起你的記憶。

  防止可能的客戶錯誤的另一個方法是限制對一個類型能夠做的事情。施加限制的一個普通方法就是加上 const。例如,Item 3 解釋了使 Operator* 的返回類型具有 const 資格是如何能夠防止客戶對用戶自定義類型犯下這樣的錯誤:

if (a * b = c) ... // oops, meant to do a comparison!
  實際上,這僅僅是另一條使類型易于正確使用而難以錯誤使用的普遍方針的一種表現:除非你有很棒的理由,否則就讓你的類型的行為與內建類型保持一致??蛻粢呀浿老?int 這樣的類型如何表現,所以你應該努力使你的類型的表現無論何時都同樣合理。例如,假如 a 和 b 是 int,給 a*b 賦值是非法的。所以除非有一個非常棒理由脫離這種表現,否則,對你的類型來說這樣做也應該是非法的。

  避免和內建類型毫無理由的不相容的真正原因是為了提供行為一致的接口。很少有特性比一致性更易于引出易于使用的接口,也很少有特性比不一致性更易于引出令人郁悶的接口。STL 容器的接口在很大程度上(雖然并不完美)是一致的,而且這使得它們相當易于使用。例如,每一種 STL 容器都有一個名為 size 的成員函數可以知道容器中有多少對象。與此對比的是 java,在那里你對數組使用 length 屬性,對 String 使用 length 方法,而對 List 卻要使用 size 方法,在 .NET 中,Array 有一個名為 Length 的屬性,而 ArrayList 卻有一個名為 Count 的屬性。一些開發人員認為集成開發環境(IDEs)能補償這些瑣細的矛盾,但他們錯了。矛盾在開發者工作中強加的精神折磨是任何 IDE 都無法完全消除的。

  任何一個要求客戶記住某些事情的接口都是有錯誤使用傾向的,因為客戶可能忘記做那些事情。例如,Item 13 介紹了一個 factory 函數,它返回一個指向動態分配的 Investment 繼續體系中的對象的指針。


Investment* createInvestment(); // from Item 13; parameters omitted
// for simplicity
  為了避免資源泄漏,createInvestment 返回的指針最后必須被刪除,但這就為至少兩種類型的客戶錯誤創造了機會:刪除指針失敗,或刪除同一個指針一次以上。

  我在前面展示了客戶可以怎樣將 createInvestment 的返回值存入一個類似 auto_ptr 或 tr1::shared_ptr 智能指針,從而將使用 delete 的職責交給智能指針。但是假如客戶忘記使用智能指針呢?在很多情況下,一個更好的接口會預先判定將要出現的問題,從而讓 factory 函數在第一現場即返回一個智能指針:

std::tr1::shared_ptr<Investment> createInvestment();
  這就從根本上強制客戶將返回值存入一個 tr1::shared_ptr,幾乎完全消除了當底層的 Investment 對象不再使用的時候忘記刪除的可能性。

  實際上,返回一個 tr1::shared_ptr 使得接口的設計者預防許多其它客戶的與資源泄漏相關的錯誤成為可能,因為,就像 Item 14 解釋的:當一個智能指針被創建的時候,tr1::shared_ptr 答應將一個資源釋放(resource-release)函數——一個 "deleter" ——綁定到智能指針上。(auto_ptr 則沒有這個能力。)

  假設從 createInvestment 得到一個 Investment* 指針的客戶期望將這個指針傳給一個名為 getRidOfInvestment 的函數,而不是對它使用 delete。這樣一個接口又為一種新的客戶錯誤打開了門,這就是客戶可能使用了錯誤的資源析構機制(也就是說,用了 delete 而不是 getRidOfInvestment)。createInvestment 的實現可以通過返回一個在它的 deleter 上綁定了 getRidOfInvestment 的 tr1::shared_ptr 來預防這個問題。

  tr1::shared_ptr 提供了一個需要兩個參數(要被治理的指針和當引用計數變為零時要調用的 deleter)的構造函數。這里展示了創建一個以 getRidOfInvestment 為 deleter 的 null tr1::shared_ptr 的方法:

std::tr1::shared_ptr<Investment> // attempt to create a null
pInv(0, getRidOfInvestment); // shared_ptr with a custom deleter;
// this won’t compile
  唉,這不是合法的 C++。tr1::shared_ptr 的構造函數果斷要求它的第一個參數應該是一個指針,而 0 不是一個指針,它是一個 int。當然,它能轉型為一個指針,但那在當前情況下并不夠好用,tr1::shared_ptr 果斷要求一個真正的指針。用強制轉型解決這個問題:

std::tr1::shared_ptr<Investment> // create a null shared_ptr with
pInv(static_cast<Investment*>(0), // getRidOfInvestment as its
getRidOfInvestment); // deleter; see Item 27 for info on
// static_cast
  據此,實現返回一個以 getRidOfInvestment 作為 deleter 的 tr1::shared_ptr 的 createInvestment 的代碼看起來就像這個樣子:

std::tr1::shared_ptr<Investment> createInvestment()
{
std::tr1::shared_ptr<Investment> retVal(static_cast<Investment*>(0),
getRidOfInvestment);

retVal = ... ; // make retVal point to the
// correct object

return retVal;
}
  當然,假如將被 pInv 治理的裸指針可以在創建 pInv 時被確定,最好是將這個裸指針傳給 pInv 的構造函數,而不是將 pInv 初始化為 null 然后再賦值給它。。

  tr1::shared_ptr 的一個非凡好的特性是它自動逐指針地使用 deleter 以消除另一種潛在的客戶錯誤——“cross-DLL 問題?!边@個問題發生在這種情況下:一個對象在一個動態鏈接庫(dynamically linked library (DLL))中通過 new 被創建,在另一個不同的 DLL 中被刪除。在許多平臺上,這樣的 cross-DLL new/delete 對會引起運行時錯誤。tr1::shared_ptr 可以避免這個問題,因為它的缺省的 deleter 只將 delete 用于這個 tr1::shared_ptr 被創建的 DLL 中。這就意味著,例如,假如 Stock 是一個繼續自 Investment 的類,而且 createInvestment 被實現如下,

std::tr1::shared_ptr<Investment> createInvestment()
{
return std::tr1::shared_ptr<Investment>(new Stock);
}
  返回的 tr1::shared_ptr 能在 DLL 之間進行傳遞,而不必關心 cross-DLL 問題。指向這個 Stock 的 tr1::shared_ptr 將保持對“當這個 Stock 的引用計數變為零的時候,哪一個 DLL 的 delete 應該被使用”的跟蹤。

  本文不是關于 tr1::shared_ptr 的——而是關于使接口易于正確使用,而難以錯誤使用的——但 tr1::shared_ptr 正是這樣一個消除某些客戶錯誤的簡單方法,值得用一個概述來看看使用它的代價。最通用的 tr1::shared_ptr 實現來自于 Boost(參見 Item 55)。Boost 的 shared_ptr 的大小是裸指針的兩倍,將動態分配內存用于簿記和 deleter 專用(deleter-specific)數據,當調用它的 deleter 時使用一個虛函數來調用,在一個它認為是多線程的應用程序中,當引用計數被改變,會導致線程同步開銷。(你可以通過定義一個預處理符號來使多線程支持失效。)在缺點方面,它比一個裸指針大,比一個裸指針慢,而且要使用輔助的動態內存。在許多應用程序中,這些附加的運行時開銷并不顯著,而對客戶錯誤的減少卻是每一個人都看得見的。

  Things to Remember

  ·好的接口易于正確使用,而難以錯誤使用。你應該在你的所有接口中為這個特性努力。

  ·使易于正確使用的方法包括在接口和行為兼容性上與內建類型保持一致。

  ·預防錯誤的方法包括創建新的類型,限定類型的操作,約束對象的值,以及消除客戶的資源治理職責。

  ·tr1::shared_ptr 支持自定義 deleter。這可以防止 cross-DLL 問題,能用于自動解鎖互斥體等。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
日韩电影在线观看中文字幕| www亚洲欧美| 国产欧美在线视频| 国产精品h在线观看| 成人福利网站在线观看11| 国产亚洲精品激情久久| 精品国偷自产在线视频99| 日韩在线中文字幕| 精品动漫一区二区| 欧美视频精品一区| 亚洲成人动漫在线播放| 91免费人成网站在线观看18| 国产成人久久久精品一区| 九九久久精品一区| 欧洲成人在线观看| 亚洲欧美日韩国产精品| 日韩福利伦理影院免费| 欧美电影在线观看完整版| 国产精品久久久久久av福利软件| 国产成人精品a视频一区www| 国产成人精品亚洲精品| 久久精品电影一区二区| 亚洲精品视频在线观看视频| 美女av一区二区三区| 亚洲一区二区久久久久久久| 国产精品美女久久久免费| 亚洲男女自偷自拍图片另类| 国产精品久久久av久久久| 这里只有精品视频| 久热爱精品视频线路一| 国产91精品黑色丝袜高跟鞋| 亚洲国产精品久久91精品| 永久免费毛片在线播放不卡| 国产婷婷97碰碰久久人人蜜臀| 午夜精品在线视频| 久久久久久有精品国产| 精品久久香蕉国产线看观看gif| 青青草99啪国产免费| 亚洲开心激情网| 国产精品白丝av嫩草影院| 91久久久久久久| 日韩美女中文字幕| 69久久夜色精品国产69| 庆余年2免费日韩剧观看大牛| 国产亚洲精品成人av久久ww| 91精品国产高清久久久久久久久| 日韩在线视频导航| 97人人模人人爽人人喊中文字| 亚洲国产小视频| 国产视频999| 久久久国产91| 中文字幕亚洲欧美在线| 91亚洲国产精品| 国产精品白丝av嫩草影院| 国产精品麻豆va在线播放| 萌白酱国产一区二区| 久久99精品久久久久久琪琪| 国产成人一区二区在线| 亚洲无亚洲人成网站77777| 国产亚洲欧洲高清| 91精品中国老女人| 亚洲在线视频观看| 91国产一区在线| 一本色道久久88综合日韩精品| 欧美另类高清videos| 国产精品一区二区三区久久久| www.亚洲免费视频| 亚洲乱码国产乱码精品精| 97视频在线观看播放| 国产视频丨精品|在线观看| 欧美亚洲第一区| 在线播放国产一区中文字幕剧情欧美| 国产精品日韩电影| 国产91久久婷婷一区二区| 欧美日韩国产二区| 伊人成人开心激情综合网| 久久精品国产69国产精品亚洲| 久久久欧美精品| 国产精品777| 日韩精品中文字幕视频在线| 久久久999国产| 亚洲区bt下载| 91免费看国产| 欧美丰满片xxx777| 亚洲伊人成综合成人网| 亚洲片在线资源| 亚洲成人激情在线观看| 欧美日韩一区二区免费视频| 91精品国产高清| 国产精品白丝jk喷水视频一区| 欧美华人在线视频| 亚洲国产成人91精品| 国产成人91久久精品| 成年人精品视频| 日本sm极度另类视频| 亚洲成色777777在线观看影院| 亚洲在线第一页| 国产成人午夜视频网址| 欲色天天网综合久久| 国产成人精品久久| 国产精品久久久久久婷婷天堂| 伊人伊成久久人综合网小说| 91精品啪aⅴ在线观看国产| 亚洲自拍偷拍在线| 国产精品国产三级国产专播精品人| 97国产一区二区精品久久呦| 日本精品免费观看| 成人福利在线观看| 91国产精品91| 亚洲片在线资源| 中文字幕亚洲一区| 91精品国产综合久久香蕉最新版| 亚洲精品99久久久久| 97视频在线观看免费| 国产精品成人v| 亚洲图片欧洲图片av| 国产日韩精品一区二区| 亚洲精品一区二三区不卡| 国产精品久久久久久久美男| 久久精品国产清自在天天线| 亚洲欧美国产另类| 国产精品自在线| 九九视频直播综合网| 美女视频久久黄| 日韩精品在线播放| 久久久91精品国产| 中文字幕欧美亚洲| 欧美性猛交99久久久久99按摩| 久久久久久久久久久久久久久久久久av| 久久6免费高清热精品| 久久激情视频免费观看| 亚洲丁香婷深爱综合| 日韩欧美在线视频观看| 大胆人体色综合| 日韩福利视频在线观看| 上原亚衣av一区二区三区| 国产精品扒开腿爽爽爽视频| 欧美高清视频一区二区| zzijzzij亚洲日本成熟少妇| 精品视频在线播放| 国产视频欧美视频| 4388成人网| 国产精品96久久久久久又黄又硬| 欧美成人免费在线观看| 欧美性理论片在线观看片免费| 韩剧1988在线观看免费完整版| 亚洲性无码av在线| 日韩av中文在线| 亚洲国产日韩欧美在线99| 92国产精品久久久久首页| 91亚洲国产成人久久精品网站| 久久免费在线观看| 国产a级全部精品| 国产精品免费看久久久香蕉| 久久久久久久激情视频| 亚洲精品免费网站| 欧美日韩成人在线视频| 成人啪啪免费看| 欧美日韩在线视频首页| 成人精品久久一区二区三区| www.日韩不卡电影av| 欧美一区二区三区四区在线| 亚洲护士老师的毛茸茸最新章节| 日本一区二三区好的精华液|