Effective BCB Form Program(2) ——窗体事件驱动链引发程序错误分析

二、由程序员用new的方法创建的窗体事件驱动链

在程序中无数次地见到过以下代码:

TfrmConstant *pForm=new TfrmConstant(this); //创建一个窗体

        pForm->Show();

        ……

        delete pForm;

我们经常需要动态创建一个Form,再显示它,然后在合适的地方销毁它。

请注意上面代码中的this参数,它被传送给TfrmConstant窗体的构造函数,这一构造函数有一个唯一的形参——Owner。这个参数决定了由谁负责销毁它。

BCB的帮助文件中对Owner参数的描述如下:

Owner描述了谁负责销毁这个组件。

使用Owner参数来存取component所有者的接口。当一个组件拥有另一个组件时,当父组件内存被释放时,子组件所占的内存资源也被释放。这就是说,当一个窗体被销毁时,窗体上的所有组件也会被删除。

缺省情况下,一个窗体拥有放在其上的所有组件,同样地,Application对象则拥有所有的Form对象。这样,当程序结束时,Application对象会释放所有Form占用的资源。

下面是我对Owner参数的实验记录

实验一:主窗体(Form1new一个普通窗体(Form2)Owner=this

Form1中的代码示例:

p2=new TForm2(this);

        p2->Show();

创建:

先创建完主窗体后,只调用普通窗体Form2的构造函数,不管显不显示,事件驱动链与以前的实验一样。

关闭:

主窗体:OnCloseQueryàOnCloseàOnHide()àOnDestory()à主窗体析构函数àForm2析构函数

关闭次序是先主窗体再普通窗体,如果创建了多个普通窗体,根据创建的先后顺序按后创建先销毁的原则逐个销毁窗体:

 

普通窗体是显示还是隐藏对事件驱动链无影响

实验二:主窗体(Form1) new一个普通窗体Form2Owner=Application

创建:

同实验一

关闭:

首先是Form1OnCloseQuery()àOnClose()àForm2析构函数(如果有多个,按照后进先出的次序调用) àForm1:OnHide()àOnDestory()àForm1析构函数.

 

实验三:主窗体(Form1) new一个继承窗体(Form2继承自Form1),Owner=this

创建:

创建主窗体Form1,事件链同单窗体的创建;

接着在代码中开始new Form2:

Form2 OnCreate()àForm1的构造函数àForm2 的构造函数à创建结束

 

我们看到要先调用父类的构造函数

关闭:

1Form2未显示

Form1: OnCloseQuery()àOnClose()àOnHid()eàOnDestory()àForm1的析构函数

àForm2的析构函数à Form1的析构函数à Form2  OnDestory()

注意:并无Form1OnDestory,同样地:Form1的析构函数被执行了两次

2Form2显示情况下::

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()àMainForm Hideà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=ApplicationMainForm

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的析构次序。

 

有用的结论

从实验中我们可以得出一些有用的结论:

关于OnCreateOnDestory事件:

1. FormOnCreate事件是不可靠的,并不像名字所说的当窗体一创建时就执行,而是当窗体是工程的主窗体或是一个继承其它窗体的子窗体时才执行.所以,OnCreate()中初始化变量和创建对象是不合适的;

2. 在有继承情况下OnCreate事件先于构造函数发生对应地OnDestroy事件后于析构函数发生。

关于窗体的构造函数和析构函数

1. 于任何窗体,构造函数和析构函数是可靠的,因为它每一次创建和销毁窗体时都会执行。所以我们应在构造函数中进行程序的初始化操作,在析构函数中清理资源;而不要在OnCreateOnDestory事件中进行处理;

2. 在继承情况下BCB子窗体对象的构造过程是先构造父窗体,再构造子窗体,这点与C++完全一样,销毁过程则反之;

3. 在有继承的情况下,父类窗体的构造函数和析构函数则会被执行多次,所以,在这两个地方不要newdelete对象,而应将这类代码放到具体的子窗体类中。如果不这样做,可能会由于多次删除同一对象而引发错误,除非将父类窗体的对象指针声明为static,保证所有子类共享此new的唯一对象!

4析构函数总是最后调用的,而构造函数总是第一个调用。

 

关于Owner参数:

Owner=Application时,先销毁new出来的窗体,再销毁主窗体;

而当Owner=this(指主窗体)时,先销毁主窗体,再销毁子窗体;

这个原则对于在窗体内部动态地生成VCL控件时也适用。特别地,当程序中用到了数据模块,或需要动态地生成DataSet等对象时更要注意。因为一个DataSet可能会向多个窗体提供数据(应该避免这样做,合适的方法是new TDataSetOwner参数不设为Application)。

其它:

1.  一个Visible=true的窗体一定要先Hide,才能被销毁。

2. 程序事件链的源头为工程中的主窗体的构造函数

3. 在有继承情况下,子类窗体实例一定要先Hide才可以被销毁。

4. 多窗体的管理BCB采用了栈的方法,按创建的次序,先创建的后销毁,千万要避免先创建的窗体含有对后创建的窗体(或其上的控件)的指针。

5. 关闭时一定会发生OnCloseQuery事件,如果是MDI,此事件还会在MDIChild窗体中传播,所以,在这个地方提示用户改变是合适的。

6. 有些事件一发生,则定然会跟着一系列的事件,这些事件发生的次序是不变的,比如,只要OnShow事件发生,则一定会接着发生以下事件:

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

OnCloseQuery之后一定是OnClose()

 

 

 

 

 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值