C++進階--類的繼承
阿新 • • 發佈:2018-12-25
//############################################################################ /* * 公有,保護,私有繼承 */ class B { }; class D_priv : private B { }; //私有繼承 class D_prot : protected B { }; //保護繼承 class D_pub : public B { }; //公有繼承 /* 不同的繼承方法指定了派生類對基類不同的訪問控制權限 上述三個派生類: 1. 都不能訪問B中的私有成員. 2. D_pub繼承B中的公有成員為公有,繼承B中的保護成員為保護 3. D_priv繼承B中的公有保護成員為私有 4. D_prot繼承B中的公有保護成員為保護 轉換: 1. 任何人都可以將一個D_pub*轉換為B*. D_pub是B的特殊情況 2. D_priv的成員和友元可以將D_priv*轉成B* 3. D_prot的成員,友元及子女可以將D_prot*轉成B* 注意:只有公有繼承是is-a的關係 */ /* 詳細例項 */ class B { public: void f_pub() { cout << "f_pub is called.\n"; } protected: void f_prot() { cout << "f_prot is called.\n"; } private: void f_priv() { cout << "f_priv is called.\n"; } }; class D_pub : public B { //對於公有繼承 public: void f() { f_pub(); // OK. D_pub的公有成員函式 f_prot(); // OK. D_pub的保護成員函式 f_priv(); // Error. B的私有成員函式 } }; class D_priv : private B { //對於私有繼承 public: using B::f_pub; //使其在D_priv的作用域內可見 void f() { f_pub(); // OK. D_priv的私有成員函式 f_prot(); // OK. D_priv 的私有成員函式 f_priv(); // Error. B的私有成員函式 } }; int main() { D_pub D1; D1.f_pub(); // OK. f_pub()是D_pub的公有成員函式 D_priv D2; D2.f_pub(); // Error. f_pub()是D_priv的私有成員函式,增加using B::f_pub之後OK B* pB = &D1; // OK 可以轉換 pB = &D2; // Error 不能轉換 ... } //############################################################################ /* * 私有繼承: 類似於has-a關係,跟組合類似 */ class Ring { virtual tremble(); public: void tinkle() {...; tremble(); } }; /* 組合 */ class Dog { Ring m_ring; public: void tinkle() {m_ring.tinkle();} // 向前呼叫 }; /* 私有繼承*/ class Dog_Priv : private Ring { public: using Ring::tinkle; }; /* * 組合的優點: 比如可以有多個ring,ring可以切換 //通常情況下傾向於組合,更低耦合,更靈活 * 私有繼承的優點:更優雅的多型,比如 * * 在ring類中增加虛擬函式tremble(), 該函式在tinkle中被呼叫 */ /* * 公有繼承 => "is-a" 關係 * * 基類能做的任何事情,派生類需要也能做 */ //像以下類的設計就是不合適的 class Bird { public: void fly(); }; class Penguin : public Bird {}; Penguin p; p.fly(); // class flyableBird : public bird {}; // public: // void fly(); //penguin p; //p.fly(); // 看幾個例子 class Dog { public: void bark() { cout << "Whoof, I am just a dog.\n";}; }; class Yellowdog : public Dog{ public: void bark() { cout << "Whoof, I am a yellow dog.\n";}; }; int main() { Yellowdog* py = new Yellowdog(); py->bark(); Dog* pd = py; pd->bark(); } OUTPUT: Whoof, I am a yellow dog. Whoof, I am just a dog. /* * 結論:不要覆寫非虛擬函式 */ class Dog { public: void bark(int age) { cout << "I am " << age; } virtual void bark(string msg = "just a" ) { cout << "Whoof, I am " << msg << " dog." << endl; } }; class Yellowdog : public Dog { public: using Dog::bark; virtual void bark(string msg="a yellow" ) { cout << "Whoof, I am " << msg << " dog." << endl; } }; int main() { Yellowdog* py = new Yellowdog(); py->bark(5); } OUTPUT: Whoof, I am a yellow dog. Whoof, I am just a dog. /* * 不要重新定義虛擬函式的預設引數 * - 預設引數是靜態繫結的 * - 虛擬函式是動態繫結的 */ /* * 在類dog中增加如下函式: * virtual void bark(int age) { cout << "I am " << age << " years old"<< endl; } * in main(), * py->bark(5); // 編譯不過 * // 可以通過在yellowdog中加"using Dog::bark;"修復 為了保持is-a關係 */ // 防止意外覆寫,或者沒有覆寫,增加了override關鍵字 class Dog { public: virtual void bark() { cout << "I am just a dog.\n";}; void run(); }; class Yellowdog : public Dog{ public: virtual void barks() { cout << "I am a yellow dog.\n";}; //在舊標準中不會報錯,等出錯了除錯比較困難 }; // C++ 11 standard: class Yellowdog : public Dog{ public: virtual void barks() override; // 編譯錯誤:沒有覆寫的函式 virtual void bark() const override; // 編譯錯誤:沒有覆寫的函式 void run() override; // 壓根不是虛擬函式,錯誤 }; /* * 避免採坑: * 1. 類的精確定義 * 2. 不要覆寫非虛擬函式 * 3. 不要覆寫虛擬函式的預設引數 * 4. 強制繼承被遮蓋的函式 * 5. 小心函式覆寫時的錯字 */