Взаимная ссылка на записи в Delphi (Win32) - PullRequest
9 голосов
/ 11 июля 2010

Есть ли обходной путь для создания записей взаимных ссылок в Delphi?Вот упрощенная версия кода:

MyRec1 = record
  arr: MyRec2Array;
end;

MyRec2 = record
  mr: MyRec1;
end;

MyRec2Array = array of MyRec2;

Видимо, прямое объявление типов записей

MyRec2 = record;

не работает в Delphi для Win32.

Переключение на классывместо записей это нехорошо, потому что это увеличит потребление памяти и сложность кода, поэтому я бы предпочел остаться с записями.

Есть предложения?

Ответы [ 3 ]

14 голосов
/ 11 июля 2010

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

Но вы можете объявить тип указателя на запись как прямое объявление, например:

PMyRec2 = ^MyRec2
...
MyRec2 = record
  ...
end;

Конечно, как только вы начнете использовать указатели на записи, вам придется беспокоиться о выделении и освобождении памяти, и сложность кода, которую вы пытались избежать, не используя классы, появляется в вашем проекте. Итог: сделайте это с классами. Сделайте одну из записей, если не обе, классом. Это действительно самый простой способ.

И дополнительные затраты памяти незначительны. Он указывает на указатель на объект для каждой ссылки, который вам в любом случае понадобится для указателей на объекты, плюс одно скрытое поле (4 байта) на экземпляр перед D2009 или два из них (8 байт) на D2009 или более поздней. Это совсем немного.

4 голосов
/ 11 июля 2010

Вопрос выглядит для меня как шутка - речь идет не о взаимных ссылках, а о бесконечном цикле определения типа. Что касается взаимных ссылок, они могут быть разрешены путем определения типов в записи (я использовал Delphi 2009):

type
  MyRec2 = record
  type
    MyRec2Array = array of MyRec2;
  type
    MyRec1 = record
      arr: MyRec2Array;
    end;
  var
    mr: MyRec1;
  end;

Но как использовать вышеуказанный тип записи? Действительно смешно. :)

procedure TForm1.Button1Click(Sender: TObject);
var
  R: MyRec2;

begin
  SetLength(R.mr.arr, 1);
//  R.mr.arr[0]:= ???;
end;
4 голосов
/ 11 июля 2010

Хотя я полностью согласен с Мэйсоном, есть способ обойти ограничение.По сути, вы можете использовать помощник по записи для определения необходимых функций после объявления MyRec2.

type
  MyRec1 = record
    arr: array of byte;
  end;

  MyRec2 = record
    mr: MyRec1;
  end;

  MyRec1Helper = record helper for MyRec1
    procedure AllocateMyRec2(numItems: integer);
    function  GetMyRec2(i: integer): MyRec2;
    procedure SetMyRec2(i: integer; const value: MyRec2);
    property Rec2[i: integer]: MyRec2 read GetMyRec2 write SetMyRec2;
  end;

procedure MyRec1Helper.AllocateMyRec2(numItems: integer);
begin
  SetLength(arr, numItems * SizeOf(myRec2));
end;

function MyRec1Helper.GetMyRec2(i: integer): MyRec2;
begin
  Move(arr[i*SizeOf(MyRec2)], Result, SizeOf(MyRec2));
end;

procedure MyRec1Helper.SetMyRec2(i: integer; const value: MyRec2);
begin
  Move(value, arr[i*SizeOf(MyRec2)], SizeOf(MyRec2));
end;

var
  my: MyRec2;

begin
  my.mr.AllocateMyRec2(2);
  my.mr.Rec2[0].mr.AllocateMyRec2(3);
end.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...