顺序列表实现

时间:2012-12-24 04:40:41

标签: delphi

我试图实现类似于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;

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

2 个答案:

答案 0 :(得分:1)

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


您的代码的基本问题是您错误地使用动态数组。您不能使用ReallocMem之类的原始分配函数。像GetMemReallocMem等函数用于原始指针。动态数组的生命周期由编译器/运行时库管理。

要分配动态数组,您必须使用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版本中,您可以使用TObjectList单元中的Contnrs

答案 1 :(得分:0)

delete-method中的这一行肯定是错误的:

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

改为呼叫Delete(aValue)

但是,您的代码并未实现链接列表。它看起来非常像您正在尝试实现TObjectList。