C++程序設(shè)計第8章.ppt_第1頁
C++程序設(shè)計第8章.ppt_第2頁
C++程序設(shè)計第8章.ppt_第3頁
C++程序設(shè)計第8章.ppt_第4頁
C++程序設(shè)計第8章.ppt_第5頁
免費預(yù)覽已結(jié)束,剩余49頁可下載查看

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)

文檔簡介

1、習(xí)題: P307 習(xí)題8.6 幾何圖形的派生關(guān)系 本周實驗內(nèi)容: 1. 習(xí)題調(diào)試上傳 2. MFC作業(yè)設(shè)計 第8章類的繼承與派生 (1)理解繼承的概念,了解面向?qū)ο笤O(shè)計中繼承和多態(tài)的合理性; (2)掌握派生類的構(gòu)造與析構(gòu); (3)掌握在對象中使用類層次和繼承思想進(jìn)行設(shè)計、實現(xiàn)和測試; (4)理解多重繼承,了解虛基類; (5)區(qū)別運行時的多態(tài)性的實現(xiàn),理解重載與同名覆蓋的差異; (6)理解虛函數(shù)與多態(tài)性。 (7)實現(xiàn)運行時多態(tài)性的程序設(shè)計; 注:本章8.6.3,8.6.4節(jié)不要求,class Date int Year,Month,Day; /分別存放年、月、日 public: Date(int

2、 y=0, int m=0,int d=0); void SetDate(int ,int ,int ); / 完成數(shù)據(jù)成員的賦初值 void GetDate(char *); / 將日期(整數(shù))轉(zhuǎn)換成“年/月/日” 形式的字符串后,存放到參數(shù)所指向的字符串中 ; class Time int Hours,Minutes,Seconds;/時、分、秒 public: Time(int h=0,int m=0, int s=0); void SetTime(int h,int m, int s); / 完成數(shù)據(jù)成員的賦初值 void GetTime(char *); / 將時間(整數(shù))轉(zhuǎn)換成“時

3、:分:秒”形式的字符串,存/放到參數(shù)所指向的字符串中 ;,class DateTime:public Date,public Time/公有派生 public: DateTime(); DateTime(int y,int m,int d,int h,int min,int s); void GetDateTime(char *); void SetDateTime(int y,int m,int d,int h,int min,int s); ; int main() 結(jié)果在此輸出 ,輸出結(jié)果為: 實驗日期為:2013/5/9 實驗時間為:9:30:50 實驗日期和實驗時間分別是:2013/

4、5/9; 9:30:50 實驗日期和實驗時間分別是:2013/5/9; 11:15:10,把一個整數(shù)變換成字符串可通過庫函數(shù): #include char * itoa(int a , char *s, int b); 參數(shù)a為要變換的整數(shù),b為數(shù)制的基數(shù)(如10, 表示將a轉(zhuǎn)換為對應(yīng)的十進(jìn)制的字符串),轉(zhuǎn)換的結(jié)果 存放到s所指向的字符串中。函數(shù)返回變換后字符串的 首指針。例: itoa(Year,s,10);/將年變換為字符串表示 itoa(Month,t,10);/將月變換為字符串表示,Date: Date(int y=0, int m=0,int d=0) Year= y; Month

5、= m; Day = d; void Date:SetDate(int y,int m,int d ) Year= y; Month = m; Day = d; void Date:GetDate(char *s) char t20; itoa(Year,s,10); strcat(s,/); itoa(Month,t,10); strcat(s,t); strcat(s,/); itoa(Day,t,10); strcat(s,t); ,Time:Time(int h=0,int m=0, int s=0) Hours = h; Minutes = m; Seconds = s; void

