C++繼承與多態(tài)性.ppt_第1頁
C++繼承與多態(tài)性.ppt_第2頁
C++繼承與多態(tài)性.ppt_第3頁
C++繼承與多態(tài)性.ppt_第4頁
C++繼承與多態(tài)性.ppt_第5頁
已閱讀5頁,還剩72頁未讀, 繼續(xù)免費閱讀

下載本文檔

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

文檔簡介

1、1,第五章:繼承性和多態(tài)性,2,第1部分繼承與派生,繼承的概念與派生的定義 派生類的繼承方式 派生類的構(gòu)造和析構(gòu)函數(shù) 多重繼承 基類和派生類的轉(zhuǎn)換,3,1. 繼承的概念和派生類的定義 繼承和派生的基本概念: 繼承性是面向?qū)ο蟪绦蛟O(shè)計的重要特性之一,C+程序的可重用性是通過繼承機制來實現(xiàn)的。 繼承在概念上將具有從屬關(guān)系的類連接起來,便于描述現(xiàn)實實體的層次關(guān)系。 在C+中,所謂“繼承”就是在一個已存在的類的基礎(chǔ)上建立一個新的類。,基類(父類),派生類(子類),一個新類從已有的類那里獲得其已有特性,稱為類的繼承。,4,派生類繼承了基類(父類)的所有數(shù)據(jù)成員和成員函數(shù),并可以對成員作必要的增加或調(diào)整。

2、 基類和派生類是相對而言的,形成類的繼承層次結(jié)構(gòu): 父類可以派生出多個子類。 子類又可以作為父類,再派生出新的派生類。 所有的子孫后代都繼承了祖輩的基本特征,同時又有區(qū)別和發(fā)展。,相反,從已有的類產(chǎn)生一個新的子類,稱為類的派生。,5,單繼承:一個派生類只繼承一個基類,這種繼承關(guān)系所形成的層次是一個樹形結(jié)構(gòu)。,例:,6,MFC關(guān)鍵類的層次體系,7,多繼承:一個派生類繼承兩個或多個基類。,8,派生類的定義方法: 前例中,已聲明過一個基類Student,在此基礎(chǔ)上通過單繼承建立一個派生類Student1。,class Student1 : public Student private: int ag

3、e; string addr; public: void diaplay_1(); void Student1:diaplay_1(); cout“age:”ageendl; cout“address:”addrendl; ,派生類名,繼承方式,基類名,9,單繼承派生類的聲明格式: class : ; 多繼承派生類的定義格式為: class : , ; 繼承方式包括:public(公用的),private(私有的)和protected(受保護的),缺省為私有的。,10,派生類的構(gòu)成: 派生類成員有兩部分:一是從基類繼承過來的成員,體現(xiàn)共性;二是自己增加的成員,體現(xiàn)個性。,問題:是不是簡單地將基

4、類成員和派生類自己的新增成員加在一起,就構(gòu)成一個新的派生類呢?,11,構(gòu)造一個派生類包括以下3部分工作: 不可選擇地從父類接收全部成員(構(gòu)造和析構(gòu)函數(shù)除外)。 缺陷:數(shù)據(jù)冗余、空間浪費和效率降低。這在目前C+中無法解決。 解決:在設(shè)計基類時要充分考慮到派生類的需要。(事實上有些類是專門作為基類而設(shè)計的) 調(diào)整從基類接收的成員: 方法一:改變基類成員在派生類中的訪問屬性通過指定繼承方式來實現(xiàn)。 方法二:在派生類聲明同名成員覆蓋基類成員。!注意,對于成員函數(shù),不僅要同名還要參數(shù)個數(shù)和類型都相同。(請問,這是為什么?) 在聲明派生類時增加成員: 體現(xiàn)派生類對基類功能的擴展,同時定義自己的構(gòu)造和析構(gòu)函

5、數(shù),這需要精心設(shè)計。,12,程序5-1:CPoint類及其派生類CRect,/基類Point類的聲明 class Point private: float X,Y; public: void InitP(float xx=0, float yy=0) X=xx; Y=yy; void Move(float xOff, float yOff) X+=xOff; Y+=yOff; float GetX() return X; float GetY() return Y; ;,/派生類聲明 class CRect: public Point private:/新增私有數(shù)據(jù)成員 float W,H;

