《C++程序設(shè)計(jì)》-第12章n課件_第1頁(yè)
《C++程序設(shè)計(jì)》-第12章n課件_第2頁(yè)
《C++程序設(shè)計(jì)》-第12章n課件_第3頁(yè)
《C++程序設(shè)計(jì)》-第12章n課件_第4頁(yè)
《C++程序設(shè)計(jì)》-第12章n課件_第5頁(yè)
已閱讀5頁(yè),還剩105頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

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

文檔簡(jiǎn)介

第12章多態(tài)性與虛函數(shù)12.1多態(tài)性的概念12.2一個(gè)典型的例子12.3虛函數(shù)12.4純虛函數(shù)與抽象類第12章多態(tài)性與虛函數(shù)12.1多態(tài)性的概念多態(tài)性(polymorphism)是面向?qū)ο蟪绦蛟O(shè)計(jì)的一個(gè)重要特征。利用多態(tài)性可以設(shè)計(jì)和實(shí)現(xiàn)一個(gè)易于擴(kuò)展的系統(tǒng)。在C++程序設(shè)計(jì)中,多態(tài)性是指具有不同功能的函數(shù)可以用同一個(gè)函數(shù)名,這樣就可以用一個(gè)函數(shù)名調(diào)用不同內(nèi)容的函數(shù)。在面向?qū)ο蠓椒ㄖ械亩鄳B(tài)性:向不同的對(duì)象發(fā)送同一個(gè)消息(函數(shù)名),不同的對(duì)象在接收時(shí)會(huì)產(chǎn)生不同的行為(即方法)。也就是說(shuō),每個(gè)對(duì)象可以用自己的方式去響應(yīng)共同的消息。12.1多態(tài)性的概念多態(tài)性(polymorphism)是面向?qū)ο蟪绦蛟O(shè)計(jì)的一個(gè)重優(yōu)點(diǎn):在C++程序設(shè)計(jì)中,在不同的類中定義了其響應(yīng)消息的方法,那么使用這些類時(shí),不必考慮它們是什么類型,只要發(fā)布消息即可。多態(tài)性分為兩類:靜態(tài)多態(tài)性和動(dòng)態(tài)多態(tài)性。靜態(tài)多態(tài)性:函數(shù)重載和運(yùn)算符重載屬于靜態(tài)多態(tài)性,在程序編譯時(shí)系統(tǒng)就能決定調(diào)用的是哪個(gè)函數(shù),因此靜態(tài)多態(tài)性又稱編譯時(shí)的多態(tài)性。動(dòng)態(tài)多態(tài)性:是在程序運(yùn)行過(guò)程中才動(dòng)態(tài)地確定操作所針對(duì)的對(duì)象。它又稱運(yùn)行時(shí)的多態(tài)性。動(dòng)態(tài)多態(tài)性是通過(guò)虛函數(shù)(virtualfunction)實(shí)現(xiàn)的。在本章中主要介紹動(dòng)態(tài)多態(tài)性和虛函數(shù)。優(yōu)點(diǎn):在C++程序設(shè)計(jì)中,在不同的類中定義了其響應(yīng)消息的方法下面是一個(gè)承上啟下的例子。一方面它是有關(guān)繼承和運(yùn)算符重載內(nèi)容的綜合應(yīng)用的例子,通過(guò)這個(gè)例子可以進(jìn)一步融會(huì)貫通前面所學(xué)的內(nèi)容,另一方面又是作為討論多態(tài)性的一個(gè)基礎(chǔ)用例。12.2一個(gè)典型的例子下面是一個(gè)承上啟下的例子。一方面它是有關(guān)繼承和運(yùn)算符重載內(nèi)容例12.1先建立一個(gè)Point(點(diǎn))類,包含數(shù)據(jù)成員x,y(坐標(biāo)點(diǎn))。以它為基類,派生出一個(gè)Circle(圓)類,增加數(shù)據(jù)成員r(半徑),再以Circle類為直接基類,派生出一個(gè)Cylinder(圓柱體)類,再增加數(shù)據(jù)成員h(高)。要求編寫程序,重載運(yùn)算符“<<”和“>>”,使之能用于輸出以上類對(duì)象。對(duì)于一個(gè)比較大的程序,應(yīng)當(dāng)分成若干步驟進(jìn)行。先聲明基類,再聲明派生類,逐級(jí)進(jìn)行,分步調(diào)試。(1)聲明基類Point類寫出聲明基類Point的部分:#include<iostream>//聲明類Point//聲明一個(gè)類包括什么?classPoint{public:Point(floatx=0,floaty=0);//有默認(rèn)參數(shù)的構(gòu)造函數(shù)例12.1先建立一個(gè)Point(點(diǎn))類,包含數(shù)據(jù)成員x,y

voidsetPoint(float,float);//設(shè)置坐標(biāo)值

floatgetX()const{returnx;}//讀x坐標(biāo)

floatgetY()const{returny;}//讀y坐標(biāo)

friendostream&operator<<(ostream&,constPoint&);//重載運(yùn)算符“<<”protected://受保護(hù)成員

floatx,y;};//下面定義Point類的成員函數(shù)//Point的構(gòu)造函數(shù)Point::Point(floata,floatb)//對(duì)x,y初始化{x=a;y=b;}//設(shè)置x和y的坐標(biāo)值voidPoint::setPoint(floata,floatb)//為x,y賦新值{x=a;y=b;}//重載運(yùn)算符“<<”,使之能輸出點(diǎn)的坐標(biāo)ostream&operator<<(ostream&output,constPoint&p){output<<″[″<<p.x<<″,″<<p.y<<″]″<<endl;returnoutput;}以上完成了基類Point類的聲明。voidsetPoint(float,float);現(xiàn)在要對(duì)上面寫的基類聲明進(jìn)行調(diào)試,檢查它是否有錯(cuò),為此要寫出main函數(shù)。實(shí)際上它是一個(gè)測(cè)試程序。intmain(){Pointp(3.5,6.4);//建立Point類對(duì)象pcout<<″x=″<<p.getX()<<″,y=″<<p.getY()<<endl;//輸出p的坐標(biāo)值

p.setPoint(8.5,6.8);//重新設(shè)置p的坐標(biāo)值

cout<<″p(new):″<<p<<endl;//用重載運(yùn)算符“<<”輸出p點(diǎn)坐標(biāo)}程序編譯通過(guò),運(yùn)行結(jié)果為x=3.5,y=6.4p(new):[8.5,6.8]測(cè)試程序檢查了基類中各函數(shù)的功能,以及運(yùn)算符重載的作用,證明程序是正確的。現(xiàn)在要對(duì)上面寫的基類聲明進(jìn)行調(diào)試,檢查它是否有錯(cuò),為此要寫出(2)聲明派生類CircleclassCircle:publicPoint//circle是Point類的公用派生類{public:Circle(floatx=0,floaty=0,floatr=0);//構(gòu)造函數(shù)

voidsetRadius(float);//設(shè)置半徑值

floatgetRadius()const;//讀取半徑值

floatarea()const;//計(jì)算圓面積

friendostream&operator<<(ostream&,constCircle&);//重載運(yùn)算符“<<”

