![]() |
|
|
Регистрация Восстановить пароль |
Регистрация | Задать вопрос |
Заплачу за решение |
Новые сообщения |
Сообщения за день |
Расширенный поиск |
Правила |
Всё прочитано |
![]() |
|
|
Опции темы | Поиск в этой теме |
![]() |
#1 |
Форумчанин
Регистрация: 16.05.2024
Сообщений: 200
|
![]()
Сколько видел программ на Free Pascal'е - везде объекты уничтожаются вручную, через Free или FreeAndNil. А ведь можно сделать чтобы это происходило автоматически. Для этого надо с объектами работать через интерфейсы и классы делать производными от TInterfacedObject. Переменная типа interface является интеллектуальным указателем, который контролирует использование объекта. Если пытаться использовать переменную типа interface без инициализации, то компилятор выдаст предупреждение. Когда уже никто не использует объект, переменная-интерфейс уничтожит его автоматически.
Вот пример: создадим объект очередь (queue) с двумя методами - Put (положить в очередь) и Get (взять из очереди). Создадим для этого объекта интерфейс QueueObj . Новая очередь будет создаваться в функции New: Код:
Код:
$ ./project1 Put called. Get called. 5 queue destructor called. ------- Объект уничтожается автоматически. Деструктор вызывается. |
![]() |
![]() |
![]() |
#2 |
Форумчанин
Регистрация: 27.04.2022
Сообщений: 519
|
![]()
Положили 7, а достали 5, любая бухгалтерия позваидует
![]() Pascal последний раз мимопроходил в 93 году. В какой момент вызывается деструктор? Когда программа встречает директиву end?
стимулятор https://yoomoney.ru/to/41001303250491
|
![]() |
![]() |
![]() |
#3 | |
Форумчанин
Регистрация: 16.05.2024
Сообщений: 200
|
![]()
Это не баг, это фича
![]() Цитата:
В данной программе счётчик q перед вызовом Proc1( q ) равен 1, внутри процедуры Proc1 счётчик увеличивается, так как происходит неявное присваивание параметру процедуры qu := q , поэтому он там 2. После исполнения Proc1 счётчик q опять уменьшился в 1, потому что qu уже нет. Так как q определён как глобальная переменная, то при завершении программы он исчезает из области программы и счётчик уменьшается в 0. Тогда и уничтожается объект. Вот можно почитать про Free Pascal https://castle-engine.io/modern_pascal |
|
![]() |
![]() |
![]() |
#4 |
Форумчанин
Регистрация: 27.04.2022
Сообщений: 519
|
![]()
Я не правильно выразился. Я имел ввиду именно ваш код. В какой момент именно в вашем коде.
В PHP поведение деструкторов похожее, хотя в силу специфики языка там трудно найти им применение.
стимулятор https://yoomoney.ru/to/41001303250491
Последний раз редактировалось Valick; 05.04.2025 в 18:01. |
![]() |
![]() |
![]() |
#5 |
Форумчанин
Регистрация: 16.05.2024
Сообщений: 200
|
![]()
Конкретно в этой программе - в самом конце блока операторов программы, перед end.
Деструктор вызывается при уничтожении объекта. А объект уничтожается когда количество ссылок на него 0. То есть если допустим, переменная-интерфейс инициализируется в какой-то функции, и по завершении этой функции больше никто не ссылается на эту переменную, то как функция завершится то уничтожится объект, связанный с переменной-интерфейсом. |
![]() |
![]() |
![]() |
#6 |
Форумчанин
Регистрация: 16.05.2024
Сообщений: 200
|
![]()
Интеллектуальные указатели это хорошо, но ведь они могут зациклиться, и тогда память не освободится. Значит надо использовать анализаторы памяти для нахождения утечек памяти. Другое дело что утечки могут быть и не в вашем коде, но всё же. Во Free Pascal'е есть встроенный анализатор памяти - HeapTrc, также можно использовать внешний - valgrind.
Чтобы включить HeapTrc, надо включить опции компиляции -gl -gh . В Lazarus IDE надо открыть Project Options, там Compiler Options -> Debugging и там установить флажки для -gl и -gh . Вот программа, на которой будем проверять работу анализатора памяти. В ней два объекта x1 и x2, с которыми работаем через интерфейсы (то есть они должны автоматически уничтожатся когда не используются). В x1 (объект T1) есть ссылка на объект T2, а в x2 (объект T2) есть ссылка на объект T1. В процедуре Test тестируем без зацикливания. x1 ссылается (во внутреннем поле) на x2, а x2 ни на что не ссылается. То есть зацикливания нет и оба объекта должны освободиться при завершении программы. В процедуре Test2 делаем зацикливание. x1 ссылается на x2, а x2 ссылается на x1. Вот текст программы: Код:
$ ./project1 Heap dump by heaptrc unit of /home/ville/mydevelop/laz/mleak/project1 2 memory blocks allocated : 80/80 2 memory blocks freed : 80/80 0 unfreed memory blocks : 0 True heap size : 32768 True free heap : 32768 то есть всё в порядке, память освободилась. Вот результат работы Test2: $ ./project1 Heap dump by heaptrc unit of /home/ville/mydevelop/laz/mleak/project1 2 memory blocks allocated : 80/80 0 memory blocks freed : 0/0 2 unfreed memory blocks : 80 True heap size : 32768 True free heap : 32256 Should be : 32304 Call trace for block $00007FF012D23200 size 40 $00000000004012D0 Test2, line 63 of project1.lpr $00000000004013AE $main, line 70 of project1.lpr Call trace for block $00007FF012D23100 size 40 $00000000004012C7 Test2, line 62 of project1.lpr $00000000004013AE $main, line 70 of project1.lpr Отловил два неосбождённых блока памяти. Утечка. |
![]() |
![]() |
![]() |
#7 |
personality
Старожил
Регистрация: 28.04.2009
Сообщений: 2,899
|
![]()
это потому что очередь совсем бессмысленно написана, не хранит, чего закинуто
![]() По теме: не особо могу сказать чего, т.к. здесь более менее азбучные истины, и автор, полагаю, решил начать свой просветительский/изыскательский блог здесь. Это похвально, конечно, и лично я не против, но, по сути, тема не ставит никакой цели/вопроса перед остальным сообществом. А если есть, то озвучьте, пожалуйста. Приятно также наличие ссылки на годный труд от автора движка Касла, интересуетесь, кстати ? Также могу по мелочи покритиковать/попридираться ![]() Например, для метода Гет очереди стоит использовать out параметры (а булевый можно ещё и результатом функции передать, чтобы не писать if has ) Также конвенции бы соблюсти - I и T префиксы Ещё недо-фабрика New: мало того что идентификатор зарезервирован (https://wiki.freepascal.org/Reserved_words) так ещё и смысла в ней мизер, т.к. она генерит только единственный тип, можно легко это и явным вызовом конструктора делать, и не надо определение класса скрывать в имплементации Последний раз редактировалось phomm; 07.04.2025 в 14:26. |
![]() |
![]() |
![]() |
#8 |
Форумчанин
Регистрация: 16.05.2024
Сообщений: 200
|
![]()
phomm, спасибо за замечания, всегда есть что улучшить в коде. Да, я здесь вопрос не задаю, это для популяризации Паскаля. Люди ведь интересуются, вот недавно один человек спрашивал https://programmersforum.ru/showthread.php?t=347710
Я считаю, что Free Pascal - лучший язык для изучения основ программирования, ну это, императивное, структурное, процедурное, и т.д. , единственно, что на Free Pascal'е нельзя функциональное программирование, так это и не надо, для этого Lisp. А конкретно про интерфейсы - я сразу же сказал, что заметил что народ не особо их использует, считает что это для работы с COM и CORBA, но это ведь не так. Насчёт ваших замечаний по коду: 1. out - да, лучше так. 2. префиксы I и T - я это не люблю. 3. Get в виде функции - нет, это не нравится. Процедуры-функции введены в Pascal только лишь для удобства записи математических функций. Ещё можно использовать для функций-фабрик объектов. И всё. Дело в том, что программирование процедур-функций в Паскале ненадёжно (даже при использовании Result), поэтому их надо избегать. 4. Насчёт New: я конечно, сильно удивлён, почему парни написали там что New это зарезервированное ключевое слово. Это не так. New - это всего лишь процедура из модуля system. Когда в тексте написано New(p), то компилятор автоматически переводит это в system.New(p). Поэтому можно в любом модуле описывать свои процедуры New, и никаких коллизий не возникнет. 5. Насчёт "явным вызовом конструктора делать, и не надо определение класса скрывать в имплементации" - так ведь, извините меня, вся эта белиберда с интерфейсами и задумана чтобы скрывать реализацию объекта. Это называется инкапсуляция. ------ Вот корректная реализация очереди из первого сообщения. Там используется список из модуля IntegerList, а этот модуль находится в пакете LazUtils, то есть этот пакет надо подключить к зависимостям проекта. Для этого в Lazarus'е открыть Project Inspector, щёлнуть правой конпкой мыши по тексту Required Packages, выбрать Add, и в списке выбрать LazUtils. Код:
Код:
Код:
|
![]() |
![]() |
![]() |
#9 | |||
personality
Старожил
Регистрация: 28.04.2009
Сообщений: 2,899
|
![]()
пу-пу-пу, ладно, интересное следование одним "правилам" и нелюбовь к другим
Цитата:
Цитата:
Цитата:
про явный вызов имелось в виду что мы в интерфейсную переменную можем положить любую реализацию, например , из другого модуля, а функция только множит сущности без надобности, поэтому я назвал её недофабрикой Вот если бы был чисто интефейс в модуле, и его подключал потребитель, а также модуль где была бы нормальная фабрика, которая, например, имела выбор между несколькими реализациями, через IntegerList из пакета либо через Generics.Collections.List<Integer> то из всего этого появляется смысл. а так- скорытие ради сокрытия, лишние сущности. не стоит агрументировать, ссылаясь на народ, пропагандистский прием. Люди делают как удобно и как знают, к тому же, подозреваю, что у вас выборка (для громкого слова народ) не такая уж и большая, надеюсь, вы не только этот форум рассматриваете, посмотрите какие-нибудь серьёзные open-source проекты на паскале, например https://github.com/exilon/QuickLib/b.../Quick.IOC.pas там вполне активно используются интерфейсы, где нужно. |
|||
![]() |
![]() |
![]() |
#10 |
Форумчанин
Регистрация: 16.05.2024
Сообщений: 200
|
![]()
Форум хорош для высказывания своего мнения, можно узнать что-то новое, никто не может всё знать. Но только когда обсуждение ведётся в вежливой и конструктивной форме и не переходит на личностные нападки.
Я напишу кое-какие свои соображения, но это только для тех, кто мыслит конструктивно. Есть такая практика: кодирование в имени переменной тип этой переменной. Хорошо это или плохо? Это ведь появилось не от хорошей жизни. В низкоуровневых языках и сценарных языках нет типизации или она слабая или динамическая, поэтому, глядя на текст программы, совершенно непонятно что в этой переменной. И кодирование типа в имени позволяет программисту понять что там и избежать ошибок. Например, x_i = y2_b; сразу бросается в глаза несоответствие, что мы целой переменной пытаемся присвоить логическое значение, а это ошибка. Однако в языках со строгой статической типизацией это кодирование излишне и только ухудшает ясность. Точно так же идея кодирования типа в названии типа (путем префикса T и I ) появилась не от хорошей жизни. Это пришло из C++, где нет отдельного типа для интерфейсов, а интерфейсы описываются при помощи классов. То есть объекты описываются классами, и интерфейсы тоже классами. И глядя на текст программы трудно понять с чем мы имеем дело - с объектом или интерфейсом. Поэтому и стали добавлять префикс T в название типа объекта и I - для интерфейсов. Но во Free Pascale для интерфейсов есть отдельный тип - interface, и он совершенно другой чем class. Невозможно синтаксически перепутать объект и интерфейс, так как у интерфейса нет конструкторов, деструкторов и полей. Поэтому использование префиксов T и I во Free Pascale я считаю излишним и ухудшающим чтение. Придираться к моей реализации очереди не смысла, так как цель была показать не как надо программировать очередь, а интерфесы, как определить и использовать. Тип-интерфейс как контракт по использованию и переменная-интерфейс как интеллектуальный указатель. Про функции: компилятор Free Pascal и Lazarus, действительно, в своём коде широко используют процедуры-функции, однако кто сказал что это правильно? Если посмотреть в их код - то это вообще, пример как не надо программировать. Во Free Pascale существуют все средства для надёжного программирования, это безопасные указатели (переменные типа class), интерфейсы (контракты) и автоматическое управление памятью (переменные типа interface). Однако в коде Free Pascalя и Lazarusа и их фремворках повсеместно используются небезопасные указатели (pointer и ^T), функции, вычисления по короткой схеме, а интерфейсы вообще не используются. Их код - это не то, на что людям следует ориентироваться. Ненадёжность программирования функций во Паскале признают все, даже на wiki free Pascalя сказано что легко случайно сделать бесконечную рекурсию. И Никлаус Вирт тоже признал. В своих последующих языках после Паскаля - Modula-2 и Oberon - он изменил синтаксис функций, они стали возврат значения делать через return. Всех поздравляю с серединой весны! |
![]() |
![]() |
![]() |
|
Опции темы | Поиск в этой теме |
![]() |
||||
Тема | Автор | Раздел | Ответов | Последнее сообщение |
Уничтожение объектов | atenon | C++ Builder | 3 | 01.06.2012 18:44 |
Создание и уничтожение объектов. Время жизни объектов C++/C# | Anett// | Помощь студентам | 0 | 24.10.2011 23:26 |
Автоматическое уничтожение объекта | mutabor | Общие вопросы Delphi | 6 | 25.06.2008 22:25 |
Создание/уничтожение объектов | nimf | Общие вопросы Delphi | 10 | 14.04.2008 10:54 |