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

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

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

    PS,1880后程序員

    看不完的牙,寫不完的程序,跑不完的步。
    隨筆 - 97, 文章 - 34, 評論 - 10, 引用 - 0
    數據加載中……

    Effecitive C++讀書筆記 Chapter4 設計與聲明

     

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

    好的接口設計應該是既容易被正確使用,又不易被誤用。

    就例如書中的Sample,關于時間的,我們一般的做法就是在創建Day對象時,追加校驗函數來判斷年月日是不是有效。

    建議的做法一是:創建新的類型

    定義:

    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) );

    為啥不直接使用靜態變量?

    參考Item4 P30,簡單說就是不能保證在使用non-local static objects時,這個對象就已經初始化了。如果non-local static objects在另一個文件了,又恰巧沒有初始化,系統當然就會翹辮子了。

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

    先參考Item3 P19

    class Rational { };

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

    之所以強制設置為const就是為了避免client在使用時出錯。因為如果沒有clien,那么:

    Rational a,b,c;

    (a*b)=c

    這種寫法是對的,但是如果ab是內置類型,這種寫法就是錯誤的。

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

    一致性導致接口容易被正確使用。

    STL是榜樣,java在這里成了反面教材,因為如果想知道容器內對象的數量,用Array,要訪問屬性lengthString要用length函數,ArrayList要用size函數,這就是不一致性。

    使用std::tr1::shared_ptr,消除client對資源管理的責任

    找出以下寫法的兩個易錯的地方:

    Investment* createInvestment();

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

    2 刪除這個指針多次

    so,修改定義:

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

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

    這里就出現了一個新的client易錯的點,用戶或許會使用錯的資源釋放機制。因為deletegetRidOfInvestment取代了。

    std::tr1::shared_ptr< Investment >

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

    那么定義就應該是這樣的:

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

    {

       std::tr1::shared_ptr< Investment >

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

    retVal = ;

    return retVal;

    }

    tr1::shared_ptr的優點是允許在一個DLL創建對象,在另一個DLL里刪除對象。

    牢記

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

    Item19Treat class design as type design.設計class猶如設計type

    在設計一個類的時候,要回答一系列的問題哦。

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

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

    But先學習一個單詞,characteristicKAO,這竟然是個名詞。

    再學一個地道的說法:解決問題的方法:The way around the slicing problem is…

    函數都是值傳遞。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.這樣當然開銷就大了,每次都先copy一份進來,完事以后,再copy一份出去。

    假設函數的參數是一個Student對象,bool validateStudent(Student s);調用這個函數,額外的隱性開銷包括要先調用copy constructor創建一個Student對象用于函數內部,函數執行結束再調用析構函數釋放這個對象。

    開銷太大了,改進一下:pass by reference-to-const

    bool validateStudent(const Student& s);

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

    但是這個規則對于內置數據類型不適用,也不是適用STL iterator和函數對象function objects

    即使再小的對象也應該不要使用值傳遞,而是要使用pass by reference-to-const

    關于slicing problem的另一種描述

    slicing problem是在多態規則里面容易產生的。

    看一個簡單的基類、派生類的定義

    class Window

    {

    public:

    int height;

    int width;

    };

    class TextWindow : public Window

    {

    public:

    int cursorLocation;

    };

    Window win;

    TextWindow *tWinPtr;

    tWinPtr = new TextWindow;

    win = *tWinprt;

    win是一個Window對象,C++規定:給win分配的內存看見的大小,由其靜態類型決定。就是說默認的拷貝函數導致信息會出現丟失。這就是slicing problem

    試想一下這要是通過值傳遞的方式傳遞參數,實參一copy就已經丟失信息了。

    牢記

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

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

    heap and stack

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

    heap:堆

    • 棧是系統提供的功能,特點是快速高效,缺點是有限制,數據不靈活;
    • 堆是函數庫內部數據結構,不一定唯一。
    • 堆空間的分配總是動態的,雖然程序結束時所有的數據空間都會被釋放回系統,但是精確的申請內存/釋放內存匹配是良好程序的基本要素。
    • 使用new創建的對象是在heap上分配內存空間。

    stack:棧

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

    牢記

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

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

    why data members shouldn’t be public?

    argument

    (against) 爭論,意見

    實參

    形參是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函數

    這是一個典型的例子:

    class WebBrowser {

    public:

    void clearCache();

    void clearHistory();

    void removeCookies();

    };

    為了提供一個執行所有操作的函數,所以就在WebBrowser里面追加定義:

    void clearEverything();

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

    void clearBrowser(WebBrowser wb)

    {

    wb.clearCache();

    wb.clearHistory();

    wb.removeCookies();

    }

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

    這就要解釋一下什么叫做“封裝”?以及封裝的判別標準。

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

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

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

    第三:增加函數的可擴展性。

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

    牢記

    • 優先使用non-member non-friend函數來替換member函數。

    Item 24Declare non-member functions when type conversions should apply to all parameters.若所有參數皆需類型轉換,請為此采用non-member函數

    原因:

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

    結論:

    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 () );

    }

    一個誤區:

    如果一個函數,和某個類相關,而又不能定義成member,那么這個函數就一定要定義成friend

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

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

    1. default swap

    就是指std里面定義的swap

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

    member swap

    Widget:我們希望的是交換指針,但swap實際做的是不僅copy3Widget對象,而且還copy3WidgetImpl對象。太浪費了!都低碳時代了。

    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不是類而是類模板會怎么樣?

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

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

    解決方法是把它定義到一個自己的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

    如果僅僅是針對一個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還對于C++name lookup的規則進行了詳細的描述。值得重新溫習。

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

    主站蜘蛛池模板: 久久亚洲美女精品国产精品| 免费人成网站在线高清| 国产偷v国产偷v亚洲高清| v片免费在线观看| 亚洲AV无码一区二三区| 相泽南亚洲一区二区在线播放| 欧洲黑大粗无码免费| 亚洲六月丁香六月婷婷蜜芽| 69堂人成无码免费视频果冻传媒| 久久综合亚洲色HEZYO社区| 亚洲成人免费在线观看| 亚洲精品乱码久久久久久下载 | 亚洲精品成人片在线观看精品字幕| 国产免费A∨在线播放| 亚洲熟女乱综合一区二区| 久久九九久精品国产免费直播| 久久久久久亚洲精品不卡| 91视频免费观看| 久久精品国产亚洲av麻豆色欲| 成年人免费的视频| 亚洲AV色无码乱码在线观看| 亚洲国产精品嫩草影院久久 | 男女一边桶一边摸一边脱视频免费| 亚洲精品tv久久久久| 国产又黄又爽又大的免费视频| 久久亚洲综合色一区二区三区 | 黑人粗长大战亚洲女2021国产精品成人免费视频| 亚洲中文字幕久久无码| 国产无遮挡又黄又爽免费视频| 国产精品福利在线观看免费不卡| 亚洲精品无码鲁网中文电影| 亚洲一区二区三区免费在线观看 | 免费无遮挡无码永久在线观看视频| 一个人免费观看www视频| 久久青青草原亚洲av无码app| 曰曰鲁夜夜免费播放视频| 免费大片av手机看片高清| 亚洲av永久无码精品表情包| 野花高清在线电影观看免费视频| 亚洲AV无码国产一区二区三区 | 亚洲人成电影在线观看青青|