Глубокая копия записи с R1: = R2, или Есть ли хороший способ реализовать матрицу NxM с записью?

Я реализую матрицу (класс) N x M с записью и внутренним динамическим массивом, как показано ниже.

TMat = record
public     
  // contents
  _Elem: array of array of Double;

  //
  procedure SetSize(Row, Col: Integer);

  procedure Add(const M: TMat);
  procedure Subtract(const M: TMat);
  function Multiply(const M: TMat): TMat;
  //..
  class operator Add(A, B: TMat): TMat;
  class operator Subtract(A, B: TMat): TMat;
  //..
  class operator Implicit(A: TMat): TMat; // call assign inside proc.
                                          // <--Self Implicit(which isn't be used in D2007, got compilation error in DelphiXE)

  procedure Assign(const M: TMat); // copy _Elem inside proc.
                                   // <-- I don't want to use it explicitly.
end;

Я выбираю запись, потому что не хочу создавать / Free / Assign to use it.

Но с динамическим массивом значения нельзя (глубоко) скопировать с помощью M1: = M2 вместо M1.Assign (M2).

Я попытался объявить себя Метод неявного преобразования, но его нельзя использовать для M1: = M2.

(Неявный (const pA: PMat): TMat и M1: = @ M2работает, но довольно некрасиво и нечитабельно ..)

Есть ли способ перехватить присвоение записи?

Или есть предложение реализовать матрицу N x M с записями?

Заранее спасибо.

Изменить:

Я реализовал, как показано ниже, с помощью метода Барри и подтвердил, что работает правильно.

type
  TDDArray = array of array of Double;

  TMat = record
  private
     procedure CopyElementsIfOthersRefer;
  public
    _Elem: TDDArray;
    _FRefCounter: IInterface;
   ..
  end;

procedure TMat.SetSize(const RowSize, ColSize: Integer);
begin
  SetLength(_Elem, RowSize, ColSize);

  if not Assigned(_FRefCounter) then
    _FRefCounter := TInterfacedObject.Create;
end;

procedure TMat.Assign(const Source: TMat);
var
  I: Integer;
  SrcElem: TDDArray;
begin
  SrcElem := Source._Elem; // Allows self assign

  SetLength(Self._Elem, 0, 0);
  SetLength(Self._Elem, Length(SrcElem));

  for I := 0 to Length(SrcElem) - 1 do
  begin
    SetLength(Self._Elem[I], Length(SrcElem[I]));
    Self._Elem[I] := Copy(SrcElem[I]);
  end;
end;

procedure TMat.CopyElementsIfOthersRefer;
begin
  if (_FRefCounter as TInterfacedObject).RefCount > 1 then
  begin
    Self.Assign(Self); // Self Copy
  end;
end;

Я согласен, что это неэффективно. Просто использовать Assign с чистой записью абсолютно быстрее.

Но это довольно удобно и более читабельно (и интересно .: -)

Я думаю, что это полезно для легких расчетов или предварительного прототипирования. Не так ли?

Edit2:

kibab дает функцию получения счетчика ссылок самого динамического массива.

Решение Барри более не зависит от внутреннего impl и, возможно, работает на будущих 64-битных компиляторах без каких-либо изменений, но в этом случае Я предпочитаю кибаб за его простоту и эффективность. Спасибо.

  TMat = record
  private
     procedure CopyElementsIfOthersRefer;
  public
    _Elem: TDDArray;
   ..
  end;

procedure TMat.SetSize(const RowSize, ColSize: Integer);
begin
  SetLength(_Elem, RowSize, ColSize);
end;    

function GetDynArrayRefCnt(const ADynArray): Longword;
begin
  if Pointer(ADynArray) = nil then
    Result := 1 {or 0, depending what you need}
  else
    Result := PLongword(Longword(ADynArray) - 8)^;
end;

procedure TMat.CopyElementsIfOthersRefer;
begin
  if GetDynArrayRefCnt(_Elem) > 1 then
    Self.Assign(Self);
end;
12
задан benok 10 December 2010 в 06:11
поделиться