当前位置 : 主页 > 编程语言 > delphi >

delphi – 使用类运算符是否允许隐式类型转换?

来源:互联网 收集:自由互联 发布时间:2021-06-23
我的记录看起来像: TBigint = record PtrDigits: Pointer; -- The data is somewhere else. Size: Byte; MSB: Byte; Sign: Shortint; ... class operator Implicit(a: TBigint): TBigint; -- is this allowed? .... 代码是类前运算符遗留代
我的记录看起来像:

TBigint = record
    PtrDigits: Pointer;                  <-- The data is somewhere else.
    Size: Byte;
    MSB: Byte;
    Sign: Shortint;
    ...
    class operator Implicit(a: TBigint): TBigint;  <<-- is this allowed?
    ....

代码是类前运算符遗留代码,但我想添加运算符.

我知道数据应该存储在一个动态的byte数组中,但我不想更改代码,因为所有的东西都在x86-assembly中.

我想跟随代码触发底部的类操作符:

procedure test(a: TBignum);
var b: TBignum;
begin
  b:= a;  <<-- naive copy will tangle up the `PtrDigit` pointers.
  ....

如果我将隐式类型转换添加到自身,是否会执行以下代码?

class operator TBigint.Implicit(a: TBigint): TBigint;
begin
  sdpBigint.CreateBigint(Result, a.Size);
  sdpBigint.CopyBigint(a, Result);
end;

(如果按预期工作,将测试并添加答案).

我的 first answer试图阻止重写赋值运算符的想法.我仍然坚持这个答案,因为很多问题都可以用对象更好地解决.

但是,David非常正确地指出TBigInt是作为利用运算符重载的记录实现的.即a:= b c;.这是坚持基于记录的实现的一个非常好的理由.

因此,我提出了这种替代解决方案,可以一石二鸟:

>它消除了我在其他答案中解释的内存管理风险.
>并提供了一种实现Copy-on-Write语义的简单机制.

(我仍然建议除非有充分的理由保留基于记录的解决方案,否则请考虑切换到基于对象的解决方案.)

总体思路如下:

>定义一个表示BigInt数据的接口. (这最初可以是极简主义的,并且仅支持指针的控制 – 如我的示例所示.这将使现有代码的初始转换更容易.)
>定义将由TBigInt记录使用的上述接口的实现.
>接口解决了第一个问题,因为接口是托管类型;当记录超出范围时,Delphi将取消引用该接口.因此,当不再需要时,底层对象将自我毁灭.
>界面还提供了解决第二个问题的机会,因为我们可以检查RefCount以了解我们是否应该写入Copy-On.
>请注意,从长期来看,将一些BigInt实现从记录转移到类&接口.

下面的代码是精简的“大型int”实现,纯粹是为了说明这些概念. (即,“大”整数仅限于常规的32位数,并且仅实现了添加.)

type
  IBigInt = interface
    ['{1628BA6F-FA21-41B5-81C7-71C336B80A6B}']
    function GetData: Pointer;
    function GetSize: Integer;
    procedure Realloc(ASize: Integer);
    function RefCount: Integer;
  end;

type
  TBigIntImpl = class(TInterfacedObject, IBigInt)
  private
    FData: Pointer;
    FSize: Integer;
  protected
    {IBigInt}
    function GetData: Pointer;
    function GetSize: Integer;
    procedure Realloc(ASize: Integer);
    function RefCount: Integer;
  public
    constructor CreateCopy(ASource: IBigInt);
    destructor Destroy; override;
  end;

type
  TBigInt = record
    PtrDigits: IBigInt;
    constructor CreateFromInt(AValue: Integer);
    class operator Implicit(AValue: TBigInt): Integer;
    class operator Add(AValue1, AValue2: TBigInt): TBigInt;
    procedure Add(AValue: Integer);
  strict private
    procedure CopyOnWriteSharedData;
  end;

{ TBigIntImpl }

constructor TBigIntImpl.CreateCopy(ASource: IBigInt);
begin
  Realloc(ASource.GetSize);
  Move(ASource.GetData^, FData^, FSize);
