《CC++語言程序設計》課件第12章 多態性_第1頁
《CC++語言程序設計》課件第12章 多態性_第2頁
《CC++語言程序設計》課件第12章 多態性_第3頁
《CC++語言程序設計》課件第12章 多態性_第4頁
《CC++語言程序設計》課件第12章 多態性_第5頁
已閱讀5頁,還剩83頁未讀 繼續免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

第十二章多態性12.1多態性概述12.2函數重載12.3運算符重載12.4虛函數12.5抽象類12.6綜合實例——俄羅斯方塊游戲12.7小結12.1多態性概述靜態聯編動態聯編靜態多態性(編譯時多態性)動態多態性(運行時多態性)12.2函數重載編譯時的多態性可以通過函數重載實現.函數重載有兩種情況:參數有所差別的重載函數所帶參數完全相同,只是它們屬于不同的類【例12.1】在基類和派生類中重載函數#include<iostream.h>classbase{ intx,y;public: base(inta,intb) {x=a;y=b;} voidshow()//基類中的show()函數 { cout<<"執行基類中的show()函數"<<endl; cout<<x<<","<<y<<endl; }};classderived:publicbase{ intz;public: derived(inta,intb,intc):base(a,b) { z=c;} voidshow()//派生類中的show()函數 { cout<<"執行派生類中的show()函數"<<endl; cout<<z<<endl; }};voidmain(){ baseb(20,20); derivedd(8,8,30); b.show();//執行基類中的show()函數 d.show();//執行派生類中的show()函數 d.base::show();//執行基類中的show()函數 }

在基類和派生類中進行函數重載時,在編譯時可以用下面兩種方法區別重載函數:使用對象名加以區分。例如:b.show()和d.show()分別調用類base和derived的show()函數。使用“類名::”加以區分。例如:d.base::show()調用的是base的show()函數。12.3運算符重載自定義的類的運算往往用運算符重載函數來實現運算符重載的目的:擴充語言的功能,即將運算符擴充到用戶定義的類型上去。運算符重載通過創建運算符函數operator()來實現。可以重載成為類的成員,也可是類的友元。12.3.1運算符重載的規則為了表達方便,人們希望能對自定義的類型進行運算,希望內部運算符(如“+”、“-”、“*”、“/”等)在特定的類對象上以新的含義進行解釋,即實現運算符的重載。C++為運算符重載提供了一種方法,使用以下形式進行運算符重載:

其中,@表示要重載的運算符,type是返回類型。typeoperator@(參數表);除了“.”、“.*”、“::”、“?:”、“#”、“##”其它都可以重載。delete、new、指針、引用也可以重載。運算符函數可以定義為內聯函數。用戶定義的運算符不改變運算符的優先次序。不可以定義系統定義的運算符集之外的運算符。不能改變運算符的的語法結構。【例12.2】使用運算符重載函數將類對象相加。

#include<iostream.h> classcomplex{ public: doublereal; doubleimag; complex(doubler=0,doublei=0) {real=r;imag=i;} }; complexoperator+(complexco1,complexco2) { complextemp; temp.real=co1.real+co2.real; temp.imag=co1.imag+co2.imag; returntemp; } main() { complexcom1(1.1,2.2),com2(3.3,4.4),total1,total2; total1=operator+(com1,com2);//調用運算符函數operater+()的第一種方式 cout<<"real1="<<total1.real<<"“

<<"imag1="<<total1.imag<<endl; total2=com1+com2;//調用運算符函數operater+()的第二種方式

cout<<"real2="<<total2.real<<"“

<<"imag2="<<total2.imag<<endl; return0; }12.3.2運算符重載為成員函數成員運算符定義的語法形式

