---
# System prepended metadata

title: 技術｜C++｜C++ 筆記
tags: [技術]

---

# C++ 筆記


- [C++ 短字串最佳化（Short String Optimization）](https://tigercosmos.xyz/post/2022/06/c++/sso/)
- C++ `#define` 和 `#include` 觀察
    ```text=
    假設有多個 xxx.h 和 xxx.cpp 檔案，include 的階層如下
    a -> b -> c
           -> d
      -> e -> f
           -> g
           
    若在 c.h, d.h, f.h, g.h 當中有衝突的 macro，e.g. '#define cout cout<<"C: "'
    則最後 apply 到各個 xxx.cpp 的 macro
    (1) parent 會影響到 child
        - b 的 macro 不會套用在 e.cpp 上
    (2) 根據 child include parent header 的順序，apply 最後一個
        - a.cpp 的結果，根據 include b.h 和 e.h 的順序
          及對應的 include c.h, d.h 和 f.h, g.h 順序決定
    ```
- 常見 C++ QA
- **(1) C++ 和 C 語言有什麼區別？**
    - Object Oriented
    - Template
    - STL
- **(2) 物件導向 : 封裝、繼承、多型**
    - 封裝
        - 將細節隱藏，通過訪問修飾詞（public、private、protected）來實現
        - (ef22) 透過封裝，為所有可能的不同實現方式提供彈性，使用者只需重新編譯
    - 繼承
        - 擴展類的功能
            - base 類別的 private 成員不被繼承
            - public 繼承 -> 保持 base 類別成員的 public 和 protected 關係
            - protected 繼承 -> 繼承之後皆為 protected
            - private 繼承 -> 繼承之後皆為 private
        - 減少代碼重複，提高可維護性；但是過多的繼承可能導致類層次結構過於複雜
        - 繼承應該符合 "is-a" 原則
    - 多型
        - 不同的物件對象，提供統一的介面，但有不同行為
        - 動態多型 (Dynamic Binding 動態繫結)
            - 虛擬函數、純虛函數
            - Override 取代 base 的行為
            - `base class 的指針或引用呼叫虛擬函數時，執行時期判斷要調用哪一個版本`
        - 靜態多型
            - Static polymorphism 當中有用到，`編譯時期判斷要調用哪一個版本`
            ```c++=
            template<typename T> class Tool {
            public:
                void Plot() { static_cast<T*>(this)->PlotImpl(); }
                void PlotImpl() {puts("Default Tool plotting."); }
            private:    // 限定只有繼承的子類 T 可以使用這個 private constructor
                Tool() {}
                friend T;
            }

            template<typename T> void ToolHandler(Tool<T> &tool) {
                tool.Plot();
            }
            ```
    - 多載和多型不同
        - 函數重載（Function Overloading）: 同一個作用域中，多個同名函數，參數列表不同，呼叫時編譯會做 function match
        - 運算符重載（Operator Overloading）: 定義自訂類別的運算子行為
- **(3) 動態多態性，虛擬函數**
    - [C++中關於 virtual 的兩三事](https://medium.com/theskyisblue/c-%E4%B8%AD%E9%97%9C%E6%96%BC-virtual-%E7%9A%84%E5%85%A9%E4%B8%89%E4%BA%8B-1b4e2a2dc373)
    - `base class 的指針或引用呼叫虛擬函數時，編譯器動態判斷，決定要調用哪一個版本`
    - 虛擬函數
        - 派生類型可以選擇是否重寫這個函數
        - 如果沒有重寫，將調用 base class 中定義的版本
    - 純虛函數
        - 規範派生類型，必須實現這個函數
    ```c++=
    class Animal {
    public:
        virtual void Cry(){ cout<<"Animal Cry"<<endl;   }
        // (1) 當沒有宣告 virtual 時，f() 函數會印 Animal Cry
        // (2) 若宣告 virtual，f() 函數會在 執行期 判斷指向的對象決定
    };

    class Dog: public Animal{
    public:
        void Cry(){ cout<<"Dog Cry"<<endl;  } // 若 base 有加 virtual，繼承後也會自動補上關鍵字 virtual
    };

    void f(Animal &ptr){
        ptr.Cry();
    }
    ```
- **(4) C++ 中的 static 關鍵字**
    - global variable : 這個變數僅在當前檔案內可見
    - global function : 這個函數僅在當前檔案內可見
    - local variable : 在函式調用時只初始化一次，並保留其值
    - member variable : 所有物件共用的，與類別相關聯而不是物件
    - member function : 所有物件共用的，與類別相關聯而不是物件
- **(5) C++ 中的 STL**
    - 標準模板庫（Standard Template Library）
    - 提供了一系列常用的資料結構和算法，方便開發者快速開發高效、可靠的應用程序
    - 容器（Containers）
        - 循序容器（Sequential Containers）
            - vector：動態陣列，支持隨機存取和動態大小調整。
            - list：雙向鏈表，支持插入、刪除和遍歷。
            - deque：雙端隊列，支持快速隨機存取和動態大小調整。
        - 關聯式容器（Associative Containers） 對數級
            - set/multiset：紅黑樹，自動排序，支持插入、刪除和查詢操作。
            - map/multimap：紅黑樹，自動排序，支持鍵值對的插入、刪除和查詢操作。
    - 容器配接器(container adapters) : 建立在其他容器之上的，以提供特定的行為
        - stack：堆疊，LIFO 的資料結構，通常底層使用 vector 或 deque
        - queue：隊列，FIFO 的資料結構，通常底層使用 deque 或 list
        - priority_queue：優先隊列，根據優先級排序，通常底層使用 vector 或 deque
    - 無序容器（Unordered Containers） : C++11 加入
        - unordered_set/unordered_multiset ： 使用哈希表來實現
        - unordered_map/unordered_multimap ： 使用哈希表來實現
- **(6) RAII（Resource Acquisition Is Initialization）**
    - 一種管理資源的好方法，源於 C++
    - 動態分配的記憶體、文件描述符、鎖
    - 資源的申請和釋放應該與對象的生命週期綁定在一起，避免資源泄漏和多次釋放等問題
    ```c++=
    void foo() {
        std::unique_ptr<int> ptr(new int(10));  // 分配動態記憶體
        // ...
    }  // 離開函數時，std::unique_ptr 會自動釋放記憶體
    ```
- **(7) C++ 中的智能指針 : shared_ptr / unique_ptr / weak_ptr**
- **(8) C++ 的模板（template）**
    - 在不知道實際使用的資料型別或參數的情況下，編寫通用的程式碼，提高程式碼的重複利用性和可維護性
    - 編譯器會根據實際使用的資料型別或參數進行實例化
    - 類型模板 template class
        ```c++=
        template<typename T>
        class Stack {
        private:
            std::vector<T> stack_;
        public:
            void push(const T& item) { stack_.push_back(item); }
            void pop() { stack_.pop_back(); }
            T& top() { return stack_.back(); }
            bool empty() const { return stack_.empty(); }
            size_t size() const { return stack_.size(); }
        };
        ```
    - 函數模板 template function
        ```c++=
        template<typename T>
        T max(T a, T b) {
            return a > b ? a : b;
        }
        ```
- **(9) C++ 的常量和引用**
    - 字面值常量 : 特定字面值表示的常量，如 1、2、"hello" 等
    - 符號常量 : 使用 const 關鍵字聲明，如 const int MAX_SIZE = 100
    - 引用
        - 一個已存在變量的別名
        - 一種類型安全的指針，不需要使用 * 和 -> 運算符來訪問
        - 引用的初始化只能在聲明時進行，不能再進行修改
- **(10) C++ 中的移動語意（move semantics）。**
    - 通過將對象的所有權（ownership）從一個對象轉移到另一個對象，以提高程序的效率
    - 通過使用右值引用（rvalue references）實現
- **(11) 修改 pointer 指向的用法**
    ```c++=
    int B = 2;
    void f0(int *p){ p = &B; } // wrong
    void f1(int **p){ *p = &B; } // correct
    void f2(int *&p){ p = &B; } // correct
    
    int main(){
        int A = 1;
        int *ptrA = &A;

        // f0(ptrA);
        // f1(&ptrA);
        // f2(ptrA);
        printf("%d\n", *ptrA);
        return 0;
    }
    ```
- 解釋以下語法和關鍵字
    ```c++=
    int *const ptr = &num; // 不可以指向不同位置，但可以改值
    int const *ptr2 = &num; // 指向 const int，不可以改值
    const int *ptr3 = &num; // 指向 const int，不可以改值
    
    int (*ptr)[10];  // 一個指標，指向 int [10] 的位置
    int *p[10];      // 十個指標，指向不同 int 的位置
    cout<<sizeof(*ptr)<<endl; // 10 乘以整數的大小
    cout<<sizeof(p)<<endl; // 10 乘以指標的大小
    
    struct : 包含一些成員，這些成員可以同時存在於內存中
    union : 包含一些成員，這些成員共享內存
    volatile : 告訴編譯器變量可能會被意想不到地改變，避免過度優化（尤其在嵌入式系統）
    
    Macro : 在預處理時單純的文字替換
    Inline function : 在 compile 階段取代
    
    ```
- 考題
    - Hash table
        - 事先預留空間，透過 hash function 把資料定址
        - (1) [open addressing](http://alrightchiu.github.io/SecondRound/hash-tableopen-addressing.html)
        - (2) [chaining](http://alrightchiu.github.io/SecondRound/hash-tablechaining.html)
    - vector, array, list 差別
        - vector : 動態數組，底層空間連續，支持隨機訪問和快速的尾部插入和刪除，可以重新分配空間
        - array : 靜態數組，底層空間連續，元素個數在編譯時就已經確定了，元素個數變動時不方便，支持隨機訪問
        - list : 雙向鏈表，底層使用指針，支持快速的插入和刪除操作，不支持隨機訪問
    - strcat, strcpy
        - [strcat](https://skylinelimit.blogspot.com/2018/02/c-5.html) : 複雜度為 $O(n+m)$ 而非 $O(m)$
        - [strcpy](https://skylinelimit.blogspot.com/2018/02/c-2.html)

