На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
Модераторы: Rouse_, jack128, Krid
  
    > Некоторые хитрости динамического размещения и удаления записей с полями типа string
      AllocMem + поля записи ссылочного типа

      Очень часто приходится хранить список записей, место под которые выделяется динамически. При этом также не хочется ограничиваться лишь простыми типами данных, а использовать всю мощь ссылочных типов вроде string и DynArray. Однако при работе с ними есть некоторые тонкости.
      Во-первых, после выделения памяти под структуру все такие поля необходимо занулять. Почему? Рассмотрим запись
      ExpandedWrap disabled
        type
         TRec = record
           s1: string;
         end;


      После выделения памяти через GetMem в полученной области находится случайный мусор. На месте s1 практически наверняка присутствует набор байт, который будет считаться указателем на строку. При первом же присвоении программа подумает, что в строке уже что-то есть, и попытается освободить память. Что, разумеется, приведёт к исключению. AllocMem сама обнуляет выделенную память, так что предпочтительней использовать именно её.

      Во-вторых, что уже сложнее, проблемы возникают при освобождении памяти. Поля ссылочного типа освобождаются неявно, с подсчётом ссылок. Если забивать их нулями, получим утечку памяти. Но и не вручную же каждый раз их все обнулять перед FreeMem! К счастью, мне удалось найти решение - выход в способности Delphi копировать записи простым присваиванием. Задача решается созданием специальной нулевой записи соответствующего типа.

      ExpandedWrap disabled
        type TRec = record
               s1, s2: string;
               arr: array of byte;
             end;
         
        var ZeroRec: TRec;
         
        ...
         
        initialization
          ZeroMem(ZeroRec, SizeOf(ZeroRec));
         
        ...


      Способ использования:
      ExpandedWrap disabled
        var pRec: ^TRec;
         
        ...
         
          pRec := AllocMem(SizeOf(TRec));
          pRec.s1 := 'wefwefwef';
          pRec.s2 := '234234234';
          SetLength(pRec.arr, 10);
          pRec^ := ZeroRec;
          FreeMem(pRec);


      Как можно убедиться, трассируя ассемблерный код на строчке pRec^ := ZeroRec, программа очищает все ссылочные поля надлежащим образом.

      Остаётся добавить, что у меня этот способ применяется в нескольких проектах, которые непрерывно крутятся в течение недель и месяцев, и никаких проблем не возникает.

      P.S. Да, конечно, можно юзать классы, но по моему глубокому убеждению, класс должен уметь сам что-то делать, запись же создана исключительно для хранения данных. Кроме того, объекты не присвоишь так же легко, как записи.

      Эта тема была разделена из темы "Alloc/GetMem + поля записи ссылочного типа"
      0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
      0 пользователей:


      Рейтинг@Mail.ru
      [ Script execution time: 0,0177 ]   [ 17 queries used ]   [ Generated: 28.03.24, 11:58 GMT ]