Возможно, пора подвести какой-то итог, а то тема, кажется, заглохла.
Меня это обсуждение подвигло усомниться в необходимости объектных деструкторов для Оберона. (Вообще-то, изначально, опираясь на опыт Си++, я в ней мало сомневался.)
Какие могут быть причины ввести в Оберон/КП деструкторы?
1. Освобождение памяти.В основном деструкторы Си++ занимаются освобождением памяти. Рантайм Оберона полностью берёт эту работу на себя. Это мне было ясно и до обсуждения.
2. Освобождение внешних ресурсов (файлов, устройств и т.д.; потоки я здесь не рассматриваю).
В КП есть финализаторы. Рано или поздно они сработают (при сборке мусора), и внешние ресурсы будут возвращены. Но здесь остается общая для финализаторов проблема - непредсказуемость времени их вызова. Поэтому мы переходим к следующему пункту.
3. К какому событию привязать освобождение внешнего устройства.Сначала казалось очевидным, что к уничтожению объекта, управляющего внешним устройством.
Но потом я стал в этом сомневаться. Например, в прежние ("дообъектные") времена считалось хорошим тоном освобождать ресурсы в той же процедуре, в какой их захватывали. Думаю, то же можно распространить и на метод. Учитывая, что внешние устройства (по большей части файлы) используются значительно реже, чем динамическая память, можно применить этот "старый" подход. (Исключения настолько редки, что, IMHO, такими объектами без труда можно управлять и явно.) Но тогда возникает новая опасность: в результате выброса исключения мы можем не выполнить действия по освобождению ресурса в конце процедуры.
Это приводит к следующему пункту.
4. Процедурные секции CLOSE (по аналогии с модулем) и их привязка к обработке исключения.
Можно ввести секции в язык процедурные секции CLOSE (аналог finally) и поручить обработчику трапов их вызвать (в порядке LIFO, разумеется). Уверен, что это не является проблемой. Таким образом, мы гарантируем своевременное освобождение внешнего ресурса даже в случае исключения.
5. Каких проблем мы избегаем, не вводя конструкторы/деструкторы.
Конструкторы и деструкторы обычно работают не так, как другие методы. Они предполагают вызовы для всех классов в иерархии наследования и агрегации. Учитывая,
- что большей частью они используются для того, чтобы управлять памятью, что для оберонов излишне,
- что, используя неявные вызовы, они создают определенное концептуальное противоречие с духом языка и опасность массы бесполезных вызовов;
- что для конструкторов есть неплохая замена в виде фабричных функций (особенно, если принять во внимание, что в Обероне практически не используются глубокие иерархии наследования),
то, отказываясь вводить в язык конструкторы/деструкторы, мы избегаем излишних хлопот и большой головной боли.
Поэтому я предлагаю ограничиться введением процедурной секции CLOSE (хотя еще надеюсь на некий аналог секции EXCEPT (XDS) или RESCUE (GPCP)).
Это, конечно, IMHO. Можно критиковать.