![]() |
|
|
Регистрация | << Правила форума >> | FAQ | Пользователи | Календарь | Поиск | Сообщения за сегодня | Все разделы прочитаны |
![]() |
|
Опции темы | Поиск в этой теме | Опции просмотра |
#1
|
||||
|
||||
![]() Про массивы написано много, но одного момента я уяснить так и не смог:
если увеличивать размер массива объектов, под дополнительные объекты просто будет выделена память, сами новые объекты надо создать самостоятельно. Вроде все понятно. А вот что происходит с объектом когда он оказывается за пределами массива при уменьшении размера? По логике - вроде бы система должна "забыть" про этот объект и считать выделенную под него память свободной. Как это вообще работает, нужно ли уничтожать объект? А ну да, объект создается как то так: Код:
Последний раз редактировалось Помидоркин, 21.07.2022 в 08:47. |
#2
|
|||
|
|||
![]() В данном случае, массива объектов, на самом деле в массиве выделяется только память под указатели на объекты. Память под сами объекты выделяется отдельно при создании самого объекта. Таким образом, если ты уменьшишь массив не удалив предварительно сам объект, то получишь мемори лик (Memory leak).
Условно говоря, одна ячейка массива (берем 32-битное приложение, для 64-битного тоже самое, просто размеры больше) занимает 4 байта. Пусть сам объект занимает 1кб (поля + все нужое служебное типа таблицы динамических методов и т.д.). Тогда свм массив из 100 ячеек будет занимать 400 байт (ну там есть еще несколько байт на служебную инфу, но это фикс. размер для массива). Плюс еще отдельно выделено 100 кб для самих объектов. При уменьшении размера массива на 10 элементов у тебя изменится только память, занимаемая массивом - теперь станет 360 байт. А вот если ты не вызвал деструкторы для самих объектов, то под объекты так и останется выделенно 100 кб. И при этом ты потеряешь ссылки на те 10 объектов, т.е. после этого эти 10 кб вообзе зависнут. Все вышесказанное относится к классическим объектам, т.е. классам, унаследованным от TObject. Для COM объектов ситуация немного другая - там есть механизм счетчика ссылок и как только для инстанса он сравнивается с 0, то происходит автоматическое удаление объекта. Вроде что-то писали про подобную реализацию и для классических объектов, но, по крайней мере в 10.2, такого пока нет. Надеюсь, объяснил достаточно понятно... ЗЫ. Для хранения коллекции объектов тогда лучше использовать TObjectList. Там можно сделать так (при вызове конструктора списка надо в параметрах указать, что он является "владельцем" помещенных в него объектов), что при удалении элемента будет автоматически вызван деструктор этого объекта. Да и удалять тогда можно из произваольного места списка, а не только с конца, как в случае массива. Последний раз редактировалось lmikle, 21.07.2022 в 17:36. |
Этот пользователь сказал Спасибо lmikle за это полезное сообщение: | ||
Помидоркин (25.07.2022)
|
#3
|
|||||
|
|||||
![]() Попробую не плодить тем, спрошу здесь
Цитата:
А вопрос возник следующий: как именно следует уничтожать объекты? Нашел вот это Код:
В одном месте прочитал туманное объяснение что TObject - прародитель всех объектов и поэтому его деструктор вызывается как-то хитро и он все-таки что-то делает, но подробностей я так и не понял. И в комменте написано: "в ARC этот метод фактически не вызывается, поскольку компилятор преобразует вызов в простое присваивание переменной экземпляра nil, которое затем вызывает _Its Clear" Ну т.е. это и все, можно со спокойным сердцем считать что память из-под объекта освободилась? И еще: Код:
2-На данный момент в объектах только строки и числа, а как быть со всякими StringList или JPEGImage? Их тоже удалять в ручную? |
#4
|
|||
|
|||
![]() Вообще, методы, объявленные в коде как constructor и destructor, таковыми на самом деле не являются. Правдя тут надо отметить, что доступа к реальным конструктору и деструктору в Дельфи просто нет (не считаем различные хаки). Поэтому данные методы и используются для реализации выделения памяти под атрибуты классов (имеется в виду сложные атрибуты, являющиеся так же объектами классов, память под простые типы выделяется именно реальным конструктором, но вызов его скрыт от программиста). Т.е. по сути constructor и destructor являются инициализатором и деинициализатором по своей сути. Это для начала.
Теперь по повду "излишней функциональности". Во первых, ее всегда можно скрыть: Код:
Таким образом ты прячешь "излишнюю функциональность", но можешь ею пользоваться, если надо. Теперь ответы на последние 2 вопроса. 1. Да, надо. Для проверки, если используешь одну из последних версий Дельфи, можешь включить отчет об утечках памяти. При закрытии приложения тебе менеджер памяти скажет, если есть утечки. 2. Да, их тоже надо удалять в деструкторе. В принципе, при правльном дизайне, это по одной строчке в конструкторе и деструкторе (создать и, соответсвенно, удалить). |
Этот пользователь сказал Спасибо lmikle за это полезное сообщение: | ||
Помидоркин (24.08.2022)
|
#5
|
|||
|
|||
![]() Для примера, вот моделька из одного из моих проектов. Она простая, там всего 2 уровня - сама модель и ее итемы.
Код:
PS. Пришлось удалить пару методов, что бы влезть в ограничение длинны сообщения... |
Этот пользователь сказал Спасибо lmikle за это полезное сообщение: | ||
Помидоркин (24.08.2022)
|
#6
|
||||
|
||||
![]() Еще один вопрос возник, что происходит с данными при уменьшении длинны массива? Например запись
Код:
Последний раз редактировалось Помидоркин, 22.09.2022 в 11:48. |
#7
|
|||
|
|||
![]() как раз объект и надо уничтожать - иначе он останется "висеть" в куче и удалить его не будет возможности, т.к. ссылка на него будет потеряна.
А вот с "числами" как раз все все нормально будет, бо как это не указатели. Они будут удалены автоматически. Память будет помечена как свободная и может быть переиспользованна. При следующем выделении памяти компилятор просто зачистит выделенное место, т.е. старых данных ты уже не увидишь. |
Этот пользователь сказал Спасибо lmikle за это полезное сообщение: | ||
Помидоркин (23.09.2022)
|
#8
|
||||
|
||||
![]() Цитата:
Ну т.е. я так себе представляю - при задании длинны массива выделяется память (в моем случае) - (<сколько-то там под указатель>+2байта+2байта)*длинна массива, как-то так. |
#9
|
|||
|
|||
![]() Ну, правильно представляеш в общем.
"<сколько-то там под указатель>" - это конкретно 32 или 64 бита (4 или 8 байт). Память под сам объект выделвется при его создании отдельно и к массиву отношения не имеет (это просто для уточнения). Ну и еще там есть место под длинну массива (если мы говорим о динамических массивах), но оно тебе на прямую не доступно (находится по отрицательному адресу относительно адреса первого элемента массива). Опять же, это просто для общего понимания. ЗЫ. Тут есть момент, связанный с оптимизацией. Для тебя он "прозрачен". А именно, как выделяется память при изменении размера массива. Если есть последовательно свободная память, то при увеличении размера массива она "прирезается" к уже выделенной. А вот ести нужного куска нет, то внутри происходит выделение полноразмерного куска в новом месте и копирование данных из старой в новую память. Поэтому для оптимизации рекомендуют либо заранее вычислить нужный размер массива и выделить память, либо выделять как минимум достаточно большими кусками. Кстати, именно так реализованы объекты-списки в VCL - они выделяют место кусками под несколько элементов и уже внутри себя контролируют использование. Последний раз редактировалось lmikle, 23.09.2022 в 19:50. |