C++子類繼承抽象類不實現虛方法
在閱讀UE4原始碼的時候發現一個有趣的問題,
UnrealEngine/SceneViewExtension.h-97-ISceneViewExtension
UnrealEngine/SceneViewExtension.h-230-FSceneViewExtensionBase
ISceneViewExtension
是一個抽象類,但是它的子類FSceneViewExtensionBase
並沒有實現它的純虛擬函式,以前學習的時候,純虛擬函式的規則是派生類必須去override,所有沒有得到一個合理的解釋。
class ENGINE_API FSceneViewExtensionBase : public ISceneViewExtension, public TSharedFromThis<FSceneViewExtensionBase, ESPMode::ThreadSafe> { };
有如下解釋:
C++規定,當一個成員函式被宣告為虛擬函式後,其派生類中的同名函式都自動成為虛擬函式。因此在派生類重新宣告該虛擬函式時,可以加virtual,也可以不加,但習慣上一般在每一層宣告該函式時都加virtual,使程式更加清晰。如果在派生類中沒有對基類的虛擬函式重新定義,則派生類簡單地繼承其直接基類的虛擬函式。
下面複製一份虛擬函式詳解的文章,方便記錄學習:
純虛擬函式
首先:強調一個概念
定義一個函式為虛擬函式,不代表函式為不被實現的函式。
定義他為虛擬函式是為了允許用基類的指標來呼叫子類的這個函式。
定義一個函式為純虛擬函式,才代表函式沒有被實現。
定義純虛擬函式是為了實現一個介面,起到一個規範的作用,規範繼承這個類的程式設計師必須實現這個函式。
定義
純虛擬函式也可以叫抽象函式,一般來說它只有函式名、引數和返回值型別,不需要函式體。
純虛擬函式是一種特殊的虛擬函式,它的一般格式如下(C++格式):
struct Abstract {
virtual void f() = 0; // pure virtual
}; // "Abstract" is abstract
在許多情況下,在基類中不能對虛擬函式給出有意義的實現,而把它宣告為純虛擬函式,它的實現留給該基類的派生類去做。這就是純虛擬函式的作用。
引入原因
1、為了方便使用多型特性,我們常常需要在基類中定義虛擬函式。
2、在很多情況下,基類本身生成物件是不合情理的。例如,動物作為一個基類可以派生出老虎、孔雀等子類,但動物本身生成物件明顯不合常理。
為了解決上述問題,引入了純虛擬函式的概念,將函式定義為純虛擬函式(方法:virtual ReturnType Function()= 0;) ,則編譯器要求在派生類中必須予以重寫 以實現多型性。同時含有純虛擬函式的類稱為抽象類,它不能生成物件 。這樣就很好地解決了上述兩個問題。
聲明瞭純虛擬函式的類是一個抽象類。所以,使用者不能建立類的例項,只能建立它的派生類的例項。
純虛擬函式最顯著的特徵
它們必須在繼承類中重新宣告函式(不要後面的=0,否則該派生類也不能例項化),而且它們在抽象類中往往沒有定義。
定義純虛擬函式的目的在於,使派生類僅僅只是繼承函式的介面。
純虛擬函式的意義,讓所有的類物件(主要是派生類物件)都可以執行純虛擬函式的動作,但類無法為純虛擬函式提供一個合理的預設實現。所以類純虛擬函式的宣告就是在告訴子類的設計者,"你必須提供一個純虛擬函式的實現,但我不知道你會怎樣實現它" 。
何時使用純虛擬函式?
(1)當想在基類中抽象出一個方法,且該基類只做能被繼承,而不能被例項化;
(2)這個方法必須在派生類(derived class)中被實現;
如果滿足以上兩點,可以考慮將該方法申明為pure virtual function.
相似概念
抽象類的介紹
抽象類是一種特殊的類,它是為了抽象和設計的目的為建立的,它處於繼承層次結構的較上層。
(1)抽象類的定義:稱帶有純虛擬函式的類為抽象類。
(2)抽象類的作用:抽象類的主要作用是將有關的操作作為結果介面組織在一個繼承層次結構中,由它來為派生類提供一個公共的根,派生類將具體實現在其基類中作為介面的操作。所以派生類實際上刻畫了一組子類的操作介面的通用語義,這些語義也傳給子類,子類可以具體實現這些語義,也可以再將這些語義傳給自己的子類。
(3)使用抽象類時注意:
抽象類只能作為基類來使用,其純虛擬函式的實現由派生類給出。如果派生類中沒有重新定義純虛擬函式,而只是繼承基類的純虛擬函式,則這個派生類仍然還是一個抽象類 。如果派生類中給出了基類純虛擬函式的實現,則該派生類就不再是抽象類了,它是一個可以建立物件的具體的類。
抽象類是不能定義物件的。
2.虛擬函式的介紹
(1)虛擬函式的定義:是在基類中被宣告為virtual,並在派生類中重新定義的成員函式,可實現成員函式的動態過載。
(2)虛擬函式的使用方法:
a.在基類用virtual宣告成員函式為虛擬函式。這樣就可以在派生類中重新定義此函式,為它賦予新的功能,並能方便地被呼叫。在類外定義虛擬函式時,不必再加virtual。
b.在派生類中重新定義此函式,要求函式名、函式型別、函式引數個數和型別全部與基類的虛擬函式相同,並根據派生類的需要重新定義函式體。
C++規定,當一個成員函式被宣告為虛擬函式後,其派生類中的同名函式都自動成為虛擬函式。因此在派生類重新宣告該虛擬函式時,可以加virtual,也可以不加,但習慣上一般在每一層宣告該函式時都加virtual,使程式更加清晰。如果在派生類中沒有對基類的虛擬函式重新定義,則派生類簡單地繼承其直接基類的虛擬函式。
c.定義一個指向基類物件的指標變數,並使它指向同一類族中需要呼叫該函式的物件。
d.通過該指標變數呼叫此虛擬函式,此時呼叫的就是指標變數指向的物件的同名函式。
通過虛擬函式與指向基類物件的指標變數的配合使用,就能方便地呼叫同一類族中不同類的同名函式,只要先用基類指標指向即可。如果指標不斷地指向同一類族中不同類的物件,就能不斷地呼叫這些物件中的同名函式。
需要說明;有時在基類中定義的非虛擬函式會在派生類中被重新定義,如果用基類指標呼叫該成員函式,則系統會呼叫物件中基類部分的成員函式;如果用派生類指標呼叫該成員函式,則系統會呼叫派生類物件中的成員函式,這並不是多型性行為(使用的是不同型別的指標),沒有用到虛擬函式的功能。
總結
1、純虛擬函式宣告如下:virtual void funtion1()=0;
純虛擬函式一定沒有定義 ,純虛擬函式用來規範派生類的行為,即介面。包含純虛擬函式的類是抽象類,抽象類不能定義例項,但可以宣告指向實現該抽象類的具體類的指標或引用。
2、虛擬函式宣告如下:virtual ReturnType FunctionName(Parameter)
虛擬函式必須實現 ,如果不實現,編譯器將報錯,錯誤提示為:
error LNK*** *: unresolved external symbol "public: virtual void __thiscall ClassName::virtualFunctionName(void)"
3、對於虛擬函式(不是純虛擬函式)來說,父類和子類都有各自的版本。由多型方式呼叫的時候動態繫結。
4、實現了純虛擬函式的子類,該純虛擬函式在子類中就變成了虛擬函式,子類的子類即孫子類可以覆蓋該虛擬函式,由多型方式呼叫的時候動態繫結 。
5、虛擬函式是C++中用於實現多型(polymorphism)的機制(重要) 。核心理念就是通過基類訪問派生類定義的函式。
6、在有動態分配堆上記憶體的時候,解構函式必須是虛擬函式,但沒有必要是純虛的。
7、友元不是成員函式,只有成員函式才可以是虛擬的 ,因此友元不能是虛擬函式。但可以通過讓友元函式呼叫虛擬成員函式來解決友元的虛擬問題。
8、解構函式應當是虛擬函式,將呼叫相應物件型別的解構函式,因此,如果指標指向的是子類物件,將呼叫子類的解構函式,然後自動呼叫基類的解構函式 。
相關參考
virtual 函式說明符 - cppreference.com
Derived classes - cppreference.com
【C++學習筆記】 純虛擬函式詳解,絕不純屬虛構! - 沐歌愛程式設計 - 部落格園 (cnblogs.com)