譚浩強(qiáng)C程序設(shè)計 nPPT課件
《譚浩強(qiáng)C程序設(shè)計 nPPT課件》由會員分享,可在線閱讀,更多相關(guān)《譚浩強(qiáng)C程序設(shè)計 nPPT課件(55頁珍藏版)》請在裝配圖網(wǎng)上搜索。
1、多態(tài)性(polymorphism)是面向?qū)ο蟪绦蛟O(shè)計的一個重要特征。利用多態(tài)性可以設(shè)計和實現(xiàn)一個易于擴(kuò)展的系統(tǒng)。p在C+程序設(shè)計中,多態(tài)性是指具有不同功能的函數(shù)可以用同一個函數(shù)名,這樣就可以用一個函數(shù)名調(diào)用不同內(nèi)容的函數(shù)。p在面向?qū)ο蠓椒ㄖ械亩鄳B(tài)性: 向不同的對象發(fā)送同一個消息(函數(shù)名),不同的對象在接收時會產(chǎn)生不同的行為(即方法)。也就是說,每個對象可以用自己的方式去響應(yīng)共同的消息。12.1 多態(tài)性的概念第1頁/共55頁優(yōu)點(diǎn):在C+程序設(shè)計中,在不同的類中定義了其響應(yīng)消息的方法,那么使用這些類時,不必考慮它們是什么類型,只要發(fā)布消息即可。多態(tài)性分為兩類: 靜態(tài)多態(tài)性和動態(tài)多態(tài)性。p靜態(tài)多態(tài)性
2、:函數(shù)重載和運(yùn)算符重載屬于靜態(tài)多態(tài)性,在程序編譯時系統(tǒng)就能決定調(diào)用的是哪個函數(shù),因此靜態(tài)多態(tài)性又稱編譯時的多態(tài)性。p動態(tài)多態(tài)性:是在程序運(yùn)行過程中才動態(tài)地確定操作所針對的對象。它又稱運(yùn)行時的多態(tài)性。動態(tài)多態(tài)性是通過虛函數(shù)(virtual function)實現(xiàn)的。在本章中主要介紹動態(tài)多態(tài)性和虛函數(shù)。第2頁/共55頁下面是一個承上啟下的例子。一方面它是有關(guān)繼承和運(yùn)算符重載內(nèi)容的綜合應(yīng)用的例子,通過這個例子可以進(jìn)一步融會貫通前面所學(xué)的內(nèi)容,另一方面又是作為討論多態(tài)性的一個基礎(chǔ)用例。12.2 一個典型的例子第3頁/共55頁例12.1 先建立一個Point(點(diǎn))類,包含數(shù)據(jù)成員x,y(坐標(biāo)點(diǎn))。以它為
3、基類,派生出一個Circle(圓)類,增加數(shù)據(jù)成員r(半徑),再以Circle類為直接基類,派生出一個Cylinder(圓柱體)類,再增加數(shù)據(jù)成員h(高)。要求編寫程序,重載運(yùn)算符“”,使之能用于輸出以上類對象。對于一個比較大的程序,應(yīng)當(dāng)分成若干步驟進(jìn)行。先聲明基類,再聲明派生類,逐級進(jìn)行,分步調(diào)試。(1) 聲明基類Point類 寫出聲明基類Point的部分:#include /聲明類Point /聲明一個類包括什么?class Pointpublic: Point(float x=0,float y=0);/有默認(rèn)參數(shù)的構(gòu)造函數(shù)第4頁/共55頁 void setPoint(float,flo
4、at); /設(shè)置坐標(biāo)值 float getX( ) const return x; /讀x坐標(biāo) float getY( ) const return y; /讀y坐標(biāo) friend ostream & operator(ostream &,const Point &);/重載運(yùn)算符“”protected: /受保護(hù)成員 float x,y;/下面定義Point類的成員函數(shù) /Point的構(gòu)造函數(shù)Point:Point(float a,float b) /對x,y初始化x=a;y=b;/設(shè)置x和y的坐標(biāo)值void Point:setPoint(float a,float b) /為x,y賦新值x
5、=a;y=b;/重載運(yùn)算符“”,使之能輸出點(diǎn)的坐標(biāo)ostream & operator(ostream &output,const Point &p)outputp.x,p.yendl; return output;以上完成了基類Point類的聲明。第5頁/共55頁現(xiàn)在要對上面寫的基類聲明進(jìn)行調(diào)試,檢查它是否有錯,為此要寫出main函數(shù)。實際上它是一個測試程序。int main( )Point p(3.5,6.4);/建立Point類對象p coutx=p.getX( ),y=p.getY( )endl;/輸出p的坐標(biāo)值 p.setPoint(8.5,6.8); /重新設(shè)置p的坐標(biāo)值 cout
6、p(new):pendl; /用重載運(yùn)算符“”輸出p點(diǎn)坐標(biāo)程序編譯通過,運(yùn)行結(jié)果為x=3.5,y=6.4p(new):8.5,6.8測試程序檢查了基類中各函數(shù)的功能,以及運(yùn)算符重載的作用,證明程序是正確的。第6頁/共55頁(2) 聲明派生類Circleclass Circle:public Point/circle是Point類的公用派生類public: Circle(float x=0,float y=0,float r=0); /構(gòu)造函數(shù) void setRadius(float); /設(shè)置半徑值 float getRadius( ) const; /讀取半徑值 float area (
7、) const; /計算圓面積 friend ostream &operator(ostream &,const Circle &);/重載運(yùn)算符“” private:/?protected: float radius;/定義構(gòu)造函數(shù),對圓心坐標(biāo)和半徑初始化Circle:Circle(float a,float b,float r):Point(a,b),radius(r) void Circle:setRadius(float r) /設(shè)置半徑值radius=r;float Circle:getRadius( ) const return radius; /讀取半徑值float Circle
8、:area( ) const第7頁/共55頁return 3.14159*radius*radius;/重載運(yùn)算符“”,使之按規(guī)定的形式輸出圓的信息ostream &operator(ostream &output,const Circle &c)outputCenter=c.x,c.y,r=c.radius,area=c.area( )endl; return output;為了測試Circle類的定義,可以寫出下面的主函數(shù):第8頁/共55頁i n t m a i n ( ) C i r c l e c ( 3 . 5 , 6 . 4 , 5 . 2 ) ; / / 建 立 C i r c
9、l e 類 對 象 c , 并 給 定 圓 心 坐 標(biāo) 和 半 徑 c o u t o r i g i n a l c i r c l e : n x = c . g e t X ( ) , y = c . g e t Y ( ) , r = c . g e t R a d i u s ( ) , a r e a = c . a r e a ( ) e n d l ; / / 輸 出 圓 心 坐 標(biāo) 、 半 徑 和 面 積 c . s e t R a d i u s ( 7 . 5 ) ; / / 設(shè) 置 半 徑 值 c . s e t P o i n t ( 5 , 5 ) ; / / 設(shè)
10、置 圓 心 坐 標(biāo) 值 x , y c o u t n e w c i r c l e : n c ; / / 用 重 載 運(yùn) 算 符 “ ” 輸 出 圓 對 象 的 信 息 P o i n t & p R e f = c ; / / p R e f 是 P o i n t 類 的 引 用 變 量 , 被 c 初 始 化 c o u t p R e f : p R e f ; / / 輸 出 p R e f 的 信 息 r e t u r n 0 ;程 序 編 譯 通 過 , 運(yùn) 行 結(jié) 果 為 o r i g i n a l c i r c l e : ( 輸 出 原 來 的 圓 的 數(shù) 據(jù)
11、 )x = 3 . 5 , y = 6 . 4 , r = 5 . 2 , a r e a = 8 4 . 9 4 8 6n e w c i r c l e : ( 輸 出 修 改 后 的 圓 的 數(shù) 據(jù) )C e n t e r = 5 , 5 , r = 7 . 5 , a r e a = 1 7 6 . 7 1 4p R e f : 5 , 5 ( 輸 出 圓 的 圓 心 “ 點(diǎn) ” 的 數(shù) 據(jù) )第9頁/共55頁(3) 聲明Circle的派生類Cylinderclass Cylinder:public Circle/ Cylinder是Circle的公用派生類public: Cylin
12、der (float x=0,float y=0,float r=0,float h=0);/構(gòu)造函數(shù) void setHeight(float); /設(shè)置圓柱高 float getHeight( ) const; /讀取圓柱高 float area( ) const; /計算圓表面積 float volume( ) const; /計算圓柱體積 friend ostream& operator(ostream&,const Cylinder&);/重載運(yùn)算符“” protected: float height; /圓柱高;Cylinder:Cylinder(float a,float b,f
13、loat r,float h) /定義構(gòu)造函數(shù) :Circle(a,b,r),height(h)void Cylinder:setHeight(float h)height=h; /設(shè)置圓柱高float Cylinder:getHeight( ) const return height; /讀取圓柱高第10頁/共55頁/計算圓表面積float Cylinder:area( ) const return 2*Circle:area( )+2*3.14159*radius*height;float Cylinder:volume() const/計算圓柱體積return Circle:area()
14、*height;/重載運(yùn)算符“”ostream &operator(ostream &output,const Cylinder& cy)outputCenter=cy.x,cy.y,r=cy.radius,h=cy.height narea=cy.area( ), volume=cy.volume( )endl; return output;第11頁/共55頁 可以寫出下面的主函數(shù): int main( )Cylinder cy1(3.5,6.4,5.2,10);/定義Cylinder類對象cy1 coutn original cylinder:nx=cy1.getX( ), y=cy1.g
15、etY( ), r= cy1.getRadius( ), h=cy1.getHeight( )narea=cy1.area() ,volume=cy1.volume()endl;/用系統(tǒng)定義的運(yùn)算符“”輸出cy1的數(shù)據(jù) cy1.setHeight(15); /設(shè)置圓柱高 cy1.setRadius(7.5); /設(shè)置圓半徑 cy1.setPoint(5,5); /設(shè)置圓心坐標(biāo)值x,y coutnnew cylinder:ncy1; /用重載運(yùn)算符“”輸出cy1的數(shù)據(jù) Point &pRef=cy1; /pRef是Point類對象的引用變量第12頁/共55頁 coutnpRef as a Poi
16、nt:pRef; /pRef作為一個“點(diǎn)”輸出 Circle &cRef=cy1; /cRef是Circle類對象的引用變量 coutncRef as a Circle:display( );”可以調(diào)用不同派生層次中的display函數(shù),只需在調(diào)用前給指針變量pt賦以不同的值(使之指向不同的類對象)即可。C+中的虛函數(shù)就是用來解決這個問題的。虛函數(shù)的作用是允許在派生類中重新定義與基類同名的函數(shù),并且可以通過基類指針或引用來訪問基類和派生類中的同名函數(shù)。方法:由基類指針,訪問各層次中的同名函數(shù)。例12.2。再討論使用虛函數(shù)的情況。第15頁/共55頁例12.2 基類與派生類中有同名函數(shù)。在下面的程
17、序中Student是基類,Graduate是派生類,它們都有display這個同名的函數(shù)。#include #include using namespace std;/聲明基類Studentclass Studentpublic: Student(int, string,float);/聲明構(gòu)造函數(shù) void display( ); /聲明輸出函數(shù) protected: /受保護(hù)成員,派生類可以訪問 int num; string name; float score; ;第16頁/共55頁 /Student類成員函數(shù)的實現(xiàn)Student:Student(int n, string nam,fl
18、oat s) /定義構(gòu)造函數(shù) num=n;name=nam;score=s;void Student:display( ) /定義輸出函數(shù)coutnum:numnname:namenscore:scorenn;第17頁/共55頁/聲明公用派生類Graduateclass Graduate:public Studentpublic: Graduate(int, string, float, float); /聲明構(gòu)造函數(shù) void display( ); /聲明輸出函數(shù)private: float pay;/ Graduate類成員函數(shù)的實現(xiàn)void Graduate:display( ) /定
19、義輸出函數(shù) coutnum:numnname:namenscore:scorenpay=paydisplay( ); pt=&grad1; pt-display( ); return 0; 第19頁/共55頁運(yùn)行結(jié)果如下,請仔細(xì)分析。num:1001(stud1的數(shù)據(jù))name:Liscore:87.5num:2001 (僅輸出了grad1中基類部分的數(shù)據(jù),沒實現(xiàn)多態(tài)性!)name:wangscore:98.5下面對程序作一點(diǎn)修改,在Student類中聲明display函數(shù)時,在最左面加一個關(guān)鍵字virtual,即virtual void display( );變成虛函數(shù)就行了這樣就把Stud
20、ent類的display函數(shù)聲明為虛函數(shù)。程序其他部分都不改動。再編譯和運(yùn)行程序,請注意分析運(yùn)行結(jié)果: 第20頁/共55頁num:1001(stud1的數(shù)據(jù))name:Liscore:87.5num:2001 (grad1中基類部分的數(shù)據(jù),達(dá)到目的!)name:wangscore:98.5pay=1200 (這一項以前是沒有的)由虛函數(shù)實現(xiàn)的動態(tài)多態(tài)性就是: 同一類族中不同類的對象,對同一函數(shù)調(diào)用作出不同的響應(yīng)。多態(tài)性理解:起始地址相同的不同對象指針,調(diào)同名函數(shù)時,響應(yīng)不同(調(diào)了各自對應(yīng)的函數(shù))。實現(xiàn)方法:將同名函數(shù)聲明為虛函數(shù)。第21頁/共55頁虛函數(shù)的使用方法是: 注意規(guī)則! (1) 在基
21、類用virtual聲明成員函數(shù)為虛函數(shù)。這樣就可以在派生類中重新定義此函數(shù),為它賦予新的功能,并能方便地被調(diào)用。 在類外定義虛函數(shù)時,不必再加virtual。(2) 在派生類中重新定義此函數(shù),要求函數(shù)名、函數(shù)類型、函數(shù)參數(shù)個數(shù)和類型全部與基類的虛函數(shù)相同,并根據(jù)派生類的需要重新定義函數(shù)體。 第22頁/共55頁C+規(guī)定,當(dāng)一個成員函數(shù)被聲明為虛函數(shù)后,其派生類中的同名函數(shù)都自動成為虛函數(shù)。因此在派生類重新聲明該虛函數(shù)時,可以加virtual,也可以不加,但習(xí)慣上一般在每一層聲明該函數(shù)時都加virtual,使程序更加清晰。如果在派生類中沒有對基類的虛函數(shù)重新定義,則派生類簡單地繼承其直接基類的虛函
22、數(shù)。(3) 必須定義一個指向基類對象的指針變量,并使它指向同一類族中需要調(diào)用該函數(shù)的對象。(4) 通過該指針變量調(diào)用此虛函數(shù),此時調(diào)用的就是指針變量指向的對象的同名函數(shù)。第23頁/共55頁舉例2 :用引用也可,如將上面例題改為void fun( Student & x)/必須為基類指針或引用! x.display( );/主函數(shù)int main() Student s1(1001,Li,87.5); Graduate gd1(2001,Wang,98.5,563.5); fun( s1); fun (gd1); return 0;第24頁/共55頁顯示結(jié)果:num:1001(stud1的數(shù)據(jù))
23、name:Liscore:87.5num:2001 (grad1的數(shù)據(jù))name:wangscore:98.5pay=1200第25頁/共55頁注意區(qū)別:函數(shù)重載處理的是同一層次上的同名函數(shù)問題,而虛函數(shù)處理的是不同派生層次上的同名函數(shù)問題,前者是橫向重載,后者可以理解為縱向重載。但與重載不同的是: 同一類族的虛函數(shù)的首部是相同的,而函數(shù)重載時函數(shù)的首部是不同的(參數(shù)個數(shù)或類型不同)。第26頁/共55頁這樣編譯系統(tǒng)在對程序進(jìn)行編譯時,即能確定調(diào)用的是哪個類對象中的函數(shù)。確定調(diào)用的具體對象的過程稱為關(guān)聯(lián)(binding)。在這里是指把一個函數(shù)名與一個類對象捆綁在一起,建立關(guān)聯(lián)。一般地說,關(guān)聯(lián)指把
24、一個標(biāo)識符和一個存儲地址聯(lián)系起來。12.3.2 靜態(tài)關(guān)聯(lián)與動態(tài)關(guān)聯(lián) 第27頁/共55頁函數(shù)重載和通過對象名調(diào)用的虛函數(shù),在編譯時即可確定其調(diào)用的虛函數(shù)屬于哪一個類,其過程稱為靜態(tài)關(guān)聯(lián)(static binding),由于是在運(yùn)行前進(jìn)行關(guān)聯(lián)的,故又稱為早期關(guān)聯(lián)(early binding)。函數(shù)重載屬靜態(tài)關(guān)聯(lián)。 而運(yùn)行時的多態(tài)性,編譯系統(tǒng)在編譯該行時是無法確定調(diào)用哪一個類對象的虛函數(shù)的。因為編譯只作靜態(tài)的語法檢查,光從語句形式是無法確定調(diào)用對象的。第28頁/共55頁例如,先使pt指向grad1,再執(zhí)行“pt-display( )”,當(dāng)然是調(diào)用grad1中的display函數(shù)。由于是在運(yùn)行階段把虛
25、函數(shù)和類對象“綁定”在一起的,因此,此過程稱為動態(tài)關(guān)聯(lián)(dynamic binding)。這種多態(tài)性是動態(tài)的多態(tài)性,即運(yùn)行階段的多態(tài)性。在運(yùn)行階段,指針可以先后指向不同的類對象,從而調(diào)用同一類族中不同類的虛函數(shù)。由于動態(tài)關(guān)聯(lián)是在編譯以后的運(yùn)行階段進(jìn)行的,因此也稱為滯后關(guān)聯(lián)(late binding)。第29頁/共55頁使用虛函數(shù)時,有兩點(diǎn)要注意: (1)只能用virtual聲明類的成員函數(shù),使它成為虛函數(shù),而不能將類外的普通函數(shù)聲明為虛函數(shù)。因為虛函數(shù)的作用:是允許在派生類中對基類的虛函數(shù)重新定義。顯然,它只能用于類的繼承層次結(jié)構(gòu)中。12.3.3 在什么情況下應(yīng)當(dāng)聲明虛函數(shù)第30頁/共55頁(
26、2) 應(yīng)考慮對成員函數(shù)的調(diào)用是通過對象名還是通過基類指針或引用去訪問,如果是通過基類指針或引用去訪問的,則應(yīng)當(dāng)聲明為虛函數(shù)。需要說明的是: 使用虛函數(shù),系統(tǒng)要有一定的空間開銷。當(dāng)一個類帶有虛函數(shù)時,編譯系統(tǒng)會為該類構(gòu)造一個虛函數(shù)表(virtual function table,簡稱vtable),它是一個指針數(shù)組,存放每個虛函數(shù)的入口地址。系統(tǒng)在進(jìn)行動態(tài)關(guān)聯(lián)時的時間開銷是很少的,因此,多態(tài)性是高效的。第31頁/共55頁多態(tài)性進(jìn)一步理解:多態(tài)性是指在運(yùn)行時,能根據(jù)其類型確認(rèn)調(diào)用哪個函數(shù)的能力。只支持類而不支持多態(tài),稱基于對象的;如VB。只有支持多態(tài),才成為面向?qū)ο蟮?。好處分析:如計算學(xué)生的學(xué)費(fèi),
27、有大學(xué)生,研究生,博士生等。從程序設(shè)計看,用采取繼承方式設(shè)計。希望只有一個收費(fèi)員(函數(shù))可以收各種學(xué)生的費(fèi)用。 而不是首先有一個管理者判斷是什么學(xué)生?再分派給各個類型的收費(fèi)員(函數(shù))去收費(fèi)。 希望只有一個收費(fèi)員void fn(Student &x) x.calcTuition();若不這樣, fn中要判斷,分別調(diào)用,維護(hù)量大,面向?qū)ο髢?yōu)越性被限制,又回到面向過程。 第32頁/共55頁析構(gòu)函數(shù)的作用是在對象撤銷之前做必要的“清理現(xiàn)場”的工作。當(dāng)派生類的對象從內(nèi)存中撤銷時一般先調(diào)用派生類的析構(gòu)函數(shù),然后再調(diào)用基類的析構(gòu)函數(shù)。但用delete 刪除對象時存在如下問題,即沒有達(dá)到期望的目標(biāo)。如何解決?
28、12.3.4 虛析構(gòu)函數(shù)(還有什么函數(shù)可以虛之?)(還有什么函數(shù)可以虛之?)第33頁/共55頁例12.3 基類中有非虛析構(gòu)函數(shù)時的執(zhí)行情況。#include using namespace std;class Point/定義基類Point類public: Point( ) /Point類構(gòu)造函數(shù) Point()coutexecuting Point destructorendl;/Point類析構(gòu)函數(shù);class Circle:public Point /定義派生類Circle類public: Circle( ) /Circle類構(gòu)造函數(shù) Circle( )coutexecuting Cir
29、cle destructorendl;/Circle類析構(gòu)函數(shù) private: int radius;第34頁/共55頁int main( ) Point *p=new Circle; /用new開辟動態(tài)存儲空間delete p; /用delete釋放動態(tài)存儲空間return 0;這只是一個示意的程序。p是指向基類的指針變量,指向new開辟的動態(tài)存儲空間,希望用detele釋放p所指向的空間,即元類對象的空間。但運(yùn)行結(jié)果為executing Point destructor表示只執(zhí)行了基類Point的析構(gòu)函數(shù),而沒有執(zhí)行派生類Circle的析構(gòu)函數(shù)。第35頁/共55頁原因是:如果希望能執(zhí)行派
30、生類Circle的析構(gòu)函數(shù),方法:可以將基類的析構(gòu)函數(shù)聲明為虛析構(gòu)函數(shù),如virtual Point()coutexecuting Point destructorendl;程序其他部分不改動,再運(yùn)行程序,結(jié)果為executing Circle destructorexecuting Point destructor先調(diào)用了派生類的析構(gòu)函數(shù),再調(diào)用了基類的析構(gòu)函數(shù),符合人們的愿望。第36頁/共55頁特點(diǎn)與好處:如果將基類的析構(gòu)函數(shù)聲明為虛函數(shù)時,由該基類所派生的所有派生類的析構(gòu)函數(shù)也都自動成為虛函數(shù)。這樣,如果程序中顯式地用了delete運(yùn)算符準(zhǔn)備刪除一個對象,而delete運(yùn)算符的操作對象用
31、了指向派生類對象的基類指針,則系統(tǒng)會調(diào)用相應(yīng)類的析構(gòu)函數(shù)。一般即使基類并不需要析構(gòu)函數(shù),也顯式地定義一個函數(shù)體為空的虛析構(gòu)函數(shù),以保證在撤銷動態(tài)分配空間時能得到正確的處理。構(gòu)造函數(shù)不能聲明為虛函數(shù)。這是因為在執(zhí)行構(gòu)造函數(shù)時類對象還未完成建立過程,當(dāng)然談不上函數(shù)與類對象的綁定。第37頁/共55頁例如在本章的例12.1程序中,基類Point中沒有求面積的area函數(shù),因為“點(diǎn)”是沒有面積的,也就是說,基類本身不需要這個函數(shù),所以在例12.1程序中的Point類中沒有定義area函數(shù)。但是,在其直接派生類Circle和間接派生類Cylinder中都需要有area函數(shù),而且這兩個area函數(shù)的功能不同
32、,一個是求圓面積,一個是求圓柱體表面積。僅供派生而無實際意義的函數(shù),故純虛之。12.4 純虛函數(shù)與抽象類 12.4.1 純虛函數(shù)第38頁/共55頁有的讀者自然會想到,在這種情況下應(yīng)當(dāng)將area聲明為虛函數(shù)。可以在基類Point中加一個area函數(shù),并聲明為虛函數(shù): virtual float area( ) const return 0;其返回值為0,表示“點(diǎn)”是沒有面積的。為簡化,可以不寫出這種無意義的函數(shù)體,只給出函數(shù)的原型,并在后面加上“=0”,如virtual float area( ) const =0;/純虛函數(shù)這就將area聲明為一個純虛函數(shù)(pure virtual funct
33、ion)。純虛函數(shù)是在聲明虛函數(shù)時被“初始化”為0的函數(shù)。聲明純虛函數(shù)的一般形式是virtual 函數(shù)類型 函數(shù)名 (參數(shù)表列) =0;第39頁/共55頁注意: 純虛函數(shù)沒有函數(shù)體;最后面的“=0”并不表示函數(shù)返回值為0,告訴編譯系統(tǒng)“這是純虛函數(shù)”; 這是一個聲明語句,最后應(yīng)有分號。純虛函數(shù)的作用:純虛函數(shù)只有函數(shù)的名字而不具備函數(shù)的功能。它只是通知編譯系統(tǒng): “在這里聲明一個虛函數(shù),留待派生類中定義”。純虛函數(shù)的作用是在基類中為其派生類保留一個函數(shù)的名字,以便派生類根據(jù)需要對它進(jìn)行定義。如果在基類中沒有保留函數(shù)名字,則無法實現(xiàn)多態(tài)性。如果在一個類中聲明了純虛函數(shù),而在其派生類中沒有對該函數(shù)
34、定義,則該虛函數(shù)在派生類中仍然為純虛函數(shù)。第40頁/共55頁如果聲明了一個類,一般可以用它定義對象。但是在面向?qū)ο蟪绦蛟O(shè)計中,往往有一些類,它們不用來生成對象。定義這些類的惟一目的是用它作為基類去建立派生類。這種不用來定義對象而只作為一種基本類型用作繼承的類,稱為抽象類(abstract class),由于它常用作基類,通常稱為抽象基類(abstract base class)。12.4.2 抽象類第41頁/共55頁凡是包含純虛函數(shù)的類都是抽象類。因為純虛函數(shù)是不能被調(diào)用的,包含純虛函數(shù)的類是無法建立對象的。抽象類的作用是作為一個類族的共同基類,或者說,為一個類族提供一個公共接口。抽象類的條件
35、:包含純虛函數(shù)使用規(guī)則:如果在抽象類所派生出的新類中對基類的所有純虛函數(shù)進(jìn)行了定義,那么這些函數(shù)就被賦予了功能,可以被調(diào)用。這個派生類就不是抽象類,而是可以用來定義對象的具體類(concrete class)。如果在派生類中沒有對所有純虛函數(shù)進(jìn)行定義,則此派生類仍然是抽象類,不能用來定義對象。第42頁/共55頁雖然抽象類不能定義對象(或者說抽象類不能實例化),但是可以定義指向抽象類數(shù)據(jù)的指針變量。當(dāng)派生類成為具體類之后,就可以用這種指針指向派生類對象,然后通過該指針調(diào)用虛函數(shù),實現(xiàn)多態(tài)性的操作。總結(jié):抽象類的實際作用:定義指向抽象類的指針,實現(xiàn)多態(tài)性操作。即對各純虛函數(shù)可實現(xiàn)多態(tài)性操作。第43
36、頁/共55頁例12.4 虛函數(shù)和抽象基類的應(yīng)用。在本章例12.1介紹了以Point為基類的點(diǎn)圓圓柱體類的層次結(jié)構(gòu)。現(xiàn)在要對它進(jìn)行改寫,在程序中使用虛函數(shù)和抽象基類。類的層次結(jié)構(gòu)的頂層是抽象基類Shape(形狀)。Point(點(diǎn)), Circle(圓), Cylinder(圓柱體)都是Shape類的直接派生類和間接派生類。下面是一個完整的程序,為了便于閱讀,分段插入了一些文字說明。程序如下: 12.4.3 應(yīng)用實例第44頁/共55頁第(1)部分#include using namespace std;/ /聲明抽象基類Shape 因含純虛函數(shù)!class Shape /目的:可使各純虛函數(shù)實現(xiàn)多
37、態(tài)性public: virtual float area( ) const return 0.0;/虛函數(shù) virtual float volume() const return 0.0; /虛函數(shù) virtual void shapeName() const =0; /純虛函數(shù); /第(2)部分/聲明Point類class Point:public Shape/Point是Shape的公用派生類public: Point(float=0,float=0); void setPoint(float,float); float getX( ) const return x; float getY
38、( ) const return y; virtual void shapeName( ) const coutPoint:; /對虛函數(shù)進(jìn)行再定義 friend ostream & operator(ostream &,const Point &);protected: float x,y;第45頁/共55頁 ; Point:Point(float a,float b)x=a;y=b;void Point:setPoint(float a,float b)x=a;y=b;ostream & operator(ostream &output,const Point &p)outputp.x,p
39、.y;return output;/第(3)部分/聲明Circle類class Circle:public Pointpublic: Circle(float x=0,float y=0,float r=0); void setRadius(float); float getRadius( ) const; virtual float area( ) const; virtual void shapeName( ) const coutCircle:;/對虛函數(shù)進(jìn)行再定義 friend ostream &operator(ostream &,const Circle &);protected:
40、第46頁/共55頁 float radius;/聲明Circle類成員函數(shù)Circle:Circle(float a,float b,float r):Point(a,b),radius(r) void Circle:setRadius(float r):radius(r) float Circle:getRadius( ) const return radius;float Circle:area( ) const return 3.14159*radius*radius;ostream &operator(ostream &output,const Circle &c)outputc.x,
41、c.y, r=c.radius; return output;第(4)部分/聲明Cylinder類class Cylinder:public Circlepublic: Cylinder (float x=0,float y=0,float r=0,float h=0); void setHeight(float); virtual float area( ) const; virtual float volume( ) const;第47頁/共55頁 virtual void shapeName( ) const coutCylinder:;/對虛函數(shù)進(jìn)行再定義 friend ostream
42、& operator(ostream&,const Cylinder&); protected: float height; /定義Cylinder類成員函數(shù)Cylinder:Cylinder(float a,float b,float r,float h) :Circle(a,b,r),height(h) void Cylinder:setHeight(float h)height=h;float Cylinder:area( ) const return 2*Circle:area( )+2*3.14159*radius*height;float Cylinder:volume( ) co
43、nstreturn Circle:area( )*height;ostream &operator(ostream &output,const Cylinder& cy)outputcy.x,cy.y, r=cy.radius, h=cy.height;return output;第48頁/共55頁/main函數(shù)int main( )Point point(3.2,4.5); /建立Point類對象point Circle circle(2.4,1.2,5.6); /建立Circle類對象circle Cylinder cylinder(3.5,6.4,5.2,10.5); /建立Cylind
44、er類對象cylinder point.shapeName(); /靜態(tài)關(guān)聯(lián) coutpointendl; circle.shapeName(); /靜態(tài)關(guān)聯(lián) coutcircleendl; cylinder.shapeName(); /靜態(tài)關(guān)聯(lián) coutcylinderendlshapeName( ); /動態(tài)關(guān)聯(lián) coutx=point.getX( ),y=point.getY( )narea=area( ) nvolume=volume()shapeName( ); /動態(tài)關(guān)聯(lián) coutx=circle.getX( ),y=circle.getY( )narea=area( ) nvol
45、ume=volume( )shapeName( ); /動態(tài)關(guān)聯(lián) coutx=cylinder.getX( ),y=cylinder.getY( )narea=area( )nvolume=volume( )nn; return 0;以上程序也可:先定義函數(shù) void fn( Shape & x) x. shapeName( ); 再 void main( ) fn(point); fn(circle); fn(cylinder); .程序運(yùn)行結(jié)果如下:第50頁/共55頁P(yáng) o i n t : 3 . 2 , 4 . 5 ( P o i n t 類 對 象 p o i n t 的 數(shù) 據(jù) :
46、點(diǎn) 的 坐 標(biāo) )C i r c l e : 2 . 4 , 1 . 2 , r = 5 . 6 ( C i r c l e 類 對 象 c i r c l e 的 數(shù) 據(jù) : 圓 心 和 半 徑 )C y l i n d e r : 3 . 5 , 6 . 4 , r = 5 . 5 , h = 1 0 . 5 ( C y l i n d e r 類 對 象 c y l i n d e r 的 數(shù) 據(jù) : 圓 心 、 半 徑 和 高 )P o i n t : x = 3 . 2 , y = 4 . 5 ( 輸 出 P o i n t 類 對 象 p o i n t 的 數(shù) 據(jù) : 點(diǎn) 的 坐
47、 標(biāo) )a r e a = 0 ( 點(diǎn) 的 面 積 )v o l u m e = 0 ( 點(diǎn) 的 體 積 )C i r c l e : x = 2 . 4 , y = 1 . 2 ( 輸 出 C i r c l e 類 對 象 c i r c l e 的 數(shù) 據(jù) : 圓 心 坐 標(biāo) )a r e a = 9 8 . 5 2 0 3 ( 圓 的 面 積 )v o l u m e = 0 ( 圓 的 體 積 )C y l i n d e r : x = 3 . 5 , y = 6 . 4 ( 輸 出 C y l i n d e r 類 對 象 c y l i n d e r 的 數(shù) 據(jù) : 圓 心
48、 坐 標(biāo) )a r e a = 5 1 2 . 5 9 5 ( 圓 的 面 積 )v o l u m e = 8 9 1 . 9 6 ( 圓 柱 的 體 積 ) 第51頁/共55頁從本例可以進(jìn)一步明確以下結(jié)論:(1) 一個基類如果包含一個或一個以上純虛函數(shù),就是抽象基類。抽象基類不能也不必要定義對象。(2) 抽象基類與普通基類不同,它不是現(xiàn)實存在的對象的抽象(例如圓形(Circle)就是千千萬萬個實際的圓的抽象),它可以沒有任何物理上的或其他實際意義方面的含義。(3) 在類的層次結(jié)構(gòu)中,頂層或最上面的幾層可以是抽象基類。抽象基類體現(xiàn)了本類族中各類的共性,把各類中共有的成員函數(shù)集中在抽象基類中聲
49、明。(4) 抽象基類是本類族的公共接口?;蛘哒f,從同一基類派生出的多個類有同一接口。 (5) 區(qū)別靜態(tài)關(guān)聯(lián)和動態(tài)關(guān)聯(lián)。第52頁/共55頁(6) 如果在基類聲明了虛函數(shù),則在派生類中凡是與該函數(shù)有相同的函數(shù)名、函數(shù)類型、參數(shù)個數(shù)和類型的函數(shù),均為虛函數(shù)(不論在派生類中是否用virtual聲明)。(7) 使用虛函數(shù)提高了程序的可擴(kuò)充性。把類的聲明與類的使用分離。這對于設(shè)計類庫的軟件開發(fā)商來說尤為重要。開發(fā)商設(shè)計了各種各樣的類,但不向用戶提供源代碼,用戶可以不知道類是怎樣聲明的,但是可以使用這些類來派生出自己的類。利用虛函數(shù)和多態(tài)性,程序員的注意力集中在處理普遍性,而讓執(zhí)行環(huán)境處理特殊性。第53頁/共55頁虛基類與抽象基類的區(qū)別:前者解決模糊二義性問題;后者解決繼承中的多態(tài)性問題。實現(xiàn)時,前者由在繼承方式前加virtual; 后者靠純虛函數(shù)實現(xiàn)。虛函數(shù)與純虛函數(shù)對比:純在抽象類中,被“初始化”為0的函數(shù)。沒有實際意義,為派生類保留名字,方便該函數(shù)實現(xiàn)多態(tài)性。虛函數(shù)有實際意義,也是為實現(xiàn)多態(tài)性服務(wù)。第54頁/共55頁感謝您的觀看!第55頁/共55頁
- 溫馨提示:
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)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 6.煤礦安全生產(chǎn)科普知識競賽題含答案
- 2.煤礦爆破工技能鑒定試題含答案
- 3.爆破工培訓(xùn)考試試題含答案
- 2.煤礦安全監(jiān)察人員模擬考試題庫試卷含答案
- 3.金屬非金屬礦山安全管理人員(地下礦山)安全生產(chǎn)模擬考試題庫試卷含答案
- 4.煤礦特種作業(yè)人員井下電鉗工模擬考試題庫試卷含答案
- 1 煤礦安全生產(chǎn)及管理知識測試題庫及答案
- 2 各種煤礦安全考試試題含答案
- 1 煤礦安全檢查考試題
- 1 井下放炮員練習(xí)題含答案
- 2煤礦安全監(jiān)測工種技術(shù)比武題庫含解析
- 1 礦山應(yīng)急救援安全知識競賽試題
- 1 礦井泵工考試練習(xí)題含答案
- 2煤礦爆破工考試復(fù)習(xí)題含答案
- 1 各種煤礦安全考試試題含答案