首页 > 解决方案 > 通用树节点结构:释放子节点的问题

问题描述

基于这个GenericTree我已经实现了以下通用树节点结构:

type
  TTreeNode<T>=class
  private
    procedure FreeChildNodes; 
    procedure RemoveMyselfFromParentChildNodesList;
    function GetIndexInParentChildNodesList: Integer;
  public
    NodeData: T;
    ChildNodes: TList<TTreeNode<T>>;
    ParentNode: TTreeNode<T>;
    constructor Create(const AParentNode: TTreeNode<T>); overload;
    destructor Destroy; override;
  end;

implementation

constructor TTreeNode<T>.Create(const AParentNode: TTreeNode<T>);
begin
  inherited Create;
  ParentNode:=AParentNode;
  ChildNodes:=TList<TTreeNode<T>>.Create;
end;

destructor TTreeNode<T>.Destroy;
begin
  FreeChildNodes;
  RemoveMyselfFromParentChildNodesList;
  ChildNodes.Free;
  inherited;
end;

procedure TTreeNode<T>.FreeChildNodes;
var
  i: Integer;
begin
  for i := ChildNodes.Count-1 downto 0 do
  begin
    ChildNodes[i].Free;
  end;
  ChildNodes.Clear;
end;

function TTreeNode<T>.GetIndexInParentChildNodesList: Integer;
var
  i: Integer;
begin
  Result:=-1;
  if ParentNode<>nil then
  begin
    Result:=ParentNode.ChildNodes.IndexOf(Self);
  end;
end;

procedure TTreeNode<T>.RemoveMyselfFromParentChildNodesList;
begin
  if ParentNode<>nil then
  begin
    ParentNode.ChildNodes.Delete(GetIndexInParentChildNodesList);
  end;
end;

这工作正常。

现在我想创建一个具有特定对象类型的后代类。

对象类型为:

type
  TMyObject=class
  public
    Value: string;
    constructor Create; override;
    destructor Destroy; override;
  end;

和新的后代类:

type
  TMyTreeNode=class(TTreeNode<TMyObject>)
  private
  public
    constructor Create(const AParentNode: TMyTreeNode); overload;
    destructor Destroy; override;
  end;

implementation

constructor TMyTreeNode.Create(const AParentNode: TMyTreeNode);
begin
  inherited Create(AParentNode);
  NodeData:=TMyObject.Create;
end;

destructor TMyTreeNode.Destroy;
begin
  NodeData.Free;
  inherited;
end;

当我释放TMyTreeNode带有TMyTreeNode.Destroy继承TTreeNode<T>.Destroy的 a 时,会调用以ChildNodes递归方式释放。问题是所有的子节点都被释放,TTreeNode<T>.Destroy因此没有TMyObjects被释放,留下内存泄漏。

我也尝试使用TObjectList而不是TListfor ChildNodes。然而,TObjectList在我释放子节点之前,它似乎会自行破坏。

如何解决这个问题呢?

标签: delphigenericstree

解决方案


你可以使用:

procedure TTreeNode<T>.FreeChildNodes;
var
  i: Integer;
begin
  for i := ChildNodes.Count-1 downto 0 do
  begin
    ChildNodes[i].NodeData.free;
    ChildNodes[i].Free;
  end;
  ChildNodes.Clear;
end;

如果析构函数只影响父类型的字段,则无需重写析构函数。在您的情况下,TMyTreeNode.Destroy唯一从父级释放 NodeData,因此您最好在TTreeNode<T>.FreeChildNodes过程中执行此操作。


推荐阅读