6、public: /新增公有成員函數(shù) void InitR(float x, float y, float w, float h) InitP(x,y); /調(diào)用基類公有成員函數(shù)W=w;H=h; float GetH() return H; float GetW() return W; ;,#include #include int main() CRect rect; rect.InitR(2,3,20,10); /通過派生類對象訪問基類公有成員 rect.Move(3,2); coutrect.GetX(), rect.GetY(), rect.GetH(), rect.GetW()endl

7、; return 0; ,運行結(jié)果: 5, 5, 10, 20 Press any key to continue,Prm5_1.cpp,13,派生類成員的訪問屬性: 派生類具有兩部分成員,且不是簡單直接繼承,因而其訪問屬性勢必復(fù)雜。,基類成員在派生類中的訪問屬性涉及2點: 基類成員本身聲明的訪問屬性: 共有、私有、保護類型 派生類對基類的繼承方式: 共有繼承:保持原數(shù)據(jù)訪問屬性不變。 私有繼承:繼承來的成員都變?yōu)樗接小?保護繼承:原私有屬性不變,其他都為保護類型。,14,2、派生類的繼承方式: 公有繼承(public):保持C+封裝特性,保護私有成員,依然是不變的原則。,:public,例,

8、顯示學(xué)生完整情況 class Student private: int num; string name; char sex; public: void display(); ,class Student1 : public Student private: int age; string addr; public: void diaplay_1(); void Student1:display_1(); cout“num:”numendl; cout“name:”nameendl; cout“sex:”sexendl; cout“age:”ageendl; cout“address:”add

9、rendl; ,函數(shù)是否正確?為什么?,需要做怎樣的修改?,1,int main() Student1 st1; . . st1.display(); /調(diào)用基類公有函數(shù) st1.display_1(); /調(diào)用派生類函數(shù) return 0; ,15,可以知道:一個成員在不同派生層次中的訪問屬性可能是不同的,它與繼承方式有關(guān)。,私有繼承(private): 私有繼承可以阻斷下一代派生類繼續(xù)調(diào)用基類成員。,:private,16,class Student1 : private Student private: int age; string addr; public: void diaplay

10、_1(); void Student1:display_1(); cout“age:”ageendl; cout“address:”addrendl; ,int main() Student1 stud; . . stud.diaplay(); /調(diào)用基類公有函數(shù) stud.display_1(); /調(diào)用派生類函數(shù) return 0; ,程序應(yīng)該做怎樣的修改?, display(); cout“age:”ageendl; cout“address:”addrendl; ,分析主程序錯在哪里?,1,例, class Student private: int num; string name;

11、char sex; public: void display(); ,17,保護繼承(protected): 由protected聲明的成員稱為“受保護的成員”,或簡稱“保護成員”。 保護成員不能被類外訪問(等價于私有成員),但可以被派生類的成員函數(shù)引用(相當(dāng)于公有成員)。 保護繼承中,基類的public和protected成員都以protected身份出現(xiàn)在派生類中,基類的private成員不可訪問。,18,class A protected: int x; int main( ) A a; a.X=5; /錯誤 ,class A protected: int x; ; class B: p

12、rotected A public: void Function( ); ; void B:Function( ) X=5; /正確 ,19,class Student1 : protected Student private: int age; string addr; public: void diaplay_1(); void Student1:diaplay_1(); cout“num:”numendl; cout“name:”nameendl; cout“sex:”sexendl; cout“age:”ageendl; cout“address:”addrendl; ,int mai

13、n() Student1 stud; . . stud.display_1(); /調(diào)用派生類函數(shù) stud.num=10023; return 0; ,引用基類保護成員,非法!,引用基類保護成員,合法!,例, class Student protected: int num; string name; char sex; public: void diaplay(); ,20,分析上表: 基類中的私有成員在派生類中均為不可訪問。 其他成員總是在自身訪問屬性和繼承方式中選擇較嚴格者作為派生類中成員的訪問屬性。 比較私有繼承和保護繼承: 在直接派生類中的實際作用是相同的,即在類外都不能訪問,類中

14、的成員函數(shù)可以訪問。 在新的派生類中的作用不同,即原來私有基類中的成員在新類中都不能訪問,原來保護基類中的成員可以在新類中被訪問。,繼承方式總結(jié):,21,由此看出: 公有繼承是一種可持續(xù)式的繼承; 注意:欲在派生類中引用的基類成員,不要聲明為私有屬性。 私有繼承是一種絕斷式的繼承; 注意:再次派生將變得沒有意義。 保護繼承是一種隔絕式的繼承; 注意:類外不能訪問該派生類中的任何成員(包括成員函數(shù))。 派生類成員有4種訪問屬性,如下:,22,多級派生時的訪問屬性: A與B構(gòu)成直接基類和直接派生類的關(guān)系; B與C構(gòu)成直接基類和直接派生類的關(guān)系; A與C構(gòu)成間接基類和間接派生類的關(guān)系。,公有,保護,

