如何在Delphi中为记录重载赋值运算符

6

我希望使用动态数组来进行记录类型的操作。

使用此类型的变量A和B,我希望能够执行A:=B(和其他)操作,并且能够修改A的内容而不影响B,就像下面的代码片段一样:

    type
      TMyRec = record
        Inner_Array: array of double;
      public
        procedure SetSize(n: integer);
        class operator Implicit(source: TMyRec): TMyRec;
      end;

    implementation

    procedure TMyRec.SetSize(n: integer);
    begin
      SetLength(Inner_Array, n);
    end;

    class operator TMyRec.Implicit(source: TMyRec): TMyRec;
    begin
    //here I want to copy data from source to destination (A to B in my simple example below)
    //but here is the compilator error
    //[DCC Error] : E2521 Operator 'Implicit' must take one 'TMyRec' type in parameter or result type
    end;


    var
      A, B: TMyRec;
    begin
      A.SetSize(2);
      A.Inner_Array[1] := 1;
      B := A;
      A.Inner_Array[1] := 0;
//here are the same values inside A and B (they pointed the same inner memory)

有两个问题:
1. 当我在TMyRec中不使用覆盖赋值操作符时,A:=B表示A和B(它们的Inner_Array)指向内存中相同的位置。
2. 为了避免问题1),我想使用以下方式重载赋值操作符:
class operator TMyRec.Implicit(source: TMyRec): TMyRec;
但编译器(Delphi XE)报错:
[DCC Error] : E2521 Operator 'Implicit' must take one 'TMyRec' type in parameter or result type 如何解决这些问题。 我在stackoverflow上读了几篇类似的帖子,但它们在我的情况下不起作用(如果我理解得正确的话)。
Artik

1
引入一个名为 TMyRec.Clone 的函数。 - LU RD
2
你应该将 Inner_Array 设为不可变的;像 A.Inner_Array[1] := 1; 这样的代码将被禁止 - 对 Inner_Array 的任何写入都应该创建一个新的数组实例。阅读 http://sergworks.wordpress.com/2013/04/10/on-the-operator-overloading-in-delphi/ 获取更多提示。 - kludg
@user246408 不可变的向量/矩阵类型通常很不方便,而且会导致性能下降。例如,许多矩阵算法都是就地操作的。 - David Heffernan
谢谢。我被你的观点所说服。 - Artik
https://www.thedelphigeek.com/2015/01/implementing-record-assignment-operator_9.html - Gabriel
显示剩余2条评论
2个回答

6

这是否意味着没有办法捕获赋值 A:= B 并将数据从 A 复制到 B? - Artik
2
可以在动态数组上引入COW功能。这将在写入数组时处理克隆,就像“String”类型一样。这有点混乱,但我设法让它工作。我很快会发布代码。最主要的问题是内置函数如“SetLength”,“Length”等无法作为运算符重载引入。 - LU RD

0

唯一已知的方法是使用指针,但这并不安全,因此您必须了解自己在做什么。

大致上是这样的:

type
  PMyRec = ^TMyRec;
  TMyRec = record
    MyString : string;
    class operator Implicit(aRec : PMyRec) : TMyRec;
  end;
....
class operator TMyRec.Implicit(aRec : PMyRec) : TMyRec;
begin
  if aRec = nil then // to do something...
    raise Exception.Create('Possible bug is here!');
  Result.MyString := aRec^.MyString;
end;

而且调用示例应该是这样的:

var
  aRec1, aRec2 : TMyRec;
begin
  aRec1.MyString := 'Hello ';
  aRec2.MyString := 'World';
  writeln(aRec1.MyStr, aRec2.MyStr);
  aRec2 := @aRec1;
  writeln(aRec1.MyStr, aRec2.MyStr);
end.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接