直接上例子了,基础知识自己去了解,首先定义一个类:

  TPerson = class
  public
    name: string;
    age: Integer;
    constructor Create(name: string; age: Integer);
  end;
  
constructor TPerson.Create(name: string; age: Integer);
begin
  Self.name := name;
  Self.age := age;
end;

 

例子1,首先了解Free,仅仅是销毁堆中数据,栈中变量的值依然是堆中的地址:

procedure TForm3.btn1Click(Sender: TObject);
var
  ps: TPerson;
begin
  ps := TPerson.Create('小李', 10);

  ps.Free; //这里调用Free,堆中的数据已经被释放,但是ps这个指针的值还是堆中对象的地址

  if Assigned(ps) then //Assigned方法,当ps指针的值为nil的时候,才返回False,由于ps的值不为nil,这里显然会返回True
  begin
    mmo1.Lines.Add('yes');
    mmo1.Lines.Add(ps.name); //这里产生了Av错误,原因是ps堆中的对象已经被释放
  end else begin
    mmo1.Lines.Add('no');
  end;
end;

 

 

例子2,知道即使你不创建实例,Assigned依然是true;

procedure TForm3.btn3Click(Sender: TObject);
var
  p: TPerson;
begin
  //p不初始化,会自由随机分配一个地址和值,值也是原来的自由值,不一定是nil,通常都不是nil
  if Assigned(p) then
  begin
    //只要p指针的值不是nil,这里就会返回true,所以是yes
    mmo1.Lines.Add('yes');
  end else begin
    mmo1.Lines.Add('no');
  end;
end;

 

 

例子3:对例子1进行改造,使用FreeAndNil,将堆中数据释放,同时将栈中指针的值变为nil;

procedure TForm3.btn3Click(Sender: TObject);
var
  ps: TPerson;
begin
  ps := TPerson.Create('小李', 10);

  FreeAndNil(ps); //指针的值也变成nil了

  if Assigned(ps) then
  begin
    mmo1.Lines.Add('yes');
  end else begin
    mmo1.Lines.Add('no'); //此时就自然到这里了
  end;
end;

 

 

例子4,最后一个例子,得出官方的Assigned 不是我们需要的:

 

procedure TForm3.btn4Click(Sender: TObject);
var
  ps, ps1: TPerson;
begin
  ps := TPerson.Create('小李', 10);

  ps1 := ps;

  FreeAndNil(ps);

  if Assigned(ps) then
  begin
    mmo1.Lines.Add('yes');
  end else begin
    mmo1.Lines.Add('no'); //ps指针的值是nil了,所以no
  end;

  if Assigned(ps1) then
  begin
    mmo1.Lines.Add('yes'); //ps1指针的值不是nil
    //mmo1.Lines.Add(ps1.name); //这里就会报错,因为ps堆中的数据已经释放
  end else begin
    mmo1.Lines.Add('no');
  end;

  //显然上面不是我们要的结果,使用我们自己的函数
  if AssignedEx(ps1, TPerson) then
  begin
    mmo1.Lines.Add('yes');
  end else begin
    mmo1.Lines.Add('no'); //由于ps1指向的堆中数据已经不存在,这里会no
  end;
end;

 

好了综上得出官方的Assigned 有点简陋,不是我们需要的,引出我们的修改版:

/// <summary>
/// 判断实例释放释放
/// </summary>
/// <param name="AVar">实例变量</param>
/// <param name="className">实例类型</param>
function AssignedEx(AVar: TObject; AClass: TClass): Boolean;
begin
  //首先判断变量指针的值是否为nil
  if not Assigned(AVar) then
  begin
    Exit(False);
  end;

  //判断堆中的数据是否是当前类
  try
    //若AVar指向的堆中数据已释放,则这里调用.ClassName会AV异常
    if AVar.ClassName = AClass.ClassName then
    begin
      Exit(True);
    end else begin
      Exit(False);
    end;
  except
    //有异常说明堆中已经被释放掉了
    Exit(False);
  end;
end;

 

==========================================================

最后贴上全部测试代码:

全部测试代码
unit Unit3;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type
  TForm3 = class(TForm)
    mmo1: TMemo;
    btn1: TButton;
    btn2: TButton;
    btn3: TButton;
    btn4: TButton;
    btn5: TButton;
    procedure btn1Click(Sender: TObject);
    procedure btn2Click(Sender: TObject);
    procedure btn3Click(Sender: TObject);
    procedure btn4Click(Sender: TObject);
    procedure btn5Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

  TPerson = class
  public
    name: string;
    age: Integer;
    constructor Create(name: string; age: Integer);
  end;