15、保護,保護,保護,保護,不可訪問,不可訪問,不可訪問,私有,私有,不可訪問,私有,私有,不可訪問,可見:類的成員在不同作用域中有不同訪問屬性; 私有成員只能在本類中被訪問,畢竟派生類和基類不是同一個類;,23,3.派生類的構(gòu)造函數(shù)和析構(gòu)函數(shù): 派生類構(gòu)造函數(shù): 定義原則:除了對派生類數(shù)據(jù)成員初始化外,還要對基類的數(shù)據(jù)成員初始化。 解決思路:執(zhí)行派生類構(gòu)造函數(shù)時,調(diào)用基類構(gòu)造函數(shù)(基類構(gòu)造函數(shù)不能被繼承)。,24,例,顯示學(xué)生全部情況 class Student protected: int num; string name; char sex; public: Student(int n,st

16、ring nam,char s) num=n;name=nam;sex=s; Student() ,class Student1 : public Student private: int age; string addr; public: Student1(int n,string nam,char s,int a,string ad): Student(n,nam,s) age=a; addr=ad; void diaplay_1(); void Student1:diaplay_1(); cout“num:”numendl; cout“name:”nameendl; cout“sex:”

17、sexendl; cout“age:”ageendl; cout“address:”addrendlendl; ,int main() Student1 st1(10010,”Wang-li”,f,19,”Beijing”); Student1 st2(10012,”Xv-lin”,m,20,”Shenzhen”); . st1.display_1(); /輸出第一個學(xué)生的數(shù)據(jù) st2.display_1(); /輸出第二個學(xué)生的數(shù)據(jù) return 0; ,運行結(jié)果: num:10010 name:Wang-li sex:f age:19 address:Beijing num:10012 n

18、ame:Xv-lin sex:m age:20 address:Shenzhen,25,派生類構(gòu)造函數(shù)的一般形式: 派生類構(gòu)造函數(shù)名(總參數(shù)列表): 基類構(gòu)造函數(shù)名(參數(shù)表) 派生類中新增數(shù)據(jù)成員初始化語句 2點說明: (總參數(shù)列表)為派生類構(gòu)造函數(shù)定義的形參,需要參數(shù)類型說明; (參數(shù)表)為調(diào)用基類構(gòu)造函數(shù)傳遞的實參,不需要說明參數(shù)類型;,26,請看派生類Student1的構(gòu)造函數(shù): Student1(int n,string nam,char s,int a,string ad):Student(n,nam,s) age=a; addr=ad; 可以寫成: Student1(int n,s

19、tring nam,char s,int a,string ad):Student(n,nam,s),age(a),addr(ad);,定義派生類構(gòu)造函數(shù)形參表,調(diào)用基類構(gòu)造函數(shù) 傳遞實參,27,派生類構(gòu)造函數(shù)的特殊形式: 當(dāng)不需要對派生類新增成員初始化時,派生類構(gòu)造函數(shù)體可以為空,而構(gòu)造函數(shù)僅僅用于完成向基類構(gòu)造函數(shù)傳遞參數(shù)的任務(wù)。 Student1(int n,string nam,char s):Student(n,nam,s) 如果基類中沒有定義構(gòu)造函數(shù),或只定義了不帶參數(shù)或帶缺省參數(shù)的構(gòu)造函數(shù),則派生類構(gòu)造函數(shù)中可以不寫基類構(gòu)造函數(shù)。 Student1(int a,string ad

20、) age=a;addr=ad; 如果滿足上面2種情況,可以不必顯式地定義派生類構(gòu)造函數(shù)。 如果基類中既有不帶參數(shù)的、又有帶參數(shù)的構(gòu)造函數(shù),則在派生類構(gòu)造函數(shù)中可以包含、也可以不包含基類的構(gòu)造函數(shù)。,28,4.基類與派生類的轉(zhuǎn)換:(P149 5.4) 對于數(shù)值類型變量,C+是一個弱類型語言。它允許不同類型的變量在一定的條件下進行轉(zhuǎn)換:“較小” 類型的數(shù)值變量賦值給“較大”類型的數(shù)值變量。 如,整型數(shù)雙精度數(shù)(隱式類型轉(zhuǎn)換) 對于指針、引用、結(jié)構(gòu)、類等類型變量,C+又是一個強類型語言。它不允許不同類型的變量在同一表達式中出現(xiàn)。,對于基類和派生類對象,C+允許通過公有繼承方式聯(lián)系起來的兩個對象間進

