Effective BCB Form Program(2) ——窗體事件驅動鏈引發程式錯誤分析 (轉)

worldblog發表於2007-12-11
Effective BCB Form Program(2) ——窗體事件驅動鏈引發程式錯誤分析 (轉)[@more@]

二、由員用new的方法建立的窗體事件鏈

在程式中無數次地見到過以下程式碼:

TfrmConstant *pForm=new TfrmConstant(this); //建立一個窗體

  pForm->Show();

  ……

  delete pForm;

我們經常需要動態建立一個Form,再顯示它,然後在合適的地方銷燬它。

請注意上面程式碼中的this引數,它被傳送給TfrmConstant窗體的構造,這一建構函式有一個唯一的形參——Owner。這個引數決定了由誰負責銷燬它。

在BCB的幫助中對Owner引數的描述如下:

Owner描述了誰負責銷燬這個。:namespace prefix = o ns = "urn:schemas--com::office" />

使用Owner引數來存取component所有者的介面。當一個元件擁有另一個元件時,當父元件被釋放時,子元件所佔的記憶體資源也被釋放。這就是說,當一個窗體被銷燬時,窗體上的所有元件也會被刪除。

預設情況下,一個窗體擁有放在其上的所有元件,同樣地,Application則擁有所有的Form物件。這樣,當程式結束時,Application物件會釋放所有Form佔用的資源。

下面是我對Owner引數的實驗記錄

實驗一:主窗體(Form1)new一個普通窗體(Form2),Owner=this

Form1中的程式碼示例:

p2=new TForm2(this);

  p2->Show();

建立:

先建立完主窗體後,只普通窗體Form2的建構函式,不管顯不顯示,事件驅動鏈與以前的實驗一樣。

關閉:

主窗體:OnCloseQueryàOnCloseàOnH()àOnDestory()à主窗體解構函式àForm2解構函式

關閉次序是先主窗體再普通窗體,如果建立了多個普通窗體,根據建立的先後順序按後建立先銷燬的原則逐個銷燬窗體:

 

普通窗體是顯示還是隱藏對事件驅動鏈無影響

實驗二:主窗體(Form1) new一個普通窗體Form2,Owner=Application

建立:

同實驗一

關閉:

首先是Form1:OnCloseQuery()àOnClose()àForm2解構函式(如果有多個,按照後進先出的次序呼叫) àForm1:OnHide()àOnDestory()àForm1解構函式.

 

實驗三:主窗體(Form1) new一個繼承窗體(Form2繼承自Form1),Owner=this

建立:

建立主窗體Form1,事件鏈同單窗體的建立;

接著在程式碼中開始new Form2:

Form2 OnCreate()àForm1的建構函式àForm2 的建構函式à建立結束

 

我們看到要先呼叫父類的建構函式

關閉:

1.Form2未顯示

Form1: OnCloseQuery()àOnClose()àOnHid()eàOnDestory()àForm1的解構函式

àForm2的解構函式à Form1的解構函式à Form2  OnDestory()

注意:並無Form1的OnDestory,同樣地:Form1的解構函式被了兩次

2.Form2顯示情況下::

Form1: OnCloseQuery()àOnClose()àOnHide()àOnDestory()àForm1的解構函式

Form2 OnHide()à Form2的解構函式à Form1的解構函式à Form2  OnDestory

僅多了一個Form2 OnHide()過程

實驗四:主窗體new一個繼承窗體(Form2繼承自Form1),Owner=Application:

建立(Form2不顯示情況):

Form2 OnCreate()àForm1的建構函式à Form2的建構函式

關閉(Form2不顯示情況)

Form1 OnCloseQuery()àOnClose()àForm2的解構函式à Form1的解構函式à Form2  OnDestory()àMainFoHideàMainForm的解構函式à MainForm  的解構函式

建立(Form2顯示情況):

Form2 OnCreate()àForm1的建構函式à Form2的建構函式à Form2 OnShow()à ……à Form2:OnResize()

多了一個顯示的事件

關閉(Form2顯示情況)

