1. 程式人生 > >13.多型、虛擬函式

13.多型、虛擬函式

多型與虛擬函式:

什麼是虛擬函式:

用virtual關鍵字宣告的函式都是虛擬函式。虛擬函式存在的唯一目的,就是為了實現多型(動態繫結/執行時繫結)。

virtual 關鍵字只在類定義中的成員函式宣告處使用,不能在類外部寫成員函式體時使用。所有派生類中具有覆蓋關係的同名函式都將自動成為虛擬函式。

靜態成員函式不能是虛擬函式。

再說簡單點:有virtual宣告的函式都是虛擬函式。如果沒有virtual,那麼派生類中的同名函式會把基類中的同名函式隱藏了。如果有,那麼派生類的同名函式(同參同返回)會在虛擬函式表中將基類的同名函式覆蓋掉。

什麼是多型:

        多型性可以簡單概括為“一個介面,多種行為”。

動態繫結(執行階段)是多型的基礎。

基類指標或引用,指向一系列的派生類物件, 呼叫派生類物件的同名覆蓋方法(也就是那個與基類虛擬函式同名同參同返回的函式),指標指向誰,就會呼叫誰的方法。

多型分為兩種:

        (1)編譯時多型(也叫靜態的多型):主要通過函式的過載和模板來實現。

        (2)執行時多型(也叫動態的多型):主要通過虛擬函式來實現。

覆蓋:

       基類的某個成員函式為虛擬函式,派生類又定義一個成員函式,函式名、形參、返回型別都與基類的成員函式相同。那麼就會用派生類的函式覆蓋掉基類的虛擬函式

說一下多型是如何實現的:

在程式碼編譯階段產生一張虛擬函式表

vftable。執行的時候,會載入到資料段。

如果成員裡有虛擬函式,在成員變數裡只會多一個虛擬函式指標(物件的前4個位元組),指向虛擬函式表vftable()存放虛擬函式地址。虛擬函式的個數只會影響表的大小。不會影響物件的大小。

基類有虛擬函式,派生類如果有同名同參數列表同返回值的函式,派生類的函式會自動變成虛擬函式,在虛擬函式表中派生類會覆蓋掉原有的函式。

動態繫結與靜態繫結:

在使用的時候,用一個基類的指標指向了一個派生類的物件,如果呼叫這個虛擬函式,會先找虛擬函式指標,再找虛擬函式表,再再找虛擬函式地址。而這個繫結過程就是動態繫結(執行時期)。

如果你不用指標呼叫,而是用物件本身呼叫函式,不論是否是虛擬函式,都是靜態繫結

(編譯時期)。

用指標呼叫如果是虛擬函式,指標識別的就是執行時期的型別;如果呼叫的是一般的函式,指標識別就是在編譯時期。

純虛擬函式:

一般情況下,基類是不希望定義物件的。基類只是為了將共有的屬性統一起來。

為了實現這一目的:在基類提供的一個虛擬函式,為所有派生類提供統一的虛擬函式介面,具體實現讓派生類自己去重寫的。

virtual void show() = 0;  // 在虛擬函式後面加   =  0    就是純虛擬函式,不用去實現。

純虛擬函式實際上是不存在的,引入純虛擬函式就是為了便於實現多型。

擁有純虛擬函式的類叫做抽象類。抽象類不能例項化。一般基類都應該實現為抽象類。

那麼問題來了:

1、基類在沒有更多方法的時候,把誰實現成純虛擬函式呢?-------->  解構函式

首先明確一個函式想要成為虛擬函式     1、它得有地址;2、得依賴物件

1)建構函式能不能是虛擬函式?

建構函式不依賴物件,建構函式執行完才有物件,有物件才有虛擬函式指標,所以不能是虛擬函式。

2)static成員方法能不能是虛擬函式?  virtual static

靜態函式也不依賴物件,可以直接使用類名呼叫,也不能是虛擬函式。

3)inline函式能不能是虛擬函式?  =>  virtual inline  

行內函數直接在程式內展開,沒有地址,無法往虛擬函式表放。也不能是虛擬函式。

4)解構函式能不能實現成虛擬函式?

解構函式依賴物件,有地址。可以寫成虛擬函式