private://?protected:floatradius;};//定義構(gòu)造函數(shù),對(duì)圓心坐標(biāo)和半徑初始化Circle::Circle(floata,floatb,floatr):Point(a,b),radius(r){}voidCircle::setRadius(floatr)//設(shè)置半徑值{radius=r;}floatCircle::getRadius()const{returnradius;}//讀取半徑值floatCircle::area()const(2)聲明派生類Circle{return3.14159*radius*radius;}//重載運(yùn)算符“<<”,使之按規(guī)定的形式輸出圓的信息ostream&operator<<(ostream&output,constCircle&c){output<<″Center=[″<<c.x<<″,″<<c.y<<″],r=″<<c.radius<<″,area=″<<c.area()<<endl;returnoutput;}為了測(cè)試Circle類的定義,可以寫出下面的主函數(shù):{return3.14159*radius*radius;intmain(){Circlec(3.5,6.4,5.2);//建立Circle類對(duì)象c,并給定圓心坐標(biāo)和半徑

cout<<″originalcircle:\\nx=″<<c.getX()<<″,y=″<<c.getY()<<″,r=″<<c.getRadius()<<″,area=″<<c.area()<<endl;//輸出圓心坐標(biāo)、半徑和面積

c.setRadius(7.5);//設(shè)置半徑值

c.setPoint(5,5);//設(shè)置圓心坐標(biāo)值x,ycout<<″newcircle:\\n″<<c;//用重載運(yùn)算符“<<”輸出圓對(duì)象的信息

Point&pRef=c;//pRef是Point類的引用變量,被c初始化

cout<<″pRef:″<<pRef;//輸出pRef的信息

return0;}程序編譯通過(guò),運(yùn)行結(jié)果為originalcircle:(輸出原來(lái)的圓的數(shù)據(jù))x=3.5,y=6.4,r=5.2,area=84.9486newcircle:(輸出修改后的圓的數(shù)據(jù))Center=[5,5],r=7.5,area=176.714pRef:[5,5](輸出圓的圓心“點(diǎn)”的數(shù)據(jù))intmain()(3)聲明Circle的派生類CylinderclassCylinder:publicCircle//Cylinder是Circle的公用派生類{public:Cylinder(floatx=0,floaty=0,floatr=0,floath=0);//構(gòu)造函數(shù)

voidsetHeight(float);//設(shè)置圓柱高

floatgetHeight()const;//讀取圓柱高

floatarea()const;//計(jì)算圓表面積

floatvolume()const;//計(jì)算圓柱體積

friendostream&operator<<(ostream&,constCylinder&);//重載運(yùn)算符“<<”

protected:floatheight;//圓柱高};Cylinder::Cylinder(floata,floatb,floatr,floath)//定義構(gòu)造函數(shù):Circle(a,b,r),height(h){}voidCylinder::setHeight(floath){height=h;}//設(shè)置圓柱高floatCylinder::getHeight()const{returnheight;}//讀取圓柱高(3)聲明Circle的派生類Cylinder//計(jì)算圓表面積floatCylinder::area()const{return2*Circle::area()+2*3.14159*radius*height;}floatCylinder::volume()const//計(jì)算圓柱體積{returnCircle::area()*height;}//重載運(yùn)算符“<<”ostream&operator<<(ostream&output,constCylinder&cy){output<<″Center=[″<<cy.x<<″,″<<cy.y<<″],r=″<<cy.radius<<″,h=″<<cy.height<<″\\narea=″<<cy.area()<<″,volume=″<<cy.volume()<<endl;returnoutput;}//計(jì)算圓表面積

可以寫出下面的主函數(shù):intmain(){Cylindercy1(3.5,6.4,5.2,10);//定義Cylinder類對(duì)象cy1cout<<″\\noriginalcylinder:\\nx=″<<cy1.getX()<<″,y=″<<cy1.getY()<<″,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,ycout<<″\\nnewcylinder:\\n″<<cy1;//用重載運(yùn)算符“<<”輸出cy1的數(shù)據(jù)

Point&pRef=cy1;//pRef是Point類對(duì)象的引用變量

cout<<″\\npRefasaPoint:″<<pRef;//pRef作為一個(gè)“點(diǎn)”輸出

Circle&cRef=cy1;//cRef是Circle類對(duì)象的引用變量

cout<<″\\ncRefasaCircle:″<<cRef;//cRef作為一個(gè)“圓”輸出

return0;}運(yùn)行結(jié)果如下:originalcylinder:(輸出cy1的初始值)x=3.5,y=6.4,r=5.2,h=10(圓心坐標(biāo)x,y。半徑r,高h(yuǎn))area=496.623,volume=849.486(圓柱表面積area和體積volume)newcylinder:(輸出cy1的新值)Center=[5,5],r=7.5,h=15(以[5,5]形式輸出圓心坐標(biāo))area=1060.29,volume=2650.72(圓柱表面積area和體積volume)pRefasaPoint:[5,5](pRef作為一個(gè)“點(diǎn)”輸出)cRefasaCircle:Center=[5,5],r=7.5,area=176.714(cRef作為一個(gè)“圓”輸出)在本例中存在靜態(tài)多態(tài)性,這是運(yùn)算符重載引起的。可以看到,在編譯時(shí)編譯系統(tǒng)即可以判定應(yīng)調(diào)用哪個(gè)重載運(yùn)算符函數(shù)。稍后將在此基礎(chǔ)上討論動(dòng)態(tài)多態(tài)性問(wèn)題。cout<<″\\npRefasaPoint:″<<在類的繼承層次結(jié)構(gòu)中,在不同的層次中可以出現(xiàn)名字相同、參數(shù)個(gè)數(shù)和類型都相同而功能不同的函數(shù)。編譯系統(tǒng)按照同名覆蓋的原則決定調(diào)用的對(duì)象。在例12.1程序中用cy1.area()調(diào)用的是派生類Cylinder中的成員函數(shù)area。如果想調(diào)用cy1中的直接基類Circle的area函數(shù),應(yīng)當(dāng)表示為:cy1.Circle::area()。用這種方法來(lái)區(qū)分兩個(gè)同名的函數(shù)。但是這樣做很不方便。12.3虛函數(shù)

12.3.1虛函數(shù)的作用在類的繼承層次結(jié)構(gòu)中,在不同的層次中可以出現(xiàn)名字相同、參數(shù)個(gè)能否用同一個(gè)調(diào)用形式,既能調(diào)用派生類又能調(diào)用基類的同名函數(shù)?在程序中不是通過(guò)不同的對(duì)象名去調(diào)用不同派生層次中的同名函數(shù),而是通過(guò)指針調(diào)用它們。例如,用同一個(gè)語(yǔ)句“pt->display();”可以調(diào)用不同派生層次中的display函數(shù),只需在調(diào)用前給指針變量pt賦以不同的值(使之指向不同的類對(duì)象)即可。C++中的虛函數(shù)就是用來(lái)解決這個(gè)問(wèn)題的。虛函數(shù)的作用是允許在派生類中重新定義與基類同名的函數(shù),并且可以通過(guò)基類指針或引用來(lái)訪問(wèn)基類和派生類中的同名函數(shù)。方法:由基類指針,訪問(wèn)各層次中的同名函數(shù)。例12.2。再討論使用虛函數(shù)的情況。能否用同一個(gè)調(diào)用形式,既能調(diào)用派生類又能調(diào)用基類的同名函數(shù)?例12.2基類與派生類中有同名函數(shù)。在下面的程序中Student是基類,Graduate是派生類,它們都有display這個(gè)同名的函數(shù)。#include<iostream>#include<string>usingnamespacestd;//聲明基類StudentclassStudent{public:Student(int,string,float);//聲明構(gòu)造函數(shù)

