- •С примерами на Object pascal (72 часа)
- •Введение
- •1.1. Основной принцип структурного программирования
- •1.2. Основные задачи структурного программирования
- •1.3. Основные принципы объектно-ориентированного программирования (ооп)
- •2.1. Алфавит языка pascal. Идентификаторы.
- •2.2. Простые и составные специальные символы, используемые на пк.
- •2.3. Стандартные типы данных. Двоичное представление чисел и дополнительный код.
- •2.4. Особенности работы с некоторыми типами данных
- •2.5. Арифметические операторы.
- •2.6. Логические операторы и операции отношения.
- •2.7. Потеря точности, потеря порядка и переполнение при арифметических операциях.
- •2.8. Структура простейшей программы.
- •2.9. Простейшие операторы языка Object pascal
- •1) Цикл от меньшего к большему значению:
- •2) Цикл от большего к меньшему значению:
- •2.10. Структурные типы данных
- •2.11. Указатели. Динамические переменные. Динамическое выделение и высвобождение памяти
- •2.12. Процедуры и функции. Формальные и фактические параметры. Передача параметров по ссылке, по значению и через указатель.
- •2.13. Локальные и глобальные переменные. Побочный эффект функции. Вложенность процедур и функций. Правила видимости.
- •2.14. Пример на правила видимости и передачи параметров
- •2.15. Рекурсия
- •2.1. Инкапсуляция. Объект. Поля данных и методы объекта
- •3.2 Задание модуль класса (статическая объектная модель)
- •4.10. Создание и уничтожение объектов. Конструкторы и деструкторы.
- •4.11. Наследование. Статические, виртуальные, динамические и абстрактные методы.
- •4.12. Области видимости объектов.
- •4.13. Обработка исключительных ситуаций.
- •4.14. Специальные средства для работы с ms Windows: сообщения.
- •4.15. События и их делегирование. Обработка событий.
- •4.16. Методы класса и указатели на класс.
- •4.17. Дополнительные возможности Object Pascal.
- •5. Визуальное проектирование в среде Delphi.
- •5.8. Создание экранных форм. Инспектор объектов.
- •5.9. Палитра компонентов.
- •6. Интерфейсы и множественное наследование
- •6.1. Общие представления об интерфейсах в Object pascal
- •6.2. Реализация интерфейсов свойством
- •6.3. Различение имен при реализации нескольких интерфейсов
- •5.11. Спецификаторы вызовов процедур и функций
- •5.12. Динамические массивы
- •5.13. Перезагрузка (overloading) методов, процедур и функций
- •5.14. Параметры по умолчанию
- •5.17. Технологии dde и ole
- •5.18. Технология ActiveX
- •5.20. Использование библиотек OpenGl и glu
- •5.21. Создание программистом собственного компонента Литература
4.10. Создание и уничтожение объектов. Конструкторы и деструкторы.
В Object Pascal объекты могут быть только динамическими! Любая переменная объектного типа — это указатель, но доступ к объекту идет без 'шляпки', а создание нового объекта осуществляется не оператором New, а с помощью вызова конструктора — функции, возвращающей указатель на создаваемый объект. Соответственно, уничтожение объекта производится не с помощью Dispose, а вызовом деструктора. Поэтому обычно в Delphi конструктор называют Create ("создать"), а деструктор — Destroy ("уничтожить, разрушить"):
aMyObject:=tMyObject.Create; {создали объект aMyObject типа tMyObject
и запомнили указатель на него}
...
aMyObject.Destroy; {уничтожили объект}
Заметим, что конструктор — это метод класса, и он работает без экземпляра объекта (экземпляра еще нет, но метод создает его). В Delphi конструкторы могут быть виртуальными, и их может быть несколько. Сразу при входе в конструктор желательно проинициализировать прародителя:
constructor tMyObject.Create;
begin
inherited Create;
...
end;
В данном примере инициализация прародителя осуществляется с помощью специального ключевого слова inherited, которое появилось еще в TurboPascal 7.0. Оно означает, что следующий за ним вызов относится к непосредственному прародителю данного объекта. Многократные вызовы типа inherited inherited запрещены, чтобы не вызвать неправильной инициализации объекта.
Если наш класс описан как
tMyObject=class(tObj1)
...
end;
то вызов inherited Create абсолютно логичен вызову tObj1.Create.
4.11. Наследование. Статические, виртуальные, динамические и абстрактные методы.
Все классы Delphi — потомки класса tObject. Если нет указания, какой класс является прародителем, значит, это tObject. Определение
tMyObject=class
...
end;
полностью равносильно
tMyObject=class(tObject)
...
end;
Вызываемые методы делятся на статические (static), виртуальные (virtual) и динамические (dynamic). Есть еще методы, которые не содержат никаких действий (нет реализации), никогда не вызываются и нужны только для наследования — абстрактные (abstract). Абстрактными могут быть только виртуальные или динамические методы, причем у них имеется только заголовок (в описании класса) и отсутствует реализация.
Переопределение методов в потомках называется перекрытием этих методов, а сами переопределенные методы прародителя — перекрытыми (override) потомком.
Перекрытие статических методов происходит так же, как в Turbo Pascal. Для виртуальных методов синтаксис изменен: в первом из прародителей, где определяется виртуальный метод, ставится ключевое слово virtual, а в потомках для перекрывающих методов — override.
Для виртуальных методов в классе постоянно хранятся адреса всех имеющихся в данном классе виртуальных методов независимо от того, унаследованы они или перекрыты. Виртуальные методы вызываются быстро, но таблица виртуальных методов (VMT) может занимать много места, если имеется длинная цепочка наследования с большим количеством методов в каждом классе. Динамические методы являются более экономичными по памяти и более медленным аналогам виртуальных. Каждому динамическому методу системой присваивается уникальный индекс. В таблице динамических методов (DMT) класса хранятся индексы и адреса только тех динамических методов, которые описаны в данном классе. Если при вызове нужный метод в таблице не найден, просматривается таблица прародителя, и т.д. вплоть до tObject. В остальном разницы между виртуальными и динамическими методами нет. Стоит отметить, что Delphi предоставляет существенно больший набор средств программирования, чем, например, Java, где ни статических, ни виртуальных методов нет, а только динамические.
Вызов не перекрытого абстрактного метода (т.е. помеченного директивой abstract) дает диагностику ошибки. Абстрактные методы используются на начальном уровне описания иерархии, а такая диагностика помогает при отладке программы находить потомки, в которых программист забыл перекрыть необходимые методы.
Пример использования виртуальных и абстрактных методов:
type
tField=
class function GetData:string;virtual;abstract; {нужна для работы
ShowData1}
tStringField=
class(tField)
fData:string;
function GetData:string;override;{перекрыли абстрактный метод}
end;
tIntegerField=
class(Field)
fData:Integer;
function GetData:string;override; {то же}
end;
...
{-реализация перекрывающих методов-}
function tStringField.GetData:string;
begin
GetData:=fData; {присваивание строкового поля}
end;
function tIntegerField.GetData:string;
begin
GetData:=IntToStr(fData); {преобразование из целого в строку}
end;
...
{-процедура, которая работает для разных типов полей-}
procedure ShowData(aField:tField);
begin
Form1.Label1.Caption:=aField.GetData;{вызов метода, который для
класса tField абстрактный}
end;
Надо отметить, что на этапе написания процедуры ShowData мы используем алгоритм для экземпляра класса, в котором описан наш абстрактный метод. Такой класс логично называть абстрактным и считать, что экземпляров данного класса быть не может. На деле, вместо экземпляров абстрактного класса будут подставляться экземпляры классов-потомков, в которых все абстрактные методы перекрыты. А формальное использование объекта из прародительского класса дает возможность писать единый алгоритм для всех классов-потомков разветвляющейся иерархии с разными типами аналогичных по смыслу полей в каждой ветви иерархии.
Особенность нового синтаксиса для объектов — то, что динамически созданный объект как параметр можно передавать как по значению (см. ShowData), так и по имени (см. далее ShowData1):
procedure ShowData1(var aField:tField);
begin
...
end.
При этом в случае передачи по значению котируется только указатель на объект, но не его тело. В Object Pascal, в отличие от C++, нет понятия "конструктора копии", и при необходимости скопировать тело объекта надо просто задать метод объекта, который занимается его копированием.