• 学习 TList 类的实现[6]


    实现 TMyList.Add 函数.

    TList 中的 Add 函数用到了一个 Grow 方法, 它的原理是元素越多就为以后准备更多内存, 我们这里省略为预留 4 个元素的内存;

    TList 中的 Add 函数还同时触动了一个 Notify 方法, 这应该是为它们的子类准备的(估计是用它来激发一个事件的), 也不要了.
    function TMyList.Add(Item: Pointer): Integer;
    begin
      {如果预申请的内存用完了, 再申请够 4 个元素用的}
      if FCount = FCapacity then SetCapacity(FCapacity + 4);
    
      {Add 是添加在最后面, 把指针赋予数组}
      FList^[FCount] := Item;
    
      {函数返回}
      Result := FCount;
    
      {元素总数 + 1}
      Inc(FCount);
    end;

    再实现 TMyList.Delete 过程.

    同前, 把错误处理也简化成一个异常; 也省略了对 Notify 方法的触动.

    其中用到了 System.Move, 可以参考: http://www.cnblogs.com/del/archive/2008/03/27/1126226.html

    这里有一个问题是需要高度注意的: TList 在删除一个元素(它的元素就是指针)时, 并没有释放指针指向的对象, 只是从列表开除;
    如果要同时 Free 掉对象, 应该使用 Contnrs 单元下的 TObjectList 类.
    procedure TMyList.Delete(Index: Integer);
    begin
      {如果给的 Index 不符合要求, 就抛出异常}
      if (Index < 0) or (Index >= FCount) then
        raise Exception.CreateFmt('非法的 Index:%d', [Index]);
    
      {删除的过程就是把当前元素后面的所有元素向前挪动一个位置}
      if Index < FCount then
        System.Move(FList^[Index + 1], FList^[Index], (FCount - Index) * SizeOf(Pointer));
    
      {总数 - 1}
      Dec(FCount);
    end;

    还要实现 TMyList.SetCount 方法.

    之前我没有想到 Count 属性还是可写的; 这可够狠的, 譬如已经有 100 个元素, 如果让 Count := 1; 这一下就要删除后面 99 个元素!

    还有不理解的是: 譬如已经有 100 个元素, 如果让 Count := 200; 那后面的 100 个元素即便是填充了空字符, 用指针读过来也不是对象啊? 觉得不妥. 不过暂时也这样了.
    procedure TMyList.SetCount(const Value: Integer);
    var
      i: Integer;
    begin
      {如果参数非法, 抛出异常}
      if (Value < 0) or (Value > MaxListSize) then
        raise Exception.CreateFmt('非法数据:%d', [Value]);
    
      {如果预留内存不够, 马上申请}
      if Value > FCapacity then SetCapacity(Value);
    
      {如果参数大于元素总数没有赋值的用空字符填充}
      {如果参数小于元素总数, 删除多余的元素      }
      if Value > FCount then
        FillChar(FList^[FCount], (Value - FCount) * SizeOf(Pointer), 0)
      else
        for i := FCount - 1 downto Value do
          Delete(I);
    
      {新的元素总数}
      FCount := Value;
    end;

    还有一个 TMyList.Clear 方法.

    因为不用考虑列表中对象释放的问题, 这个就简单多了.
    procedure TMyList.Clear;
    begin
      SetCount(0);
      SetCapacity(0);
    end;

    至此, 已经声明的方法都实现了, 这个 TMyList 类也该能凑合使用了.

    源码如下:
    unit MyList;
    
    interface
    
    uses SysUtils;
    
    const
      MaxListSize = Maxint div 16;
    
    type
      PPointerList = ^TPointerList;
      TPointerList = array[0..MaxListSize - 1] of Pointer;
    
      TMyList = class(TObject)
      private
        FList: PPointerList;
        FCount: Integer;
        FCapacity: Integer;
        procedure SetCapacity(const Value: Integer);
        procedure SetCount(const Value: Integer);
      public
        destructor Destroy; override;
        function Add(Item: Pointer): Integer;
        procedure Clear;
        procedure Delete(Index: Integer);
        property Capacity: Integer read FCapacity write SetCapacity;
        property Count: Integer read FCount write SetCount;
        property List: PPointerList read FList;
      end;
    
    implementation
    
    { TMyList }
    
    function TMyList.Add(Item: Pointer): Integer;
    begin
      if FCount = FCapacity then SetCapacity(FCapacity + 4);
      FList^[FCount] := Item;
      Result := FCount;
      Inc(FCount);
    end;
    
    procedure TMyList.Clear;
    begin
      SetCount(0);
      SetCapacity(0);
    end;
    
    procedure TMyList.Delete(Index: Integer);
    begin
      if (Index < 0) or (Index >= FCount) then
        raise Exception.CreateFmt('非法的 Index:%d', [Index]);
    
      if Index < FCount then
        System.Move(FList^[Index+1], FList^[Index], (FCount-Index)* SizeOf(Pointer));
    
      Dec(FCount);
    end;
    
    destructor TMyList.Destroy;
    begin
      Clear;
      inherited;
    end;
    
    procedure TMyList.SetCapacity(const Value: Integer);
    begin
      if (Value < FCount) or (Value > MaxListSize) then
        raise Exception.CreateFmt('非法数据:%d', [Value]);
    
      if FCapacity <> Value then
      begin
        ReallocMem(FList, Value * SizeOf(Pointer));
        FCapacity := Value;
      end;
    end;
    
    procedure TMyList.SetCount(const Value: Integer);
    var
      i: Integer;
    begin
      if (Value < 0) or (Value > MaxListSize) then
        raise Exception.CreateFmt('非法数据:%d', [Value]);
    
      if Value > FCapacity then SetCapacity(Value);
    
      if Value > FCount then
        FillChar(FList^[FCount], (Value - FCount) * SizeOf(Pointer), 0)
      else
        for i := FCount - 1 downto Value do
          Delete(I);
    
      FCount := Value;
    end;
    
    end.

  • 相关阅读:
    【数据结构】线段树(Segment Tree)
    c++基础--数字读入及优化
    转:async异步、thread多线程
    走进 Akka.NET
    基于 Docker 的 DevOps 搭建
    (翻译)与.NET容器映像保持同步
    (翻译)使用 AppCenter 持续输出导出到 Application Insights
    (翻译)Xamarin.Essentials 最新预览版的更多跨平台 API
    (翻译)在 Xamarin 应用中使用 MongoDB
    (翻译)一起使用 .NET 和 Docker——DockerCon 2018 更新
  • 原文地址:https://www.cnblogs.com/shijiaoyun/p/3844510.html
Copyright © 2020-2023  润新知