Presentation is loading. Please wait.

Presentation is loading. Please wait.

第十一章 繼承類別和朋友關係.

Similar presentations


Presentation on theme: "第十一章 繼承類別和朋友關係."— Presentation transcript:

1 第十一章 繼承類別和朋友關係

2 本章學習目標: 兩個類別產生繼承後,基底類別和衍生類別之間的屬性要處理的步驟為何!
說明基底類別和衍生類別之間的成員函數,具有新增、取代和擴充的情形。 介紹朋友函數和朋友類別。 說明多重繼承機制,利用虛擬繼承來管理類別架構。 繼承的另一種模式—組合:將物件作為其他物件的物件。 如何讓開發的程式碼提高再用性,本章節介紹了另一種物件導向技術—繼承,產生繼承的兩個類別,子類別如何呼叫父類別的成員來使用!類別之間如何建立朋友關係!物件的組合關係又是如何產生的!查辦將帶領讀者一一探討:

3 章節目錄 11-1 瞭解繼承 11-2 類別的繼承 11-3 成員與繼承的關係 11-4 朋友關係 11-5 多重繼承機制
繼承的相關名詞 為什麼要使用繼承 11-1 瞭解繼承 繼承的等級 private存取範圍 protected存取範圍 public存取範圍 11-2 類別的繼承 類別的屬性關係 類別的成員函數 建構式的呼叫 11-3 成員與繼承的關係 定義朋友關係 朋友類別 朋友函數 朋友的繼承 11-4 朋友關係 認識類別的多重繼承 使用虛擬繼承 11-5 多重繼承機制 11-6 組合模式

4 章節目錄 11-2 類別的繼承 11-3 成員與繼承的關係 11-4 朋友關係 11-5 多重繼承機制 11-6 組合模式
繼承的等級 private存取範圍 protected存取範圍 public存取範圍 11-2 類別的繼承 類別的屬性關係 類別的成員函數 建構式的呼叫 11-3 成員與繼承的關係 定義朋友關係 朋友類別 朋友函數 朋友的繼承 11-4 朋友關係 認識類別的多重繼承 使用虛擬繼承 11-5 多重繼承機制 11-6 組合模式 繼承的相關名詞 為什麼要使用繼承 11-1 瞭解繼承

5 章節目錄 11-1 瞭解繼承 繼承的相關名詞 為什麼要使用繼承

6 章節目錄 11-1 瞭解繼承

7 11-1 瞭解繼承 繼承(Inheritance,請參考9-3)是表示類別之間產生繼承關係時,子類別會擁有父類別的屬性和方法。
在未進入繼承之前,先瞭解繼承的相關名詞。

8 章節目錄 11-1 瞭解繼承 繼承的相關名詞

9 11-1-1 繼承的相關名詞 一般來說,衍生類別除了繼承基底類別所定義的資料成員和成員函數外,還能自行定義本身使用的資料成員和成員函數。
父類別 (Super class) 也稱「基底類別」(Base Class),表示它是一個被繼承的類別。 子類別 (Sub class) 也稱「衍生類別」(Derived Class),表示它是一個繼承他人的類別。 類別階層 (Class Hierarchy) 產生繼承關係後所形成的繼承架構。 一般來說,衍生類別除了繼承基底類別所定義的資料成員和成員函數外,還能自行定義本身使用的資料成員和成員函數。 從OOP觀點來看,在類別架構下 層次愈低的衍生類別,「特化」(Specialization)的作用就會愈強 同樣地,基底類別的層次愈高,表示「通化」(Generalization)的作用也會愈高

10 章節目錄 11-1 瞭解繼承 繼承的相關名詞 為什麼要使用繼承

11 類別(class)語法解說: 站在程式碼使用的觀點來看,繼承提供了軟體的「再使用性」(reuse)。
當我們撰寫一個運作較為複雜的系統,如果以物件導向技術來處理,使用繼承至少有兩個優點: 減少系統 開發的時間 讓系統在開發過程,利用模組化概念,加入繼承的做法,讓物件能集中管理。由於程式碼能夠重複使用,不但能縮短開發過程,爾後的維護也較為方便。 擴充系統 更為簡單 新的軟體模組可以透過繼承而建置在現存的軟體模組上。當我們宣告新的類別時,可從現有類別的方法,重覆定義,達到共享程式碼和軟體架構的目的。

