0

我试图实现一个类似于 Delphi 的 TList 类的顺序链表类。在这里,类将自动增加/减少 count 属性,而不是允许用户设置 count 属性。因此,该类不会在 setcount 过程中初始化数组,而是在将对象引用添加到数组之前初始化数组元素。这是代码:

    PObjectarray = ^TObjectarray;
    TObjectarray = array of TObject;
    TSequentialList = class
    private
      FObjects: PObjectarray;
      FCapacity: Integer;
      FCount: Integer;
    protected
      procedure GrowList;virtual;
      procedure SetCapacity(aValue: Integer);
    public
      constructor Create(aCapacity: Integer); overload;
      Constructor Create;overload;
      procedure Add(aObject:TObject);
      procedure Delete(aIndex:Integer);
      destructor Destroy;override;    
   End;

执行: -

    procedure TSequentialList.Add(aObject: TObject);
    begin
      if FCount=FCapacity then
        GrowList;
      FillChar(FObjects^[FCount],sizeof(TObject),0);//Initialize the FCount's member space
      FObjects^[FCount]:=aObject;
      Inc(FCount);
    end;

    constructor TSequentialList.Create(aCapacity: Integer);
    begin
      FCapacity := 0;
      FCount := 0;
      SetCapacity(aCapacity);
    end;

    constructor TSequentialList.Create;
    begin
      FCapacity := 0;
      FCount := 0;
    end;

    procedure TSequentialList.Delete(aIndex: Integer);
    var tmpObj:TObject;
    i:Integer;
    begin
     if (aIndex>=FCount) or (aIndex<0) then raise ELinkedListException.Create('Invalid Index in Delete..!');
     tmpObj:=FObjects^[aIndex];
     tmpObj.Free;

     System.Move(FObjects^[aIndex+1],FObjects^[aIndex],(FCount-aIndex)* SizeOf(TObject));
     Dec(FCount);
    end;

    destructor TSequentialList.Destroy;
    begin
      SetCapacity(0);
      inherited;
    end;

    procedure TSequentialList.GrowList;
    var delta:Integer;
    begin
      if FCapacity>64 then
        delta:=FCapacity div 64
      else if FCapacity>16  then
           delta:=8
      else delta:=4;
      SetCapacity(FCapacity+delta);
    end;

    procedure TSequentialList.SetCapacity(aValue: Integer);
    var i:Integer;
    begin
      if FCapacity <> aValue then begin
         if aValue<FCount then begin
            for i := FCount-1 downto  aValue do
               Delete(i);
         end;
         ReallocMem(FObjects, aValue*sizeof(TObject));
         FCapacity := aValue;
      end;
    end;

用法://不起作用

    procedure TForm2.Button1Click(Sender: TObject);
    var lst:TSequentialList;
    obj:TIntObj;
    begin
      lst:=TSequentialList.Create(4);
      obj:=TIntObj.Create(10);
      lst.Add(obj);
      lst.Add(TIntObj.Create(20));
      lst.Free;
    end;

退出上述方法后系统挂起。请指导我程序有什么问题。

4

2 回答 2

1

我首先要说这不是一个链表。这是一个非常不同的结构。TObjectList更重要的是,您应该简单地使用 Delphi 提供的内置类,而不是重新实现。


您的代码的根本问题是您错误地使用了动态数组。您不得使用原始分配函数,如ReallocMem. GetMem等函数ReallocMem用于原始指针。动态数组的生命周期由编译器/运行时库管理。

要分配动态数组,您必须使用SetLength.

另一个重大错误是调用System.Move移动了错误数量的元素。

下面的代码是您的代码的简化和更正变体。

type
  TMyObjectList = class
  private
    FObjects: array of TObject;
    FCount: Integer;
  protected
    procedure GrowList;
    procedure SetCapacity(aValue: Integer);
  public
    constructor Create(aCapacity: Integer); overload;
    constructor Create; overload;
    destructor Destroy; override;
    procedure Add(aObject: TObject);
    procedure Delete(aIndex: Integer);
  end;

constructor TMyObjectList.Create(aCapacity: Integer);
begin
  inherited Create;
  SetCapacity(aCapacity);
end;

constructor TMyObjectList.Create;
begin
  Create(0);
end;

destructor TMyObjectList.Destroy;
begin
  SetCapacity(0);
  inherited;
end;

procedure TMyObjectList.Add(aObject: TObject);
begin
  if FCount = Length(FObjects) then
    GrowList;
  FObjects[FCount] := aObject;
  Inc(FCount);
end;

procedure TMyObjectList.Delete(aIndex: Integer);
begin
  if (aIndex >= FCount) or (aIndex < 0) then
    raise Exception.Create('Invalid Index in Delete..!');

  FObjects[aIndex].Free;
  System.Move(FObjects[aIndex+1], FObjects[aIndex], 
    (FCount-aIndex-1)*SizeOf(TObject));
  Dec(FCount);
end;

procedure TMyObjectList.GrowList;
var
  delta: Integer;
begin
  if Length(FObjects) > 64 then
    delta := Length(FObjects) div 64
  else if Length(FObjects) > 16 then
    delta := 8
  else
    delta := 4;
  SetCapacity(Length(FObjects) + delta);
end;

procedure TMyObjectList.SetCapacity(aValue: Integer);
var
  i: Integer;
begin
  if Length(FObjects) <> aValue then
  begin
    for i := aValue to FCount-1 do
      FObjects[i].Free;
    SetLength(FObjects, aValue);
  end;
end;

但我强烈建议您使用已知有效的内置 RTL 类。例如,如果您使用的是现代版本的 Delphi,您可以使用System.Generics.Collections. 在你的情况下TObjectList<T>似乎是你需要的。在较旧的 Delphi 版本中,您将TObjectListContnrs单元中使用。

于 2012-12-24T10:31:25.213 回答
0

删除方法中的这一行绝对是错误的:

 for i := FCount-1 downto  aValue do
           Delete(i);

Delete(aValue)而是打电话。

但是,您的代码没有实现链表。看起来很像您正在尝试实现 TObjectList。

于 2012-12-24T07:46:47.667 回答