但現在比較常碰C++,碰到virtual時我以為和C#是一樣的,代表繼承後可供修改,
但為什麼後面還要加個 = 0 呢?
一查才發現原來這邊加上 = 0 比較像是C#中的抽象函式(abstract function),
讓我們來比較一下。
抽象類別
我想大家都知道,存有抽象函式(abstract function)的類別就會被稱作抽象類別,
這裡是第一個C++和C#不同的地方。
然而C++的抽象類別宣告上並不需要加上abstract關鍵字,
只要類別中帶有抽象函式(abstract function)即可,
但C#需要加上關鍵字abstract,如下所示:
[不過我試著不加上關鍵字,編譯卻通過了 ??? ]
抽象函式宣告
C#中如果沒有定義實體,需要子類別去實現,我們就會定義成抽象函式(abstract function)。
一樣利用關鍵字abstract加在function名稱前面來宣告,如下所示:
記得一定要是public,否則子類別無法看到。[看不到是要怎麼覆寫呢?]
而繼承AbstractClassDemo類別的子類別NormalClass,
如果沒有實作abstractFunc()的話,就會出現以下問題:
因此加上abstract關鍵字的抽象函式(abstract function),會強制子類別去實現,
[補充]如果子類別NormalClass這時候還不想實現繼承來的抽象函式(abstract function),
則可以繼續加上abstract在function宣告前面,讓繼承NormalClass子類去實現,
當然這時候NormalClass也必須加上abstract在class宣告前面,也成為了抽象類別。
那C++的抽象函式(abstract function)該怎麼宣告呢?
在C++中,宣告後不實作內容的函式,稱作"純虛擬函式(純虛函式)",
這邊可能會讓人搞混的地方是,C++在這裡宣告function時還是有使用virtual關鍵字,
[單純只使用virtual關鍵字的function是虛擬函式,不是抽象函式!]
但在function宣告的最後面必須加上 =0 ,如下所示:
這邊要特別提醒,其實C#的抽象函式(abstract function)本身就是要被覆寫的,
所以已經帶有了virtual的意義 - "供子類別覆寫",
只是C#把沒有實作內容的虛擬函式(virtual function)又再定義成另一個關鍵字abstract,
用以區別是否有實作內容。
這邊把C#與C++的兩個抽象函式(abstract function)放在一起比較:
C#:
C++:
覆寫抽象函式 - 使用override
在C#中,如果要覆寫虛擬函式(virtual function)或實作抽象函式(abstract function),
都必須在要覆寫的function定義前加上override關鍵字,如下:
當我在C++也想這麼做的時候,發現原來原本Override語法在C++中是在版本11時才有的,
原先只需要宣告的和父類別的virtual function宣告相同即可,編譯器自動會幫你辨識是否覆寫。
父類中的方法若被宣告為virtual,子類別重新定義方法時自然就會被歸類為 virtual,
因此子類別宣告時可以自行選擇是否把 virtual關鍵字補上。
但這樣會衍生很多問題,假設你不小心參數少了一個,參數型別錯誤等等,
編譯時都不會發生問題,因為編譯器將會認為這是一個新的function,編譯上是沒問題的,
例如下面宣告同名的virtual function,但參數多一個int,如下:
但當你想要建立子類別物件時就會發生問題,
由於你沒有實作父類別的virtual function(純虛函式),
也就是因為不小心宣告錯誤導致編譯器認為那是一個新function,
因此該子類別也會被視為抽象類別,也就不能實作了,如下所示:
這種執行時期才會發生的問題,是最可怕的,我們希望在編譯期間就能發現問題,
因此這時候才有override關鍵字來輔助檢查。
加上了override之後,編譯時期就會告訴你這個function並不會覆寫父類別的virtual function,
可以比較放心的確保該覆寫的function都有覆寫到。
比較安全的 C++ 虛擬函式寫法:C++11 override 與 final
virtual / abstract function 簡單比較
C#:
virtual => 可供子類別修改,可以擁有函式內容,不修改的話就延用父類別的內容。
abstract => 子類別一定要覆寫,不可有函式內容。
C++:
virtual => 可供子類別修改,可以擁有函式內容,不修改的話就延用父類別的內容。
virtual + "=0" => 子類別一定要覆寫,不可有函式內容。
沒有留言:
張貼留言