Партнерка на США и Канаду по недвижимости, выплаты в крипто
- 30% recurring commission
- Выплаты в USDT
- Вывод каждую неделю
- Комиссия до 5 лет за каждого referral
18 Наследование
*Из прошлого семестра: С помощью наследования можно строить новые классы на основании существующих, вместо того, чтобы переделывать что-то на пустом месте. Когда необходим измененный объект, создается потомок уже существующего объекта, в котором добавляются поля и методы к уже имеющемуся объекту. При этом не корректируется исходный код, что могло бы привести к возникновению ошибок. Проверяются и отлаживаются только новые методы. Отсюда следует более высокая защищенность ооп-программ. Защищённые поля и инкапсуляция. Создадим на базе класса TDate новый класс, в составе которого будет присутствовать измененная версия функции GetText. Код метода GetText класса TNewDate будет откомпилирован только в том случае, если мы напишем его в том же модуле, где и класс TDate. Это происходит по следующим причинам. В этом методе мы будем обращаться к полю FDate, унаследованному от предка и описанному в private. Если же мы поместим определение нового класса в новый модуль, то для работы метода мы будем вынуждены объявить поле FDate как protected в классе TDate. Описание большинства полей как protected делает классы более гибкими и упрощает написание подклассов. Но это разрушает идею инкапсуляции. Гибкость и инкапсуляция зачастую являются прямопротивоположными целями. Если приходится выбирать – предпочтение надо отдавать инкапсуляции. Иначе программа не будет соответствовать принципам ООП. (Однако не стоит забывать, что модульность это тоже инкапсуляция). Использование инкапсуляции с достаточной гибкостью позволяет обеспечить механизм позднего связывания. Для использования механизма наследования в Delphi необходимо в объявлении класса справа от слова class указать класс предок:{ TAncestor = class private protected public procedure VirtualProcedure; virtual; abstract; procedure StaticProcedure; end; TDescendant = class(TAncestor) private protected public procedure VirtualProcedure; override; procedure StaticProcedure; end;}Абсолютно все классы в Delphi являются потомками класса TObject. Если класс-предок не указан, то подразумевается, что новый класс является прямым потомком класса TObject.
19 Совместимость типов
*Из прошлого семестра:
В ТП подкласс наследует совместимость типов со своим суперклассом. Это расширенная совместимость действует между: - экземплярами объектов; - указателями на экземпляры объектов; - формальными и фактическими параметрами. Т. е. подклассы могут свободно использоваться вместо суперклассов, но не наоборот. Производные типы посредством наследования содержат всё то, что имеют прародительские типы, следовательно, потомок имеет либо такой же размер либо несколько больший, но никогда не меньший. Присвоение же предка потомку приведет к тому, что некоторые из полей потомка останутся неопределенными, что опасно и недопустимо. Наследоваие и совместимость типов. Расширенная совместимость типов позволяет использовать объект класса потомка всюду, где ожидается объект класса предка. Обратное неверно.
20 Полиморфизм и позднее связывание
Статическое (раннее) связывание означает, что адрес, по которому в памяти расположен исполнительный код метода, определяется на стадии компиляции и компоновки. При этом в точке исполняемого кода, где требуется обратиться к процедуре или функции, подставляется конкретный адрес, по которому расположена эта процедура или функция. ОО языки программирования, а в том числе и OP, поддерживают динамическое (позднее) связывание. В этом случае фактический адрес метода, которому требуется предоставить управление, определяется во время работы программы и зависит от класса объекта, используемого при осуществлении вызова. Благодаря позднему связыванию реализуется полиморфизм. *Из прошлого семестра: Виртуальные методы реализуют очень мощное средство – полиморфизм – термин теории типизации, обозначающий возможность использовать одно имя для объектов различных классов, имеющих общий суперкласс. В результате объект с таким именем может по-разному реагировать на выполнение общих наборов операций. Полиморфизм заключается в том, что одно и то же имя может соответствовать различным действиям в зависимости от типа объекта. Полиморфизм напрямую связан с механизмом позднего связывания. Решение о том, какая операция должна быть выполнена в конкретной ситуации, принимается во время выполнения программы. Полиморфизм – способность объектов менять своё поведение в зависимости от того, к какому классу они принадлежат. Например, имеется переменная, которая может ссылаться на объекты разных классов. Для этих классов один и тот же метод может быть определен по-разному. В исходный код программы можно поместить обращение к этому методу и он должен быть выполнен в описании того объекта, на который в данный момент ссылается переменная. Фактический класс объекта, на который ссылается переменная можно определить только во время выполнения программы. Полиморфизм является ключевой техникой (причиной), по которой ООЯП располагают повторным использованием кода, т. е. можно написать код с использованием кода, входящего в состав других классов иерархии, при этом не имея представления о конкретных классах, входящих в эту иерархию. Полиморфизм позволяет писать более абстрактные программы и повысить коэффициент повторного использования кода. Полиморфизм реализуется с помощью наследования классов и виртуальных функций. Класс-потомок наследует сигнатуры методов класса-родителя, но реализация этих методов может быть другой, соответствующей специфике класса-потомка. Это называется переопределением метода. Другие функции могут работать с объектом класса-родителя, при этом вместо него во время исполнения будет подставляться один из классов-потомков. Это называется поздним связыванием. Класс-потомок сам может быть родителем. {tуре TPerson = class
fname: string; constructor Create(name:string); function info: string; virtual;
end; TStud = class(TPerson) fgr:integer; constructor Create(name:string;gr:integer); function info: string; override; end; TProf = class(TPerson) fdep:string; constructor Create(name:string;dep:string); function info: string; override; end; function TPerson. info:string; … end; function TStud. info:string; … end; function TProf. info:string; … end; …. list: array[l..SZL] of TPerson; …… st := ''; for i:=l to SZL do if list[i] <> NIL then st := st + list[i].Info + #13; ShowMessage (st);}
21 Виртуальные, динамические методы.
В Delphi поддерживаются два различных варианта реализации позднего связывания. 1. Можно определить метод с помощью ключевого слова virtual. 2. Можно определить метод с помощью ключевого слова dynamic. Синтаксис один, семантически это одно и то же, результат тот же самый. Различие состоит во внутреннем механизме, используемом компонентами для реализации позднего связывания. Обращение к любому виртуальному методу происходит через таблицу VMT, которая представляет собой массив адресов методов. Каждый класс обладает своей собственной таблицей, где хранятся адреса виртуальных методов этого класса. Чтобы передать управление некоторому виртуальному методу, компилятор генерирует код, выполняет переход по адресу, указанному в ячейке, соответствующей указанному методу. Т. е. VMT обеспечивает быстрое выполнение вызовов. Недостатком является то, что требуется отдельной ячейки для каждого виртуального метода каждого дочернего класса, даже если этот метод не был перекрыт ни в одном из дочерних классов. Для больших иерархий требуются большие объемы памяти. Для осуществления вызова динамического метода, используется специальный уникальный номер, с помощью которого метод идентифицируется. Поиск адреса соответствующей функции или процедуры, как правило, осуществляется медленнее, чем по таблице. Применение динамического метода заключается в том, что информация о методах копируется в дочерний класс только в том случае, если метод был перекрыт в этом классе. Для глубоких иерархий использование такого метода может привести к существенной экономии памяти. Пусть определены три класса, один из которых является базовым для двух других: {Tуре TPerson = class fname: string; // имя constructor Create(name:string); function info: string; virtual; end; TStud = class(TPerson) fgr:integer; // номер учебной труппы constructor Create(name:string;gr:integer); function info: string; override; end; TPerson TProf = class(TPerson) fdep:string; // название кафедры constructor Create(name:string;dep:string); function info: string; override; end;}
22 Абстрактные методы
Ключевое слово abstract используется для объявления методов, которые будут определены только в наследниках тех классов, т. е. указывают на то, что класс содержит только объявление метода, но не содержит определения. Директива abstract полностью определяет метод, т. е. при попытке добавить в код реализацию этого метода, компилятор выдаст ошибку. OP позволяет сделать экземпляр класса, содержащего абстрактные методы, однако при этом будет выдано соответствующее предупреждение. Благодаря использованию абстрактных методов упрощается реализация полиморфизма. Например, если класс TAnimal имеет абстрактный метод Voice, то каждый потомок класса может использовать общий объект класса TAnimal, и при этом можно обратиться к методу Voice объекта MyAnimal, вне зависимости от того, к какому подклассу он фактически принадлежит этот объект. Если бы в классе TAnimal не было абстрактного метода Voice, вызов было бы осуществить невозможно.
23 Безопасное преобразование типов
Правила OP о совместимости типов позволяют использовать класс потомка там, где ожидается использование класса предка. Пример. TAnimal = class private kind: string; public constructor Create(k : string); function Say: string; virtual; abstract; end; TDog = class(TAnimal) public function Say: string; override; function Eat: string; end; TCat = class(TAnimal) public function Say: string; override; function Eat: string; end; В нашем примере в основном коде программы мы везде писали класс TAnimal, т. е. ожидается его использование, но используем его потомков. Если мы попытаемся вызвать метод Eat, то можем использовать механизм прямого преобразования типов. Но, если оказывается, что переменная указывает на объект другого класса, возникает ошибка времени выполнения: TDog(MyAnimal).Eat – т. к. компилятор не может определить, является тип корректным. Остается использовать механизм RTTI. Каждый объект знает свой тип и тип своего родителя. К этой информации можно обратиться, используя операцию is или метод InheritsFrom. Параметрами операции is является тип класса, а взовращаемое значение – boolean. Т. е. выражение истинно, если объект MyAnimal ссылается в данный момент на объект класса TDog или его потомка. После того, как мы уверены, что наша пременная совместима с классом TDog, то в дальнейшем можем использовать прямое преобразование типов. Все эти действия выполняются в комплексе с другой операцией RTTI, которая обозначается as. Эта операция преобразовывает объект только в том случае, если тип запрашиваемого класса совместим с текущим. Параметрами операции as является объект и тип класса, а результатом является объект, приведенный к этому типу. MyDog:= MyAnimal as TDog; Text:= MyDog. Eat; или, например, (MyAnimal as TDog).Eat. Операция as вызывает исключительную ситуацию, если тип объекта не совместим с типом класса, к которому мы собираемся его привести. Чтобы избежать возникновения этой ситуации, нужно использовать операцию is, и, если она даёт результат true, можно выполнить прямое преобразование типов. (Нет необходимости использовать as и is последовательно, дважды выполняя проверку). Операции is и as – чрезвычайно мощные, однако их применение следует ограничить несколькими специальными случаями. При необходимости решить сложную задачу, связанную с несколькими классами, надо попытаться использовать полиморфизм, и только потом, если необходимо, дополнить его операциями RTTI. Обратное – плохой стиль программирования и снижает производительность программы, так как для проверки корректности классов необходимо проходить по всей иерархии классов, а для обращения к виртуальному методу требуется просто прочитать его адрес из памяти.
|
Из за большого объема этот материал размещен на нескольких страницах:
1 2 3 4 5 6 |


