![C 程序設(shè)計課件:第八章 繼承與多態(tài)_第1頁](http://file4.renrendoc.com/view/043a527531f6544aa6956663b14ee659/043a527531f6544aa6956663b14ee6591.gif)
![C 程序設(shè)計課件:第八章 繼承與多態(tài)_第2頁](http://file4.renrendoc.com/view/043a527531f6544aa6956663b14ee659/043a527531f6544aa6956663b14ee6592.gif)
![C 程序設(shè)計課件:第八章 繼承與多態(tài)_第3頁](http://file4.renrendoc.com/view/043a527531f6544aa6956663b14ee659/043a527531f6544aa6956663b14ee6593.gif)
![C 程序設(shè)計課件:第八章 繼承與多態(tài)_第4頁](http://file4.renrendoc.com/view/043a527531f6544aa6956663b14ee659/043a527531f6544aa6956663b14ee6594.gif)
![C 程序設(shè)計課件:第八章 繼承與多態(tài)_第5頁](http://file4.renrendoc.com/view/043a527531f6544aa6956663b14ee659/043a527531f6544aa6956663b14ee6595.gif)
版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)
文檔簡介
1、繼承(inheritance):該機制是面向?qū)ο蟪绦蛟O(shè)計使代碼可以復用的最重要的手段,它允許程序員在保持原有類特性的基礎(chǔ)上進行擴展,增加功能。這樣產(chǎn)生新的類,稱派生類。繼承呈現(xiàn)了面向?qū)ο蟪绦蛟O(shè)計的層次結(jié)構(gòu)。體現(xiàn)了由簡單到復雜的認識過程。第八章 繼承與多態(tài)多態(tài)性(polymorphism):多態(tài)性包括靜態(tài)的多態(tài)性和動態(tài)的多態(tài)性。前者亦稱編譯時的多態(tài)性,包括函數(shù)的重載和運算符的重載。后者亦稱運行時的多態(tài)性,這是以虛函數(shù)為基礎(chǔ)的,是面向?qū)ο蟪绦蛟O(shè)計的標志性特征。 體現(xiàn)了類推和比喻的思想方法。 第八章 繼承與多態(tài)8.1 繼承與派生的概念 8.4 虛基類 (選讀) 8.3 多重繼承與派生類成員標識 (選
2、讀) 8. 6 多態(tài)性與虛函數(shù) 8.5 派生類應(yīng)用討論 8.2 派生類的構(gòu)造函數(shù)與析構(gòu)函數(shù) 附錄: UML類圖中的依賴關(guān)系附錄:用UML類圖表示派生8.1 繼承與派生的概念層次概念是計算機的重要概念。通過繼承(inheritance)的機制可對類(class)分層,提供類型/子類型的關(guān)系。 C+通過類派生(class derivation)的機制來支持繼承。被繼承的類稱為基類(base class)或超類(superclass),新的類為派生類(derived class)或子類(subclass)。基類和派生類的集合稱作類繼承層次結(jié)構(gòu)(hierarchy)。 如果基類和派生類共享相同的公有接
3、口,則派生類被稱作基類的子類型(subtype)。 層次概念:派生反映了事物之間的聯(lián)系,事物的共性與個性之間的關(guān)系。 派生與獨立設(shè)計若干相關(guān)的類,前者工作量少,重復的部分可以從基類繼承來,不需要單獨編程。 8.1 繼承與派生的概念8.1.1 類的派生與繼承 8. 1.2 公有派生與私有派生 派生類的定義:class 派生類名:訪問限定符 基類名1,訪問限定符 基類名2,訪問限定符 基類名n private: 成員表1; /派生類增加或替代的私有成員public:成員表2; /派生類增加或替代的公有成員protected:成員表3; /派生類增加或替代的保護成員;/分號不可少其中基類1,基類2,
4、是已聲明的類。 在派生類定義的類體中給出的成員稱為派生類成員,它們是新增加成員,它們給派生類添加了不同于基類的新的屬性和功能。派生類成員也包括取代基類成員的更新成員。8.1.1 類的派生與繼承8.1.1 類的派生與繼承訪問限定符兩方面含義:派生類成員(新增成員)函數(shù)對基類(繼承來的)成員的訪問(調(diào)用和操作),和從派生類對象之外對派生類對象中的基類成員的訪問。放在后面討論。公有派生限制最少,是派生的主流。訪問限定符:基類名前的訪問限定符,是對基類成員進一步的限制。訪問控制也是三種:公有(public)方式,亦稱公有繼承保護(protected)方式,亦稱保護繼承私有(private)方式, 亦稱
5、私有繼承。 基類1基類2基類n派生類1派生類2基類派生類1派生類2(a)多重繼承 (b)單繼承 圖8.1 多重繼承與單繼承 一個基類可以直接派生出多個派生類 派生類可以由多個基類共同派生出來,稱多重繼承。8.1.1 類的派生與繼承多重繼承:如果一個派生類可以同時有多個基類,稱為多重繼承(multiple-inheritance),這時的派生類同時得到了多個已有類的特征。單繼承:派生類只有一個直接基類的情況稱為單繼承(single-inheritance)。8.1.1 類的派生與繼承 在派生過程中,派生出來的新類同樣可以作為基類再繼續(xù)派生出更新的類,依此類推形成一個層次結(jié)構(gòu)。直接參與派生出某類稱
6、為直接基類,而基類的基類,以及更深層的基類稱為間接基類。類族: 同時一個基類可以直接派生出多個派生類。這樣形成了一個相互關(guān)聯(lián)的類族。如MFC就是這樣的族類,它由一個CObject類派生出200個MFC類中的絕大多數(shù)。 多層次繼承:編制派生類時可分四步 吸收基類的成員 改造基類成員 發(fā)展新成員 重寫構(gòu)造函數(shù)與析構(gòu)函數(shù) 8.1.1 類的派生與繼承不論是數(shù)據(jù)成員,還是函數(shù)成員,除構(gòu)造函數(shù)與析構(gòu)函數(shù)外全盤接收 聲明一個和某基類成員同名的新成員,派生類中的新成員就屏蔽了基類同名成員派生類新成員必須與基類成員不同名,它的加入保證派生類在功能上有所發(fā)展。 派生編程步驟:8.1.1 類的派生與繼承第二步中,新
7、成員如是成員函數(shù),參數(shù)表和返回值也完全一樣,稱為同名覆蓋(Override),否則是重載 。第三步中,獨有的新成員才是繼承與派生的核心特征。第四步是重寫構(gòu)造函數(shù)與析構(gòu)函數(shù),派生類不繼承這兩種函數(shù)。不管原來的函數(shù)是否可用一律重寫可免出錯。方式類似聚合含成員對象的類的構(gòu)造函數(shù)。詳細內(nèi)容后文討論?!纠?.1】由在冊人員類公有派生學生類【例8.1】由在冊人員類公有派生學生類。我們希望基類和派生類共享相同的公有接口,只能采用公有派生來實現(xiàn)。基類:class Personstring IdPerson;/身份證號,18位數(shù)字string Name;/姓名Tsex Sex; /性別enum Tsexmid,
8、man,woman;int Birthday;/生日,格式1986年8月18日寫作19860818string HomeAddress;/家庭地址public:Person(string, string,Tsex,int, string);/構(gòu)造函數(shù) Person(); /默認的構(gòu)造函數(shù) Person(); /析構(gòu)函數(shù)【例8.1】由在冊人員類公有派生學生類void SetName(string); /修改名字string GetName()return Name; /提取名字void SetSex(Tsex sex)Sex=sex; /修改性別Tsex GetSex()return Sex;
9、/提取性別void SetId(string id)IdPerson=id;/修改身份證號string GetId()return IdPerson; /提取身份證號void SetBirth(int birthday)Birthday=birthday; /修改生日int GetBirth()return Birthday; /提取生日void SetHomeAdd(string ); /修改住址string GetHomeAdd()return HomeAddress; /提取住址void PrintPersonInfo(); /輸出個人信息;/接口函數(shù):【例8.1】由在冊人員類公有派生學
10、生類派生的學生類:class Student:public Person /定義派生的學生類 string NoStudent; /學號 course cs30; /30門課程與成績public: Student(string id, string name,Tsex sex,int birthday, string homeadd, string nostud);/注意派生類構(gòu)造函數(shù)聲明方式 Student(); /默認派生類構(gòu)造函數(shù) Student(); /派生類析構(gòu)函數(shù) SetCourse(string ,int); /課程設(shè)置 int GetCourse(string ); /查找成績
11、 void PrintStudentInfo(); /打印學生情況;struct course string coursename; int grade;驗證主函數(shù)8.2 派生類的構(gòu)造函數(shù)與析構(gòu)函數(shù)注意: 本例中標準C+字符串string是作為成員對象使用的(聚合),動態(tài)內(nèi)存分配的構(gòu)造和析構(gòu)被封裝起來,使用十分簡單。如使用動態(tài)生成的C風格字符串,要考慮深復制,那要復雜得多。 提倡完善的類對象封裝,不僅封裝數(shù)據(jù)和對數(shù)據(jù)的操作,而且封裝資源的動態(tài)分配與釋放,形成一個完備的子系統(tǒng)。在一個有層次結(jié)構(gòu)的類體系中資源的動態(tài)分配與釋放應(yīng)封裝在成員對象中,如同使用標準的string字符串類那樣。 聚合是一種完
12、善的封裝。采用成員對象將大大簡化層次結(jié)構(gòu)的類體系中資源的動態(tài)分配與釋放的處理方法,不再出現(xiàn)難度極大的多層次的深復制。8.1.2 公有派生與私有派生訪問限定符討論:派生類成員(新增成員)函數(shù)對基類(繼承來的)成員的訪問(調(diào)用和操作),和從派生類對象之外對派生類對象中的基類成員的訪問。下面進行詳細討論:8.1.2 公有派生與私有派生不可直接訪問 不可直接訪問 private 不可直接訪問 private protected 不可直接訪問 private public 私有派生所有接口必須重寫 不可直接訪問 不可直接訪問 private 不可直接訪問 protected protected 可直接訪
13、問 public public 公有派生所有接口均可使用 在派生類對象外訪問派生類對象的基類成員 在派生類中對基類成員的訪問限定 基類中的訪問限定 派生方式 公有派生是絕對主流。 8.1.2 公有派生與私有派生保護派生:直接派生: 基類的私有成員仍是派生類的私有成員,不可直接訪問,而基類中的公有和保護成員全部成為派生類的保護成員,在派生類中可以直接訪問。但在派生類對象之外是不能直接訪問該對象的任何基類成員的。與私有派生相同。多層派生:把保護派生類作為基類或把私有派生類作為基類再作一層保護派生。在新的保護派生類中可直接訪問由保護派生傳遞過來的底層基類的公有和保護成員,而不可直接訪問由私有派生傳遞
14、來的底層基類的公有和保護成員。但在該類對象之外都不可直接訪問類對象底層基類的公有成員。合理使用保護限定方式可以在復雜的類層次關(guān)系中取一個共享訪問和成員封裝隱蔽性的折衷。派生類構(gòu)造函數(shù)的定義:派生類名:派生類名(參數(shù)總表):基類名1(參數(shù)名表1),基類名2(參數(shù)名表2),基類名n(參數(shù)名表n),成員對象名1(成員對象參數(shù)名表1),成員對象名m(成員對象參數(shù)名表m)/派生類新增成員的初始化; /所列出的成員對象名全部為新增成員對象的名字注意: 在構(gòu)造函數(shù)的聲明中,冒號及冒號以后部分必須略去。 所謂不能繼承并不是不能利用,而是把基類的構(gòu)造函數(shù)作為新的構(gòu)造函數(shù)的一部分,或者講調(diào)用基類的構(gòu)造函數(shù)?;惷?/p>
15、僅指直接基類,寫了底層基類,編譯器認為出錯。 冒號后的基類名,成員對象名的次序可以隨意,這里的次序與調(diào)用次序無關(guān)。 8.2 派生類的構(gòu)造函數(shù)與析構(gòu)函數(shù)派生類構(gòu)造函數(shù)各部分執(zhí)行次序: 1.調(diào)用基類構(gòu)造函數(shù),按它們在派生類定義的先后順序,順序調(diào)用。 2.調(diào)用成員對象的構(gòu)造函數(shù),按它們在類定義中聲明的先后順序,順序調(diào)用。3.派生類的構(gòu)造函數(shù)體中的操作。8.2 派生類的構(gòu)造函數(shù)與析構(gòu)函數(shù)注意: 在派生類構(gòu)造函數(shù)中,只要基類不是使用無參的默認構(gòu)造函數(shù)都要顯式給出基類名和參數(shù)表。 如果基類沒有定義構(gòu)造函數(shù),則派生類也可以不定義,全部采用系統(tǒng)給定的默認構(gòu)造函數(shù)。 如果基類定義了帶有形參表的構(gòu)造函數(shù)時,派生類
16、就應(yīng)當定義構(gòu)造函數(shù)。8.2 派生類的構(gòu)造函數(shù)與析構(gòu)函數(shù)析構(gòu)函數(shù):析構(gòu)函數(shù)的功能是作善后工作。 只要在函數(shù)體內(nèi)把派生類新增的一般成員處理好就可以了,而對新增的成員對象和基類的善后工作,系統(tǒng)會自己調(diào)用成員對象和基類的析構(gòu)函數(shù)來完成。 析構(gòu)函數(shù)各部分執(zhí)行次序與構(gòu)造函數(shù)相反,首先對派生類新增一般成員析構(gòu),然后對新增對象成員析構(gòu),最后對基類成員析構(gòu)。8.3 多重繼承與派生類成員標識(選讀)由多個基類共同派生出新的派生類,這樣的繼承結(jié)構(gòu)被稱為多重繼承或多繼承(multiple-inheritance) 椅子床沙發(fā)(單繼承)躺椅(多重繼承)兩用沙發(fā)(多重繼承)圖8.2 椅子,床到兩用沙發(fā)多重繼承實例:在冊人
17、員學生(單繼承)教職工(單繼承)兼職教師(單繼承)教師(單繼承)行政人員(單繼承)工人(單繼承)研究生(單繼承)行政人員兼教師(多重繼承)在職研究生(多重繼承)研究生助教(多重繼承)圖8.3 大學在冊人員繼承關(guān)系8.3 多重繼承與派生類成員標識(選讀)派生出來的新類同樣可以作為基類再繼續(xù)派生出更新的類,依此類推形成一個層次結(jié)構(gòu)。 8.3 多重繼承與派生類成員標識(選讀)歧義性問題:參見圖8.3,比如行政人員兼教師,在其基類教師中有一個“教職工編號”,另一基類行政人員中也有一個“教職工編號”,如果只講教職工編號那么是哪一個基類中的呢?這兩者可能是一回事,但計算機系統(tǒng)并不這么認為。進一步,如果“教
18、職工編號” 是由兩個基類“教師”和“行政人員”共同的基類“教職工”類繼承來的,只有同一個標識符,也不能用改標識符來區(qū)分。 唯一標識問題:通常采用作用域分辨符“:”:基類名:成員名; /數(shù)據(jù)成員基類名:成員名(參數(shù)表); /函數(shù)成員 class EGStudent int No在職學號 class GStudent int No研究生號 .class Student int No學生號 . class Person int No身份證號 .class Employee int No工作證號 .class Person int No身份證號 .圖8.4(a)在職研究生派生類關(guān)系 定義EGStude
19、nt類對象EGStudent1,并假定派生全部為公有派生,而int No全為公有成員:EGStud1.No /在職學號EGStud1.GStudent:No /研究生號EGStud1.GStudent.Student:No /學生號 EGStud1.GStudent.Student. Person:No /身份證號EGStud1.Employee:No /工作證號EGStud1.Employee.Person:No /身份證號兩個身份證號從邏輯上講應(yīng)是一回事,但是物理上是分配了不同內(nèi)存空間,是兩個變量,請參見圖8.4(b)。Person Person StudentEmployee GStud
20、ent EGStudentPerson成員 Person成員 Student新成員 GStudent新成員 Employee新成員 EGStudent新成員 圖8.4(b)在職研究生派生類存儲圖 建議采用有確定字面意思的標識符,它可以被編譯器簡單區(qū)分出來。 如果class Person的身份證號標識為int IdPerson,則寫為:EGStud1.GStudent:IdPersonEGStud1.Employee:IdPerson不必標出那么多層次的類,但寫EGStud1:IdPerson是錯的。 作用域分辨符不能嵌套使用,如:EGStud1.GStudent:Student:No/學生號E
21、GStud1.GStudent:Student:Person:No /身份證號是錯誤的。 8.3 多重繼承與派生類成員標識(選讀)8.3 多重繼承與派生類成員標識(選讀)一般數(shù)據(jù)成員總是私有成員,派生類對基類的訪問只能間接進行。訪問身份證號,應(yīng)通過class Person中的公有成員函數(shù)(接口)GetNo()和SetNo()進行:EGStud1.Employee.Person:SetNo(no);no=EGStud1.Employee.Person:GetNo();注意:【例8.2】由圓和高多重繼承派生出圓錐。 因為公有派生時,在派生類中不可以直接訪問基類的私有成員,但可以直接訪問基類的保護成
22、員,當需要在派生類中訪問基類的數(shù)據(jù)成員時,可以將它們定義為保護的,而不是私有的。 本例中類Circle為圓;類Line為高;類Cone為圓錐,由Circle和Line公有派生而來。在Cone類中,Circle和Line類的接口完全不變,可以直接調(diào)用,這就是公有派生的優(yōu)點。在Cone的成員函數(shù)中可直接訪問Circle和Line中的公有成員和保護成員。 【例8.2】由圓和高多重繼承派生出圓錐檢證主程序:圓類Circle定義高類Line定義圓錐類Cone定義虛基類的引入:在圖8.4中,兩個身份證號顯然是不合理的??梢园裞lass Person這個共同基類設(shè)置為虛基類,這樣就僅有一個Person基類成
23、員,從不同路徑繼承來的同名數(shù)據(jù)成員(身份證號)在內(nèi)存中就是同一個數(shù)據(jù)。 8.4 虛基類(選讀)注意:virtual 關(guān)鍵字只對緊隨其后的基類名起作用:class Student:virtual public Person.;class Employee:virtual public Person.;虛基類(virtual base class)定義:class 派生類名:virtual 訪問限定符 基類類名.;class 派生類名:訪問限定符 virtual 基類類名.;8.4 虛基類(選讀)圖8.5 采用虛基類后在職研究生類儲存圖StudentGStudentEGStudentPersonS
24、tudent新成員GStudent新成員PersonEmployee新成員Person成員EGStudent新成員PersonPersonEmployee這種繼承稱為虛擬繼承虛擬繼承:在Person的位置上放的是指針,兩個指針都指向Person成員存儲的內(nèi)存。這種繼承稱為虛擬繼承(virtual inheritance)。8.4 虛基類(選讀)派生類名:派生類名(參數(shù)總表):基類名1(參數(shù)名表1),基類名2(參數(shù)名表2),基類名n(參數(shù)名表n),成員對象名1(成員對象參數(shù)名表1),成員對象名m(成員對象參數(shù)名表m),底層虛基類名1(參數(shù)名表1), 底層虛基類名r(參數(shù)名表r)/派生類新增成員的
25、初始化; /所列出的成員對象名全部為新增成員對象的名字在多層虛擬繼承構(gòu)造函數(shù)中,基類名不僅要列出直接基類,而且要列出底層虛基類,否則編譯器認為出錯。如不是虛擬繼承只能列直接基類。虛擬繼承的構(gòu)造函數(shù):8.4 虛基類(選讀)在派生類對象的創(chuàng)建中:首先是虛基類的構(gòu)造函數(shù)并按它們聲明的順序構(gòu)造。第二批是非虛基類的構(gòu)造函數(shù)按它們聲明的順序調(diào)用。第三批是成員對象的構(gòu)造函數(shù)。最后是派生類自己的構(gòu)造函數(shù)被調(diào)用。構(gòu)造函數(shù)執(zhí)行次序:8.4 虛基類(選讀)【例8.3】在采用虛基類的多重繼承中構(gòu)造與析構(gòu)的次序。class Objectpublic:Object()coutconstructor Objectn;Obj
26、ect()coutdeconstructor Objectn;class Bclass1public:Bclass1()coutconstructor Bclass1n;Bclass1()coutdeconstructor Bclass1n;class Bclass2public:Bclass2()coutconstructor Bclass2n;Bclass2()coutdeconstructor Bclass2n;8.4 虛基類(選讀)【例8.3】在采用虛基類的多重繼承中,構(gòu)造與析構(gòu)的次序。class Bclass3public:Bclass3()coutconstructor Bclas
27、s3n;Bclass3()coutdeconstructor Bclass3n;class Dclass:public Bclass1,virtual Bclass3,virtual Bclass2 Object object;public: Dclass():object(),Bclass2(),Bclass3(),Bclass1() cout派生類建立!n; Dclass()cout派生類析構(gòu)!n;int main() Dclass dd; coutPrintStudentInfo();pper4=pstu4;delete pper4;/用基類指針撤銷派生類,動態(tài)生成的對象必須顯式撤銷8.
28、6.1 虛函數(shù)的定義在主函數(shù)中添加以下內(nèi)容: 通過在析構(gòu)函數(shù)中加顯示語句發(fā)現(xiàn)先調(diào)Student析構(gòu)函數(shù),后調(diào)Person析構(gòu)函數(shù)。 這里再次強調(diào)動態(tài)生成的對象必須顯式撤銷。純虛函數(shù): 純虛函數(shù)(pure virtual function)是指被標明為不具體實現(xiàn)的虛擬成員函數(shù)。它用于這樣的情況:定義一個基類時,會遇到無法定義基類中虛函數(shù)的具體實現(xiàn),其實現(xiàn)依賴于不同的派生類。8.6.2 純虛函數(shù)純虛函數(shù)的定義:virtual 返回類型 函數(shù)名(參數(shù)表)=0;含有純虛函數(shù)的基類是不能用來定義對象的。純虛函數(shù)沒有實現(xiàn)部分,不能產(chǎn)生對象,所以含有純虛函數(shù)的類是抽象類。1 定義純虛函數(shù)時,不能定義虛函數(shù)的
29、實現(xiàn)部分。即使是函數(shù)體為空也不可以,函數(shù)體為空就可以執(zhí)行,只是什么也不做就返回。但根本不能調(diào)用純虛函數(shù)。2 “=0”表明程序員將不定義該函數(shù),函數(shù)聲明是為派生類保留一個位置。“=0”本質(zhì)上是將指向函數(shù)體的指針定為NULL。3 在派生類中必須有重新定義的純虛函數(shù)的函數(shù)體,這樣的派生類才能用來定義對象。8.6.2 純虛函數(shù)定義純虛函數(shù)的要點:【例8.8】學校對在冊人員進行獎勵,依據(jù)是業(yè)績分,但是業(yè)績分的計算方法只能對具體人員進行,如學生,教師,行政人員,工人,算法都不同,所以可以將在冊人員類作為一個抽象類,業(yè)績計算方法作為一個純虛函數(shù)。在主函數(shù)中全部用指向基類的指針來調(diào)用8.6.2 純虛函數(shù)業(yè)績分
30、基類定義業(yè)績分學生派生類定義業(yè)績分教師派生類定義驗證主函數(shù)【例8.9】用虛函數(shù)來實現(xiàn)辛普生法求函數(shù)的定積分。8.6.2 純虛函數(shù)純虛函數(shù)實現(xiàn)通用算法:辛普生法求定積分類在派生類中加被積函數(shù):驗證主函數(shù)8.6.3 繼承與多態(tài)的應(yīng)用單鏈表派生類(選讀)【例8.10】通用單鏈表派生類。第一步改造【例7.4】的頭文件,不采用模板類,而采用虛函數(shù)實現(xiàn)多態(tài)性,達到通用的目的。結(jié)點類數(shù)據(jù)域被改造為指針,而把數(shù)據(jù)放在一個抽象類中,由指針與之建立聯(lián)系。數(shù)據(jù)域(指向抽象數(shù)據(jù)類的指針)由抽象類派生的數(shù)據(jù)類對象(如串對象)指針域(指向下一結(jié)點)結(jié)點類對象動態(tài)建立的數(shù)據(jù)類對象圖8.9 結(jié)點構(gòu)造class Object
31、/數(shù)據(jù)類為抽象類public: Object() virtual bool operator(Object &)=0; /純虛函數(shù),參數(shù)必須為引用或指針 virtual bool operator!=(Object &)=0; /純虛函數(shù),參數(shù)必須為引用或指針 virtual void Print()=0; /純虛函數(shù) virtual Object() ; /析構(gòu)函數(shù)可為虛函數(shù),構(gòu)造函數(shù)不行8.6.3 繼承與多態(tài)的應(yīng)用單鏈表派生類(選讀)結(jié)點組織,采用結(jié)點類加數(shù)據(jù)類數(shù)據(jù)類定義:本題要點:采用虛函數(shù)實現(xiàn)多態(tài)性,達到通用的目的。堆內(nèi)存的分配與釋放,關(guān)鍵不是創(chuàng)建,而是釋放!說明:數(shù)據(jù)抽象類中含有三個
32、純虛函數(shù):輸出函數(shù)和兩個比較函數(shù)。當抽象類在派生時重新定義三個純虛函數(shù),可以進行各種類型,包括類和結(jié)構(gòu)對象的比較和輸出。本例介紹程序總體組成為主,鏈表的操作由學生自己仔細閱讀。8.6.3 繼承與多態(tài)的應(yīng)用單鏈表派生類(選讀)抽象類中的析構(gòu)函數(shù)也是虛函數(shù),這一點非常重要。當由結(jié)點類指向抽象基類的指針刪除釋放動態(tài)分配的由抽象類派生的數(shù)據(jù)類對象時,必須由數(shù)據(jù)類自定義的虛析構(gòu)函數(shù)來釋放該類對象數(shù)據(jù)部分占用內(nèi)存。 Class Node Object* info; /數(shù)據(jù)域用指針指向數(shù)據(jù)類對象 Node* link; /指針域public: Node(); /生成頭結(jié)點的構(gòu)造函數(shù) Node(); /析構(gòu)函
33、數(shù) void InsertAfter(Node* P); /在當前結(jié)點后插入一個結(jié)點 Node* RemoveAfter(); /刪除當前結(jié)點的后繼結(jié)點,返回該結(jié)點備用 void Linkinfo(Object* obj); /把數(shù)據(jù)對象連接到結(jié)點 friend class List; /以List為友元類,List可直接訪問Node的私有函數(shù),;8.6.3 繼承與多態(tài)的應(yīng)用單鏈表派生類(選讀)結(jié)點類定義:class List Node *head,*tail; /鏈表頭指針和尾指針public: List(); /構(gòu)造函數(shù),生成頭結(jié)點(空鏈表) List(); /析構(gòu)函數(shù) void Make
34、Empty(); /清空鏈表,只余表頭結(jié)點 Node* Find(Object & obj); /搜索數(shù)據(jù)域與定值相同的結(jié)點,返回該結(jié)點的地址 int Length(); /計算單鏈表長度 void PrintList(); /打印鏈表的數(shù)據(jù)域 void InsertFront(Node* p); /可用來向前生成鏈表 void InsertRear(Node* p); /可用來向后生成鏈表 void InsertOrder(Node* p); /按升序生成鏈表 Node* CreatNode(); /創(chuàng)建一個結(jié)點(孤立結(jié)點) Node* DeleteNode(Node* p); ; /刪除指
35、定結(jié)點8.6.3 繼承與多態(tài)的應(yīng)用單鏈表派生類(選讀)鏈表類定義:第二步,取代模板定義泛型類型為具體類型(包括類)的步驟是由抽象類派生數(shù)據(jù)類。數(shù)據(jù)類的數(shù)據(jù)采用字符類串string,動態(tài)分配和釋放內(nèi)存都在string類中完成。為了完成數(shù)據(jù)類的比較和輸出,超載了比較運算符和輸出函數(shù)(虛函數(shù))。數(shù)據(jù)類的比較實際是字符串string的比較。 8.6.3 繼承與多態(tài)的應(yīng)用單鏈表派生類(選讀)class StringObject:public Object string sptr;public: StringObject() sptr=; StringObject(string s)sptr=s; Str
36、ingObject(); /析構(gòu)函數(shù) bool operator(Object &); /大于函數(shù) bool operator!=(Object &); /不等于函數(shù) void Print(); /打印函數(shù);驗證主函數(shù)運行結(jié)果分析與比較: 在該程序中,特別要仔細揣摩堆內(nèi)存的分配與釋放。刪除一個結(jié)點時系統(tǒng)自動調(diào)用結(jié)點類析構(gòu)函數(shù)釋放結(jié)點占用的動態(tài)內(nèi)存,而結(jié)點類析構(gòu)函數(shù)自動調(diào)用數(shù)據(jù)域類虛析構(gòu)函數(shù),數(shù)據(jù)域類析構(gòu)函數(shù)自動調(diào)用string類的析構(gòu)函數(shù)釋放所占用的動態(tài)內(nèi)存。一環(huán)套一環(huán),一步都不能錯。這是使用動態(tài)內(nèi)存分配的關(guān)鍵。即關(guān)鍵不是創(chuàng)建,而是釋放! 運行時的多態(tài)性需要維護一個動態(tài)指針表才能正確指向各相關(guān)
37、類中的同名虛函數(shù)。所以多態(tài)與模板比較,模板的效率更高,標準模板庫中用容器來泛型化數(shù)據(jù)結(jié)構(gòu)中的許多算法。對數(shù)據(jù)結(jié)構(gòu)的使用當然借助模板庫。多態(tài)不適用于性能要求很高的實時應(yīng)用程序,但繼承與多態(tài)可用與其它更多方面,每一種技術(shù)都有可以充分發(fā)揮自己能力的地方。8.6.3 繼承與多態(tài)的應(yīng)用單鏈表派生類(選讀)動態(tài)綁定(dynamic binding)亦稱滯后綁定(late binding),對應(yīng)于靜態(tài)綁定(static binding)。如果使用對象名和點成員選擇運算符“.”引用特定的一個對象來調(diào)用虛函數(shù),則被調(diào)用的虛函數(shù)是在編譯時確定的(稱為靜態(tài)綁定) 如果使用基類指針或引用指明派生類對象并使用該指針調(diào)用
38、虛函數(shù)(成員選擇符用箭頭號“-”),則程序動態(tài)地(運行時)選擇該派生類的虛函數(shù),稱為動態(tài)綁定。8.6.4 動態(tài)綁定(選讀)綁定是指計算機程序自身彼此關(guān)聯(lián)的過程,是把一個標識符名和一個存儲地址聯(lián)系在一起的過程,也就是把一條消息和一個對象的操作相結(jié)合的過程 。圖8.9 虛函數(shù)調(diào)用的控制流程“dog”StringObject動態(tài)無名對象StringObject動態(tài)無名對象“cat”指向Object類指針指向結(jié)點類指針指向Object類指針指向結(jié)點類指針指向Object類指針指向結(jié)點類指針StringObject動態(tài)無名對象“cock” 析構(gòu)函數(shù)指針比較函數(shù)指針輸出函數(shù)指針StringObject虛函
39、數(shù)表抽象類Object虛函數(shù)表析構(gòu)函數(shù)指針比較函數(shù)指針輸出函數(shù)指針ComplexObject虛函數(shù) 析構(gòu)函數(shù)指針 比較函數(shù)指針 輸出函數(shù)指針 默認析構(gòu)函數(shù)釋放動態(tài)串析構(gòu)函數(shù)串比較函數(shù)打印串函數(shù)默認析構(gòu)函數(shù)復數(shù)模大小比較函數(shù)打印復數(shù)函數(shù)8.6.4 動態(tài)綁定(選讀) C+編譯器編譯含有一個或幾個虛函數(shù)的類及 其派生類時,對該類建立虛函數(shù)表(Virtual function table,vtable)。 虛函數(shù)表使執(zhí)行程序正確選擇每次執(zhí)行時應(yīng)使用的虛函數(shù)。 多態(tài)是由復雜的數(shù)據(jù)結(jié)構(gòu)實現(xiàn)的,參見圖8.10。圖8.10是以【例8.10】為基礎(chǔ)的,不過增加了一個由抽象類Object派生的復數(shù)數(shù)據(jù)類Compl
40、exObject。圖中列出了基類和各派生類的虛函數(shù)表,這些表是由指向函數(shù)的指針組成的。 8.6.4 動態(tài)綁定(選讀) 還有第二層指針,在實例化帶虛函數(shù)的類(創(chuàng)建對象)時,編譯器在對象前加上一個指向該類的虛函數(shù)表的指針。 第三層指針是鏈表結(jié)點類對象中指向抽象基類Object的指針(這也可以是引用,但本例是指針)。 虛函數(shù)的調(diào)用是這樣進行的,考慮虛函數(shù)Compare(),則看含“cat”的結(jié)點。由該結(jié)點的info指針找到含“cat”的無名對象,再由對象前的指針找到StringObject虛函數(shù)表,移動4個字節(jié)(一個指針占4個字節(jié))找到比較函數(shù)指針,進入串比較函數(shù)。UML類圖中的依賴關(guān)系依賴(dep
41、endency)依賴表示兩個元素之間存在一種關(guān)系,其中一個元素(提供者)的變化將影響另一個元素(客戶),或向它(客戶)提供所需信息。但兩者反過來是不成立的。這是將數(shù)種不同的建模關(guān)系組織到一起的簡便方法。在UML的基本模型中的依賴關(guān)系在C+編程中常見的有:綁定(bind):為模板參數(shù)指定值,以生成一個新的模型元素。等效C+模板中的實例化。屬綁定依賴。訪問(access):允許某個包訪問另一個包的內(nèi)容。以下屬許可依賴。友元(friend):允許某元素訪問另一個元素,而不管被訪問者是否可見。UML類圖中的依賴關(guān)系依賴(dependency)調(diào)用(call):聲明某個類調(diào)用其他類的操作方法。以下屬使用
42、依賴。參數(shù)(parameter):一個操作和它的參數(shù)之間的關(guān)系。實例化(instantiate):這里的實例化不同于C+模板,而是泛指從概念到實體,即創(chuàng)建實例。如由類創(chuàng)建對象、由用例創(chuàng)建用例實例。創(chuàng)建實例的機制是運行時環(huán)境的職責。發(fā)送(send):信號發(fā)送者與接受者之間的關(guān)系。另外有抽象依賴,包括跟蹤(trace)、精化(refine)、實現(xiàn)(realize)、導出(derive)。UML類圖中的依賴關(guān)系依賴通常用一個從客戶指向提供者的虛箭頭表示。例:下圖中類time12是12小時計時,精確到分;而time24是24小時計時,精確到秒??梢詫?4小時制轉(zhuǎn)換到12小時制,反之不行。類time12
43、是客戶,類time24是提供者。UML類圖中的依賴關(guān)系*綁定: 在計算機編程中,綁定是在某個時間范圍和特定的位置內(nèi)為兩個或更多編程對象或值對象創(chuàng)建聯(lián)系的過程,內(nèi)涵十分廣泛。編譯程序時,綁定意味著用一個真實值替換程序中的變量值,或用來保證另一些程序和被編譯的程序一起被加載到存儲器中。在本C+教材中提到靜態(tài)綁定和動態(tài)綁定是一個具體的實例,也譯作靜態(tài)聯(lián)編和動態(tài)聯(lián)編;本教材也提到類模板和函數(shù)模板分兩步進行編譯,其中模板實例化也是綁定的一個實例。但是在同一個地方總是要用不同的名稱來區(qū)分具體的事物,這就造成了C+和UML的術(shù)語選用的差異。綁定這個術(shù)語也用于網(wǎng)絡(luò)通信中,任何兩個網(wǎng)絡(luò)終端、實體、過程或邏輯單元
44、之間建立起一個明確的連接都可以稱為綁定。 完第八章 繼承與派生謝謝!【例8.1】由在冊人員類公有派生學生類Person:Person(string id, string name,Tsex sex,int birthday, string homeadd)IdPerson=id;Name=name;Sex=sex;Birthday=birthday;HomeAddress=homeadd; /作為一個管理程序,這個構(gòu)造函數(shù)并無必要,因為數(shù)據(jù)總是另外輸入的。僅為說明語法存在。分析構(gòu)造函數(shù):【例8.1】由在冊人員類公有派生學生類Person:Person()IdPerson=#;Name=#;Se
45、x=mid;Birthday=0;HomeAddress=#;分析默認的構(gòu)造函數(shù):分析析構(gòu)函數(shù):Person:Person() /string內(nèi)部動態(tài)數(shù)組的釋放,由string自帶的析構(gòu)函數(shù)完成【例8.1】由在冊人員類公有派生學生類void Person:SetName(string name)Name=name; /拷入新姓名修改名字:void Person:SetHomeAdd(string homeadd)HomeAddress=homeadd;修改住址:【例8.1】由在冊人員類公有派生學生類void Person:PrintPersonInfo()int i;cout身份證號:IdPe
46、rsonn姓名: Namen性別:;if(Sex=man)cout男n;else if(Sex=woman)cout女n; else cout n;cout出生年月日:;i=Birthday;couti/10000年;i=i%10000;couti/100月i%100日n 家庭住址:HomeAddressn;輸出個人信息:【例8.1】由在冊人員類公有派生學生類Student:Student(string id, string name,Tsex sex,int birthday, string homeadd, string nostud) :Person(id,name,sex,birthd
47、ay,homeadd) /注意Person參數(shù)名表不用類型int i;NoStudent=nostud;for(i=0;i30;i+) /課程與成績清空csi.coursename=#;csi.grade=0;派生類構(gòu)造函數(shù):【例8.1】由在冊人員類公有派生學生類Student:Student()/基類默認的無參數(shù)構(gòu)造函數(shù)不必顯式給出int i; NoStudent=“#;for(i=0;i30;i+) /課程與成績清空,將來由鍵盤輸入 csi.coursename=“#;csi.grade=0;Student:Student() /基類析構(gòu)函數(shù)以及成員對象析構(gòu)函數(shù)自動調(diào)用默認派生類構(gòu)造函數(shù):
48、派生類析構(gòu)函數(shù):【例8.1】由在冊人員類公有派生學生類int Student:SetCourse(string coursename,int grade) bool b=false; /標識新輸入的課程,還是更新成績 int i; for(i=0;i30;i+) if(csi.coursename=#) /判表是否進入未使用部分(如有對應(yīng)刪除,應(yīng)按順序表方式)csi.coursename=coursename;csi.grade=grade;b=false;break; else if(csi.coursename=coursename)/是否已有該課程記錄 csi.grade=grade;b
49、=true;break; if(i=30) return 0; /成績表滿返回0 if(b) return 1; /修改成績返回1 else return 2; /登記成績返回2學生類課程設(shè)置函數(shù):【例8.1】由在冊人員類公有派生學生類int Student:GetCourse(string coursename) int i; for(i=0;i30;i+) if(csi.coursename=coursename) return csi.grade; return -1; /找到返回成績,未找到返回-1查找學生課程成績函數(shù):【例8.1】由在冊人員類公有派生學生類void Student:P
50、rintStudentInfo()int i;cout學號:NoStudentn;PrintPersonInfo();for(i=0;i30;i+) /打印各科成績if(csi.coursename!=#) coutcsi.coursename tcsi.graden;else break;cout-完- endl;打印學生情況函數(shù):例8.1驗證用主函數(shù):int main(void) char temp30; int i,k; Person per1(320102820818161,沈俊, man,19820818,南京四牌樓2號); Person per2; per2.SetName(朱明)
51、; per2.SetSex(woman); per2.SetBirth(19780528); per2.SetId(320102780528162); per2.SetHomeAdd(南京市成賢街9號); per1.PrintPersonInfo(); per2.PrintPersonInfo(); Student stu1(320102811226161,朱海鵬, man,19811226,南京市黃浦路1號,06000123); cout請輸入各科成績:temp; /輸入格式:物理 80 if(!strcmp(temp,end) break; cink; i=stu1.SetCourse(t
52、emp,k); if(i=0) cout成績列表已滿!n; else if(i=1) cout修改成績n; else cout登記成績n; stu1.PrintStudentInfo(); while(1) cout查詢成績n請輸入科目:temp; if(!strcmp(temp,end) break; k=stu1.GetCourse(temp); if(k=-1) cout未查到n; else coutkn; return 0;【例8.2】由圓和高多重繼承派生出圓錐class Circleprotected: float x,y,r; /(x,y)為圓心,r為半徑public: Circl
53、e(float a=0,float b=0,float R=0)x=a;y=b;r=R; void Setcoordinate(float a,float b)x=a;y=b; /設(shè)置圓心坐標 void Getcoordinate(float &a,float &b)a=x;b=y; void SetR(float R)r=R;/設(shè)置半徑 float GetR()return r;/取圓半徑 float GetAreaCircle()return float(r*r*3.14159); /取圓面積 float GetCircumference()return float(2*r*3.14159
54、); /取圓周長;高類Line:【例8.2】由圓和高多重繼承派生出圓錐class Lineprotected: float High;public: Line(float a=0)High=a; void SetHigh(float a)High=a; float GetHigh()return High;class Cone:public Circle,public Linepublic: Cone(float a,float b,float R,float d):Circle(a,b,R),Line(d) float GetCV()return float(GetAreaCircle()*
55、High/3); /取得圓錐體積 float GetCA() /取得圓錐表面積 return float(GetAreaCircle()+r*3.14159*sqrt(r*r+High*Hgih); /共有派生類中能直接訪問直接基類的保護成員【例8.2】由圓和高多重繼承派生出圓錐派生類圓錐:在VC+平臺上運行例8.2int main()Cone c1(5,8,3,4);float a,b;cout圓錐體積:c1.GetCV()n;cout圓錐表面積:c1.GetCA()n;cout圓錐底面積:c1.GetAreaCircle()n;cout圓錐底周長:c1.GetCircumference()
56、n;cout圓錐底半徑:c1.GetR()n;c1.Getcoordinate(a,b);cout圓錐底圓心坐標:(a,b)n;cout圓錐高:c1.GetHigh()n;return 0;【例8.2】由圓和高多重繼承派生出圓錐檢證主程序:class Student:public virtual Personstring NoStudent; /學號/30門課程與成績略public:Student(string id, string name,Tsex sex,int birthday, string homeadd, string nostud);Student(); Student()co
57、ut析構(gòu)Studentendl;void PrintStudentInfo(); ;Student:Student(string id, string name,Tsex sex, int birthday, string homeadd, string nostud) :Person(id,name,sex,birthday,homeadd) /注意Person參數(shù)名表不用類型cout構(gòu)造Studentendl;NoStudent=nostud;例8.4 虛基類與在職研究生以虛基類定義公有派生的學生類:Student:Student() /基類默認的無參數(shù)構(gòu)造函數(shù)不必顯式給出cout構(gòu)造St
58、udentendl;void Student:PrintStudentInfo()cout學號:NoStudentn;PrintPersonInfo();例8.4 虛基類與在職研究生以虛基類定義公有派生的學生類:class GStudent:public Student /以虛基類定義公有派生的研究生類 string NoGStudent; /研究生號,其他略public: GStudent(string id, string name,Tsex sex,int birthday, string homeadd, string nostud,string nogstudent); /注意派生類
59、構(gòu)造函數(shù)聲明方式 GStudent(); GStudent()cout析構(gòu)GStudentendl; void PrintGStudentInfo();GStudent:GStudent(string id, string name,Tsex sex,int birthday, string homeadd, string nostud, string nogstud): Student(id,name,sex,birthday,homeadd,nostud), Person(id,name,sex,birthday,homeadd) /因Person是虛基類,盡管不是直接基類, Person
60、必須出現(xiàn)。 /不定義對象可不出現(xiàn),為通用應(yīng)出現(xiàn)。如不是虛基類,出現(xiàn)是錯誤的 cout構(gòu)造GStudentendl; NoGStudent=nogstud;例8.4 虛基類與在職研究生以虛基類定義公有派生的研究生類:GStudent:GStudent() /基類默認的無參數(shù)構(gòu)造函數(shù)不必顯式給出cout構(gòu)造GStudentendl;void GStudent:PrintGStudentInfo()cout研究生號:NoGStudentn;PrintStudentInfo();例8.4 虛基類與在職研究生以虛基類定義公有派生的研究生類:例8.4 虛基類與在職研究生class Employee:pub
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
- 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 專利購買合同范本
- 中藥材種植技術(shù)服務(wù)合同
- 個人理財顧問合同「樣本」
- 二手教練車交易合同模板
- 三方資產(chǎn)轉(zhuǎn)讓合同標準范本
- 上海市租賃住房租賃合同
- 二手房裝修改造合同簡單范本
- 個人向公司借款合同范例
- 不可撤銷合同擔保協(xié)議范本大全
- 個人購房借款合同范本
- 家庭法律服務(wù)行業(yè)發(fā)展趨勢及前景展望分析報告
- 20210年中考英語復習:閱讀理解信息歸納摘錄考題匯編(含答案)
- 團餐服務(wù)培訓資料
- (正式版)SHT 3225-2024 石油化工安全儀表系統(tǒng)安全完整性等級設(shè)計規(guī)范
- 人教版六年級數(shù)學上冊《應(yīng)用題》專項練習題(含答案)
- 第三單元 嘆錦繡中華書傳統(tǒng)佳話(教學設(shè)計) 三年級語文下冊大單元教學(部編版)
- 商業(yè)秘密培訓課件模板
- 網(wǎng)絡(luò)與信息安全管理培訓資料2024
- 洛奇化石復原腳本
- 道路交通安全法律法規(guī)課件
- 人教版三年級上冊豎式計算練習300題及答案
評論
0/150
提交評論