2022年3月28日 星期一

[C++ / C# ] abstract / virtual function - 使用C#與C++的比較

先前比較常碰C#,因此抽象函式(abstract function)也是常會碰到的名詞,

但現在比較常碰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" => 子類別一定要覆寫,不可有函式內容。

沒有留言:

張貼留言

社會新鮮人如何投資?

我的觀點是,在 沒有很多 本錢 的情況下, 別寄望每個月幾千元放到股票或者最近很夯的高股息ETF就能讓你致富, 先投資自己,讓自己的本業收入提高吧。