
- •ЛР 1. Операторы и выражения Delphi
- •Оператор присваивания
- •Арифметические операторы
- •Оператор конкатенации строк
- •Логические операторы
- •Операторы поразрядного сдвига
- •Операторы отношения
- •Операторы множеств
- •Составной оператор begin..end
- •Условный оператор if..then..else
- •Оператор-селектор case
- •Оператор перехода goto
- •Оператор with..do
- •Организация циклов
- •Цикл с параметром for..do
- •Инструкция in в цикле for..do
- •Цикл с предусловием while..do
- •Цикл с постусловием repeat..until
- •Вложенные циклы
- •Операторы break и continue
- •ЗАДАНИЕ
- •Вариант 1.
- •Вариант 2
- •Вариант 3
- •Вариант 4
- •Вариант 5
- •Вариант 6
- •Вариант 7
- •Вариант 8
- •Вариант 9
- •Вариант 10
- •Вариант 11
- •Вариант 12
- •ЛР 2. Структурные типы
- •Множества
- •Записи
- •Вариантные поля
- •Усовершенствованная запись
- •Массивы
- •Объявление массива
- •Обращение к ячейкам массива
- •Динамический массив
- •ЗАДАНИЕ
- •Вариант 1
- •Вариант 2
- •Вариант 3
- •Вариант 4
- •Вариант 5
- •Вариант 6
- •Вариант 7
- •Вариант 8
- •Вариант 9
- •Вариант 10
- •Вариант 11
- •Вариант 12
- •ЛР 3. Объекты и классы
- •Проектирование класса
- •Управление жизненным циклом объекта
- •Опережающее объявление класса
- •Ограничение видимости членов класса
- •Свойства объекта
- •Особенности объявления методов
- •Поля класса и методы класса
- •Иерархия наследования
- •Полиморфизм
- •ЗАДАНИЕ
- •Вариант 1
- •Вариант 2
- •Вариант 3
- •Вариант 4
- •ЛР4. Опорные классы VCL
- •Класс TObject
- •Класс TPersistent
- •Основа компонента, класс TComponent
- •Владение компонентом
- •Класс TControl
- •Принадлежность к родительскому контейнеру
- •Размещение и размеры элемента управления
- •События, связанные с изменением размеров
- •Выравнивание элемента управления в контейнере
- •Видимость и активность элемента управления
- •Задание
- •Вариант 1
- •Вариант 2
- •Вариант 3
- •Вариант 4
- •Вариант5
- •Совет
- •ЛР5. Обработка клавиатурных событий и событий мышки
- •Щелчки кнопками мыши
- •Перемещение указателя мыши
- •Вращение колесика мыши
- •Операция перетаскивания drag and drop
- •Обработка событий клавиатуры
- •Задание
- •Вариант 1
- •Вариант 2
- •Вариант 3
- •Вариант 4
- •Вариант 5
- •ЛР 6. Классы потоков данных
- •Прототип потоков данных, класс TStream
- •Потоки с дескриптором, класс THandleStream
- •Файловый поток данных, класс TFileStream
- •Пример работы с файловым потоком данных
- •Задание
- •Вариант 1.
- •Вариант 2.
- •Вариант 3.
- •Вариант 4.
- •Вариант 5.

