Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лекции програм_new_последняя версия1.doc
Скачиваний:
14
Добавлен:
14.09.2019
Размер:
697.34 Кб
Скачать

Наследование конструкторов и деструкторов.

Наследование – это способность порожденного класса использовать характеристики класса-предка. Благодаря новым свойствам, которыми дополняется потомок, порожденный класс может обладать бỏльшими возможностями, чем его предок. Любой класс может быть порожден от другого класса, для этого при его описании указывается имя класса-родителя.

Потомок автоматически наследует поля, методы и свойства своего родителя и может дополнять их новыми.

Большинство конструкторов реализуют некоторые дополнительные действия, необходимые для правильной работы объекта, поэтому в конструкторе класса–потомка необходимо сначала вызвать конструктор родителя, а затем выполнять дополнительные действия.

Вызов конструктора родительского класса достигается с помощью слова Inherited (унаследованный):

constructor <имя класса-потомка>. create;

begin

inherited create;

{дополнительные действия}

End;

Аналогично можно в деструкторе класса-потомка вызвать деструктор класса-предка.

destructor <имя класса-потомка>. destroy;

begin

{дополнительные действия}

inherited destroy

end;

Слово inherited вызывает любой метод родительского класса, а не только конструктор и деструктор.

Например:

Type TPerson=class

name string;

constructor.create (x:string);

end;

Teacher=class (TPerson)

School: integer;

constructor.create (x:string,y:integer);

end;

constructor TPerson.create;

begin

name:=x;

end;

constructor Teacher.create;

begin

inherited.create (x); {вызывает конструктор родителя}

school:=y; {дополнительное действие – присвоение полю

School значения параметра y }

end;

var a: Teacher;

b: TPerson;

begin

a:= Teacher.create(‘Иванов’,131);

b:= TPerson.create(‘Руслан’);

. . . . .

a.free;

b.free;

. . . . .

End.

Свойства и инкапсуляция.

Инкапсуляция означает объединение данных и действий над ними в одном объектном типе.

Для обеспечения надежности нежелателен прямой доступ к полям объекта: чтение и обновление содержимого полей должно производиться посредством соответствующих методов – это называется инкапсуляцией.

В Delphi ограничение доступа к полям объекта осуществляется при помощи свойств объекта.

Определение: Свойства – это специальный механизм классов, регулирующий доступ к полям. Свойство определяется тремя элементами: полем и двумя методами, которые осуществляют его чтение и запись.

Свойства объявляются с помощью трех зарезервированных слов:

Property,

read,

write,

причем read и write считаются зарезервированными только в контексте объявления свойства.

Обычно свойство связано с некоторым полем, оно указывает те методы класса, которые должны исполняться при записи в это поле, и при чтении из него. Описание свойства:

Property <имя свойства>: <тип> read <имя метода 1>

write <имя метода 2>;

Методы чтения (вывода) и записи (ввода) всегда оформляются так:

Function <имя метода 1> : <тип>; { метод чтения}

Procedure <имя метода 2> (<параметр> : <тип>); {метод записи}

Например:

Type TInt=class

data:integer; // поле класса

Procedure impdata (x:integer);

Function Outdata:integer;

Procedure Add(x:integer);

Property dataInt:integer //свойство

read Outdata // метод чтения

write Impdata; // метод записи

end;

Доступ к значению свойства dataInt осуществляется через вызовы методов Outdata и Impdata. Однако в явном обращении к этим методам нет необходимости. Достаточно написать, например, для объекта a этого класса:

Var a:TInt; {объявляем a как объект класса TInt}

Begin . . . . . . . . . . . . .

a. dataInt:=<значение>;

{<значение> –это число, которое присваивается полю a }

Затем переменной <результату> присвоить:

<результат>:= a.dataInt;

Компилятор при этом оттранслирует эти операторы в вызовы методов Outdata и Impdata. Т.о. свойство выглядит как обычное поле, но за всяким обращением к нему стоят нужные действия.

Создадим класс целых чисел, реализующий арифметические операции над ними (например, сложение). Для программы используем консольное приложение. На примере создания класса целых чисел рассмотрим 2 случая:

  1. В описании класса нет свойства, но есть методы чтения (вывода) и записи (ввода) данных в поле объекта.

  2. В описании класса есть свойство.

Создание класса целых чисел без свойства:

Program Integer_class1;

{$APPTYPE CONSOLE}

uses SysUtils;

Type TInt=class

data:integer; // поле класса

Procedure Impdata (x:integer); //метод записи в поле

Function Outdata:integer; // метод чтения из поля

Procedure Add(x:integer); //метод сложения целых чисел

end;

Procedure TInt.Impdata; //описание метода записи Impdata

begin

data:=x; {записываем значение пар-ра x в поле data}

end;

Function TInt.Outdata; // описание метода чтения Outdata

begin

result:=data; {метод возвращает значение поля data}

end;

Procedure TInt.Add; {метод сложения поля data с пар-ром x }

begin

data:=data+x; {к полю data прибав. x, результат–в поле data}

end;

Var a:TInt; {объявляем a как объект класса TInt}

x:integer;

begin

{ TODO …………………………………………………}