6、Time: SetTime(int h,int m, int s) Hours = h; Minutes = m;Seconds = s; void Time:GetTime(char *s) char t20; itoa(Hours,s,10); strcat(s,:); itoa(Minutes,t,10); strcat(s,t); strcat(s,:); itoa(Seconds,t,10); strcat(s,t); ,DateTime :DateTime():Date(),Time() DateTime : DateTime(int y,int m,int d,int h,int

7、 min,int s): Date(y,m,d),Time(h,min,s) void DateTime:GetDateTime(char *s) char s1100,s2100; GetDate(s1); GetTime(s2); strcpy(s,實驗日期和實驗時間分別是:); strcat(s,s1); strcat(s,; );strcat(s,s2); void DateTime:SetDateTime(int y,int m,int d,int h,int min,int s) SetDate(y,m,d); SetTime(h,min,s); ,int main(void )

8、Date d1(2013,5,9); char s200; d1.GetDate(s); cout實驗日期為:sn; Time t1(9,30,50); t1.GetTime(s); cout實驗時間為:sn; DateTime dt1(2013,5,9, 9,30,50); dt1.GetDateTime(s); coutsn; dt1.SetDateTime(2013,5,9,11,15,10); dt1.GetDateTime(s); coutsn; return 0; ,下面是用鏈表實現(xiàn)對候選人的得票數(shù)進(jìn)行統(tǒng)計的程序。 函數(shù)Statistic的輸入?yún)?shù)head指向鏈?zhǔn)祝琻ame存放候選

9、人的姓名。 該函數(shù)的功能為:若在鏈表的結(jié)點上找到name,則將姓名為name的 結(jié)點上的得票數(shù)加1;否則新建一個結(jié)點,初始化其姓名和得票數(shù), 并將新結(jié)點插入鏈尾。最后返回鏈表的首指針。,struct Node char name12; /候選人姓名 int count; /計數(shù)候選人的得票 Node *next; ;,int main(void) /連續(xù)輸入得票候選人的姓名,以輸入字符O,結(jié)束 Node *head=0; char name12; coutname; while(strcmp(name,O)!=0) head=Statistic(head,name); coutname; co

10、ut統(tǒng)計得票結(jié)果:n姓名 得票數(shù)n; List(head); Free(head); return 0; ,Node *Statistic(Node *head,char *name) Node *p1=head,*p2; if(head=0) head=new Node; strcpy(head-name,name); head-count=1; head-next=0; else while(p1) if( strcmp(p1-name,name)=0 ) p1-count+; break; else p2=p1; p1=p1-next ; /while循環(huán)結(jié)束,if( p1=NULL )

11、 p1=new Node; strcpy(p1-name,name); p1-count=1; p1-next=0; p2-next=p1;/p2-next=p1 /else結(jié)束 return head ; ,void List(Node *head) /輸出結(jié)果 Node *p=head ; while(p) coutnamecountnext ; void Free(Node *head) /釋放鏈表空間 Node *p; while(head) p=head; head=head-next; delete p; ,類B 是類A的派生類,A類,B類,C類,D類,類A拷貝,類C 是類A的派生

12、類,類D 是類B和類C的派生類,這樣,類D中就有兩份類A的拷貝,B類,C類,類A拷貝,虛基類(virtual base class),這種同一個公共的基類在派生類中產(chǎn)生多個拷貝,不僅多占用了存儲空間,而且可能會造成多個拷貝中的數(shù)據(jù)不一致 和模糊的引用。,D d; d.x=10; /模糊引用,使用虛基類使得公共基類在它的派生類對象中只產(chǎn)生一個基類對象 。虛基類的定義格式如下:,class 派生類名:virtual 訪問控制符 基類類名.; class 派生類名:訪問控制符 virtual 基類類名.;,virtual 關(guān)鍵字只對緊隨其后的基類名起作用:,class B:public virtua

