# 技術|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; } ```
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up