Абстрактный виртуальный метод 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Абстрактный виртуальный метод



При построении иерархии классов часто возникает ситуация, когда работа виртуального метода в базовом классе не известна и наполняется содержанием только в наследниках. Конечно, тело метода всегда можно сделать пустым или почти пустым, но лучше воспользоваться директивой abstract:

 

function ParseLine(constLine: string): Integer; virtual; abstract;

 

Директива abstract записывается после слова virtual и исключает необходимость написания кода виртуального метода для данного класса. Такой метод называется абстрактным, т.е. подразумевает логическое действие, а не конкретный способ его реализации. Абстрактные виртуальные методы часто используются при создании классов-полуфабрикатов. Свою реализацию такие методы получают в законченных наследниках.

Delphi:

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

В последних версиях Delphi также может быть объявлен абстрактным сам класс:

TAbstractClass = class abstract

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


C++:

Абстрактный класс объявляется включением хотя бы одной чистой виртуальной функции

virtual void Abstr (void) = 0; // Чистая (пустая) виртуальная функция. void fun (void) { std::cout << "Реализация не будет наследоваться!"; } ~CA () { std::cout << "." << std::endl; } //Вызывается в обр. порядке class CB: public CA { public: CB (void) { std::cout << "CB;"; } void Abstr (void){ std::cout << " call function cb.Abstr();"; } //Подменяющая функция. void fun (void){ std::cout << " call function cb.fun()"; } ~CB () {} // Неверно для абстр. кл. ~CB(){ ~CA(); } };

C#:

Модификатор abstract указывает на то, что класс может быть использован только как базовый класс при наследовании. Абстрактные классы могут содержать абстрактные методы и методы доступа. Создавать экземпляры абстрактного класса нельзя через вызов конструктора, но экземпляр абстрактного класса создается неявно при построении экземпляра производного конкретного класса. Неабстрактный класс, являющийся производным от абстрактного, должен содержать фактические реализации всех наследуемых абстрактных методов и методов доступа. Чтобы указать отсутствие реализации в методе или свойстве, воспользуйтесь модификатором abstract в объявлении метода или свойства. Абстрактный метод - это неявный виртуальный метод. Объявления абстрактных членов (методов, свойств, событий) допускаются только в абстрактных классах и интерфейсах. Поскольку объявление абстрактного метода не предоставляет фактической реализации, тело метода отсутствует, объявление метода просто заканчивается точкой с запятой, аналогично объявлению прототипов:

public abstract void AbstractMethod();

Реализация предоставляется методом переопределения override, который является членом неабстрактного класса.

Использование статических или виртуальных модификаторов в объявлении абстрактного метода или свойства является недопустимым. Действие абстрактных свойств аналогично абстрактным методам, за исключением отличий в синтаксисе объявлений и вызовов. Абстрактное унаследованное свойство может быть переопределено в производном классе за счет включения объявления свойства, использующего модификатор переопределения.

abstract class BaseClass // Abstract class { protected int _x = 100; public abstract void AbstractMethod(); // Abstract method public abstract int X { get; } } DerivedClass: BaseClass { public override void AbstractMethod() { _x++; } public override int X // overriding property { get { return _x + 10; } } static void Main() { DerivedClass o = new DerivedClass(); o.AbstractMethod(); Console.WriteLine("x = {0}", o.X); } }

Абстрактный класс с модификатором sealed изменить нельзя, поскольку эти два модификатора имеют взаимоисключающие значения. Модификатор sealed запрещает наследовать классу, в то время как модификатор abstract указывает, что класс обязан иметь производные классы.

Динамические методы

Разновидностью виртуальных методов являются так называемые динамические методы. При их объявлении вместо ключевого слова virtual записывается ключевое слово dynamic.

 

function ParseLine(constLine: string): Integer; dynamic;

 

В наследниках динамические методы перекрываются так же, как и виртуальные – с помощью зарезервированного слова override.

 

По смыслу динамические и виртуальные методы идентичны. Различие состоит только в механизме их вызова.

 

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

 

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

Каждому динамическому методу системой присваивается уникальный индекс. В таблице динамических методов класса хранятся индексы и адреса только тех динамических методов, которые описаны в данном классе. При вызове динамического метода происходит поиск в этой таблице; в случае неудачи просматриваются таблицы DMT всех классов-предков в порядке иерархии и, наконец, класс TObject, где имеется стандартный обработчик вызова динамических методов.


 

Методы обработки сообщений

 

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

 

procedure CMKeyDown(varMsg: TCMKeyDown); message CM_KEYDOWN;

 