12 章節目錄 11-1 瞭解繼承 11-2 類別的繼承 11-1-1 繼承的相關名詞 11-2-1 繼承的等級 11-1-2 為什麼要使用繼承
private存取範圍 protected存取範圍 public存取範圍 11-2 類別的繼承

13 章節目錄 11-2 類別的繼承

14 11-2類別的繼承 宣告類別時,C++將存取權限分為三個等級,當我們實作繼承時,這三個存取等級也會影響繼承的結果
本節中就先探討存取權限對繼承的影響

15 章節目錄 11-2 類別的繼承 類別的等級

16 11-2-1 繼承的等級 先瞭解繼承的方式,語法如下: 此處繼承存取權限用來設定衍生類別應如何繼承基底類別的成員,存取權限包含三種:
public(公開) protected(保護) private(私有)

17 衍生類別無法繼承的情形. 如果繼承的過程中並未指定存取權限,系統預設為「public」;值得一提的是,下列四種情形是衍生類別無法繼承:
基底類別的建構式和解構式。 基底類別的朋友類別與朋友函數。 基底類別的private資料成員與成員函數。 基底類別的Operator assignment運算子。

18 衍生類別無法繼承的情形. 建構式與解構式就如同是基底類別的「生」與「死」,理所當然,它們都不能被繼承
而基底類別的朋友就如同現實生活中,我們的遺傳特性不太可能和父親的朋友有關 而private與Operator assignment是基底類別私有的特質,也不可能被繼承 瞭解繼承的限制與原理後,我們針對繼承的存取權限做進一步探討。

19 章節目錄 11-2 類別的繼承 類別的等級 private存取範圍

20 11-2-2 private存取範圍 private代表的是一個私有的存取範圍,語法如下:
當繼承存取權限為private,表示基底類別的private成員無法被衍生類別繼承;基底類別為public或protected的成員才能在衍生類別中使用。 衍生類別的存取權限若為private,表示衍生類別外的任何物件都無法進行存取。以下表11-1來表示: 基底類別存取權限 衍生類別存取權限 public private protected 無法繼承

21 範例:SubPrivate.vcproj(1)

22 範例:SubPrivate.vcproj(2)

23 執行程式:SubPrivate.vcproj
執行時會顯示「SubPrivate.cpp(38) : error C2247: error C2247: ‘People::display’ 無法存取,因為‘Hobbit’ 類別使用‘private’ 來繼承‘People’ 類別」。

24 程式解說:SubPrivate.vcproj
第6~11行宣告了父類別People,其中的成員函數display()存取權限為public,而資料成員height為protected。 第18~23行宣告繼承People類別的Hobbit類別,也宣告了本身的成員函數show()和資料成員hair。 由於Hobbit繼承People類別的成員函數和資料成員,因此第36行的flores物件應該可以使用父類別的display()成員函數。事實不然,當我們將繼承的存取權限設成private,表示Hobbit類別繼承的父類別成員都變成了private。當主程式中子類別flores物件去呼叫父類別的成員函數display()時就會發生錯誤! 第25~30行定義子類別的成員函數show(),因為是在繼承的範例內,所以第26行能直接設定父類別People屬性height!

25 使用父類別的函數 如果想要使用父類別的成員函數display()時,要如何修改?
最簡單的方式就是將第38行先予以註解再來編譯程式,就能正確執行。 第二個方式就是讓刪除第38行的程式碼,將display()成員函數擺放在子類別能存取的範例內:

26 範例:SubPrivate.vcproj修正

27 章節目錄 11-2 類別的繼承 protected存取範圍 類別的等級 private存取範圍

28 11-2-3 protected存取範圍 protected的存取權限為保護,表示繼承的類別為其存取範圍,語法如下:
基底類別存取權限 衍生類別存取權限 public protected private 無法繼承