13、l A public: int y; B(int a=0, int b=0 ):A(b) y=a; ;,A類,B類,C類,D類,一份拷貝,在D( )的構(gòu)造函數(shù)中直接調(diào)用A(),由虛基類派生出的對象初始化時,直接調(diào)用虛基類的構(gòu)造函數(shù)。因此,若將一個類定義為虛基類,則一定有正確的構(gòu)造函數(shù)可供所有派生類調(diào)用。,再次強調(diào),用虛基類進(jìn)行多重派生時,若虛基類沒有缺省的構(gòu)造函數(shù),則在每一個派生類的構(gòu)造函數(shù)中都必須有對虛基類構(gòu)造函數(shù)的調(diào)用 (且首先調(diào)用)。,賦值兼容規(guī)則,基類對象,派生類對象,相互之間能否賦值?,可以將派生類對象的值賦給基類對象。反之不行,Base b;,Derive d;,b = d;,只是

14、給從基類繼承來的成員賦值。,20,可以將一個派生類對象的地址賦給基類的指針變量。,基類對象,派生類對象,Base b;,Derive d;,Base *basep;,basep,basep=,basep,basep = ,Base ,基類對象,派生類對象,Base b;,Derive d;,別名b,b只能引用從基類繼承來的成員。,基類引用,派生類對象,賦值兼容規(guī)則,公有派生類對象可以賦值給其基類的對象,反之不行; 派生類的對象可以初始化基類的引用; 派生類對象的地址可以賦給基類型的指針。,8.6 多態(tài)性與虛函數(shù),多態(tài)性是面向?qū)ο蟪绦蛟O(shè)計的關(guān)鍵技術(shù)之一。指的是調(diào)用同樣的函數(shù)實現(xiàn)不同的功能。,在C

15、+中有兩種多態(tài),編譯時的 多態(tài)性 (靜態(tài)多態(tài)性),運行時的多態(tài)性,在程序執(zhí)行前無法確定調(diào)用哪一個函數(shù),必須在程序執(zhí)行過程中,根據(jù)執(zhí)行的具體情況來動態(tài)地確定。通過類繼承關(guān)系和虛函數(shù)來實現(xiàn)的。目的:建立一種通用的程序。,通過函數(shù)的重載和運算符的重載實現(xiàn)。,多態(tài)性是“一個接口,多種方法”: 通過繼承產(chǎn)生了相關(guān)的不同的派生類,和基類成員同 名的成員在不同的派生類中有不同的含義。,虛函數(shù)的作用: 允許在派生類中重新定義與基類同名的函數(shù),并且 可以通過基類指針或引用來訪問基類和派生類中的同名 函數(shù)。,虛函數(shù)是一個類的成員函數(shù),定義虛函數(shù)的格式如下: virtual 返回類型 函數(shù)名(參數(shù)表);,注:vir

16、tual僅用于類定義中,若虛函數(shù)在類外定義,不能 加virtual(類中函數(shù)說明要加virtual )。,當(dāng)某一個類的一個類成員函數(shù)被定義為虛函數(shù),則由 該類派生出來的所有派生類中,該函數(shù)始終保持虛函數(shù)的 特征。,8.6.1 虛函數(shù)的定義,在派生類中重新定義虛函數(shù)(稱超載或覆蓋)時,不必再加關(guān)鍵字virtual,但函數(shù)頭一定要完全相同。,虛函數(shù)的使用: (1) 在基類用virtual聲明成員函數(shù)為虛函數(shù)。,(2) 在派生類中重新定義此函數(shù),要求函數(shù)名、函數(shù)類型、 函數(shù)參數(shù)個數(shù)和類型全部與基類的虛函數(shù)相同,并根據(jù) 派生類的需要重新定義函數(shù)體。,(3) 定義一個指向基類對象的指針變量,并使它指向同

17、一 類族中需要調(diào)用該函數(shù)的對象。,(4) 通過該指針變量調(diào)用此虛函數(shù),此時調(diào)用的就是指針 變量指向的對象的同名函數(shù)。,例: class A public: virtual void show(); ; void A:show() coutA:show()endl; class B:public A public: void show() coutB:show()endl; ; class C:public B public: void show( ) coutC:show()endl; ;,int main() A a1, *p; B b1; C c1; p= /動態(tài)調(diào)用 ,運行結(jié)果: A:s

