---
# System prepended metadata

title: 技術｜C++｜C++ 繼承

---

# 技術｜C++ ｜C++ 繼承

## 繼承
- 目的：
    - 1.代碼重用
    - 2.表達“是某種”關係：繼承表現的是一種“is-a”關係
    - 3.實現多型（Polymorphism）
    - 4.擴展與定製
    - 5.提升可維護性
    - 注意`里氏替換原則`
- 存取權限表
    | 存取權限表 | `public` 繼承     | `protected` 繼承  | `private` 繼承  |
    |----------------|------------------|------------------|----------------|
    | `public` 成員  | `public`          | `protected`       | `private`      |
    | `protected` 成員| `protected`       | `protected`       | `private`      |
    | `private` 成員 | 無法存取          | 無法存取          | 無法存取        |
- static member 的繼承
    ```c++=
    class Base{
    public:
        static int x;
    };
    int Base::x = 0;
    class Derived : public Base {};

    
    int main() {
        Base a, b;
        Derived c, d;
        a.shared = 1; std::cout << a.shared << std::endl; // 1
        b.shared = 2; std::cout << a.shared << std::endl; // 2
        c.shared = 3; std::cout << a.shared << std::endl; // 3
        d.shared = 4; std::cout << a.shared << std::endl; // 4
        return 0;
    }
    ```
- 父類別和子類別的轉換
    ```c++=
    int main() {
        printf("hello\n");
        Derived *dp = new Derived;
        Base *bp = dp; // 可以

        Base *bp = new Base;
        Derived *dp = bp; // 不行

        Base *bp = new Derived;
        Derived *dp = bp; // 不行
        Derived *dp = dynamic_cast<Derived*>(bp);  // downcast

        Derived *dp = new Base; // 不行
    }
    ```
- 初始化一個子類的順序
    1. `父類構造函數`先執行
    2. 子類 data，按照`聲明的順序`初始化，無論初始化列表
    3. `子類構造函數`最後執行
- 繼承常見的用法
    1. `Base 的 destructor 必須宣告為 virtual`
        - dynamic binding 要能正確刪除動態記憶體
    2. `避免於 constructor 和 destructor 中呼叫 virtual function`
        - 物件可能處於建構或解構過程中的不完整狀態
    3. constructor，通常會在初始化陣列使用 Base 的 constructor
    4. move operator，通常會在第一行使用 Base 的 move operator
    5. destructor，不須特別呼叫 Base 的 destructor，會自動隱含
## 多重繼承
- 根據衍生串列 (derivation list)
    - constructor 順序由左而右
    - destructor 順序由右而左
    ```c++=
    #include <iostream>
    using namespace std;

    class Base1{
    public:
        Base1(){cout<<"Construct base1\n";}
        virtual void f1(){cout<<"base1\n";};
        ~Base1(){cout<<"Destruct base1\n";}
    };
    class Base2{
    public:
        Base2(){cout<<"Construct base2\n";}
        virtual void f2(){cout<<"base2\n";};
        ~Base2(){cout<<"Destruct base2\n";}
    };
    class Derived: public Base1, public Base2{
    public:
        Derived(){cout<<"Construct derived\n";}
        void f1(){cout<<"derivedf1\n";};
        void f2(){cout<<"derivedf2\n";};
        ~Derived(){cout<<"Destruct derived\n";}
    };

    int main(){
        Base1 *pB1 = new Derived();
        // Construct base1
        // Construct base2
        // Construct derived

        Derived *pD = dynamic_cast<Derived*>(pB1);
        Base2 *pB2 = dynamic_cast<Base2*>(pB1);

        pB1->f1();    // derivedf1
        pB2->f2();    // derivedf2
        pD->f1();    // derivedf1
        pD->f2();    // derivedf2

        delete pD;
        // Construct derived
        // Construct base2
        // Construct base1
        return 0;
    }
    ```
## 虛擬繼承
- 解決`菱形繼承問題（或稱鑽石繼承問題）`
- 避免共用的地方，在底層有多份複製
    ```c++=
    class A{};
    class B: virtual public A{};
    class C: virtual public A{};
    class Final: public B, public C{};

    int main(){
        Final d;
        return 0;
    }

    // 如果 B 和 C 是一般繼承，D 裡面的 A 會有兩份
    // 如果 B 和 C 是虛擬繼承，D 裡面的 A 共用一份
    ```
    - [虛擬繼承的邪惡](https://chhsia0.github.io/The-Evil-Within-Virtual-Inheritance/)
    - C++ 在初始化物件的時候，有下面幾個步驟：
        - 按 DFS 對每一個 virtual base class 初始化一次。
        - 依序初始化所有的 direct non-virtual base classes。
        - 依序初始化所有的 non-static 成員變數。
        - 執行當前的 constructor。
        ```c++=
        class A{
        public:
            A(const string& id = "foo") : pid(id) {}
            string pid;
        };

        class B : public virtual A {};

        class C : public B{
        public:
            C(const string& id) : A(id){}
        };

        class D : public C{
        public:
            D(const string& id) : C(id){}
        };

        int main() {
            cout << C("bar").pid << endl; // bar，先初始化虛擬部分 A，這時候 A(id) 有正確帶入 bar
            cout << D("bar").pid << endl; // foo，先初始化虛擬部分 A，這時候用 A 的 default constructor，後續不會初始化 A 第二次
            return 0;
        }
        ```
