正文 第九章 虛函數與多態性(3 / 3)

注意,在一個抽象類中,並非所有函數都是純虛函數。

三、虛析構函數

例9.14通過一個例子,來說明什麼是虛析構函數。

通過例9.14將使讀者了解到,在繼承關係下以動態連接方式釋放對象占用的存儲空間時,將會發生什麼問題。

在函數destoryobj()中使用delete語句來調用類的析構函數,以釋放一個對象占用的存儲空間。程序的本意是希望函數destoryobj()可以像函數Funcobj()—樣,能夠自動判別基類指針實際指向的對象類型,以調用相對應的析構函數。但由程序的輸出結果可以發現編譯器並沒有這樣做。

可見函數destoryobj()並沒有達到動態連接的效果,因為析構函數沒有虛函數的性質。正確的做法是把基類的析構函數Parent()說明成虛函數。

隻要把基類的析構函數說明為虛函數,那麼派生類的析構函數也全為虛函數,無須使用關鍵字virtual指明。

這樣,當調用destoryobj()時,就可以根據基類指針實際指向的派生類對象,調用相對應的派生類的析構函數。

和創建派生類對象時構造函數的執行順序相反,撤消派生類對象時,析構函數的執行順序為:

1.派生類自身的析構函數

2.對象成員的析構函數(如果有的話)

3.基類的析構函數

在撤消一個派生類對象時,Visual C++會自動激活基類析構函數。所以把基類的析構函數說明成虛函數後,程序的輸出結果為:

執行缺省構造函數Parent()

執行缺省構造函數ChildA()

執行缺省構造函數Parent()

執行缺省構造函數ChildB()

執行ChildA::Func()執行ChildB::Func()destroytheobject

執行析構函數-ChildA()

執行析構函數-Parent()

執行析構函數-ChildB()

執行析構函數-Parent()

虛函數的觀念很簡單,但在麵向對象程序設計中卻是很重要的技巧,一般的原則是將繼承關係中的最基礎類(根類)的析構函數說明成虛函數。但是構造函數不能是虛函數,構造函數也沒有任何理由需要成為虛函數。

9.7舉例

例9.15類RoadVehicle播述交通工具的概念,它有兩個公有派生類Truck和Automo-bile。類Truck描述的是載貨卡車,類Automobile描述的是客車,它們除了具有一般交通工具的特征外,還有自己的特征。對該程序有如下說明:

1.類Automobile中的數據成員type記錄汽車的類型,它是一個枚舉類型常量,枚舉類型中的三個常童分別表示為:轎車(car)、公共汽車(bus)和轎貨汽車(wagon)。

2.基類RoadVehide中的成員函數Show()被說明為虛函數,在說明派生類的Show()函數時,無論是否使用關鍵字virtual,也是虛函數。

3.由於Show()函數是虛函數,所以在派生類中訪問基類的Show()函數,必須顯式調用虛函數:RoadVehicle::Show();

4.我們還定義了一個顯示交通工具信息的函數:ShowRoadVehicle(RoadVehicle*Robj);

該函數的參數是基類指針,我們通過傳遞派生類的對象的地址,根據基類指針實際指向的對象來調用相對應的虛函數。

5.本例的要點在於:虛函數的作用在派生類中顯式調用虛函數.通過基類指針訪問。

虛函數對該程序有如下說明:

1.在基類Shape中,成員函數Area()是一個純虛函數,因此,在派生類Circle和Rectangle中,成員函數Area()都是虛函數。

2.由於基類的Area()函數是一個純虛函數,所以它是一個抽象基類,這樣,在基類就不必給出Area()的函數定義。在派生類Circle和Rectangle中,都定義了自己的計算麵積的成員函數Area()。

3.對於抽象基類,不能創建它的對象,但是可以說明抽象基類的指針。我們在程序中提供了一個用來計算各類形狀總麵積的函數。