var
  Form3: TForm3;

implementation

{$R *.dfm}

constructor TPerson.Create(name: string; age: Integer);
begin
  Self.name := name;
  Self.age := age;
end;

/// <summary>
/// 判断实例释放释放
/// </summary>
/// <param name="AVar">实例变量</param>
/// <param name="className">实例类型</param>
function AssignedEx(AVar: TObject; AClass: TClass): Boolean;
begin
  //首先判断变量指针的值是否为nil
  if not Assigned(AVar) then
  begin
    Exit(False);
  end;

  //判断堆中的数据是否是当前类
  try
    //若AVar指向的堆中数据已释放,则这里调用.ClassName会AV异常
    if AVar.ClassName = AClass.ClassName then
    begin
      Exit(True);
    end else begin
      Exit(False);
    end;
  except
    //有异常说明堆中已经被释放掉了
    Exit(False);
  end;
end;

procedure TForm3.btn1Click(Sender: TObject);
var
  ps: TPerson;
begin
  ps := TPerson.Create('小李', 10);

  ps.Free; //这里调用Free,堆中的数据已经被释放,但是ps这个指针的值还是堆中对象的地址

  if Assigned(ps) then //Assigned方法,当ps指针的值为nil的时候,才返回False,由于ps的值不为nil,这里显然会返回True
  begin
    mmo1.Lines.Add('yes');
    mmo1.Lines.Add(ps.name); //这里产生了Av错误,原因是ps堆中的对象已经被释放
  end else begin
    mmo1.Lines.Add('no');
  end;
end;

procedure TForm3.btn2Click(Sender: TObject);
var
  p: TPerson;
begin
  //p不初始化,会自由随机分配一个指针,指针的值也是原来的自由值,不一定是nil
  if Assigned(p) then
  begin
    //只要p指针的值不是nil,这里就会返回true,所以是yes
    mmo1.Lines.Add('yes');
  end else begin
    mmo1.Lines.Add('no');
  end;
end;

procedure TForm3.btn3Click(Sender: TObject);
var
  ps: TPerson;
begin
  ps := TPerson.Create('小李', 10);

  FreeAndNil(ps); //指针的值也变成nil了

  if Assigned(ps) then
  begin
    mmo1.Lines.Add('yes');
  end else begin
    mmo1.Lines.Add('no'); //此时就自然到这里了
  end;
end;

procedure TForm3.btn4Click(Sender: TObject);
var
  ps, ps1: TPerson;
begin
  ps := TPerson.Create('小李', 10);

  ps1 := ps;

  FreeAndNil(ps);

  if Assigned(ps) then
  begin
    mmo1.Lines.Add('yes');
  end else begin
    mmo1.Lines.Add('no'); //ps指针的值是nil了,所以no
  end;

  if Assigned(ps1) then
  begin
    mmo1.Lines.Add('yes'); //ps1指针的值不是nil
    //mmo1.Lines.Add(ps1.name); //这里就会报错,因为ps堆中的数据已经释放
  end else begin
    mmo1.Lines.Add('no');
  end;

  //显然上面不是我们要的结果,使用我们自己的函数
  if AssignedEx(ps1, TPerson) then
  begin
    mmo1.Lines.Add('yes');
  end else begin
    mmo1.Lines.Add('no'); //由于ps1指向的堆中数据已经不存在,这里会no
  end;
end;

procedure TForm3.btn5Click(Sender: TObject);
var
  ps, ps1: TPerson;
begin
  ps := TPerson.Create('小李', 10);

  ps1 := ps;

  FreeAndNil(ps);

  if Assigned(ps) then
  begin
    mmo1.Lines.Add('yes');
  end else begin
    mmo1.Lines.Add('no'); //ps指针的值是nil了,所以no
  end;

  if Assigned(ps1) then
  begin
    mmo1.Lines.Add('yes'); //ps1指针的值不是nil
    //mmo1.Lines.Add(ps1.name); //这里就会报错,因为ps堆中的数据已经释放
  end else begin
    mmo1.Lines.Add('no');
  end;

  //显然上面不是我们要的结果,使用我们自己的函数
  if AssignedEx(ps1, TPerson) then
  begin
    mmo1.Lines.Add('yes');
  end else begin
    mmo1.Lines.Add('no'); //由于ps1指向的堆中数据已经不存在,这里会no
  end;
