1 / 35

第 15 章 繼承與多重繼承

第 15 章 繼承與多重繼承. 15-1 繼承的基礎 15-2 覆寫與隱藏父類別的成員 15-3 子類別的建構與解構子 15-4 多重繼承 15-5 軟體工程與繼承 15-6 類別的型態轉換與檢查. 15-1 繼承的基礎. 15-1-1 類別繼承的基礎 15-1-2 實作繼承 15-1-3 父類別的存取控制. 15-1-1 類別繼承的基礎 - 圖例. 「繼承」( Inheritance )是物件導向程式設計的一種進階觀念,繼承就是物件的再利用,當定義好一個類別後,其他類別可以繼承這個類別的成員資料和函數。

ivria
Download Presentation

第 15 章 繼承與多重繼承

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 第15章 繼承與多重繼承 • 15-1 繼承的基礎 • 15-2 覆寫與隱藏父類別的成員 • 15-3 子類別的建構與解構子 • 15-4 多重繼承 • 15-5 軟體工程與繼承 • 15-6 類別的型態轉換與檢查

  2. 15-1 繼承的基礎 • 15-1-1 類別繼承的基礎 • 15-1-2 實作繼承 • 15-1-3 父類別的存取控制

  3. 15-1-1 類別繼承的基礎-圖例 • 「繼承」(Inheritance)是物件導向程式設計的一種進階觀念,繼承就是物件的再利用,當定義好一個類別後,其他類別可以繼承這個類別的成員資料和函數。 • 類別繼承也是在模擬真實世界,例如:學生和老師都是人,我們可以先定義Person類別來模擬人類,然後擴充Person類別建立Student類別來模擬學生,如右圖所示:

  4. 15-1-1 類別繼承的基礎-圖例說明 • Person類別是Student類別的「父類別」(Superclass)或「基礎類別」(Base Class),反之Student類別是Person類別的「子類別」(Subclass)或「延伸類別」(Derived Class)。 • UML類別圖的繼承是使用空心的箭頭線來標示兩個類別間的關係。

  5. 15-1-1 類別繼承的基礎-類別架構 • 繼承不只可以多個子類別繼承同一個父類別,還可以擁有很多層的繼承,如果將整個類別關聯性(Relationships)的樹狀結構都繪出來,稱為「類別架構」(Class Hierarchy),如下圖所示:

  6. 15-1-1 類別繼承的基礎-兄弟類別 • Truck、Car和Motorcycle類別是「兄弟類別」(Sibling Classes),因為擁有相同Vehicle父類別。當然我們可以繼續繼承類別Car,類別SportsCar和Jeep也是類別Vehicle的子類別,不過並不是直接繼承的子類別。 • 簡單的說,Car類別是SportsCar和Jeep的直接父類別(Direct Base Class),Vehicle類別則是SportsCar和Jeep的間接父類別(Indirect Base Class)。

  7. 15-1-2 實作繼承-父類別的宣告 • 父類別vehicle定義車輛的基本資料,如下所示: class vehicle { private: int engineNo; string owner; public: void setNumber(int no) { engineNo = no; } void setOwner(string owner) { this->owner = owner; } void printVehicle() { } };

  8. 15-1-2 實作繼承-子類別的宣告(語法) • 類別如果是繼承自存在的其他類別,其宣告語法,如下所示: class 子類別名稱 : 存取修飾子 父類別名稱 { // 擴充的成員資料和函數 }; • 上述類別宣告使用「:」運算子後跟著父類別,表示擴充父類別的宣告,在父類別前方的存取修飾子可以定義繼承父類別的存取範圍,其值可以是private、protected和public,詳細說明請參閱下一節。

  9. 15-1-2 實作繼承-子類別的宣告(範例) • 因為車輛可以分成很多種,例如:卡車、機車和轎車等,以轎車car子類別為例的類別宣告,如下所示: class car : public vehicle { private: int doors; public: car(string owner, int no, int doors) { } void printCar() { } };

  10. 15-1-2 實作繼承-UML類別圖 • UML類別圖,如下圖所示:

  11. 15-1-2 實作繼承-繼承的成員種類 • 在C++語言宣告的子類別可以繼承父類別的所有成員資料和函數,但是並不包含: • 父類別的建構子和解構子。 • 父類別的朋友關係。 • 父類別的指定運算子=。

  12. 15-1-3 父類別的存取控制-說明 • 在C++語言的子類別使用哪一種存取控制來繼承父類別,將影響成員的存取範圍,如下所示: 父類別名稱 { private: …… protected: …… public: …… }; class 子類別名稱 : 存取修飾子 父類別名稱 { …… };

  13. 15-1-3 父類別的存取控制-父類別的存取控制 • 在子類別是使用存取修飾子private、protected和public來繼承父類別。父類別各種存取控制的說明,如下表所示:

  14. 15-1-3 父類別的存取控制-子類別的存取控制 • 子類別是否能夠存取父類別指定區塊的成員函數和資料,需視它屬於類別的public、protected和private成員而定,筆者整理如下表所示:

  15. 15-2 覆寫與隱藏父類別的成員 • 15-2-1 覆寫父類別的成員函數 • 15-2-2 隱藏父類別的成員資料

  16. 15-2-1 覆寫父類別的成員函數-說明 • 在父類別的成員函數如果不符合需求,子類別可以宣告同名、同參數列和傳回值的函數來取代父類別的成員函數,稱為「覆寫」(Override)。

  17. 15-2-1 覆寫父類別的成員函數-父類別 • 在父類別vehicle擁有一個靜態成員函數和成員函數需要覆寫,如下所示: class vehicle { private: ……… public: static void showBrand() { } ……… void printVehicle(int index) { } }; • 上述showBrand()和printVehicle()是需要覆寫的成員函數。

  18. 15-2-1 覆寫父類別的成員函數-子類別 • 子類別car使用public繼承父類別vehicle,如下所示: class car : public vehicle { private: ……… public: ……… static void showBrand() { } void printVehicle(int no) { } }; • 在程式碼呼叫car物件的成員函數時,就是呼叫子類別car的函數,而不是父類別的同名函數。

  19. 15-2-2 隱藏父類別的成員資料-說明 • 除了父類別的成員函數外,子類別也可以隱藏父類別成員資料的變數,只需變數名稱相同,就算變數型態不同也一樣可以隱藏。

  20. 15-2-2 隱藏父類別的成員資料-父類別 • 例如:父類別vehicle的成員變數owner是宣告在public區塊的string字串型態,如下所示: class vehicle { private: ……… public: string owner; ……… };

  21. 15-2-2 隱藏父類別的成員資料-子類別 • 在子類別car使用public存取修飾子繼承父類別vehicle,如下所示: class car : public vehicle { private: int owner; ……… public: ……… }; • car物件的成員變數owner是整數int,不再是string字串,原來父類別public區塊的owner成員變數被隱藏起來。

  22. 15-3 子類別的建構與解構子 • 15-3-1 子類別的建構與解構順序 • 15-3-2 子類別傳遞參數給父類別

  23. 15-3-1 子類別的建構與解構順序 • 當建立子類別的物件呼叫子類別的建構子時,它會先初始化父類別的成員,也就是呼叫父類別的建構子。如果子類別沒有建構子,在建立物件時,預設建構子(Default Constructor)就會呼叫父類別的預設建構子。 • 簡單的說,在呼叫子類別的建構子前,會先呼叫父類別的建構子,而解構子剛好與建構子是相反順序,也就是子類別的建構子是在父類別的建構子之前呼叫。

  24. 15-3-2 子類別傳遞參數給父類別 • 當父類別擁有建構子時,在子類別可以傳遞參數給父類別的建構子。例如:car類別是繼承自vehicle,此時car子類別的建構子,如下所示: car(int owner, int no, int doors) : vehicle(owner, no) { this->doors = doors; } • 上述建構子的「:」運算子後是傳遞給父類別vehicle建構子的參數,如果父類別不只一個,請使用「,」號分隔。 • 其中傳遞給父類別參數的值,就是傳入子類別建構子的參數值,以此例,owner和no也是子類別建構子的參數。

  25. 15-4 多重繼承-說明 • 「多重繼承」(Multiple Inheritance)是指一個類別能夠繼承多個父類別,如下圖所示:

  26. 15-4 多重繼承-語法 • 多重繼承。其宣告語法如下所示: class 子類別名稱 : 存取修飾子 父類別名稱, 存取修飾子 父類別名稱….. { …… // 額外的成員資料和函數 }; • 上述類別和繼承類別的語法相似,因為繼承類別不只一個,需要使用「,」逗號來分隔。

  27. 15-4 多重繼承-範例 • 例如:繼承自truck和driver類別的子類別driven_tuck,如下所示: class driven_truck : public truck, public driver { public: driven_truck(float pl, float fl, float pay, int pd): truck(pl, fl), driver( pay, pd) { } float costPerTom( float cost_of_gas ) { } void printData() { } };

  28. 15-5 軟體工程與繼承-圖例 • 軟體工程的繼承是類別關聯性(Relationships),它是指不同類別間的關係。例如:繼承是Is-a類別關聯性,稱為一般關係(Gereralization)。成品和零件(Whole-Part)的類別關聯性,即Part-of和Has-a關係,如下圖所示:

  29. 15-5 軟體工程與繼承-圖例說明 • Part-of和Has-a關係的說明,如下所示: • Part-of關係:指此類別是其他類別的零件,以上圖為例Wheel車輪和Door車門是Car車類別的零件。 • Has-a關係:相反於Part-of關係,Car類別Has-a擁有Wheel和Door類別。 • 在UML的上述關係稱為「聚合關係」(Aggregation),或是另一種更強調Whole-part關係稱為「組成關係」(Composition)。

  30. 15-6 類別的型態轉換與檢查-說明 • 在ANSI-C++定義reinterpret_cast、static_cast、dynamic_cast和const_cast四種新的型態轉換運算子,可以使用在類別型態的轉換,其基本語法如下所示: static_cast<類別*>(運算式); const_cast<類別*>(運算式); dynamic_cast<類別*>(運算式); reinterpret_cast<類別*>(運算式); • ANSI-C++還定義全新typeid運算子,可以檢查指定運算式或類別的型態,其語法如下所示: typeid(運算式)

  31. 15-6 類別的型態轉換與檢查-static_cast運算子 • C++的static_cast運算子可以將類別型態指標轉換成其他類別型態的指標,只需是編譯程式能夠自動轉型的型態迫換,都可以使用static_cast運算子來明確表示所需的型態轉換,如下所示: class A {}; class B: public A {}; A *a = new A; B *b = static_cast<B*>(a); • 上述程式碼使用static_cast<B*>運算子,將父類別A物件指標a,型態轉換成子類別B的物件指標b。

  32. 15-6 類別的型態轉換與檢查-const_cast運算子 • C++的const_cast運算子可以取消常數物件指標成為一般物件指標,如下所示: class C {}; const C *c1 = new C; C *c2 = const_cast<C*>(c1); • 上述程式碼使用const_cast<C*>運算子,將常數物件指標c1型態轉換成非常數物件指標c2。

  33. 15-6 類別的型態轉換與檢查-dynamic_cast運算子 • C++的dynamic_cast運算子主要是在類別架構中,安全的進行型態轉換,static_cast和dynamic_cast運算子的差異,在於dynamic_cast會進行檢查,這是在執行期檢查型態轉換的指標是否指向合法的需求型態,如果不是,就傳回NULL指標,如下所示: class D { virtual void dummy() {}; }; class E: public D {}; D *d1 = new E; D *d2 = new D; E *e1 = dynamic_cast<E*>(d1); E *e2 = dynamic_cast<E*>(d2); // 傳回NULL指標

  34. 15-6 類別的型態轉換與檢查-reinterpret_cast運算子 • C++的reinterpret_cast運算子可以在完全無關的類別型態間進行轉換,其操作是二進位複製,從一個指標複製至另一個指標,如下所示: class F {}; class G {}; F *f = new F; G *g = reinterpret_cast<G*>(f); • 上述程式碼使用reinterpret_cast<G*>運算子,將類別F物件指標f,型態轉換成類別G的物件指標g。

  35. 15-6 類別的型態轉換與檢查-typeid運算子 • C++的typeid運算子可以在執行期傳回運算式的型態資訊,我們可以使用關係運算子「==」和「!=」來比較其傳回值,如下所示: class H {}; H *h1; H h2; if ( typeid(h1) != typeid(h2) ) { cout << "h1與h2型態不同\n"; } • 上述程式碼使用typeid運算子檢查物件變數h2和物件指標變數h1是否是相同型態。

More Related