18、how() B:show() C:show(),int main() C c; c.A:show(); /靜態(tài)調(diào)用 c.B:show(); /靜態(tài)調(diào)用 c.show(); /靜態(tài)調(diào)用 ,【例8.6】計算學(xué)分。 由本科生類派生出研究生類GradeStudent,但它們各自的從課程學(xué)時數(shù)折算為學(xué)分?jǐn)?shù)的算法是不同的,本科生是16個學(xué)時一學(xué)分,而研究生是20個學(xué)時一學(xué)分。 class Student char coursename20;/課程名 int classhour;/學(xué)時 int credit; /學(xué)分,未考慮0.5學(xué)分 public: Student();/構(gòu)造函數(shù),將類成員賦值為0 vir

19、tual void Calculate();/ 計算學(xué)分(考慮該函數(shù)的設(shè)置) void SetCourse(char *str,int hour);/設(shè)置課程和學(xué)時 int GetHour();/獲取學(xué)時數(shù) void SetCredit(int cred); /獲取學(xué)分 void Print();/顯示學(xué)時和學(xué)分 ;,class Student char coursename20; /課程名 int classhour; /學(xué)時 int credit; /學(xué)分 public: Student() coursename0=0; classhour=0; credit=0; virtual voi

20、d Calculate() credit=classhour/16; void SetCourse(char *str,int hour) /設(shè)置課程和學(xué)時 strcpy(coursename,str); classhour=hour; ,int GetHour() return classhour; /獲取學(xué)時數(shù) void SetCredit(int cred) credit=cred; /獲取學(xué)分 voidPrint() /顯示學(xué)時和學(xué)分 coutcoursenametclasshour學(xué)時 tcredit學(xué)分endl; ;,class GradeStudent:public Studen

21、t public: GradeStudent() ; /基類缺省構(gòu)造函數(shù)調(diào)用可省 void Calculate() SetCredit(GetHour()/20); ;,int main() Student s,*ps; GradeStudent g; s.SetCourse(物理,80); s.Calculate(); g.SetCourse(物理,80); g.Calculate(); cout本科生:t; s.Print(); cout“研究生:t; g.Print(); s.SetCourse(“數(shù)學(xué),160);,g.SetCourse(數(shù)學(xué),160); ps= ,輸出結(jié)果為: 本科生