voiddisplay();//聲明輸出函數(shù)

protected://受保護(hù)成員,派生類可以訪問(wèn)

intnum;stringname;floatscore;};例12.2基類與派生類中有同名函數(shù)。

//Student類成員函數(shù)的實(shí)現(xiàn)Student::Student(intn,stringnam,floats)//定義構(gòu)造函數(shù){num=n;name=nam;score=s;}voidStudent::display()//定義輸出函數(shù){cout<<″num:″<<num<<″\\nname:″<<name<<″\\nscore:″<<score<<″\\n\\n″;}

//聲明公用派生類GraduateclassGraduate:publicStudent{public:Graduate(int,string,float,float);//聲明構(gòu)造函數(shù)

voiddisplay();//聲明輸出函數(shù)private:floatpay;};//Graduate類成員函數(shù)的實(shí)現(xiàn)voidGraduate::display()//定義輸出函數(shù){cout<<″num:″<<num<<″\\nname:″<<name<<″\\nscore:″<<score<<″\\npay=″<<pay<<endl;}Graduate::Graduate(intn,stringnam,floats,floatp):Student(n,nam,s),pay(p){}《C++程序設(shè)計(jì)》-第12章n課件

//主函數(shù)intmain(){Studentstud1(1001,″Li″,87.5);//定義Student類對(duì)象stud1Graduategrad1(2001,″Wang″,98.5,563.5);//定義Graduate類對(duì)象grad1Student*pt=&stud1;//定義指向基類對(duì)象的指針變量pt(必須?。。?/pt可以指向基類或派生類對(duì)象,pt運(yùn)行時(shí)變化,故多態(tài)pt->display();pt=&grad1;pt->display();return0;}

//主函數(shù)運(yùn)行結(jié)果如下,請(qǐng)仔細(xì)分析。num:1001(stud1的數(shù)據(jù))name:Liscore:87.5num:2001(僅輸出了grad1中基類部分的數(shù)據(jù),沒(méi)實(shí)現(xiàn)多態(tài)性!)name:wangscore:98.5下面對(duì)程序作一點(diǎn)修改,在Student類中聲明display函數(shù)時(shí),在最左面加一個(gè)關(guān)鍵字virtual,即virtualvoiddisplay();變成虛函數(shù)就行了這樣就把Student類的display函數(shù)聲明為虛函數(shù)。程序其他部分都不改動(dòng)。再編譯和運(yùn)行程序,請(qǐng)注意分析運(yùn)行結(jié)果:運(yùn)行結(jié)果如下,請(qǐng)仔細(xì)分析。num:1001(stud1的數(shù)據(jù))name:Liscore:87.5num:2001(grad1中基類部分的數(shù)據(jù),達(dá)到目的!)name:wangscore:98.5pay=1200(這一項(xiàng)以前是沒(méi)有的)由虛函數(shù)實(shí)現(xiàn)的動(dòng)態(tài)多態(tài)性就是:同一類族中不同類的對(duì)象,對(duì)同一函數(shù)調(diào)用作出不同的響應(yīng)。多態(tài)性理解:起始地址相同的不同對(duì)象指針,調(diào)同名函數(shù)時(shí),響應(yīng)不同(調(diào)了各自對(duì)應(yīng)的函數(shù))。實(shí)現(xiàn)方法:將同名函數(shù)聲明為虛函數(shù)?!禖++程序設(shè)計(jì)》-第12章n課件虛函數(shù)的使用方法是:注意規(guī)則!??!

(1)在基類用virtual聲明成員函數(shù)為虛函數(shù)。這樣就可以在派生類中重新定義此函數(shù),為它賦予新的功能,并能方便地被調(diào)用。在類外定義虛函數(shù)時(shí),不必再加virtual。(2)在派生類中重新定義此函數(shù),要求函數(shù)名、函數(shù)類型、函數(shù)參數(shù)個(gè)數(shù)和類型全部與基類的虛函數(shù)相同,并根據(jù)派生類的需要重新定義函數(shù)體。

虛函數(shù)的使用方法是:注意規(guī)則!??!C++規(guī)定,當(dāng)一個(gè)成員函數(shù)被聲明為虛函數(shù)后,其派生類中的同名函數(shù)都自動(dòng)成為虛函數(shù)。因此在派生類重新聲明該虛函數(shù)時(shí),可以加virtual,也可以不加,但習(xí)慣上一般在每一層聲明該函數(shù)時(shí)都加virtual,使程序更加清晰。如果在派生類中沒(méi)有對(duì)基類的虛函數(shù)重新定義,則派生類簡(jiǎn)單地繼承其直接基類的虛函數(shù)。(3)必須定義一個(gè)指向基類對(duì)象的指針變量,并使它指向同一類族中需要調(diào)用該函數(shù)的對(duì)象。(4)通過(guò)該指針變量調(diào)用此虛函數(shù),此時(shí)調(diào)用的就是指針變量指向的對(duì)象的同名函數(shù)。C++規(guī)定,當(dāng)一個(gè)成員函數(shù)被聲明為虛函數(shù)后,其派生類中的同名舉例2:用引用也可,如將上面例題改為voidfun(Student&x)//必須為基類指針或引用?。?!{x.display();}//主函數(shù)intmain(){Students1(1001,″Li″,87.5);Graduategd1(2001,″Wang″,98.5,563.5);fun(s1);fun(gd1);return0;}舉例2:用引用也可,如將上面例題改為顯示結(jié)果:num:1001(stud1的數(shù)據(jù))name:Liscore:87.5num:2001(grad1的數(shù)據(jù))name:wangscore:98.5pay=1200顯示結(jié)果:注意區(qū)別:函數(shù)重載處理的是同一層次上的同名函數(shù)問(wèn)題,而虛函數(shù)處理的是不同派生層次上的同名函數(shù)問(wèn)題,前者是橫向重載,后者可以理解為縱向重載。但與重載不同的是:

