unit Unit1;interfaceuses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;type
  TA = class
  private
    Fs: string;
  public
   constructor Create(s: string);
   destructor Destroy;override;
   procedure test;
  end;
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;var
  Form1: TForm1;implementation{$R *.dfm}procedure TForm1.Button1Click(Sender: TObject);
var
  A: TA;
begin
  A := TA.Create('test');
  A.Free;
  //A := nil;
  //if A <> nil then
  if Assigned(A) then
     A.test;
end;{ TA }constructor TA.Create(s: string);
begin
  Fs:= s;
end;destructor TA.Destroy;
begin
  Fs:= '';
  inherited;
end;procedure TA.test;
begin
  ShowMessage(Fs);
end;end.
例如以上代码,A对象所指向的堆内存已经free了,但无论判断A是否为nil或者用assigned判断,都可以调用A.test而不出错。应该怎么判断A已经Free而不至于执行A.test呢?

解决方案 »

  1.   

    事先说明:A.Free后没有被设为nil的
      

  2.   

    所以free的时候 你用freeandnil吧.
    不然貌似delphi里面还真的不能判断.
      

  3.   

      //A := nil;
      //if A <> nil then正确的做法就应该是这样啊,为什么要注释掉.Free以后立即赋值为nil,为以后使用前判断提供依据.
      

  4.   

    我的目的就是想在A不为nil的情况下如何判断堆内存是否已释放
      

  5.   

    对,顶2楼,也可以直接用FreeAndNil(...)方法.
    var
      A: TA;
    begin
      A := TA.Create('test');
      FreeAndNil(A); {和A.Free; A:=nil;是一个效果}
      if Assigned(A) then
        A.test; {这句将不会被执行了}
    end;
      

  6.   

    是否可以说说你更深层的目的是什么?
    或者说说你遇到了什么样的情况非要这样判断,而不能在free后赋值nil(因为这才是推荐的编码方法).
      

  7.   

    因为程序中对一个对象的引用变量可能不止一个,当对一个对象的引用变量FreeAndNil后,有可能忘记将另外的引用变量为nil,所以有可能出现我所问的问题
      

  8.   

    [Quote=引用 9 楼 chenyq2008 的回复:]
    引用 7 楼 JPEXE 的回复:
    我的目的就是想在A不为nil的情况下如何判断堆内存是否已释放 
    /Quote]A不为NIL的时候还有可能被释放了吗?
      

  9.   

    不知道这样能不能满足你的要求.
    unit Unit1;interfaceuses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, StdCtrls;type
      { 用这个类实现标记/判断对象是否已被释放 }
      TCheckIsAlive = class
      private
        flag: Boolean; { 类创建时成员变量 flag 一定会被初始化为 False }
        function GetIsAlive: Boolean;
      public
        destructor Destroy; override;
        property IsAlive: Boolean read GetIsAlive;
      end;  TA = class(TCheckIsAlive) { 注意继承 }
      private
        Fs: string;
      public
        constructor Create(s: string);
        destructor Destroy; override;
        procedure test;
      end;  TForm1 = class(TForm)
        btn1: TButton;
        procedure btn1Click(Sender: TObject);
      private
        { Private declarations }
      public
        { Public declarations }
      end;var
      Form1: TForm1;implementation{$R *.dfm}procedure TForm1.btn1Click(Sender: TObject);
    var
      A: TA;
    begin
      A := TA.Create('test');
      A.Free;  { 检查是否 alive }
      if A.IsAlive then
        A.test;
    end;{ TA }constructor TA.Create(s: string);
    begin
      Fs := s;
    end;destructor TA.Destroy;
    begin
      Fs := '';
      inherited;
    end;procedure TA.test;
    begin
      ShowMessage(Fs);
    end;{ TCheckIsFree }destructor TCheckIsAlive.Destroy;
    begin
      flag := True; { 修改标记 }
    end;function TCheckIsAlive.GetIsAlive: Boolean;
    begin
      Result := not flag;
    end;end.
    不需改变对TA类的使用,只需让TA从TCheckIsAlive继承就行了.
      

  10.   

    有句注释写错了
    { TCheckIsFree } 应该是 { TCheckIsAlive }
      

  11.   

    再完善了一下代码,下面的代码应该能印证楼主的需求:"因为程序中对一个对象的引用变量可能不止一个,当对一个对象的引用变量FreeAndNil后,有可能忘记将另外的引用变量为nil,所以有可能出现我所问的问题"unit Unit1;interfaceuses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, StdCtrls;type
      { 用这个类实现标记/判断对象是否已被释放 }
      TCheckIsAlive = class
      private
        flag: Boolean; { 类创建时成员变量 flag 一定会被初始化为 False }
        function GetIsAlive: Boolean;
      public
        destructor Destroy; override;
        property IsAlive: Boolean read GetIsAlive;
      end;  TA = class(TCheckIsAlive) { 注意继承 }
      private
        Fs: string;
      public
        constructor Create(s: string);
        destructor Destroy; override;
        procedure test;
      end;  TForm1 = class(TForm)
        btn1: TButton;
        btn2: TButton;
        procedure btn1Click(Sender: TObject);
        procedure btn2Click(Sender: TObject);
      private
        FA: TA;
      end;var
      Form1: TForm1;implementation{$R *.dfm}procedure TForm1.btn1Click(Sender: TObject);
    var
      A: TA;
    begin
      A := TA.Create('test'); { 创建 }
      FA := A; { 引用 }  { 检查是否 alive }
      if A.IsAlive then
        A.test; { 未释放->会执行 }  A.Free; { 释放 }
    end;procedure TForm1.btn2Click(Sender: TObject);
    begin
      { 检查是否 alive }
      if FA.IsAlive then
        FA.test; { 已被释放->不会执行 }
    end;{ TA }constructor TA.Create(s: string);
    begin
      Fs := s;
    end;destructor TA.Destroy;
    begin
      Fs := '';
      inherited;
    end;procedure TA.test;
    begin
      ShowMessage(Fs);
    end;{ TCheckIsAlive }destructor TCheckIsAlive.Destroy;
    begin
      flag := True; { 修改标记 }
    end;function TCheckIsAlive.GetIsAlive: Boolean;
    begin
      Result := not flag;
    end;end.
      

  12.   

    @JPEXE
    这样实现是不行的。
      

  13.   

    To JPEXE:
    你只是在特定的类中做标记,我是想问在普通的类应用中有没可能做判断
      

  14.   

    InstanceSize 返回类对象占据的内存大小var a:TA;
    begin
       a:=TA.Create;
       A.i:=100;
       ShowMessage(IntToStr(a.InstanceSize));
       a.Free;
       ShowMessage(IntToStr(a.InstanceSize));
    end;
      

  15.   

      TA=class
         i:integer;
      end;
      

  16.   

    无论一个对象有几个引用,只有有一个调用了Free,这个对象所在内存的空间就被“毁”了。所以像使用flag等实例字段根本没有作用。其实根本的重点不在于判断对象指针所指向的堆内存是否已经释放,而是某个对象是不是应该有多个引用。根据领域驱动设计的思想,有些对象只能通过它的聚合根来访问。这样不仅能减少对象引用,还可以让程序更容易理解和维护。如果有很多地方需要使用这个对象,是不是应该抽象出一个接口,然后由其自己(接口的引用计数)管理引用?
      

  17.   

    @chijingdeInstanceSize是类方法(class function),其隐含的Self指针实际上指向的是某个具体的Class。
    所以无论a释放与否,结果都是一样。
      

  18.   

    没什么好办法,主要还得靠程序员养成良好习惯按理说,内存使用情况,操作系统是最清楚的,但是操作系统一般不会具备这种机制
    因此java和.net才要在虚拟机上搞内存回收
      

  19.   

    万年不回贴一回贴就丢个大人不懂装懂真是害人啊MLGBD