Ответы на экзамен (987689), страница 5
Текст из файла (страница 5)
∙•••••••• Свойства - это обобщение понятия поле, свойству должна соответствовать функция, позволяющая определить его значение на основе значения (значений) данных или процедура, позволяющая определить значение данных на основе значения свойства.
Объединение в одном типе данных – классе – полей, методов и свойств называется инкапсуляцией. Инкапсуляция является первым фундаментальным принципом объектно-ориентированного программирования.
Объявление класса:
Type
Имя_класса = class
Объявление данных
Объявление методов
Объявление свойств
End;
Пример:
type
cla=class
i, j :integer; {поля}
Constructor Create; {конструктор}
Function Geti: integer; {метод - функция}
procedure Seti (k:integer); {метод - процедура}
end;
{
тексты методов должны быть за пределами объявления класса,
Перед именем метода должно быть имя класса для отличия процедур (функций) –
членов класса от обычных процедур и функций
}
constructor cla.Create;
begin
inherited Create;
write('i= ');
readln(i);
write('j= ');
readln(j);
end;
Function cla.Geti;
begin
result:=i;
end;
Procedure cla.Seti; {формальные параметры здесь можно повторно не указать}
begin
i:=k;
end;
Поля класса являются для методов своего класса глобальными переменными. Правила использования параметров в функциях и процедурах остаются прежними: переменная к является формальным параметром.
Можно использовать и глобальные переменные в традиционном смысле, но тогда они должны быть объявлены вне класса. В методах класса можно использовать и локальные переменные, правила их объявления не меняются.
Объявление объекта и его использование показаны ниже.
var
mycla :cla; {объявим указатель на класс}
begin
mycla:=cla.Create; {вызываем конструктор}
writeln('Result=', mycla.Geti); {вызывает метод - функцию}
readln;
end.
Объявление
var
mycla :cla;
Означает объявление указателя на класс cla. Напомним, что указатель – это переменная, значением которой является адрес в памяти. Специальный знак ^, который обычно отличает указатель на Pascal от обычных переменных, для указателей на класс не ставится. По умолчанию, указателю никакое значение не соответствует и поэтому до его использования необходимо присвоить нужное значение.
Для присвоения значения указателю вызываем конструктор:
mycla:=cla.Create;
в нашем случае это дополнительно означает выполнение ввода двух переменных.
Примечание. В принципе, отсутствие конструктора в объявлении класса не ошибка. Но приведенный выше его вызов – обязателен. В таком случае конструктор выполняет только стандартные действия по инициализации экземпляра класса. Почему это так, будет ясно в следующем параграфе.
Остались нерассмотренными свойства. Свойства являются расширением понятия поле. В простейшем случае свойство просто представляет собой поле. Пример такого свойства и его использование в приведенной ниже программе обозначены комментарием {2}. По сути дела, такое свойство равносильно своему полю. Возникает законный вопрос: для чего нужны такие свойства? Методика объектно-ориентированного программирования не рекомендует прямые обращения к полям классов. Атрибутами доступа можно даже запретить прямое обращение к ним. Свойствами можно представить только те поля, с которыми пользователю разрешено прямое обращение.
В более сложном случае свойству могут соответствовать функция, позволяющая вычислить его значение по значениям полей по сколь угодно сложному алгоритму, и процедура, позволяющая определить значение поля (полей) на основе заданного значения свойства. Такая процедура может содержать, например, проверку корректности предложенного значения и выдачу сообщения, если оно не удовлетворяет условиям. Таким образом, можно уменьшить вероятность использования неверных данных в программе. Примеры таких свойств в программе обозначены комментариями {1},{3}.
Если рассматривать переменные a, b, c как стороны треугольника, то свойство Perim задает его периметр, а функция Per позволяет периметр вычислить. Так как на основе периметра стороны треугольника вычислить невозможно, то соответствующая процедура смысла не имеет и часть write в свойстве {1} отсутствует.
Свойство {3} имеет обе части: за read записано имя функции определения значения свойства, в за write – имя процедуры определения значения поля (в общем случае полей) на основе значения свойства.
Type
cla=class
a, b, c :real;
Constructor Create;
Destructor Destroy;
Function Geta :real;
Function Per :real;
Procedure Seta (w :real);
Property Perim :real read Per; { 1 }
Property yy :real read b write b; { 2 }
Property xx :real read Geta write Seta; { 3 }
end;
Constructor cla.Create;
{}
Destructor cla.Destroy;
{}
Function cla.Per;
begin
result:=a+b+c;
end;
Function cla.Geta;
begin
result:=a;
end;
Procedure cla.Seta;
begin
a:=w;
end;
{-----------------------------------}
var
my:cla;
begin
my := cla.Create; {создание объекта, запускается конструктор}
writeln ('Свойство yy ', my.yy:6:2); {вывод поля через свойство}
writeln ('Периметр через Perim=' , my.Perim:6:2);
{вычисление периметра через свойство}
my.xx := 10; {присвоение значения свойству}
my.yy := 20;
writeln (‘Свойство xx=' , my.xx:6:2); {вывод поля через свойство}
my.Free; {уничтожение объекта, без каких либо специальных действий}
my:=cla .Create; {создание нового объекта}
writeln (''Периметр через Per=' , my.Per:6:2); {вычисление периметра через функцию}
my.Destroy; {уничтожение объекта с выполнением деструктора}
readln;
end.
Наследование
Вторым фундаментальным принципом объектно-ориентированного программирования является наследование. Для определения отношения наследования при объявлении класса указывается имя класса – его предшественника (только одного!). Рассмотрим это на примере: определить сумму всех элементов массива и количество элементов, значения которых больше заданного.
Type
cla=class
mas: array of real;
Constructor Create(n: integer); overload;{разрешается наличие нескольких конструкторов}
Constructor Create; overload; {в таком случае слово overload обязательно}
Procedure Inp;
Function Sum :real;
Property Answer :real read Sum;
end;
clb = class(cla)
lim: real;
Constructor Create(n:integer; gr:real); overload;
Constructor Create; overload;
Function Kol :integer;
Property KolVo:integer read Kol;
end;
{методы класса cla }
Constructor cla.Create(n: Integer);
begin
inherited Create;
setlength(mas,n);
end;
Constructor cla.Create;
var k:integer;
begin
inherited Create;
Write('N='); readln(k);
setlength(mas,k);
end;
Procedure cla.Inp;
var i:integer;
begin
for I := 0 to length (mas)- 1 do
begin
write(i,' '); readln (mas[i]);
end;
end;
Function cla.Sum;
var i:integer;
s:real;
begin
s:=0;
for I := 0 to length(mas)- 1 do
s:= s+mas[i];
result:=s;
end;
{методы класса clb }
Constructor clb.Create(n: Integer; gr: real);
begin
inherited Create(n); {передача фактического параметра конструктору предка}
lim:=gr;
end;
Constructor clb.Create;
begin
inherited Create;
write('Border='); readln (lim);
end;
Function clb.Kol;
var i,k:integer;
begin
k:=0;
for I := 0 to length(mas) - 1 do
if mas[i]>lim then k:=k+1;
result:=k;
end;
{------------------}
var x:clb;
begin
x:=clb.Create(5,3.2); {создаем объект, используем первый конструктор}
x.Inp; {вызов метода}
writeln('Summa=', x.Answer:6:2); {использование свойства}
writeln('Count=', x.KolVo);
x.Free; {уничтожение объекта}
x:=clb.Create; {создаем объект, используем второй конструктор}
x.Inp;
writeln('Summa=', x.Answer:6:2);
writeln('Count=', x.KolVo);
readln;
end.
В нашем примере класс cla является базовым классом: он не имеет предков. Равнозначные термины: родительский класс, класс – предок. Класс clb является классом наследником (равнозначные термины: класс – потомок, дочерний класс). Как видно из примера, наследование поддерживается путем указания класса предка при объявлении класса. В Pascal класс может иметь только одного предка, но класс может иметь любое количество наследников.
Что означает наследование по существу?
Наследование означает, что класс наследник имеет в своем составе все поля класса – предка и может использовать его методы. В нашем случае класс clb имеет следующие данные: lim и массив mas. Из них mas получен по наследству из класса cla, lim добавлен.
Внимание! Повторное объявление в классе-наследнике данных класса-предка недопустимо! Повторное использование имен методов разрешено.
В Delphi предком всех создаваемых программистами классов является стандартный класс TObject. Укажем мы его или нет, не имеет никакого ни практического, ни теоретического значения. В составе TObject имеется и конструктор. Вот почему отсутствие в нашем собственном классе конструктора не является ошибкой. При создании объекта нашего класса в таком случае просто запускается конструктор TObject. Естественно, что стандартный конструктор не сможет выполнить ничего для определенного нами класса, он лишь выделит память и присвоит ее адрес указателю.
Факт наличия класса-предка предъявляет новые требования к структуре конструктора. Конструктор класса-наследника должен вызывать и конструктор класса предка. Это правило распространяется и на конструктор класса TObject. При наличии у конструктора класса-предка формальных параметров, конструктор класса-наследника должен выполнять их инициализацию. Как это делать – видно из приведенного примера.
Очевидно, что класс-наследник имеет в общем случае больше переменных, чем его класс-предок. Поэтому необходимо позаботиться о присвоении начальных значений не только переменным, объявленным в классе-наследнике, но и переменным, полученным по наследованию.
12.Статические и виртуальные методы на Delphi. Полиморфизм.
Delphi поддерживает четыре разновидностей методов:
∙•••••••• Статические.
∙•••••••• Методы сообщений.
∙•••••••• Виртуальные.
∙•••••••• Динамические.
До сих пор мы работали со статическими методами (по умолчанию все методы - статические). Статические методы связываются с их классами во время трансляции (раннее связывание) и к моменту запуска программы адреса всех методов известны и не могут быть пересмотрены во время выполнения.
Методы сообщений позволяют написать реакцию на сообщения (Message) операционной системы Windows.