Языки программирования
Обязательно обратите внимание на небольшое изменение в порядке объявления классов. Раньше (см. листинги 3.2 и 3.3) описание каждого из классов начиналось с отдельного ключевого слова type, в новой версии кода для обеспечения видимости опережающего объявления класса TAutomobile мы ограничились одним словом type.
Еще одна синтаксическая особенность опережающего объявления в том, что заявление о существовании класса заканчивается не ключевым словом end, как того требует синтаксис (листинг 1). Вместо этого точка с запятой ставится сразу после инструкции class.
Добившись того, что класс TEngine узнал о существовании класса TAutomobile, усовершенствуем конструктор автомобиля (листинг 3.9).
Листинг 3.9. Исправление конструктора класса TAutomobile
constructor TAutomobile.Create; begin
fEngine:=TEngine.Create; |
//создаем двигатель |
fEngine.fAutomobile:=self; |
//двигатель получил ссылку на автомобиль |
fEngine.SetEnabled(False); |
//исходное состояние — двигатель выключен |
end; |
|
Теперь, в момент создания автомобиля, ссылка на экземпляр класса TAutomobile будет передана в соответствующее поле экземпляра класса TEngine. Благодаря ссылке двигатель получает возможность обращаться к полям и методам автомобиля.
Нам осталось сделать последний штрих — дополнить метод включения/отключения двигателя SetEnabled() командой на остановку автомобиля (листинг 3.10).
Листинг 3.10. Остановка автомобиля в момент выключения двигателя
procedure TEngine.SetEnabled(Value: Boolean); begin
fEnabled:=Value; if Value=false then
if Assigned(fAutomobile) then {если есть связанный объект}
fAutomobile.fSpeed:=0;
end;
Ограничение видимости членов класса
При описании класса программист имеет право определять степень доступности (видимости) его полей, свойств и методов. Это один из способов защиты наиболее критичных элементов класса от несанкционированного вмешательства сторонних разработчиков. Область видимости поля (метода) класса зависит от того, в какой из четырех возможных секций оно объявлено: private, protected, public и published (листинг 3.11).
Листинг 3.11. Секции видимости членов класса
type
TAutomobile = class private
... { секция частных объявлений } protected
... { секция защищенных объявлений } public
30
СКФУ Кафедра компьютерной безопасности

Языки программирования
... { секция общих объявлений } published
... { секция опубликованных объявлений } end;
Поля и методы, доступ к которым нежелателен, обычно размещаются в секциях private и protected. Наиболее защищена секция private. К размещенным в ней полям и методам возможно обращение только из того же программного модуля, в котором описан этот класс. Секция protected несколько приоткрывает завесу секретности — находящаяся в ней информация без каких-либо ограничений доступна для классов-потомков. Секция public предоставляет объявленные в ней поля и методы для общего пользования всем желающим. Секция published самая доброжелательная. Например, объявленными в ней данными умеет пользоваться Инспектор объектов. Это возможно благодаря тому, что для этой секции класса генерируется информация о времени выполнения (Run Time Type Information, RTTI). Поэтому в секции published обычно объявляют все свойства и обработчики событий объекта.
Свойства объекта
Как правило, объект не должен предоставлять прямой доступ к своим полям. Это требование инкапсуляции, благодаря которому поддерживается целостность объекта. Поэтому при проектировании класса одновременно с полями объявляют особых посредников, называемых
свойствами (properties).
В первую очередь в обязанность свойств входит проверка корректности передаваемых в поля данных, для этого они могут задействовать все мощь языка Delphi. В самом простейшем случае синтаксическая конструкция объявления свойства выглядит следующим образом:
property Имя_свойства: Тип_свойства Read (способ чтения) Write (способ записи);
За инструкциями Read и Write могут следовать названия процедур, соответственно отвечающих за чтение данных из поля и запись данных в поле.
Возьмем в качестве примера класс двигателя TEngine, он обладает парой полей fEnabled и fAutomobile, которые следует обязательно защитить. Листинг 3.12 демонстрирует один из возможных сценариев усовершенствования класса.
Листинг 3.12. Объявление свойств
type TEngine=class private
fEnabled :boolean; //двигатель включен - true, выключен - false
fAutomobile:TAutomobile; //ссылка на автомобиль public
procedure SetEnabled(Value:Boolean); //запуск или останов двигателя
function GetEngineState:boolean; //состояние двигателя published
property Enabled:boolean Read fEnabled Write SetEnabled;
property Automobile:TAutomobile Read fAutomobile; end;
Все поля класса "спрятаны" в секцию private, что существенно усложнит обращение к полям извне. Наоборот, методы и свойства, к которым мы намерены разрешить полный доступ, размещены в секции публичных объявлений. Теперь для включения/отключения двигателя надо воспользоваться свойством Enabled. Еще большую степень защиты мы обеспечили полю
31
СКФУ Кафедра компьютерной безопасности