29 Protected存取權限 我們將上一個範例「SubPrivate.vcproj」的第18行更改為protected存取權限,若在主程式透過flores物件呼叫父類別的成員函數還是會發生錯誤! 表示子類別繼承的父類別成員,也只能在子類別的範圍內存取。

30 章節目錄 11-2 類別的繼承 11-2-3 protected存取範圍 11-2-1 類別的等級 11-2-4 public存取範圍
private存取範圍

31 11-2-4 public存取範圍 public表示任何類別皆能存取,語法如下:
基底類別存取權限 衍生類別存取權限 public protected private 無法繼承 延續上一個範例「SubPrivate.vcproj」,將類別更改為public存取權限,進行程式碼修改如下。

32 範例:SubPublic.vcproj(1)

33 範例:SubPublic.vcproj (2)

34 執行程式:SubPublic.vcproj
第13~16行定義display()成員函數,將接收的參數值指派給父類別People的資料成員height。因為繼承的存取權限為public,表示在主程式中也能呼叫父類別的成員來使用。 第25~28行定義show()成員函數,只需輸出Hobbit類別中的資料成員hair。 主程式中,第36行flores物件可以呼叫父類別的成員函數display()進行參數傳遞,也能呼叫本身的成員函數,因此輸出結果和上一個範例「SubPrivate.vcproj」相同。

35 章節目錄 11-1 瞭解繼承 11-2 類別的繼承 11-3 成員與繼承的關係 11-1-1 繼承的相關名詞 11-2-1 繼承的等級
為什麼要使用繼承 11-1 瞭解繼承 繼承的等級 private存取範圍 protected存取範圍 public存取範圍 11-2 類別的繼承 類別的屬性關係 類別的成員函數 建構式的呼叫 11-3 成員與繼承的關係

36 章節目錄 11-3 成員與繼承的關係

37 11-3 成員與繼承的關係 在繼承的程式碼中,我們不難發現,衍生類別的屬性一部份來自於基底類別,另一部份是本身自行定義。
而衍生類別的成員函數也具有相同的情形,如果衍生類別的成員函數名稱和基底類別成員函數名稱相同時,會發生什麼情形? 讓我們利用本節內容來釐清一些觀念!

38 章節目錄 11- 3 成員與繼承的關係 類別的屬性關係

39 11-3-1 類別的屬性關係 當衍生類別的資料成員為自行定義,表示父類別和子類別的屬性是一種增加的關係。
當衍生類別的資料成員和基底類別的資料成員名稱一樣時,表示兩個類別之間具有修正的關係。 延續上一個範例,如果我們將People和Hobbit類別的屬性設為相同,並將Hobbit類別的屬性重新定義,執行時會如何?如範例「AttriWrite.vcproj」所示。

40 範例:AttriWrite.vcproj (1)

41 範例:AttriWrite.vcproj(2)

42 執行程式:AttriWrite.vcproj
雖然子類別繼承了父類別的成員,卻是子類別的有效範圍涵蓋了父類別的有效範圍。當我們在show()成員函數中重新定義了height,編譯器會讀取子類別的有效範圍而非父類別,執行時就只會輸出show()成員函數的輸出結果。

43 程式解說:AttriWrite.vcproj
第6~12行宣告父類別People,第18行宣告了子類別Hobbit,兩個類別各有一個名稱相同的資料成員height。 第26~29行定義了子類別成員函數show(),在27行重新設定了height的屬性值,而主程式中,第34行則利用flores物件呼叫父類別的成員函數,並傳遞參數值170;在35行呼叫本身定義的成員函數show()。 由執行結果得知,只輸出子類別物件呼叫show()成員函數的結果,而非父類別display()成員函數傳遞結果,這是為什麼?其實這和變數的有效範圍(scope)有關。

44 修改範例:AttriWrite.cpp(部份程式)
如果要存取的對象是基底類別的屬性,可利用範圍解析運算子,因此我們可以將上一個範例改寫如下:

45 執行程式:AttriWrite.vcproj
在子類別的show()成員函數中,利用範圍解析運算子來取得父類別People屬性。因此輸出結果時不但可以看到父類別的屬性,也能輸出子類別的屬性。

