Планирование уроков на учебный год (по учебнику К.Ю. Полякова, Е.А. Еремина, полный углублённый курс, по 4 часа в неделю)



Уроки 92 - 94
Иерархия классов
(§50. Иерархия классов)




Содержание урока

Классификации

Иерархия логических элементов

Базовый класс

Классы-наследники

Модульность

Сообщения между объектами

Вопросы и задания

Задачи


Классы-наследники


Теперь займёмся классами-наследниками от TLogElement. Поскольку у нас будет единственный элемент с одним входом (НЕ), сделаем его наследником прямо от TLogElement (не будем вводить специальный класс «элемент с одним входом»).

type
TNot = class(TLogElement) protected
procedure calc; override; 
end;

После слова class в скобках указано название базового класса. Все объекты класса TNot обладают всеми свойствами и методами класса TLogElement.

Новый класс переопределяет метод calc, на это указывает слово override (в переводе с англ. — перекрыть). Заметим, что у базового класса TLogElement этот метод не реализован — он абстрактный, поэтому в данном случае мы фактически программируем метод, объявленный в базовом классе. Для элемента «НЕ» он выглядит очень просто:

procedure TNot.calc; 
begin
FRes := not In1
end;

Класс TNot уже не абстрактный, потому что абстрактный метод предка переопределён и теперь известно, что делать при вызове метода calc. Поэтому можно создавать объект этого класса и использовать его:

var n: TNot;
...
n:=TNot.Create ; 
n. In1:=False; 
writeln(n.Res) ;

Остальные элементы имеют два входа и будут наследниками класса.

TLog2In = class(TLogElement) 
public
	property In2; 
end;

Единственное, что делает этот класс, — переводит свойство In2 в раздел public, т. е. делает его общедоступным. Отметим, что видимость можно только повышать, т. е. нельзя, например, в наследнике сделать общедоступное свойство класса-предка закрытым или защищённым.

Класс TLog2In — это тоже абстрактный класс, потому что он не переопределил метод calc. Это сделают его наследники TAnd (элемент И) и ТОr (элемент ИЛИ), которые определяют конкретные логические элементы:

type
TAnd = class (TLog2ln} 
protected
procedure calc; override; 
end ;
TOr = class(TLog2In) 
protected
procedure calc; override; 
end;

Реализация переопределённого метода calc для элемента «И» выглядит так:

procedure TAnd.calc; 
begin
FRes := In1 and In2 
end;

Для элемента «ИЛИ» этот метод определяется аналогично.

Обратим внимание на метод setln1, введённый в базовом классе:

procedure TLogElement.setln1(newln1: boolean);
begin
FIn1:=newln1; calc;
end;

В нём вызывается метод calc, который пересчитывает значение на выходе логического элемента при изменении входа. Какой же метод будет вызван, если в базовом классе TLogElement он только объявлен, но не реализован?

Проблема в том, что для вызова любой процедуры нужно знать её адрес в памяти. Для обычных методов транслятор сразу записывает в машинный код нужный адрес, потому что он заранее известен. Это так называемое статическое связывание (связывание на этапе трансляции), при выполнении программы этот адрес не меняется.

В нашем случае адрес метода неизвестен: в классе TLogElement его нет вообще, а у каждого класса-наследника адрес метода calc свой собственный. Чтобы выйти из положения, используется динамическое связывание, т. е. адрес вызываемой процедуры определяется при выполнении программы, когда уже определён тип объекта, с которым мы работаем. Такой метод нужно объявлять виртуальным, что мы и сделали ранее. Это означает не только то, что его могут переопределять наследники, но и то, что будет использоваться динамическое связывание. Теперь можно дать полное определение виртуального метода.

Виртуальный метод — это метод базового класса, который могут переопределить классы-наследники, при этом конкретный адрес вызываемого метода определяется только при выполнении программы.

Теперь мы готовы к тому, чтобы создавать и использовать построенные логические элементы. Например, таблицу истинности для последовательного соединения элементов «И» и «НЕ» можно построить так:

Сначала создаются два объекта — логические элементы НЕ (класс TNot) и ИЛИ (класс TAnd). Далее в двойном цикле перебираются все возможные комбинации значений логических переменных А и В, они подаются на входы элемента И, а его выход — на вход элемента НЕ. Чтобы при выводе логических значений вместо False и True выводились более компактные обозначения 0 и 1, значения входов и выхода преобразуются к целому типу (integer).

Следующая страница Модульность



Cкачать материалы урока






Наверх