深刻解析C++編程中的純虛函數和籠統類。本站提示廣大學習愛好者:(深刻解析C++編程中的純虛函數和籠統類)文章只能為提供參考,不一定能成為您想要的結果。以下是深刻解析C++編程中的純虛函數和籠統類正文
C++純虛函數詳解
有時在基類中將某一成員函數定為虛函數,其實不是基類自己的請求,而是斟酌到派生類的須要,在基類中預留了一個函數名,詳細功效留給派生類依據須要去界說。
純虛函數是在聲明虛函數時被“初始化”為0的函數。聲明純虛函數的普通情勢是
virtual 函數類型 函數名 (參數表列) = 0;
關於純虛函數須要留意的幾點:
純虛函數只要函數的名字而不具有函數的功效,不克不及被挪用。它只是告訴編譯體系:“在這裡聲明一個虛函數,留待派生類中界說”。在派生類中對此函數供給界說後,它能力具有函數的功效,可被挪用。
純虛函數的感化是在基類中為其派生類保存一個函數的名字,以便派生類依據須要對它停止界說。
假如在基類中沒有保存函數名字,則沒法完成多態性。假如在一個類中聲清楚明了純虛函數,而在其派生類中沒有對該函數界說,則該虛函數在派生類中依然為純虛函數。
再談C++籠統類
假如聲清楚明了一個類,普通可以用它界說對象。然則在面向對象法式設計中,常常有一些類,它們不消來生成對象。界說這些類的唯一目標是用它作為基類去樹立派生類。它們作為一種根本類型供給給用戶,用戶在這個基本上依據本身的須要界說出功效各別的派生類。用這些派生類去樹立對象。
打個比喻,汽車制作廠常常向客戶供給卡車的底盤(包含動員機、傳動部門、車輪等),組裝廠可以把它組裝成貨車、公共汽車、工程車或客車等分歧功效的車輛。底盤自己不是車輛,要經由加工能力成為車輛,但它是車輛的根本構成部門。它相當於基類。在古代化的臨盆中,年夜多采取專業化的臨盆方法,充足應用專業化工場臨盆的部件,加工集成為新種類的產物。臨盆公共汽車的廠家決不會從制作動員機到臨盆輪胎、制作車箱都由本廠完成。其實,分歧品牌的電腦外面的根本部件是一樣的或類似的。這類不雅念對軟件開辟是非常主要的。一個優良的軟件任務者在開辟一個年夜的軟件時,決不會從頭至尾都由本身編寫法式代碼,他會充足應用已有資本(例如類庫)作為本身任務的基本。
這類不消來界說對象而只作為一種根本類型用作繼續的類,稱為籠統類(abstract class ),因為它經常使用作基類,平日稱為籠統基類(abstract base class )。但凡包括純虛函數的類都是籠統類。由於純虛函數是不克不及被挪用的,包括純虛函數的類是沒法樹立對象的。
籠統類的感化是作為一個類族的配合基類,或許說,為一個類族供給一個公共接口。一個類條理構造中固然也可不包括任何籠統類,每條理的類都是現實可用的,可以用來樹立對象的。
然則,很多好的面向對象的體系,其條理構造的頂部是一個籠統類,乃至頂部有好幾層都是籠統類。
假如在籠統類所派生出的新類中對基類的一切純虛函數停止了界說,那末這些函數就被付與了功效,可以被挪用。這個派生類就不是籠統類,而是可以用來界說對象的詳細類(concrete class )。
假如在派生類中沒有對一切純虛函數停止界說,則此派生類依然是籠統類,不克不及用來界說對象。固然籠統類不克不及界說對象(或許說籠統類不克不及實例化),然則可以界說指向籠統類數據的指針變量。當派生類成為詳細類以後,便可以用這類指針指向派生類對象,然後經由過程該指針挪用虛函數,完成多態性的操作。
幾個關於C++純虛函數與籠統類的實例
上面是一個完全的法式,為了便於浏覽,分段拔出了一些文字解釋。法式以下:
第(1)部門
#include <iostream> using namespace std; //聲明籠統基類Shape class Shape { public: virtual float area( )const {return 0.0;} //虛函數 virtual float volume()const {return 0.0;} //虛函數 virtual void shapeName()const =0; //純虛函數 };
Shape類有3個成員函數,沒稀有據成員。3個成員函數都聲明為虛函數,個中shapeName聲明為純虛函數,是以Shape是一個籠統基類。shapeName函數的感化是輸入詳細的外形(如點、圓、圓柱體)的名字,這個信息是與響應的派生類親密相干的,明顯這不該當在基類中界說,而應在派生類中界說。所以把它聲明為純虛函數。Shape固然是籠統基類,然則也能夠包含某些成員的界說部門。類中兩個函數area(面積)和volume (體積)包含函數體,使其前往值為0(由於可以以為點的面積和體積都為0)。因為斟酌到在Point類中不再對area和volume函數從新界說,是以沒有把area和volume函數也聲明為純虛函數。在Point類中繼續了Shape類的area和volume函數。這3個函數在各派生類中都要用到。
第(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( )const {return y;} virtual void shapeName( )const {cout<<"Point:";}//對虛函數停止再界說 friend ostream & operator <<(ostream &,const Point &); protected: float x,y; }; //界說Point類成員函數 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) { output<<"["<<p.x<<","<<p.y<<"]"; return output; }
Point從Shape繼續了3個成員函數,因為“點”是沒有面積和體積的,是以不用從新界說area和volume。固然在Point類頂用不到這兩個函數,然則Point類依然從Shape類繼續了這兩個函數,以便其派生類繼續它們。shapeName函數在Shape類中是純虛函數, 在Point類中要停止界說。Point類還有本身的成員函數( setPoint, getX, getY)和數據成 員(x和y)。
第(3)部門
//聲明Circle類 class Circle:public Point { public: Circle(float x=0,float y=0,float r=0); void setRadius(float ); float getRadius( )const; virtual float area( )const; virtual void shapeName( )const {cout<<"Circle:";}//對虛函數停止再界說 friend ostream &operator <<(ostream &,const Circle &); protected: float radius; }; //聲明Circle類成員函數 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<<"["<<c.x<<","<<c.y<<"], r="<<c.radius; return output; }
在Circle類中要從新界說area函數,由於須要指定求圓面積的公式。因為圓沒有體積,是以不用從新界說volume函數,而是從Point類繼續volume函數。shapeName函數是虛函數,須要從新界說,付與新的內容(假如不從新界說,就會繼續Point類中的 shapeName函數)。另外,Circle類還有本身新增長的成員函數(setRadius, getRadius)和數據成員(radius)。
第(4)部門
//聲明Cylinder類 class Cylinder:public Circle { public: Cylinder (float x=0,float y=0,float r=0,float h=0); void setHeight(float ); virtual float area( )const; virtual float volume( )const; virtual void shapeName( )const { cout<<"Cylinder:"; }//對虛函數停止再界說 friend ostream& operator <<(ostream&,const Cylinder&); protected: float height; }; //界說Cylinder類成員函數 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( )const{ return Circle::area( )*height; } ostream &operator <<(ostream &output,const Cylinder& cy){ output<<"["<<cy.x<<","<<cy.y<<"], r="<<cy.radius<<", h="<<cy.height; return output; }
Cylinder類是從Circle類派生的。因為圓柱體有外面積和體積,所以要對area和 volume函數從新界說。虛函數shapeName也須要從新界說。另外,Cylinder類還有自已 的成員函數setHeight和數據成員radius。
第(5)部門
//main函數 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); //樹立Cylinder類對象cylinder point.shapeName(); //靜態聯系關系 cout<<point<<endl; circle.shapeName(); //靜態聯系關系 cout<<circle<<endl; cylinder.shapeName(); //靜態聯系關系 cout<<cylinder<<endl<<endl; Shape *pt; //界說基類指針 pt=&point; //指針指向Point類對象 pt->shapeName( ); //靜態聯系關系 cout<<"x="<<point.getX( )<<",y="<<point.getY( )<<"\narea="<<pt->area( ) <<"\nvolume="<<pt->volume()<<"\n\n"; pt=&circle; //指針指向Circle類對象 pt->shapeName( ); //靜態聯系關系 cout<<"x="<<circle.getX( )<<",y="<<circle.getY( )<<"\narea="<<pt->area( ) <<"\nvolume="<<pt->volume( )<<"\n\n"; pt=&cylinder; //指針指向Cylinder類對象 pt->shapeName( ); //靜態聯系關系 cout<<"x="<<cylinder.getX( )<<",y="<<cylinder.getY( )<<"\narea="<<pt->area( ) <<"\nvolume="<<pt->volume( )<<"\n\n"; return 0; }
在主函數中挪用有關函數並輸入成果。先分離界說了 Point類對象point,Circle類對象circle和Cylinder類對象cylinder。然後分離經由過程對象名point, circle和cylinder挪用 了shapeNanme函數,這是屬於靜態聯系關系,在編譯階段就可以肯定應挪用哪個類的 shapeName函數。同時用重載的運箅符“<<”來輸入各對象的信息,可以驗證對象初始化能否准確。
再界說一個指向基類Shape對象的指針變量pt,使它前後指向3個派生類對象 point, Circle和cylinder,然後經由過程指針挪用各函數,如 pt->shapeName( ),pt ->area(), pt->volume( )。這時候是經由過程靜態聯系關系分離肯定應當挪用哪一個函數。分離輸入分歧類對象的信息。
法式運轉成果以下:
Point:[3.2,4.5](Point類對象point的數據:點的坐標) Circle:[2.4,1.2], r=5.6 (Circle類對象circle的數據:圓心和半徑) Cylinder:[3.5,6.4], r=5.5, h=10.5 (Cylinder類對象cylinder的數據: 圓心、半徑和高) Point:x=3.2,y=4.5 (輸入Point類對象point的數據:點的坐標) area=0 (點的面積) volume=0 (點的體積) Circle:x=2.4,y=1.2 (輸入Circle類對象circle的數據:圓心坐標) area=98.5203 (圓的面積) volume=0 (圓的體積) Cylinder:x=3.5,y=6.4 (輸入Cylinder類對象cylinder的數據:圓心坐標) area=512.595 (圓的面積) volume=891.96 (圓柱的體積)
從本例可以進一步明白以下結論:
一個基類假如包括一個或一個以上純虛函數,就是籠統基類。籠統基類不克不及也不用要界說對象。
籠統基類與通俗基類分歧,它普通其實不是實際存在的對象的籠統(例如圓形(Circle)就是千萬萬萬個現實的圓的籠統),它可以沒有任何物理上的或其他現實意義方面的寄義。
在類的條理構造中,頂層或最下面的幾層可所以籠統基類。籠統基類表現了本類族中各類的個性,把各類中共有的成員函數集中在籠統基類中聲明。
籠統基類是本類族的公共接口。或許說,從統一基類派生出的多個類有統一接口。
差別靜態聯系關系和靜態聯系關系。假如是經由過程對象名挪用虛函數(如point.shapeName()),在編譯階段就可以肯定挪用的是哪個類的虛函數,所以屬於靜態聯系關系。 假如是經由過程基類指針挪用虛函數(如pt ->shapeName()),在編譯階段沒法從語句自己肯定挪用哪個類的虛函數,只要在運轉時,pt指向某一類對象後,能力肯定挪用的是哪 一個類的虛函數,故為靜態聯系關系。
假如在基類聲清楚明了虛函數,則在派生類中但凡與該函數有雷同的函數名、函數類型、參數個數和類型的函數,均為虛函數(豈論在派生類中能否用virtual聲明)。
應用虛函數進步了法式的可擴大性。把類的聲明與類的應用分別。這關於設計類庫的軟件開辟商來講尤其主要。
開辟商設計了各類各樣的類,但不向用戶供給源代碼,用戶可以不曉得類是如何聲明的,然則可使用這些類來派生出本身的類。應用虛函數和多態性,法式員的留意力集中在處置廣泛性,而讓履行情況處置特別性。
多態性把操作的細節留給類的設計者(他們多為專業人員)去完成,而讓法式人員(類的應用者)只須要做一些微觀性的任務,告知體系做甚麼,而不用斟酌怎樣做,極年夜地簡化了運用法式的編碼任務,年夜年夜加重了法式員的累贅,也下降了進修和應用C++編程的難度,使更多的人能更快地進入C++法式設計的年夜門。