同一類族的虛函數(shù)的首部是相同的,而函數(shù)重載時(shí)函數(shù)的首部是不同的(參數(shù)個(gè)數(shù)或類型不同)。注意區(qū)別:這樣編譯系統(tǒng)在對(duì)程序進(jìn)行編譯時(shí),即能確定調(diào)用的是哪個(gè)類對(duì)象中的函數(shù)。確定調(diào)用的具體對(duì)象的過(guò)程稱為關(guān)聯(lián)(binding)。在這里是指把一個(gè)函數(shù)名與一個(gè)類對(duì)象捆綁在一起,建立關(guān)聯(lián)。一般地說(shuō),關(guān)聯(lián)指把一個(gè)標(biāo)識(shí)符和一個(gè)存儲(chǔ)地址聯(lián)系起來(lái)。12.3.2靜態(tài)關(guān)聯(lián)與動(dòng)態(tài)關(guān)聯(lián)這樣編譯系統(tǒng)在對(duì)程序進(jìn)行編譯時(shí),即能確定調(diào)用的是哪個(gè)類對(duì)象中函數(shù)重載和通過(guò)對(duì)象名調(diào)用的虛函數(shù),在編譯時(shí)即可確定其調(diào)用的虛函數(shù)屬于哪一個(gè)類,其過(guò)程稱為靜態(tài)關(guān)聯(lián)(staticbinding),由于是在運(yùn)行前進(jìn)行關(guān)聯(lián)的,故又稱為早期關(guān)聯(lián)(earlybinding)。函數(shù)重載屬靜態(tài)關(guān)聯(lián)。

而運(yùn)行時(shí)的多態(tài)性,編譯系統(tǒng)在編譯該行時(shí)是無(wú)法確定調(diào)用哪一個(gè)類對(duì)象的虛函數(shù)的。因?yàn)榫幾g只作靜態(tài)的語(yǔ)法檢查,光從語(yǔ)句形式是無(wú)法確定調(diào)用對(duì)象的。函數(shù)重載和通過(guò)對(duì)象名調(diào)用的虛函數(shù),在編譯時(shí)即可確定其調(diào)用的虛例如,先使pt指向grad1,再執(zhí)行“pt->display()”,當(dāng)然是調(diào)用grad1中的display函數(shù)。由于是在運(yùn)行階段把虛函數(shù)和類對(duì)象“綁定”在一起的,因此,此過(guò)程稱為動(dòng)態(tài)關(guān)聯(lián)(dynamicbinding)。這種多態(tài)性是動(dòng)態(tài)的多態(tài)性,即運(yùn)行階段的多態(tài)性。在運(yùn)行階段,指針可以先后指向不同的類對(duì)象,從而調(diào)用同一類族中不同類的虛函數(shù)。由于動(dòng)態(tài)關(guān)聯(lián)是在編譯以后的運(yùn)行階段進(jìn)行的,因此也稱為滯后關(guān)聯(lián)(latebinding)。例如,先使pt指向grad1,再執(zhí)行“pt->display使用虛函數(shù)時(shí),有兩點(diǎn)要注意:(1)只能用virtual聲明類的成員函數(shù),使它成為虛函數(shù),而不能將類外的普通函數(shù)聲明為虛函數(shù)。因?yàn)樘摵瘮?shù)的作用:是允許在派生類中對(duì)基類的虛函數(shù)重新定義。顯然,它只能用于類的繼承層次結(jié)構(gòu)中。12.3.3在什么情況下應(yīng)當(dāng)聲明虛函數(shù)使用虛函數(shù)時(shí),有兩點(diǎn)要注意:12.3.3在什么情況下應(yīng)當(dāng)(2)應(yīng)考慮對(duì)成員函數(shù)的調(diào)用是通過(guò)對(duì)象名還是通過(guò)基類指針或引用去訪問(wèn),如果是通過(guò)基類指針或引用去訪問(wèn)的,則應(yīng)當(dāng)聲明為虛函數(shù)。需要說(shuō)明的是:使用虛函數(shù),系統(tǒng)要有一定的空間開銷。當(dāng)一個(gè)類帶有虛函數(shù)時(shí),編譯系統(tǒng)會(huì)為該類構(gòu)造一個(gè)虛函數(shù)表(virtualfunctiontable,簡(jiǎn)稱vtable),它是一個(gè)指針數(shù)組,存放每個(gè)虛函數(shù)的入口地址。系統(tǒng)在進(jìn)行動(dòng)態(tài)關(guān)聯(lián)時(shí)的時(shí)間開銷是很少的,因此,多態(tài)性是高效的。(2)應(yīng)考慮對(duì)成員函數(shù)的調(diào)用是通過(guò)對(duì)象名還是通過(guò)基類指針或多態(tài)性進(jìn)一步理解:多態(tài)性是指在運(yùn)行時(shí),能根據(jù)其類型確認(rèn)調(diào)用哪個(gè)函數(shù)的能力。只支持類而不支持多態(tài),稱基于對(duì)象的;如VB。只有支持多態(tài),才成為面向?qū)ο蟮?。好處分析:如?jì)算學(xué)生的學(xué)費(fèi),有大學(xué)生,研究生,博士生等。從程序設(shè)計(jì)看,用采取繼承方式設(shè)計(jì)。希望只有一個(gè)收費(fèi)員(函數(shù))可以收各種學(xué)生的費(fèi)用。

而不是首先有一個(gè)管理者判斷是什么學(xué)生?再分派給各個(gè)類型的收費(fèi)員(函數(shù))去收費(fèi)。

希望只有一個(gè)收費(fèi)員voidfn(Student&x){x.calcTuition();}若不這樣,fn中要判斷,分別調(diào)用,維護(hù)量大,面向?qū)ο髢?yōu)越性被限制,又回到面向過(guò)程。

多態(tài)性進(jìn)一步理解:析構(gòu)函數(shù)的作用是在對(duì)象撤銷之前做必要的“清理現(xiàn)場(chǎng)”的工作。當(dāng)派生類的對(duì)象從內(nèi)存中撤銷時(shí)一般先調(diào)用派生類的析構(gòu)函數(shù),然后再調(diào)用基類的析構(gòu)函數(shù)。但用delete刪除對(duì)象時(shí)存在如下問(wèn)題,即沒(méi)有達(dá)到期望的目標(biāo)。如何解決?12.3.4虛析構(gòu)函數(shù)(還有什么函數(shù)可以虛之?)析構(gòu)函數(shù)的作用是在對(duì)象撤銷之前做必要的“清理現(xiàn)場(chǎng)”的工作。當(dāng)例12.3基類中有非虛析構(gòu)函數(shù)時(shí)的執(zhí)行情況。#include<iostream>usingnamespacestd;classPoint//定義基類Point類{public:Point(){}//Point類構(gòu)造函數(shù)~Point(){cout<<″e(cuò)xecutingPointdestructor″<<endl;}//Point類析構(gòu)函數(shù)};classCircle:publicPoint//定義派生類Circle類{public:Circle(){}//Circle類構(gòu)造函數(shù)~Circle(){cout<<″e(cuò)xecutingCircledestructor″<<endl;}//Circle類析構(gòu)函數(shù)