46 章節目錄 11- 3 成員與繼承的關係 類別與成員函數 類別的屬性關係

47 11-3-2 類別與成員函數 如果子類別和父類別的成員函數同名稱時,將會產生什麼結果?
一般來說,成員函數和類別之間的關係就較為複雜,共歸納成三種情形,分別為 「增加」 「取代」 「擴充」 接下來,我們分別解說這三種情形。

48 子類別除了可以擁有父類別的成員函數display()之外,也能自行定義本身的成員函數show()。
增加功能 我們曾經說過衍生類別具有特化的特性,因此衍生類別可利用成員函數來執行特殊的功能,增加基礎類別所沒有的資料成員,敘述如下: 子類別除了可以擁有父類別的成員函數display()之外,也能自行定義本身的成員函數show()。

49 取代功能 取代功能就是衍生類別將繼承後的成員函數重新定義。在很多情形之下,繼承後的資料成員對於衍生類別來說,並不合用,必須重新定義成員函數的內容,敘述如下:

50 擴充功能 以上這些敘述,處理程式碼方式都是替舊換新,不過這樣的處理方式已經不符合程式碼的「再用性」,因此有擴充功能的觀念出現。
簡單的說,我們在執行新的程式碼之前,我們先執行舊有的程式碼,來達到程式再利用的理念。透過範例來說明:

51 範例:ExtendFunc.vcproj(1)

52 範例:ExtendFunc.vcproj(2)

53 範例:ExtendFunc.vcproj(3)

54 執行程式:ExtendFunc.vcproj
第6~13行宣告了父類別People,第21~25行宣告了子類別Hobbit。 第27~34行定義子類別成員函數display(),第29行利用範圍解析運算子呼叫父類別的成員函數display(),然後擴充本身的功能,在30~33行,以if/else敘述來判斷接受的參數值是否有小於120。 第42行flores物件傳入115參數值,表示小於120,所以顯示「哈比人身高 = 115公分」結果於畫面上。

55 編譯器如何執行函數?

56 章節目錄 11- 3 成員與繼承的關係 建構式的呼叫 類別與成員函數 類別的屬性關係

57 11-3-3 建構式的呼叫 類別之間可以產生繼承機制,但是建構式卻是各自獨立
無論是基底類別或是衍生類別都有自己的建構式,用來初始化該類別的物件 因為它與物件本身的生命週期有極密切關係,主宰物件的生與死,所以不會產生繼承機制 如何在繼承機制下使用建構式呢!先看底下的範例「DeriveCont.vcproj」。

58 範例:DeriveCont.vcproj(1)

59 範例:DeriveCont.vcproj(2)

60 執行程式:DeriveCont.vcproj
第7~16行宣告了父類別Car,並在第24行定義父類別的建構式。第26~30行宣告了子類別Roadster子類別,並在31行定義子類別的建構式。 主程式中,第34行宣告子類別物件Teana,然後在36和37行呼叫父類別的成員函數setColor()和getColor()。 由執行結果得知,Teana物件呼叫父類別的成員函數時,會先呼叫父類別的建構式,再呼叫本身的建構式;這說明產生子類別物件時,會先建立父類別物件。

61 基底類別的建構式 若要在子類別中呼叫父類別的建構式,是否也可在子類別建構式中呼叫父類別的建構式,利用「起始值設定序列」方式來取得父類別建構式的回傳值。 請參考範例「BaseCont.vcproj」的實作。

62 範例:BaseCont.vcproj(1)

63 範例:BaseCont.vcproj(2)

64 範例:BaseCont.vcproj(3)

65 執行程式:BaseCont.vcproj 延續上一個範例「DeriveCont.cpp」,然後在第19~22行新定義一個含有參數值的父類別建構式。 第34~38行定義子類別建構式,並在第34行以「起始值設定序列」方式來呼叫父類別建構式來取得回傳值。 主程式定義兩個物件:第45行不含參數的Teana物件,第47行含有參數的March物件;執行時,Teana物件會去呼叫不含參數的父、子類別建構式;而March物件則會去呼叫含有參數的父、子類別建構式。