21、行隱式轉(zhuǎn)換。 隱式轉(zhuǎn)換規(guī)則: 與數(shù)值類型隱式轉(zhuǎn)換的規(guī)則相反: 將“較大” 的對象(派生類對象)賦值給“較小”的對象(基類對象)。,29, 派生類對象可以向基類對象賦值:,如在程序5-1中的CPoint基類和CRect派生類中,若定義兩個對象: CPoint point; CRect rect; 則: point = rect; /O.K,派生類對象賦給基類對象 rect = point; /error,基類對象不能直接賦給派生類對象 (CPoint)rect = point; /O.K,派生類對象經(jīng)過顯式類型轉(zhuǎn)換成基類對象 rect = (CRect)point; /error,基類對象不能顯

22、式轉(zhuǎn)換成派生類,30,說明: 可以用公用派生類對象對其基類對象賦值,在賦值時舍棄派生類自己的成員。 事實上,所謂賦值只是對數(shù)據(jù)成員賦值,對成員函數(shù)不存在賦值問題。 請注意:賦值后不能企圖通過基類對象訪問派生類成員。,大材小用式賦值派生類對象賦值給基類對象。,31, 派生類對象可以代替基類對象 向基類對象的引用進行初始化: 如,已定義了基類A對象a1,可以定義a1的引用:,A a1;/定義基類A對象a1 B b1;/定義公有派生類B對象b1 A /定義基類A對象的引用r,并用a1對其初始化,A a1;/定義基類A對象a1 B b1;/定義公有派生類B對象b1 A /定義基類A對象的引用r,并用b

