<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    PS,1880后程序員

    看不完的牙,寫不完的程序,跑不完的步。
    隨筆 - 97, 文章 - 34, 評(píng)論 - 10, 引用 - 0
    數(shù)據(jù)加載中……

    Effecitive C++讀書筆記 Chapter4 設(shè)計(jì)與聲明

     

    Item18 Make interfaces easy to use correctly and hard to use incorrectly.讓接口容易被正確使用,不易被誤用

    好的接口設(shè)計(jì)應(yīng)該是既容易被正確使用,又不易被誤用。

    就例如書中的Sample,關(guān)于時(shí)間的,我們一般的做法就是在創(chuàng)建Day對(duì)象時(shí),追加校驗(yàn)函數(shù)來判斷年月日是不是有效。

    建議的做法一是:創(chuàng)建新的類型

    定義:

    class Month {

    public:

       static Month Jan() {return Month(1); }

       static Month feb() {return Month(2); }

    private Month(int m);

    };

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

    為啥不直接使用靜態(tài)變量?

    參考Item4 P30,簡單說就是不能保證在使用non-local static objects時(shí),這個(gè)對(duì)象就已經(jīng)初始化了。如果non-local static objects在另一個(gè)文件了,又恰巧沒有初始化,系統(tǒng)當(dāng)然就會(huì)翹辮子了。

    另一種方法:加上const來限制type可以做的事情。

    先參考Item3 P19

    class Rational { };

    const Rational operator*(const Rational &lhs, const Rational &rhs);

    之所以強(qiáng)制設(shè)置為const就是為了避免client在使用時(shí)出錯(cuò)。因?yàn)槿绻麤]有clien,那么:

    Rational a,b,c;

    (a*b)=c

    這種寫法是對(duì)的,但是如果ab是內(nèi)置類型,這種寫法就是錯(cuò)誤的。

    除非有必要,否則就要保證你的類型type的行為和內(nèi)置類型一致。

    一致性導(dǎo)致接口容易被正確使用。

    STL是榜樣,java在這里成了反面教材,因?yàn)槿绻胫廊萜鲀?nèi)對(duì)象的數(shù)量,用Array,要訪問屬性lengthString要用length函數(shù),ArrayList要用size函數(shù),這就是不一致性。

    使用std::tr1::shared_ptr,消除client對(duì)資源管理的責(zé)任

    找出以下寫法的兩個(gè)易錯(cuò)的地方:

    Investment* createInvestment();

    1 忘記刪除createInvestment()返回的指針

    2 刪除這個(gè)指針多次

    so,修改定義:

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

    如果出現(xiàn)這種情形:從createInvestment得到Investment*的函數(shù)要把這個(gè)指針傳遞個(gè)給叫做getRidOfInvestment,由getridOfInvestment取代使用delete

    這里就出現(xiàn)了一個(gè)新的client易錯(cuò)的點(diǎn),用戶或許會(huì)使用錯(cuò)的資源釋放機(jī)制。因?yàn)?/span>deletegetRidOfInvestment取代了。

    std::tr1::shared_ptr< Investment >

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

    那么定義就應(yīng)該是這樣的:

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

    {

       std::tr1::shared_ptr< Investment >

    retVal(static_cast<Investment*>(0), getRidOfInvestment); //這不能讓client來做

    retVal = ;

    return retVal;

    }

    tr1::shared_ptr的優(yōu)點(diǎn)是允許在一個(gè)DLL創(chuàng)建對(duì)象,在另一個(gè)DLL里刪除對(duì)象。

    牢記

    • Good interfaces are easy to use correctly and hard to use in correctly.
    • 接口一致性,于內(nèi)置數(shù)據(jù)類型的行為兼容
    • 阻止錯(cuò)誤的方式還包括創(chuàng)建新的類型(例如Month),限制類型上的操作,束縛對(duì)象值,以及消除客戶的資源管理的責(zé)任
    • tr1::shared_ptr支持定制類型的刪除器deleter,允許在一個(gè)DLL創(chuàng)建對(duì)象,在另一個(gè)DLL里刪除對(duì)象。

    Item19Treat class design as type design.設(shè)計(jì)class猶如設(shè)計(jì)type

    在設(shè)計(jì)一個(gè)類的時(shí)候,要回答一系列的問題哦。

    參考大師在P85-P86之間給出的常常的清單吧,其實(shí)實(shí)際上,我在設(shè)計(jì)類的時(shí)候的確沒有想過這么多,問過自己這么的為什么,所以這也是我總是在追求代碼重用,卻總是發(fā)現(xiàn)自己寫的代碼重用度很低的一個(gè)原因把。

    Item20Prefer pass-by-reference-to-const to pass-by-value.寧以pass-by-reference-to-const替換pass-by-value

    But先學(xué)習(xí)一個(gè)單詞,characteristicKAO,這竟然是個(gè)名詞。

    再學(xué)一個(gè)地道的說法:解決問題的方法:The way around the slicing problem is…

    函數(shù)都是值傳遞。pass by-valuefunction parameters are initialized with copies of the actual arguments, and function callers goes back a copy of the value returned by the function.這樣當(dāng)然開銷就大了,每次都先copy一份進(jìn)來,完事以后,再copy一份出去。

    假設(shè)函數(shù)的參數(shù)是一個(gè)Student對(duì)象,bool validateStudent(Student s);調(diào)用這個(gè)函數(shù),額外的隱性開銷包括要先調(diào)用copy constructor創(chuàng)建一個(gè)Student對(duì)象用于函數(shù)內(nèi)部,函數(shù)執(zhí)行結(jié)束再調(diào)用析構(gòu)函數(shù)釋放這個(gè)對(duì)象。

    開銷太大了,改進(jìn)一下:pass by reference-to-const

    bool validateStudent(const Student& s);

    引用是通過指針來實(shí)現(xiàn)實(shí)現(xiàn)的,因此傳遞引用實(shí)際上就是在傳遞指針。references are typically implemented as pointers.

    但是這個(gè)規(guī)則對(duì)于內(nèi)置數(shù)據(jù)類型不適用,也不是適用STL iterator和函數(shù)對(duì)象function objects

    即使再小的對(duì)象也應(yīng)該不要使用值傳遞,而是要使用pass by reference-to-const

    關(guān)于slicing problem的另一種描述

    slicing problem是在多態(tài)規(guī)則里面容易產(chǎn)生的。

    看一個(gè)簡單的基類、派生類的定義

    class Window

    {

    public:

    int height;

    int width;

    };

    class TextWindow : public Window

    {

    public:

    int cursorLocation;

    };

    Window win;

    TextWindow *tWinPtr;

    tWinPtr = new TextWindow;

    win = *tWinprt;

    win是一個(gè)Window對(duì)象,C++規(guī)定:給win分配的內(nèi)存看見的大小,由其靜態(tài)類型決定。就是說默認(rèn)的拷貝函數(shù)導(dǎo)致信息會(huì)出現(xiàn)丟失。這就是slicing problem

    試想一下這要是通過值傳遞的方式傳遞參數(shù),實(shí)參一copy就已經(jīng)丟失信息了。

    牢記

    • Prefer pass-by-reference-to-const over pass-by-value.這樣既有效,又可以避免slicing problem
    • 但是這個(gè)規(guī)則對(duì)于內(nèi)置數(shù)據(jù)類型,STL iterator和函數(shù)對(duì)象function objects不適用。對(duì)于它們傳遞值就好了。

    Item 21Don't try to return a reference when you must return an object.必須返回對(duì)象時(shí),別妄想返回其reference

    heap and stack

    堆和棧這是2個(gè)不同的概念,哎喲,我一直以為是一個(gè)詞。

    heap:堆

    • 棧是系統(tǒng)提供的功能,特點(diǎn)是快速高效,缺點(diǎn)是有限制,數(shù)據(jù)不靈活;
    • 堆是函數(shù)庫內(nèi)部數(shù)據(jù)結(jié)構(gòu),不一定唯一。
    • 堆空間的分配總是動(dòng)態(tài)的,雖然程序結(jié)束時(shí)所有的數(shù)據(jù)空間都會(huì)被釋放回系統(tǒng),但是精確的申請(qǐng)內(nèi)存/釋放內(nèi)存匹配是良好程序的基本要素。
    • 使用new創(chuàng)建的對(duì)象是在heap上分配內(nèi)存空間。

    stack:棧

    • 而堆是函數(shù)庫提供的功能,特點(diǎn)是靈活方便,數(shù)據(jù)適應(yīng)面廣泛,但是效率有一定降低。
    • 棧是系統(tǒng)數(shù)據(jù)結(jié)構(gòu),對(duì)于進(jìn)程/線程是唯一的;不同堆分配的內(nèi)存無法互相操作。
    • 棧空間分靜態(tài)分配和動(dòng)態(tài)分配兩種。靜態(tài)分配是編譯器完成的,比如自動(dòng)變量(auto)的分配。動(dòng)態(tài)分配由alloca函數(shù)完成。棧的動(dòng)態(tài)分配無需釋放(是自動(dòng)的),也就沒有釋放函數(shù)。為可移植的程序起見,棧的動(dòng)態(tài)分配操作是不被鼓勵(lì)的!
    • 定義的局部變量是在stack上分配內(nèi)存空間的。

    牢記

    • 簡單一句話就是必須要返回對(duì)象。

    Item22Declare data members private.將成員變量聲明為private

    why data members shouldn’t be public?

    argument

    (against) 爭論,意見

    實(shí)參

    形參是parameters

    protected data member is mot more encapsulated than public one.

    牢記

    • data member一定要封裝。
    • protected不必public有更好的封裝。

    Item 23Prefer non-member non-friend functions to member functions.寧以non-membernon-friend替換member函數(shù)

    這是一個(gè)典型的例子:

    class WebBrowser {

    public:

    void clearCache();

    void clearHistory();

    void removeCookies();

    };

    為了提供一個(gè)執(zhí)行所有操作的函數(shù),所以就在WebBrowser里面追加定義:

    void clearEverything();

    哎,我一直就是這么寫的,并自以為有很好的封裝,But

    void clearBrowser(WebBrowser wb)

    {

    wb.clearCache();

    wb.clearHistory();

    wb.removeCookies();

    }

    第一:前者并不比后者有很好的封裝

    這就要解釋一下什么叫做“封裝”?以及封裝的判別標(biāo)準(zhǔn)。

    封裝的判別標(biāo)準(zhǔn):可以通過統(tǒng)計(jì)能夠訪問這個(gè)data的函數(shù)的數(shù)目來計(jì)算,函數(shù)越多,這個(gè)data封裝也就月不好,因此前一種寫法的封裝就沒有后者好。這也可以用來解釋Item22里面,為什么要求數(shù)據(jù)成員不能定義為public。另外增加clearEverything()作為member function,實(shí)際上是降低了封裝性。而后面的non-member non-friend functions的定義就沒有改變WebBrowser的封裝性。

    第二:后者還能提供更加靈活的打包package,增加擴(kuò)展性。

    put all convenience functions in multiple header files, but one namespace.

    第三:增加函數(shù)的可擴(kuò)展性。

    你可以定義自己的convenience functions,寫到一個(gè)header file里面,放到同一個(gè)namespace里面。這是member function做不到的。

    牢記

    • 優(yōu)先使用non-member non-friend函數(shù)來替換member函數(shù)。

    Item 24Declare non-member functions when type conversions should apply to all parameters.若所有參數(shù)皆需類型轉(zhuǎn)換,請(qǐng)為此采用non-member函數(shù)

    原因:

    Parameters are eligible for implicit type conversion only if they are listed in the parameter list.

    結(jié)論:

    make operator* a non-member function, thus allowing compilers to perform implicit type conversions on all arguments.

    class Rational {

    };

    const Rational operatior*(const Rational& lhs, Rational& rhs)

    {

       return Rationan(lhs.numerator()*rhs.numerator(),

    lhs.denominator()*rhs. denominator () );

    }

    一個(gè)誤區(qū):

    如果一個(gè)函數(shù),和某個(gè)類相關(guān),而又不能定義成member,那么這個(gè)函數(shù)就一定要定義成friend

    上面這個(gè)例子就說明這個(gè)說法并不正確。真愛生命,慎用friend functions

    Item 25Consider support for a non-throwing swap.考慮寫出一個(gè)不拋異常的swap函數(shù)

    1. default swap

    就是指std里面定義的swap

    1. member swap
    2. nonmember swap
    3. specializations of std::swap

    member swap

    Widget:我們希望的是交換指針,但swap實(shí)際做的是不僅copy3個(gè)Widget對(duì)象,而且還copy3個(gè)WidgetImpl對(duì)象。太浪費(fèi)了!都低碳時(shí)代了。

    class Widget{

    void swap(Widget& other)

    {

    using std::swap;

    swap(pImpl, other.pImpl;);

    }

    };

    template<> void swap<Widget>( Widget& a, Widget&b)

    {

       a.wap(b);

    }

    nonmember swap

    接下來要討論的是如果WidgetWidgetImpl不是類而是類模板會(huì)怎么樣?

    約束條件:不能在std里面增加新的template,只能特化(specializestd內(nèi)的template

    如果非要定義,say sorrybehavior is undefinedKAO,其實(shí)這比異常還討厭。

    解決方法是把它定義到一個(gè)自己的namespace里面,而不要定義到std里面。

    namespace WidgetStuff {

    template<typename T>

    class Widget{…};

    template<typename T>

    void swap(Widget<T>& a, Widget<T>& b)

    {

    a.swap(b);

    }

    }

    specializations of std::swap

    如果僅僅是針對(duì)一個(gè)class,那就特化std::swap

    If you want to have your class-specializing version of swap called in as many contexts as possible, you need to write both a non-member version in the same namespace as your class and a specialization of std::swap.

    這部分十分繞,P111還對(duì)于C++name lookup的規(guī)則進(jìn)行了詳細(xì)的描述。值得重新溫習(xí)。

    posted on 2010-03-22 11:28 amenglai 閱讀(398) 評(píng)論(0)  編輯  收藏 所屬分類: Effecitive C++讀書筆記

    主站蜘蛛池模板: 国产拍拍拍无码视频免费| ASS亚洲熟妇毛茸茸PICS| 国产成人亚洲精品91专区高清 | 亚洲18在线天美| 中国xxxxx高清免费看视频| 亚洲αv在线精品糸列| 免费一区二区无码东京热| 亚洲国产另类久久久精品黑人| 久久成人18免费网站| 亚洲成a人片在线观看无码 | 亚洲国产成人无码AV在线影院| 我想看一级毛片免费的| 亚洲另类自拍丝袜第五页| 日本免费一区二区三区最新vr| 精品成人一区二区三区免费视频| 国产网站在线免费观看| 永久免费观看黄网站| 亚洲一区二区女搞男| 免费黄色电影在线观看| 久久国产亚洲高清观看| 人成午夜免费视频在线观看| 亚洲日韩国产AV无码无码精品| 国产又黄又爽又猛的免费视频播放| 免费在线观看一区| 亚洲成AV人片在线观看WWW| 67194国产精品免费观看| 亚洲黄页网在线观看| 亚洲国产成人久久一区久久| 中文字幕无码毛片免费看| 亚洲激情电影在线| 最好免费观看韩国+日本| 国产99久久久久久免费看| 久久亚洲精品无码AV红樱桃| 影音先锋在线免费观看| 一级毛片无遮挡免费全部| 亚洲美女视频一区二区三区| 狠狠久久永久免费观看| a级成人毛片免费视频高清| 国产亚洲国产bv网站在线| JLZZJLZZ亚洲乱熟无码| free哆啪啪免费永久|