Языки программирования
fAutomobile, хранящему ссылку на автомобиль, в который установлен двигатель — свойство допускает только чтение информации из поля.
Особенности объявления методов
Хотя по своей сути метод является процедурой или функцией, в объектно-ориентированном программировании в него вкладывается значительно более глубокий смысл. Концепция ООП предполагает возможность класса наследовать методы своего предшественника, а при необходимости и переопределять их поведение. Для обеспечения этой, очень сложной задачи в Delphi методы разделяются на три категории:
статические (директива static);
динамические (директива virtual);
виртуальные (директива dynamic).
По умолчанию, любой вновь описанный метод, становится статическим. Такое название метод заслужил из-за того, что во время компиляции статическому методу выдается постоянный адрес, такой способ называется статическим связыванием (static binding). У метода с постоянным адресом есть две особенности: высокая скорость вызова (что не может не радовать) и абсолютная невозможность изменить поведение этого метода в дочерних классах (что является отрицательной стороной статического связывания). Одним словом, объявляя статический метод, мы должны понимать, что наносим удар по полиморфизму (наследник класса утратит возможность переопределить этот метод).
В отличие от своего статического собрата виртуальные и динамические методы исповедуют идеи позднего связывания (late binding). Это означает, что адреса таких методов не "высечены в граните", а могут изменяться. Так адреса виртуальных методов класса заносятся в таблицу виртуальных методов этого класса, а адреса динамических методов — в специальный список методов этого класса. Благодаря технологии позднего связывания доступ к виртуальным и динамическим методам осуществляется несколько медленнее, но зато они допускают переопределение своего поведения у классов-потомков.
Поля класса и методы класса
Одно из правил объектно-ориентированного программирования предполагает, что обращение к полям и методам, описанным в классе, невозможно до тех пор, пока из класса не будет создан объект — экземпляр класса. С точки зрения программы Delphi это означает, что начало работы со свойствами и методами объекта должно предваряться вызовом его конструктора Create().
Однако из правил существуют исключения. В состав класса могут входить особые поля и методы, которые остаются доступными даже в отсутствие экземпляра объекта. Такие поля и методы называют соответственно полями класса (class fields) и методами класса (class methods).
По своей сути поле класса представляет собой обычную переменную, для ее объявления следует воспользоваться ключевыми словами class var или просто var.
Метод класса представляет собой процедуру или функцию с первым словом в заголовке class,
например class procedure или class function.
Благодаря полю класса и методу класса мы можем внести очередное усовершенствование класса двигателя TEngine (листинг 3.13).
Листинг 3.13. Объявление поля класса и метода класса TEngine
TEngine=class private
32
СКФУ Кафедра компьютерной безопасности

Языки программирования fEnabled :boolean;
fAutomobile:TAutomobile; public
class var fMax_HorsePower : word; //переменная класса class function GetMax_HorsePower : word; //метод класса
...
end;
{реализация функции класса}
class function TEngine.GetMax_HorsePower: word; begin
Result:=fMax_HorsePower; end;
Как и утверждалось ранее, для обращения к полям и методам класса совсем не обязательно создавать объект, вполне допустимо воспользоваться строками кода из листинга 3.14.
Листинг 3.14. Обращение к полю класса и методу класса TEngine
var X:Word; begin
with TEngine do begin
fMax_HorsePower:=250;
X:=GetMax_HorsePower; end;
end;
Иерархия наследования
Вновь возвратимся к очень важному ингредиенту ООП — наследованию. Мы уже знаем, что в основе концепции наследования лежит возможность строительства нового класса на базе уже существующего родительского класса. Благодаря наследованию дочерний класс впитывает в себя все родовые черты своего предка. Ограничений на длину цепочки наследования нет, в визуальной библиотеке компонентов Delphi объявлены классы, имеющие в родословной больше десятка предков. Таким образом, самый последний в древе наследования класс вбирает в себя все лучшее от всех своих предшественников.
Для того чтобы понять какие уникальные возможности предоставляет программисту наследование, перейдем к практической стороне вопроса. В нашем распоряжении уже имеется класс TAutomobile, олицетворяющий все наши знания в области ООП. Попробуем развить первоначальный успех и создать программный класс, способный смоделировать пассажирский автобус — класс TAutobus.
Автобус — это всего лишь разновидность автомобиля. У него, как и у автомобиля, есть двигатель, автобус также может набирать и снижать скорость движения. Одним словом, за основу нашего нового класса можно взять уже существующий TAutomobile и дополнить его всего одной возможностью — перевозить пассажиров (листинг 3.15).
Листинг 3.15. Создание дочернего класса TAutobus
type TAutobus=class(TAutomobile) private
fPassenger:byte; //поле — число пассажиров
procedure SetPassenger(Value:byte); //управление числом пассажиров
33
СКФУ Кафедра компьютерной безопасности