23、1對其初始化,!注意:此時r并不是b1的別名,也不與b1共享同一段存儲單元。它只是b1中基類部分的別名,與b1中基類部分共享同一段存儲單元。,用派生類對象b1代替基類對象a1初始化r.,32, 如果函數(shù)的形參是基類對象或基類對象的引用,相應(yīng)的實參可以用派生類對象: 如,有一函數(shù)fun():,void fun(A /輸出引用r所代表對象的數(shù)據(jù)成員num,A a1; B b1; fun(b1); /用派生類對象b1作實參傳遞數(shù)據(jù),!注意:由于基類和派生類對象能自動隱式轉(zhuǎn)換,故可以用b1代替a1作為實參。此時輸出的是派生類B對象b1中的基類數(shù)據(jù)成員num。,用派生類對象b1代替基類對象a1作實參,3

24、3, 派生類對象的地址可以賦值給基類對象的指針變量,或說,指向基類的指針也可以指向派生類: 注意2點: 指向不同數(shù)據(jù)類型的指針和引用是不能互相轉(zhuǎn)換的。這一點對指向不同類對象的指針也適用。 如果兩個指針所指向的對象是通過公共繼承方式相關(guān)聯(lián)的話,則允許一定程度的賦值轉(zhuǎn)換。 轉(zhuǎn)換規(guī)則: 基類對象指針(或引用)賦值給派生類對象指針(或引用)時為不安全轉(zhuǎn)換。 因為,派生類對象指針將失去訪問派生類成員的功能,會出現(xiàn)語法錯誤。 p_b1 = p_a1; ,34,派生類對象指針(或引用)賦值給基類對象指針(或引用)是安全的。 因為,派生類指針除了能夠訪問派生類的功能,還能訪問基類的功能。而基類指針僅僅只能訪問

25、基類的功能。 此時,基類指針只能訪問派生類從基類繼承的功能,不能訪問派生類自身的功能。除非對基類指針作強制類型轉(zhuǎn)換。 p_a1 = p_b1; (B)p_a1 = p_b1; ,只能訪問b類中基類a1的功能,可以訪問派生類b1的全部功能,35,/派生類聲明 class CRect: public Point private: int H,W; /新增私有數(shù)據(jù)成員 public: /新增公有成員函數(shù) CRect()CPoint:set(0,0);H=0;W=0; /調(diào)用基類公有成員函數(shù) CRect(int left,int top,int w_val,int h_val) CPoint:set(

26、left,top); H=h_val; W=w_val; void getSize(int ,class CPoint/基類Point類的聲明 private: int X,Y; public: CPoint(int a=0, int b=0) X=a; Y=b; void set(int a, int b) X=a; Y=b; void move(int a, int b) X+=a; Y+=b; void get(int ,例5-3: 類對象之間 的轉(zhuǎn)換,36,int main() int h,w; CRect rect(20,30,40,50); /定義一個派生類對象 rect.getS

27、ize(h,w); /調(diào)用派生類對象成員函數(shù) coutgetSize(h, w); /用派生類指針調(diào)用派生類成員函數(shù) coutmove(10, 20); /用派生類指針調(diào)用基類成員函數(shù) p_rect-print();,CPoint *p_point=p_rect; p_point-move(10, 20); / p_point-print(); (CRect *)p_point)-print(); p_point=new CPoint(10,20); / p_rect-p_point; p_point-set(30,40); (Crect *)p_point)-print(); delete

28、p_point; delete p_rect; return 0; ,37,類對象轉(zhuǎn)換總結(jié): 對象之間: 派生類對象可以直接賦值給基類對象; point=rect; 賦值后的基類并不能訪問派生類對象成員;即使經(jīng)過顯式的類型轉(zhuǎn)換也不行。 point.show(); (Crect)point.show(); 基類對象不可以直接賦值給派生類對象,除非經(jīng)過顯式的類型轉(zhuǎn)換。 rect=point; (CPoint)rect=point; 賦值后的派生類對象依然可以訪問派生類成員; rect.show(); ,38,指針(引用)之間: 派生類對象指針可以直接賦值給基類指針;(引用) p_point=p_r

29、ect; 賦值后的基類指針并不能訪問派生類對象成員;除非經(jīng)過顯式的類型轉(zhuǎn)換。 p_point-show(); (Crect *)p_point-show(); 基類對象指針不能直接賦值給派生類對象指針。 p_rect=p_point; ,類對象、對象指針、對象引用轉(zhuǎn)換的必要條件: 基類和派生類必須是通過公有繼承方式聯(lián)系起來的,則相互之間可以進行隱式類型轉(zhuǎn)換和賦值。,39,5.多重繼承:(P160 5.6) 一個派生類同時繼承多個基類,叫做多重繼承。 聲明多重繼承的方法: 如果已聲明了類A、類B和類C,可以聲明多重繼承的派生類D: class D : public A,private B,pro

30、teced C 類D新增加的成員; 多重繼承派生類的構(gòu)造函數(shù): 派生類構(gòu)造函數(shù)名(總參數(shù)列表): 基類1構(gòu)造函數(shù)名(參數(shù)列表), 基類2構(gòu)造函數(shù)名(參數(shù)列表), 派生類中新增數(shù)據(jù)成員初始化語句 其中,中要包含所有基類構(gòu)造函數(shù)中的參數(shù); 調(diào)用基類構(gòu)造函數(shù)的順序按照聲明時出現(xiàn)的順序。,40,派生類構(gòu)造函數(shù)的調(diào)用順序: 基類構(gòu)造函數(shù) 子對象成員類的構(gòu)造函數(shù)(如果有子對象成員的話) 派生類構(gòu)造函數(shù) 派生類的析構(gòu)函數(shù): 析構(gòu)函數(shù)也不能被繼承,因而當(dāng)派生類對象消亡調(diào)用派生類的析構(gòu)函數(shù)時,基類的析構(gòu)函數(shù)也將同時被調(diào)用。 派生類析構(gòu)函數(shù)的執(zhí)行順序: 派生類析構(gòu)函數(shù) 子對象成員類的析構(gòu)函數(shù)(如果有子對象成員的話

31、) 基類析構(gòu)函數(shù),41,例5-8:聲明一個教師(Teacher)類和一個學(xué)生類(Student),用多重繼承的方式聲明一個在職研究生(Graduate)派生類。,class Student protected: string name1; char sex; float score; public: Student(string nam,char s,float sc) :name1(nam),sex(s),score(sc) void display(); ;,class Teacher public: Teacher(string nam,int a,string t) name=nam;

32、age=a;title=t; void display(); protected: string name; int age; string title; ;,class Graduate:public Teacher,public Student protected: float wage; public: Graduate (string nam,int a,string t, char s, float sc,float w):Teacher(nam,a,t), Student(nam,s,sc),wage(w) void show(); ;,int main() Graduate gr

33、d1(“Wang-li”,24, ”assistant”,f,89.5,1234.5); grd1.show(); /調(diào)用派生類函數(shù) return 0; ,運行結(jié)果: name: Wang-li age: 24 sex: f score: 89.5 title: assistant wages: 1234.5,void Graduate:show() cout“name:” name endl; ,一個問題: 多重繼承會從不同基類 繼承一些重復(fù)數(shù)據(jù), 容易產(chǎn)生二義性!,name;,Teacher:name,42,多重繼承引起的二義性問題: 多重繼承有效反映并處理現(xiàn)實復(fù)雜問題,卻增加了程序編寫和

34、維護的難度。 例如,類C是類A、類B的直接派生類。討論下面三種情況: 兩個基類有同名成員。,43,class A public: int a; void display(); ,class B public: int a; void display(); ,class C :public A,public B public: int b; void show(); ,int main() C c1; c1.a=3; c1.display(); ,int main() C c1; c1.A:a=3; c1.A:display(); ,44, 兩個基類和派生類都有同名成員。,int main()

35、C c1; c1.a=3; c1.display(); ,執(zhí)行時訪問的是哪個類的 成員?,C+的覆蓋規(guī)則: 派生類新增加的數(shù)據(jù)成員覆蓋基類中的同名成員。 對于成員函數(shù)來說,不僅需要同名,還要求參數(shù)個數(shù)和類型均相同,才會被覆蓋。否則屬于函數(shù)重載。,45, 如果類A和類B是從同一個基類N派生的。,class A :public N public: int a1; ,class B:public N public: int a2; ,class C :public A,public B public: int b; void show(); ,class N public: int a; void

36、display() cout“A:a=”aendl; ,類AB分別從類N繼承了成員a和display().,46,void C:show() ,請問,怎樣輸出類A中的成員a?,int main() C c1; ,請問,怎樣訪問類A中的成員a和display()?,問題: 共同間接基類的多 份同名成員,造成 空間浪費、訪問復(fù)雜, 產(chǎn)生二義性!,coutA:a;,c1.a=3; c1.display();,c1.A:a=3; c1.A:display();,47,虛基類:(P163 5.6.3) 虛基類方法使得在繼承間接共同基類時只保留一份成員。 假設(shè),A、B、C、D類呈左圖繼承關(guān)系,其成員情況如

37、右圖所示。類D保留了從B、C繼承來的2份A類成員。,48,如果,將類A聲明為虛基類,方法如下:,class A ; /聲明類B是類A的公有派生類,A是B的虛基類 class B :virtual public A ; /聲明類C是類A的公有派生類,A是C的虛基類 class C :virtual public A ;,!注意:虛基類并不是在聲明基類時聲明的,而是在聲明派生類,指定繼承方式時聲明的。 虛基類聲明的一般形式: class 派生類名:virtual 繼承方式 基類名,49,在派生類B和C做了上面的聲明后,派生類D中的成員如右圖: 基類A成員只保留一次繼承。 !注意: 為保證虛基類在派

38、生類中只繼承一次,應(yīng)當(dāng)在該基類所有直接派生類中都聲明為虛基類。否則仍會出現(xiàn)對基類的多次繼承。 如右圖,類D中沒有聲明類A為虛基類,則類E會保留對基類A成員的2次繼承。,50,繼承在軟件開發(fā)中的重要意義: 要求保留原有的基類不被改變繼承建立新類,即繼承了基類的所有特性,又不會破壞基類。 用戶往往得不到基類的源代碼無法對基類進行修改以適應(yīng)程序的需要。 類庫中的基類不允許修改一個基類可能已與多個用戶程序建立了某種關(guān)系。 許多基類是專門作為基類設(shè)計的,并沒有獨立功能這些基類只是一個框架,或者說是抽象類。 需要設(shè)計類的層次結(jié)構(gòu)不斷地從抽象到具體,逐步實現(xiàn)。,思考:為什么人們?nèi)绱丝粗乩^承?他們不是將已有的