a:=TInt.Create; {создаем объект a с помощью конструктора}

a.Impdata(5); {с помощью метода Impdata полю data присвоим 5}

writeln('data=', a.Outdata); {метод Outdata читает полученное значение поля data объекта a и выводит его на экран}

write('x=');

readln(x); {вводим x с клавиатуры }

a.Add(x);{объект a вызывает метод Add с парам. x, т.е. к полю data прибавляется x)}

writeln('data=', a.Outdata); {метод Outdata читает значение поля data объекта a }

a.Free; {уничтожаем объект a с помощью деструктора}

readln;

End.

Программа со свойством

program Integer_class2;

{$APPTYPE CONSOLE}

uses SysUtils;

Type TInt=class

data:integer;

Procedure impdata (x:integer);

Function Outdata:integer;

Procedure Add(x:integer);

Property dataInt:integer //свойство

read Outdata //чтение из поля

write Impdata; //запись в поле

end;

Procedure TInt.impdata;

begin

data:=x;

end;

Function TInt.Outdata;

begin

result:=data;

end;

Procedure TInt.Add;

begin

data:=data+x;

end;

Var a:TInt;

x:integer;

Begin

{ TODO……………………………… }

a:=TInt.Create; {создаем объект a с помощью конструктора}

(1) a.dataInt:=5;{св-во dataInt, вызванное объектом a, с помощью метода impdata заносит 5 в поле data}

writeln('data=', a.dataInt); { св-во dataInt, вызванное объектом a, с помощью метода Outdata выводит значение поля data}

write('x=');

readln(x);

a.Add(x);

writeln('data=', a.dataInt);

a.Free;

readln;

end.

Таким образом, использование методов чтения и записи, а также свойств обеспечивает сохранение содержимого полей, прямой доступ к полям не желателен, хотя и не запрещен.

Например, если в операторе присваивания вместо имени свойства a.dataint:=5; указать имя поля a.data:=5; то на первый взгляд делается то же самое. Однако разница заключается в том, что при обращении к свойству dataint автоматически подключается метод impdata, в котором реализуется запись в поле, а также могут быть другие действия.

Есть другие варианты определения свойств:

  1. В методах, входящих в состав свойств, может осуществляться проверка устанавливаемой величины на попадание в допустимый диапазон значений и вызов других процедур. Если же потребности в специальных методах чтения и записи нет, можно вместо имен методов применить имена полей. Рассмотрим следующую конструкцию:

Type TInt = class

data: integer;

Procedure DoSomething;

Procedure Imput (x: integer);

Property DataInt: integer read data {чтение поля}

write Imput;

end;

Procedure TInt.Imput (x:integer); //метод записи в поле

begin

if x <> data then data:=x;

DoSomething;

end;

В этом примере чтение значения свойства DataInt означает просто чтение поля data. Зато при присваивании значения полю внутри Imput вызывается метод DoSomething.

  1. Если необходимо чтобы поле было доступным или только для чтения, или только для записи, следует опустить (не писать) соответственно части read или write.

  2. Для присвоения свойству значения по умолчанию используется ключевое слово default. Например, для свойств со значением Boolean можно записать:

Property Visible: Boolean read GetV

write SetV default True;

  1. Свойство может быть векторным, тогда внешне оно выглядит как массив:

Property Elem [j:integer]: real read OutElem //квадратные скобки!

write ImpElem;

Для векторного свойства необходимо описать не только тип элементов массива, но также имя и тип индекса. После ключевых слов read, write должны стоять имена методов. Использование здесь полей массивов недопустимо. Метод, читающий значения векторного свойства, должен быть описан как функция, возвращающая значение того же типа, что и элементы свойства и имеющая единственный параметр того же типа и с тем же именем, что и индекс свойства:

Function OutElem (j:integer): Real;

Метод, помещающий значения в такое свойство, описывается как процедура и первым параметром имеет индекс, а вторым – переменную нужного типа (которая может быть как параметром–значением, так и параметром – переменной):

Procedure ImpElem (j:integer; r:real);

  1. Заметим, что у векторных свойств есть важная особенность: некоторые классы в Delphi «построены» вокруг основного векторного свойства. Основной метод такого класса дает доступ к некоторому массиву, а все остальные методы являются как бы вспомогательными. Специально для облегчения работы в этом случае оно может быть описано с ключевым словом default.

Отметим, что слово default для обычных и векторных свойств применяется с разным синтаксисом:

Type TVect = class

…………………………………………

Property Elem[j:integer]:Real read OutElem

write ImpElem; default;

end;

Если у объекта есть такое свойство, то можно в программе его не упоминать, а ставить индекс в квадратные скобки сразу после имени объекта.

Var x: TVect;

begin . . . .

x.Elem[1]:=0.1; {первый способ}

x[2]:=0.2; {второй способ}

. . . .

End.

Наследование и перекрытие методов.

Унаследованные от класса – предка поля и методы доступны в классе – потомке. Если имеет место совпадение имен методов, то, говорят, что они перекрываются. По тому, какие действия происходят при вызове, методы делятся на три группы:

1. Статические методы.

2. Виртуальные и динамические методы (Virtual and Dynamic).

3. Перегружаемые методы (Overload).