end;

destructor TBigIntImpl.Destroy;
begin
  FreeMem(FData);
  inherited;
end;

function TBigIntImpl.GetData: Pointer;
begin
  Result := FData;
end;

function TBigIntImpl.GetSize: Integer;
begin
  Result := FSize;
end;

procedure TBigIntImpl.Realloc(ASize: Integer);
begin
  ReallocMem(FData, ASize);
  FSize := ASize;
end;

function TBigIntImpl.RefCount: Integer;
begin
  Result := FRefCount;
end;

{ TBigInt }

class operator TBigInt.Add(AValue1, AValue2: TBigInt): TBigInt;
var
  LSum: Integer;
begin
  LSum := Integer(AValue1) + Integer(AValue2);
  Result.CreateFromInt(LSum);
end;

procedure TBigInt.Add(AValue: Integer);
begin
  CopyOnWriteSharedData;

  PInteger(PtrDigits.GetData)^ := PInteger(PtrDigits.GetData)^ + AValue;
end;

procedure TBigInt.CopyOnWriteSharedData;
begin
  if PtrDigits.RefCount > 1 then
  begin
    PtrDigits := TBigIntImpl.CreateCopy(PtrDigits);
  end;
end;

constructor TBigInt.CreateFromInt(AValue: Integer);
begin
  PtrDigits := TBigIntImpl.Create;
  PtrDigits.Realloc(SizeOf(Integer));
  PInteger(PtrDigits.GetData)^ := AValue;
end;

class operator TBigInt.Implicit(AValue: TBigInt): Integer;
begin
  Result := PInteger(AValue.PtrDigits.GetData)^;
end;

在构建提出的解决方案时,编写了以下测试.他们证明:一些基本功能,即写时复制按预期工作,并且没有内存泄漏.

procedure TTestCopyOnWrite.TestCreateFromInt;
var
  LBigInt: TBigInt;
begin
  LBigInt.CreateFromInt(123);
  CheckEquals(123, LBigInt);
  //Dispose(PInteger(LBigInt.PtrDigits)); //I only needed this until I 
                                          //started using the interface
end;

procedure TTestCopyOnWrite.TestAssignment;
var
  LValue1: TBigInt;
  LValue2: TBigInt;
begin
  LValue1.CreateFromInt(123);
  LValue2 := LValue1;
  CheckEquals(123, LValue2);
end;

procedure TTestCopyOnWrite.TestAddMethod;
var
  LValue1: TBigInt;
begin
  LValue1.CreateFromInt(123);
  LValue1.Add(111);

  CheckEquals(234, LValue1);
end;

procedure TTestCopyOnWrite.TestOperatorAdd;
var
  LValue1: TBigInt;
  LValue2: TBigInt;
  LActualResult: TBigInt;
begin
  LValue1.CreateFromInt(123);
  LValue2.CreateFromInt(111);

  LActualResult := LValue1 + LValue2;

  CheckEquals(234, LActualResult);
end;

procedure TTestCopyOnWrite.TestCopyOnWrite;
var
  LValue1: TBigInt;
  LValue2: TBigInt;
begin
  LValue1.CreateFromInt(123);
  LValue2 := LValue1;

  LValue1.Add(111); { If CopyOnWrite, then LValue2 should not change }

  CheckEquals(234, LValue1);
  CheckEquals(123, LValue2);
end;

编辑

添加了一个测试,证明使用TBigInt作为过程的值参数.

procedure TTestCopyOnWrite.TestValueParameter;
  procedure CheckValueParameter(ABigInt: TBigInt);
  begin
    CheckEquals(2, ABigInt.PtrDigits.RefCount);
    CheckEquals(123, ABigInt);
    ABigInt.Add(111);
    CheckEquals(234, ABigInt);
    CheckEquals(1, ABigInt.PtrDigits.RefCount);
  end;
var
  LValue: TBigInt;
begin
  LValue.CreateFromInt(123);
  CheckValueParameter(LValue);
end;
网友评论