1. 程式人生 > >c++ 多型 虛擬函式 解構函式 抽象類(轉)

c++ 多型 虛擬函式 解構函式 抽象類(轉)

1. 解構函式和虛解構函式
如果基類的解構函式是虛的,那麼它的派生類的解構函式都是虛的
這將導致:當派生類析構的時候,它的所有的基類的解構函式都將得到呼叫
否則,只調用派生類的解構函式(這可能導致基類的某些物件沒有得到釋放)
所以CObject類的解構函式是虛的,所有由它派生的類析構的時候一級一級的進行,不會造成記憶體洩漏。

無論基類的解構函式是否為虛解構函式. 基類的解構函式總是會被自動呼叫的;但是, 如果用基類指標去操作一個了派生類物件,如果不為虛就不能保證派生類的解構函式被呼叫。

2. 純虛解構函式

解構函式的純虛性唯一效果就是保證抽象類的例項化。

《Effective C++》中第14條條款的一部分,既是對虛解構函式的徹底理解,亦是對純虛解構函式作用的解釋。

在某些類裡宣告純虛解構函式很方便。純虛擬函式將產生抽象類——不能例項化的類(即不能建立此型別的物件)。有些時候,你想使一個類成為抽象類,但剛好又沒有任何純虛擬函式。怎麼辦?因為抽象類是準備被用做基類的,基類必須要有一個虛解構函式,純虛擬函式會產生抽象類,所以方法很簡單:在想要成為抽象類的類裡宣告一個純虛解構函式。

這裡是一個例子:
class awov {
public:
virtual ~awov() = 0; // 宣告一個純虛解構函式
};

這個類有一個純虛擬函式,所以它是抽象的,而且它有一個虛解構函式,所以不會產生解構函式問題。但這裡還有一件事:必須提供純虛解構函式的定義:

awov::~awov() {} // 純虛解構函式的定義

這個定義是必需的,因為虛解構函式工作的方式是:最底層的派生類的解構函式最先被呼叫,然後各個基類的解構函式被呼叫。這就是說,即使是抽象類,編譯器也要產生對~awov的呼叫,所以要保證為它提供函式體。如果不這麼做,連結器就會檢測出來,最後還是得回去把它添上。

3. 虛擬函式

【1】在基類用virtual宣告成員函式為虛擬函式。這樣就可以在派生類中重新定義此函式,為它賦予新的功能,並能方便地被呼叫。

【2】在派生類中重新定義此函式,要求函式名、函式(返回)型別、函式引數個數和型別與基函式的虛擬函式相同。如果在派生類中沒有對基類的虛擬函式重定義,則派生類簡單地繼承直接基類的虛擬函式。

【3】C++規定,當一個成員函式被宣告為虛擬函式後,其派生類中的同名函式(符合2中定義的函式)都自動成為虛擬函式。
【4】定義一個指向基類物件的指標變數,並使其指向同一類族中的某個物件。通過該指標變數呼叫此函式,此時呼叫的就是指標變數指向的物件的同名函式。
【5】只能用virtual宣告類的成員函式,使它成為虛擬函式,而不能將類外的普通函式宣告為虛擬函式。
【6】一個成員函式被宣告為虛擬函式後,在同一類族中的類就不能再定義一個非virtual的但與該虛擬函式具有相同引數(個數與型別)和函式返回值型別的同名函式。
【7】靜態成員函式不能是虛擬函式,因為靜態成員函式不受限於某個物件。
【8】inline函式不能是虛擬函式,因為inline函式是不能在執行中動態確定其位置的。即使虛擬函式在類的內部定義,編譯時,仍將其視為非inline的。
【5】使用虛擬函式,系統要有一定的空間開銷。當一個類帶有虛擬函式時,編譯器會為該類構造一個虛擬函式表(virtual function tanle,vtable),它是一個指標陣列,存放每個虛擬函式的入口地址。
4. 純虛擬函式


一個函式宣告為純虛後,純虛擬函式的意思是:我是一個抽象類!不要把我例項化!純虛擬函式用來規範派生類的行為,實際上就是所謂的“介面”。它告訴使用者,我的派生類都會有這個函式。
virtual void show()=0;//純虛擬函式

這裡將show()宣告為純虛擬函式(pure virtual function)。純虛擬函式是在宣告虛擬函式時被“初始化”為0的虛擬函式。
宣告純虛擬函式的一般形式為,
virtual 函式型別 函式名(引數列表)=0;

