---
# System prepended metadata

title: Effective C++
tags: [筆記]

---

# Effective C++
- [细读 Effective C++](https://harttle.land/effective-cpp.html)
- [《Effective C++》讀後感——從自己的角度講一講《Effective C++》](https://jonny.vip/2020/06/02/effective-cplusplus-%E8%AE%80%E5%BE%8C%E6%84%9F/)
- [【Effective C++ 閱讀筆記】](https://lkm543.medium.com/effective-c-%E9%96%B1%E8%AE%80%E7%AD%86%E8%A8%98-fb68f00a6b83)

### Chapter 1: 習慣 C++
#### 01 視 C++ 為語言聯邦
- C++ 可以被視為四種語言的集合
    - C : 高效程式語言
    - Objected-Oriented C++ ： C with classes
    - Template C++ : 樣板、泛型程式設計
    - STL : 標準庫
#### 02 盡量以 const, enum, inline 取代 define
- 使用 const 變數取代 define 的原因
    - `確保 identifier 有進入 symbol table`，容易 debug
    - 使用常數 `const double AspectRatio = 1.653` 相比於 `#define` 可以產生更小的 object code
    - 無法使用 private 、 public 等封裝
- 使用 enum 取代 define，定義 class 內靜態變數
    - 方法一
        ```c++=
        class Game {
        private:
            static const int GameTurn = 10;
            int scores[GameTurn];
        };

        const int Game::GameTurn;
        ```
    - 方法二
        ```c++=
        class Game {
        private:
            static const int GameTurn;
        };

        const int Game::GameTurn = 10;
        ```
    - 方法三 `enum hack`
        ```c++=
        class Game {
        private:
            enum {GameTurn = 10};
            int scores[GameTurn];
        };

        ```
- 使用 inline 取代 define，避免不預期行為
    ```c++=
    #define CALL_WITH_MAX(a, b) f( (a) > (b) ? (a) : (b) )
    void f(int a) {}
    CALL_WITH_MAX(++a, b);     // a : 5 -> 7
    CALL_WITH_MAX(++a, b+10);  // a : 7 -> 8
    
    // 建議做法
    template<typename T>
    inline void CALL_WITH_MAX(const T& a , const T& b){
        f(a>b?a:b);
    }
    ```
#### 03 盡量使用 const
- 使用 const 語意約束，可以獲得編譯器的協助
- const 指針用法：
    ```c++=
    int *const ptr = &num; // 不可指向不同位置
    int const *ptr2 = &num; // 指向 const int
    const int *ptr3 = &num; // 指向 const int

    const vector<int>::iterator 等效於 T* const // 不可指向不同位置
    vector<int>::const_iterator 等效於 const T* // 指向 const int
    ```
- 避免用戶錯誤使用
    ```c++=
    const Rational operator* (const Rational& lhs, const Rational& rhs);
    if(a*b=c){...} // 避免了此錯誤
    ```
- Mutable 的使用方法
    ```c++=
    class CTextBlock {
    public:
      std::size_t length() const{
      if (!lengthIsValid) {
        textLength = std::strlen(pText);
        lengthIsValid = true;          
      }
      return textLength;
    };

    private:
      char *pText;

      mutable std::size_t textLength;   // these data members may
      mutable bool lengthIsValid;       // always be modified
    };   
    ```
- 利用 non-const 版本呼叫 const 版本，避免代碼重複
    ```c++=
    class Example {
    public:
        // const 版本的函式
        int getValue() const {
            std::cout << "Calling const version" << std::endl;
            // Step 1
            // Step 2
            // Step 3
            return value;
        }

        // non-const 版本的函式，利用 const_cast 呼叫 const 版本
        int getValue() {
            std::cout << "Calling non-const version" << std::endl;
            return const_cast<const Example*>(this)->getValue();
        }

    private:
        int value = 42;
    };
    ```
#### 04 確保 object 被使用前有初始化
- 讀取未初始化的值，會導致不明確的行為
- 對於 class 而言，賦值和初始化不同，`建議使用成員初始列 member initialization list`
    ```c++=
    Data::Data(const std::string& name) { // 兩次操作，先 default 初始化，才 copy assignment
        theName = name;
    }

    Data::Data(const std::string& name) // 一次操作，copy constructor
    : theName = name {

    }
    ```
- `不同檔案中的 non-local static, 初始化順序無法確定`
    - 可以使用 `Singleton Pattern` 解決
    ```c++=
    // a.cpp
    FileSystem tfs = tfs();
    Directory td = td();
    
    // b.cpp 避免 a.cpp 使用這些變數時還未初始化，包成 function
    FileSystem& tfs() {
        static FileSystem fs;
        return fs;
    }
    Directory& tempDir() {
        static Directory td;
        return td;
    }
    ```
### Chapter 2: 建構子/解構子/賦值運算
#### 05 了解 Ｃ++ 自動產生哪些函數
- 當這些函數被需要調用，編譯器會自動建立出來，他們都是 non-virtual
    - default constructor
    - copy constructor
    - destructor
    - copy assignment
#### 06 若不想用自動產生的函數，就明確拒絕
- 將該 member function 放到 private
- 將該 member function 宣告為 delete
- 將該 class 繼承自一個 Uncopyable 的 class
#### 07 為多型 base class 宣告 virtual destructor
- (1) 為了`多型設計`所定義的 base class 必須宣告 virtual destructor
    - 當 base class 指針，指向 derived class 的物件時，呼叫 delete 要能正確釋放 derived class 的記憶體
    - 非為了多型而設計的類別不在此限
- (2) 帶有 virtual function 的 class 都應該要有 virtual destructor
- (3) 不去繼承沒有提供 virtual destructor 的類別，如 STL 中的 string, vector, ... ，否則 base class pointer to derived class 呼叫 delete 會出現未定義行為
    ```c++=
    class SpecialString: public std::string{} // 會有問題
    
    SpecialString *pss = new SpecialString("data");
    std::string *ps;
    ps = pss;
    delete ps; // 未定義行為
    ```
#### 08 別讓 exception 逃離 destructor
- 當 destructor 可能出現 exception 時
    - 用 `try ... catch` 去捕捉它，紀錄錯誤
    - 用 `try ... catch` 去捕捉它，終止程式
- 重新設計 class 讓客戶端可以對出現的異常做出反應
    ```c++=
    class DBConn{
    public:
        void close() {
            db.close();
            closed = true;
        }
        ~DBConn() {
            if(!closed){
                try {
                    db.close();
                } catch (...) {
                    // log of abort
                }
            }
        }
    private:
        DBConnetion db;
        bool closed;
    }
    ```
#### 09 不應該在 constructor（建構子）或 destructor（解構子）中呼叫 virtual function
- 當前的類別尚未被完整初始化或完全解構，因此其 virtual dispatch（虛擬函式調用機制）無法正常運作。
    ```c++=
    #include <iostream>

    class Base {
    public:
        Base() {
            std::cout << "Base Constructor" << std::endl;
            init(); // 嘗試在 constructor 中呼叫 virtual function
        }
        virtual ~Base() {
            std::cout << "Base Destructor" << std::endl;
            cleanup(); // 嘗試在 destructor 中呼叫 virtual function
        }

        virtual void init() {
            std::cout << "Base init" << std::endl;
        }
        virtual void cleanup() {
            std::cout << "Base cleanup" << std::endl;
        }
    };

    class Derived : public Base {
    public:
        Derived() {
            std::cout << "Derived Constructor" << std::endl;
        }
        ~Derived() {
            std::cout << "Derived Destructor" << std::endl;
        }

        void init() override {
            std::cout << "Derived init" << std::endl;
        }
        void cleanup() override {
            std::cout << "Derived cleanup" << std::endl;
        }
    };

    int main() {
        Derived d;
        return 0;
    }
    ```
    ```c++=
    Base Constructor
    Base init        // 並非 Derived init
    Derived Constructor
    Derived Destructor
    Base Destructor
    Base cleanup     // 並非 Derived cleanup
    ```
#### 10 令 operator= 返回 reference to \*this
- 這是個協議，並無強制性，許多 STL 類型也都是如此時做的。
- 目的是為了處理連鎖形式
    ```c++=
    x = y = z = 15
    
    class Widget{
        Widget& operator+=(const Widget& rhs){
            //...
            return *this; // left value
        }
        Widget& operator=(const Widget& rhs){
            //...
            return *this; // left value
        }
    }
    ```
#### 11 在 operator= 中處理自我賦值
- 解決的問題
    ```c++=
    Widget w;
    w = w;         // 自我賦值
    
    a[i] = a[j]                      // 可能自我賦值
    *px = *py                        // 可能自我賦值
    void func(Base& rb, Derived* pd) // 可能同一對象
    ```
- 方法一 ：identity test
    ```c++=
    Widget::operator=(const Widget& rhs){
        if(this == &rhs)    return *this;
        delete pb;
        pb = new Bitmap(*rhs.pb);
        return *this;
    }
    ```
- 方法二 ：安排檢查順序
    ```c++=
    Widget::operator=(const Widget& rhs){
        Bitmap* pOrig = pb;
        pb = new Bitmap(*rhs.pb);
        delete pOrig;
        return *this;
    }
    ```
- 方法三 ：copy and swap
    ```c++=
    Widget::operator=(const Widget& rhs){
        Widget tmp(rhs);
        swap(tmp);
        return *this;
    }
    ```
#### 12 複製 object 時記得每一個部分
- 自定義 copy constructor 和 copy assignment 時，要注意每一個部分都要處理到
    - 新增變數時 ： （編譯器不會警告）
        - 記得更新 copy constructor
        - 記得更新 assignment
    - 繼承其他 class 時 ： 
        - copy constructor 要呼叫 base class 的 copy constructor
        - copy assignment 要呼叫 base class 的 copy assignment
- copy constructor 和 copy assignment 不應該互相呼叫
    - 將重複的部份抽離到第三個 function 讓他們共同使用

### Chapter 3: 資源管理
#### 13 以 object 管理資源
- 資源 : memory、file descriptor、mutex、dbConnection、socket
- 物件的 constructor 獲得資源，物件的 desctructor 釋放資源
- RAII (資源取得即初始化) : 將資源的生命週期與物件的生命週期綁定
    - (1) 若變數，可以使用智能指標 (呼叫 delete)
    - (2) 若陣列，可以使用 vector 或是 string
#### 14 資源管理的四種 copy 行為
- 使用 object 管理資源，需注意當 object 複製或轉移給另一個 object 時，底層的資源該如何對應的轉移
- (1)禁止複製，如自己實作的 `lock`
    ```c++=
    class Lock {
    public:
        explicit Lock(Mutex* pm) : mutexPtr(pm) {
            lock(mutexPtr);
        }
        ~Lock() {
            unlock(mutexPtr);
        }
    private:
        Lock(const Lock&) = delete;            // 禁止 copy constructor
        Lock& operator=(const Lock&) = delete; // 禁止 copy assignment
        Mutex *mutexPtr;
    }
    ```
- (2)紀錄 reference count 並共享資源，如`shared_ptr`
    ```c++=
    shared_ptr<int> ptr1 = make_shared<int>();
    shared_ptr<int> ptr2 = ptr1;
    ```
- (3)深度複製資源，如 `string`
    ```c++=
    string a = "aaaaa";
    string b = a;        // deep copy
    ```
- (4)轉移資源，如 `unique_ptr`
    ```c++=
    unique_ptr<int> ptr1 = make_unique<int>();
    unique_ptr<int> ptr2 = move(ptr1);
    ```
#### 15 資源管理提供對原始資料的訪問
- APIs 往往要求訪問原始資源，所以每一個 RAII class 應該提供取得資源的方法
- 使用 `unique_ptr` 和 `shared_ptr` 管理資源時，若有需要得到原始指標，可以使用 `get()`
    ```C++=
    shared_ptr<int> pInv(new int);
    unique_ptr<int> pInv2(new int);
    
    int* p1 = pInv.get();
    int* p2 = pInv.get();
    ```
- 若使用自定義 class 管理資源時，最好也提供得到原始資源的方式
    ```c++=
    // 自定義 class
    class Font {
    public:
        FontHandle get() const { return f; }     // 提供顯式轉換
        operator FontHandle() const { return f;} // 提供隱式轉換，較方便，但非常不建議
    private:
        FontHandle f;
    }
    ```
#### 16 成對的使用 new 和 delete
- 如果呼叫 new 時有使用 [], 則呼叫 delete 時也要使用 [];如果呼叫 new 時未使用 [], 則呼叫 delete 時也不該使用 [];
- 當錯誤使用時，會產生未定義行為
    ```c++=
    string* p1 = new string;
    string* p2 = new string[100];
    
    delete p1;
    delete [] p2;
    ```
    - 最好不要對 array 做 typedef 動作
#### 17 以獨立語句將 newed object 放入智能指針
- 使用獨立語句，可以在 exception 發生時避免資源洩漏
    ```c++=
    // 不建議的寫法
    f1(unique_ptr<int>(new int), f2())
    
    // 建議的寫法
    unique_ptr<int> x(new int);
    f1(x, f2());
    ```
    - 不確定 f2 發生異常時，unique_ptr 是否有正確釋放資源，e.g.
        - (1) 執行 new int
        - (2) 執行 f2() --> 可能 raise exception
        - (3) 呼叫 unique_ptr constructor
    
### Chapter 4: 設計和宣告
#### 18 讓接口容易被正確使用，不易被誤用
- 不要預設 client 會記住所有細節
- 促進正確使用 ： 自訂 class/type 保持和 int 有"一致"的行為
- 阻止誤用 ： 限制操作類型，限制對象值
#### 19 設計 class 如同 type
- 設計 class 需考慮的面向
    - (1) 新的 type 如何被創建和銷毀
    - (2) 物件的初始化和賦值有什麼差別
    - (3) 新的 type 如果 pass by value 行為為何
    - (4) 新的 type 合法值為何
    - (5) 新的 type 需要配合某繼承圖嗎
    - (6) 新的 type 需要什麼樣的轉換
    - (7) 新的 type 需要什麼樣的操作符和函數
    - (8) 新的 type 需要拒絕什麼樣的函數
    - (9) 誰能存取新的 type
    - (10) 新的 type 有哪些潛在的 interface
    - (11) 新的 type 有多麽一般化
    - (12) 你真的需要新 type 嗎 -> 也許定義非成員函數或者模板即可
#### 20 以 pass by reference to const 取代 pass by value
- (1)避免多次調用 constructor 和 destructor 造成效率浪費
- (2)避免傳遞時，資料被切割 slicing
    ```c++=
    bool validate(Student s);        // pass by value
    -->
    bool validate(const Student &s); // pass by reference to const
    ```
#### 21 函數回傳值不可以是 reference
- 若回傳值是在 stack 上的 local 變數的 reference 會存取不合法記憶體
- 若回傳值是在 heap 上記憶體的 reference，客戶端很可能沒正確釋放記憶體
- 若回傳值是 class 的 static 對象的 reference，客戶端很可能出現錯誤邏輯
- 正確：回傳值為一個新 object，return value instead of reference
#### 22 將 member variable 聲明為 private
- 保持 client 端都是使用小括號存取成員變數的一致性
- 透過成員函數設定每個成員變數的存取權限
- 將成員變數封裝在 class 中，保持實作的彈性
    - `封裝的重要性遠比你最初見到他還重要`
- 從封裝的角度，只有兩種訪問權限 `private(有封裝）` 和 `其他（不封裝）`
    - 宣告為 `public` ，所有客戶端都可能使用邏輯
    - 宣告為 `protected` ，所有 derived class 都可能使用邏輯
#### 23 以 non-member、non-friend 替換一般 member function
- 一味地去追求物件導向程式設計的守則，`有時候`並不是一個好的選擇
- 對於底下的例子，使用(1)一般 function 比使用 (2)member function 帶來更好的封裝性
    ```c++=
    // (1) 一般 function
    void clearEverything()(WebBrowser &wb){
        wb.clearCache();
        wb.clearHistory();
        wb.removeCookies();
    }
    // (2) member function
    class WebBrowser {
    public:
        void clearEverything(){
            wb.clearCache();
            wb.clearHistory();
            wb.removeCookies();
        }
    }
    ```
#### 24 若所有參數都需要類型轉換，請使用 non-member function
- 如果 constructor 為 non-explicit
    ```c++=
    class Rational {
    public:
        Rational(int n = 0, int d = 1) : num(n), den(d) {}
        const Rational operator*(Rational rhs) const {
            return Rational(num * rhs.den, den* rhs.num);
        }
        int num, den;
    };
    void f1() {
        Rational r1(1, 2);
        Rational result = r1 * 2;
        Rational result2 = 2 * r1; // error
    }
    ```
- 如果 constructor 為 explicit
    ```c++=
    class Rational2 {
    public:
        explicit Rational2(int n = 0, int d = 1) : num(n), den(d) {}
        const Rational2 operator*(Rational2 rhs) const {
            return Rational2(num * rhs.den, den* rhs.num);
        }
        int num, den;
    };
    void f2() {
        Rational2 r2(1, 2);
        Rational2 result3 = r2 * 2; // error
        Rational2 result4 = 2 * r2; // error
    }
    ```
- 一般情況，滿足交換律，都需要類型轉換
    ```c++=
    class Rational3 {
    public:
        Rational3(int n = 0, int d = 1) : num(n), den(d) {}
        int num, den;
    };
    Rational3 operator*(Rational3 lhs, Rational3 rhs) {
        return Rational3(lhs.num * rhs.den, lhs.den * rhs.num);
    }
    void f3() {
        Rational3 r3(1, 2);
        Rational3 result5 = r3 * 2; // OK
        Rational3 result6 = 2 * r3; // OK
    }
    ```
#### 25 寫出沒有異常的 swap
- 在 c++11 之後，std::swap 的底層實現是使用「移動語意」來提升性能
- 為了支持移動語意，你需要在類中實現`移動構造函數`和`移動賦值運算符`
    ```c++=
    template <typename T>
    void swap(T& a, T& b) {
        T temp = std::move(a);  // 使用移動建構
        a = std::move(b);       // 使用移動賦值
        b = std::move(temp);    // 使用移動賦值
    }
    ```

### Chapter 5: 實作
#### 26 盡可能延後變數定義的時間
- 延後變數出現時間
    - 避免 constructor 和 destructor 被呼叫到
    - 迴圈的變數宣告在外部或內部
        - 外部: 一個 constructor、一個 desctructor、n 個 assign
        - 內部: n 個 constructor、n 個 desctructor，但較好維護
#### 27 盡量少做轉型
#### 28 避免返回 handles 指向 object 內部結構
#### 29 為 "異常安全" 努力
#### 30 徹底了解 "inline" 的所有細節
#### 31 將編譯 dependency 降到最低


- 轉型
    ```c++=
    // 舊式轉型
    (T)expression
    T(expression)
    
    // 新式轉型
    const_cast<T>(expression)
    dynamic_cast<T>(expression)
    reinterpret_cast<T>(expression)
    static_cast<T>(expression)
    
    // 錯誤的轉型
    class SpecialWindow: public Window {
    public:
        // 假設要先調用 Window 的 onResize 才做其他操作
        virtual void onResize() {
            static_cast<Window>(*this).onResize(); // (1) 錯誤，因為會作用在 *this 的 base class 成分副本而已
            Window::onResize();                    // (2) 正確
            
            ...
        }
    }
    ```
- C++ 轉型
    ```c++=
    (T) expression
    T (expression)
    const_cast<T> (expression)
    dynamic_cast<T> (expression)
    reinterpret_cast<T> (expression)
    static_cast<T> (expression)
    ```
    - [const_cast-详解一篇就够了](https://blog.csdn.net/qq_45853229/article/details/124669527?spm=1001.2101.3001.6650.10&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7ERate-10-124669527-blog-81449296.pc_relevant_3mothn_strategy_recovery&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7ERate-10-124669527-blog-81449296.pc_relevant_3mothn_strategy_recovery&utm_relevant_index=14)
    - 呼叫 parent class 的函數
        ```c++=
        class SpecialWindow: public Window{
        public:
            virtual void onResize(){
                Window::onResize();    // 先呼叫 parent class 的函數
                // 接著做其他操作
            }
        };
        ```
- https://goodspeedlee.blogspot.com/2016/11/c.html
### Chapter 6: 繼承與物件導向
#### 32 Public 繼承滿足 "is-a" 的關係
- Liskov Substitution Principle
- 適用於 base class 上的每一件事情，一定也適用於 derived class 上
- penguin 不能直接 public 繼承 bird
- square 不能直接 public 繼承 rectangle
#### 33 避免遮蓋繼承而來的名稱
- 依序由內至外尋找不同 scope
    - 變數宣告
    ```c++=
    int x; // global variable
    void func(){
        double x; // local variable
        // 若使用變數 x，會先找 local scope 再找 global scope
    }
    ```
    - 一般繼承下的函數
    ```c++=
    class Base {
        virtual void mf1() = 0;
        virtual void mf2();
        void mf3();
        // 以上三種行為一樣
    }
    class Derived: public Base {
        virtual void mf1();
        void mf4();
    }
    int main(){
        Derived d;
        // 會先找 derived class scope 再找 base class scope
        d.mf1(); // Derived::mf1
        d.mf2(); // Base::mf2
        d.mf3(); // Base::mf3
        d.mf4(); // Derived::mf4
    }
    ```
- `繼承 + 函數重載會造成名稱遮蓋問題`
    - 編譯器避免使用者定義 Derived Class 時誤用了 Base Class 中的重載函數
        ```c++=
        class Base {
            virtual void mf1() = 0;
            virtual void mf1(int);
            virtual void mf2();
        }
        class Derived: public Base {
            virtual void mf1(); // 名稱遮蓋 Base 中的重載函數
        }
        int main(){
            Derived d;
            d.mf1(); // Derived::mf1
            d.mf1(3); // Error! Base::mf1(int) 被名稱遮蓋
            d.mf2(); // Derived::mf2
        }
        ```
    - public 繼承下，使用 `using Base::` 解決名稱遮蓋問題，使用 Base Class 中的函數
        - public 繼承應滿足 is-a 關係，正常來說，應繼承所有 Base Class 中的重載函數
        ```c++=
        class Base {
            virtual void mf1() = 0;
            virtual void mf1(int);
            virtual void mf2();
        }
        class Derived: public Base {
            using Base::mf1; // 看見 Base class 內所有的 mf1
            virtual void mf1(); // 宣告 Derived::mf1()
        }
        int main(){
            Derived d;
            d.mf1(); // Derived::mf1
            d.mf1(3); // OK! Base::mf1
            d.mf2(); // Derived::mf2
        }
        ```
    - private 繼承下，如果只想使用 Base Class 中的函數，透過 `forwarding function`
        ```c++=
        class Base {
            virtual void mf1();
            virtual void mf1(int);
            virtual void mf2();
        }
        class Derived: private Base {
            virtual void mf1() { // forwarding function
                Base::mf1();
            }
        }
        int main(){
            Derived d;
            d.mf1(); // OK! Derived::mf1() 間接呼叫 Base::mf1()
            d.mf1(3); // Error! Base::mf1(int) 被名稱遮蓋
        }
        ```
#### 34 區分 interface 繼承和 implementation 繼承
- `interface 總是被繼承`
    - public 繼承應滿足 is-a 關係，所有對 base class 為真的事，對 derived class 也應為真
- `讓 derived class 只繼承 interface : pure virutal function`
    ```c++=
    class Shape {
        virtual void draw() const = 0; // pure virtual function
    }
    ```
- `讓 derived class 繼承 interface 和 default implementation`
    - 使用 `impure virtual function`，可能會讓 derived class 不小心繼承到不預期的 default implementation，比較不建議使用，以下提供兩種方式
    - 方法一 : 使用 `pure virtual function + protected function`
        ```c++=
        class Airplane {
        public:
            virtual void fly() const = 0; // pure virtual function
        protected:
            void defaultFly() {
                // default implementation
            };
        }
        class ModelA : public Airplane {
        public:
            void fly(){
                defaultFly();
            }
        }
        ```
    - 方法二 : 使用 `pure virtual function + pure function implementation`
        ```c++=
        class Airplane {
        public:
            virtual void fly() const = 0; // pure virtual function
        }
        void Airplane::defaultFly() {
            // default implementation of pure virtual function
        };
        class ModelA : public Airplane {
        public:
        public:
            void fly(){
                Airplane::fly();
            }
        }
        ```
- `讓 derived class 繼承 interface 和強制性的 implementation : non-virtual function`
    ```c++=
    class Shape {
    public:
        int objectID() const {
            if (_object_ID == 0){
                _object_ID = randInt();
            }
            return _object_ID;
        }
    }
    ```
#### 35 考慮 virtual function 以外的選擇
- (1) 使用 `Non Virtual Interface (NVI)` 實現 Template Method 設計模式
    - 將 public virtual 拆分為 public non-virtual + private virtual
    - 可以做一些 preprocess/postprocess
    - NVI 手法並不一定要讓 virtual 函數是 private
    ```c++=
    class Game Character {
    public:
        int healthValue() const {
            // preprocess
            int retVal = doHealthValue();
            // postprocess
        }
    private:
        virtual int doHealthValue() const {
            // ...
        }
    };
    ```
- (2) 使用 function pointer 實現 Strategy Method 設計模式
    ```c++=
    class EvilBadGuy: public GameCharacter {
    public:
    explicit EvilBadGuy(HealthCalcFunc hcf = defaultHealthCalc) 
                                        : GameCharacter(hcf) {}
    };

    int loseHealthQuickly(const GameCharacter&);
    int loseHealthSlowly(const GameCharacter&);
    // behavior
    EvilBadGuy ebg1(loseHealthQuickly);
    EvilBadGuy ebg2(loseHealthSlowly);
    ```
#### 36 絕不重新定義繼承而來的 non-virtual function
- `non-virtual function 是靜態綁定的`，繼承時不應該覆寫
    ```c++=
    class B {
    public:
        void mf();
    }
    class D : public B {
    public:
        void mf();
    }

    int main(){
        D x;
        B* pB = &x;
        pB->mf(); // B::mf() 靜態綁定

        D* pD = &x;
        pD->mf(); // D::mf() 靜態綁定
    }
    ```
- `virtual function 是動態綁定的`
    ```c++=
    class B {
    public:
        virtual void mf();
    }
    class D : public B {
    public:
        virtual void mf();
    }

    int main(){
        D x;
        B* pB = &x;
        pB->mf(); // D::mf() 動態綁定

        D* pD = &x;
        pD->mf(); // D::mf() 動態綁定
    }
    ```
#### 37 絕不重新定義繼承而來的 default parameter 值
- `default parameter 是靜態綁定的`，繼承時不應該覆寫
    ```c++=
    class Shape {
    public:
    virtual void draw(ShapeColor color = Red) const = 0;
    };
    
    class Circle Shape {
    public:
    virtual void draw(ShapeColor color = Green) const = 0;
    }
    
    int main(){
        Shape *pr = new Circle;
        pr->draw(); // Red !!!
    }
    ```
#### 38 透過 composition 表現 "has-a" 的模型或 "is implemented in terms of"
- composition 表現 "has-a"
    - 發生在應用域 application domain: people、car、canvas
    ```c++=
    class Person {
    private:
        string name;
        Address addr;
        PhoneNumber voiceNumber;
    }
    ```
- composition 表現 "is implemented in terms of"
    - 發生在實現域 implementation domain: buffer、mutex、search tree
    ```c++=
    template<class T>
    class Set {
    public:
        bool member(const T& item) const;
        void insert(const T& item);
        void remove(const T& item);
        size_t size() const;
    private:
        list<T> rep; // is implementation in terms of
    }
    ```
#### 39 private 繼承表示 "is implemented in terms of"
- private 繼承
    - 只是一種實現技術，所以 base class 每樣東西都成為 private，因為他們都只是實現細節
    - 在設計上沒有意義，意義只在實現層面
- ```盡可能使用 composition，必要時才使用 private 繼承```
    ```c++=
    class Widget {
    private:
        class Widget Timer: pubilc Timer {
        public:
            virtual void onTick() const;
        };
        WidgetTimer timer;
    }
    ```
    - 唯一建議使用 private 繼承的例外，`Empty Base Optimization(EBO)`
    ```c++=
    class Empty {}; // 可能有 typedefs, enums, static member 等等
    
    class HoldsAnInt { // 應該只需要一個 int 空間，但是 member 會被 c++ 多安插一個 char 空記憶體，然後還會補上 padding 空間(放大到可以存放一個 int)
    private:
        int x;
        Empty e;
    }
    class HoldsAnInt: private Empty { // 根據 EBO，sizeof(int) == sizeof(HoldsAnInt)
    private:
        int x;
    }
    ```
#### 40 審慎使用多重繼承
- public 繼承應該總是 virtual public 繼承，但會造成大小、速度、初始化複雜度等成本
- 多重繼承的一個情境: public 繼承某 interface + private 繼承某 implementation
### Chapter 7: 模板和泛型
#### 41 了解 implicit interface 和 compile-time polymorphism
- class : 顯式接口 (explicit interface)、運行期多型 (virtual function)
- template : 隱式接口 (implicit interface)、編譯期多型
#### 42 typename 的雙重意義
- 宣告 template 參數時，typename 和 class 可互換
    - 以下寫法等價
    ```c++=
    template <class T> class Widget1;
    template <typename T> class Widget2 {
    public:
        Widget2() {
            cout << "Widget<" << typeid(T).name() << "> created" << endl;
        }
    };
    int main() {
        Widget2<int> w1;
        Widget2<float> w2;
        return 0;
    }
    ```
- 在 template function 中的 `嵌套從屬名稱 dependent name` 需使用 typename
    - `從屬名稱 dependent name` : template 內出現相依於 template 參數的名稱
    - `嵌套從屬名稱 nested dependent name` : 從屬名稱於 class 中呈現嵌套狀
    - `非從屬名稱 non-dependent name` : 不依賴 template 參數的名稱
    ```c++=
    template <typename C>
    void f2(const C& c, typename C::iterator it1) { // C 不得使用 typename，it1 必須使用 typename
        typename C::const_iterator it2 = c.begin(); // it2 必須使用 typename
        for(; it2 != c.end(); ++it2) {
            cout << *it2 << endl;
        }
    }
    
    int main() {
        f2(string("hello"), string("hello").begin());
        return 0;
    }
    ```
- 在 `基類列 base class lists` 或 `成員初值列 member initial list` 內，不得使用 typename
    ```c++=
    template <typename C>
    class Base {
    public:
        class Nested {
        public:
            explicit Nested(int x) {
                cout << "Nested(" << x << ") created" << endl;
            }
        };
    };

    template <typename T>
    class Derived : public Base<T>::Nested { // 不得使用 typename
    public:
        explicit Derived(int x) : Base<T>::Nested(x) { // 不得使用 typename
            x++;
            typename Base<T>::Nested temp(x);
            cout << "Derived(" << x << ") created" << endl; // 必須使用 typename
        }
    };
    int main() {
        Derived<int> d(42);
        return 0;
    }
    ```
- 使用 `std::iterator_traits` 的常見用法
    ```c++=
    template <typename T>
    void f4(T t) {
        typename iterator_traits<T>::value_type temp = *t; // 必須使用 typename
        typedef typename iterator_traits<T>::value_type value_type; // typedef 和 typename 常常一起使用
        value_type temp2 = *t;
        cout << typeid(temp).name() << endl;
        cout << typeid(temp2).name() << endl;
    };

    int main() {
        std::vector<int> vec = {1, 2, 3};
        f4(vec.begin());
        return 0;
    }
    ```
#### 43 處理 scope to base class template
- derived class template 預設不會去找 base class template 裡的 member function
    - 因為在模板全特化的情況下 base class template，不一定存在那個 member function
    ```c++=
    template <typename T>
    class MsgSender {
    public:
        void sendClear() {
            cout << "MsgSender::sendClear" << endl;
        }
    };

    template <>              // MsgSender<int> is a full specialization
    class MsgSender<int> {}; // which has no member function sendClear

    template <typename T> // this will lead to compiler error, as it won't search in base class template
    class LoggingMsgSender : public MsgSender<T> {
    public:
        void sendClearMsg() {
            cout << "LoggingMsgSender::sendClearMsg" << endl;
            // sendClear();
        }
    };
    ```
- 有以下三種解決方法，進入 base class template 查找
    - 不管是哪一種方式，都表示向 compiler 承諾 base class template，一定存在那個 member function 的接口
    ```c++=
    template <typename T>   // method 1
    class LoggingMsgSender1 : public MsgSender<T> {
    public:
        void sendClearMsg() {
            cout << "LoggingMsgSender1::sendClearMsg" << endl;
            this->sendClear();
        }
    };

    template <typename T>   // method 2
    class LoggingMsgSender2 : public MsgSender<T> {
    public:
        using MsgSender<T>::sendClear;
        void sendClearMsg() {
            cout << "LoggingMsgSender2::sendClearMsg" << endl;
            sendClear();
        }
    };

    template <typename T>   // method 3
    class LoggingMsgSender3 : public MsgSender<T> {
    public:
        void sendClearMsg() {
            cout << "LoggingMsgSender3::sendClearMsg" << endl;
            MsgSender<T>::sendClear();
        }
    };
    ```
#### 44 將與參數無關的程式碼抽離 template
- 用心感受 template 被具現化多次時可能的重複
- `非類型模板參數 non type template parameters` 造成的程式碼膨脹，通常可以函數參數化和 class member 替換 template 參數來降低
    ```c++=
    // causing binary codes duplication
    template <typename T, size_t N>
    class SquareMatrix1 {
    public:
        void invert() {
            cout << "Implementation of invert " << typeid(T).name() << " " << N << endl;
        }
    private:
        T data[N * N];
    };


    // avoid binary codes duplication, but performance may be worse
    template <typename T>
    class SquareMatrixBase {
    protected:
        SquareMatrixBase(size_t n, T* pMem) : size(n), pData(pMem) {};
        void invert(size_t matrixSize) {
            cout << "Implementation of invert " << typeid(T).name() << endl;
        }
    private:
        size_t size;
        T* pData;
    };

    template <typename T, size_t N>
    class SquareMatrix2 : private SquareMatrixBase<T> {
    public:
        SquareMatrix2() : SquareMatrixBase<T>(N, data) {};
        void invert() {
            cout << "Wrapper of invert " << typeid(T).name() << " " << N << endl;
            SquareMatrixBase<T>::invert(N);
        }
    private:
        T data[N * N];
    };


    int main() {
        SquareMatrix1<int, 5> m1;
        SquareMatrix1<int, 10> m2;
        m1.invert();
        m2.invert();
        SquareMatrix2<int, 5> m3;
        SquareMatrix2<int, 10> m4;
        m3.invert();
        m4.invert();
        return 0;
    }
    ```
- `類型參數 type template parameters` 造成的程式碼膨脹，可透過共享完全相同的 binary representation 來降低

#### 45 使用 member function template
- 在 template class 必須使用 `member function template` 達成 `implicit conversion from derived class pointer to base class pointer`
    - 原本的 class 繼承關係支援 `implicit conversion`
    ```c++=
    class Top {};
    class Middle : public Top {};
    class Bottom : public Middle {};
    
    void f1(){ // OK, implicit conversion
        Top* pt1 = new Middle;
        Top* pt2 = new Bottom;
        const Top* pct1 = pt1;
    }
    ```
    - 以下的 template class 不支援 `implicit conversion`
    ```c++=
    template <typename T>
    class SmartPtr {
    public:
        explicit SmartPtr(T* realPtr) : ptr(realPtr) {}
    private:
        T* ptr;
    };

    void f2(){ // Compile error, don't know how to implict conversion
        SmartPtr<Top> sp1 = SmartPtr<Middle>(new Middle);
        SmartPtr<Top> sp2 = SmartPtr<Bottom>(new Bottom);
        SmartPtr<const Top> sp3 = sp1;
    }
    ```
    - 使用 `member function template` 支援 `implicit conversion`
    ```c++=
    template <typename T>
    class SmartPtr2 {
    public:
        explicit SmartPtr2(T* realPtr) : ptr(realPtr) {}
        template <typename U>
        SmartPtr2(const SmartPtr2<U>& other) : ptr(other.get()) {}
        T* get() const { return ptr; }
    private:
        T* ptr;
    };

    void f3(){
        // support implicit conversion from derived to base
        SmartPtr2<Top> sp1 = SmartPtr2<Middle>(new Middle);
        SmartPtr2<Top> sp2 = SmartPtr2<Bottom>(new Bottom);
        SmartPtr2<const Top> sp3 = sp1;

        // from base to derived will cause compile error
        SmartPtr2<Middle> sp4 = SmartPtr2<Top>(new Top);
    }
    ```
#### 46 使用 non-member function template
- 單純使用 template non-member function，沒有能力自動做類型轉換
    ```c++=
    template<typename T>
    class Rational {
    public:
        Rational(const T& n = 0, const T& d = 1) : num(n), den(d) {}
        T num, den;
    };

    template<typename T>
    Rational<T> operator*(Rational<T> &lhs, Rational<T> &rhs) {
        return Rational<T>(lhs.num * rhs.den, lhs.den * rhs.num);
    }

    void f() {
        Rational<int> r1(1, 2);
        Rational<int> result = r1 * 2;  // Error
        Rational<int> result2 = 2 * r1; // Error
    }
    ```
- 改法一：可以 compile 但不能 link，因為編譯器找到 declaration 但找不到 implementation
    ```c++=
    template<typename T>
    class Rational {
    public:
        Rational(const T& n = 0, const T& d = 1) : num(n), den(d) {}
        T num, den;
        friend const Rational<T> operator*(const Rational<T> &lhs, const Rational<T> &rhs);
    };

    template<typename T>
    const Rational<T> operator*(const Rational<T> &lhs, const Rational<T> &rhs) {
        return Rational<T>(lhs.num * rhs.den, lhs.den * rhs.num);
    }

    void f() {
        Rational<int> r1(1, 2);
        Rational<int> result = r1 * 2;
        Rational<int> result2 = 2 * r1;
    }
    ```
- 改法二：在 class 中使用 friend 宣告 non-member function
    ```c++=
    template<typename T>
    class Rational {
    public:
        Rational(const T& n = 0, const T& d = 1) : num(n), den(d) {}
        T num, den;
        friend const Rational<T> operator*(const Rational<T> &lhs, const Rational<T> &rhs) {
            return Rational<T>(lhs.num * rhs.den, lhs.den * rhs.num);
        }
    };
    
    void f() {
        Rational<int> r1(1, 2);
        Rational<int> result = r1 * 2;  // OK
        Rational<int> result2 = 2 * r1; // OK
    }
    ```
- 改法三 : 讓 friend 函數調用輔助函數
    ```c++=
    template<typename T> class Rational;

    template <typename T>
    const Rational<T> doMultiply(const Rational<T> &lhs, const Rational<T> &rhs) {
        return Rational<T>(lhs.num * rhs.num, lhs.den * rhs.den);
    }

    template<typename T>
    class Rational {
    public:
        Rational(const T& n = 0, const T& d = 1) : num(n), den(d) {}
        T num, den;
        friend const Rational<T> operator*(const Rational<T> &lhs, const Rational<T> &rhs) {
            return doMultiply(lhs, rhs);
        }
    };


    void f() {
        Rational<int> r1(1, 5);
        Rational<int> result = r1 * 6;  // OK
        Rational<int> result2 = 7 * r1; // OK
    }
    ```
#### 47 使用 traits class 表現 types 資訊
- 使用 `std::iterator_traits` 得到 type 資訊
    ```c++=
    #include <iterator>
    #include <typeinfo>
    #include <vector>
    #include <list>
    #include <iostream>

    using namespace std;

    namespace other{
        struct input_iterator_tag {};
        struct output_iterator_tag {};
        struct forward_iterator_tag : public input_iterator_tag {};
        struct bidirectional_iterator_tag : public forward_iterator_tag {};
        struct random_access_iterator_tag : public bidirectional_iterator_tag {};

        // check type at runtime
        template<typename IterT, typename destT>
        void advance1(IterT& it, destT d) {
            if(typeid(typename iterator_traits<IterT>::iterator_category) == typeid(random_access_iterator_tag)) {
                it += d;
            } else {
                if( d > 0 ) { while( d-- ) {++it;} }
                else { while( d++ ) {--it;} }
            }
        }

        // check type at compile time
        template<typename IterT, typename destT>
        void doAdvance(IterT& it, destT d, std::random_access_iterator_tag) {
            it += d;
        }
        template<typename IterT, typename destT>
        void doAdvance(IterT& it, destT d, std::input_iterator_tag) {
            if (d < 0)  throw "input iterator does not support negative advance";
            while(d--) { ++it; }
        }
        template<typename IterT, typename destT>
        void doAdvance(IterT& it, destT d, std::bidirectional_iterator_tag) {
            if( d > 0 ) { while( d-- ) {++it;} }
            else { while( d++ ) {--it;} }
        }
        template<typename IterT, typename destT>
        void advance2(IterT& it, destT d) {
            doAdvance(it, d, typename iterator_traits<IterT>::iterator_category());
        }
    }




    int main() {
        vector<int> v = {1, 2, 3, 4, 5, 6, 7};
        auto it = v.begin();
        other::advance1(it, 3); // OK
        cout << *it << endl;
        other::advance2(it, 2); // OK
        cout << *it << endl;

        // list<int>::iterator it2;
        // other::advance1(it2, 3); // compile error
        return 0;
    }
    ```
#### 48 認識 template metaprogramming
- 將工作由 runtime 移往 compile time，實現早期錯誤偵測和更高執行效率
    - 情境一: 確保度量單位正確
    - 情境二: 優化矩陣運算
    - 情境三: 生成客戶訂製的 design pattern
    ```c++=
    #include <iostream>

    using namespace std;

    template <unsigned n>
    struct Factorial {
        static const unsigned value = n * Factorial<n - 1>::value;
    };

    template <>
    struct Factorial<0> {
        static const unsigned value = 1;
    };

    int main() {
        cout << Factorial<5> :: value << endl;
        return 0;
    }
    ```
### Chapter 8: 客製化 new 和 delete
#### 49 了解 new-handler 的行為
#### 50 了解 new 和 delete 的合理替換時機
#### 51 編寫 new 和 delete 的慣例
#### 52 寫了 placement new 也要寫 placement delete

- 了解 new_handler 行為
    ```c++=
    // global 設定
    #include <iostream>
    #include <new>

    using namespace std;


    // example1 of using typedef of a function ==============================
    typedef void (*func)(int);
    func f;
    void example1(){
        f = [](int x) { cout << x << endl; };
        f(5);
    }


    // example2 of using std::set_new_handler ==============================
    void outOfMem1() {
        cerr << "Unable to satisfy request for memory1\n";
        abort();
    }
    void example2(){
        std::set_new_handler(outOfMem1);
        int *p = new int[1000000000000];
    }

    // example3 customize new handler
    class C3 {
    public:
        static void* operator new(size_t size) {
            cout << "Using operator new(size), size: " << size << endl;
            return ::operator new(size);
        }

        static void* operator new[](size_t size) {
            cout << "Using operator new[](size), size: " << size << endl;
            return ::operator new(size);
        }
    private:
        int i, j, k;
    };
    void example3() {
        C3 *p = new C3;
        delete p;
        C3 *p2 = new C3[10];
        delete[] p2;
    }

    // example4 of using new_handler with class ==============================
    class NewHandlerHolder {
    public:
        explicit NewHandlerHolder(std::new_handler nh) : handler(nh) {}
        ~NewHandlerHolder() { std::set_new_handler(handler); }
    private:
        std::new_handler handler;
        NewHandlerHolder(const NewHandlerHolder&); // prevent copying
        NewHandlerHolder& operator=(const NewHandlerHolder&); // prevent copying
    };

    class Widget {
    public:
        static std::new_handler set_new_handler(std::new_handler p) noexcept(false) {
            std::new_handler oldHandler = currentHandler;
            currentHandler = p;
            return oldHandler;
        }
        static void* operator new[](size_t size) {
            std::cout << "size: " << size << std::endl;
            NewHandlerHolder h(std::set_new_handler(currentHandler));
            return ::operator new(size);
        }
    private:
        static std::new_handler currentHandler;
    };
    std::new_handler Widget::currentHandler = 0; // initial to NULL

    void outOfMem4() {
        cerr << "Unable to satisfy request for memory4\n";
        abort();
    }

    void example4(){
        Widget::set_new_handler(outOfMem4);
        Widget *pw1 = new Widget[1000000000000];
    }

    // example5 of using new_handler with template of class ==============================
    template<typename T>
    class NewHandlerSupport {
    public:
        static std::new_handler set_new_handler(std::new_handler p) throw() {
            std::new_handler oldHandler = currentHandler;
            currentHandler = p;
            return oldHandler;
        }
        static void* operator new[](size_t size) {
            NewHandlerHolder h(std::set_new_handler(currentHandler));
            return ::operator new(size);
        }
    private:
        static std::new_handler currentHandler;
    };

    template<typename T>
    std::new_handler NewHandlerSupport<T>::currentHandler = 0; // initial to NULL

    class Widget5 : public NewHandlerSupport<Widget5> {
    public:
        static void outOfMem5() {
            cerr << "Unable to satisfy request for memory5\n";
            abort();
        }
    };

    void example5(){
        Widget5::set_new_handler(Widget5::outOfMem5);
        Widget5 *pw5 = new Widget5[1000000000000];
    }


    // example6 using nothrow new operator ==============================
    void example6(){
        try {
            int *p = new int[1000000000000];    // throw std::bad_alloc if failed
            if (p == nullptr) {
                cerr << "Get nullptr\n";
            }
        } catch (std::bad_alloc) {
            cerr << "Get std::bad_alloc\n";
        }

        try {
            int *p = new (std::nothrow) int[1000000000000]; // return nullptr if failed
            if (p == nullptr) {
                cerr << "Get nullptr\n";
            }
        } catch (std::bad_alloc) {
            cerr << "Get std::bad_alloc\n";
        }
    }

    // main function
    int main() {
        example5();
        return 0;
    }

    // g++ -std=c++11 49.cpp -o 49 && ./49
    ```
- 了解 new 和 delete 的合理替換時機
    - 檢測運用錯誤
    - 強化效能
    - 收集使用上統計資訊
- 實作自訂 new 和 delete 的規則
    - operator new 會被 derived class 繼承
- 寫了 placement new 也要寫 placement delete
### Chapter 9: 雜項討論
#### 53 不要輕忽編譯器警告
#### 54 熟悉 TR1 等標準程式庫
#### 55 熟悉 Boost 等程式庫



###### tags: `筆記`