程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> 關於C++ >> 詳解C++編程的多態性概念

詳解C++編程的多態性概念

編輯:關於C++

詳解C++編程的多態性概念。本站提示廣大學習愛好者:(詳解C++編程的多態性概念)文章只能為提供參考,不一定能成為您想要的結果。以下是詳解C++編程的多態性概念正文


多態性(polymorphism)是面向對象法式設計的一個主要特點。假如一種說話只支撐類而不支撐多態,是不克不及被稱為面向對象說話的,只能說是基於對象的,如Ada、VB就屬此類。C++支撐多態性,在C++法式設計中可以或許完成多態性。應用多態性可以設計和完成一個易於擴大的體系。

望文生義,多態的意思是一個事物有多種形狀。多態性的英文單詞polymorphism起源於希臘詞根poly(意為“許多”)和morph(意為“形狀”)。在C ++法式設計中,多態性是指具有分歧功效的函數可以用統一個函數名,如許便可以用一個函數名挪用分歧內容的函數。在面向對象辦法中普通是如許表述多態性的:向分歧的對象發送統一個新聞, 分歧的對象在吸收時會發生分歧的行動(即辦法)。也就是說,每一個對象可以用本身的方法去呼應配合的新聞。所謂新聞,就是挪用函數,分歧的行動就是指分歧的完成,即履行分歧的函數。

其實,我們曾經屢次接觸過量態性的景象,例如函數的重載、運算符重載都是多態景象。只是那時沒有效到多態性這一專門術語罷了。例如,應用運算符“+”使兩個數值相加,就是發送一個新聞,它要挪用operator +函數。現實上,整型、單精度型、雙精度型的加法操作進程是互不雷同的,是由分歧內容的函數完成的。明顯,它們以分歧的行動或辦法來呼應統一新聞。

在實際生涯中可以看到很多多態性的例子。如黉捨校長向社會宣布一個新聞:9月1日新學年開學。分歧的對象會作出分歧的呼應:先生要預備好教材准時到校上課;家長要籌集膏火;教員要備好課;後勤部分要預備好教室、宿捨和食堂……因為事前對各類人的義務已作了劃定,是以,在獲得統一個新聞時,各類人都曉得本身應該怎樣做,這就是 多態性。可以假想,假如晦氣用多態性,那末校長就要分離給先生、家長、教員、後勤部分等很多分歧的對象分離發告訴,分離詳細劃定每種人接到告訴後應當怎樣做。明顯這是一件非常龐雜而過細的任務。一人包辦一切,費勁還不谄谀。如今,應用了多態性機制,校長在宣布新聞時,不用逐個詳細斟酌分歧類型人員是如何履行的。至於各類人員在接到新聞後應氣做甚麼,其實不是暫時決議的,而是黉捨的任務機制事前支配決議好的。校長只需赓續宣布各類新聞,各類人員就會按預定計劃有條不紊地任務。

異樣,在C++法式設計中,在分歧的類中界說了其呼應新聞的辦法,那末應用這些類 時,不用斟酌它們是甚麼類型,只需宣布新聞便可。正如在應用運算符“ ”時不用斟酌相加的數值是整型、單精度型照樣雙精度型,直接應用“+”,豈論哪類數值都能完成相加。可以說這是以不變應萬變的辦法,豈論對象千變萬化,用戶都是用統一情勢的信息去挪用它們,使它們依據事前的支配作出反響。

從體系完成的角度看,多態性分為兩類:靜態多態性和靜態多態性。之前學過的函數重載和運算符重載完成的多態性屬於靜態多態性,在法式編譯時體系就可以決議挪用的是哪一個函數,是以靜態多態性又稱編譯時的多態性。靜態多態性是經由過程函數的重載完成的(運算符重載本質上也是函數重載)。靜態多態性是在法式運轉進程中才靜態地肯定操作所針對的對象。它又稱運轉時的多態性。靜態多態性是經由過程虛函數(Virtual fiinction)完成的。

上面是一個承先啟後的例子。一方面它是有關繼續和運算符重載內容的綜合運用的例子,經由過程這個例子可以進一步融合貫穿後面所學的內容,另外一方面又是作為評論辯論多態性的一個基本用例。

願望年夜家耐煩、深刻地浏覽和消化這個法式,弄清個中的每個細節。

[例] 先樹立一個Point(點)類,包括數據成員x,y(坐標點)。以它為基類,派生出一個Circle(圓)類,增長數據成員r(半徑),再以Circle類為直接基類,派生出一個Cylinder(圓柱體)類,再增長數據成員h(高)。請求編寫法式,重載運算符“<<”和“>>”,使之能用於輸入以上類對象。

這個例題難度不年夜,但法式很長。關於一個比擬年夜的法式,應該分紅若干步調停止。先聲明基類,再聲明派生類,逐級停止,分步驟試。

1) 聲明基類Point

類可寫作聲明基類Point的部門以下:

#include <iostream>
//聲明類Point
class Point
{
public:
 Point(float x=0,float y=0); //有默許參數的結構函數
 void setPoint(float ,float); //設置坐標值
 float getX( )const {return x;} //讀x坐標
 float getY( )const {return y;} //讀y坐標
 friend ostream & operator <<(ostream &,const Point &); //重載運算符“<<”
protected: //受掩護成員
 float x, y;
};
//上面界說Point類的成員函數
Point::Point(float a,float b) //Point的結構函數
{ //對x,y初始化
 x=a;
 y=b;
}
void Point::setPoint(float a,float b) //設置x和y的坐標值
{ //為x,y賦新值
 x=a;
 y=b;
}
//重載運算符“<<”,使之能輸入點的坐標
ostream & operator <<(ostream &output, const Point &p)
{
 output<<"["<<p.x<<","<<p.y<<"]"<<endl;
 return output;
}

以上完成了基類Point類的聲明。

為了進步法式調試的效力,倡導對法式分步驟試,不要將一個長的法式都寫完今後才同一調試,那樣在編譯時能夠會同時湧現年夜量的編譯毛病,面臨一個長的法式,法式人員常常難以敏捷精確地找到失足地位。要擅長將一個年夜的法式分化為若干個文件,分離編譯,或許分步驟試,先經由過程最根本的部門,再慢慢擴大。

如今要對下面寫的基類聲明停止調試,檢討它能否有錯,為此要寫出main函數。現實上它是一個測試法式。

int main( )
{
 Point p(3.5,6.4); //樹立Point類對象p
 cout<<"x="<<p.getX( )<<",y="<<p.getY( )<<endl; //輸入p的坐標值
 p.setPoint(8.5,6.8); //從新設置p的坐標值
 cout<<"p(new):"<<p<<endl; //用重載運算符“<<”輸入p點坐標
 return 0;
}

getX和getY函數聲明為常成員函數,感化是只許可函數援用類中的數據,而不許可修正它們,以包管類中數據的平安。數據成員x和y聲明為protected,如許可以被派生類拜訪(假如聲明為private,派生類是不克不及拜訪的)。

法式編譯經由過程,運轉成果為:

x=3.5,y=6.4
p(new):[8.5,6.8]

測試法式檢討了基類中各函數的功效,和運算符重載的感化,證實法式是准確的。

2)聲明派生類Circle

在下面的基本上,再寫作聲明派生類Circle的部門:

class Circle:public Point //circle是Point類的公用派生類
{
public:
Circle(float x=0,float y=0,float r=0); //結構函數
void setRadius(float ); //設置半徑值
float getRadius( )const; //讀取半徑值
float area ( )const; //盤算圓面積
friend ostream &operator <<(ostream &,const Circle &); //重載運算符“<<”
private:
float radius;
};
//界說結構函數,對圓心坐標和半徑初始化
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)
{
 output<<"Center=["<<c.x<<","<<c.y<<"],r="<<c.radius<<",area="<<c.area( )<<endl;
 return output;
}

為了測試以上Circle類的界說,可以寫出上面的主函數:

int main( )
{
Circle c(3.5,6.4,5.2); //樹立Circle類對象c,並給定圓心坐標和半徑
cout<<"original circle:\\nx="<<c.getX()<<", y="<<c.getY()<<", r="<<c.getRadius( )<<", area="<<c.area( )<<endl; //輸入圓心坐標、半徑和面積
c.setRadius(7.5); //設置半徑值
c.setPoint(5,5); //設置圓心坐標值x,y
cout<<"new circle:\\n"<<c; //用重載運算符“<<”輸入圓對象的信息
Point &pRef=c; //pRef是Point類的援用變量,被c初始化
cout<<"pRef:"<<pRef; //輸入pRef的信息
return 0;
}

法式編譯經由過程,運轉成果為:

original circle:(輸入本來的圓的數據)
x=3.5, y=6.4, r=5.2, area=84.9486
new circle:(輸入修正後的圓的數據)
Center=[5,5], r=7.5, area=176.714
pRef:[5,5] (輸入圓的圓心“點”的數據)

可以看到,在Point類中聲清楚明了一次運算符“ <<”重載函數,在Circle類中又聲清楚明了一次運算符“ <<”,兩次重載的運算符“<<”內容是分歧的,在編譯時編譯體系會依據輸入項的類型肯定挪用哪個運算符重載函數。main函數第7行用“cout<< ”輸入c,挪用的是在Circle類中聲明的運算符重載函數。

請留意main函數第8行:

 Point & pRef = c;

界說了 Point類的援用變量pRef,並用派生類Circle對象c對其初始化。後面我們曾經講過,派生類對象可以替換基類對象為基類對象的援用初始化或賦值(概況請檢查:C++基類與派生類的轉換)。如今 Circle是Point的公用派生類,是以,pRef不克不及以為是c的別號,它獲得了c的肇端地址, 它只是c中基類部門的別號,與c中基類部門同享統一段存儲單位。所以用“cout<<pRef”輸入時,挪用的不是在Circle中聲明的運算符重載函數,而是在Point中聲明的運算符重載函數,輸入的是“點”的信息,而不是“圓”的信息。

