Заглавная страница Избранные статьи Случайная статья Познавательные статьи Новые добавления Обратная связь КАТЕГОРИИ: АрхеологияБиология Генетика География Информатика История Логика Маркетинг Математика Менеджмент Механика Педагогика Религия Социология Технологии Физика Философия Финансы Химия Экология ТОП 10 на сайте Приготовление дезинфицирующих растворов различной концентрацииТехника нижней прямой подачи мяча. Франко-прусская война (причины и последствия) Организация работы процедурного кабинета Смысловое и механическое запоминание, их место и роль в усвоении знаний Коммуникативные барьеры и пути их преодоления Обработка изделий медицинского назначения многократного применения Образцы текста публицистического стиля Четыре типа изменения баланса Задачи с ответами для Всероссийской олимпиады по праву Мы поможем в написании ваших работ! ЗНАЕТЕ ЛИ ВЫ?
Влияние общества на человека
Приготовление дезинфицирующих растворов различной концентрации Практические работы по географии для 6 класса Организация работы процедурного кабинета Изменения в неживой природе осенью Уборка процедурного кабинета Сольфеджио. Все правила по сольфеджио Балочные системы. Определение реакций опор и моментов защемления |
Основные механизмы и положения объектно - ориентированного программированияСтр 1 из 8Следующая ⇒
Основные механизмы и положения объектно - ориентированного программирования Объектно-ориентированное программирование является стандартом в технологии современного программирования и подавляющее большинство новейших средств разработки программных продуктов построены на основе ООП. Delphi не является исключением и обладает всеми необходимыми возможностями современного объектно-ориентированного языка программирования.
Инкапсуляция
5.1.1. Понятие класса и объекта Классом называется описание некоторой структуры программы, обладающей набором внутренних переменных — свойств, и функций (процедур), имеющих доступ к свойствам — методов. Процесс объединения переменных и методов, в результате которого и получается класс, называется инкапсуляцией. Итак, класс — это всего лишь описание, аналогичное описанию типа данных и недоступное для прямого использования в программе. Для получения доступа к свойствам и методам класса (за исключением методов класса, описанных ниже) необходимо создать экземпляр класса, называемый также объектом. Отметим различие в терминологии Turbo Pascal и Delphi. В Pascal объектом называлась структура, получаемая в результате инкапсуляции. В Delphi такие структуры называются классами, а объекты — это экземпляры классов. 5.1.2. Структура класса
Описание класса. Свойства и методы
Описание классов разделено на две части — интерфейсную («заголовочную») и описательную. В интерфейсной части располагается заголовок класса, в котором указываются название класса, идентифицирующее его в программе, а также описания свойств и заголовков методов. В описательной части располагается программный код (реализация) методов, заголовки которых указаны в интерфейсной части описания класса. Интерфейсная часть описания класса располагается в разделах описания типов данных модулей и основных частей программ. Выглядит она следующим образом: Туре ……………………. <Имя класса> = class {Заголовок описания} <Имя свойства 1>: <Тип свойства 1>; {Описание свойства 1] <Имя свойства N>: <Тип свойства N>; {Описание свойства N} <Заголовок метода 1>; {Описание метода 1} ……………………… <Заголовок метода М>; {Описание метода М}
End;
Описательная часть класса находится в разделе описания локальных подпрограмм. Методы, заявленные в интерфейсной части реализуются по обычным правилам описания процедур и функций. Для связи подпрограмм с классом, методами которого они являются, название класса указывается перед именем самой подпрограммы: Procedure <Имя класса>. <Имя метода> (<Список параметров>); или для методов-функций: Function <Имя класса>. <Имя метода> (<Список параметров>): <Тип значения>; Отличие методов от обычных процедур и функций состоит в том, что они могут обращаться к свойствам классов по имени без указания объектов. На этапе выполнения программы такие обращения будут перенаправлены к свойствам объектов, из которых вызываются соответствующие методы. В листинге 5.1 приведен пример описания модуля, содержащего описание класса.
Листинг 5.1. Описания модуля, содержащего описание класса Unit UsingClasses; Interface Type T с ar = class {Заголовок класса содержит его название Tсar} Mark: AnsiString; {Описание текстового свойства Mark для хранения марки автомобиля } EngineVolume: Double; {Описание вещественного свойства для хранения объема двигателя} Fuel: Double; {Описание вещественного свойства для хранения объема топлива} Function StartEngine: Boolean; {Заголовок функции-метода "Запустить двигатель"} Procedure StopEngine; {Заголовок процедуры-метода "Остановить двигатель"} Procedure AddFuel(NewFuel: Double); {Заголовок процедуры-метода "Заправить топливом"}
Implementation Begin Result:= (Fuel>0); end; {Описание метода StartEngine. В качестве результата возвращается значение логического выражения (Fuel>0)} Function TCar.StopEngine: Begin end; {Описание метода StopEngine, не выполняющего никаких действий} Procedure AddFuel (NewFuel: Double); {Описание метода AddFuel} Begin Fuel:= Fuel + NewFuel; {Увеличение значения свойства Fuel на величину, переданную в качестве параметра NewFuel при вызове метода} end; End. {Окончание модуля}
Обратим внимание на использование свойства Fuel в методе AddFuel. Метод AddFuel, который является частью описания класса TCar, обращается к данному свойству по имени, хотя на этапе создания класса и неизвестно, какому именно экземпляру будет принадлежать изменяемое свойство. Однако точно известно, что каждый экземпляр обладает свойством Fuel, причем объект, из которого вызван метод AddFuel, также может быть определен системой. При вызове метода AddFuelH3 какого-либо экземпляра класса тсаг в качестве изменяемого свойства Fuel автоматически будет выбрано то, которое принадлежит этому же экземпляру.
Возможность такого абстрактного подхода к описанию классов без указания их конкретных экземпляров и отличает объектно-ориентированный подход к созданию программы от модульного, несмотря на их некоторое сходство.
Методы класса
Интересным нововведением в Delphi являются так называемые методы класса, особенность которых состоит в том, что для их вызова нет необходимости создавать объект (экземпляр класса), а можно использовать их как обычные подпрограммы. Для описания таких методов используется ключевое слово class перед заголовком метода: Туре …………. <Имя класса> = class ……… class <Заголовок метода>; {Описание метода класса} End; При реализации методов класса в описательной части класса ключевое слово class также указывается: class <Имя класса>.<Заголовок метода>;
Методы класса применяются для создания библиотек специализированных методов, не требующих доступа к каким-либо данным, которые могут храниться в пределах объекта. В качестве примера приведем описание класса MthdClass, метод Get5 которого является методом класса (листинг 5.2).
Листинг 5.2. Пример описания методов класса
Interface Type MthdClass = class End; Implementation End.
Для вызова метода класса необходимо указать название класса и название метода со списком необходимых параметров. Название класса и его метода разделяются точкой: <Класс>.<Метод>(<Список параметров>); Использование метода класса отражено в следующем листинге 5.3.
Листинг 5.3. Использование метода класса Unit UsingClassMethods; Uses DeclaringClassMethods; {Подключаем модуль DeclaringClassMethods, в котором описан класс MthdClass} Interface Implementation Procedure UsingClass; {Описание процедуры UsingClass} Var A: Integer; {Описание целочисленной переменной А} Begin A:= MthdClass. Get 5; {Вызов метода класса по имени класса и метода, в переменную А заносится значение 5} End; End.
Отметим, что методы класса иногда называют статическими методами.
Перегружаемые методы
Перегружаемые методы аналогичны перегружаемым процедурам и имеют такой же механизм работы. Пример, возможного описания класса с перегружаемыми методами приведен в листинге 5.4. Листинг 5.4. Описание класса с перегружаемыми методами Туре AClass = class Function Mult(A, B: Integer): Integer; overload; {Заголовок метода Mult с двумя целочисленными параметрами, возвращающий целочисленное значение} Function Mult(A, В: Double): Double; overload; {Заголовок метода Mult с двумя вещественными параметрами, возвращающий вещественное значение} end; Function AClass.Mult(A, B: Integer): Integer; Begin Result:= A * B; end; {Описание первого варианта метода Mult} Function AClass.Mult(A, B: Double): Double;
Begin Result: = A * B; end; {Описание второго варианта метода Mult}
Области видимости элементов класса
Для разграничения доступа к свойствам и методам экземпляров классов между различными частями программы предусмотрены модификаторы доступа («видимости»), приведенные в табл. 5.1.
Модификатор доступа в Delphi (как и в Pascal, но в отличие от некоторых других языков программирования) относится не к конкретному свойству или методу класса, а ко всем элементам класса (свойствам и методам), описание которых располагается после указания модификатора. Один и тот же модификатор может указываться в описании класса более одного раза. Сравнительная таблица модификаторов доступа в Pascal и Delphi Таблица 5.1
Туре …………………….. <Имя класса> = class Private <Имя свойства 1>: <Тип свойства 1>; {Описание свойств класса, имеющих область видимости private} ……………….. <Имя свойства N>: <Тип свойства N>; <Заголовок метода 1>; {Описание методов класса, имеющих область видимости private} <3аголовок метода М>; Protected <Имя свойства 1>: <Тип свойства 1>; {Описание свойств класса, имеющих область видимости protected} ……………….. <Имя свойства N>: <Тип свойства N>; <Заголовок метода 1>; {Описание методов класса, имеющих область видимости protected} ……………….. <Заголовок метода М>; public <Имя свойства 1>: <Тип свойства 1>; {Описание свойств класса, имеющих-область видимости public} ……………….. <Имя свойства N>: <Тип свойства N>; <Заголовок метода 1>; {Описание методов класса, имеющих область видимости public} ……………….. <Заголовок метода М>; Published ......... {Описание специальных свойств класса (property), имеющих область видимости published} End; Различные области видимости свойств и методов объекта предназначены для упрощения поддержания целостности информации в объектах. Рассмотрим класс, в котором содержатся три свойства — а, b и с, причем с является произведением а и b. Если свойства а, b и с будут иметь широкую область видимости public, то есть будут доступны из любого фрагмента программы, то объект — экземпляр такого класса может содержать некорректные данные, так как существует возможность изменения свойств а и b без пересчета свойства с. Для выхода из такой ситуации можно предложить два подхода, и оба они связаны с использованием областей видимости свойств объекта.
Первый подход (см. листинг 5.5) состоит в том, чтобы скрыть свойства а и b от вызывающих подпрограмм, назначив им узкие области видимости, например private или protected. Выбор того или иного модификатора определяется необходимостью использования скрываемых свойств в классах-потомках данного класса. Если такой необходимости нет, то назначается область видимости private, если объекты-потомки должны иметь прямой доступ к свойствам, то назначается область видимости protected. В любом случае, внешние подпрограммы не смогут обратиться к свойствам а и b, поэтому придется предусмотреть методы, которые их устанавливают. В этих методах, помимо установки значений свойств, должен производиться пересчет свойства с. Это будет гарантировать целостность данных объекта при изменении свойств а и b. Заметим, что методы установки должны иметь широкую область видимости, чтобы к ним можно было обратиться из любого места программы.
Листинг 5.5. Задание областей видимости. Первый подход Unit UsingPrivatel; {Заголовок модуля} Interface {Начало интерфейсной части} Туре ABC = class {Заголовок класса ABC} private {Начало области видимости private) a, b: Double; (Свойства а и b имеют узкую область видимости} public {Начало области видимости public} с: Double; {Свойство с имеет широкую область видимости} Procedure SetA (NewA: Double); {Метод SetK имеет широкую область видимости} Procedure SetB (NewB: Double); {Метод SetB имеет широкую область видимости} End; Implementation {Начало описательной части} Procedure ABC.SetA(NewA: Double); {Описание метода SetA класса А В С} Begin а: = NewA; {Установка значения свойства а.] с:= а * Ь; {Обновление значения свойства c- поддержание целостности данных} end; Procedure ABC.SetB(NewB: Double); {Описание метода SetB класса ABC Begin b:= NewB; {Установка значения свойства b} с:= a * b; {Обновление значения свойства с - поддержание целостности данных} End; end. {Окончание модуля}
При такой конструкции класса вызывающая подпрограмма не сможет изменить значения свойств а и Ь, не повлияв тем самым на значение свойства с, поэтому целостность данных не будет нарушена. Однако свойство с доступно для изменения, даже если его значение пересчитано при установке значений а и Ь. В итоге оно может измениться, вызвав несоответствие данных. Таким образом, лучше было бы скрыть свойство с, реализовав специальный метод доступа к его значению (листинг 5.6).
Листинг 5.6. Задание областей видимости. Второй подход Unit UsingPrivate2; {Заголовок модуля}
Interface {Начало интерфейсной части}
Type ABC 2 = class {Заголовок класса ABC} Private {Начало области видимости private} с: Double; {Свойство с имеет узкую область видимости}
Public {Начало области видимости public} a, b: Double; {Свойства а и b имеют широкую область видимости} Function GetC: Double; {Метод GetC имеет широкую область видимости} end;
Implementation {Начало описательной части} Function ABC2.GetC:Double; {Описание метода GetC класса АВС2} Begin с:= а* b; {Обновляем значение свойства с} Result:= с { Возвращаем значение с вызывающей подпрограмме} end; end. {Окончание модуля}
При такой конструкции класса целостность данных не может быть нарушена вызывающими подпрограммами, если только они не находятся в одном модуле с описываемым классом, то есть, в данном случае, в модуле UsingPrivate 2. Единственным недостатком такого подхода является постоянный пересчет значения с при каждом обращении к методу GetC. Поэтому наиболее оптимальным вариантом решения нашей задачи будет сокрытие всех свойств класса и реализация методов доступа к ним через методы (см. листинг 5.7).
Листинг 5.7. Задание областей видимости. Третий подход (оптимальный) unit UsingPrivate 3; {Заголовок модуля} Interface Type АВСЗ = class private a, b, c: Double; {Все свойства имеют узкую область видимости} public Procedure SetA(NewA: Double); {Все методы имеют широкую область видимости} Implementation Procedure ABC3.SetA(NewA: Double); {Описание метода SetA класса АВСЗ} Begin a: = NewA; с:= a * b; end; Procedure АВСЗ.SetB(NewB: Double); {Описание метода SetB класса АВСЗ} Begin b:= NewB; с:=. a * b; end; Function ABC3.GetC: Double; {Описание метода GetC класса АВСЗ} Begin Result:= с; {Просто возвращаем значение с} end;
End.
Property-свойства
Свойства property аналогичны свойствам объекта в смысле их использования. Однако внутренний механизм их работы намного более сложен. Такие свойства не являются простым отображением памяти, доступным для чтения и изменения, а подразумевают вызов методов объекта. Подробнее на свойствах property мы остановимся при рассмотрении создания компонентов, а пока просто приведем сокращенный формат описания таких свойств с краткими пояснениями.
Итак, описание свойств property в простейшем виде выглядит следующим образом: property <Имя свойства>: <Тип> read <Функция чтения значения> write <Процедура установки значения>;
Когда вызывающая подпрограмма обращается к свойству property для получения его значения, вместо конкретного значения возвращается результат функции, указанной по имени после модификатора read. Аналогично производится установка значения свойства — вместо прямой записи значения вызывается процедура, указанная после модификатора write. Соответственно, данная процедура должна принимать один параметр, причем его тип должен быть таким же, как тип самого свойства. Модификаторы read и write могут не присутствовать в описании property-свойства одновременно. Если для свойства задана функция чтения, но не задана процедура записи, то такое свойство может быть использовано только для получения значения. Если задана процедура установки значения, но не задана функция чтения, то property-свойство можно использовать только в левой части оператора присваивания. В рассматриваемом нами примере свойства а и b должны быть доступны вызывающему фрагменту и для чтения и для записи, тогда как свойство с может быть доступно только для чтения. Пример описания и использования property-свойства для поддержания корректности данных приведен в листинге 5.8. Листинг 5.8. Пример описания и использования property -свойства Unit Properties; {Заголовок модуля} Interface Type ABC4 = class Private fa, fb, fc: Double; {Все свойства имеют узкую область видимости} protected {Все методы имеют область видимости protected} Procedure SetA (NewA: Double); {Процедура установки значения свойства fa} Procedure SetB (NewB: Double); {Процедура установки значения свойства fb}
Function GetA: Double; {Функция получения значения свойства fa} Function GetB: Double; {Функция получения значения свойства fb} Function GetC: Double; {Функция получения значения свойства fc} Published {Раздел описания property-свойств, доступных вызывающему фрагменту программы} property A:Double read GetA write SetA; {Описание property-свойства А, для которого задана функция чтения GetA и процедура установки SetA. Свойство будет доступно вызывающему фрагменту программы для чтения и записи} property B: Double read GetB write SetB; {Описание property-свойства В, для которого задана функция чтения GetB и процедура установки SetB. Свойство будет доступно вызывающему фрагменту программы для чтения и записи} Implementation Procedure ABC4.SetA(NewA: Double); {Описание метода SetA} Begin fa:= NewA; {Занесение нового значения в private-свойство fa} fc:= fa * fb; {Пересчет произведения и занесение результата в свойство fc}. end; Procedure ABC4.SetB(NewB: Double); {Описание метода SetB} Begin fb:= NewB; {Занесение нового значения в private-свойство fb} fc:= fa * fb; {Пересчет произведения и занесение результата в свойство fc} end; Function ABC4.GetA: Double; {Описание метода GetA} Begin Result:= fa; {Результат функции — значение private-свойства fa} end; Function ABC4.GetB: Double; {Описание метода GetB} Begin Result:= fb; {Результат функции — значение private-свойства fb} end; Function ABC4.GetC: Double; {Описание метода GetC} Begin Result:= fc; {Результат функции — значение private-свойства fc} end; end. {Окончание модуля} Таким образом, property-свойства предоставляют возможность переноса информационных свойств в области класса с узкой видимостью, и обеспечивают доступ к их значениям через специальные методы, но в значительно более удобной форме. Для вызывающего фрагмента программ не имеет значения, к обычному свойству он обращается, или к property-свойству, тогда как класс имеет возможность подержания внутренней целостности. Заметим, что вместо функции чтения и процедуры установки может, быть указано одно из обычных свойств, вне зависимости от его области видимости. В рассмотренном примере такую возможность следует применить при описании property-свойств а, b и с, указав вместо функций чтения свойства fa, fb и fc, соответственно. Модифицированный текст модуля приведен в листинге 5.9. Листинг 5.9. Пример описания и использования property-свойства (модифицированный) Unit Properties; {Заголовок модуля} Interface Type ABC4 = class Private fa, fb, fc: Double; {Все свойства имеют узкую область видимости} protected {Все методы имеют область видимости protected} Procedure SetA (NewA: Double); {Процедура установки значения свойства fa} Procedure SetB (NewB: Double); {Процедура установки значения свойства fb} Published {Раздел описания property-свойств, доступных вызывающему фрагменту программы} property A: Double read fa write SetA; {Для установки значения property-свойства используется метод SetA, а для чтения private-свойство fa} Implementation Procedure ABC4.SetA(NewA: Double); {Описание метода SetA} Begin fa:= NewA; fc:= fa * fb; end; Procedure ABC4.SetB(NewB: Double); {Описание метода SetB} Begin fb:= NewB; fc:= fa * fb; End; end. {Окончание модуля} Такой подход также не дает возможности нарушить целостность данных вызывающим фрагментом, так как не обеспечивается прямой доступ к установке значений свойств fa, fb и fc. Наследование
5.2.1. Основы наследования Один из основных механизмов объектно-ориентированного программирования — наследование — построение нового класса на основе ранее описанного класса. Полученные в результате наследования классы называются классами-наследниками (или дочерними классами), а классы на основе которых они построены — классами-родителями (или родительскими классами). При наследовании дочерний класс приобретает все свойства и методы родительского класса и имеет доступ к любому его элементу, за исключением описанных с областью видимости private. При описании дочернего класса с использованием наследования имя родительского класса указывается в скобках после ключевого слова с1ass| в заголовке интерфейсной части описываемого класса: Туре <Имя класса> = class (<Имя родительского класса>) {Заголовок описания} ......... {Описание собственных свойств и методов } End; Класс-наследник может быть описан на основе любого другого класса, вне зависимости от того, является ли родительский класс в свою очередь дочерним. Более того, все классы в Delphi являются наследниками от класса TObject, даже если это явно не указывается. Класс TObject инкапсулирует основные методы и свойства, связанные с общим поведением объектов. К ним относятся такие как создание, инициализация и разрушение объектов; поддержка идентификации их принадлежности к классу во время выполнения программы и многие другие, связанные с интеграцией объектов, созданных в Delphi, в среду выполнения программы, то есть операционную систему. Таким образом, экземпляры любого класса в Delphi автоматически приобретают все эти возможности.
5.2.2. Переопределение методов Часто встречается ситуация, когда один или несколько методов дочернего класса должны работать не так, как в родительском классе. В качестве примера можно привести класс TGeomFigure, представляющий собой абстрактную геометрическую фигуру, и имеющий метод Draw для ее рисования. Наследниками этого класса могли бы быть классы TCircle для работы с окружностями, TLine для работы с линиями, и так далее. Очевидно, что все дочерние классы в такой ситуации должны изменить метод Draw, чтобы корректно отображать фигуры, которым они соответствуют. Изменение функциональности унаследованных методов в дочерних классах называется переопределением методов. Механизм переопределения
Для переопределения метода, реализованного в объекте-родителе, следует:
♦ указать его заголовок в интерфейсной части описания дочернего класса без изменения имени, списка параметров и возвращаемого значения (если метод является функцией); ♦ указать после заголовка метода ключевое слово override. Если ключевое слово override не указано, то метод не переопределяется. ♦ реализовать метод (создать программный код) в описательной части объекта по обычным правилам. При этом в заголовке метода ключевое слово override не указывается.
Чтобы метод мог быть переопределен в дочерних классах, он должен быть помечен ключевыми словами virtual или dynamic в интерфейсной части класса-родителя. Ключевое слово virtual указывает на то, что метод должен быть занесен в так называемую таблицу виртуальных методов ТВМ), а ключевое слово dynamic на то, что метод должен быть найден ло имени в дереве родительских объектов. Разница между использованием virtual и dynamic заключается в направлении оптимизации компилятором вызовов переопределяемых методов. Методы, помеченные virtual, оптимизируются по скорости, а zynamic-методы по размеру программного кода. В большинстве случаев рекомендуется использование виртуальных методов, а использование динамических методов целесообразно при высоких степенях вложенности cвязей родитель-наследник. Приведем описание класса TwoNums с двумя свойствами а и Ь, и методом GetResult, возвращающим сумму свойств. Далее, от этого класса эпишем наследника ThreeNums, имеющего уже три свойства — а, b и с, и переопределяющего метод GetResult таким образом, чтобы возвращать сумму не двух, а трех чисел (листинг 5.10). Листинг 5.10. Использование переопределения Unit Overriding 1; Interface Type TwoNums = class public {Заголовок класса TwoNums} a, b: Integer; {Описание двух свойств} function GetResult: Integer; virtual; {Описание заголовка метода; после описания указано ключевое слово virtual, то есть этот метод может быть переопределен в дочернем классе} End; ThreeNums = class (TwoNums) Public {Заголовок класса ThreeNums, в скобках после ключевого слова class указан класс-родитель} с: Integer; {Описание свойства с'. Свойства а и b наследуются от класса-родителя TwoNums} function GetResult: Integer; override; {Описание заголовка метода, идентичного заголовку объекта-родителя; после описания указано ключевое слово override, указывающее на переопределение функциональности родительского метода} End; Implementation function TwoNums.GetResult: Integer; {Описание метода GetResult класса TwoNums} Begin Result:= a + b; {Результат функции — сумма двух свойств} End; function ThreeNums. GetResult: Integer; {Описание переопределенного метода GetResult класса ThreeNums} Begin Result:= a + b + c; {Результат функции — сумма трех свойств} end; End.
Переопределение методов с сохранением функциональности
В приведенном примере метод GetResult класса TwoNums полностью переопределен в классе ThreeNums, то есть его изначальная функциональность полностью утеряна, но включена в функциональность замещающего метода с помощью копирования. Это практически всегда возможно, если разработчик класса имеет доступ к исходному тексту класса-родителя, но не всегда удобно, так как программный код, реализующий метод, может иметь немалые размеры. Для сохранения функциональности переопределенного метода в Delphi имеется возможность его вызова из переопределяющего метода с помощью ключевого слова inherited, используемого следующим образом: Inherited <Название метода>(<список параметров>); Аналогично могут вызываться и переопределенные методы, которые являютсяфункциями: ….:= Inherited <Название метода>(<Список параметров>); Изменим предыдущий пример таким образом, чтобы сумма свойств в методе GetResult вычислялась с использованием переопределенного вариантаэтого метода, описанного в классе TwoNums (листинг 5.11).
Листинг 5.11. Переопределение методов с сохранением функциональности Unit 0verriding2; Interface Type TwoNums = class public a, b: Integer; function GetResult: Integer; virtual; End; ThreeNums = class (TwoNums) public c: Integer; function GetResult: Integer; override; End; Implementation Begin Result: = Inherited GetResult + c; {Результат функции — сумма значения, выдаваемого переопределенным методом (сумма а и Ь), и свойства с} end; End. Var <Название переменной>: <Название класса>; Такая переменная автоматически рассматривается как ссылка на объект, и дополнительное указание компилятору на динамическую природу переменной не требуется. Листинг 5.12. Описание и вызов конструктора unit Geoml; Interface Type TGeomFigure = class {Заголовок класса TGeomFigure, класс автоматически является наследником класса TObject} End; Implementation Procedure CreateFigure; Var Figure: TGeomFigure;{Описание переменной — ссылки на экземпляр класса. Несмотря на то, что не указан модификатор ^,переменная Figure является переменной-указателем} B egin Figure:= TGeomFigure. Create;{Вызов конструктора класса TGeomFigure, унаследованный от класса TObject, и сохранение ссылки на новый объект в переменной Figure} end; END.
Переопределение конструктора
Конструктор может быть переопределен в описании класса для выполнения дополнительной инициализации, характерной для экземпляров именно этого класса. Например, в конструкторе можно установить начальные значения свойств. Переопределение конструктора выполняется по обычным правилам переопределения методов, за исключением следующих моментов: 1.Конструктор родительского класса не обязательно должен быть помечен ключевым словом virtual, для того, чтобы его переопределить. Соответственно, если конструктор родительского класса не помечен как виртуальный или динамический, не требуется указание ключевого слова override в описании дочернего класса. Виртуальные конструкторы необходимы для работы с типами данных, являющимися ссылками на классы (а не на объекты). Такая ссылка используется, например, в конструкторе класса TCollection, предназначенного в Delphi для хранения динамических списков. 2. Существенным отличием в переопределении конструкторов и обычных методов, является возможность изменения параметров конструктора дочернего класса относительно конструктора родительского класса. Однако это возможно только в том случае, если конструктор не является виртуальным. Заметим, что полное переопределение конструктора обычно недопустимо, так как конструктор, определенный в родительском классе, от которого наследуется дочерний класс, может выполнять некоторые действия, без которых невозможна корректная работа объекта. Для вызова версии конструктора, замененной в классе-наследнике, используется ключевое слово Inherited: Inherited; Таким образом, любой переопределенный конструктор может и (обычно) должен сначала вызвать ту версию конструктора, которую он переопределяет. Если в классе-наследнике изменен список параметров родительского конструктора (например, список расширен), конструктор родительского класса вызывается с указанием его названия и передачей ему необходимых параметров: Inherited Create (<Список параметров>); Рассмотрим пример создания объекта с переопределением конструктора (листинг 5.13). Листинг 5.13. Пример создания объекта с переопределением конструктора unit Geom2; Interface Type TGeomFigure = class {Заголовок класса TgeomFigure, класс автоматически является наследником класса Tobject} Protected Color: Integer; {Описание свойства с областью видимости protected) Public Constructor Create(aColor: Integer); {Заголовок конструктора (область видимости public), список параметров изменен по отношению к родительскому классу TObject, конструктор которого не имеет параметров} End; Implementation Constructor TGeomFigure. Create; {Заголовок описательной части конструктора} Begin Inherited Create; {Вызов конструктора родительского класса} Color:= aColor; {Инициализация свойства, которая не может быть выполнена в родительском классе, так как свойство Color описано в классе TGeomFigure} end; End.
5.3.2. Использование экземпляра класса Использование объекта заключается в вызове его методов и обращении к его свойствам аналогично работе с обычными переменными и подпрограммами, но с указанием переменной-объекта, к которому относятся эти свойства и методы: <Объект>.<Свойство> ИЛИ <Объект>.<Метод>(<Список параметров>); Пример использования экземпляра класса показан в листинге 5.14. Листинг 5.14. Пример использования экземпляра класса unit Geom 3; Interface Type TGeomFigure = class Protected Color: Integer; public Implementation ......... {Описание конструктора} ......... {Описание метода Draw} Procedure UsingFigure; {Описание процедуры, использующей объект класса TGeomFigure} Var Figure: TGeomFigure; begin Figure:= TGeomFigure.Create(3); {Создание экземпляра класса TGeomFigure} F |
| Поделиться: |
Последнее изменение этой страницы: 2021-04-04; просмотров: 49; Нарушение авторского права страницы; Мы поможем в написании вашей работы!
infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 18.222.80.122 (0.351 с.)