Form1: OnCloseQuery()àOnClose()à Form2 :OnHide()àForm2的解構函式à Form1的解構函式à Form2  OnDestory()

àForm1 OnHide()àForm1 OnDestoryà MainForm1解構函式

 

三、MDI型應用程式事件發生次序

主窗體為MainForm,子窗體為MDIChild

建立

MainForm建構函式àOnCreate()à……à OnResize()

新建一個MDI子窗體時,Owner=Application或MainForm

MDIChild 建構函式àOnCreateàshowàActivate

關閉

Owner=Application時:

MDIChild OnCloseQuery()(有幾個子窗體就執行幾次)àMainForm OnCloseQuery()àMainForm OnClose()à MDIChild OnDestory()à MDIChild 解構函式àMainForm OnDestory()àMainForm解構函式

 

Owner=MainForm時:

MDIChild CloseQuery()(有幾個子窗體就執行幾次)àMainForm OnCloseQuery()àMainForm OnClose()àMainForm OnDestory()à MainForm解構函式à MDIChild OnDestory()à MDIChild 解構函式

 

顯然,Owner引數決定了主窗體與MDIChild的析構次序。

 

有用的結論

從實驗中我們可以得出一些有用的結論:

關於OnCreate和OnDestory事件:

1. Form的OnCreate事件是不可靠的,並不像名字所說的當窗體一建立時就執行,而是當窗體是工程的主窗體或是一個繼承其它窗體的子窗體時才執行.所以,在OnCreate()中初始化變數和建立物件是不合適的;

2. 在有繼承情況下,OnCreate事件先於建構函式發生,對應地OnDestroy事件後於解構函式發生。

關於窗體的建構函式和解構函式

1. 對於任何窗體,建構函式和解構函式是可靠的,因為它每一次建立和銷燬窗體時都會執行。所以我們應在建構函式中進行程式的初始化操作,在解構函式中清理資源;而不要在OnCreate和OnDestory事件中進行處理;

2. 在繼承情況下BCB子窗體物件的構造過程是先構造父窗體,再構造子窗體,這點與C++完全一樣,銷燬過程則反之;

3. 在有繼承的情況下,父類窗體的建構函式和解構函式則會被執行多次,所以,在這兩個地方不要new和delete物件,而應將這類程式碼放到具體的子窗體類中。如果不這樣做,可能會由於多次刪除同一物件而引發錯誤,除非將父類窗體的物件指標宣告為static的,保證所有子類共享此new的唯一物件!

4.解構函式總是最後呼叫的,而建構函式總是第一個呼叫。

 

關於Owner引數:

當Owner=Application時,先銷燬new出來的窗體,再銷燬主窗體;

而當Owner=this(指主窗體)時,先銷燬主窗體,再銷燬子窗體;

這個原則對於在窗體內部動態地生成VCL時也適用。特別地,當程式中用到了資料模組,或需要動態地生成DataSet等物件時更要注意。因為一個DataSet可能會向多個窗體提供資料(應該避免這樣做,合適的方法是new TDataSet時Owner引數不設為Application)。

其它:

1.  一個Visible=true的窗體一定要先Hide,才能被銷燬。

2. 程式事件鏈的源頭為工程中的主窗體的建構函式

3. 在有繼承情況下,子類窗體例項一定要先Hide才可以被銷燬。

4. 多窗體的管理BCB採用了棧的方法,按建立的次序,先建立的後銷燬,千萬要避免先建立的窗體含有對後建立的窗體(或其上的控制元件)的指標。

5. 關閉時一定會發生OnCloseQuery事件,如果是MDI,此事件還會在MDIChild窗體中傳播,所以,在這個地方提示改變是合適的。

6. 有些事件一發生,則定然會跟著一系列的事件,這些事件發生的次序是不變的,比如,只要OnShow事件發生,則一定會接著發生以下事件:

àOnActivate()àOnCanResize()àOnConstrainedResize()àOnResize()

OnCloseQuery之後一定是OnClose()

 

 

 

 


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-991462/,如需轉載,請註明出處,否則將追究法律責任。

相關文章