Метод обработки сообщений имеет формат процедуры и содержит единственный var-параметр. При перекрытии такого метода название метода и имя параметра могут быть любыми, важно лишь, чтобы неизменным остался номер сообщения, используемый для вызова метода. Вызов метода выполняется не по имени, как обычно, а с помощью обращения к специальному методу Dispatch, который имеется в каждом классе (TObject).

Методы обработки сообщений применяются внутри библиотеки VCL для обработки команд пользовательского интерфейса и редко нужны при написании прикладных программ.

 

Логика возникновения подобного трюка совершенно ясна – разработчики Delphi хотели ясный, понятный и читаемый код при разработке визуальных компонент. Как всем известно, при создании окна необходимо задать оконную функцию. То есть некую callback-функцию, которая принимает по ссылке сообщение и возвращает результат. Во всех примерах приложений на чистом WinAPI эта оконная функия состоит из case с перечислением всех сообщений, которое окно хочет обрабатывать и вызова DefWindowProc для остальных сообщений. Для обработки небольшого числа сообщений этот case не страшен. Однако если окно обрабатывает большое количество сообщений и обработчики сообщений содержат ветвления, секции исключений и прочие запутывающие вещи, то отладка, поиск обработки нужного сообщения и чтение подобного превращается в сущий кошмар.

Message методы – часть языка Delphi, эта функциональность реализована уже в TObject, так что создавая любой класс вы уже имеете возможность обрабатывать сообщения. При добавлении message метода в класс этот метод располагается в vmt по адресу, кратному числовому значению сообщения, стоящего после ключевого слова message. Message методы - динамические, ведь обработка сообщения должна быть не только у базового класса, но и у его потомков. Чтобы "послать сообщение", классу нужно сформировать структуру Message и вызвать метод Dispatch нужного класса. Этот метод производит поиск в vmt адрес метода по смещению Msg структуры Message у класса и, если его нет непосредственно у класса, среди message методов его родителей. Если адрес метода не найден (то есть обработка такого сообщения не присутствует ни у одного класса в иерархии), то производится вызов метода DefaultHandler.

Выводы:
Во-первых, методы динамические. При этом не обязательно писать override или называть метод тем же именем и даже принимать структуру того же типа, размера и с тем же выравниванием полей, вызов inherited внутри message метода приведет к вызову message метода родителя с передачей туда структуры Message, либо, если у родительских классов нет обработки этого сообщения, к вызову DefaultHandler.


Во-вторых, message методы не обязательно должны принимать именно тип TMessage, описанный в модуле Messages. Достаточно того, чтобы структура имела первое поле типа DWORD, чтобы можно было осуществить переход по адресу, равному числовому значению этого поля. На остальные параметры структуры не налагается ограничений. VCL использует TMessage, поскольку все сообщения Windows, а также пользовательские сообщения CN_BASE + xxx имеют одну (или сходную по размерам) структуру. Однако структуру обязательно нужно передавать по ссылке.

 

Надо заметить, что на диапазон обрабатываемых сообщений налагается ограничение, а именно от 1 до 49151. Почему 49151? Потому что данный прием был введен прежде всего для обработки сообщений Windows, а в Windows номера сообщений от 1 до WM_USER-1 зарезервированы системой, от WM_USER до $7FFF - для пользовательских сообщений и от WM_APP до $C000-1 (49151) - для сообщений на уровне приложения. От $C000 до $FFFF идет диапазон строковых пользовательских сообщений уровня приложения, создаваемых через RegisterWindowMessage, результат вызова фунции невозможно предсказать на этапе компиляции, поэтому логику обработки подобных сообщений лучше делать в оконной процедуре.

Не всегда в VCL можно решить задачу обработки сообщения исключительно переопределением message метода. Иногда это не приводит ни к какому результату, потому, что помимо обработки сообщения в message методе идет ее обработка и в WndProc. Поэтому иногда все-таки приходится лезть в WndProc, хоть это и нехорошо:).

Однако использование message методов не ограничивается только лишь оконными (то есть обладающими хендлом) компонентами, и класс TControl тому подтверждение – все неоконные контролы способны обрабатывать сообщения. И оконный компонент ответственнен за перенаправление сообщений подчиненным неоконным контролам. Именно message методы сделали это возможным! Более того, их использование также не ограничивается только обработкой сообщений Windows - вы можете с таким же успехом обрабатывать свои сообщения, вооружившись знанием о том, как они работают.

Термин «полиморфизм»

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

 

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


 



Поделиться:


Последнее изменение этой страницы: 2016-08-14; просмотров: 325; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.135.202.224 (0.013 с.)