66 未呼叫父類別的建構式時 或許大家會這樣想: 如果範例中第34行不去呼叫父類別的建構式,會產生怎樣的執行結果?
March物件就不會去呼叫含有參數的父類別建構式。 第二個問題: 為什麼不可以在34行利用範圍解析運算子去呼叫父類別的建構式?這在編譯過程就會發生錯誤,顯示「error C2143: 語法錯誤」!因為不可以把建構式視為一般成員函數來進行擴充功能。

67 建構式、解構式的呼叫順序 如果建立一個簡單的類別階層 執行時建構式和解構式的執行順序又是如何?
Car為基底類別 Roadster繼承Car類別 Z305繼承Roadster類別 執行時建構式和解構式的執行順序又是如何? 我們利用範例「Destructor.vcproj」來說明。

68 範例:Destructor.vcproj(1)

69 範例:Destructor.vcproj(2)

70 範例:Destructor.vcproj(3)

71 範例:Destructor.vcproj(4)

72 執行程式:Destructor.vcproj

73 程式解說:Destrouctor.vcproj
延續上一個範例架構,第7~14行宣告基底類別Car,第23~29行Roadster為Car衍生類別,第43~49行Z305繼承了Roadster類別。 主程式中,第65行建立Car類別的物件Teana,不含參數值;第67行的TiiDa物件為Z305類別的實體。執行時發現:建構式會先呼叫層次愈高的類別,所以Car類別建構式會先呼叫,再呼叫Roadster類別的建構式,最後才會Z305建構式;而解構式恰好相反,會從層次最低的解構式先呼叫,所以子子類別的Z305的解構式最先執行,依序往上呼叫;最早建立的Teana物件,其解構式則是最後呼叫。

74 章節目錄 11-1 瞭解繼承 11-2 類別的繼承 11-3 成員與繼承的關係 11-4 朋友關係 11-1-1 繼承的相關名詞
為什麼要使用繼承 11-1 瞭解繼承 繼承的等級 private存取範圍 protected存取範圍 public存取範圍 11-2 類別的繼承 類別的屬性關係 類別的成員函數 建構式的呼叫 11-3 成員與繼承的關係 定義朋友關係 朋友類別 朋友函數 朋友的繼承 11-4 朋友關係

75 章節目錄 11-4 朋友關係

76 11-4 朋友關係 我們討論繼承時,曾經談到類別與類別的繼承方式,除了private之外,其它的函數成員及資料成員都會被衍生類別所繼承,而朋友關係則打破繼承的存取關係。 不管是類別或是函數,只要被視為朋友關係,類別的所有成員都能任意存取使用。 這樣看來,與我們先前探討的資料保護不就互相抵觸了嗎?其實不然,因為朋友函數或朋友類別最主要不是用於繼承關係,也沒有涵蓋在繼承範圍內。 就像我們會把物品借給好朋友,卻不希望陌生人任意來取走的道理一樣; 當我們針對某些特定類別存取時,就能透過朋友關係進行處理。

77 章節目錄 11-4 朋友關係 定義朋友關係

78 11-4-1 定義朋友關係 C++提供的朋友關係有兩種:
一種是朋友類別(Friend Class) 一種是朋友函數(Friend Function) 從朋友類別與朋友函數來看,只要是它們為某一個類別的朋友,它們的所有資料成員及函數成員都能被這一個類別所存取使用(包含private方式) 接下來我們分別來探討這兩種朋友關係的定義。

79 章節目錄 11-4 朋友關係 定義朋友關係 朋友類別

80 11-4-2 朋友類別 所謂「朋友類別」就是某個類別的整個成員都為另一個類別的朋友,利用friend關鍵字將A類別宣告為B類別的朋友。
讓我們藉由範例「FriendClass.vcproj」來說明:

81 範例:FriendClass.vcproj(1)

82 範例:FriendClass.vcproj(2)

83 範例:FriendClass.vcproj(3)