private:intradius;};例12.3基類中有非虛析構(gòu)函數(shù)時(shí)的執(zhí)行情況。intmain(){Point*p=newCircle;//用new開辟動(dòng)態(tài)存儲(chǔ)空間deletep;//用delete釋放動(dòng)態(tài)存儲(chǔ)空間return0;}這只是一個(gè)示意的程序。p是指向基類的指針變量,指向new開辟的動(dòng)態(tài)存儲(chǔ)空間,希望用detele釋放p所指向的空間,即元類對(duì)象的空間。但運(yùn)行結(jié)果為executingPointdestructor表示只執(zhí)行了基類Point的析構(gòu)函數(shù),而沒(méi)有執(zhí)行派生類Circle的析構(gòu)函數(shù)。intmain()原因是:如果希望能執(zhí)行派生類Circle的析構(gòu)函數(shù),方法:可以將基類的析構(gòu)函數(shù)聲明為虛析構(gòu)函數(shù),如virtual~Point(){cout<<″e(cuò)xecutingPointdestructor″<<endl;}程序其他部分不改動(dòng),再運(yùn)行程序,結(jié)果為executingCircledestructorexecutingPointdestructor先調(diào)用了派生類的析構(gòu)函數(shù),再調(diào)用了基類的析構(gòu)函數(shù),符合人們的愿望。原因是:如果希望能執(zhí)行派生類Circle的析構(gòu)函數(shù),方法:可特點(diǎn)與好處:如果將基類的析構(gòu)函數(shù)聲明為虛函數(shù)時(shí),由該基類所派生的所有派生類的析構(gòu)函數(shù)也都自動(dòng)成為虛函數(shù)。這樣,如果程序中顯式地用了delete運(yùn)算符準(zhǔn)備刪除一個(gè)對(duì)象,而delete運(yùn)算符的操作對(duì)象用了指向派生類對(duì)象的基類指針,則系統(tǒng)會(huì)調(diào)用相應(yīng)類的析構(gòu)函數(shù)。一般即使基類并不需要析構(gòu)函數(shù),也顯式地定義一個(gè)函數(shù)體為空的虛析構(gòu)函數(shù),以保證在撤銷動(dòng)態(tài)分配空間時(shí)能得到正確的處理。構(gòu)造函數(shù)不能聲明為虛函數(shù)。這是因?yàn)樵趫?zhí)行構(gòu)造函數(shù)時(shí)類對(duì)象還未完成建立過(guò)程,當(dāng)然談不上函數(shù)與類對(duì)象的綁定。特點(diǎn)與好處:例如在本章的例12.1程序中,基類Point中沒(méi)有求面積的area函數(shù),因?yàn)椤包c(diǎn)”是沒(méi)有面積的,也就是說(shuō),基類本身不需要這個(gè)函數(shù),所以在例12.1程序中的Point類中沒(méi)有定義area函數(shù)。但是,在其直接派生類Circle和間接派生類Cylinder中都需要有area函數(shù),而且這兩個(gè)area函數(shù)的功能不同,一個(gè)是求圓面積,一個(gè)是求圓柱體表面積。僅供派生而無(wú)實(shí)際意義的函數(shù),故純虛之。12.4純虛函數(shù)與抽象類

12.4.1純虛函數(shù)例如在本章的例12.1程序中,基類Point中沒(méi)有求面積的a有的讀者自然會(huì)想到,在這種情況下應(yīng)當(dāng)將area聲明為虛函數(shù)??梢栽诨怭oint中加一個(gè)area函數(shù),并聲明為虛函數(shù):virtualfloatarea()const{return0;}其返回值為0,表示“點(diǎn)”是沒(méi)有面積的。為簡(jiǎn)化,可以不寫出這種無(wú)意義的函數(shù)體,只給出函數(shù)的原型,并在后面加上“=0”,如virtualfloatarea()const=0;//純虛函數(shù)這就將area聲明為一個(gè)純虛函數(shù)(purevirtualfunction)。純虛函數(shù)是在聲明虛函數(shù)時(shí)被“初始化”為0的函數(shù)。聲明純虛函數(shù)的一般形式是virtual函數(shù)類型函數(shù)名(參數(shù)表列)=0;有的讀者自然會(huì)想到,在這種情況下應(yīng)當(dāng)將area聲明為虛函數(shù)。注意:①純虛函數(shù)沒(méi)有函數(shù)體;②最后面的“=0”并不表示函數(shù)返回值為0,告訴編譯系統(tǒng)“這是純虛函數(shù)”;③這是一個(gè)聲明語(yǔ)句,最后應(yīng)有分號(hào)。純虛函數(shù)的作用:純虛函數(shù)只有函數(shù)的名字而不具備函數(shù)的功能。它只是通知編譯系統(tǒng):“在這里聲明一個(gè)虛函數(shù),留待派生類中定義”。純虛函數(shù)的作用是在基類中為其派生類保留一個(gè)函數(shù)的名字,以便派生類根據(jù)需要對(duì)它進(jìn)行定義。如果在基類中沒(méi)有保留函數(shù)名字,則無(wú)法實(shí)現(xiàn)多態(tài)性。如果在一個(gè)類中聲明了純虛函數(shù),而在其派生類中沒(méi)有對(duì)該函數(shù)定義,則該虛函數(shù)在派生類中仍然為純虛函數(shù)。注意:①純虛函數(shù)沒(méi)有函數(shù)體;②最后面的“=0”并不表示函數(shù)如果聲明了一個(gè)類,一般可以用它定義對(duì)象。但是在面向?qū)ο蟪绦蛟O(shè)計(jì)中,往往有一些類,它們不用來(lái)生成對(duì)象。定義這些類的惟一目的是用它作為基類去建立派生類。這種不用來(lái)定義對(duì)象而只作為一種基本類型用作繼承的類,稱為抽象類(abstractclass),由于它常用作基類,通常稱為抽象基類(abstractbaseclass)。12.4.2抽象類如果聲明了一個(gè)類,一般可以用它定義對(duì)象。但是在面向?qū)ο蟪绦蛟O(shè)凡是包含純虛函數(shù)的類都是抽象類。因?yàn)榧兲摵瘮?shù)是不能被調(diào)用的,包含純虛函數(shù)的類是無(wú)法建立對(duì)象的。抽象類的作用是作為一個(gè)類族的共同基類,或者說(shuō),為一個(gè)類族提供一個(gè)公共接口。抽象類的條件:包含純虛函數(shù)使用規(guī)則:如果在抽象類所派生出的新類中對(duì)基類的所有純虛函數(shù)進(jìn)行了定義,那么這些函數(shù)就被賦予了功能,可以被調(diào)用。這個(gè)派生類就不是抽象類,而是可以用來(lái)定義對(duì)象的具體類(concreteclass)。如果在派生類中沒(méi)有對(duì)所有純虛函數(shù)進(jìn)行定義,則此派生類仍然是抽象類,不能用來(lái)定義對(duì)象。凡是包含純虛函數(shù)的類都是抽象類。因?yàn)榧兲摵瘮?shù)是不能被調(diào)用的,雖然抽象類不能定義對(duì)象(或者說(shuō)抽象類不能實(shí)例化),但是可以定義指向抽象類數(shù)據(jù)的指針變量。當(dāng)派生類成為具體類之后,就可以用這種指針指向派生類對(duì)象,然后通過(guò)該指針調(diào)用虛函數(shù),實(shí)現(xiàn)多態(tài)性的操作??偨Y(jié):抽象類的實(shí)際作用:定義指向抽象類的指針,實(shí)現(xiàn)多態(tài)性操作。即對(duì)各純虛函數(shù)可實(shí)現(xiàn)多態(tài)性操作。雖然抽象類不能定義對(duì)象(或者說(shuō)抽象類不能實(shí)例化),但是可以定例12.4虛函數(shù)和抽象基類的應(yīng)用。在本章例12.1介紹了以Point為基類的點(diǎn)—圓—圓柱體類的層次結(jié)構(gòu)?,F(xiàn)在要對(duì)它進(jìn)行改寫,在程序中使用虛函數(shù)和抽象基類。類的層次結(jié)構(gòu)的頂層是抽象基類Shape(形狀)。Point(點(diǎn)),Circle(圓),Cylinder(圓柱體)都是Shape類的直接派生類和間接派生類。下面是一個(gè)完整的程序,為了便于閱讀,分段插入了一些文字說(shuō)明。程序如下:12.4.3應(yīng)用實(shí)例例12.4虛函數(shù)和抽象基類的應(yīng)用。12.4.3應(yīng)用實(shí)例第(1)部分#include<iostream>usingnamespacestd;////聲明抽象基類Shape因含純虛函數(shù)!classShape//目的:可使各純虛函數(shù)實(shí)現(xiàn)多態(tài)性{public:virtualfloatarea()const{return0.0;}//虛函數(shù)

virtualfloatvolume()const{return0.0;}//虛函數(shù)

virtualvoidshapeName()const=0;//純虛函數(shù)};//第(2)部分//聲明Point類classPoint:publicShape//Point是Shape的公用派生類{public:Point(float=0,float=0);voidsetPoint(float,float);floatgetX()const{returnx;}floatgetY()const{returny;}virtualvoidshapeName()const{cout<<″Point:″;}//對(duì)虛函數(shù)進(jìn)行再定義

friendostream&operator<<(ostream&,constPoint&);protected:floatx,y;第(1)部分};Point::Point(floata,floatb){x=a;y=b;}voidPoint::setPoint(floata,floatb){x=a;y=b;}ostream&operator<<(ostream&output,constPoint&p){output<<″[″<<p.x<<″,″<<p.y<<″]″;returnoutput;}//第(3)部分//聲明Circle類classCircle:publicPoint{public:Circle(floatx=0,floaty=0,floatr=0);voidsetRadius(float);floatgetRadius()const;virtualfloatarea()const;virtualvoidshapeName()const{cout<<″Circle:″;}//對(duì)虛函數(shù)進(jìn)行再定義

