物件導向程式設計中的多型性是指向不同的物件傳送同乙個訊息,不同物件對應同一訊息產生不同行為。在程式中訊息就是呼叫函式,不同的行為就是指不同的實現方法,即執行不同的函式體。也可以這樣說就是實現了「乙個介面,多種方法」。
從實現的角度來講,多型可以分為兩類:編譯時的多型性和執行時的多型性。前者是通過靜態聯編來實現的,比如c++中通過函式的過載和運算子的過載。後者則是通過動態聯編來實現的,在c++中執行時的多型性主要是通過虛函式來實現的,也正是今天我們要講的主要內容。
不過在說虛函式之前,我想先介紹乙個有關於基類與派生類物件之間的複製相容關係的內容。它也是之後學習虛函式的基礎。我們有時候會把整型資料賦值給雙精度型別的變數。在賦值之前,先把整形資料轉換為雙精度的,在把它賦值給雙精度型別的變數。這種不同型別資料之間的自動轉換和賦值,稱為賦值相容。同樣的,在基類和派生類之間也存在著賦值相容關係,它是指需要基類物件的任何地方都可以使用公有派生類物件來代替。為什麼只有公有繼承的才可以呢,因為在公有繼承中派生類保留了基類中除了構造和析構之外的所有成員,基類的公有或保護成員的訪問許可權都按原樣保留下來,在派生類外可以呼叫基類的公有函式來訪問基類的私有成員。因此基類能實現的功能,派生類也可以。
那麼它們具體是如何體現的呢?(1)派生類物件直接向基類賦值,賦值效果,基類資料成員和派生類中資料成員的值相同;
(2)派生類物件可以初始化基類物件引用;
(3)派生類物件的位址可以賦給基類物件的指標;
(4)函式形參是基類物件或基類物件的引用,在呼叫函式時,可以用派生類的物件作為實參;
說明:第一,在基類和派生類物件的賦值時,該派生類必須是公有繼承的。第二,只允許派生類物件向基類物件賦值,反過來不允許;
緊接著來講一下虛函式,它允許函式呼叫與函式體之間的聯絡在執行時才建立,即在執行時才決定如何動作。虛函式宣告的格式:
virtual 返回型別 函式名(形參表)
那麼定義虛函式有什麼用呢?讓我們先來看看下面這個示例:
結果似乎和我們想象的不一樣,既然graph類(圖形類)的物件graph指標分別指向了rectangle類(矩形類)物件,********類(三角類)物件,以及circle類(圓類)物件,那麼就應該執行它們自己所對應成員函式showarea(),怎麼結果會是graph類(圖形類)的物件graph裡的成員函式呢?這跟我們所想的派生類成員覆蓋了基類中使用相同名稱的成員(派生類物件呼叫同名成員函式是來自於自己類中成員函式,而非基類中上的)有所不同啊,其實當基類物件
指標指向公有派生類的物件時,
它只能訪問從基類繼承下來的成員,而不能訪問派生類中定義的成員。但是使用動態指標就是為了表達一種動態呼叫的性質即當前指標指向哪個物件,就呼叫那個物件對應類的成員函式。那要怎麼來解決的,這時虛函式就體現出了它的作用。其實我們只需要對上乙個示例**中所有的類裡出現的showarea()函式宣告之前加乙個關鍵字virtual:
在基類中的某成員函式被宣告為虛函式後,在之後的派生類中可以重新來定義它。
但定義時,其函式原型,包括返回型別、函式名、引數個數、引數型別的順序,都必須和基類中的原型完全相同。其實在上述修改後的示例**裡,只要在基類中顯式宣告了虛函式,那麼在之後的派生類中就需要用virtual來顯式宣告了,可以略去,因為系統會根據其是否和基類中虛函式原型完全相同來判斷是不是虛函式。因此,上述派生類中的虛函式如果不顯式宣告也還是虛函式。
最後對虛函式做幾點補充說明: (1)因為虛函式使用的基礎是賦值相容,
而賦值相容成立的條件是派生類之從基類公有派生而來。所以使用虛函式,
派生類必須是基類公有派生的;
(2)定義虛函式,不一定要在最高層的類中,而是看在需要動態多型性的幾個層次中的最高層類中宣告虛函式;
(3)雖然在上述示例**中main()主函式實現部分,我們也可以使用相應圖形物件和點運算子的方式來訪問虛函式,rectangcle.showarea(),但是這種呼叫在編譯時進行靜態聯編,它沒有充分利用虛函式的特性。只有通過基類物件來訪問虛函式才能獲得動態聯編的特性;
(4)乙個虛函式無論配公有繼承了多少次,它仍然是虛函式;
(5) 虛函式必須是所在類的成員函式,而不能是友元函式,也不能是靜態成員函式。因為虛函式呼叫要靠特定的物件類決定該啟用哪乙個函式;
(6)內聯函式不能是虛函式,因為內聯函式是不能在執行中動態確定其位置的即使虛函式在類內部定義,編譯時將其看作非內聯;
(7)建構函式不能是虛函式,但析構函式可以是虛函式;
如果在main()主函式中用new建立乙個派生類無名物件和定義乙個基類物件指標,並將無名物件的位址賦給基類物件指標時,當我們用delete運算子來撤銷無名物件時,系統只執行基類析構函式,而不執行派生類析構函式。比如:
因為在撤銷指標graph所指的派生類物件,在呼叫析構函式時,採用靜態聯編,只呼叫了graph類的析構函式。如果也想呼叫派生類rectangle類的析構函式的話,可將graph類的析構函式定義為虛析構函式。其定義的一般格式:
virtual ~類名() ;
雖然派生類的析構函式與基類的析構函式名字不同,但是如果將基類的析構函式定義為虛函式,由該基類派生而來的所有派生類的析構函式都自動成為虛函式。我們把上一示例中的graph類的析構函式前加上關鍵字virtual,那麼執行結果:
上述示例中用了虛函式後,會發現其實graph類(圖形類)中的虛函式的函式體根本沒有被用到過,就算被用到,該基類體現了圖形的抽象的概念,並不與具體事物相聯絡。所以基類中的虛函式也沒有實質性的功能。因此我們只需要在基類中留下乙個函式名,而具體的實現留給派生類去定義。在c++中就是用純虛函式來說明的。純虛函式的一般形式:
virtual 返回型別 函式名(形參表)=0;
這裡的"=0"並不是函式的返回值等於零,它只是起到形式上的作用,告訴編譯系統"這是純虛函式"。純虛函式不具備函式功能,不能被呼叫。
如果乙個類中至少有乙個純虛函式,那麼就稱該類為抽象類。所以上述中graph類就是抽象類。對於抽象類有以下幾個注意點:(1)抽象類只能作為其他類的基類來使用,不能建立抽象類物件;(2)不允許從具體類中派生出抽象類(不包含純虛函式的普通類);(3)抽象類不能用作函式的引數型別、返回型別和顯示轉化型別;(4)如果派生類中沒有定義純虛函式的實現,而只是繼承成了基類的純虛函式。那麼該派生類仍然為抽象類。一旦給出了對基類中虛函式的實現,那麼派生類就不是抽象類了,而是可以建立物件的具體類;
#include "stdafx.h"
#include #include class graph //抽象類
;graph::graph(double x,double y)
void graph::showarea()
return0;
}
C 之多型性與虛函式
物件導向程式設計中的多型性是指向不同的物件傳送同乙個訊息,不同物件對應同一訊息產生不同行為。在程式中訊息就是呼叫函式,不同的行為就是指不同的實現方法,即執行不同的函式體。也可以這樣說就是實現了 乙個介面,多種方法 從實現的角度來講,多型可以分為兩類 編譯時的多型性和執行時的多型性。前者是通過靜態聯編...
VC 之 多型性與虛函式
多型性是物件導向程式設計的關鍵技術之一。利用多型性技術,可以呼叫同乙個函式名的函式,實現完全不同的功能。若程式語言不支援多型性,不能稱為物件導向的語言。在c 中有兩種多型性 1 定義格式 虛函式是乙個類的成員函式,定義格式如下 virtual 返回型別 函式名 參數列 說明 2 通過虛函式實現多型性...
C 多型性與虛函式
物件導向程式設計中的多型性是指向不同的物件傳送同乙個訊息,不同物件對應同一訊息產生不同行為。在程式中訊息就是呼叫函式,不同的行為就是指不同的實現方法,即執行不同的函式體。也可以這樣說就是實現了 乙個介面,多種方法 從實現的角度來講,多型可以分為兩類 編譯時的多型性和執行時的多型性。前者是通過靜態聯編...