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

前端之家收集整理的这篇文章主要介绍了delphi – 使用类运算符是否允许隐式类型转换?前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我的记录看起来像:

  1. TBigint = record
  2. PtrDigits: Pointer; <-- The data is somewhere else.
  3. Size: Byte;
  4. MSB: Byte;
  5. Sign: Shortint;
  6. ...
  7. class operator Implicit(a: TBigint): TBigint; <<-- is this allowed?
  8. ....

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

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

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

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

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

  1. class operator TBigint.Implicit(a: TBigint): TBigint;
  2. begin
  3. sdpBigint.CreateBigint(Result,a.Size);
  4. sdpBigint.CopyBigint(a,Result);
  5. end;

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

解决方法

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

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

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

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

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

总体思路如下:

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

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

  1. type
  2. IBigInt = interface
  3. ['{1628BA6F-FA21-41B5-81C7-71C336B80A6B}']
  4. function GetData: Pointer;
  5. function GetSize: Integer;
  6. procedure Realloc(ASize: Integer);
  7. function RefCount: Integer;
  8. end;
  9.  
  10. type
  11. TBigIntImpl = class(TInterfacedObject,IBigInt)
  12. private
  13. FData: Pointer;
  14. FSize: Integer;
  15. protected
  16. {IBigInt}
  17. function GetData: Pointer;
  18. function GetSize: Integer;
  19. procedure Realloc(ASize: Integer);
  20. function RefCount: Integer;
  21. public
  22. constructor CreateCopy(ASource: IBigInt);
  23. destructor Destroy; override;
  24. end;
  25.  
  26. type
  27. TBigInt = record
  28. PtrDigits: IBigInt;
  29. constructor CreateFromInt(AValue: Integer);
  30. class operator Implicit(AValue: TBigInt): Integer;
  31. class operator Add(AValue1,AValue2: TBigInt): TBigInt;
  32. procedure Add(AValue: Integer);
  33. strict private
  34. procedure CopyOnWriteSharedData;
  35. end;
  36.  
  37. { TBigIntImpl }
  38.  
  39. constructor TBigIntImpl.CreateCopy(ASource: IBigInt);
  40. begin
  41. Realloc(ASource.GetSize);
  42. Move(ASource.GetData^,FData^,FSize);
  43. end;
  44.  
  45. destructor TBigIntImpl.Destroy;
  46. begin
  47. FreeMem(FData);
  48. inherited;
  49. end;
  50.  
  51. function TBigIntImpl.GetData: Pointer;
  52. begin
  53. Result := FData;
  54. end;
  55.  
  56. function TBigIntImpl.GetSize: Integer;
  57. begin
  58. Result := FSize;
  59. end;
  60.  
  61. procedure TBigIntImpl.Realloc(ASize: Integer);
  62. begin
  63. ReallocMem(FData,ASize);
  64. FSize := ASize;
  65. end;
  66.  
  67. function TBigIntImpl.RefCount: Integer;
  68. begin
  69. Result := FRefCount;
  70. end;
  71.  
  72. { TBigInt }
  73.  
  74. class operator TBigInt.Add(AValue1,AValue2: TBigInt): TBigInt;
  75. var
  76. LSum: Integer;
  77. begin
  78. LSum := Integer(AValue1) + Integer(AValue2);
  79. Result.CreateFromInt(LSum);
  80. end;
  81.  
  82. procedure TBigInt.Add(AValue: Integer);
  83. begin
  84. CopyOnWriteSharedData;
  85.  
  86. PInteger(PtrDigits.GetData)^ := PInteger(PtrDigits.GetData)^ + AValue;
  87. end;
  88.  
  89. procedure TBigInt.CopyOnWriteSharedData;
  90. begin
  91. if PtrDigits.RefCount > 1 then
  92. begin
  93. PtrDigits := TBigIntImpl.CreateCopy(PtrDigits);
  94. end;
  95. end;
  96.  
  97. constructor TBigInt.CreateFromInt(AValue: Integer);
  98. begin
  99. PtrDigits := TBigIntImpl.Create;
  100. PtrDigits.Realloc(SizeOf(Integer));
  101. PInteger(PtrDigits.GetData)^ := AValue;
  102. end;
  103.  
  104. class operator TBigInt.Implicit(AValue: TBigInt): Integer;
  105. begin
  106. Result := PInteger(AValue.PtrDigits.GetData)^;
  107. end;

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

  1. procedure TTestCopyOnWrite.TestCreateFromInt;
  2. var
  3. LBigInt: TBigInt;
  4. begin
  5. LBigInt.CreateFromInt(123);
  6. CheckEquals(123,LBigInt);
  7. //Dispose(PInteger(LBigInt.PtrDigits)); //I only needed this until I
  8. //started using the interface
  9. end;
  10.  
  11. procedure TTestCopyOnWrite.TestAssignment;
  12. var
  13. LValue1: TBigInt;
  14. LValue2: TBigInt;
  15. begin
  16. LValue1.CreateFromInt(123);
  17. LValue2 := LValue1;
  18. CheckEquals(123,LValue2);
  19. end;
  20.  
  21. procedure TTestCopyOnWrite.TestAddMethod;
  22. var
  23. LValue1: TBigInt;
  24. begin
  25. LValue1.CreateFromInt(123);
  26. LValue1.Add(111);
  27.  
  28. CheckEquals(234,LValue1);
  29. end;
  30.  
  31. procedure TTestCopyOnWrite.TestOperatorAdd;
  32. var
  33. LValue1: TBigInt;
  34. LValue2: TBigInt;
  35. LActualResult: TBigInt;
  36. begin
  37. LValue1.CreateFromInt(123);
  38. LValue2.CreateFromInt(111);
  39.  
  40. LActualResult := LValue1 + LValue2;
  41.  
  42. CheckEquals(234,LActualResult);
  43. end;
  44.  
  45. procedure TTestCopyOnWrite.TestCopyOnWrite;
  46. var
  47. LValue1: TBigInt;
  48. LValue2: TBigInt;
  49. begin
  50. LValue1.CreateFromInt(123);
  51. LValue2 := LValue1;
  52.  
  53. LValue1.Add(111); { If CopyOnWrite,then LValue2 should not change }
  54.  
  55. CheckEquals(234,LValue1);
  56. CheckEquals(123,LValue2);
  57. end;

编辑

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

  1. procedure TTestCopyOnWrite.TestValueParameter;
  2. procedure CheckValueParameter(ABigInt: TBigInt);
  3. begin
  4. CheckEquals(2,ABigInt.PtrDigits.RefCount);
  5. CheckEquals(123,ABigInt);
  6. ABigInt.Add(111);
  7. CheckEquals(234,ABigInt);
  8. CheckEquals(1,ABigInt.PtrDigits.RefCount);
  9. end;
  10. var
  11. LValue: TBigInt;
  12. begin
  13. LValue.CreateFromInt(123);
  14. CheckValueParameter(LValue);
  15. end;

猜你在找的Delphi相关文章