C 中虛函式與多型

2021-06-05 00:25:05 字數 3666 閱讀 8253

物件導向理論中的3個術語:物件、方法和訊息。物件(object):不言而喻,它是構成系統的基本單位,有屬性和行為兩個要素,在c++中,每個物件都是由資料和函式這兩部分組成的,資料即是物件的屬性,行為稱之為方法(method),方法是對資料的操作,通常由函式實現。呼叫物件中的函式就是向該物件傳送乙個訊息(message),所謂「訊息」,其實就是乙個命令。例如:

stud.display();

就是向物件stud發出的乙個「訊息」,通知它執行其中的display「方法」(即display函式)。即:stud是物件,display()是方法,語句「stud.display();」是訊息。

1.多型性(polymorphism)

多型性定義:由繼承而產生的相關的不同的類,向其物件傳送同乙個訊息,不同的物件接收到後會產生不同的行為(即方法)。

多型性分為兩類:靜態多型性和動態多型性。函式過載和運算子過載實現的多型性屬於靜態多型性,在程式編譯時系統就能決定呼叫的是哪個函式,因此靜態多型性有稱為編譯時的多型性。靜態多型性是通過函式的過載實現的(運算子過載實質上也是函式過載)。動態多型性是在程式執行過程中才動態地確定操作所針對的物件,故稱之為執行時的多型性。動態多型性是通過虛函式實現的。

關於靜態多型性和動態多型性,請看下面的例子:

定義3個類:點、圓和圓柱

#include

//定義point基類

class point ;

point::point(float a, float b)

ostream & operator <<(ostream &output, const point &p)

void point::display()

//定義circle基類

class circle: public point ;

circle::circle(float a,float b,float r):point(a,b),radius(r)

float circle::area( ) const

ostream & operator <<(ostream &output, const circle &c)

void circle::display()

//定義圓柱體類

class cylinder: public circle ;

cylinder::cylinder(float a,float b,float r,float h):circle(a,b,r),height(h){}

float cylinder::area( ) const

float cylinder::volume() const

ostream & operator <<(ostream & output, const cylinder &cy)

void cylinder::display()

主函式(1),靜態關聯

int main()

由該主函式可知:1. 圓柱物件cy1可以直接賦值給其基類的物件;2. circle類和cylinder類中都有乙個area( )函式,之所以在cylinder中用area( )能直接呼叫cylinder::area( )而沒有呼叫circle:: area( )是因為「同名覆蓋」的緣故,預設cylinder中的area( )覆蓋了基類中的area( )函式(如果不想覆蓋,可以用純虛函式,能夠對基類函式重新定義,但是哪個效果更高還不好說)。3. 三個類中都包含了同名的過載運算子「<<」函式,但是他們的第二個引數型別互不相同,所以不能看做是同名覆蓋,實際上,是屬於靜態關聯。「<<」運算子之所以能準確地呼叫不同類中的過載函式,是因為系統在編譯時就已經確定了呼叫物件。

主函式(2),動態關聯:

int main( )

首先應該明確一點:定義為指向point基類物件的指標,當改變方向,指向派生類物件後,它僅指向派生類物件中基類的部分物件(例如當pt=&c1後,呼叫pt->display()相當於呼叫pt->point::display()),所以上面呼叫的display()函式只能輸出基類物件的值(即:定義為point型別的指標pt根本就無法指向派生類增加的資料或函式,例如pt-> circle::display()就會出錯,提示「'circle' : is not a member of 'point'」)。

要想pt能指向circle::display(),就必須用虛成員函式來實現,即把基類中的display()函式宣告為virtual型別。基類中的display()函式宣告為了virtual型別,代表了它可以在派生類中被重新定義,為它賦予新功能(所以可以基類中的虛函式的函式體可以為空,或者寫成純虛函式的形式),注意是「重新定義」而不是「共存」,即此時circle中定義的display()函式不再看做是增加的部分,而是看做基類的部分,所以直接用pt->display()或者pt->point::display()就可以呼叫circle類中定義的display()函式了,寫成pt-> circle::display()反而會出錯。

2. 虛函式

上面已經說了,c++的動態多型性是通過虛函式來實現的。「虛成員函式」簡稱「虛函式」,c++不允許在類外宣告虛函式。「虛函式允許派生類取代基類所提供的實現。編譯器確保當物件為派生類時,取代者(譯註:即派生類的實現)總是被呼叫,即使物件是使用基類指標訪問而不是派生類的指標。」

上面的例子,寫成虛函式的形式:

class

//宣告為空的虛函式 }

int mai()

也寫成純虛函式的形式:

class

int mai()

因為純虛函式「徒有其名,而無其實」,所以包含純虛函式的類都只作為基類,相當於提供一種基本的型別,它的不能被初始化,這種類被稱為「抽象基類」,它總是被呼叫的。

例如,可以給點、圓和圓柱體定義乙個抽象基類shape(形狀):

class shape

//虛函式

virtual float volume() const    //虛函式

virtual void shapename() const =0;      //純虛函式 };

3. 虛析構函式

如果用new運算子建立了臨時物件,若基類中有析構函式,並且定義了乙個指向該基類的指標變數。在程式用帶指標引數的delete運算子撤銷物件時,會發生乙個情況:系統會只執行基類的析構函式,而不執行派生類的析構函式。例如:

#include

//定義point基類

class point

; //定義建構函式

~point() ;

class circle: public point ;

~circle() ;

int main( )

希望用delete釋放p所指的空間,但執行結果卻為:

point ok!

表示只執行了基類piont的析構函式,而沒有執行派生類circle的析構函式。如果希望能執行派生類中的析構函式,可以將基類的析構函式宣告為虛函式,如

virtual ~ point() { cout<<"point ok!"<

如果將基類的析構函式宣告為虛函式,由該基類所派生的所有派生類的析構函式也自動成為虛函式,即使它們名字不同。可見原理和格式與上面所說的虛函式是一樣的。執行結果為:

point ok!

circle ok!

專業人員一般都習慣宣告虛析構函式,即使基類並不需要析構函式,也顯式地定義乙個函式體為空的析構函式,以保證在撤銷動態儲存空間時能得到正確的處理。

**:

C 虛函式與多型

1.1 虛函式概念 1.定義 在乙個類的成員函式前面加上virtual關鍵字,則該函式就稱為虛函式。2.如果乙個函式不是類的成員函式,則該函式不能定義為虛函式。即就是類外面不能使用virtual關鍵字 1.2 純虛函式與抽象類 1.純虛函式 在虛函式的後面加上 0 virtual void disp...

c 多型與虛函式

多型按字面的意思就是多種形態。當類之間存在層次結構,並且類之間是通過繼承關聯時,就會用到多型。c 多型意味著呼叫成員函式時,會根據呼叫函式的物件的型別來執行不同的函式。下面的例項中,基類 shape 被派生為兩個類,如下所示 include using namespace std class sha...

C 多型與虛函式

這一篇介紹一下 c 物件導向三大特徵之一的多型 之前面試某大廠的實習生被問到多型,後來又了解到一些設計模式,才體會到多型的強大,在這裡把對多型的一點點淺顯認識總結一下 虛表 class test cout sizeof test endl test p new test p vfunc 將類指標p強...