日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

读书笔记 effective c++ Item 18 使接口容易被正确使用,不容易被误用

發布時間:2025/3/20 c/c++ 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 读书笔记 effective c++ Item 18 使接口容易被正确使用,不容易被误用 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

1. 什么樣的接口才是好的接口

C++中充斥著接口:函數接口,類接口,模板接口。每個接口都是客戶同你的代碼進行交互的一種方法。假設你正在面對的是一些“講道理”的人員,這些客戶嘗試把工作做好,他們希望能夠正確使用你的接口。在這種情況下,如果接口被誤用,你的接口應該至少負一部分的責任。理想情況下,如果使用一個接口沒有做到客戶希望做到的,代碼應該不能通過編譯;如果代碼通過了編譯,那么它就能做到客戶想要的

2. 編寫好的接口的方法列舉

2.1 使接口不容易被誤用——通過引入新的類型

開發出容易被正確使用不容易被誤用的接口需要你考慮客戶可能出現的所有類型的錯誤。舉個例子,假設你正在為一個表示日期的類設計一個構造函數:

1 class Date { 2 3 public: 4 5 Date(int month, int day, int year); 6 7 ... 8 9 };

乍一看,這個接口可能看上去去合理的,但是客戶很容易犯至少兩種錯誤。

第一,他們可能搞錯參數的傳遞順序

1 Date d(30, 3, 1995); // Oops! Should be “3, 30” , not “30, 3”

?第二,他們可能傳遞一個無效的月份或者天數(day number):

1 Date d(3, 40, 1995); // Oops! Should be “3, 30” , not “3, 40”

(最后一個例子看上去很病態,但是不要忘了在鍵盤上,數字4和3是挨著的,將3錯打成4這樣的錯誤不是不常見。)

通過引入新的類型,許多客戶錯誤就能被避免。確實,類型系統(type system)是你阻止不合要求的代碼編譯通過的主要盟友。在這種情況下,我們可以引入簡單的包裝類型來區分天,月和年,然后在Date構造函數中使用這些類型:

1 struct Day{ 2 explicit Day(int d): val(d) {} 3 int val; 4 }; 5 struct Month { 6 explicit Month(int m): val(m) {} 7 int val; 8 }; 9 struct Year { 10 explicit Year(int y): val(y){} 11 int val; 12 }; 13 class Date { 14 public: 15 Date(const Month& m, const Day& d, const Year& y); 16 ... 17 }; 18 Date d(30, 3, 1995); // error! wrong types 19 Date d(Day(30), Month(3), Year(1995)); // error! wrong types 20 Date d(Month(3), Day(30), Year(1995)); // okay, types are correct

將Day,Month和Year數據封裝在羽翼豐滿的類中比上面簡單的使用struct要更好(Item 22),但是使用struct就足以證明,明智的引入新類型可以很好的阻止接口被誤用的問題。

一旦正確的類型準備好了,就能夠合理的約束這些類型的值。舉個例子,只有12個月份應該能夠通過Month類型反映出來。一種方法是使用一個枚舉類型來表示月份,但是枚舉不是我們喜歡的類型安全的類型。例如,枚舉可以像int一樣使用(Item 2)。一個更加安全的解決方案是預先將所有有效的月份都定義出來。

1 class Month { 2 3 public: 4 5 static Month Jan() { return Month(1); } // functions returning all valid 6 7 static Month Feb() { return Month(2); } // Month values; see below for 8 9 ... // why these are functions, not 10 11 static Month Dec() { return Month(12); } // objects 12 13 ... // other member functions 14 15 private: 16 17 explicit Month(int m); // prevent creation of new 18 19 // Month values 20 21 ... // month-specific data 22 23 }; 24 25 Date d(Month::Mar(), Day(30), Year(1995));

?

如果使用函數代替對象來表示指定月份值會讓你覺的奇怪的話,可能是因為你忘記了非本地static對象的初始化是有問題的(見 Item 4)。

2.2 使接口不容易被誤用——對類型的操作進行限定

另外一種防止類似錯誤的方法是對類型能夠做什么進行限制。進行限制的一般方法是添加const。舉個例子,Item 3解釋了對于用戶自定義的類型,把operator*的返回類型加上const能夠防止下面錯誤的發生:

1 if (a * b = c) ... // oops, meant to do a comparison!

?

2.3 使接口容易被正確使用——提供行為一致的接口

事實上,這只是“使類型容易正確使用不容易被誤用”的另外一個指導方針的表現形式:除非有更好的理由,讓你的自定義類型同內建類型的行為表現一致。客戶已經知道像int一樣的內建類型的行為是什么樣子的,所以在任何合理的時候你應該努力使你的類型表現與其一致。舉個例子,如果a和b是int類型,那么賦值給a*b是不合法的,所以除非有一個好的理由偏離這種行為,你應該使你的類型同樣不合法。每當你不確定自定義類型的行為時,按照int來做就可以了。

防止自定義類型同內建類型無端不兼容的真正原因是提供行為一致的接口。沒有特征比“一致性”更能使接口容易被使用了,也沒有特征比“不一致性”更加導致接口容易被誤用了。STL容器的接口大體上(雖然不是完全一致)是一致的,這使得它們使用起來相當容易。舉個例子,每個STL容易有一個size成員函數,用來指出容器中的對象數量。與Java相比,arrays使用length屬性(property)來表示對象數量,而String使用length方法(method)來表示,List使用size方法來表示;對于.NET來說,Array有一個Length屬性,而ArrayList有一個Count屬性。一些開發人員認為集成開發環境(IDE)使這種不一致性不再重要,但他們錯了。不一致性會將精神摩擦強加到開發人員的工作中,沒有任何IDE能夠將其擦除。

2.4 使接口不容易被誤用——使用shared_ptr消除客戶管理資源的責任

2.4.1 讓函數返回一個智能指針

一個要讓客戶記住做某事的接口比較容易被用錯,因為客戶有可能會忘記做。舉個例子,Item 13中引入一個工廠函數,在一個Investment繼承體系中返回指向動態分配內存的指針:

1 Investment* createInvestment(); // from Item 13; parameters omitted 2 3 // for simplicity

為了防止資源泄漏,createInvesment返回的指針最后必須被delete,但是這為至少兩類客戶錯誤的出現創造了機會:delete指針失敗,多次delete同一個指針。

Item 13展示了客戶如何將createInvestment的返回值存入像auto_ptr或者tr1::shared_ptr一樣的智能指針中,這樣就將delete的責任交給智能指針。但是如果客戶忘記使用智能指針該怎么辦?在許多情況下,更好的接口是要先發制人,讓函數首先返回一個智能指針

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

這就強制客戶將返回值保存在tr1::shared_ptr中,從而完全消除了忘記delete不再被使用的底層Investment對象的可能性。

2.4.2 返回綁定刪除器的智能指針

事實上,對于一個接口設計者來說,返回tr1::shared_ptr能夠避免許多其他的有關資源釋放的客戶錯誤,因為Item 14中解釋道,在創建智能指針時,tr1::shared_ptr允許將一個資源釋放函數——釋放器(deleter)——綁定到智能指針上。

?

假設客戶從createInvestment得到一個Investment*指針,我們通過將這個指針傳遞給一個叫做getRidOfInvestment的函數來釋放資源而不是直接使用delete。這樣的接口開啟了另外一類客戶錯誤的大門:客戶可能會使用錯誤的資源析構機制(用delete而不是用提供的getRidOfInvestment接口)。createInvestment的實現者可以先發制人,返回一個tr1::shared_ptr,并將getRidOfInvestment綁定為刪除器

Tr1::shared_ptr提供了一個有兩個參數的構造函數:需要被管理的指針和當引用計數為0時需要被調用的刪除器。這就提供了一個創建用getRidOfInvestment作為刪除器的空tr1::shared_ptr的方法:

1 std::tr1::shared_ptr<Investment> // attempt to create a null 2 3 pInv(0, getRidOfInvestment); // shared_ptr with a custom deleter; 4 5 // this won’t compile

上面不是有效的c++,tr1::shared_ptr構造函數的第一個參數必須為指針,但是0不是指針。雖然它可以轉換成指針,但是在這個例子中不夠好;tr1::shared_ptr堅持使用真實的指針。一個cast就能解決問題:

1 std::tr1::shared_ptr<Investment> // create a null shared_ptr with 2 3 pInv( static_cast<Investment*>(0), // getRidOfInvestment as its 4 5 getRidOfInvestment); // deleter; see Item 27 for info on 6 7 // static_cast

這意味著實現一個createInvestment的代碼如下(返回值為綁定了getRidOfInvestment作為刪除器的tr1::shared_ptr):

1 std::tr1::shared_ptr<Investment> createInvestment() 2 3 { 4 5 std::tr1::shared_ptr<Investment> retVal(static_cast<Investment*>(0), 6 7 getRidOfInvestment); 8 9 ... // make retVal point to the 10 11 // correct object 12 13 return retVal; 14 15 }

?

當然,如果在創建一個retVal之前就能夠決定一個原生指針是不是由reVal來管理,將原生指針直接傳遞給retVal的構造函數比先將retVal初始化為null然后做一個賦值操作要好。為什么請看 Item 26。

2.5 使用智能指針消除交叉-DLL錯誤

Tr1::shared_ptr的一個特別好的性質是它可以用它的刪除器來消除另外一個客戶錯誤——交叉(cross)-DLL錯誤。當一個對象在一個DLL中使用new被創建,但是在另外一個DLL中被delete時這個問題就會出現。在許多平臺中,這樣的交叉-DLL new/delete對會導致運行時錯誤。使用tr1::shared_ptr可以避免這種錯誤,因為它使用的默認的刪除器來自創建tr1::shared_ptr的DLL。這就意味著,例如,如果Stock是一個繼承自Investment的類,createInvestment實現如下:

1 std::tr1::shared_ptr<Investment> createInvestment() 2 3 { 4 5 return std::tr1::shared_ptr<Investment>(new Stock); 6 7 }

?

返回的tr1::shared_ptr可以在DLL之間被傳遞而不用考慮cross-DLL問題。在Stock的引用計數為0的時候,指向Stock的tr1::shared_ptr指針會追蹤哪個DLL的刪除器被用來釋放資源。

3.使用智能指針的代價

這個Item不是關于tr1::shared_ptr的——它是關于“使接口容易被正確使用不容易被誤用”這個議題的——但是使用tr1::shared_ptr是一個如此容易的消除客戶錯誤的方法,所以值得將使用它的代價做一個概述。Tr1::shared_ptr的最一般的實現來自Boost(Item 55)。Boost中的shared_ptr占用內存是原生指針的兩倍,為bookkeeping(引用計數)和deleter-specific(專屬刪除器) 數據分配動態內存,調用刪除器的時候使用虛函數,當在一個應用中修改引用計數時,如果它認為自己是多線程的,會引發線程同步開銷。(你可以通過定義一個預處理符號來disable多線程支持)一句話,它比原生指針占用內存多,比原生指針慢,并且使用了輔助的動態內存。但是在許多應用中,這些額外的運行時開銷是不明顯的,但是客戶錯誤的消除對每個人來說都是顯而易見的。

?

4.總結

  • 好的接口容易被正確使用不容易被誤用,你應該使所有的接口滿足這兩個特征。
  • 接口被正確使用的方法包括接口的一致性和同內建類型的行為兼容。
  • 接口不容易被誤用的方法包括,創建新的類型,對類型上的操作進行限制,約束對象值,去除客戶管理資源的責任。
  • Tr1::shared_ptr支持個性化刪除器。這避免了交叉-DLL問題,可以被用來自動unlock互斥器(Item 14)等等。

轉載于:https://www.cnblogs.com/harlanc/p/6431766.html

總結

以上是生活随笔為你收集整理的读书笔记 effective c++ Item 18 使接口容易被正确使用,不容易被误用的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 欧美性猛交xxxx免费看 | 欧美淫 | 中文字幕人妻丝袜乱一区三区 | 亚洲精品97久久中文字幕无码 | 亚洲精品久久久久国产 | 91精品国产色综合久久不卡98口 | 欧美日韩激情 | 午夜视频免费在线 | 国产午夜福利一区二区 | 992tv在线影院 | 91国产网站 | 日本黄网在线观看 | 欧美gv在线观看 | 女生高潮视频在线观看 | 国产三级国产精品 | 久久久精品视频在线 | a国产精品| 精品国产视频 | 成人精品999 | 国产日韩欧美视频在线观看 | 伊人久久在线 | 污视频在线免费 | 天天狠天天操 | 国产粉嫩在线 | www.国产免费 | 日韩视频一区二区三区 | 少妇情理伦片丰满午夜在线观看 | 黄色片毛片 | 国产麻豆精品在线 | 欧美亚洲激情视频 | 国产成人无码a区在线观看视频 | 国内视频一区二区三区 | 黄色三级免费网站 | 国产日韩精品在线 | 午夜激情福利电影 | av夜色| 在线视频在线观看 | 亚洲一区电影 | 黄网站在线免费 | 婷婷久久伊人 | 午夜天堂精品 | 亚洲一区有码 | 日本黄色www | 免费成人视屏 | 日韩免费高清一区二区 | 黄瓜视频在线免费观看 | 香蕉国产999 | 国产一区 在线播放 | 欧美用舌头去添高潮 | 激情四射网 | 69堂在线观看 | 国产一区二区在线播放 | 老汉av在线| 操操色 | 中国男人操女人 | 日韩一区二区毛片 | 天天干天天搞天天射 | 日日射夜夜操 | 一本色道久久综合亚洲二区三区 | 国产情侣一区 | 黑森林av凹凸导航 | 亚洲男女在线 | 神马久久影院 | 色性网站| 国产精品人八做人人女人a级刘 | 亚洲精品视频91 | 国产成人免费视频网站 | 欧美一级视频免费观看 | 精品国产黄 | 日本久色 | 久久九九热视频 | av资源在线播放 | 成人亚洲精品久久久久软件 | 四虎影成人精品a片 | 天天爽天天爽夜夜爽毛片 | 黄色草逼视频 | 男人天堂视频在线 | 黄色性情网站 | 涩涩一区| 精品无码久久久久久久久果冻 | 琪琪色影音先锋 | 激情视频网 | 人妖av在线 | 一炮成瘾1v1高h | 久久综合狠狠综合久久综合88 | 激情 小说 亚洲 图片 伦 | 无码人妻丰满熟妇区bbbbxxxx | 中文字幕丝袜诱惑 | 国产在线v| 国产精品5| 天堂中文在线免费观看 | 夜夜操国产 | 日本精品一区二区在线观看 | 欧美91精品久久久久国产性生爱 | 天堂999 | 亚洲午夜不卡 | 女的被男的操 | 色婷婷精品久久二区二区密 | 色资源在线观看 |