純虛擬函式沒有函式體;最後的“=0”並不代表函式返回值為0,它只起形式上的作用,告訴編譯器“這是純虛擬函式”;這個一個宣告語句,最後有分號。
宣告純虛擬函式是告訴編譯器,“在這裡聲明瞭一個虛擬函式,留待派生類中定義”。在派生類中對此函式提供了定義後,它才能具備函式的功能,可以被呼叫。
純虛擬函式的作用是在基類中為其派生類保留了一個函式的名字,以便派生類根據需要對它進行定義。
如果在一個類中聲明瞭純虛擬函式,而在其派生類中沒有對該函式定義,則該函式在派生類中仍為純虛擬函式。

1.虛擬函式和純虛擬函式可以定義在同一個類(class)中,含有純虛擬函式的類被稱為抽象類(abstract class),而只含有虛擬函式的類(class)不能被稱為抽象類(abstract class)。
2. 虛擬函式可以被直接使用,也可以被子類(sub class)過載以後以多型的形式呼叫,而純虛擬函式必須在子類(sub class)中實現該函式才可以使用,因為純虛擬函式在基類(base class)
只有宣告而沒有定義。

3. 虛擬函式和純虛擬函式都可以在子類(sub class)中被過載,以多型的形式被呼叫。

4.虛擬函式和純虛擬函式通常存在於抽象基類(abstract base class -ABC)之中,被繼承的子類過載,目的是提供一個統一的介面。

5.虛擬函式的定義形式:virtual {method body}
純虛擬函式的定義形式:virtual { } = 0;
在虛擬函式和純虛擬函式的定義中不能有static識別符號,原因很簡單,被static修飾的函式在編譯時候要求前期bind,然而虛擬函式卻是動態繫結(run-time bind),而且被兩者修飾的函式生命週期(life recycle)也不一樣。
6. 如果一個類中含有純虛擬函式,那麼任何試圖對該類進行例項化的語句都將導致錯誤的產生,因為抽象基類(ABC)是不能被直接呼叫的。必須被子類繼承過載以後,根據要求呼叫其子類的方法。

5 純抽象類

從C++的角度來看,一個抽象類和一個介面之間沒有任何區別。有時,我們習慣使用“純抽象類”這個詞來表示某個類僅僅只含有純虛擬函式(不包含任何資料成員),它是抽象類的最常見的形式。

使用純抽象類有什麼好處?最明顯的例子就是“多介面、單實現”,這是一種很常見的情況。

在C++中加入了純虛擬函式的概念,一個純虛擬函式必須被其派生類重寫。藉助此概念,你可以在一個C++類中通過將其成員函式宣告為純虛擬函式的方法表明該類是一個純介面類。從那以後,我就一直強調在C++中,有一種主要的使用類的方法就是讓該類不包含任何狀態,而僅僅作為一個介面。

6 抽象類

將不用來定義物件而只作為一種基本型別用作繼承的類,稱為抽象類(abstract class),由於它常用作基類,通常稱為抽象基類。凡是包含純虛擬函式的類都是抽象類。
如果在派生類中沒有對所有的純虛擬函式進行定義,則此派生類仍然是抽象類,不能用來定義物件。
可以定義指向抽象類資料的指標變數。當派生類成為具體類後,就可以用這個指標指向派生類物件,然後通過該指標呼叫虛擬函式。

帶有純虛擬函式的類稱為抽象類。抽象類是一種特殊的類,它是為了抽象和設計的目的而建立的,它處於繼承層次結構的較上層。抽象類是不能定義物件的,在實際中為了強調一個類是抽象類,可將該類的建構函式說明為保護的訪問控制權限。抽象類的主要作用是將有關的組織在一個繼承層次結構中,由它來為它們提供一個公共的根,相關的子類是從這個根派生出來的。抽象類刻畫了一組子類的操作介面的通用語義,這些語義也傳給子類。一般而言,抽象類只描述這組子類共同的操作介面,而完整的實現留給子類。抽象類只能作為基類來使用,其純虛擬函式的實現由派生類給出。如果派生類沒有重新定義純虛擬函式,而派生類只是繼承基類的純虛擬函式,則這個派生類仍然還是一個抽象類。如果派生類中給出了基類純虛擬函式的實現,則該派生類就不再是抽象類了,它是一個可以建立物件的具體類了。