лекции (2008) (by Михайлишин Алексей_ Жбанков Денис_ Щербинин Виктор_ Чеботарев Павел), страница 10
Описание файла
PDF-файл из архива "лекции (2008) (by Михайлишин Алексей_ Жбанков Денис_ Щербинин Виктор_ Чеботарев Павел)", который расположен в категории "". Всё это находится в предмете "языки программирования" из 7 семестр, которые можно найти в файловом архиве МГУ им. Ломоносова. Не смотря на прямую связь этого архива с МГУ им. Ломоносова, его также можно найти и в других разделах. .
Просмотр PDF-файла онлайн
Текст 10 страницы из PDF
в C# и С++ разные порядки вызовов конструкторов и инициализаторов. С++: Base(), BaseObj(),DerivedObj(), Derived(); С#: DerivedObj(), BaseObj(), Base(), Derived().[lect25] О создании и уничтожении объектовDelphi:• конструкторы и деструкторы наследуются• всегда вызываются явноtype X = classconstructor load; // называться могут как угодноdestructor destroy;По умолчанию ничего не генерируется, потому что все наследуется (даже неявно из класса TObject, вкотором есть методы Create и Destroy).
Всегда перед созданием и удалением создается таблицавиртуальных методов, поэтому в отличие от си, можно вызывать в конструкторе-деструкторе виртуальныеметоды.inherited (соответствует super в Java и base в C# -- это ссылки), но inherited - не ссылка, а ключевое слово.inherited create; - наследует конструктор из предка.По умолчанию конструктор забивает объект нулями, поэтому вызывать его надо первым. В Дельфах нетавтоматического сборщика мусора, как C# и Java. Еще в одном смысле Delphi "застряли посередине",например можно работать с интерфейсами (например, IXML Document), в которых автоматическая сборкамусора есть.Теперь о синтаксисе:1) Созданиеvar a:X; // еще не созданный объект класса Xa:=X.create(1); // конструктор всегда вызывается явно2) УничтожениеВ TObject есть метод Free, поэтому удалить можно командой a.Free, но это не деструктор, хотявнутри Free действительно вызывается деструктор.
Поэтому деструктор не вызывается явно.Причем, при удалении ссылка (self) не становится == null, потому что ссылки определяются какконстанты (нельзя писать self = ...).3) Инициализация статических объектовВ Си мы не можем сказать, в каком порядке и в какой момент будут инициализированы статическиепеременные (единственное, что можно сказать, что все их конструкторы будут до main, адеструкторы -- после), поэтому в конструкторах нельзя рассчитывать на какой-либо порядокинициализации.В C# и Java ситуация лучше, потому что инициализация вызывается строго (C#: static Y a = new Y();) ипорядок вызова определяется порядком записи.Java:class X{static int[]a;static{a = new int[N];for (int i = 0; i < N; i++) a[i] = i;}}Часто статические инициализации выводятся даже в другой поток, поэтому если вдруг там возникнетисключение, то непонятно в какой момент времени оно придет в главный поток.В Delphi:Статических объектов вообще нет.
Но в общем случае unit в Delphi может иметь кроме interface части, еще иimplementation часть, которая может быть поставлена в соответствие статическим переменным.Деструкторов нет, но им может соответствовать блок finalization в части implementation (перед которым естьблок initialization).C++:В нем абсолютно точно известно, когда будет вызван конструктор и деструктор (выход из зоны действия,либо вызов delete). И еще насчет понятия "свертка стека": для любого локального объекта, захваченного вконструкторе, C++ гарантирует вызов соответствующего деструктора.
Поэтому утечка ресурсов можетвозникать только при работе с динамической памятью.C# & Java:Есть сборка мусора.Но универсальных алгоритмов сборки нет. Один из популярных: контроль за количеством ссылок. Но такойалгоритм не подходит для кольцевых списков. Поэтому используется более продвинутая версия алгоритма(выбирается объект, про который известно, что он точно живой -> находятся ссылки из него на другиеобъекты и эти объекты тоже помечаются живыми и т. д.). Главная проблема этого алгоритма неизвестность момента начала и если много объектов, то алгоритм занимает много времени.
А бонус этогоалгоритма - параллельная дефрагментация памяти. К тому же ссылка в процессе сборки мусора можетизмениться, поэтому мы не можем отождествить в этих языках ссылку с указателем.Сборка мусора часто не выполняется вообще, пока памяти продолжает хватать.Пример на C#:Image im = Image.FromFile(filename);<do smth>im.SaveToFile(filename);Сохранить в тот же файл не удается, потому что он захвачен функцией FromFile. Это логично, потому чтоесли файл большой, то читать его целиком в память нецелесообразно.
Очевидный выбор: освободитьресурс im и сохранить копию в тот же файл:Image im = Image.FromFile(filename);<do smth>Image im1 = im;im = null;im1.SaveToFile(filename);Но в этом случае все равно не будет гарантии, что этот код заработает, потому что хоть мы и присвоили im= null, но мы не можем сказать, что после этого начнется очистка ресурсов и, следовательно, закроетсяфайл. И это лишь одна из многих проблем.Еще о минусах сборки:Сейчас запускается низкоприоритетный процесс сборки мусора, потому что в интерфейсных программахесть куча времени, пока пользователь тупит, а сборщик тем временем тихо собирает мусор.В Java должен быть финализатор, потому что он - последняя надежда.Java: protected void finalize(){<свой деструктор>if (!close) Close(); // их деструктор}В C# еще хуже, потому что даже есть понятие деструктора, который на самом деле не работает так, как отнего ожидают.
На самом деле внутри любого деструктора вызывается просто финализатор класса Object.Еще метод для Java:с = new X();try{<...>return;}finally{<этот блок будет гарантированно выполнен в любом случае (будет исключение илинет)>c.Close();}В Delphi то же самое, но там между try и finally нельзя писать catch. То есть либо try-catch, либо try-finally.В C# ввели специальный интерфейс IDisposeable (утилизируемые) объекты.
Метод Dispose(); В примере скартинками надо написать не im = null, а im.Dispose();А еще можно так:using (Image im = Image.FromFile(..)){<smth>}На выходе из using вызывается Dispose для всех объектов, бывших внутри. Кусок кода полностьюэквивалентен этому:Image im = Image.FromFile(..);try{<smth>}finally{im.Dispose();}Никакой из традиционных языков (Ада, М-2 и т. д.) эти проблемы (создания-уничтожения) не решают, ониих игнорируют и предоставляют программисту.P. S. В Java есть термин "слабая ссылка" (WeakReference), имеющая непосредственное отношение ксборщику мусора.
Содержит метод Object GetReference(); Слабая ссылка - с точки зрения программистаэта память уже освобождена, но финализатор еще не применен. Теоретически, ресурсы еще можновосстановить методом GetReference(), превращая слабую ссылку в живую.Пример: кэш браузера. Переходим на следующую страницу, очевидно, что ресурсы старой страницы нужноосвободить. Мы обнуляем ссылки, и сборщик переводит их в разряд слабых. А если нажимается кнопкаBack, можно еще посмотреть очередь слабых ссылок, прежде чем лезть снова запрашивать объекты.IV) Дополнительные проблемы, связанные с классами••копирование, сравнениенеявные преобразованияЕсть понятие "глубокая" (deep) и "поверхностная" (shallow) копия.В C++ по умолчанию определяется побитовая копия (поверхностная), а если ее не хватает, то нужноопределить конструктор копирования.
Сравнение тоже может быть определено по-разному. Например,сравнивать ссылки или содержимое по ссылкам.Delphi игнорирует проблему копирования.В C# и Java есть метод Clone() интерфейса ICloneable, который можно переопределить для глубокогокопирования, но лучше так не делать, потому что не факт, что реализованная копия будет глубокой. Вклассе Object есть метод MemberWiseClone() - делает поверхностную копию.[lect26]В Java и C# есть специальные средства – рефлексии, для того, чтобы узнать поддерживается или нетопределенный интерфейс.В языке С++ есть три стратегии клонирования:• глубокое копирование• побитовое копирование• запрет копирования вообщеА вот в Java – 4 стратегии клонирования:• Разрешение клонирования - класс сообщает, что он клонирован и в нем обязательно определенпубличный метод клонированияclass X implement IClonable {public Object Clone() throw() {};Если вызывается недопустимая конструкция клонирование, то кидается специальное исключение•Запрещение клонирования – также кидается исключение•Условная поддержка клонирования – явным образом реализуется интерфейс IClonable, но вотподобъекты могут не клонироваться – тогда исключение•Неявная поддержка клонирование – не реализуется интерфейс, но делается побитовая копия:Class X { protected Object clone() {return …} }Также существует проблема сравнений по операциям равно (==) и неравно (!=).