【Effective C++ 讀書筆記】導讀 Introduction

學習程序語言根本大法是一回事,學習如何以某種語言設計並實現高效程序則是另外一回事。css

一組明智選擇並精心設計的classes、functions、templates可以使程序編寫容易、直觀、高效、而且遠離錯誤。git

帶着問題去品讀這本經驗著做:程序員

設計上的討論:
「如何在兩個不一樣的作法中擇一完成某項任務?」數組

  • 選擇繼承(inheritance) or 模板(templates)?
  • 選擇public繼承仍是private繼承?
  • private繼承仍是composition(複合)?
  • 選擇number函數仍是non-number函數?
  • 選擇pass-by-value仍是pass-by-reference?

即便徹底知道該作什麼,徹底進入正軌可能仍是可能有點棘手。函數

  • 什麼是assignment操做符的適當返回類型(return type)?
  • 什麼時候該令析構函數爲virtual?
  • 當operator new沒法找到合適的內存空間時該如何行事?

榨出這些細節很重要,本書將帶你趨兇避吉,避免那些未可預期、神祕難解的程序行爲。學習

軟件設計和實現是複雜的差事,被硬件、操做系統、應用程序的約束條件塗上五光十色,因此我能作的最好的就是提供指南,讓你得以創造出更棒的程序。ui

準則天生就帶有例外。這就是爲何每一個條款都有解釋與說明。這些解釋與說明是本書最重要的一部分。惟有了解條款背後的基本原理,你纔可以決定是否將它套用於你所開發的軟件,並奉行其所昭示的獨特約束。spa

本書的最佳用途:操作系統

本書的最佳用途就是完全瞭解 C++ 如何行爲爲何那樣行爲以及如何運用其行爲造成優點設計

下面是每一個C++程序員都應該瞭解的一份小小的 C++ 詞彙。

  1. 聲明式

    所謂聲明式(declaration)是告訴編譯器某個東西的名稱和類型,可是略去細節。

    extern int x;       //對象聲明式
        std::size_t numDigits(int number); //函數聲明式
        class Widget;           //類聲明式
    
        template<typename T>    //模板聲明式
        class GraphNode;
  2. 簽名式

    每一個函數的聲明揭示其簽名式(signature),也就是參數和返回值類型。一個函數的簽名就等同於該函數的類型。numDigits函數的簽名是std::size_t (int),也就是說「這個函數得到一個int 並返回一個 std::size_t」。

  3. 定義式(definition)

    定義式的任務是提供編譯器一些聲明式所遺漏的細節。對對象而言,定義式是編譯器爲此對象撥發內存的地點
    對function 或者 function template 而言,定義式提供了代碼本體。對 class 或者 class template 而言,定義式列出它的成員:

    int x;                     //對象定義式
        std::size_t numDigits(int number)  //函數定義式
        {
        //返回其參數的數字個數
            std::size_t digitsSoFar = 1;
            while ((number /= 10) != 0)
                ++ digitsSoFar;
            return digitsSoFar;
        }
    
        class Widget                    //class 定義式
        {
            public:
            Widget();
            ~Widget();
            ...
        };
    
        template<typename T>            //template 的定義式
        class GraphNode{
            public:
            GraphNode();
            ~GraphNode();
            ...
        };
  4. 初始化(initialization)

    初始化(initialization)是「給予對象初值」的過程。對用戶自定義類型的對象而言,初始化由構造函數執行。
    所謂default構造函數是一個可被調用而不帶任何實參者。這樣的構造函數
    要不沒有參數要不就是每一個參數都有缺省值

    class A{
        publicA();                        //default構造函數
    };
    
    class B{
        public:
        explicit B(int x = 0, bool b = true); //default構造函數
    }
    
    class C{
        public:
        explicit C(int x);          //不是default構造函數
    }

    上述的class B 和 C的構造函數都被聲明爲 explicit,這可阻止它們被用來執行隱式類型轉換(implicit type conversions),但他們仍可被用來進行顯示類型轉換(explicit type conversions):

    void doSomething(B bobject);     //此函數接受一個類型爲B的對象
    
    B bObj1;                         //一個類型爲B的對象
    doSomething(bObj1);             //沒問題,傳遞一個B給
                                    //doSomething函數
    
    B bObj2(28);                    //沒問題,根據int 28 創建一個B
                                    //(函數的bool參數缺省爲true)
    doSomething(28);                //錯誤!doSomething 應該接受
                                    //一個B,不是一個 int ,
                                    //int 和 B 之間沒有隱式轉換
    doSomething(B(28));             //沒問題,使用 B 構造函數將 int 
                                    //顯式轉換爲一個 B 以促成此調用

    被聲明成 explicit 的構造函數禁止編譯器執行非預期的類型轉換。除非你有一個好理由容許構造函數被用於隱式類型轉換,不然你應該把他聲明爲 explicit 。

  5. copy構造函數 和 copy assignment操做符

    copy構造函數被用來」以同類型對象初始化自我對象」
    copy assignment 操做符被用來「從另外一個同類型對象中拷貝其值到自我對象」:

    class Widget{
        public:
        Widget();                             //default構造函數
        Widget(const Widget& rhs);            //copy構造函數
        Widget& operator=(const Widget& rhs); //copy assignment操做符
        ...
    };
    Widget w1;                               //調用default構造函數
    Widget w2(w1);                           //調用copy構造函數
    w1 = w2;                        //調用 copy assignment操做符

    當你看到賦值符號時請當心,由於「=」語法也可用來調用copy構造函數:

    Widget w3 = w2; //調用copy構造函數

    如何區別是」copy構造」仍是」copy賦值」:
    若是一個新對象被定義,必定會有一個構造函數被調用,不可能調用賦值操做。若是沒有新對象被定義(例如前面的 「w1 = w2」語句),就不會有構造函數被調用,那麼固然就是賦值操做被調用。

    copy構造函數是一個尤爲重要的函數,由於他定義了一個對象如何 pass-by-value(以值傳遞)。
    舉個例子:

    bool hasAcceptableQuality(Widget w);
    ...
    Widget aWidget;
    if (hasAcceptableQuality(aWidget)) ...

    參數w是以 by value 方式傳遞給 hasAcceptableQuality,因此在上述調用中 aWidget被複制到 w 體內。這個複製動做由 Widget 的copy構造函數完成。pass-by-value 意味「調用 copy 構造函數」。以 by value 傳遞用戶自定義類型一般是個壞主意, Pass-by-Reference-to-const每每是比較好的選擇。

  6. 未定義行爲(Undefined behavior)[不明確行爲]

    int* p = 0;               //p是一個null指針
    std::cout << *p;          //對一個null指針取值會致使不明確的行爲
                              //null指針可讀不可寫
    
    char name[] = "Darla";    //name 是個數組,大小爲6(別忘記最尾端的null)
    char c = name[10];          //只涉及一個無效的數組索引
相關文章
相關標籤/搜索