classX{//…typeoperator@(參數表);//…};typeX::operator@(參數表){//函數體}1.雙目運算符重載為成員函數成員運算符函數的參數表中只有一個參數,它作為運算符的右操作數,此時當前對象作為運算符的左操作數,它是通過this指針隱含的傳遞給函數的。aa@bb;//隱式調用aa.operator@(bb);//顯式調用例12.3

雙目運算符重載為成員函數應用。2.單目運算符重載為成員函數成員運算符函數的參數表中沒有參數,此時當前對象作為運算符的一個操作數

@aa//隱式調用aa.operator@();//顯式調用例12.4

單目運算符重載為成員函數應用。12.3.3友元運算符函數友元運算符函數定義的語法形式類內:friendtypeoperator@(參數表);

類外:

typeoperator@(參數表){//函數體}1.雙目運算符重載為友元函數當用友元函數重載雙目運算符時,兩個操作數都要傳遞給運算符函數,調用時可采用以下兩種方式:

aa@bb;//隱式調用,aa和bb分別為左、右操作數

operator@(aa,bb);//顯式調用雙目友元運算符函數operator@所需要的兩個操作數都在參數表中由對象aa和bb顯式調用。例12.5雙目運算符重載為友元函數。2.單目運算符重載為友元函數當用友元函數重載單目運算符時,需要一個顯式的操作數,調用時可采用以下兩種方式:@aa;//隱式調用,aa為操作數operator@(aa);//顯式調用例12.6單目運算符重載為友元函數應用。注意:使用友元運算符重載“++”、“--”這樣的運算符,可能會出現一些問題。不能用友元函數重載的運算符是:=、()、[]、->,其余的運算符都可以使用友元函數來實現成員運算符函數與友元運算符函數的比較對雙目運算符而言,成員運算符函數帶有一個參數,友元運算符函數帶由兩個參數;對單目運算符而言,成員運算符函數不帶參數,而友元運算符函數帶一個參數。雙目運算符一般可被重載為友元運算符函數或成員運算符函數,但有一種情況,必須使用友元函數。在左操作數為系統預定義數據類型,或必須為一個不同的類對象時。一般而言,對于雙目運算符,將它重載為一個友元函數比重載為一個成員函數便于使用。若一個運算符的操作需要修改類對象的狀態,則選擇成員運算符函數較好。如果運算符所需的操作數(尤其是第一個操作數)希望有隱式轉換,則運算符重載必須使用友元函數。12.3.4“++”和“--”的重載前綴方式和后綴方式聲明不同運算符“++”和“--”作前綴和后綴是有區別的。但是,C++V2.1之前的版本在重載“++”或“--”時,不能顯示區分是前綴方式還是后綴方式。在C++V2.1及以后的版本中,編輯器可以通過在運算符函數表中是否插入關鍵字int來區分這兩種方式。例12.7運算符“++”和“--”作前綴和后綴重載。12.4虛函數虛函數是重載的另一種表現形式。虛函數允許函數調用與函數體之間的聯系在運行時才建立,也就是在運行時才決定如何動作,即所謂的動態聯編。12.4.1引入派生類后的對象指針聲明為指向基類對象的指針可以指向它的公有派生的對象,但不允許指向它的私有派生對象。允許將一個聲明為指向基類的指針指向其公有派生類的對象,但是不能將一個聲明為指向派生類對象的指針指向其基類的對象。聲明為指向基類對象的指針,當其指向其公有派生類對象時,只能用它來直接訪問派生類中從基類繼承來的成員,而不能直接訪問公有派生類中定義的成員。12.4.2虛函數的定義及使用虛函數的作用

“同一接口,多種方法”虛函數的定義在基類中被關鍵字virtual說明,并在派生類中重新定義的函數。在派生類中重新定義時,其函數原型,包括返回類型、函數名、參數個數與參數類型的順序,都必須與基類中的原型完全相同。例12.9使用虛函數實現動態多態性。說明:在基類中,用關鍵字virtual可以將其public或protected部分的成員函數聲明為虛函數。在派生類對基類中聲明的虛函數進行重新定義時,關鍵字virtual可以寫也可以不寫。虛函數被重新定義時,其函數的原型與基類中的函數原型必須完全相同。一個虛函數無論被公有繼承多少次,它仍然保持虛函數的特性。虛函數必須是其所在類的成員函數,而不能是友元函數,也不能是靜態成員函數,因為虛函數調用要靠特定的對象來決定該激活哪個函數。但是虛函數可以在另一個類中被聲明為友元函數。12.4.3虛析構函數構造函數不能是虛函數,但析構函數可以是虛函數。例12.10析構函數定義不當造成內存泄漏。#include"iostream.h"classA{public: int*a; A(){a=new(int);} virtualvoidfunc1(){} ~A(){deletea;cout<<"deletea"<<endl;}};classB:publicA{public: int*b; B(){b=new(int);} virtualvoidfunc1(){} ~B(){deleteb;cout<<"deleteb"<<endl;}};voidmain(){ A*pb=newB(); deletepb;}

在main函數中,動態創建了一個B類對象。當B對象創建時,調用的是B類的構造函數。但是,當對象析構時,卻調用的是A類的析構函數,B類的析構函數沒有被調用,因而發生了內存泄漏,這是不希望看到的。造成這種問題的原因是:當A類指針指向的內存單元(即B類對象的數據)被釋放時,編譯器看到指針類型是A類的,所以調用A類的析構函數。其實,這個時候需要調用指針所指向的對象類型的析構函數即B類的析構函數。虛函數能夠滿足這個要求。所以,可以使用虛析構函數來解決上面遇到的問題:【例12.11】虛析構函數的使用。

#include"iostream.h" classA{ public: int*a; A(){a=new(int);} virtualvoidfunc1(){} virtual~A(){deletea;cout<<"deletea"<<endl;} };

classB:publicA{ public: int*b; B(){b=new(int);} virtualvoidfunc1(){} virtual~B(){deleteb;cout<<"deleteb"<<endl;} }; voidmain() { A*pb=newB(); deletepb; }應用舉例應用C++的多態性,計算三角形、矩形和圓的面積。12.5抽象類純虛函數是一個在基類中說明的虛函數,它在基類中沒有定義,但要求在它的派生類中定義自己的版本,或重新說明為純虛函數。純虛函數的一般形式如下:virtualtype函數名(參數表)=0;【例12.12】用虛函數求三角形和矩形面積。#include<iostream.h>classShape{protected:intwidth,height;public:voidset_values(inta,intb){width=a;height=b;}virtualintarea(){cout<<"Noarea"<<endl;}};classCRectangle:publicShape{public:intarea(void){return(width*height);}};classCTriangle:publicShape{public:intarea(void){return(width*height/2);}};voidmain(){CRectanglerect;CTriangletrgl;Shape*ppoly1=▭Shape*ppoly2=&trgl;ppoly1->set_values(4,5);ppoly2->set_values(4,5);cout<<ppoly1->area()<<endl;cout<<ppoly2->area()<<endl;}抽象類如果一個類中至少包含一個純虛函數,那么就稱該類為抽象類。對于抽象類有以下幾點規定:由于抽象類中至少包含有一個沒有定義功能的純虛函數,因此抽象類只能用作其它類的基類,不能建立抽象類對象。抽象類不能用作參數類型、函數返回類型或顯示轉換的類型。但可以聲明指向抽象類的指針或引用,此指針可以指向它的派生類,進而實現多態性。如果在抽象類的派生類中沒有重新說明純虛函數,而派生類只是繼承基類的純虛函數,則這個派生類仍然還是一個抽象類。classshape{ //…public: //…virtualvoidshow()=0;}; shapes1; shape*ptr; shapef(); shapeg(shapes); shape&h(shape&);思考12.6綜合實例——俄羅斯方塊游戲綜合前面所學的知識,熟悉面向對象程序設計的全過程,即面向對象的分析、面向對象的設計、面向對象的實現三個過程,創建一個實用的游戲程序。游戲說明及規則俄羅斯方塊是由俄羅斯人阿列克謝·帕基特諾夫發明的。俄羅斯方塊原名是俄語Тетрис(英語是Tetris),這個名字來源于希臘語tetra,意思是“四”,而游戲的作者最喜歡網球(tennis)。于是,他把兩個詞tetra和tennis合而為一,命名為Tetris,這也就是俄羅斯方塊名字的由來。游戲規則Tetris游戲在一個m*n的矩形框內進行。游戲開始時,矩形框的頂部會隨機出現一個由四個小方塊構成的磚塊,每過一個很短的時間(我們稱這個時間為一個tick),它就會下落一格,直到它碰到矩形框的底部,然后再過一個tick它就會固定在矩形框的底部,成為固定塊。接著再過一個tick頂部又會出現下一個隨機形狀,同樣每隔一個tick都會下落,直到接觸到底部或者接觸到下面的固定塊時,再過一個tick它也會成為固定塊,再過一個tick之后會進行檢查,發現有充滿方塊的行則會消除它,同時頂部出現下一個隨機形狀。直到頂部出現的隨機形狀在剛出現時就與固定塊重疊,表示游戲結束。操作說明:a—左移

d—右移

w—翻轉

s—下移Tetris游戲的分析、設計與實現1.Tetris游戲的矩形框首先描述Tetris游戲的矩形框。矩形框類——CBin名稱:矩形框屬性:高度

寬度映像方法(操作):

構造函數析構函數

獲取矩形高度值獲取矩形寬度值獲取映像數據設置映像數據刪除整行classCBin{private:unsignedintwidth;unsignedintheight;unsignedchar**image;public:CBin(unsignedintw,unsignedinth);~CBin();unsignedintgetWidth(){returnwidth;}unsignedintgetHeight(){returnheight;}voidgetImage(unsignedchar**destImage);voidsetImage(unsignedchar**srcImage);unsignedintremoveFullLines();};矩形框類方法的實現構造函數算法描述:用來初始化數據成員width和height為image分配空間并初始化CBin::CBin(unsignedintw,unsignedinth){ width=w; height=h; image=newunsignedchar*[height];for(unsignedinti=0;i<height;i++) {image[i]=newunsignedchar[width]; for(unsignedintj=0;j<width;j++) image[i][j]=0;}}析構函數算法描述:刪除在構造函數中為image分配的空間CBin::~CBin(){ for(unsignedinti=0;i<height;i++){deleteimage[i];}delete[]image;}函數功能:獲取映像數據算法描述:將游戲面板的源映像數據數據拷貝到目的映像中源映像:CBin類數據成員image:二維數組目的映像:同image大小相同的二維數組voidCBin::getImage(unsignedchar**destImage){for(unsignedinti=0;i<height;i++) for(unsignedintj=0;j<width;j++) destImage[i][j]=image[i][j];}函數功能:設置映像數據算法描述:將源映像數據數據拷貝到目的映像中源映像:同image大小相同的二維數組目的映像:CBin類數據成員image:二維數組voidCBin::setImage(unsignedchar**srcImage){ for(unsignedinti=0;i<height;i++) for(unsignedintj=0;j<width;j++) image[i][j]=srcImage[i][j];}自頂向下,逐步求精函數功能:刪除整行算法描述:第一步:根據函數功能,程序可以寫為:unsignedintCBin::removeFullLines(){檢查image的每一行是否被填滿,如果任何一行完全填滿,則刪除這一行,并讓上面行的數據下移一行,記錄刪除的總行數。返回刪除的總行數}第二步,對程序進一步細化:unsignedintCBin::removeFullLines(){ unsignedintflag,EmptyLine=0;unsignedinti,j,m; for(i=0;i<height;i++)//循環遍歷image的每一行 {

檢查一行是否被填滿,如果任何一行完全填滿,則刪除這一行,并讓上面行的數據下移一行,記錄刪除的總行數 }returnEmptyLine;//返回刪除的總行數}第三步對“檢查一行是否被填滿,如果任何一行完全填滿,則刪除這一行,并讓上面行的數據下移一行,記錄刪除的總行數”進行細化。

//檢查一行是否被填滿

flag=0;

for(j=0;j<width;j++)

{

if(image[i][j]==0) flag=1; }//如果一行完全被填滿if(flag==0){

//刪除整行,依次下移

for(m=i;m>0;m--)for(j=0;j<width;j++) image[m][j]=image[m-1][j]; //最上面一行補0

for(j=0;j<width;j++)

image[0][j]=0;

EmptyLine++;//記錄刪除的行數 }unsignedintCBin::removeFullLines(){ unsignedintflag,EmptyLine=0;unsignedinti,j,m; for(i=0;i<height;i++) { flag=0;

//檢查一行是否被填滿 for(j=0;j<width;j++) { if(image[i][j]==0) flag=1; }//一行完全被填滿if(flag==0){

//刪除整行,依次下移

for(m=i;m>0;m--)for(j=0;j<width;j++) image[m][j]=image[m-1][j]; for(j=0;j<width;j++) image[0][j]=0; EmptyLine++;//記錄刪除的行數 }}returnEmptyLine;}現在來完成一些Tetris磚塊類。Tetris的常用磚塊見下圖。經分析所有的磚塊都有共同的特征,因此可以定義一個所有磚塊共同的基類CBrick(可定義為抽象類)以“I”磚塊為例分析:CIBrick類有四個數據成員:orientation,posX,posY,andcolour。orientation表示了“I”磚塊的四個狀態,可能的取值為{0,1,2,3},磚塊的狀態如圖所示,由狀態0到狀態1是“I”磚塊固定一個特定點順時針旋轉90度。依此類推,狀態3的下一個狀態是狀態0。posX,posY記錄了特定點(圖中黑點)的坐標。Colour為磚塊的顏色值。磚塊類——CBrick名稱:磚塊類屬性:狀態

位置X坐標顏色位置Y坐標方法(操作):

設置狀態;獲取狀態

設置位置X坐標;獲取位置X坐標

設置位置Y坐標;獲取位置Y坐標

設置顏色;獲取顏色左移;右移;下移;旋轉;沖突檢查;設置游戲面板;置頂(可設置成純虛函數)磚塊類——CBrickclassCBrick{protected:intorientation;intposX;intposY;unsignedcharcolour;public:intgetOrientation(){returnorientation;};intgetPosX(){returnposX;};intgetPosY(){returnposY;};unsignedchargetColour(){returncolour;};

磚塊類——CBrickvoidsetOrientation(intnewOrient){orientation=newOrient;};voidsetPosX(intnewPosX){posX=newPosX;};voidsetPosY(intnewPosY){posY=newPosY;};voidsetColour(unsignedcharnewColour){colour=newColour;};磚塊類——CBrickintshiftLeft(CBin*bin);intshiftRight(CBin*bin);intshiftDown(CBin*bin);introtateClockwise(CBin*bin);virtualintcheckCollision(CBin*bin)=0;virtualvoidoperator>>(unsignedchar**binImage)=0;virtualvoidputAtTop(intnewOrient,intnewPosX)=0;};函數功能:左移算法描述:獲取特定點X坐標重設特定點X坐標為當前值減1檢查沖突如果沖突,則不能左移,恢復特定點X坐標,返回0值否則,返回1值intCBrick::shiftLeft(CBin*bin){ intposX; posX=getPosX();setPosX(posX-1);if(checkCollision(bin)==0){setPosX(posX);return0;}return1;}函數功能:右移算法描述:獲取特定點X坐標重設特定點X坐標為當前值加1檢查沖突如果沖突,則不能右移,恢復特定點X坐標,返回0值否則,返回1值intCBrick::shiftRight(CBin*bin){ intposX; posX=getPosX();setPosX(posX+1);if(checkCollision(bin)==0){ setPosX(posX);return0;}return1;}函數功能:下移算法描述:獲取特定點Y坐標重設特定點Y坐標為當前值加1檢查沖突如果沖突,則不能下移,恢復特定點Y坐標,返回0值否則,返回1值intCBrick::shiftDown(CBin*bin){ intposY; posY=getPosY();setPosY(posY+1);if(checkCollision(bin)==0){ setPosY(posY);return0;}return1;}函數功能:旋轉算法描述:獲取磚塊狀態如果狀態為3,則旋轉后狀態為0;否則,旋轉后狀態為當前值加1檢查沖突如果沖突,則不能旋轉,恢復原狀態,返回0值否則,返回1值intCBrick::rotateClockwise(CBin*bin){ intorientation,oldOrientation;orientation=getOrientation(); oldOrientation=orientation; if(orientation==3) orientation=0; else orientation=orientation+1;setOrientation(orientation); if(checkCollision(bin)==0){setOrientation(oldOrientation);return0;}return1;}“I”型磚塊類——CIBrickCIBrick從CBrick繼承而來需重載3個純虛函數classCIBrick:publicCBrick{public: intcheckCollision(CBin*bin); voidoperator>>(unsignedchar**binImage); voidputAtTop(intnewOrient,intnewPosX);};函數功能:檢查沖突算法描述:獲取游戲面板映像獲取當前磚塊的狀態和特定點針對磚塊不同的狀態進行檢查如果沖突,返回0值;否則,返回1值CIBrick::checkCollision函數的實現獲取游戲面板映像intCIBrick::checkCollision(CBin*bin){intwidth;intheight;unsignedchar**image; intorientation; intposX; intposY; width=bin->getWidth(); height=bin->getHeight(); image=newunsignedchar*[height];for(inti=0;i<height;i++) {image[i]=newunsignedchar[width];} bin->getImage(image);CIBrick::checkCollision函數的實現獲取當前磚塊的狀態和特定點

orientation=getOrientation();posX=getPosX(); posY=getPosY();CIBrick::checkCollision函數的實現針對磚塊不同的狀態進行檢查(以0狀態為例

if(orientation==0) {//磚塊碰到邊界

if( (posX<0)||(posX>width-1)|| (posY<1)||(posY+2>height-1)) return0;

//磚塊碰到其他固定磚塊

if((image[posY-1][posX]!=0)||(image[posY][posX]!=0)||(image[posY+1][posX]!=0)||(image[posY+2][posX]!=0))return0; }CIBrick::checkCollision函數的實現//I磚塊狀態1

if(orientation==1)

{

if((posX<2)||(posX+1>width-1)|| (posY<0)|| (posY>height-1)) return0; if((image[posY][posX-2]!=0)||(image[posY][posX-1]!=0)||(image[posY][posX]!=0)|| (image[posY][posX+1]!=0))return0; }CIBrick::checkCollision函數的實現

//“I”磚塊狀態2

if(orientation==2)

{

if((posX<0)||(posX>width-1)|| (posY<2)||(posY+1>height-1)) return0; if((image[posY-2][posX]!=0)|| (image[posY-1][posX]!=0)||(image[posY][posX]!=0)|| (image[posY+1][posX]!=0))return0; }CIBrick::checkCollision函數的實現//狀態3

if(orientation==3)

{

if((posX<1)||(posX+2>width-1)|| (posY<0)||(posY>height-1)) return0; if((image[posY][posX-1]!=0)|| (image[posY][posX]!=0)|| (image[posY][posX+1]!=0)|| (image[posY][posX+2]!=0))return0; }函數功能:設置游戲面板算法描述:獲取當前磚塊的狀態、特定點和顏色針對磚塊不同的狀態進行設置CIBrick::operator>>函數的實現獲取當前磚塊的狀態、特定點和顏色voidCIBrick::operator>>(unsignedchar**binImage){intorientation; intposX; intposY; unsignedcharcolour; posX=getPosX(); posY=getPosY(); orientation=getOrientation(); colour=getColour();CIBrick::operator>>函數的實現針對磚塊不同的狀態進行設置

if(orientation==0)//狀態0

{

binImage[posY-1][posX]=colour;binImage[posY][posX]=colour; binImage[posY+1][posX]=colour; binImage[posY+2][posX]=colour;} if(orientation==1)//狀態1

{

binImage[posY][posX-2]=colour;binImage[posY][posX-1]=colour; binImage[posY][posX]=colour; binImage[posY][posX+1]=colour;} CIBrick::operator>>函數的實現

if(orientation==2)//狀態2

{

binImage[posY-2][posX]=colour; binImage[posY-1][posX]=colour;binImage[posY][posX]=colo

溫馨提示

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

評論

0/150

提交評論