C 多型性和繼承性

2021-08-04 10:16:55 字數 2637 閱讀 4238

這篇文章是6年前的,

從自己的qq(632846506)日誌中移過來的。

前段時間有人突然問我c++

多型性的理論知識,一下子把我問蒙了,用c++近十年,讓我一下子回答多型性的理論。我答不上來,只是簡單的回答了下「多型性主要用在函式介面上,主要是虛函式和純虛函式

的使用,基類中的函式可以在不同子類中有不同的實現形式

」。汗~~~~,用了近十年卻講不出具體的理論知識來,汗顏啦~

c++的多型性:

指相同物件收到不同訊息或不同物件收到相同訊息時產生不同的實現動作。c++支援兩種多型性:編譯時多型性,執行時多型性。其中: 

a、編譯時多型性:通過過載函式實現; 

b、執行時多型性:通過虛函式實現。 

c++多型性是通過虛函式來實現的,虛函式允許子類重新定義成員函式(在子類裡重新定義父類的相關函式)。其多型性可以簡單地概括為「乙個介面(基類),多種方法(子類)」,或者說虛函式在基類中只定義不實現,在子類中定義同一函式且實現。

c++裡有重寫(覆蓋)

和過載兩個概念:

重寫發生在繼承關係中的子類中,當子類要修改從父類中繼承到的函式時,就可以在子類中宣告乙個與父類同名、同參、同返回值型別的函式,覆蓋又叫重寫。重寫有重寫成員函式和重寫虛函式,重寫虛函式的才是體現了c++多型性;

過載則是允許有多個同名的函式,但這些函式要麼滿足引數列表不同,要麼滿足引數個數不同,要麼滿足引數型別不同,或者兩者都不同。所以過載並不涉及繼承的問題。由於過載的引數不同,在呼叫的時候已經明確了要使用哪乙個函式操作和返回值,因此過載是屬於非執行時的,也就是執行之前就已經明確的。

多型的優勢:其封裝可以使得**模組化,繼承可以擴充套件已存在的**,目的都是為了**重用。而多型的目的則是為了介面重用。也就是說,不論傳遞過來的究竟是那個類的物件,函式都能夠通過同乙個介面呼叫到適應各自物件的實現方法。

最常見的用法就是宣告基類的指標,利用該指標指向任意乙個子類物件,呼叫相應的虛函式,可以根據指向的子類的不同而實現不同的方法。如果沒有使用虛函式的話,即沒有利用c++多型性,則利用基類指標呼叫相應的函式的時候,將總被限制在基類函式本身,而無法呼叫到子類中被重寫過的函式。

因為沒有多型性,函式呼叫的位址將是一定的,而固定的位址將始終呼叫到同乙個函式,這就無法實現乙個介面,多種方法的目的了。

簡單說明下純虛函式:純虛函式是在基類中宣告的虛函式,它在基類中沒有定義,但要求任何派生類都要定義自己的實現方法。在基類中實現純虛函式的方法是在函式原型後加「=0」,如virtual void funtion()=0 。

為什麼引入純虛函式:

一是為了方便使用多型特性,如常常根據需要在基類中定義虛函式;

二是在很多情況下,基類本身生成物件是不合情理的。

將函式定義為純虛函式(方法:virtual returntype function()= 0;),則編譯器要求在派生類中必須予以重寫以實現多型性。同時含有純虛函式的類稱為抽象類,它不能生成物件。這樣就很好地解決了上述兩個問題。

其他相關說明:

虛函式:是在基類中被宣告為virtual,並在派生類中重新定義的成員函式,可實現成員函式的動態

覆蓋(override)

抽象類:

包含純虛函式的類稱為抽象類。由於抽象類包含了沒有定義的純虛函式,所以

不能定義抽象類的物件

。舉例說明下:

class

a    

virtual

void

fun2()  

};  

class

b : 

public

a    

void

fun2()  

};  

voidmain(

void

)    

第乙個p->fun1()和p->fun2(),輸出結果就是a0、a1,其本身是父類指標,指向的又是父類物件,呼叫的都是父類本身的函式。

第二個輸出結果就是a0、b1。p->fun1()是父類指標指向子類物件,體現了c++多型的用法,p->fun1(),由於指標是個父類指標,指向是乙個固定偏移量的函式,因此指向的就是父類fun1()函式的**,因此輸出的結果還是1。p->fun2()指標是父類指標,指向的fun2是乙個虛函式,由於每個虛函式都有乙個虛函式列表,此時p呼叫fun2()並不是直接呼叫函式,而是通過虛函式列表找到相應的函式的位址,因此根據指向的物件不同,函式位址也將不同,這裡將找到對應的子類的fun()函式的位址,因此輸出的結果也會是子類的結果4。

那下列語句輸出什麼呢?

b *ptr = (b *)&a;  

ptr->fun1();  

ptr->fun2();

這是乙個用子類的指標去指向乙個強制轉換為子類位址的父類物件。結果,這兩句呼叫的輸出結果是3,2。

由於b是子類指標,雖然被賦予了父類物件位址,但是ptr->fun1()在呼叫的時候,由於位址偏移量固定,偏移量是子類物件的偏移量,於是即使在指向了乙個父類物件的情況下,還是呼叫到了子類的函式,儘管有時可能從始到終都沒有子類物件的例項化出現。

而ptr->fun2()的呼叫,還是因為c++多型性的原因,由於指向的是乙個父類物件,通過虛函式列表的引用,找到了父類中fun()函式的位址,因此呼叫了父類的函式。由此可見多型性的強大,可以適應各種變化,不論指標是父類的還是子類的,都能找到正確的實現方法。

繼承性就不介紹了,顧名思義,子類繼承父類/子類繼承基類。

C 的封裝性 繼承性和多型性概念

封裝 encapsulation 封裝就是將抽象得到的資料和行為 或功能 相結合,形成乙個有機的整體,也就是將資料與運算元據的源 進行有機的結合,形成 類 其中資料和函式都是類的成員。封裝的目的是增強安全性和簡化程式設計,使用者不必了解具體的實現細節,而只是要通過 外部介面,一特定的訪問許可權來使用...

C 類和物件特性(繼承性,封裝性,多型性)

基類 派生類定義及其基本定義 當建立乙個新的類時,可以指定它繼承已有的類的成員。這兩個類分別被稱為基類,派生類。繼承,就是將基類中public和protected中的成員,全部或分別,繼承到派生類中的private protected public中,單個或多個部分中 派生類可以從乙個或多個基類繼承...

物件導向 封裝性 繼承性 多型性總結

二 繼承性 三 多型性 封裝性就是將物件內部的複雜性隱藏起來,只是對外部公開簡單的介面,便於外界呼叫,從而提高系統的可擴充套件性 可維護性。通俗的說,就是把該隱藏的隱藏起來,該暴露的暴露出來。將類的屬性設為私有的 private 並提供set或者get方法。此時,針對屬性就體現了封裝性。不對外暴露的...