Можете ли вы перегрузить оператор присваивания для записей Delphi? - PullRequest
3 голосов
/ 05 июня 2009

У меня есть запись, которая содержит динамический массив. Обычно, когда вы присваиваете одну переменную массива другой, фактически присваивается только указатель на этот массив. Это означает, что когда вы делаете это, обе переменные указывают на один и тот же массив, пока вы не измените размер одной из них. Поэтому, когда я хочу назначить отдельную копию массива переменной, я использую функцию Copy ().

В этом случае, однако, мой массив является полем записи:

  TMyRec = record
    Value: integer;
    &Array: array of integer;
  end;

Когда я объявляю две переменные типа TMyRec и затем присваиваю одну другую, поля «Массив» в обеих записях будут указывать на один и тот же адрес в памяти.

Чтобы решить эту проблему, я решил перегрузить оператор присваивания следующим образом:

TMyRec = record
  Value: integer;
  &Array: array of integer;
public
  class operator Implicit(Value: TMyRec): TMyRec;
end;

class operator TMyRec.Implicit(Value: TMyRec): TMyRec;
begin
  Result := Value;
  Result.&Array := Copy(Value.&Array);
end;

Если бы это сработало, мне не пришлось бы копировать все поля массива в моих записях отдельно после назначения переменных TMyRecord друг другу.

Вот что я делаю:

var
  Rec1, Rec2: TMyRec;
begin
  Rec1.Value := 10;
  SetLength(Rec1.Array, 1);

  //I expected the "Implicit" method to be invoked here (but it is not...)
  Rec2 := Rec1;

  //if I do that, the Rec1.Array[0] will also be changed to 1 - I don't want that to happen
  Rec2.Array[0] := 1;
end;

Есть ли способ заставить перегрузку моего оператора работать так, как я хочу? Дело в том, что я пытаюсь перегрузить оператор присваивания по умолчанию. Разве это не возможно?

Ответы [ 3 ]

7 голосов
/ 05 июня 2009

Вы можете поместить свой массив в экземпляр объекта, который реализует интерфейс, и сохранить ссылку на этот интерфейс внутри записи. Таким образом, когда вы пытаетесь присвоить массив через интерфейс (и, следовательно, через объект), он может проверить его счетчик ссылок и помочь с семантикой копирования при записи.

В качестве альтернативы, вы можете использовать небезопасные трюки, чтобы узнать количество ссылок массива, и вручную скопировать перед записью, если существует несколько ссылок; но это не будет поддерживаться и оборвется, если реализация динамического массива изменится.

Я написал , как реализовать структуру массива копирования при записи, заключенную в запись. Он реализован как универсальный тип, но ничто не мешает вам использовать конкретный тип массива, просто он не будет таким общим.

3 голосов
/ 21 октября 2013

Вы можете использовать указатель TMyRec:

pMyRec = ^TMyRec;
TMyRec = record
  Value: integer;
  _Array: array of integer;
public
  class operator Implicit(Value: pMyRec): TMyRec;
end;

class operator TMyRec.Implicit(Value: pMyRec): TMyRec;
begin
  Result := Value^;
  Result._Array := Copy(Value^._Array);
end;

procedure TForm1.Button1Click(Sender: TObject);
var R1, R2 : TMyRec;
begin
  R1.Value := 1;
  SetLength( R1._Array, 2);
  R1._Array[0] := 1;
  R1._Array[1] := 2;

  R2 := @R1;
  R2._Array[0] := 3;
end;
1 голос
/ 05 июня 2009

Не делайте простого назначения. Перейти на удобочитаемость. Создайте свою собственную процедуру RecCopy, например:

procedure RecCopy( const AFrom, ATo : TMyRec );
var
  I : integer;
begin
  ATo.Value := AFrom.Value;
  SetLength( ATo.Array, Length( AFrom.Array );
  For I := 0 to Length( AFrom.Array )-1 do
    ATo.Array[I] := AFrom.Array[I];
end;

Обратите внимание, что вы можете использовать лучший способ реализации копии массива :-) Но вы понимаете мою точку зрения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...