Записи в Delphi

некоторые вопросы о записях в Delphi:

  • Поскольку записи почти похожи на классы, почему бы не использовать только классы вместо записей?
  • Теоретически память выделяется для записи, когда она объявляется переменной; но, и как память освобождается после?
  • Я могу понять полезность указателей на записи в объект списка, но с контейнерами Generics (TList<T>), нужно ли еще использовать указатель? если нет, как удалить/выпустить каждую запись в универсальный контейнер? Если я хочу удалить определенную запись в универсальный контейнер, как это сделать?

Ответы

Ответ 1

Для 1 и 2: записи являются типами значений, а классы - ссылочными типами. Они выделяются в стеке или непосредственно в пространстве памяти любой более крупной переменной, которая их содержит, а не через указатель и автоматически очищаются компилятором, когда они выходят из области видимости.

Что касается вашего третьего вопроса, TList<TMyRecord> внутренне объявляет array of TMyRecord для места хранения. Все записи в нем будут очищены, когда список будет уничтожен. Если вы хотите удалить конкретный, используйте метод Delete для удаления по индексу или метод Remove для поиска и удаления. Но имейте в виду, что поскольку это тип значения, все, что вы делаете, будет делать копии записи, а не копировать ссылки на нее.

Ответ 2

Существует множество различий между записями и классами; и нет "Указатель записи" < > "Класс". У каждого свои плюсы и минусы; одна из важных вещей о разработке программного обеспечения заключается в том, чтобы понять их, чтобы вы могли более легко выбрать наиболее подходящий для данной ситуации.

  • Этот вопрос основан на ложной предпосылке. Записи не похожи на классы, аналогично тому, как целые числа не похожи на пары.
    • Классы должны всегда создаваться динамически, тогда как это возможность, но не требование для записей.
    • Экземпляры классов (которые мы называем объектами) всегда передаются по ссылке, что означает, что несколько разделов кода будут совместно использоваться и действовать в одном экземпляре. Это важно запомнить, потому что вы можете непреднамеренно изменить объект как побочный эффект; хотя когда это сделано намеренно, это мощная функция. Записи, с другой стороны, передаются по значению; вам нужно явно указать, передаете ли вы их по ссылке.
    • Классы не копируют так же легко, как записи. Когда я говорю копию, я имею в виду отдельный экземпляр, дублирующий источник. (Это должно быть очевидно в свете вышеприведенного комментария значения/ссылки.)
    • Записи, как правило, очень хорошо работают с типизированными файлами (потому что их так легко скопировать).
    • Записи могут накладывать поля с другими полями (случай x из /union )
    • Это были комментарии к некоторым ситуационным преимуществам записей; наоборот, существуют также ситуационные преимущества для классов, о которых я не буду рассказывать.
  • Возможно, самый простой способ понять это - быть немного педантичным. Позвольте уточнить; память на самом деле не выделяется "когда она объявлена", она выделяется, когда переменная находится в области видимости, и освобождается, когда она выходит из области видимости. Таким образом, для локальной переменной она выделяется непосредственно перед началом процедуры и освобождается сразу после завершения. Для поля класса он выделяется при создании объекта и освобождается при его уничтожении.
  • Опять же, есть плюсы и минусы...
    • Он может быть медленнее и требует больше памяти для копирования целых записей (как и для генериков), чем для копирования ссылок.
    • Передача записей по ссылке (с помощью указателей) - это мощная техника, благодаря которой вы можете легко изменить что-то другое в своей копии записи. Без этого вам придется передать свою запись по значению (т.е. Скопировать ее), в результате получить измененную запись, скопировать ее снова в свои собственные структуры.
  • Являются ли указатели на записи типами классов? Нет, совсем нет. Всего две отличия:
    • Классы поддерживают полиморфное наследование.
    • Классы могут реализовывать интерфейсы.

Ответ 3

Одним из основных преимуществ записей является наличие большого массива записей. Это создается в памяти путем выделения пространства для всех записей в одном непрерывном пространстве оперативной памяти, что очень быстро. Если бы вы использовали "массив TClass", каждый объект в массиве должен был быть выделен сам по себе, что было бы медленным.

Было много работы, чтобы улучшить скорость выделения памяти, чтобы улучшить скорость строк и объектов, но она никогда не будет такой быстрой, как замена 100 000 распределений памяти с 1 распределением памяти.

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

Ответ 4

Есть несколько других различий между классом и записью. Классы могут использовать polymorphism и выставлять интерфейсы. Записи не могут реализовать деструкторы (хотя с Delphi 2006 они теперь могут реализовывать конструкторы и методы).

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

Ответ 5

1) Чтобы разрешить наследование и полиморфизм, классы имеют некоторые накладные расходы. Записи не позволяют их, и в некоторых ситуациях может быть несколько быстрее и проще в использовании. В отличие от классов, которые всегда выделяются в куче и управляются посредством ссылок, записи также могут быть выделены в стеке, также доступны напрямую и назначаются друг другу, не требуя вызова метода "Назначить". Также записи полезны для доступа к блокам памяти с заданной структурой, потому что их макет памяти точно такой, как вы его определяете. Макет памяти экземпляра класса контролируется компилятором и содержит дополнительные данные для работы объектов (т.е. Указатель на таблицу виртуальных методов).

2) Если вы не динамически распределяете записи, используя New() или GetMem(), память записи управляется компилятором как ординалы, поплавки или статические массивы: память глобальных переменных выделяется при запуске и освобождается, когда программа завершается, и локальные переменные выделяются в стеке, вводя функцию/процедуру/метод и освобождаются. Выделение/освобождение памяти в стеке происходит быстрее, потому что для вызова диспетчера памяти ему не требуются вызовы, очень мало инструкций ассемблера для изменения регистров стека. Но имейте в виду, что выделение большой структуры в стеке может привести к переполнению стека, поскольку максимальный размер стека является фиксированным и не очень большим (см. Параметры компоновщика). Если записи являются полями класса, они выделяются, когда класс создается и освобождается при освобождении класса.

3) Одно из преимуществ дженериков заключается в устранении необходимости управления указателем низкого уровня - но имейте в виду внутреннюю работу.