C 多型性與虛函式

2021-06-21 06:31:58 字數 4192 閱讀 8566

物件導向程式設計中的多型性是指向不同的物件傳送同乙個訊息,不同物件對應同一訊息產生不同行為。在程式中訊息就是呼叫函式,不同的行為就是指不同的實現方法,即執行不同的函式體。也可以這樣說就是實現了「乙個介面,多種方法」。

從實現的角度來講,多型可以分為兩類:編譯時的多型性和執行時的多型性。前者是通過靜態聯編來實現的,比如c++中通過函式的過載和運算子的過載。後者則是通過動態聯編來實現的,在c++中執行時的多型性主要是通過虛函式來實現的,也正是今天我們要講的主要內容。

1.不過在說虛函式之前,我想先介紹乙個有關於基類與派生類物件之間的

賦值相容關係的內容。它也是之後學習虛函式的基礎。我們有時候會把整型資料賦值給雙精度型別的變數。在賦值之前,先把整形資料轉換為雙精度的,在把它賦值給雙精度型別的變數。這種不同型別資料之間的自動轉換和賦值,稱為賦值相容。同樣的,

在基類和派生類之間也存在著賦值相容關係,它是指需要基類物件的任何地方都可以使用公有派生類物件來代替。為什麼只有公有繼承的才可以呢,因為在公有繼承中派生類保留了基類中除了構造和析構之外的所有成員,基類的公有或保護成員的訪問許可權都按原樣保留下來,在派生類外可以呼叫基類的公有函式來訪問基類的私有成員。因此基類能實現的功能,派生類也可以。

那麼它們具體是如何體現的呢?(1)派生類物件直接向基類賦值,賦值效果,基類資料成員和派生類中資料成員的值相同;(2)派生類物件可以初始化基類物件引用;(3)派生類物件的位址可以賦給基類物件的指標;(4)函式形參是基類物件或基類物件的引用,在呼叫函式時,可以用派生類的物件作為實參;

#include "stdafx.h"

#include#includeclass abcbase

void showabc();

};void abcbase::showabc()

結果:

要注意的是:第一,在基類和派生類物件的賦值時,該派生類必須是公有繼承的。第二,只允許派生類物件向基類物件賦值,反過來不允許;

2.緊接著來講一下虛函式,它允許函式呼叫與函式體之間的聯絡在執行時才建立,即在執行時才決定如何動作。虛函式宣告的格式:

virtual 返回型別 函式名(形參表)

那麼定義虛函式有什麼用呢?讓我們先來看看下面這個示例:

#include "stdafx.h"

#include #include class graph

;graph::graph(double x,double y)

void graph::showarea()

結果:

三個都輸出基類的showarea(),這是因為程式裡採用的是靜態聯編機制,graph的指向在編譯時就已經確定,是指向graph類的,所以輸出都是graph->showarea()。

對比一下程式:

#include #include class graph 

;graph::graph(double x,double y)

void graph::showarea()

;graph::graph(double x,double y)

void graph::showarea()

結果:

在基類中的某成員函式被宣告為虛函式後,在之後的派生類中可以重新來定義它。但定義時,其函式原型,包括返回型別、函式名、引數個數、引數型別的順序,都必須和基類中的原型完全相同。其實在上述修改後的示例**裡,只要在基類中顯式宣告了虛函式,那麼在之後的派生類中就需要用virtual來顯式宣告了,可以略去,因為系統會根據其是否和基類中虛函式原型完全相同來判斷是不是虛函式。因此,上述派生類中的虛函式如果不顯式宣告也還是虛函式。最後對虛函式做幾點補充說明:(1)因為虛函式使用的基礎是賦值相容,而賦值相容成立的條件是派生類是從基類公有派生而來。所以使用虛函式,派生類必須是基類公有派生的;(2)定義虛函式,不一定要在最高層的類中,而是看在需要動態多型性的幾個層次中的最高層類中宣告虛函式;(3)雖然在上述示例**中main()主函式實現部分,我們也可以使用相應圖形物件和點運算子的方式來訪問虛函式,如:

rectangcle.showarea(),但是這種呼叫在編譯時進行靜態聯編,它沒有充分利用虛函式的特性。只有通過基類物件來訪問虛函式才能獲得動態聯編的特性;(4)乙個虛函式無論配公有繼承了多少次,它仍然是虛函式;(5)虛函式必須是所在類的成員函式,而不能是友元函式,也不能是靜態成員函式。因為虛函式呼叫要靠特定的物件類決定該啟用哪乙個函式;(6)內聯函式不能是虛函式,因為內聯函式是不能在執行中動態確定其位置的即使虛函式在類內部定義,編譯時將其看作非內聯;(7)建構函式不能是虛函式,但析構函式可以是虛函式;

如果在main()主函式中用new建立乙個派生類無名物件和定義乙個基類物件指標,並將無名物件的位址賦給基類物件指標時,當我們用delete運算子來撤銷無名物件時,系統只執行基類析構函式,而不執行派生類析構函式。比如:

#include "stdafx.h"

#include #include class graph

;graph::graph(double x,double y)

void graph::showarea()

結果:

因為在撤銷指標graph所指的派生類物件,在呼叫析構函式時,採用靜態聯編,只呼叫了graph類的析構函式。如果也想呼叫派生類rectangle類的析構函式的話,可將graph類的析構函式定義為虛析構函式,這樣在析構物件時,由執行時多型得知graph實際上指的是rectangle類物件,從而呼叫rectangle類的析構函式(同時呼叫基類graph的析構函式)。其定義的一般格式:

virtual ~類名()

;雖然派生類的析構函式與基類的析構函式名字不同,但是如果將基類的析構函式定義為虛函式,由該基類派生而來的所有派生類的析構函式都自動成為虛函式。我們把上一示例中的graph類的析構函式前加上關鍵字virtual,那麼執行結果:

顯然這個結果才是我們所需要的。

3.上述示例中用了虛函式後,會發現其實graph類(圖形類)中的虛函式的函式體根本沒有被用到過,就算被用到,該基類體現了圖形的抽象的概念,並不與具體事物相聯絡。所以基類中的虛函式也沒有實質性的功能。因此我們只需要在基類中留下乙個函式名,而具體的實現留給派生類去定義。在c++中就是用純虛函式來說明的。純虛函式的一般形式:

virtual 返回型別 函式名(形參表)=0;

這裡的"=0"並不是函式的返回值等於零,它只是起到形式上的作用,告訴編譯系統"這是純虛函式"。純虛函式不具備函式功能,不能被呼叫。

class graph

;graph::graph(double x,double y)

4.如果乙個類中至少有乙個純虛函式,那麼就稱該類為抽象類。所以上述中graph類就是抽象類。對於抽象類有以下幾個注意點:(1)抽象類只能作為其他類的基類來使用,不能建立抽象類物件;(2)不允許從具體類中派生出抽象類(不包含純虛函式的普通類);(3)抽象類不能用作函式的引數型別、返回型別和顯示轉化型別;(4)如果派生類中沒有定義純虛函式的實現,而只是繼承成了基類的純虛函式。那麼該派生類仍然為抽象類。一旦給出了對基類中虛函式的實現,那麼派生類就不是抽象類了,而是可以建立物件的具體類;

5.最後用乙個例項來總結一下今天所講的內容(開發工具:vs2010):

#include "stdafx.h"

#include #include class graph //抽象類

;graph::graph(double x,double y)

void graph::showarea()

return0;

}

結果:

C 多型性 與 虛函式

1.運算子過載 運算子過載形式 1 過載為類的成員函式 運算子過載函式說明為類的成員函式的格式為 類名 operator 運算子 參數列 如 c1 c2 編譯器將給出的解釋為 c1.operator c2 該運算子過載函式僅僅有乙個引數,所以過載為成員函式時,雙目運算子僅有乙個引數。對於單目運算子,...

C 多型性與虛函式

多型性是物件導向程式設計的關鍵技術之一。利用多型性技術,可以呼叫同乙個函式名的函式,實現完全不同的功能。若程式語言不支援多型性,不能稱為物件導向的語言。在c 中有兩種多型性 1 定義格式 虛函式是乙個類的成員函式,定義格式如下 virtual 返回型別 函式名 參數列 說明 2 通過虛函式實現多型性...

C 多型性與虛函式

2 多型是物件導向的重要技術之一,它是一種行為的封裝,是同乙個事物所表現出來的多種形態,簡單地說就是 乙個介面 多種形態。3 在執行時使用同乙個成員名來呼叫類物件的成員函式,會呼叫哪個物件的成員函式呢?這就是多型要解決的問題!1 多型技術允許將基類指標或基類引用指向派生類物件。2 把不同派生類的物件...