3) 聲明Circle的派生類Cylinder

後面已從基類Point派生出Circle類,如今再從Circle派生出Cylinder類。

class Cylinder:public Circle// Cylinder是Circle的公用派生類
{
public:
 Cylinder (float x=0,float y=0,float r=0,float h=0); //結構函數
 void setHeight(float ); //設置圓柱高
 float getHeight( )const; //讀取圓柱高
 loat area( )const; //盤算圓外面積
 float volume( )const; //盤算圓柱體積
 friend ostream& operator <<(ostream&,const Cylinder&); //重載運算符<<
protected:
 float height;//圓柱高
};
//界說結構函數
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::getHeight( )const {return height;}
//盤算圓外面積
float Cylinder::area( )const { return 2*Circle::area( )+2*3.14159*radius*height;}
//盤算圓柱體積
float Cylinder::volume()const {return Circle::area()*height;}
ostream &operator <<(ostream &output,const Cylinder& cy)
{
 output<<"Center=["<<cy.x<<","<<cy.y<<"],r="<<cy.radius<<",h="<<cy.height <<"\\narea="<<cy.area( )<<", volume="<<cy.volume( )<<endl;
 return output;
} //重載運算符“<<”

可以寫出上面的主函數:

int main( )
{
 Cylinder cy1(3.5,6.4,5.2,10);//界說Cylinder類對象cy1
 cout<<"\\noriginal cylinder:\\nx="<<cy1.getX( )<<", y="<<cy1.getY( )<<", r="
  <<cy1.getRadius( )<<", h="<<cy1.getHeight( )<<"\\narea="<<cy1.area()
  <<",volume="<<cy1.volume()<<endl;//用體系界說的運算符“<<”輸入cy1的數據
 cy1.setHeight(15);//設置圓柱高
 cy1.setRadius(7.5);//設置圓半徑
 cy1.setPoint(5,5);//設置圓心坐標值x,y
 cout<<"\\nnew cylinder:\\n"<<cy1;//用重載運算符“<<”輸入cy1的數據
 Point &pRef=cy1;//pRef是Point類對象的援用變量
 cout<<"\\npRef as a Point:"<<pRef;//pRef作為一個“點”輸入
 Circle &cRef=cy1;//cRef是Circle類對象的援用變量
 cout<<"\\ncRef as a Circle:"<<cRef;//cRef作為一個“圓”輸入
 return 0;
}

運轉成果以下:

original cylinder:(輸入cy1的初始值)
x=3.5, y=6.4, r=5.2, h=10 (圓心坐標x,y。半徑r,高h)
area=496.623, volume=849.486 (圓柱外面積area和體積volume)
new cylinder: (輸入cy1的新值)
Center=[5,5], r=7.5, h=15 (以[5,5]情勢輸入圓心坐標)
area=1060.29, volume=2650.72(圓柱外面積area和體積volume)
pRef as a Point:[5,5] (pRef作為一個“點”輸入)
cRef as a Circle:Center=[5,5], r=7.5, area=176.714(cRef作為一個“圓”輸入)

解釋:在Cylinder類中界說了 area函數,它與Circle類中的area函數同名,依據後面我們講授的同名籠罩的准繩(概況請檢查:C++多重繼續的二義性成績),cy1.area( ) 挪用的是Cylinder類的area函數(求圓柱外面積),而不是Circle類的area函數(圓面積)。請留意,這兩個area函數不是重載函數,它們不只函數名雷同,並且函數類型和參數個數都雷同,兩個同名函數不在同 —個類中,而是分離在基類和派生類中,屬於同名籠罩。重載函數的參數個數和參數類型必需至多有一者分歧,不然體系沒法肯定挪用哪個函數。

main函數第9行用“cout<<cy1”來輸入cy1,此時挪用的是在Cylinder類中聲明的重載運算符“<<”,按在重載時劃定的方法輸入圓柱體cy1的有關數據。

main函數中最初4行的寄義與在界說Circle類時的情形相似。pRef是Point類的援用變量,用cy1對其初始化,但它不是cy1的別號,只是cy1中基類Point部門的別號,在輸入pRef時是作為一個Point類對象輸入的,也就是說,它是一個“點”。異樣,cRef是Circle類的援用變量,用cy1對其初始化,但它只是cy1中的直接基類Circle部門的別號, 在輸入 cRef 時是作為Circle類對象輸入的,它是一個"圓”,而不是一個“圓柱體”。從輸 出的成果可以看出挪用的是哪一個運算符函數。

在本例中存在靜態多態性,這是運算符重載惹起的(留意3個運算符函數是重載而不是同名籠罩,由於有一個形參類型分歧)。可以看到,在編譯時編譯體系便可以剖斷應挪用哪一個重載運算符函數。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved