- •ЛР 1. Операторы и выражения Delphi
- •Оператор присваивания
- •Арифметические операторы
- •Оператор конкатенации строк
- •Логические операторы
- •Операторы поразрядного сдвига
- •Операторы отношения
- •Условный оператор if..then..else
- •Оператор-селектор case
- •Оператор перехода goto
- •Операторы множеств
- •Составной оператор begin..end
- •Оператор 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
- •Обработка событий клавиатуры
- •Вариант 5
- •Задание
- •Вариант 1
- •Вариант 2
- •Вариант 3
- •Вариант 4
- •ЛР 6. Классы потоков данных
- •Прототип потоков данных, класс TStream
- •Потоки с дескриптором, класс THandleStream
- •Файловый поток данных, класс TFileStream
- •Пример работы с файловым потоком данных
- •Задание
- •Вариант 1.
- •Вариант 2.
- •Вариант 3.
- •Вариант 4.
- •Вариант 5.
Языки программирования
ЛР 3. Объекты и классы
Основной структурной единицей всей концепции ООП выступает класс. Класс — это программное описание будущего объекта, включающее в себя определение полей, методов и свойств. Объявление класса начинается с ключевого слова type, включает имя и признак класса class (листинг 3.1).
Листинг 3.1 Синтаксическая конструкция объявления класса
type
Имя_класса = class [abstract | sealed] (класс-предок)
//поля и методы end;
При объявлении класса можно воспользоваться одним из ключевых слов: abstract и sealed. Класс, отмеченный словом abstract, называют абстрактным. Основная особенность абстрактного класса заключается в том, что непосредственно на его основе невозможно создать самостоятельный объект или, как говорят программисты, экземпляр класса. Назначение абстрактного класса заключается в другом — он определяет концепцию построения всех своих наследников. Еще одно ключевое слово sealed запрещает классу иметь наследников.
По умолчанию новый класс создается на основе базового для всей иерархии классов визуальной библиотеки компонентов класса TObject. Но никто не запрещает нам, немного "порывшись" в дереве классов VCL или FMX, найти для своего класса наиболее подходящего предка.
Проектирование класса
Допустим, что перед нами поставлена задача — разработать класс, моделирующий автомобиль. На первых порах мы не станем углубляться в детали и обсуждать предпочтительную для нас коробку передач или особенности обвода кузова. Будем проще, в качестве прообраза будущего класса предлагаю взять листинг 3.2.
Листинг 3.2 Объявление класса TAutomobile
type TAutomobile= class(TObject) fSpeed:single; // поле скорости движения
function SetSpeed(Value:single):single; //установить скорость движения end;
Предлагаемый прототип автомобиля снабжен полем fSpeed, отвечающим за хранение данных о текущей скорости движения. За управление полем несет ответственность метод SetSpeed(), он позволит нам увеличить или уменьшить скорость автомобиля.
Каждый знает, что автомобиль без двигателя — не более чем груда металлолома. Поэтому создадим класс двигателя TEngine (листинг 3.3) и интегрируем его в автомобиль.
Листинг 3.3. Объявление класса TEngine
type TEngine= class
fEnabled :boolean; //двигатель включен - true, выключен - false procedure SetEnabled(Value:Boolean); //запуск или останов двигателя function GetEngineState:boolean; //состояние двигателя
26
СКФУ Кафедра компьютерной безопасности
Языки программирования end;
function TEngine.GetEngineState: boolean;//состояние двигателя begin
Result:= fEnabled; //если true - двигатель включен end;
procedure TEngine.SetEnabled(Value: Boolean); begin
fEnabled:=Value; end;
В нашем примере в состав двигателя вошло поле fEnabled, которое определяет состояние двигателя (true — включен, false — отключен). Метод SetEnabled() отвечает за запуск и остановку двигателя. Функция GetEngineState() информирует о текущем состоянии двигателя.
Для того чтобы двигатель оказался "под капотом" класса TAutomobile, (листинг 3.4) объявляется поле fEngine:TEngine (если вас устроит такое сравнение — поле fEngine является аналогом двигательного отсека в корпусе реального автомобиля).
Листинг 3.4. Инкапсуляция двигателя TEngine в автомобиль TAutomobile
type TAutomobile= class(TObject)
fSpeed:single; |
//поле |
скорости движения |
fEngine:TEngine; |
//поле |
двигателя |
function SetSpeed(Value:single):single; //установить скорость движения end;
Для того чтобы продемонстрировать пример взаимодействия автомобиля и его двигателя, в состав методов автомобиля добавим функцию SetSpeed(), она возьмет на себя ответственность за управление скоростью движения автомобиля (листинг 3.5).
Листинг 3.5. Реализация класса TAutomobile
function TAutomobile.SetSpeed(Value:single):single; begin
Result:=0;
if fEngine.GetEngineState=true then {если двигатель заведен} begin
if (Value>=0) and (Value<=240) then {проверим корректность значения} begin
fSpeed:=Value;
Result:=fSpeed; //скорость успешно изменена end
else Raise Exception.Create('Допустимый диапазон 0...240 км/ч'); end else
begin fSpeed:=0;
Raise Exception.Create('Необходимо запустить двигатель!'); end;
end;
27
СКФУ Кафедра компьютерной безопасности
Языки программирования
Управление жизненным циклом объекта
Любой класс автоматически вооружается парой специализированных методов, несущих прямую ответственность за жизненный цикл объекта. Это:
конструктор (constructor), который отвечает за создание из класса нового объекта (экземпляра класса). По существующей договоренности конструктор описывается в рамках метода Create();
деструктор (destructor), в обязанности которого входит уничтожение объекта. Функционал деструктора обычно закладывается в процедуру Destroy().
Описание класса автомобиля, включающее конструктор и деструктор, выглядит примерно так, как представлено в листинге 3.6.
Листинг 3.6. Объявление конструктора и деструктора класса TAutomobile
type TAutomobile= class(TObject)
...
constructor Create; //конструктор объекта destructor Destroy; //деструктор объекта
end;
constructor TAutomobile.Create; //создание автомобиля begin
fEngine:=TEngine.Create; //создаем двигатель fEngine.SetEnabled(False); //в исходном состоянии двигатель выключен
fSpeed:=0; //скорость автомобиля 0 км/ч end;
destructor TAutomobile.Destroy; //уничтожение автомобиля begin
fSpeed:=0; |
//автомобиль должен быть остановлен |
fEngine.Destroy; |
//уничтожаем двигатель |
end; |
|
В коде конструктора мы создаем двигатель автомобиля и присваиваем ссылку на него в поле fEngine. Затем мы устанавливаем параметры автомобиля в состояние по умолчанию: двигатель выключен, скорость равна 0. Во время вызова деструктора мы решаем обратную задачу — останавливаем автомобиль и уничтожаем двигатель.
Если программист, по каким-то причинам отказался от описания конструктора и деструктора разрабатываемого класса, то создание и уничтожение объекта будет осуществлено за счет конструктора и деструктора родительского класса. В нашем примере мы не стали создавать индивидуальные методы Create() и Destroy() для двигателя TEngine. Это означает, что управление существованием двигателя будет осуществляться за счет унаследованных им методов класса-предка (в нашем случае TObject).
Практический аспект применения конструктора и деструктора рассмотрен в листинге 3.7.
Листинг 3.7. Создание и уничтожение объекта в коде программы
var A : TAutomobile; //объектная переменная begin
A:=TAutomobile.Create; //создаем объект
28
СКФУ Кафедра компьютерной безопасности
Языки программирования |
|
... |
//операции с объектом |
A.Destroy; |
//разрушаем объект |
end; |
|
Опережающее объявление класса
Разрабатывая даже столь простую имитацию транспортного средства, нам все равно хочется достичь схожего поведения класса TAutomobile и реального автомобиля. Именно из-за этого, наиболее интересной частью кода стал метод SetSpeed(), управляющий скоростью машины. Благодаря программной логике, заложенной в упомянутый метод (см. листинг 5), мы не позволим набрать скорость автомобилю, если у него не включен двигатель. Предлагаю поставить перед собой еще одну задачу — при выключении двигателя скорость автомобиля должна упасть до 0.
Если вы ненадолго вернетесь к листингу 3, в котором представлены методы класса TEngine, то вы сразу вспомните, что за остановку двигателя отвечает процедура SetEnabled() с аргументом false. На первый взгляд, может показаться, что мы находимся в одном шаге от решения поставленной задачи — надо лишь дополнить метод SetEnabled() строкой кода
if Value=false then fAutomobile.fSpeed:=0;
Однако на практике это невозможно. Причина проста — класс TEngine не имеет ни малейшего представления даже о существовании класса TAutomobile, не говоря уже о его полях и методах. Поэтому упоминание поля автомобиля fSpeed в методе двигателя приведет к одному — Delphi скажет, что идентификатор не объявлен (undeclared identifier) и откажется компилировать проект.
Какой выход из создавшегося положения? Ответ таков: надо сделать так, чтобы двигатель знал, в какую машину он установлен. Для этого я введу в состав класса TEngine еще одно поле — fAutomobile:TAutomobile. Однако в нашем листинге класс TAutomobile объявлен после класса TEngine, поэтому двигатель по-прежнему не понимает, что такое TAutomobile. Для устранения этой проблемы воспользуемся еще одной хитростью Delphi — опережающим объявлением класса.
Суть идеи опережающего объявления заключается в том, что мы заявим о намерении описать класс TAutomobile практически в первых строках листинга, а реальное описание оставим на прежнем месте (после описания двигателя). Благодаря такой уловке класс TEngine перестанет отвергать поле fAutomobile.
Листинг 3.8 содержит усовершенствованное объявление классов TEngine и TAutomobile.
Листинг 3.8. Улучшенный способ объявления классов TEngine и TAutomobile
{начало объявлений, одно ключевое слово type для TEngine и TAutomobile} type
TAutomobile=class; //опережающее объявление класса TAutomobile
{полное объявление класса TEngine} TEngine=class
fEnabled :boolean;
fAutomobile :TAutomobile; //автомобиль, в который установлен двигатель
//другие поля и методы класса end;
{полное объявление класса TAutomobile} TAutomobile=class(TObject)
fSpeed:single; //скорость движения
//другие поля и методы класса end;
29
СКФУ Кафедра компьютерной безопасности
