C++ 的多態(tài)、虛函數(shù)、純虛函數(shù)介紹
一、多態(tài)
多態(tài)按字面的意思就是多種形態(tài)。當(dāng)類之間存在層次結(jié)構(gòu),并且類之間是通過繼承關(guān)聯(lián)時,就會用到多態(tài)。
C++ 多態(tài)意味著調(diào)用成員函數(shù)時,會根據(jù)調(diào)用函數(shù)的對象的類型來執(zhí)行不同的函數(shù)。
下面的實例中,基類 Shape 被派生為兩個類,如下所示:
#includeusing?namespace?std; ? class?Shape?{ ???protected: ??????int?width,?height; ???public: ??????Shape(?int?a=0,?int?b=0) ??????{ ?????????width?=?a; ?????????height?=?b; ??????} ??????int?area() ??????{ ?????????cout?<<?"Parent?class?area?:"?<<endl; ?????????return?0; ??????} }; class?Rectangle:?public?Shape{ ???public: ??????Rectangle(?int?a=0,?int?b=0):Shape(a,?b)?{?} ??????int?area?() ??????{? ?????????cout?<<?"Rectangle?class?area?:"?<<endl; ?????????return?(width?*?height);? ??????} }; class?Triangle:?public?Shape{ ???public: ??????Triangle(?int?a=0,?int?b=0):Shape(a,?b)?{?} ??????int?area?() ??????{? ?????????cout?<<?"Triangle?class?area?:"?<area(); ???//?存儲三角形的地址 ???shape?=?&tri; ???//?調(diào)用三角形的求面積函數(shù)?area ???shape->area(); ??? ???return?0; }
當(dāng)上面的代碼被編譯和執(zhí)行時,它會產(chǎn)生下列結(jié)果:
Parent?class?area Parent?class?area
導(dǎo)致錯誤輸出的原因是,調(diào)用函數(shù) area() 被編譯器設(shè)置為基類中的版本,這就是所謂的靜態(tài)多態(tài),或靜態(tài)鏈接?- 函數(shù)調(diào)用在程序執(zhí)行前就準(zhǔn)備好了。有時候這也被稱為早綁定,因為 area() 函數(shù)在程序編譯期間就已經(jīng)設(shè)置好了。
二、虛函數(shù)
但現(xiàn)在,讓我們對上述程序稍作修改,在 Shape 類中,area() 的聲明前放置關(guān)鍵字?virtual,如下所示:
class?Shape?{ ???protected: ??????int?width,?height; ???public: ??????Shape(?int?a=0,?int?b=0) ??????{ ?????????width?=?a; ?????????height?=?b; ??????} ??????virtual?int?area() ??????{ ?????????cout?<<?"Parent?class?area?:"?<<endl; ?????????return?0; ??????} };
修改后,當(dāng)編譯和執(zhí)行前面的實例代碼時,它會產(chǎn)生以下結(jié)果:
Rectangle?class?area Triangle?class?area
此時,在函數(shù)前加virtual,編譯器看的是指針的內(nèi)容,而不是它的類型。因此,由于 tri 和 rec 類的對象的地址存儲在 *shape 中,所以會調(diào)用各自的 area() 函數(shù)。
正如您所看到的,每個子類都有一個函數(shù) area() 的獨立實現(xiàn)。這就是多態(tài)的一般使用方式。有了多態(tài),您可以有多個不同的類,都帶有同一個名稱但具有不同實現(xiàn)的函數(shù),函數(shù)的參數(shù)甚至可以是相同的。
虛函數(shù)?是在基類中使用關(guān)鍵字?virtual?聲明的函數(shù)。在派生類中重新定義基類中定義的虛函數(shù)時,會告訴編譯器不要靜態(tài)鏈接到該函數(shù)。
我們想要的是在程序中任意點可以根據(jù)所調(diào)用的對象類型來選擇調(diào)用的函數(shù),這種操作被稱為動態(tài)鏈接,或后期綁定。
您可能想要在基類中定義虛函數(shù),以便在派生類中重新定義該函數(shù)更好地適用于對象,但是您在基類中又不能對虛函數(shù)給出有意義的實現(xiàn),這個時候就會用到純虛函數(shù)。
我們可以把基類中的虛函數(shù) area() 改寫如下:
?這個例子是虛函數(shù)的一個典型應(yīng)用,通過這個例子,也許你就對虛函數(shù)有了一些概念。它虛就虛在所謂“推遲聯(lián)編”或者“動態(tài)聯(lián)編”上,一個類函數(shù)的調(diào)用并不是在編譯時刻被確定的,而是在運行時刻被確定的。由于編寫代碼的時候并不能確定被調(diào)用的是基類的函數(shù)還是哪個派生類的函數(shù),所以被成為“虛”函數(shù)。
三、純虛函數(shù)
class?Shape?{ ???protected: ??????int?width,?height; ???public: ??????Shape(?int?a=0,?int?b=0) ??????{ ?????????width?=?a; ?????????height?=?b; ??????} ??????//?pure?virtual?function ??????virtual?int?area()?=?0; };
= 0 告訴編譯器,函數(shù)沒有主體,上面的虛函數(shù)是純虛函數(shù)
一、定義
純虛函數(shù)是在基類中聲明的虛函數(shù),它在基類中沒有定義,但要求任何派生類都要定義自己的實現(xiàn)方法。在基類中實現(xiàn)純虛函數(shù)的方法是在函數(shù)原型后加“=0”
virtual void funtion1()=0
二、引入原因
1、為了方便使用多態(tài)特性,我們常常需要在基類中定義虛擬函數(shù)。
2、在很多情況下,基類本身生成對象是不合情理的。例如,動物作為一個基類可以派生出老虎、孔雀等子類,但動物本身生成對象明顯不合常理。
為了解決上述問題,引入了純虛函數(shù)的概念,將函數(shù)定義為純虛函數(shù)(方法:virtual ReturnType Function()= 0;),則編譯器要求在派生類中必須予以重寫以實現(xiàn)多態(tài)性。同時含有純虛擬函數(shù)的類稱為抽象類,它不能生成對象。這樣就很好地解決了上述兩個問題。
聲明了純虛函數(shù)的類是一個抽象類。所以,用戶不能創(chuàng)建類的實例,只能創(chuàng)建它的派生類的實例。
純虛函數(shù)最顯著的特征是:它們必須在繼承類中重新聲明函數(shù)(不要后面的=0,否則該派生類也不能實例化),而且它們在抽象類中往往沒有定義。
定義純虛函數(shù)的目的在于,使派生類僅僅只是繼承函數(shù)的接口。
純虛函數(shù)的意義,讓所有的類對象(主要是派生類對象)都可以執(zhí)行純虛函數(shù)的動作,但類無法為純虛函數(shù)提供一個合理的缺省實現(xiàn)。所以類純虛函數(shù)的聲明就是在告訴子類的設(shè)計者,“你必須提供一個純虛函數(shù)的實現(xiàn),但我不知道你會怎樣實現(xiàn)它”。