39、類加以修改來滿足應(yīng)用要求,而是盡可能地通過繼承機制創(chuàng)建一批新的類?,51,第2部分多態(tài)性與虛函數(shù),多態(tài)性的概念 靜態(tài)聯(lián)編和動態(tài)聯(lián)編 虛函數(shù) 純虛函數(shù)與抽象類,52,1.多態(tài)性的概念: 多態(tài)性是面向?qū)ο蟪绦蛟O(shè)計的重要特性。利用多態(tài)性可以設(shè)計和實現(xiàn)一個易于擴展的系統(tǒng)。 在C+中,多態(tài)性是指具有不同功能的函數(shù)用同一個函數(shù)名,即用同一函數(shù)名調(diào)用不同內(nèi)容的函數(shù)。 現(xiàn)實生活中有很多多態(tài)性的例子。 多態(tài)性的一般表述:向不同的對象發(fā)送同一消息(調(diào)用函數(shù)),不同的對象會產(chǎn)生不同的行為(方法)。 例如,運算符+調(diào)用operator+函數(shù),對不同類型數(shù)據(jù)的操作互不相同。 從系統(tǒng)實現(xiàn)的角度看,多態(tài)性分為兩類: 靜態(tài)多

40、態(tài)性:系統(tǒng)在編譯的時候就能知道要調(diào)用的是哪個函數(shù)。也叫做編譯時的多態(tài)性。(如函數(shù)重載) 動態(tài)多態(tài)性:程序在運行過程中才動態(tài)地確定操作所針對的對象。又叫做運行時的多態(tài)性。,53,2.靜態(tài)聯(lián)編和動態(tài)聯(lián)編:(P152 5.4.3),靜態(tài)聯(lián)編: 對程序中出現(xiàn)的標識符進行的綁定和解析過程,在產(chǎn)生目標代碼的編譯時就固定下來。 靜態(tài)聯(lián)編又稱為靜態(tài)綁定和早期綁定。,一個靜態(tài)聯(lián)編的例子: #include using namespace std; class Point private: double x, y; public: Point(double i, double j) x=i; y=j; doubl

