【虛擬解構式 virtual destructor】
侯捷 jjhou@ccca.nctu.edu.tw
1999.10.09 第一次發表於
清大.楓橋驛站(140.114.87.5).電腦書訊版(Computer/CompBook)
本文將於日後整理於 侯捷網站/電腦散文/散文1999
侯捷網站:www.jjhou.com
----------------------------------------------------------------
●大陸讀者來函
(註:兩案的計算機術語多有不同,我已轉為英文表達)
寄件者: yhd <yd2000@263.net>
收件者: jjhou@cca.nctu.edu.tw <jjhou@ccca.nctu.edu.tw>
日期: 1999年9月12日 PM 05:12
主旨: 求救(簡體中文)
> 侯老師:
> 您好,我想請教一個問題。我拜讀了《深入淺出 WINDOWS MFC
> 程序設計》(華中理工大學出版社),收益非淺,尤其是書中
> 例子的講解,是我見過最精彩的。但是在Scribble例子中,
> 你只講了在文件檔中使用一個 CStroke﹐我想如果還有其他如
> CRect、CLine等,在 CScribbleDoc 中的 list 物件該怎樣定義呢?
> 我編寫了一個程序,所有繪圖類別都從一個叫 CBaseElement
> 的 class 繼承而來,CBaseElement 從 CObject 繼承而來,在
> CBaseElement 中定義了一些 virtual function。在 DOC 中
> 定義 CTypedPtrList<CObList,CBaseElement*>m_elementList
> 的物件指標。但是我發現一個問題﹐就是在刪除這些繪圖物件時,
> 由於使用的是一個 CBaseElement base class 指標,它不會呼叫
> derived class 的 destructor。應怎樣辦呢?望您百忙之中能
> 抽空回答我!無論怎樣,都謝謝您和您的書給我的幫助。謝謝!
> 海棟
> 99年9月11日
●永遠的習題
我在元智開了四個學期的「Object Oriented Windows Programming,
using MFC」,使用《深入淺出 MFC》做教材。每學期只有一個作業,
就是把 Scribble 範例改寫為一個繪圖程式。改得多改得好,分數高。
改得少改得不好,分數低。改不動,不及格。
有些同學只是把 Scribble 的畫筆加上顏色選擇。這種程式一律
60 分。你得到了學分,但是喪失學習 OO 一個很棒的機會。有些
同學表現很好,抓住精髓,使用 polymorphism 來完成目標。
●正確的改法
海棟先生的改法是正確的。把原本的:
CTypedPtrList<CObList,CStroke*>m_elementList;
改為:
CTypedPtrList<CObList,CBaseElement*>m_elementList;
而讓 CBaseElement 繼承 CObject(以保有 MFC 的 Serializable、
dynamic creation、RTTI 性質),又讓所有繪圖用的 classes
(如 CRectangle、CLine...)繼承 CBaseElement。
當程式 UI 部份處理了繪圖命令之後,程式的資料結構部份
便應該以 CBaseElement* 型別 new 出一個具體形狀
(例如 CRectangle):
CBaseElement* pobj = new CRectangle; // polymorphically
然後接受座標資料的輸入,然後加入 m_elementList 之中。
●海棟的問題
海棟先生的問題是,每次要 delete 一個繪圖物件時,從 list 中
取出指標,然後做 delete 動作。結果,由於指標的型別是
CBaseElement*,所以只會喚起 CBaseElement 的 destructor。
即使該繪圖物件其實是個 CRectangle object,CRectangle 的
destructor 並不會被喚起。
這當然是不好的。
●虛擬解構式(virtual destructor)
解決之道很簡單,把 CBaseElement 的 destructor 宣告
為 virtual 就好了。是的,virtual destructor 就是用來
解決這種問題。
拙作《多型與虛擬》p.59 小節講的便是這個主題。
(此書並無大陸簡體版。我本打算 2/e 再授權給大陸。
不過現在已經暫時封筆)
Effective C++ (Scott Meyers/Addison Wesley)書中
item 14 : Make destructor virtual in base classes.
講的也是這個主題。
●工業強度
你在改寫 Scribble 範例時想到上述問題,大約是因為兩種情況:
(1) 該程式本來就有一個 [delete all] 選單命令,為了
讓它可以正常運行,你必須考慮對 list 中的所有物件一一
做正確的 delete 動作。
(2) 你更進一步想要讓使用者「有選擇性地」刪除某個繪圖物件。
(2) 的學問可就大了。能夠讓使用者以滑鼠在螢幕上「精準而
快速回應」地點選某個繪圖物件,是件不容易的事。具工業強度!
這種情況從 CAD 軟體開始發展以來,就存在。
--- then end
--
※ Origin: 楓橋驛站<bbs.cs.nthu.edu.tw> ◆ Mail: jjhou@ccca.nctu.edu.tw