end;

end.

======================== 2024.2.20 更新,废弃 assignedEx 函数,以后不要使用了 ====================

首先看下 官方 对 assigned的解释,官方已经说明了 本身 assigned就无法应对 悬浮指针:

 

官方说明了2点:

1. assigned的主要用途是:

主要是 对象事件 和 过程时,不能与 nil 进行比较,应该使用 assigned,其它情况 要使用 nil进行对比是 更好的选择;

 

2. 对悬浮指针无效,悬浮指针是 开发人员设计的问题,开发设计时,要避开 悬浮指针的问题,是更好的习惯,所以废弃 assignedEx,之前的博客不删除;

 

使用try...except块进行访问测试:
一种更激进的方法是尝试访问指针指向的内存,并捕获可能发生的访问违规异常。这种方法不是推荐的做法,因为它可能会导致程序崩溃或其他不可预测的行为。

var  
  MyPointer: Pointer;  
  TestValue: Integer;  
begin  
  // 假设MyPointer在某处被赋值  
  if MyPointer <> nil then  
  begin  
    try  
      // 尝试读取指针指向的内存  
      TestValue := PInteger(MyPointer)^;  
      // 如果没有异常,说明读取操作成功了,但这并不保证指针完全有效  
    except  
      on E: Exception do  
        // 捕获异常,说明指针无效或访问违规  
    end;  
  end;  
end;

请注意,上面的try...except块方法是非常危险的,因为它实际上尝试访问可能是无效的内存地址。在大多数情况下,你应该避免这样做,除非你完全了解你在做什么,以及可能的后果。

通常,最好的做法是始终确保你的代码在逻辑上不会导致无效的指针引用。这包括正确地初始化指针,确保在释放对象后将指针设置为nil,以及避免悬挂指针(dangling pointers,即指向已经释放的内存的指针)。在Delphi中,使用智能指针(如TInterfacedObject和引用计数)也可以帮助管理对象的生命周期,并减少无效指针引用的风险。

 

===========

delphi 如何判断一个指针是 dangling pointers?????

在 Delphi 中,没有直接的方式来判断一个指针是否是 dangling pointer(悬挂指针)。悬挂指针是指向已经被释放的内存区域的指针。由于 Delphi(或任何编程语言)无法跟踪内存释放后的使用情况,因此无法直接检测一个指针是否悬挂。

然而,你可以采取一些编程实践来避免产生悬挂指针,并在可能的情况下减少它们的影响:

  1. 初始化指针:始终确保在使用指针之前将其初始化为 nil。这样,如果指针未被正确赋值,你可以通过检查它是否为 nil 来避免访问未初始化的内存。

  2. 及时置空:在释放指针所指向的内存后,立即将指针设置为 nil。这是一种良好的编程习惯,可以帮助你避免在释放后意外地使用指针。

  3. 使用智能指针:Delphi 支持智能指针的概念,尽管它不像 C++ 那样内置了智能指针类型。你可以通过实现引用计数或使用接口来达到类似的效果。智能指针可以自动管理对象的生命周期,从而减少悬挂指针的风险。

  4. 避免野指针:野指针是指那些指向未知或无效内存的指针。避免使用野指针可以减少悬挂指针的产生。确保你的代码逻辑正确,不要保留指向已释放对象的指针。

  5. 内存管理工具:使用像 FastMM、madExcept、AQTime 等内存管理工具来检测内存泄漏和访问违规。这些工具可以帮助你发现代码中的问题,包括悬挂指针的使用。

  6. 代码审查:定期进行代码审查,检查是否有可能导致悬挂指针的代码模式。这包括检查对象的创建、使用和释放过程,确保它们都是正确的。

  7. 异常处理:虽然异常处理不能直接检测悬挂指针,但它可以帮助你在访问违规时捕获异常,从而避免程序崩溃。然而,依赖异常处理来管理悬挂指针是不良的编程习惯;它应该被视为最后的防线,而不是解决问题的主要手段。

请记住,悬挂指针问题本质上是关于内存管理的,因此你需要确保你的代码遵循良好的内存管理实践。在 Delphi 中,这通常意味着正确地创建、使用和释放对象,以及谨慎地处理指针。

posted on 2023-05-28 05:43  del88  阅读(170)  评论(0编辑  收藏  举报