<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++讀書筆記 第五章 實現 Implementations

     

    Item26Postpone variable definitions as long as possible.盡可能延后變量定義的出現時間

    這里所說的變量是variable of type,它需要調用constructordestructor

    簡單情況下,如果函數拋出異常,導致變量沒有被使用到,但是由于已經定義過,其實還是需要調用它的constructordestructor

    std::string encryptPassword(const std::string& password)

    {

    std::string encrypted; //這是錯誤的寫法,這個定義已經需要調用構造函數了。

    encrypted = password;

    encrypt(encrypted);

    return encrypted;

    }

    正確的寫法:

    std::string encryptPassword(const std::string& password)

    {

    std::string encrypted(password); //正確的寫法。

    encrypt(encrypted);

    return encrypted;

    }

    postpone有兩層含義:

    1 postpone a variable’s definition until right before you have to use the variable

    2 postpone the definition until you have initialization arguments for it.

    循環怎么辦?

    最常見的寫法一種是在循環體里面定義變量,另一種是在循環體執行前定義一個變量。

    A

    B

    approach

    Widget w;

    for(int i=0; i<n;++i)

    {

    w=取決于i的某個值;

    }

    for(int i=0; i<n;++i)

    {

    Widget w(取決于i的某個值);

    }

    開銷

    1 constructor + 1 destructor + n assignment

    if assignment小于 constructor-destructor對,這種寫法比較有效

    n constructor + n destructor

    defect

    這種方法定義的變量在較大的范圍都是可見的,不好。

    結論:除非你明確知道approach A的開銷小于approach B,否則盡量使用approach B

    Item 27Minimize casting盡量少做轉型動作

    C++提供的4中cast形式

    const_cast<T>(expression)

    dynamic_cast<T>(expression)

    reinterpret_cast<T>(expression)

    static_cast<T>(expression)

    盡量使用這些新風格的轉型,最起碼的它好認啊。

    cast,到底做了什么?

    基類和派生類的指針有時是不相等的,存在一個偏移量offsetsingle object might have more than one address。為啥說有時呢,因為這取決于Compiler。所以不能想當然在基類指針上加個值就得到什么。

    dynamic_cast

    cast并不是啥都沒有做的,它會創建一個臨時的基類的copy,然后調用的是這個臨時類的方法,和當前被cast類無關哦。

    同時多級的繼承關系里面cast也要通過字符串比較來確定匹配的基類,因此如果對性能有要求,就盡量少使用cast

    解決方法

    有兩種:

    1 使用type-safe的容器

    這種方法的缺陷是容器里面只能允許有1種類型的指針

    ExampleP121

    2 在基類里面定義虛函數

    class Window{

    public:

    virtual void blink();

    };

    class SpecialWindow : public Window {

    public:

    virtual void blink();

    };

    typedef std::vector<std::tr1::shared_ptr<Window>> VPW;

    VPW winPtrs;

    for(VPW::iterator iter=winPtrs.begin(); iter!=winPtrs.end(); ++iter)

    (*iter).blink(); //注意這里就不需要dynamic_cast

    Item 28Avoid returning "handles" to object internals.避免返回handles指向對象內部成分 123

    handles

    handles可以理解為句柄,或者說是一個“把柄”,握著這個“把柄”,你可以修改對象的數據成員。

    handle包括referencepointeriterator

    問題出現的背景

    很簡單,就是看上去函數返回的是一個const對象,但是由于是handle,導致數據還是可以被client修改的。這種情況,debug時很難發現,而且給人的感覺是系統不穩定,后果很嚴重,不可輕視。

    struct rectData {

    Point ulhc;

    Point lrhc;

    };

    class Rectangle {

    public:

    Point& upperLeft() const {return pData->ulhc; }

    private:

    std::tr1::shared_ptr<RecData> pData;

    };

    const member function:不能修改數據成員。

    client調用:

    const Rectangle rec(Point (0,0), Point(100,100));

    rec.upperLeft().setX(50);//坐標就這么不知不覺中被修改了

    結論

    避免返回指向對象內部的handles,這種handles包括:pointerreferenceiterator

    如果某個函數的訪問級別低,禁止其它成員函數返回指向這個函數的指針。因為如果這樣,就可以通過函數指針來調用這個函數了。

    Item 29Strive for exception-safe code.為“異常安全”而努力是值得的 127

    class PrettyMenu{

    public:

    void PrettyMenu::changeBackgroud(std::istream& imgSrc) ;

    private:

    Mutex mutex;

    int imageChanges;

    Image * bgImage;

    };

    void PrettyMenu::changeBackgroud(std::istream& imgSrc)

    {

    Lock m1(&mutex); //Item13,對象管理資源的典型應用

    delete bgImage;

    ++imageChanges;

    bgImage = new Image(imgSrc);

    }

    異常安全性函數應該提供以下3種保證之一:

    基本保證

    如果異常拋出,程序里所有的東西都保持有效的狀態。

    增強保證

    如果異常拋出,程序的狀態保持不變。

    不拋出異常保證

    決不拋出異常。因為總能夠完成希望要做的事情。

    這同時也是一種理想狀態。

    一般情況下,我們的選擇是在basicstrong異常保證之間。

    重新寫changeBackgroud,提供basic異常保證:

    class PrettyMenu{

    std::tr1::shared_ptr<Image> bgImage;

    };

    void PrettyMenu::changeBackgroud(std::istream& imgSrc)

    {

    Lock m1(&mutex); //Item13,對象管理資源的典型應用

    bgImage.reset(new Image(imgSrc) );

    ++imageChanges;

    }

    strong異常保證的設計策略說出來也很簡單,就是copy and swap。具體描述見P131 L5開始的描述。

    strong異常保證

    基本思路就是copy and swap

    struct PMImpl {

    std::tr1::shared_prt<Image> bgImage;

    int imageChanges;

    };

    class PrettyMenu{

    private:

    Mutex mutex;

    std::tr1::shared_ptr<PMImpl> pImpl;

    };

    void PrettyMenu::changeBackgroud(std::istream& imgSrc)

    {

    using std::swap;

    Lock m1(&mutex); //Item13,對象管理資源的典型應用

    std::tr1:: shared_prt< PMImpl > pNew(new PMImpl(*pImpl));

    pNew->bgImage.reset(new Image(imgSrc)); //修改副本

    ++pNew-> imageChanges;

    swap(pImpl, pNew); //置換

    }

    trade-off折中考慮異常保證

    并不是一定要提供strong異常保證就是好的,這里是需要一個trade-offstrong異常保證的代價一定是很大的,efficiencycomplexity都是要考慮的因素。

    同時如果函數中調用了沒有異常保證的其它函數,這個函數也不能是異常保證的。

    void someFunc()

    {

    f1();

    f2();

    }

    這引申出一個結論:如果代碼中調用了沒有異常保證的C的傳統代碼,這樣的函數,就大可不必還要提供strong的異常保證了

    Item 30Understand the ins and outs of inlining.透徹了解inlining的里里外外 134

    什么是內聯函數inline

    將函數指定為 inline 函數,(通常)就是將它在程序中每個調用點上“內聯地”展開。函數本體Function body是不存在的。

    inline是對Compiler的請求,而不是對命令的。

    The inline specification is only a request to the compiler. The compiler may choose to ignore this request.

    一般來說,內聯機制適用于優化小的、只有幾行的而且經常被調用的函數。

    大多數的編譯器都不支持遞歸函數的內聯。

    復雜函數也是不可以的,一個 1200 行的函數也不太可能在調用點內聯展開。

    虛函數也是不可以的。

    inline與函數模板Function Template

    template<typename T>

    inline const T& std::max(const T& a, const T& b)

    { return a<b?b:a; }

    inline函數大多數都是定義在頭文件了,這是因為編譯器必須知道inline函數是什么樣子。

    Template也是定義在頭文件里,因為Compiler需要知道Template是什么樣子,為了初始化Template

    Template獨立于inline。這是不同的概念,當然函數模板也可以聲明成inline

    函數指針和內聯函數

    函數指針指向的內聯函數一定不會被inlineCompiler會生成這個函數的本體function body,函數指針就是指向的這個函數本體。

    inline void f() {…}

    void (*pf)() = f;

    f();

    pf();//這個調用就不會是inline,因為這是一個函數指針。

    構造函數、析構函數和內聯函數

    構造函數和析構函數不要做內聯。

    簡單的說,如果base constructor內聯,派生類中從基類繼承來的data member被初始化2次,一次是來自內聯,一次來自C++執行基類的構造函數。

    同理,析構函數,也就會被釋放2L慘了,但是實際上,只有一個對象啊。不死等什么。

    內聯函數修改的影響

    內聯函數的機制決定調用內聯函數,就是把代碼插入調用的位置,因此如果內聯函數修改了,所有調用該函數的地方都需要重新編譯。如果這個函數不是內聯的,修改后,只要重新鏈接就可以了。

    記住

    inline僅僅適用于小型的,被頻繁調用的函數。

    不要因為函數模板定義在頭文件里就把它聲明成inclineFunction Templateinline這是2個不同的概念。

    Item 31Minimize compilation dependencies between files.將文件間的編譯依存關系降至最低 140

    前向聲明forward declaration

    1.            首先前向聲明僅僅是聲明了一個類

    class Date; //forward declaration

    class Address;

    2.            其次前向聲明是一種不完全類型

    只能以有限的方式使用。只能用于定義指向該類型的指針,或者用于聲明(而不是定義)使用該類型作為形參類型或返回類型的函數。不能定義該類型的對象。

    pimplpointer to implementation

    #include <string>

    #include <memory>

    class PersonImpl;

    class date;

    class Address;

    class Person {

    public:

    Person(const std::string& name, const date& birthday, const Address& addr);

    std::string name() const;

    std::string birthday() const;

    std::string address() const;

    private:

    std::tr1::shared_ptr<PersonImpl> pImpl; //point to implementation

    };

    基本解決之道

    replacement of dependencies on definitions with dependencies on declarations.以聲明的依賴性替換定義的依賴性

    盡量讓頭文件做到自包含,萬一做不到,則讓它和其它文件內的聲明相互依賴,而不是定義。

    規則

    如果使用對象引用活對象指針可以解決問題,就避免使用對象,

    取代類定義,依賴于類聲明

    頭文件要成對出現,一個是聲明,一個是定義。

    兩種具體的操作方法

    Handle Classes

    定義Handle class

    class Person {

    public:

    Person(const std::string& name, const Date& birthday, const Address& addr);

    std::string name() const=0;

    std::string birthday() const=0;

    std::string address() const=0;

    private:

    std::tr1:shared_ptr<PersonImpl> pImpl;

    };

    std::string Person::name() const

    {

    return pImpl->name();

    }

    定義實現類PersonImpl

    Handle Classes

    這里給出的Handle Classes的定義太精辟了。喜歡J

    Classes like Person that employ the pimpl idiom are often called Handle classes.

    Making Person a Handle class doesn’t change what Person does, it just changes the way it does it.

    Interface Classes

    定義接口

    class Person {

    public:

    virtual ~Person();

    virtual std::string name() const=0;

    virtual std::string birthday() const=0;

    virtual std::string address() const=0;

    static std::tr1::shared_ptr<Person> Person::create(const std::string& name, const Date& birthday, const Address& addr );

    };

    定義實現

    class RealPerson:public Person {

    public:

    realPerson(const std::string& name, std::string& birthday, const Address& addr)

    :theName(name),theBirthdate(birthday), theAddress(addr)

    {}

    virtual ~RealPerson()

    std::string name() const;

    std::string birthday () const;

    std::string address () const;

    private:

    std::string theName;

    date theBirdate;

    Address theAddress;

    };

     

    std::tr1::shared_ptr<Person> Person::create(const std::string& name, const Date& birthday, const Address& addr )

    {

    return std::tr1::shared_ptr<Person>(new RealPerson(name, birthday,addr));

    }

    結論

    減少耦合行的慣用的思路是用聲明依賴替代定義依賴。有2種方法:Interface classesHandle classes.

    任何減少耦合的做法都是會帶來負面影響的:占用內存增加,間接調用。但是關鍵還是在于引用本身是不是對這些額外的開銷敏感。

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

    主站蜘蛛池模板: 免费A级毛片无码久久版| 成年性午夜免费视频网站不卡| 国产免费观看a大片的网站| 国产成人精品日本亚洲直接| 91大神在线免费观看| 久久亚洲精品高潮综合色a片| 91免费播放人人爽人人快乐| 亚洲国产精品免费在线观看| 国产免费一区二区三区| 亚洲w码欧洲s码免费| 国产成人午夜精品免费视频| 亚洲国产成人AV在线播放| 日本一道高清不卡免费| 黄页网址在线免费观看| 亚洲一区二区三区影院| 午夜视频在线免费观看| 国产乱子伦精品免费女| 特级毛片全部免费播放a一级 | 亚洲一区电影在线观看| 青苹果乐园免费高清在线| 亚洲欧洲∨国产一区二区三区| 国产在线精品观看免费观看| 精品亚洲综合在线第一区| 亚洲免费网站在线观看| 亚洲国产成人久久精品软件| 亚洲国产精品碰碰| 亚洲精品蜜夜内射| 亚洲精品线路一在线观看| 国产情侣久久久久aⅴ免费| 亚洲成a人片在线网站| 国产男女性潮高清免费网站| 精品一区二区三区免费视频| 日韩一区二区免费视频| h在线看免费视频网站男男| 亚洲?V无码成人精品区日韩| 亚洲一区二区三区久久| 亚洲国产精品第一区二区三区 | 91青青青国产在观免费影视| 亚洲一区二区三区写真| 在线精品亚洲一区二区小说 | 最近免费中文字幕大全免费 |