Вопрос
несколько вопросов о записях в Delphi:
- Поскольку записи почти как классы, почему бы не использовать только классы вместо записей?
- Теоретически, память выделяется для записи, когда она объявлена переменной;но, а как освобождается память после этого?
- Я могу понять полезность указателей на записи в объект списка, но с универсальными контейнерами (
TList<T>
), нужно ли еще использовать указатель?если нет, то как удалить / освободить каждую запись в общий контейнер?Если я хочу удалить определенную запись в общий контейнер, как это сделать?
Решение
Для 1 и 2:записи - это типы значений, в то время как классы - это ссылочные типы.Они размещаются в стеке или непосредственно в пространстве памяти любой переменной большего размера, которая их содержит, а не через указатель, и автоматически очищаются компилятором, когда они выходят за пределы области видимости.
Что касается вашего третьего вопроса, то TList<TMyRecord>
внутренне объявляет array of TMyRecord
для места для хранения.Все записи в нем будут очищены, когда список будет уничтожен.Если вы хотите удалить определенный файл, используйте Delete
метод удаления по индексу, или Remove
способ поиска и удаления.Но имейте в виду, что, поскольку это тип значения, все, что вы делаете, будет заключаться в создании копий записи, а не в копировании ссылок на нее.
Другие советы
Существует много различий между записями и классами;и никакого "Указателя на запись". <> "Класс".У каждого из них есть свои плюсы и минусы;одна из важных вещей в разработке программного обеспечения - понимать их, чтобы вам было легче выбрать наиболее подходящее для данной ситуации.
- Этот вопрос основан на ложной посылке.Записи почти не похожи на классы, точно так же, как целые числа почти не похожи на Двойные.
- Классы всегда должны создаваться динамически, тогда как это возможно, но не является обязательным требованием для записей.
- Экземпляры классов (которые мы называем объектами) всегда передаются по ссылке, что означает, что несколько разделов кода будут совместно использоваться в одном и том же экземпляре.Это важно помнить, потому что вы можете непреднамеренно изменить объект в качестве побочного эффекта;хотя, когда это сделано намеренно, это мощная функция.Записи, с другой стороны, передаются по значению;вам нужно явно указать, передаете ли вы их по ссылке.
- Классы "копируются не так легко, как записи".Когда я говорю копировать, я имею в виду отдельный экземпляр, дублирующий исходный код.(Это должно быть очевидно в свете приведенного выше комментария о значении / ссылке).
- Записи, как правило, очень хорошо работают с типизированными файлами (потому что их так легко скопировать).
- Записи могут накладывать поля друг на друга (случай x из /unions).
- Это были комментарии по поводу определенных ситуационных преимуществ записей;и наоборот, существуют также ситуационные преимущества для занятий, на которых я не буду подробно останавливаться.
- Возможно, самый простой способ понять это - быть немного педантичным в этом вопросе.Давайте внесем ясность;память на самом деле не выделяется "при ее объявлении", она выделяется, когда переменная находится в области видимости, и освобождается, когда она выходит за пределы области видимости.Таким образом, для локальной переменной она выделяется непосредственно перед началом процедуры и освобождается сразу после окончания.Что касается поля класса, то оно выделяется при создании объекта и освобождается при его уничтожении.
- Опять же, есть свои плюсы и минусы...
- Это может быть медленнее и требовать больше памяти для копирования целых записей (как в случае с дженериками), чем для простого копирования ссылок.
- Передача записей по ссылкам (с использованием указателей) - это мощный метод, с помощью которого вы можете легко заставить что-то другое изменить вашу копию записи.Без этого вам пришлось бы передавать свою запись по значению (т.е.скопируйте его) получите в результате измененную запись, скопируйте ее снова в свои собственные структуры.
- Являются ли указатели на записи подобными классам?Нет, вовсе нет.Всего лишь два отличия:
- Классы поддерживают полиморфное наследование.
- Классы могут реализовывать интерфейсы.
Одним из главных преимуществ записей является то, что у вас есть большой "массив записей".Это создается в памяти путем выделения места для всех записей в одном непрерывном пространстве оперативной памяти, что выполняется чрезвычайно быстро.Если бы вы использовали вместо этого "массив TClass", каждый объект в массиве должен был бы выделяться сам по себе, что происходит медленно.
Было проделано много работы по повышению скорости выделения памяти, чтобы улучшить скорость работы со строками и объектами, но это никогда не будет так быстро, как замена 100 000 выделений памяти на 1 выделение памяти.
Однако, если вы используете массив записей, не копируйте запись в локальные переменные.Это может легко свести на нет преимущество в скорости.
Есть несколько других различий между классом и записью.Классы могут использовать полиморфизм, и раскрывать интерфейсы.Записи не могут реализовывать деструкторы (хотя, начиная с Delphi 2006, теперь они могут реализовывать конструкторы и методы).
Записи очень полезны для сегментирования памяти в более логичную структуру, поскольку первый элемент данных в записи находится в той же адресной точке, что и указатель на саму запись.Это не относится к классам.
1) Чтобы обеспечить наследование и полиморфизм, классы имеют некоторые накладные расходы.Записи не допускают этого, и в некоторых ситуациях это может быть несколько быстрее и проще в использовании.В отличие от классов, которые всегда размещаются в куче и управляются с помощью ссылок, записи также могут быть размещены в стеке, доступны напрямую и назначены друг другу без необходимости вызова метода "Assign".Также записи полезны для доступа к блокам памяти с заданной структурой, потому что их расположение в памяти точно такое, как вы его определяете.Расположение памяти экземпляра класса контролируется компилятором и содержит дополнительные данные для обеспечения работы объектов (т. е.указатель на таблицу виртуальных методов).
2) Если вы не выделяете записи динамически, используя New() или GetMem() , память записей управляется компилятором в виде ординалов, плавающих значений или статических массивов:память глобальных переменных выделяется при запуске и освобождается при завершении работы программы, а локальные переменные выделяются в стеке при входе в функцию / процедуру / метод и освобождаются при выходе.Выделение / освобождение памяти в стеке происходит быстрее, потому что для этого не требуются вызовы диспетчера памяти, просто очень мало инструкций ассемблера для изменения регистров стека.Но имейте в виду, что выделение большой структуры в стеке может вызвать переполнение стека, поскольку максимальный размер стека фиксирован и не очень велик (см. Параметры компоновщика).Если записи являются полями класса, они выделяются при создании класса и освобождаются при освобождении класса.
3) Одно из преимуществ дженериков заключается в устранении необходимости низкоуровневого управления указателями, но будьте в курсе внутренней работы.