22、:物理 80 學(xué)時 5學(xué)分 研究生:物理 80 學(xué)時 4學(xué)分 本科生:數(shù)學(xué) 160學(xué)時 10學(xué)分 研究生:數(shù)學(xué) 160學(xué)時 8學(xué)分,使用基類引用去指向不同對象,同樣可實現(xiàn)運行時多態(tài)性。,void Calfun(Student ,【例8.7】計算學(xué)分,基類與派生類定義同【例8.6】(基類派生類函數(shù)不變),增加函數(shù):,幾點提示:,5.虛函數(shù)重構(gòu)不同于重載。派生類中定義虛函數(shù)必須與基類中的虛函數(shù)同名外,還必須同參數(shù)表,同返回類型。否則被認(rèn)為是重載,而不是虛函數(shù)。,1.實現(xiàn)動態(tài)多態(tài)性需要三個條件:派生類體系、虛函數(shù)、指針或引用。,2.析構(gòu)函數(shù)可定義為虛函數(shù),構(gòu)造函數(shù)不能定義虛函數(shù)。在基類及其派生類都有

23、動態(tài)分配的內(nèi)存空間時,應(yīng)當(dāng)考慮把析構(gòu)函數(shù)定義為虛函數(shù),以便能夠用基類指針實現(xiàn)撤消對象的多態(tài)性。,3. 虛函數(shù)執(zhí)行速度稍慢。為了實現(xiàn)多態(tài)性,每一個派生類中均要保存相應(yīng)虛函數(shù)的入口地址表,函數(shù)的調(diào)用機制也與一般函數(shù)不同。這是多態(tài)性為實現(xiàn)通用性付出的代價。,4. 靜態(tài)成員函數(shù)不能作為虛函數(shù),因為它不屬于某個對象,而是為所有同類對象共有。內(nèi)聯(lián)函數(shù)也不能作為虛函數(shù)。,聲明虛函數(shù)的成員函數(shù)主要考慮以下幾點:,首先看成員函數(shù)所在的類是否會作為基類。然后看成 員函數(shù)在類的繼承后有無可能被更改功能,如果希望更改 其功能的,一般應(yīng)該將它聲明為虛函數(shù)。,(2) 如果成員函數(shù)在類被繼承后功能不需修改,或派生類 用不到

24、該函數(shù),則不要把它聲明為虛函數(shù)。,(3) 應(yīng)考慮對成員函數(shù)的調(diào)用是通過對象名還是通過基類 指針或引用去訪問,如果是通過基類指針或引用去訪問 的,則應(yīng)當(dāng)聲明為虛函數(shù)。,虛析構(gòu)函數(shù),如果用new運算符建立了臨時對象,若基類中有析構(gòu) 函數(shù),并且定義了一個指向該基類的指針變量。在程序用 帶指針參數(shù)的delete運算符撤銷對象時,會出現(xiàn): 系統(tǒng)會 只執(zhí)行基類的析構(gòu)函數(shù),而不執(zhí)行派生類的析構(gòu)函數(shù)。,解決辦法: 把析構(gòu)函數(shù)定義為虛函數(shù),實現(xiàn)撤消對象時的多態(tài)性。,當(dāng)程序使用多態(tài)性并且為不同的類分配內(nèi)存時,應(yīng)該 使用虛析構(gòu)函數(shù)來保證內(nèi)存按照需要被釋放。,虛析構(gòu)函數(shù)聲明: virtual 類名( );,class

25、 A public: A() coutA析構(gòu)n; ; class B:public A public: B(); B(); private: int *p; ;,B:B() p=new int(); B:B() coutB析構(gòu)n; delete p; void fun(A *a) delete a; int main() A *a=new B; /a是指向基類的指針變量指向new開辟的動態(tài)存儲空間 fun(a); return 0; ,輸出:A析構(gòu),virtual,輸出:B析構(gòu) A析構(gòu),把基類的析構(gòu)函數(shù)聲明為虛函數(shù)??墒顾信缮惖?析構(gòu)函數(shù)自動成為虛函數(shù)(即使函數(shù)名不同)。若程序中 顯式地用

26、了delete運算符準(zhǔn)備刪除一個對象,而delete運算 符的操作對象用了指向派生類對象的基類指針,系統(tǒng)會調(diào) 用相應(yīng)類的析構(gòu)函數(shù)。,1. 虛函數(shù)是動態(tài)關(guān)聯(lián)的基礎(chǔ)。,2. 虛函數(shù)是非靜態(tài)的成員函數(shù)。,3. virtual 只用來說明類聲明中的原型,不能用在函數(shù)實 現(xiàn)時。,4. 具有繼承性,基類中聲明了虛函數(shù),派生類中無論是 否說明,同原型函數(shù)都自動為虛函數(shù)。,5. 不是重載聲明而是覆蓋。,6. 通過基類指針或引用,執(zhí)行時會根據(jù)指針指向的對象 的類,決定調(diào)用哪個函數(shù)。,class shape public: shape(char*name,int x,int y); virtual void dr

27、aw(); shape(); protected: char name64; int x,y; ; class circle:public shape public: circle(char*name,int x,int y,int radius); void draw(); circle(); private: char name64; int radius; ;,virtual,shape:shape(char*name,int x,int y) strcpy(shape:name,name); shape:x=x; shape:y=y; void shape:draw() cout“sh

28、ape:draw():nameendl; shape:shape() cout“shape 析構(gòu)endl; circle:circle(char *name,int x,int y,int radius): shape(name,x,y) circle:radius=radius; void circle:draw() cout“circles:draw():; coutx=xty=ytradius=radiusendl; circle:circle() cout“circle析構(gòu)endl; ,int main() shape *graphic=new circle(Circle,10,20,

29、30); graphic-draw(); delete graphic; ,輸出: circle:draw(): x=10 y=20 radius=30 shape析構(gòu),輸出:(將基類析構(gòu)函數(shù)說明成虛析構(gòu)函數(shù)) circle:draw(): x=10 y=20 radius=30 circle析構(gòu) shape析構(gòu),draw()函數(shù)為虛函數(shù),但shape()不是,在許多情況下,在基類中不能對虛函數(shù)給出有意義的 實現(xiàn),而把它聲明為純虛函數(shù),它的實現(xiàn)留給該基類的派 生類去做。這就是純虛函數(shù)的作用。,例如,動物作為一個基類可以派生出老虎、孔雀等子類, 但動物本身生成對象明顯不合常理。,為了解決這個問題

30、,引入了純虛函數(shù)的概念,將函數(shù) 定義為純虛函數(shù)。 若要使派生類為非抽象類,則編譯器要求在派生類中, 必須對純虛函數(shù)予以重載以實現(xiàn)多態(tài)性。同時含有純虛函 數(shù)的類稱為抽象類,但它不能生成對象。,8.6.2 純虛函數(shù),例:,class Point public: . double Area()return 0; . ;,class Circle :public Point public: double Area() return PI*radius*radius; ;,基類函數(shù)為空或可以不要,在實現(xiàn)部分仍然要寫出函數(shù)體,純虛函數(shù)可以解決接口,1. 純虛函數(shù)不同于空函數(shù),純虛函數(shù)不能調(diào)用。 2.派生類

31、中必須重新定義純虛函數(shù)的函數(shù)體。 3.含有純虛函數(shù)的基類不能用來定義對象,稱為抽象類。,純虛函數(shù)(pure virtual function)當(dāng)基類中某虛函數(shù)無法具體實現(xiàn),可定義為純虛函數(shù),具體實現(xiàn)依賴于派生類。定義格式為:,定義純虛函數(shù)要注意:,virtual 返回類型 函數(shù)名(參數(shù)表)=0;,注: 純虛函數(shù)沒有函數(shù)體; 最后面的“=0”并不表示函數(shù)返回值為0,它只 起形式上的作用,告訴編譯系統(tǒng)“這是純虛函數(shù)”; 這是一個聲明語句,最后應(yīng)有分號。,抽象類,帶有純虛函數(shù)的類稱為抽象類: class 類名 virtual 類型 函數(shù)名(參數(shù)表)=0; /純虛函數(shù) . ,抽象類作用: 通過它為一個

32、類族建立一個公共接口,使它們能夠有 效發(fā)揮多態(tài)特性。目的,通過它多態(tài)地使用各類中的成員 函數(shù)。,對于暫時無法實現(xiàn)的函數(shù),可以聲明為純虛函數(shù),留 給派生類去實現(xiàn)。,注意,抽象類只能作為基類來使用。 不能聲明抽象類的對象。 構(gòu)造函數(shù)不能是虛函數(shù),析構(gòu)函數(shù)可以是虛函數(shù)。,例: 定義一個抽象類shape用以計算面積,從中派生出計算圓、矩形和三角形面積的派生類。,const double PI =3.1415926; class shape /抽象基類 public: virtual void display( )= 0; virtual double Area()=0; /求面積 ; class circle: public shape protected: double r; public: circle(double x=0) r=x; void display( ) cout圓半徑rendl; double Area() return PI*r*r; ;,class rectangle:public shape double l,w; public: rectangle(double x=0,double y=0) l=x; w=y; void display( ) cout長: lt寬: wendl; double Area() return l*w

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論