friendostream&operator<<(ostream&,constCircle&);protected:};

floatradius;};//聲明Circle類成員函數(shù)Circle::Circle(floata,floatb,floatr):Point(a,b),radius(r){}voidCircle::setRadius(floatr):radius(r){}floatCircle::getRadius()const{returnradius;}floatCircle::area()const{return3.14159*radius*radius;}ostream&operator<<(ostream&output,constCircle&c){output<<″[″<<c.x<<″,″<<c.y<<″],r=″<<c.radius;returnoutput;}第(4)部分//聲明Cylinder類classCylinder:publicCircle{public:Cylinder(floatx=0,floaty=0,floatr=0,floath=0);voidsetHeight(float);virtualfloatarea()const;virtualfloatvolume()const;floatradius;

virtualvoidshapeName()const{cout<<″Cylinder:″;}//對(duì)虛函數(shù)進(jìn)行再定義

friendostream&operator<<(ostream&,constCylinder&);protected:floatheight;};//定義Cylinder類成員函數(shù)Cylinder::Cylinder(floata,floatb,floatr,floath):Circle(a,b,r),height(h){}voidCylinder::setHeight(floath){height=h;}floatCylinder::area()const{return2*Circle::area()+2*3.14159*radius*height;}floatCylinder::volume()const{returnCircle::area()*height;}ostream&operator<<(ostream&output,constCylinder&cy){output<<″[″<<cy.x<<″,″<<cy.y<<″],r=″<<cy.radius<<″,h=″<<cy.height;returnoutput;}virtualvoidshapeName()co//main函數(shù)intmain(){Pointpoint(3.2,4.5);//建立Point類對(duì)象pointCirclecircle(2.4,1.2,5.6);//建立Circle類對(duì)象circleCylindercylinder(3.5,6.4,5.2,10.5);//建立Cylinder類對(duì)象cylinderpoint.shapeName();//靜態(tài)關(guān)聯(lián)

cout<<point<<endl;circle.shapeName();//靜態(tài)關(guān)聯(lián)

cout<<circle<<endl;cylinder.shapeName();//靜態(tài)關(guān)聯(lián)

cout<<cylinder<<endl<<endl;Shape*pt;//定義基類指針

pt=&point;//指針指向Point類對(duì)象

pt->shapeName();//動(dòng)態(tài)關(guān)聯(lián)

cout<<″x=″<<point.getX()<<″,y=″<<point.getY()<<″\\narea=″<<pt->area()<<″\\nvolume=″<<pt->volume()<<″\\n\\n″;pt=&circle;//指針指向Circle類對(duì)象//main函數(shù)

pt->shapeName();//動(dòng)態(tài)關(guān)聯(lián)

cout<<″x=″<<circle.getX()<<″,y=″<<circle.getY()<<″\\narea=″<<pt->area()<<″\\nvolume=″<<pt->volume()<<″\\n\\n″;pt=&cylinder;//指針指向Cylinder類對(duì)象

pt->shapeName();//動(dòng)態(tài)關(guān)聯(lián)

cout<<″x=″<<cylinder.getX()<<″,y=″<<cylinder.getY()<<″\\narea=″<<pt->area()<<″\\nvolume=″<<pt->volume()<<″\\n\\n″;return0;}以上程序也可:先定義函數(shù)voidfn(Shape&x){x.shapeName();}再voidmain(){fn(point);fn(circle);fn(cylinder);…..}程序運(yùn)行結(jié)果如下:pt->shapeName();Point:[3.2,4.5](Point類對(duì)象point的數(shù)據(jù):點(diǎn)的坐標(biāo))Circle:[2.4,1.2],r=5.6(Circle類對(duì)象circle的數(shù)據(jù):圓心和半徑)Cylinder:[3.5,6.4],r=5.5,h=10.5(Cylinder類對(duì)象cylinder的數(shù)據(jù):圓心、半徑和高)Point:x=3.2,y=4.5(輸出Point類對(duì)象point的數(shù)據(jù):點(diǎn)的坐標(biāo))area=0(點(diǎn)的面積)volume=0(點(diǎn)的體積)Circle:x=2.4,y=1.2(輸出Circle類對(duì)象circle的數(shù)據(jù):圓心坐標(biāo))area=98.5203(圓的面積)volume=0(圓的體積)Cylinder:x=3.5,y=6.4(輸出Cylinder類對(duì)象cylinder的數(shù)據(jù):圓心坐標(biāo))area=512.595(圓的面積)volume=891.96(圓柱的體積)