84 執行程式:FriendClass.vcproj
第6~14行宣告Car類別,並在第10行利用friend關鍵字來宣告一個Spec類別為朋友。 第21~24行宣告Spec類別,在第23行宣告了靜態成員函數。 第26~36行定義speed()靜態成員函數,當函數接收主程式傳來的參數值後,在28行建立Car類別實體-March物件,第31行設定March屬性,並在第35行呼叫setColor()成員函數。 主程式中,第41行利用範圍解析運算子來呼叫Spec類別的靜態成員函數並進行參數的傳遞。

85 章節目錄 11-4 朋友關係 朋友函數 定義朋友關係 朋友類別

86 11-4-3 朋友函數 從上述的範例來看,我們並不一定要將整個類別當成朋友,可以提取某些部份的函數來當成朋友函數使用。
朋友函數與朋友類別一樣,它可以任意存取,不一定要宣告成public方式才可以,即使private也一樣可以使用。 朋友函數有兩種: 一般函數 類別的成員函數。 下面範例「FriendFunc.vcproj」是以一般函數為朋友函數。

87 範例:FriendFunc.vcproj(1)

88 範例:FriendFunc.vcproj(2)

89 執行程式:FriendFunc.vcproj
因為是延續範例「FriendClass.vcproj」的架構,還是宣告了Car類別,只是在第11行將一般函數speed(),利用friend宣告為朋友函數。 第26~35行定義一般函數speed(),並在函數中宣告了March物件,雖然engi資料成員在Car類別設定為privae,還是可以透過speed()函數來進行屬性的設定,並在34行呼叫Car類別的成員函數來傳遞參數值。

90 朋友中的類別成員函數 如果定義的朋友函數是以另一個類別的成員函數為朋友時,讓我們透過範例「FriendClassFunc.vcproj」來說明處理方式:

91 範例:FriendClassFunc.vcproj(1)

92 範例:FriendClassFunc.vcproj(2)

93 程式解說:FriendClassFunc.vcproj
第6~9行必須先宣告另一個類別Spec;第11~20行宣告Car類別,並在第16行將Spec類別的成員函數speed()宣告為朋友函數。 主程式中,第36行宣告pow物件為Spec類別的實體,然後呼叫speed()成員函數來傳遞參數;這樣的作用是利用另一個類別的方法來成為朋友函數,然後藉助此方法來進行存取。

94 章節目錄 11-4 朋友關係 朋友函數 定義朋友關係 朋友的繼承 朋友類別

95 11-4-4 朋友的繼承 使用friend函數和類別,是否需要建立繼承關係呢?
若從真實世界的觀點來看,父親的朋友未必是兒子的朋友; 因此基底類別的friend不一定就是衍生類別的friend。 從前面範例中已經得知,透過friend,即使是private的存取權限也能被存取,這讓資料在封裝過程中會不會有漏洞! 我們以範例「FriendInher.vcproj」來說明。

96 範例:FriendInher.vcproj(1)

97 範例:FriendInher.vcproj(2)

98 程式解說:FriendInher.vcproj
第6~13行宣告Car類別,第10行將Spec宣告為朋友類別,第13行則是定義了含有參數值的建構式。 第16~21行宣告Roadster類別繼承了Car類別,第23~25行定義建構式,並以起始值序列化方式呼叫了父類別的建構式。 第27~30行宣告Spec類別,第32~36行定義成員函數show(),並利用nissan物件來分別存取父類別的屬性engi和子類別的屬性power。 執行時,如果取消第35行的註解,編譯時會產生錯誤,表示父類別的朋友並無法存取子類別的屬性。但是也有程式中依然有不合理之處,父類別的資料成員engi被設為private,繼承後子類別是無法進行存取,卻能利用friend來進行存取,表示此處確實有安全的漏洞,這是使用friend類別要小心注意的地方。

99 章節目錄 11-1 瞭解繼承 11-2 類別的繼承 11-3 成員與繼承的關係 11-4 朋友關係 11-5 多重繼承機制
繼承的相關名詞 為什麼要使用繼承 11-1 瞭解繼承 繼承的等級 private存取範圍 protected存取範圍 public存取範圍 11-2 類別的繼承 類別的屬性關係 類別的成員函數 建構式的呼叫 11-3 成員與繼承的關係 定義朋友關係 朋友類別 朋友函數 朋友的繼承 11-4 朋友關係 認識類別的多重繼承 使用虛擬繼承 11-5 多重繼承機制

