C++隨筆-類繼承::簡單繼承與多態繼承

繼承

什麼是繼承?個人的理解就是將基類的成員作爲派生類的成員,注意是作爲派生類的成員,即加入派生類的類作用域中,叫做繼承。作爲派生類的成員意味着可以直接通過派生類對象或者派生類方法對其進行訪問。

派生類對基類數據成員的繼承:將數據成員存儲至派生類中,作爲派生類的數據成員。

派生類對基類方法成員的簡單繼承:將方法提供給派生類,派生類繼承了基類方法的接口。

值得注意的是,繼承了基類方法的接口,表示方法參數(類型、數目等)並沒有發生改變,而與基類方法的區別在於,調用方法的對象不一樣了。原先是基類方法,所以使用基類對象可以對其進行調用;現在是派生類方法,使用的是派生類對象對其進行調用。當派生類沒有對繼承來的基類方法進行重新定義時(多態繼承的內容,還涉及到隱藏基類方法的問題,後面會介紹),派生類的方法和基類的同名方法所實現的功能、參數均是完全一致的。但是注意不可以認爲此時派生類實際上是通過基類對象進行該方法的調用,要充分理解繼承的含義,這個方法就是從基類繼承過來的,這個方法可以說是“存儲”在派生類中,因此且不看是否是繼承而來的方法,實際上就是調用派生類的成員方法。、

 

繼承鏈

基類、派生類的概念是相對的,某一個派生類可以是他的進一步派生類的基類;某一個基類可以是某一個類的派生類。由此,形成一個繼承鏈,並有相鄰基類等概念,用於描述在繼承鏈中的狀態(相鄰基類:某一個類在繼承鏈中與之相鄰的那個基類)。

 

多態繼承

前述提到派生類對基類方法的簡單繼承,但是如果要對方法的行爲進行更改,使得同一個名稱的方法在基類與派生類中不同,則需要對該方法進行相應的操作。通過使用兩種機制能夠實現該目的。

  • 派生類中對基類方法的重新定義
  • 使用虛函數

其中,這兩種機制最重要的是對基類方法的重新定義,而虛方法的使用,是爲了針對指針、引用下能正確地調用類方法而存在。

在派生類中對基類方法的重新定義

首先要明確,在派生類中對基類方法的重新定義與函數重載完全不同,雖然二者都是實現多態特徵的方式。在派生類中對基類方法進行重新定義時,會將初始繼承過來的基類方法進行隱藏。注意這個隱藏不代表不存在或者刪除,而是對一種可見性的描述。一旦對該基類方法進行重新定義,則在派生類作用域中,該方法名稱說表示的就是派生類中重新定義的類方法,而不再是基類中同名的類方法。同樣,通過派生類對象對該方法進行調用時,也只能調用該重新定義的屬於派生類的該名稱的方法。所以這裏的重新定義,更多體現的是一種overwrite覆蓋的含義。但是如果要在派生類作用域下調用屬於基類的該名稱類方法也不是不可以,只不過此時需要通過作用域解析符,通過基類的作用域調用屬於基類的該名稱的方法。

所以總結一下在派生類中重新定義基類方法帶來的多態繼承和簡單的類方法繼承的區別與聯繫:

  • 簡單的類方法繼承:可以理解爲將基類的該名稱的類方法加入到派生類作用域中,使其屬於派生類。而該方法的參數名稱等未發生改變,只不過this指針此時指向的是派生類對象,所以通過派生類對象調用此名稱的類方法,實際上就是在調用屬於派生類的類方法,和派生類對象中內嵌的基類對象並無關係。很純粹的繼承。
  • 重新定義:可以理解爲這麼兩個步驟@1.先在派生類中繼承基類中的該名稱方法,也就是將基類中的該名稱方法添加進派生類作用域中。 @2.重新在派生類中定義了一個與從基類中繼承的同名的類方法,而該在派生類中重新定義的類方法會將從基類繼承得到的同名類方法隱藏,從此在派生類或者通過派生類對象進行調用的該名稱方法都是這個在派生類中經過重新定義的方法。

對派生類中重新定義的基類方法進行重載

有一個情況值得注意:當基類中對某一個方法進行了重載,如果派生類中並沒有對其進行重新定義,而僅僅是簡單繼承了該基類方法,則在派生類中同樣具有該方法的重載,並且各個重載方法的參數與基類方法一致(這裏藉助了向上轉換特性)。但是如果一旦在派生類中,對該繼承的該基類方法進行了重新定義,則這個重新定義的屬於派生類的類方法將會把所有重載的基類的該名稱的方法隱藏,或者說是覆蓋,此時在派生類中將不再具有該類方法的重載。所以如果想要在派生類中重新定義該類方法,同時又希望能保持該類方法的多個重載版本,就需要在派生類中,對基類中的該方法的各個重載分別進行重新定義,這樣才能在派生類中對該名稱類方法進行重載。

虛方法(函數)

虛方法實際上是爲了向上轉換特性而準備的。在通過指針或者引用訪問類對象中的成員的情境下,未使用虛方法時,所訪問的類對象的類型是由指針或引用決定的;使用了虛方法後,所訪問的類對象的類型則是由指針或引用所指向、所引用的類型所決定的。正是由於向上轉換類型的存在,基類類型的指針或引用可以指向、引用基類類型的對象和派生類類型的對象。因此如果基類類型的指針、引用所指向、引用的對象類型是派生類類型,但是是通過指針、引用來對對象成員進行訪問,則此時訪問的將是基類對象的成員,而並非是所指向、引用的派生類對象的成員,這在某些情況下將會帶來影響。因此引入了虛方法,通過virtual關鍵字進行聲明。值得注意的是,虛方法只需要在基類中用virtual關鍵字進行聲明即可,經過基類virtual聲明過的虛方法繼承到派生類中時會自動聲明爲虛方法而無需再顯式使用virtual進行聲明(若要顯式進行聲明也可以)。

又正如前述,基類、派生類的概念是相對的,所以對於某一個基類的派生類而言,其也可以作爲一個基類衍生出派生類。所以在某一個派生類中,相對於基類新增加的方法也可以使用virtual聲明爲虛方法(注意,此時這個新添的方法需要顯式地使用virtual進行聲明,因爲此時派生類被視爲基類)。

虛方法是實現動態聯編的重要機制。

-虛方法(函數)的實現機制

在各個類對象中,會自動生成一個虛函數表vtbl,vtbl的成員是所屬類的所有虛方法的地址。基類與派生類中的虛函數表是相互獨立的。每一個基類對象(相對概念)都會維護一個虛函數表,並且被派生類所繼承。基類的vtbl被派生類繼承時,是將基類的vtbl獨立地複製給派生類的vtbl,並且如果在派生類中有對基類的虛函數進行重新定義,這會將重新定義的虛函數的地址寫入派生類的vtbl中。以此在繼承鏈中類推下去。要使用虛方法時,則會訪問所屬類的vtbl,訪問相應地址指向的虛函數。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章