Point:[3.2,4.5](Point類對(duì)象p從本例可以進(jìn)一步明確以下結(jié)論:(1)一個(gè)基類如果包含一個(gè)或一個(gè)以上純虛函數(shù),就是抽象基類。抽象基類不能也不必要定義對(duì)象。(2)抽象基類與普通基類不同,它不是現(xiàn)實(shí)存在的對(duì)象的抽象(例如圓形(Circle)就是千千萬(wàn)萬(wàn)個(gè)實(shí)際的圓的抽象),它可以沒(méi)有任何物理上的或其他實(shí)際意義方面的含義。(3)在類的層次結(jié)構(gòu)中,頂層或最上面的幾層可以是抽象基類。抽象基類體現(xiàn)了本類族中各類的共性,把各類中共有的成員函數(shù)集中在抽象基類中聲明。(4)抽象基類是本類族的公共接口。或者說(shuō),從同一基類派生出的多個(gè)類有同一接口。(5)區(qū)別靜態(tài)關(guān)聯(lián)和動(dòng)態(tài)關(guān)聯(lián)。從本例可以進(jìn)一步明確以下結(jié)論:(6)如果在基類聲明了虛函數(shù),則在派生類中凡是與該函數(shù)有相同的函數(shù)名、函數(shù)類型、參數(shù)個(gè)數(shù)和類型的函數(shù),均為虛函數(shù)(不論在派生類中是否用virtual聲明)。(7)使用虛函數(shù)提高了程序的可擴(kuò)充性。把類的聲明與類的使用分離。這對(duì)于設(shè)計(jì)類庫(kù)的軟件開發(fā)商來(lái)說(shuō)尤為重要。開發(fā)商設(shè)計(jì)了各種各樣的類,但不向用戶提供源代碼,用戶可以不知道類是怎樣聲明的,但是可以使用這些類來(lái)派生出自己的類。利用虛函數(shù)和多態(tài)性,程序員的注意力集中在處理普遍性,而讓執(zhí)行環(huán)境處理特殊性。(6)如果在基類聲明了虛函數(shù),則在派生類中凡是與該函數(shù)有相虛基類與抽象基類的區(qū)別:前者解決模糊二義性問(wèn)題;后者解決繼承中的多態(tài)性問(wèn)題。實(shí)現(xiàn)時(shí),前者由在繼承方式前加virtual;后者靠純虛函數(shù)實(shí)現(xiàn)。虛函數(shù)與純虛函數(shù)對(duì)比:純?cè)诔橄箢愔?,被“初始化”?的函數(shù)。沒(méi)有實(shí)際意義,為派生類保留名字,方便該函數(shù)實(shí)現(xiàn)多態(tài)性。虛函數(shù)有實(shí)際意義,也是為實(shí)現(xiàn)多態(tài)性服務(wù)?!禖++程序設(shè)計(jì)》-第12章n課件第12章多態(tài)性與虛函數(shù)12.1多態(tài)性的概念12.2一個(gè)典型的例子12.3虛函數(shù)12.4純虛函數(shù)與抽象類第12章多態(tài)性與虛函數(shù)12.1多態(tài)性的概念多態(tài)性(polymorphism)是面向?qū)ο蟪绦蛟O(shè)計(jì)的一個(gè)重要特征。利用多態(tài)性可以設(shè)計(jì)和實(shí)現(xiàn)一個(gè)易于擴(kuò)展的系統(tǒng)。在C++程序設(shè)計(jì)中,多態(tài)性是指具有不同功能的函數(shù)可以用同一個(gè)函數(shù)名,這樣就可以用一個(gè)函數(shù)名調(diào)用不同內(nèi)容的函數(shù)。在面向?qū)ο蠓椒ㄖ械亩鄳B(tài)性:向不同的對(duì)象發(fā)送同一個(gè)消息(函數(shù)名),不同的對(duì)象在接收時(shí)會(huì)產(chǎn)生不同的行為(即方法)。也就是說(shuō),每個(gè)對(duì)象可以用自己的方式去響應(yīng)共同的消息。12.1多態(tài)性的概念多態(tài)性(polymorphism)是面向?qū)ο蟪绦蛟O(shè)計(jì)的一個(gè)重優(yōu)點(diǎn):在C++程序設(shè)計(jì)中,在不同的類中定義了其響應(yīng)消息的方法,那么使用這些類時(shí),不必考慮它們是什么類型,只要發(fā)布消息即可。多態(tài)性分為兩類:靜態(tài)多態(tài)性和動(dòng)態(tài)多態(tài)性。靜態(tài)多態(tài)性:函數(shù)重載和運(yùn)算符重載屬于靜態(tài)多態(tài)性,在程序編譯時(shí)系統(tǒng)就能決定調(diào)用的是哪個(gè)函數(shù),因此靜態(tài)多態(tài)性又稱編譯時(shí)的多態(tài)性。動(dòng)態(tài)多態(tài)性:是在程序運(yùn)行過(guò)程中才動(dòng)態(tài)地確定操作所針對(duì)的對(duì)象。它又稱運(yùn)行時(shí)的多態(tài)性。動(dòng)態(tài)多態(tài)性是通過(guò)虛函數(shù)(virtualfunction)實(shí)現(xiàn)的。在本章中主要介紹動(dòng)態(tài)多態(tài)性和虛函數(shù)。優(yōu)點(diǎn):在C++程序設(shè)計(jì)中,在不同的類中定義了其響應(yīng)消息的方法下面是一個(gè)承上啟下的例子。一方面它是有關(guān)繼承和運(yùn)算符重載內(nèi)容的綜合應(yīng)用的例子,通過(guò)這個(gè)例子可以進(jìn)一步融會(huì)貫通前面所學(xué)的內(nèi)容,另一方面又是作為討論多態(tài)性的一個(gè)基礎(chǔ)用例。12.2一個(gè)典型的例子下面是一個(gè)承上啟下的例子。一方面它是有關(guān)繼承和運(yùn)算符重載內(nèi)容例12.1先建立一個(gè)Point(點(diǎn))類,包含數(shù)據(jù)成員x,y(坐標(biāo)點(diǎn))。以它為基類,派生出一個(gè)Circle(圓)類,增加數(shù)據(jù)成員r(半徑),再以Circle類為直接基類,派生出一個(gè)Cylinder(圓柱體)類,再增加數(shù)據(jù)成員h(高)。要求編寫程序,重載運(yùn)算符“<<”和“>>”,使之能用于輸出以上類對(duì)象。對(duì)于一個(gè)比較大的程序,應(yīng)當(dāng)分成若干步驟進(jìn)行。先聲明基類,再聲明派生類,逐級(jí)進(jìn)行,分步調(diào)試。(1)聲明基類Point類寫出聲明基類Point的部分:#include<iostream>//聲明類Point//聲明一個(gè)類包括什么?classPoint{public:Point(floatx=0,floaty=0);//有默認(rèn)參數(shù)的構(gòu)造函數(shù)例12.1先建立一個(gè)Point(點(diǎn))類,包含數(shù)據(jù)成員x,y

voidsetPoint(float,float);//設(shè)置坐標(biāo)值

floatgetX()const{returnx;}//讀x坐標(biāo)

floatgetY()const{returny;}//讀y坐標(biāo)

friendostream&operator<<(ostream&,constPoint&);//重載運(yùn)算符“<<”protected://受保護(hù)成員