100 章節目錄 11-5 多重繼承機制

101 11-5 多重繼承機制 如果衍生類別只來自於一個基底類別,稱為「單一繼承」(Single Inheritance);
倘若衍生類別擁有兩個以上的基底類別,稱為「多重繼承」(Multiple Inheritance)。 以真實世界來對應,表示子類別同時擁有的父親和母親,而非單親。

102 章節目錄 11-5 多重繼承機制 認識類別的多重繼承

103 11-5-1 認識類別的多重繼承 C++提供多重繼承機制,程式碼表示如下: 我們同時宣告Mon和Dad類別
而Kids類別則同時繼承Mon和Dad類別 繼承的類別與類別之間要利用逗號「,」隔開

104 多重繼承的架構 當我們建立一個Kids物件時,表示Mon和Dad類別也會分別產生物件而依附在Kids物件裡。 現在問題來了!
當然得利用「範圍解析運算子」來指定呼叫的成員

105 範例:MultiInher.vcproj(1)

106 範例:MultiInher.vcproj(2)

107 範例:MultiInher.vcproj(3)

108 範例:MultiInher.vcproj(4)

109 執行程式:MultiInher.vcproj

110 程式解說:MultiInher.vcproj
第6~11行建立第一個基底類別Spot,成員函數setPoint()只接受一個參數值;第18~23行建立第二個基底類別Line,成員函數setPoint()接受二個參數值。 第31~35行宣告衍生類別Cube,同時繼承了Spot和Line類別。 第37~41行定義coord()成員函數。由於二個基底類別的成員函數名稱相同,為了讓編譯器能夠區分;第39行利用範圍解析運算子來呼叫Spot類別的成員函數setPoint()並傳入一個參數;第40行以相同方式來呼叫Line類別的成員函數setPoint()並傳入二個參數。 第43~48行定義triangle()成員函數,用來計算三角形面積,第44行計算海頓公式中S值;第46行相乘結果取得的值,利用sqrt()函數取得平方根值,即是三角形面積。 主程式中依據需求,分別宣告了one、two和poly物件,各自呼叫成員函數來進行屬性值設定。

111 章節目錄 11-5 多重繼承機制 認識類別的多重繼承 使用虛擬繼承

112 使用虛擬繼承 如果以多重繼承方式來產生類別架構,很有產生繼承衝突!多重繼承架構如下圖所示:

113 多重繼承的衝突 當我們建立Cube類別的物件時,除了有Spot的類別成員之外,還包含Line類別的成員,而這兩個類別之間還各自包含Locate類別的成員。 在這種情形下,不但造成記憶體空間的浪費,還會造成繼承衝突! 為了管理這樣的類別架構,C++提供「虛擬繼承」的概念,表示Spot和Line類別繼承Locate類別時,原來的程式碼撰寫如下:

114 使用虛擬繼承 如果引用虛擬繼承的作用,改寫的程式碼如下:
虛擬繼承方式就是在宣告繼承的地方加上virtual關鍵字,讓基底類別在衍生類別中所佔用的記憶體空間,透過虛擬繼承方式後直接抓取基底類別的記憶體空間。 延續範例「MultiInher.vcproj」的架構,瞭解虛擬繼承的建立方式。

115 部份範例:VirtualInher.vcproj(1)

116 範例:VirtualInher.vcproj(2)

117 程式解說:VirtualInher.vcproj
由於範例的架構和前一個範例「MultiInher.vcproj」相同,只是加入虛擬繼承的作法,因此執行結果也一樣。因此只針對加入虛擬繼承的程式碼來說明。 第6~9行宣告一個基底類別Locate,定義屬性px、py和pz;第11~14的Spot類別和第21~24行的Line類別分別以虛擬繼承方式繼承了Locate,所以也繼承了Locate類別的屬性。 成員函數setPoint()在Spot類別中只接受一個參數值;在Line類別中接受二個參數值;而在Cube類別中能接受三個參數值;因此不同類別的物件會自行呼叫自己的成員函數來進行屬性的改變。

