Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/delphi/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Class Delphi:在类中创建TStringList时内存泄漏_Class_Delphi_Memory Leaks_Tstringlist - Fatal编程技术网

Class Delphi:在类中创建TStringList时内存泄漏

Class Delphi:在类中创建TStringList时内存泄漏,class,delphi,memory-leaks,tstringlist,Class,Delphi,Memory Leaks,Tstringlist,我有这段代码 TSql = class private FConnString: TStringList; public property ConnString: TStringList read FConnString write FConnString; constructor Create; destructor Destroy; end; var Sql: TSql; ... implementation {$R *.dfm}

我有这段代码

TSql = class
  private
    FConnString: TStringList;
  public
    property ConnString: TStringList read FConnString write FConnString;
    constructor Create;
    destructor Destroy;
  end;

var 
  Sql: TSql;

...

implementation

{$R *.dfm}

constructor TSql.Create;
begin
  //inherited Create;
  FConnString:=TStringList.Create;
end;

destructor TSql.Destroy;
begin
  FConnString.Free;
  //inherited Destroy;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  Sql.Create;
  Sql.ConnString.Add('something');
  showmessage(Sql.ConnString.Text);
  Sql.Destroy;
end;
为什么在创建FConnString时会在按下按钮后产生内存泄漏

。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。 .................................. .................................. 编辑实际问题 问题中的原始代码如下:

procedure TForm1.Button1Click(Sender: TObject);
begin
  Sql.Create;
  Sql.ConnString.Add('something');
  showmessage(Sql.ConnString.Text);
  Sql.Destroy;
end;
问题行是
Sql.Create
应该是
Sql:=TSql.Create。这导致内存泄漏的原因如下:

procedure TForm1.Button1Click(Sender: TObject);
begin
  Sql.Create;
  Sql.ConnString.Add('something');
  showmessage(Sql.ConnString.Text);
  Sql.Destroy;
end;
  • Sql.Createnil引用调用
  • 这将调用
    TStringList.Create
    并尝试将结果分配给
    FConnString
  • 因为Sql是一个nil引用,这会触发访问冲突
  • 问题是无法销毁创建的
    TStringList
    实例
原始答案中的附加问题 您的析构函数是虚拟的,您没有重写。
你没有调用继承的析构函数

TSql = class
  private
    FConnString: TStringList;
  public
    property ConnString: TStringList read FConnString write FConnString;
    constructor Create;
    destructor Destroy; override; //Correction #1
  end;

destructor TSql.Destroy;
begin
  FConnString.Free;
  inherited Destroy; //Correction #2
end;

编辑 一些一般提示:

  • 我为您使用组合(使
    FConnString
    成为成员而不是继承
    TStringList
    )而喝彩。然而,如果将其公开,您将失去许多好处。具体来说,您将面临违规行为。我不是说永远不要这样做。但是请注意,如果大量客户端代码直接访问
    ConnString
    ,则可能会造成维护问题

  • 声明
    FConnString:TStringList
    违反了安全性原则
    TStringList
    TStrings
    的一个特定实现,此声明阻止使用
    TStrings
    的其他子类。这更像是一个与#1结合的问题:如果在几年后,您发现并希望切换到与
    TStringList
    绑定的
    TStringList
    客户端代码的不同/更好的子类实现,那么现在会产生更多的工作和风险。基本上,首选方案可归纳为:

    • 将变量声明为abtract基类型
    • 创建实例作为特定选择的实现子类
    • 让多态性确保在重写的方法中正确应用子类行为

  • 这也是一个一般性的指导方针。如果您特别需要访问在层次结构的
    TStringList
    级别添加的属性/方法,那么您必须绑定到该类。但是如果您不需要它,就不要这样做。

    您忘记了用
    重写
    说明符声明
    Destroy()
    ,因此在销毁对象时实际上不会调用
    TSql.Destroy

    destructor Destroy; override;
    

    对象创建不正确。它必须是:

    Sql:=TSql.Create;
    

    内存泄漏就从这里开始了。

    我看到了两件事。关于析构函数缺少“覆盖”的其他评论和回答已经涵盖了第一个问题

    TSql = class
      private
        FConnString: TStringList;
      public
        property ConnString: TStringList read FConnString write FConnString;
        constructor Create;
        destructor Destroy; override; //Correction #1
      end;
    
    destructor TSql.Destroy;
    begin
      FConnString.Free;
      inherited Destroy; //Correction #2
    end;
    
    第二个问题是财产申报本身。通常,您不应该声明引用“write”子句中的对象字段的属性。原因是,分配给该属性将“泄漏”该字段中的现有实例。对属性声明的“write”子句使用一种方法:

    property ConnString: TStringList read FConnString write SetConnString;
    ...
    procedure TSql.SetConnString(Value: TStringList);
    begin
      FConnString.Assign(Value);
    end;
    

    还要注意,此方法也不会覆盖FConnString字段。它只是将值TStringList的“值”或“内容”复制到FConnString实例中。通过这种方式,TSql实例在该字段的生存期内完全处于控制状态。代码负责分配该属性以控制值TStringlist的生存期。

    让我展开。如果您能够在没有错误的情况下调用Sql.Create,那么您已经有了一个有效的Tsql实例。当您对其调用Create时,它会创建另一个FConnString。看,去掉那一行,你就没事了。当然应该是
    Sql:=TSql.Create?耶。。。我的错。你是赖特!它起作用了!无论如何,我的真实代码仍然有问题。我会更深入地寻找。在本文中,我的原始代码是Sql:=TSql.Create,但仍然存在问题。我将问题回滚。瞄准一个移动的目标是没有乐趣的。这个全局变量让我感觉析构函数已经是虚拟的,在
    TObject
    中。你需要声明is as
    override
    。修正2无法修复漏洞,TObject。Destroy不起任何作用。“1也是如此。”SertacAkyuz说得对,它们是额外的问题。我已经更新了对实际内存泄漏的解释。@Craig-谢谢。我确信这不是问题所在,因为问题中会提到AV。问题是,这个问题是假的。描述得很好的答案。除了这个错误,我从你详细的回答中学到了更多的东西。谢谢@CraigYoung!没有人打电话?他正在调用Tsql.Destroy。虽然他应该重写
    Destroy
    ,但在这种情况下,代码直接调用
    Destroy
    ,因此此问题并没有解决真正的问题。不,这会导致AV。@SertacAkyuz我对您的评论投了赞成票,然后意识到错误实际上是导致内存泄漏的原因(见我的最新答案)。(唉,我不能取消对评论的投票。)@Craig-我认为我的评论没有任何问题。提问者肯定没有AV,否则他会在他的问题中有AV。