floatx,y;};//下面定義Point類的成員函數(shù)//Point的構(gòu)造函數(shù)Point::Point(floata,floatb)//對(duì)x,y初始化{x=a;y=b;}//設(shè)置x和y的坐標(biāo)值voidPoint::setPoint(floata,floatb)//為x,y賦新值{x=a;y=b;}//重載運(yùn)算符“<<”,使之能輸出點(diǎn)的坐標(biāo)ostream&operator<<(ostream&output,constPoint&p){output<<″[″<<p.x<<″,″<<p.y<<″]″<<endl;returnoutput;}以上完成了基類Point類的聲明。voidsetPoint(float,float);現(xiàn)在要對(duì)上面寫的基類聲明進(jìn)行調(diào)試,檢查它是否有錯(cuò),為此要寫出main函數(shù)。實(shí)際上它是一個(gè)測(cè)試程序。intmain(){Pointp(3.5,6.4);//建立Point類對(duì)象pcout<<″x=″<<p.getX()<<″,y=″<<p.getY()<<endl;//輸出p的坐標(biāo)值

p.setPoint(8.5,6.8);//重新設(shè)置p的坐標(biāo)值

cout<<″p(new):″<<p<<endl;//用重載運(yùn)算符“<<”輸出p點(diǎn)坐標(biāo)}程序編譯通過(guò),運(yùn)行結(jié)果為x=3.5,y=6.4p(new):[8.5,6.8]測(cè)試程序檢查了基類中各函數(shù)的功能,以及運(yùn)算符重載的作用,證明程序是正確的?,F(xiàn)在要對(duì)上面寫的基類聲明進(jìn)行調(diào)試,檢查它是否有錯(cuò),為此要寫出(2)聲明派生類CircleclassCircle:publicPoint//circle是Point類的公用派生類{public:Circle(floatx=0,floaty=0,floatr=0);//構(gòu)造函數(shù)

voidsetRadius(float);//設(shè)置半徑值

floatgetRadius()const;//讀取半徑值

floatarea()const;//計(jì)算圓面積

friendostream&operator<<(ostream&,constCircle&);//重載運(yùn)算符“<<”

private://?protected:floatradius;};//定義構(gòu)造函數(shù),對(duì)圓心坐標(biāo)和半徑初始化Circle::Circle(floata,floatb,floatr):Point(a,b),radius(r){}voidCircle::setRadius(floatr)//設(shè)置半徑值{radius=r;}floatCircle::getRadius()const{returnradius;}//讀取半徑值floatCircle::area()const(2)聲明派生類Circle{return3.14159*radius*radius;}//重載運(yùn)算符“<<”,使之按規(guī)定的形式輸出圓的信息ostream&operator<<(ostream&output,constCircle&c){output<<″Center=[″<<c.x<<″,″<<c.y<<″],r=″<<c.radius<<″,area=″<<c.area()<<endl;returnoutput;}為了測(cè)試Circle類的定義,可以寫出下面的主函數(shù):{return3.14159*radius*radius;intmain(){Circlec(3.5,6.4,5.2);//建立Circle類對(duì)象c,并給定圓心坐標(biāo)和半徑

cout<<″originalcircle:\\nx=″<<c.getX()<<″,y=″<<c.getY()<<″,r=″<<c.getRadius()<<″,area=″<<c.area()<<endl;//輸出圓心坐標(biāo)、半徑和面積

c.setRadius(7.5);//設(shè)置半徑值

c.setPoint(5,5);//設(shè)置圓心坐標(biāo)值x,ycout<<″newcircle:\\n″<<c;//用重載運(yùn)算符“<<”輸出圓對(duì)象的信息

Point&pRef=c;//pRef是Point類的引用變量,被c初始化

cout<<″pRef:″<<pRef;//輸出pRef的信息

return0;}程序編譯通過(guò),運(yùn)行結(jié)果為originalcircle:(輸出原來(lái)的圓的數(shù)據(jù))x=3.5,y=6.4,r=5.2,area=84.9486newcircle:(輸出修改后的圓的數(shù)據(jù))Center=[5,5],r=7.5,area=176.714pRef:[5,5](輸出圓的圓心“點(diǎn)”的數(shù)據(jù))intmain()(3)聲明Circle的派生類CylinderclassCylinder:publicCircle//Cylinder是Circle的公用派生類{public:Cylinder(floatx=0,floaty=0,floatr=0,floath=0);//構(gòu)造函數(shù)

voidsetHeight(float);//設(shè)置圓柱高

floatgetHeight()const;//讀取圓柱高

floatarea()const;//計(jì)算圓表面積

floatvolume()const;//計(jì)算圓柱體積

friendostream&operator<<(ostream&,constCylinder&);//重載運(yùn)算符“<<”

protected:floatheight;//圓柱高};Cylinder::Cylinder(floata,floatb,floatr,floath)//定義構(gòu)造函數(shù):Circle(a,b,r),height(h){}voidCylinder::setHeight(floath){height=h;}//設(shè)置圓柱高floatCylinder::getHeight()const{returnheight;}//讀取圓柱高(3)聲明Circle的派生類Cylinder//計(jì)算圓表面積floatCylinder::area()const{return2*Circle::area()+2*3.14159*radius*height;}floatCylinder::volume()const//計(jì)算圓柱體積{returnCircle::area()*height;}//重載運(yùn)算符“<<”ostream&operator<<(ostream&output,constCylinder&cy){output<<″Center=[″<<cy.x<<″,″<<cy.y<<″],r=″<<cy.radius<<″,h=″<<cy.height<<″\\narea=″<<cy.area()<<″,volume=″<<cy.volume()<<endl;returnoutput;}//計(jì)算圓表面積

可以寫出下面的主函數(shù):intmain(){Cylindercy1(3.5,6.4,5.2,10);//定義Cylinder類對(duì)象cy1cout<<″\\noriginalcylinder:\\nx=″<<cy1.getX()<<″,y=″<<cy1.getY()<<″,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,ycout<<″\\nnewcylinder:\\n″<<cy1;//用重載運(yùn)算符“<<”輸出cy1的數(shù)據(jù)

Point&pRef=cy1;//pRef是Point類對(duì)象的引用變量

cout<<″\\npRefasaPoint:″<<pRef;//pRef作為一個(gè)“點(diǎn)”輸出

Circle&cRef=cy1;//cRef是Circle類對(duì)象的引用變量

cout<<″\\ncRefasaCircle:″<<cRef;//cRef作為一個(gè)“圓”輸出

return0;}運(yùn)行結(jié)果如下:originalcylinder:(輸出cy1的初始值)x=3.5,y=6.4,r=5.2,h=10(圓心坐標(biāo)x,y。半徑r,高h(yuǎn))area=496.623,volume=849.486(圓柱表面積area和體積volume)newcylinder:(輸出cy1的新值)Center=[5,5],r=7.5,h=15(以[5,5]形式輸出圓心坐標(biāo))area=1060.29,volume=2650.72(圓柱表面積area和體積volume)pRefasaPoint:[5,5](pRef作為一個(gè)“點(diǎn)”輸出)cRefasaCircle:Center=[5,5],r=7.5,area=176.714(cRef作為一個(gè)“圓”輸出)在本例中存在靜態(tài)多態(tài)性,這是運(yùn)算符重載引起的??梢钥吹?/p>

溫馨提示

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

最新文檔

評(píng)論

0/150

提交評(píng)論