比如说我在unit1里生成了一些对象,不用这些对象时要销毁,用A.free;
如果不用A.free; unit1里有个form1,运行中点窗体右上角的关闭(叉子)按钮(=formdestory事件?),有没有用呢?
也就是说结束程序运行(关闭窗体?)和销毁对象之间有什么关联呢?如果我最终肯定要结束运行,那还有必要用A.free么?

解决方案 »

  1.   

    一般没有必要,因为组件(Component)自身可以管理生命周期,实际上,TApplication对象也是Component,在调用CreateForm的时候会自动成为窗体的Owner,因而自动管理窗体的生命周期。
      

  2.   

    谢了。
    但是按照楼上2位所说,会产生矛盾么。如果是一直使用到程序关闭的对象,还是动态创建的对象,需要手工释放么?
    有的资料上写创建有属主的组件不需要手工销毁。具体是不是说,比如我创建了一个属主是form1的对象,我关闭form1时,它就自动销毁了,所以不用手工销毁。而比如unit1中有form1,我还创建了一些跟form1没关系的对象,那这些对象需要手工销毁么?
      

  3.   

    就是比如说unit1里有form1,还有动态创建的对象a.但是a和form1没有任何关系。而我运行程序的时候点关闭,是关闭的form1还是unit1?而这个a是否已经销毁?是否需要在form1.destory里加上a.free?
      

  4.   

    是这样的,要解释你的问题需要从2个方面说:
    1, 如果你建立的对象的生命周期不由你管理,那么就不需要你来显示的释放 我给你举2个例子,例如窗体上的按钮,  再比如你自己动态创建的按钮, Tbutton.Create(Form1),这种形式创建的按钮,生命周期由Form1自动接管了,所以不再需要你来释放.当窗体Destroy的时候Button也会自动释放了
    2, 如果Button是你创建的,以Tbutton.Create(nil)的形式创建的那么button的生命周期完全由你自己掌握, Form不再管理,所以你一定要在Form释放之前就释放掉你创建的这个button根据以上情况,其它的非继承TComponent的对象 基本上都是需要人为参与管理对象的生命周期, 其实光这样简单的讲2句很难一下讲的很完整,最主要是靠你自己多看书和例子,慢慢就会了, 一定要多动手实验!
      

  5.   

    对象就是对象,放在那个pas 文件中不重要。窗体也是对象,只不过是可以看到,系统调用了绘制的功能。TMyForm=class(TForm) 一个对象A中生成了另一个对象B,并不是说新的对象B就属于A了,它们还都是独立的。都要是分别调用它们的
    Free才行。当然,你也可以在A的Destroy方法中调用 b.free.你摆在窗体上的控件,看似你没有调用 free ,其实是VCL的代码中都已经调用了。一定要清楚一点,窗体也是对象,和你自己写的对象没有区别。
      

  6.   

    比如说我在unit1里生成了一些对象,不用这些对象时要销毁,用A.free; 
    如果不用A.free; unit1里有个form1,运行中点窗体右上角的关闭(叉子)按钮(=formdestory事件?),有没有用呢? 
    也就是说结束程序运行(关闭窗体?)和销毁对象之间有什么关联呢?如果我最终肯定要结束运行,那还有必要用A.free么?
    ================================
    最好还是手工释放删除,比较安全.呵呵.
    在程序中,动态创建的,需要手工的删除.静态的会自动在关闭之后删除(回收).在大程序中,把不用的资源及时回收,很有用处,会使用你的程序处理反映及时.如果不及时,容易造成资源浪费,
    严重的会产生程序错误(中断程序运行)
      

  7.   

    多谢各位帮忙!看了回答,可不可以这样理解:
    1.比如一个application里就一个unit,这个unit里就一个form1.然后我在unit里生成了一个a=TABC.create(application),即属主是application的对象。而我程序运行时点关闭,这时是关闭的form还是application?我现在的理解是关闭的application,因为form1是这个application的主窗体。所以不用手动释放对象a.
    2.而如果定义时写成a=TABC.create(nil);则需要手动释放a,因为a没有指定属主。
    即要明确2个概念,
    一 关闭是关闭的application,而不是单纯的关闭form. 
    二 假如a的属主指定为组件对象(TComponent的派生类)时,不用手动释放对象a。而如果a没有指定属主时,需要手动释放a.
    不知现在对于概念一和二的理解是否正确。
      

  8.   

    看你的问题,你应该明白,如果你的应用程序都结束了,那么windows会为你回收所有你创建的内存和资源,因此,如果你想偷懒,你在应用程序结束的时候就不需要释放那些对象了,windows会为你统一释放的。
    上面说的是进程结束的情况,现在说下在进程中关于对象的创建和释放问题。首先要明确个事情,不管是谁会帮你释放,要养成这样一个开发习惯,
    不用的东西,要及时清理。比如你创建了一个对象A,如果用完了,不管这个A属于谁?你最好将其手动释放掉。原则问题说完了,再说说关于对象属于谁的问题。
    在Delphi中,从 TComponent 类开始,在 Create 方法中,多了一个 AOwner: TComponent 参数
        constructor Create(AOwner: TComponent); virtual;
    从定义上看,有两点:
    1 TComponent 的子类可以有一个Owner 
    2 能作为其他类的Owner的类必须是TComponent 或其子类。TForm和TApplication都是TComponent 的子类
    好了,现在说说这个Owner的作用。
    看看 TComponent 的Create怎么写的constructor TComponent.Create(AOwner: TComponent);
    begin
      FComponentStyle := [csInheritable];
      if AOwner <> nil then AOwner.InsertComponent(Self);
    end;从这里可以看到
       AOwner.InsertComponent(Self);
    这是管理Owner的地方,再看看 InsertComponent procedure TComponent.InsertComponent(AComponent: TComponent);
    begin
      AComponent.ValidateContainer(Self);
      if AComponent.FOwner <> nil then
        AComponent.FOwner.RemoveComponent(AComponent);
      ValidateRename(AComponent, '', AComponent.FName);
      Insert(AComponent);
      AComponent.SetReference(True);
      if csDesigning in ComponentState then
        AComponent.SetDesigning(True);
      Notification(AComponent, opInsert);
    end;这里面有个 Insert(AComponent); 看看这个Insertprocedure TComponent.Insert(AComponent: TComponent);
    begin
      if FComponents = nil then FComponents := TList.Create;
      FComponents.Add(AComponent);
      if FSortedComponents <> nil then
        AddSortedComponent(AComponent);
      AComponent.FOwner := Self;
    end;看到了吗? Owner被插入到了 FComponents 中,看看这个成员的定义    FComponents: TList;这是一个列表,其中保存这所有以这个类为Owner的类
    再看看这个FComponents有什么用处,既然大家都在关心释放问题,那就看看TComponent的Destroydestructor TComponent.Destroy;
    begin
      Destroying;
      RemoveFreeNotifications;
      DestroyComponents;
      if FOwner <> nil then FOwner.RemoveComponent(Self);
      inherited Destroy;
    end;这里面有个 DestroyComponents,看看这个都干了什么
    procedure TComponent.DestroyComponents;
    var
      Instance: TComponent;
    begin
      FreeAndNil(FSortedComponents);
      while FComponents <> nil do
      begin
        Instance := FComponents.Last;
        if (csFreeNotification in Instance.FComponentState)
          or (FComponentState * [csDesigning, csInline] = [csDesigning, csInline]) then
          RemoveComponent(Instance)
        else
          Remove(Instance);
        Instance.Destroy;
      end;
    end;看到没有,取出来这个列表中的对象Instance := FComponents.Last;,
    然后从列表中移处这个对象,然后就释放他Instance.Destroy;现在知道了为什么有Owner的对象不需要手动释放了吧。所以说,如果你在创建一个TComponent的子类的时候,如果你知道了这个类的Owner,那么,在其Owner释放的时候,会连带将其释放。好了,解释到这里,你应该清楚了吧。顺道说个小问题
    窗体关闭了,不表示窗体释放了(主窗体除外,主窗体关闭,进程就结束了,一切都释放了),
    窗体的 Create 和 Free 是一对
    窗体的 Show 和 Hide 是一对
    点击窗体的那个X不一定就是释放了窗体,可能仅仅是隐藏了,隐藏了窗体仅仅是你看不到了,你还可以继续调用Show把他显示出来。如果是Free了,那如果再想看这个窗体,你必须先Create
      

  9.   

    上面说错个问题看到了吗? Owner被插入到了 FComponents 中,看看这个成员的定义 这句说反了
    应该是
    在创建对象时,如果传入了Owner,则对象的 Owner 将对象插入到了自己的(也就是Owner的)FComponents列表中。
    例如 A := TA.Create(B),则A被插入到B的FComponents列表中,那么B在释放的时候,如果A还没有被释放,B会负责释放A顺道说一点,可能有些人会说,如果A在B释放前被释放了怎么办?这个问题,其实在
    destructor TComponent.Destroy;
    中就给出了解决的办法
    if FOwner <> nil then FOwner.RemoveComponent(Self);
    看到了吧,如果A提前释放了,他会调用Owner的RemoveComponent方法,并将自己当参数传进去,
    而RemoveComponent就护从FComponents中找到这个类并将这个类从列表中删除。从上面的操作过程可以看到,如果你打算自己管理那些动态创建的类,还是尽量不要在创建的时候指定Owner,因为一但指定了Owner,则就会涉及到很多操作(至少在A释放的时候,在B中要在FComponents中遍历查找这个类并将其移除),这样影响效率。呵呵
      

  10.   

    我是楼主,多谢楼上。
    根据所讲的再问下,
    1 比如我想写成create带参数的形式,是不是应该用constructor create(a:TWinControl);这样定义?(3中的例子就是这样写的)然后在class1:=tclass1.create(self)或者class1:=tclass1.create(nil)?
    因为我自己实验的时候如果定义成constructor create;而再用class1:=tclass1.create(nil)时,提示说参数太多。
    2 如果create定义成constructor create;的形式,即缺省参数。那此时它的属主是self还是nil呢?
    3 对于创建与释放我做了个例子想测试一下type
      tclass1=class
        i:string;
        constructor create(a:TWinControl);
      end;
    type
      tclass2=class
        j:string;
        constructor create(b:TWinControl);
      end;var
      Form1: TForm1;
      class1:tclass1;
      class2:tclass2;implementation{$R *.dfm}constructor tclass1.create(a:TWinControl);
    begin
      i:='1';
    end;
    constructor tclass2.create(b:TWinControl);
    begin
      j:='2';
    end;procedure TForm1.Button1Click(Sender: TObject);
    begin
      class1:=tclass1.create(nil);
      edit1.Text:=class1.i;
    end;procedure TForm1.Button2Click(Sender: TObject);
    begin
      class2:=tclass2.create(nil);
      edit1.Text:=class2.j;
    end;procedure TForm1.Button3Click(Sender: TObject);
    begin
      class1.free;
    end;procedure TForm1.Button4Click(Sender: TObject);
    begin
      class2.free;
    发现,
    (1)上来如果先点按钮3和4,则正常不报错。(就是一直free)
    (2)如果按13132424或者13241324这种,则正常不报错。(就是创建一次销毁一次再循环操作)
    (3)如果按113,或者224时,正常不报错。(create的次数比free的次数多)
    (4)如果按133,或者244时,画面自动消失,但没报错,肯定有问题。(free的次数比create的次数多,而1却不出错,问题点)
    而比较奇怪的是:
    (5)121233时出错,
    (6)121 33时却是画面消失但没报错。很奇怪,(5)和(6)差了一个“2”,但是(5)和(6)根本没有涉及到"2"的问题。
    (7)如果把free换成freeandnil的写法,则无论怎么点也不出错,效果一直正常,可见freeandnil要比free安全。
    这部分,我现在的理解程度是:对象创建时是在堆里分配了空间,而对象名只是在栈中的一个4字节的指针。销毁时是free的堆里面的内存,堆里面的需要手动free,而栈里面的就不用管了。可是我还是不太理解上面(1)-(7)的现象。
    以上几点原因为何?请指教。在下感激不尽!(也请帮我确认下1和2的问题)
      

  11.   

    我觉得我上面测试的那个例子中create函数好像定义的有问题。
    我主要是想用class1:=tclass1.create(nil); 的形式,即不指定属主,从而好进行下面的测试。
    但是我定义的写法是constructor create(a:TWinControl); 而a好像没有用到。
    因为如果我只定义成constructor create;则调用class1:=tclass.create(nil)时提示参数太多。意思就是说我定义时没有nil这个参数,而创建对象时多了这个参数。
    如果我想不指定属主,而用nil的形式,那应该怎么定义这个create呢?
      

  12.   

    又看了下,这部分应该是我的类定义的问题。资料上用的比如是class1:=TButton.create(self);
    TButton是继承自TComponent,所以这种create定义时可以在后面加上(self)或者(nil)之类。
    而我定义的是tclass1=class;这个tclass是继承自TObject,谈不上属主这块。
    所以上面的例子class1:=tclass1.create(nil); 并非是class1:=TButton.create(nil);这两个nil不是一种。那我那个例子就谈不上属主这块了么?
      

  13.   

    你用的是Delphi版本是多少?正确的类定义应该如下
    type 
      tclass1=class 
        i:string; 
      public
        constructor create(a:TWinControl); 
      end; 
    type 
      tclass2=class 
        j:string; 
      public
       constructor create(b:TWinControl); 
      end; 如果不写public,默认的级别是published好了,再解释你所碰到的现象,我只能说为什么,至于你说的那几点,你根据原因自己对照吧。
    要解释明白原因,需要明确几个问题:首先要说的是全局变量。
    定义在单元内部的变量叫全局变量(也就是不在过程或者类中的变量,你的Class1和class2都是全局变量)
    全局变量在程序加载的时候,是会被初始化的,也就是说 class1和class2在程序加载之后被初始化为了 空所以,你如果不创建对象而直接使用class1和class2,则这时候,class1和class2都是 nil
    然后说下类中的方法。
    首先明确以下定义,TObject 是类; obj: TObject  如果Obj被创建了,那么我简单认为Obj是对象(实际上是Obj指向的那个内存是对象)
    好了。明确了上面的定义,下面说说类中的方法
    类中的方法是类本身所具有的,也就是说,方法本身是属于类的,不是属于对象的
    例如 TButton 的 Click 方法,这个方法是属于TButton的
    换句话说,如果我们采用强制类型转换的方式调用类中的方法,如果这个类中的方法没有使用类中定义的私有变量,那么,无论你把什么强制转换成某个类,这个方法的调用将都有效。例如:
        TMyClass = class(TObject)
      public
        procedure func1(S: AnsiString);
        class procedure func2(S: AnsiString);
      end;{ TMyClass }procedure TMyClass.func1(S: AnsiString);
    begin
      ShowMessage('Func1');
    end;class procedure TMyClass.func2(S: AnsiString);
    begin
      ShowMessage('Func2');
    end;
    这个类中的func1方法,没有使用类中的任何内容,因此,下面的调用都将显示出一个内容为 Func1 的对话框
    TMyClass(0).func1('你好');
    TMyClass(100).func1('你好');
    TMyClass(form1).func1('你好');var
     a : TMyClass;
    begin
      TMyClass(a).func1('你好');
      a := TMyClass.Create;
      a.Func1('你好');
      a.free;
    end;不要觉得奇怪,这正说明了类中的方法是属于类的,不是属于对象的好了,再说下一个概念。
    类中的方法,都隐含一个参数,如果是类中的普通方法,则隐含参数是对象,如果是类方法,隐藏参数是类
    在方法中,都可以通过self这个方式访问这个隐含的参数,在一般方法中 self 就是对象的首地址,在类方法中,self 就是类信息的首地址(类也是有信息的,其中包含着这个类的一些很有用的信息)
    这个不太好理解,就拿上面的TMyClass类来说。
    func1是类中的普通方法,func2是类中的类方法
    func1 中的 self 就是在堆中开辟的内存创建对象的首地址
    func2 中的 self 就是类的描述信息的地址。
    好了,现在来说你上面遇到的问题
     因为class1是全局变量,因此最开始的时候,其值是 nil 也就是 class1的值是0
    这时候,class1.Free,也就意味着你用一个空地址作为 Free的隐藏参数调用了Free方法(Free方法是类中的普通方法,没有一般参数,但是按照上面的描述,他是有一个隐含参数的)
    看看 Free的实现
    procedure TObject.Free;
    begin
      if Self <> nil then
        Destroy;
    end;
    里面首先判断了下 self 是否为空,而 class1.Free恰恰传入了个空进去,因此什么都不做。再看看你为什么会出错,你首先创建了一个class1的对象,并将其地址给了class1,这时候,class1是有内容的,也就是说不再是 nil了。
    当你调用 class1.Free的时候,你把对象释放了,但是 class1 的值还是原来的值,就像你说的,class1仅仅是个指针,不会随着class1这个对象的释放而清零。
    所以,当你再次调用 class1.Free的时候,Free隐藏参数传入的内容就不再是空了,因此,就会执行Free中的 Destroy ,这个方法是要实际去释放 self 所指向的那块内存的,而你前面刚刚释放了这个内存,因此会出问题。这个问题不一定是会报错,因为你违规访问内存,也可能直接就被windows把你的程序给结束了。最后 FreeAniNil 这个函数的参数是 var 型的,他在释放对象的同时,会同时把指向这个对象的指针也清空
    这又回到了第一次使用class1.Free不会出错是因为class1是nil的问题上这么多字,好累
      

  14.   

    忘了一个问题
        constructor Create;
    这个类方法用不用带Owner参数取决于你这个类的功能,是否需要一个Owner,如果需要,那么你从TComponent开始继承而不是TObject,如果你一定要从TObject继承,还想保留TComponent中Owner所起的作用,那么你自己去实现关于TComponent中Owner的功能。
    如果你从TComponent继承而又按照
        constructor Create;
    方式定义
    那意味着,你将TComponent的Create(AOwner: TComponent)方法给隐藏了