41、e Area() const return 0.0; ;,void fun(Point ,class Rectangle:public Point public: Rectangle(double i, double j, double k, double l); double Area() const return w*h; private: double w,h; ; Rectangle:Rectangle(double i, double j, double k, double l) :Point(i,j) w=k; h=l; ,int main() Rectangle rec(3.0,

42、 5.2, 15.0, 25.0); fun(rec); return 0; ,運行結(jié)果: Area=0,請思考:導(dǎo)致這種運行結(jié)果的原因是什么?,原因就在于 靜態(tài)聯(lián)編!,自動根據(jù)形參 引用類型調(diào)用相應(yīng) 類的成員函數(shù)?,實現(xiàn) 動態(tài)聯(lián)編!,54,什么是動態(tài)聯(lián)編: 根據(jù)目標對象的動態(tài)類型(而不是靜態(tài)類型),在程序運行時(而不是在編譯時)將函數(shù)名綁定到具體的函數(shù)實現(xiàn)上。 動態(tài)聯(lián)編又稱為動態(tài)綁定和晚期綁定。 動態(tài)聯(lián)編的實現(xiàn)虛函數(shù): C+提供了virtual關(guān)鍵字用于指定函數(shù)是否為虛函數(shù)。如果在一個函數(shù)聲明前面加上virtual,則這個函數(shù)為虛函數(shù),系統(tǒng)將對其進行動態(tài)聯(lián)編。,問題提出: 程序員怎樣告知編譯

43、程序 哪些函數(shù)是靜態(tài)聯(lián)編?哪些函 數(shù)是動態(tài)聯(lián)編?,55,動態(tài)聯(lián)編的實現(xiàn)過程: 在程序編譯時: 為每一個有虛函數(shù)的類設(shè)置一個虛函數(shù)表v_table,一個指針數(shù)組,存放每個虛函數(shù)的入口地址。 在函數(shù)調(diào)用處插入一個隱藏的,指向虛函數(shù)表的指針v_pointer; 在程序運行中: 根據(jù)對象的v_pointer,在相應(yīng)的虛函數(shù)表中獲得函數(shù)入口,來調(diào)用正確的函數(shù)。,56,3.虛函數(shù)的定義與使用: 虛函數(shù)的定義方法: 在基類中以關(guān)鍵字virtual說明; virtual () 如: virtual double Area( ); 在派生類中重新定義一個同名非靜態(tài)成員函數(shù)。 3點說明: 派生類中同名函數(shù)必須與基

44、類虛函數(shù)完全一致(即函數(shù)名、參數(shù)個數(shù)與類型、返回類型都相同)。 采用基類對象指針或引用來調(diào)用虛函數(shù)。 派生類必須以公用方式繼承。,只有滿足上面3點,程序才會按動態(tài)聯(lián)編的方式調(diào)用函數(shù),否則虛函數(shù)將按靜態(tài)聯(lián)編的方式調(diào)用。,57,#include using namespace std; class Point private: double x, y; public: Point(double i, double j) x=i; y=j; virtual double Area() const return 0.0; ;,class Rectangle:public Point public: R

45、ectangle(double i, double j, double k, double l); virtual double Area() const return w*h; private: double w,h; ; Rectangle:Rectangle(double i, double j, double k, double l) :Point(i, j) w=k; h=l; ,void fun(Point ,int main() Rectangle rec(3.0, 5.2, 15.0, 25.0); fun(rec); return 0; ,運行結(jié)果: Area=375,58,

46、總結(jié)虛函數(shù)的使用方法: 在基類用virtual聲明成員函數(shù)為虛函數(shù); 在派生類中重新定義此函數(shù); C+規(guī)定,當(dāng)一個成員函數(shù)被聲明為虛函數(shù)后,其派生類中的同名函數(shù)都自動成為虛函數(shù)。 定義一個指向基類的指針(或引用); 通過重新對該指針(或引用)做同類族對象賦值,調(diào)用該類對象同名虛函數(shù)。,虛函數(shù)與指向基類的指針(引用)變量配合使用,就能方便地調(diào)用同一類族中不同類對象的同名函數(shù)。從而實現(xiàn)運行時的多態(tài)性。,59,class CCircularShape public: static const double PI; CCircularShape() CCircularShape(double r) r

47、adius = r; void setHeight(double h) height = h; void setRadius(double r) radius = r; /虛函數(shù)(CCircularShape) virtual char *Type() const return NULL; virtual double Volume() const return 0; virtual double SurfaceArea() const return 0; protected: double radius; double height; ;,class CCircle : public CCi

48、rcularShape public: char *Type() const return 圓; double Circumference() const double SurfaceArea() const ;,class CSphere : public CCircularShape public: char *Type() const return 球; double SurfaceArea() const double Volume() const ;,class CCylinder : public CCircle public: char *Type() constreturn 圓

49、柱; double SurfaceArea() const double Volume() const ;,class CCone : public CCircle public: char *Type() constreturn 圓錐; double SurfaceArea() const double Volume() const ;,void main() CCircularShape *p = 0; /定義基類指針p p = new CCircle(10);,cout Type() SurfaceArea(); p = new CSphere(10); cout Type() Surf

50、aceArea();,一個虛函數(shù)的例子5-6,這就是虛函數(shù)的妙用! 基類指針p可調(diào)用同類族中 不同類的虛函數(shù)多態(tài)性。,60,在什么情況下應(yīng)該聲明虛函數(shù): 函數(shù)所在的類會作為基類,并且有更改功能的需要; 對于繼承后不需要更改功能的函數(shù)不要聲明為虛函數(shù); 考慮對成員函數(shù)的調(diào)用是否通過指針或引用; 虛函數(shù)的意義: 如果說:數(shù)據(jù)封裝使得代碼模塊化; 繼承實現(xiàn)了代碼重用; 那么,虛函數(shù)采用動態(tài)聯(lián)編技術(shù)造就了程序設(shè)計的多態(tài)性接口重用。,61,析構(gòu)函數(shù)是否需要聲明為虛函數(shù)? 先看一個例題:,#include using namespace std; class Point public: Point() P

51、oint()cout“析構(gòu)Pointn”; ;,class Circle:public Point public: Circle() Circle()cout“析構(gòu)Circlen”; private: int radius; ;,int main() Point *p=new Circle; delete p; return 0; ,用new開辟Circle類對象的臨時動態(tài)存儲空間,運行結(jié)果: 析構(gòu)Point,用delete釋放該空間,virtual,運行結(jié)果: 析構(gòu)Circle 析構(gòu)Point,62,虛析構(gòu)函數(shù): 如果基類的析構(gòu)函數(shù)為虛析構(gòu)函數(shù),派生類的析構(gòu)函數(shù)也均為虛析構(gòu)函數(shù)。 如果基類的析構(gòu)函數(shù)為虛析構(gòu)函數(shù),無論基類指針指的是哪一個派生類對象,當(dāng)該對象撤銷時,系統(tǒng)都會采用動態(tài)聯(lián)編,調(diào)用相應(yīng)的析構(gòu)函數(shù)。 程序中最好把析構(gòu)函數(shù)聲明

溫馨提示

  • 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)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論