118 Virtual代表虛擬位址

119 章節目錄 11-1 瞭解繼承 11-2 類別的繼承 11-3 成員與繼承的關係 11-4 朋友關係 11-5 多重繼承機制
繼承的相關名詞 為什麼要使用繼承 11-1 瞭解繼承 繼承的等級 private存取範圍 protected存取範圍 public存取範圍 11-2 類別的繼承 類別的屬性關係 類別的成員函數 建構式的呼叫 11-3 成員與繼承的關係 定義朋友關係 朋友類別 朋友函數 朋友的繼承 11-4 朋友關係 認識類別的多重繼承 使用虛擬繼承 11-5 多重繼承機制 11-6 組合模式

120 11-6 組合模式 前面談到的繼承都是is_a的關係,本節將討論繼承的另一種模式has_a,可參考9-3-2,表示物件是其他類別的物件,最後組成一個較為完整的系統。由小物件組成大物件的模式,稱為「組合」(Composition)。 如何達成物件的組合作用,就是將物件本身利用傳參考方式或是物件指標來作為另一個物件的屬性值,我們利用範例「Composition.vcproj」來直接說明。

121 範例:Composition.vcproj(1)

122 範例:Composition.vcproj(2)

123 範例:Composition.vcproj(3)

124 範例:Composition.vcproj(4)

125 範例:Composition.vcproj(5)

126 執行程式:Composition.vcproj
延續上一個範例架構,第6~9行宣告基底類別Locate,定義屬性px、py和pz;第11~17的Spot類別和第26~34行的Line類別分別以虛擬繼承方式繼承了Locate,所以也繼承了Locate類別的屬性。 第19~21行Spot類別的建構式,以指標方式來進行參數傳遞;第36~46行的Line類別建構式,將Spot類別的物件以傳參考方式來進行傳遞,並取得Spot類別的屬性值。 Cube類別從第46~53行程式碼中,同時繼承了Spot和Line類別,因此第55~59行的建構式也以Spot類別的物件也以傳參考方式將物件進行傳遞來取得相關屬性值。 範例中只有Spot類別的物件來與其他類別的物件進行組合,利用傳參考方式來作為另一個物件屬性,讓大家瞭解最簡單的組合模式。

127 重點整理. 繼承(Inheritance)表示類別之間產生繼承關係時,子類別會擁有父類別的屬性和方法。
繼承存取權限用來設定衍生類別與基底類別繼承的關係,存取權限包含三種:public(公開)、protected(保護)及private(私有)。如果繼承的過程中並未指定存取權限,系統預設為「public」。 當兩個類別建立繼承時,如果父類別想要在子類別的成員函數中存取父類別本身的屬性,必須使用範圍解析運算子。 當兩個類別建立繼承時,成員函數會包含新增、取代和擴充的情形。若是以程式碼再用性來看,擴充原有函數的功能是一個比較好的方法。

128 重點整理.. 在父類別和子類別之間,若建立了子類別物件,執行時會先呼叫父類別的建構式,再呼叫本身類別的建構式。
在類別階層中,建構式的呼叫順序是由最上層的基底類別開始呼叫;而解構式剛好相反,它會由繼承最底端開始往上一層呼叫。 C++提供的朋友關係有兩種:朋友類別(Friend Class)、朋友函數(Friend Function)。從朋友類別與朋友函數來看,只要是它們為某一個類別的朋友,其所有資料成員及函數成員都能被這一個類別所存取使用(包含private方式)。

129 重點整理… 所謂多重繼承機,是表示衍生類別來自於兩個基底類別;單一繼承則是衍生類別只有一個基底類別。
利用多重繼承來產生類別架構時,會造成繼承管理的困難,利用虛擬繼承方式可改善衍生類別建立的物件時依附基底類別成員的困擾。 繼承的另一個方式就是has_a,也就是組合(Composition);它將物件以傳參考或物件指標方式來作為另一個物件的屬性,將小物件擴展成大系統。


Download ppt "第十一章 繼承類別和朋友關係."

Similar presentations


Ads by Google