Реферат: Программирование на Delphi
<table cellpadding=«0» "><span Tahoma",«sans-serif»; color:black">Этот курс посвящен среде разработки Delphi фирмы Borland (известной также как Inprise), которая на протяжении многих лет успешно выдерживает (и выигрывает!) жесткую конкуренцию с другими средами программирования.
<span Tahoma",«sans-serif»; color:black">Концепция Delphi1 была реализована в конце 1994 года, когда вышла первая версия среды разработки. В основу этого программного продукта легли концепции объектно-ориентированного программирования (ООП) на базе языка Object Pascal и визуального подхода к построению приложений.
<span Tahoma",«sans-serif»; color:black">После выхода Delphi 1 все компьютерные издания писали об этой среде, как об “убийце Visual Basic”. Появление Delphi 2 (32-разрядной) ознаменовало новую эпоху, — появился доступ к возможностям программных интерфейсов Windows NT и Windows 95, протоколам OLE. Delphi 2 стала средством разработки полноценных приложений клиент/сервер. Вскоре Delphi 3 предоставила разработчикам средства создания распределенных многоуровневых приложений и полноценный инструментарий проектирования приложений для Internet и intranet. Появилась полноценная поддержка COM — модели объектов, ставшей краеугольным камнем современного программирования. Четвертая версия Delphi позволяет полностью интегрировать ваши разработки с объектами COM. Поддержка архитектуры CORBA (Common Object Request Broker Architecture) открывает перед приложениями, созданными в Delphi для платформы Wintel (Windows + Intel), мир других операционных систем (UNIX, OS/2, WMS). Общаться с крупными корпоративными СУБД стало также просто, как и со старым добрым Paradox. Вы можете использовать в своей работе любые уровни межзадачного взаимодействия: от простейшего на уровне сокетов, до связи с такими перспективными инструментами, как Microsoft Transaction Server.
<span Tahoma",«sans-serif»; color:black">Delphi представляет следующие новые свойства и усовершенствования:
<span Tahoma",«sans-serif»">Новые расширения языка. В Delphi в язык Object Pascal включены динамические массивы, методы обработки переполнения, установка значения параметров по умолчанию, и многое другое. <span Tahoma",«sans-serif»">Менеджер Проекта Новый менеджер проекта позволяет Вам объединять проекты которые работают вместе в одину проектную группу. Это позволяет Вам организовать как работу взаимозависимых проектов, таких как однозадачные и многозадачные приложения или DLL, так и совместную работу исполняемых программ. <span Tahoma",«sans-serif»">Новый проводник Новый проводник содержит выполняемые классы, навигацию по модулям, и браузер кода. Проводник кода делает создание классов проще, автоматизирую многие из шагов. Введите прототип метода в разделе интерфейса и свойство выполняемого класса сгенерирует скелетный код в разделе реализации. Также проводник позволяет быстро перемещаться через файлы модуля, а так же между интерфейсом и реализацией. Использование символа Tooltip, позволяет просматривать информацию об объявлении любого идентификатора, затем используя борузер код, можно перейти к его объявлению. <span Tahoma",«sans-serif»">Закрепляемые окна инструментов. IDE (Интегрированная Среда азработки) содержит более перенастраеваемую конфигурацию окон инструментов, которые можно закреплять с редактором кода. Просто перетащите и отпустите окно инструмента к тому месту, к которому хотите. Проводник кода и менеджер проекта можно как закреплять, так и незакреплять. <span Tahoma",«sans-serif»">Улучшенная отладка. Интегрированный отладчик имеет много новых свойств, включая удаленную и многопроцессорную отладку, просмотр кода центрального процессора, инспекторов, усовершенствованные точки прерывания, отладчик специфических подменю и закрепленных окон. <span Tahoma",«sans-serif»">Поддержка MTS. Явная поддержка для использования MTS интегрирована в поддержку многоуровневых баз данных. Кроме того, новый мастер облегчит Вам создание объектов сервера MTS. <span Tahoma",«sans-serif»">Усовершенствования ActiveX. <span Tahoma",«sans-serif»">Delphi обеспечивает расширенную поддержку ActiveX. <span Tahoma",«sans-serif»">Усовершенствования VCL. Иерархия объектов Delphi быда расширена, чтобы включить новый компонент для NT Service приложений. Кроме того, новый компонент выполняемого списка (на Стандартной странице палитры), позволяет Вам централизовать управление меню и команд от кнопок. Управление VCL расширено, чтобы поддерживають drag-and-drop перетаскивания, обеспечивать дополнительный контроль над размещением окна, и многое другое. <span Tahoma",«sans-serif»">Поддержка RTL для 2000-го года. <span Tahoma",«sans-serif»">Глобальная переменная TwoDigitYearCenturWwindow используется функциями StrtToDate и StrToTateTime, чтобы управлять интерпретацией лет с двумя цифрами при преобразовании дат. <span Tahoma",«sans-serif»">Поддержка CORBA. Версии Клинт/Сервер и предприятие включают поддержку для CORBA клиент и сервер приложений. Мастера помогут Вам легко создать сервер CORBA и Динамический Интерфейс Вызова (DII), позволяя Вам записывать клиентов для существующих серверов CORBA. CORBA имеет возможность поддержки в много-уровневых баз данных. Вы можете даже создать сервер, который обрабатывает COM клиентов и CORBA клиентов одновременно.<span Tahoma",«sans-serif»; color:black">Delphi — это комбинация нескольких важнейших технологий:
<span Tahoma",«sans-serif»">Высокопроизводительный компилятор в машинный код <span Tahoma",«sans-serif»">Объектно-ориентированная модель компонент <span Tahoma",«sans-serif»">Визуальное (а, следовательно, и скоростное) построение приложений из программных прототипов <span Tahoma",«sans-serif»">Масштабируемые средства для построения баз данных<span Tahoma",«sans-serif»; color:black">Компилятор, встроенный в Delphi, обеспечивает высокую производительность, необходимую для построения приложений в архитектуре “клиент-сервер”. Он предлагает легкость разработки и быстрое время проверки готового программного блока, характерного для языков четвертого поколения (4GL) и в то же время обеспечивает качество кода, характерного для компилятора 3GL. Кроме того, Delphi обеспечивает быструю разработку без необходимости писать вставки на Си или ручного написания кода (хотя это возможно).
<span Tahoma",«sans-serif»; color:black">В процессе построения приложения разработчик выбирает из палитры компонент готовые компоненты как художник, делающий крупные мазки кистью. Еще до компиляции он видит результаты своей работы — после подключения к источнику данных их можно видеть отображенными на форме, можно перемещаться по данным, представлять их в том или ином виде. В этом смысле проектирование в Delphi мало чем отличается от проектирования в интерпретирующей среде, однако после выполнения компиляции мы получаем код, который исполняется в 10-20 раз быстрее, чем то же самое, сделанное при помощи интерпретатора. Кроме того, компилятор компилятору рознь, в Delphi компиляция производится непосредственно в родной машинный код, в то время как существуют компиляторы, превращающие программу в так называемый p-код, который затем интерпретируется виртуальной p-машиной. Это не может не сказаться на фактическом быстродействии готового приложения.
<span Tahoma",«sans-serif»; color:black">Объектно-ориентированная модель программных компонент. Основной упор этой модели в Delphi делается на максимальном реиспользовании кода. Это позволяет разработчикам строить приложения весьма быстро из заранее подготовленных объектов, а также дает им возможность создавать свои собственные объекты для среды Delphi. Никаких ограничений по типам объектов, которые могут создавать разработчики, не существует. Действительно, все в Delphi написано на нем же, поэтому разработчики имеют доступ к тем же объектам и инструментам, которые использовались для создания среды разработки. В результате нет никакой разницы между объектами, поставляемыми Borland или третьими фирмами, и объектами, которые вы можете создать.
<span Tahoma",«sans-serif»; color:black">В стандартную поставку Delphi входят основные объекты, которые образуют удачно подобранную иерархию базовых классов. Но если возникнет необходимость в решении какой-то специфической проблемы на Delphi, советуем, прежде чем попытаться начинать решать проблему “с нуля”, просмотреть список свободно распространяемых или коммерческих компонент, разработанных третьими фирмами, количество этих компонент в настоящее время составляет несколько тысяч. Событийная модель в Windows всегда была сложна для понимания и отладки. Но именно разработка интерфейса в Delphi является самой простой задачей для программиста.
<span Tahoma",«sans-serif»; color:black">Объекты БД в Delphi основаны на SQL и включают в себя полную мощь Borland Database Engine. В состав Delphi также включен Borland SQL Link, поэтому доступ к СУБД Oracle, Sybase, Informix и InterBase происходит с высокой эффективностью. Кроме того, Delphi включает в себя локальный сервер Interbase для того, чтобы можно было разработать расширяемые на любые внешние SQL-сервера приложения в офлайновом режиме. азработчик в среде Delphi, проектирующий информационную систему для локальной машины (к примеру, небольшую систему учета медицинских карточек для одного компьютера), может использовать для хранения информации файлы формата .dbf (как в dBase или Clipper) или .db (Paradox). Если же он будет использовать локальный InterBase for Windows (это локальный SQL-сервер, входящий в поставку), то его приложение безо всяких изменений будет работать и в составе большой системы с архитектурой клиент-сервер. Вот она — масштабируемость на практике — одно и то же приложение можно использовать как для локального, так и для более серьезного клиент-серверного вариантов.
<span Tahoma",«sans-serif»; color:black">1. Основы объектно-ориентированного программирования
<span Tahoma",«sans-serif»;color:black"><span Tahoma",«sans-serif»; color:black">Понятие класса.
<span Tahoma",«sans-serif»;color:black"><span Tahoma",«sans-serif»; color:black">Классом в Delphi называется особый тип, который может иметь в своем составе поля, методы и свойства. Такой тип также называют объектным типом.
type
tMyClass=class(tObject)
fMyFiled: integer;
function MyMethod: integer;
end;
<span Tahoma",«sans-serif»; color:black">Поля класса аналогичны полям записи. Это — данные, уникальные в программе для каждого созданного в программе экземпляра класса. Описанный выше класс tMyClass имеет одно поле — fMyFiled. В отличие от полей, методы у двух экземпляров одного класса общие. Методы — это процедуры и функции, описанные внутри класса, и предназначенные для операций над его полями. В состав класса входит указатель на специальную таблицу — таблицу виртуальных методов (VMT), в которой содержится вся информация, необходимая для вызова методов. От обычных процедур и функций методы отличаются тем, что при вызове в них передается указатель на экземпляр класса, который их вызвал. Поэтому обрабатываться будут поля именно того объекта, который вызвал метод. Внутри методов указатель на вызвавший их объект доступен через зарезервированное слово Self. Свойством класса называется совокупность поля и методов чтения/записи, обеспечивающих доступ к этому полю. При этом само поле декларируется как private (доступно только внутри данного класса), и доступ к нему возможен только посредством соответствующих методов. Подробнее свойства будут обсуждаться ниже.
<span Tahoma",«sans-serif»; color:black">Понятие объекта
<span Tahoma",«sans-serif»;color:black"><span Tahoma",«sans-serif»; color:black">Чтобы использовать новый тип в программе, нужно, как минимум, определить переменную этого типа. Переменная объектного типа называется экземпляром типа или объектом:
var aMyObject: tMyClass;
<span Tahoma",«sans-serif»; color:black">До введения понятия “класс” в языке Pascal существовала двусмысленность определения “объект”, который мог обозначать и тип, и переменную этого типа. Теперь существует четкая граница: класс — это описание, объект — то, что создано в соответствии с этим описанием.
<span Tahoma",«sans-serif»; color:black">Создание и уничтожение объектов
<span Tahoma",«sans-serif»;color:black"><span Tahoma",«sans-serif»; color:black">В отличие от С++ и Turbo Pascal в Delphi объекты могут быть только динамическими!!!.. Это означает, что в приведенном выше примере переменная aMyObject на самом деле является указателем, содержащем адрес объекта.
<span Tahoma",«sans-serif»; color:black">Объект создается конструктором и уничтожается деструктором.
aMyObject := tMyClass.Create;
//
// действия с созданным объектом
//
aMyObject.Destroy;
<span Tahoma",«sans-serif»; color:black">Следует обратить внимание на то, что для создания объекта aMyObject вызывается метод класса tMyClass.Create. Конструктор класса (и ряд других методов) успешно работает и до создания объекта. Однако большинство обычных методов (в частности все виртуальные и динамические методы). Вызывать до инициализации объекта не следует.
<span Tahoma",«sans-serif»; color:black">В Delphi конструкторов у класса может быть несколько. Общепринято называть конструктор Create, в отличие от Turbo Pascal, где конструкторы назывались Init, и С++, в котором имя конструктора совпадает с именем класса. Типичное название деструктора — Destroy.
type
tMyClass=class(tObject)
fMyFiled: integer;
Constructor Create;
Destructor Destroy;
function MyMethod: integer;
end;
<span Tahoma",«sans-serif»; color:black">Для уничтожения объекта в Delphi рекомендуется использовать не деструктор, а метод Free, который первоначально проверяет указатель, и только затем вызывает деструктор Destroy:
procedure tObject.Free;
<span Tahoma",«sans-serif»; color:black">До передачи управления телу конструктора происходит собственно создание объекта: под него отводится память, значения всех полей обнуляются. Далее выполняется код конструктора, написанный программистом для инициализации объектов данного класса. Таким образом, несмотря на то, что синтаксис конструктора схож с вызовом процедуры (отсутствует возвращаемое значение), на самом деле конструктор — это функция, возвращающая созданный и проинициализированный объект.
<span Tahoma",«sans-serif»; color:black">Примечание. Конструктор создает новый объект только в том случае, если перед его именем указано имя класса. Если указать имя уже существующего объекта, он поведет себя по-другому: не создаст новый объект, а только выполнит код, содержащийся в теле конструктора.
<span Tahoma",«sans-serif»; color:black">Чтобы правильно проинициализировать в создаваемом объекте поля, относящиеся к классу — предку, нужно сразу же при входе в конструктор вызвать конструктор предка при помощи зарезервированного слова inherited:
constructor tMyClass.Create;
Begin
inherited Create;
// Код инициализации tMyClass
End;
<span Tahoma",«sans-serif»; color:black">Как правило, в коде программ, написанных на Delphi, практически н встречается вызовов конструкторов и деструкторов. Дело в том, что любой компонент, попавший при визуальном проектировании в приложение из палитры компонентов, включается в определенную иерархию. Эта иерархия замыкается на форме (класс tForm): для всех ее составных частей конструкторы и деструкторы вызываются автоматически, незримо для программиста. Кто создает и уничтожает формы? Это делает приложение (объект с именем Application). В файле проекта (с расширением DPR) вы можете увидеть вызовы метода Application.CreateForm, предназначенного для этой цели.
<span Tahoma",«sans-serif»; color:black">Что касается объектов, создаваемых динамически (во время выполнения программы), то здесь нужен явный вызов конструктора и метода Free.
<span Tahoma",«sans-serif»; color:black">Свойства
<span Tahoma",«sans-serif»;color:black"><span Tahoma",«sans-serif»; color:black">Как известно, существует три основных принципа, составляющих суть объектно-ориентированного программирования: инкапсуляция, наследование и полиморфизм. Классическое правило объектно-ориентированного программирования утверждает, что для обеспечения надежности нежелателен прямой доступ к полям объекта: чтение и изменение их содержимого должно осуществляться посредством вызова соответствующих методов. Это правило называется инкапсуляцией (сокрытие данных). В старых реализациях ООП (например в Turbo Pascal) эта мысль внедрялась только посредством призывов и примеров в документации; в Delphi есть соответствующая конструкция. Пользователь объекта в Delphi может быть полностью отгорожен от полей объекта при помощи свойств.
<span Tahoma",«sans-serif»; color:black">Обычно свойство определяется тремя элементами: полем и двумя методами осуществляющими его чтение/запись:
type
tMyClass=class(tObject)
function GetaProperty: tSomeType;
procedure SetaProperty(Value: tSomeType);
property aProperty: tSomeType read GetaProperty
write SetaProperty;
end;
<span Tahoma",«sans-serif»; color:black">В данном примере доступ к значению свойства aProperty осуществляется через вызовы методов GetaProperty и SetaProperty, однако в обращении к этим методам в явном виде нет необходимости: достаточно написать
aMyObject.aProperty:=aValue;
aVarable:= aMyObject.aProperty;
<span Tahoma",«sans-serif»; color:black">и Delphi откомпилирует эти операторы в вызовы соответствующих методов. То есть внешне свойство выглядит в точности как обычное поле, но за всяким обращением к нему могут стоять вызовы необходимых программисту методов. Например, если есть объект, представляющий собой квадрат на экране, и его свойству “цвет” присваивается значение “белый”, то произойдет немедленная прорисовка, приводящая реальный цвет на экране в соответствие значению свойства.
<span Tahoma",«sans-serif»; color:black">В методах, устанавливающих значения свойства, может производиться проверка значения на попадание в заданный диапазон значений и вызов других процедур зависящих от вносимых изменений. Если же потребности в специальных процедурах чтения/записи нет, можно вместо имен методов применять имена полей.
tPropClass=class
fValue: tSomeType;
procedure SetValue(aValue: tSomeType);
property Value:tSomeType read fValue write SetValue;
End;
<span Tahoma",«sans-serif»; color:black">В этом примере поле fValue модифицируется при помощи метода SetValue, а читается напрямую.
<span Tahoma",«sans-serif»; color:black">Если свойство должно только читаться или только записываться, в его описании может присутствовать только соответствующий метод:
tReadOnlyClass=class
property NotChanged:tSomeType read GetNotChanged;
End;
<span Tahoma",«sans-serif»; color:black">В этом примере свойство доступно только для чтения. Попытка присвоить значение свойству NotChanged вызовет ошибку компиляции.
<span Tahoma",«sans-serif»; color:black">Свойствам можно присваивать значения по умолчанию. Для этого служит ключевое слово default:
Property Visible:boolean read fVisible write SetVisible default TRUE;
<span Tahoma",«sans-serif»; color:black">Это означает, что при запуске программы свойство будет установлено компилятором в TRUE.
<span Tahoma",«sans-serif»; color:black">Свойство может быть и векторным. В этом случае оно выглядит как массив:
Property Points[index:integer]:tPoint read GetPoint write SetPoint;
<span Tahoma",«sans-serif»; color:black">Для векторного свойства необходимо описать не только тип элементов массива, но также и тип индекса. После ключевых слов read и write должны идти имена соответствующих методов. Использование здесь полей массивов недопустимо. Метод, читающий значение векторного свойства, должен быть описан как функция, возвращающая значение того же типа, что и элементы свойства, и имеющая единственный параметр того же типа и с тем же именем, что и индекс свойства:
function GetPoint(index:integer):tPoint;
<span Tahoma",«sans-serif»; color:black">Аналогично, метод, помещающий значения в такое свойство, должен первым параметром иметь индекс, а вторым — переменную нужного типа.
procedure SetPoint(index:integer; Value:tPoint);
<span Tahoma",«sans-serif»; color:black">У векторных свойств есть еще одна важная особенность: некоторые классы в Delphi (списки tList, наборы строк tStrings и т.д.) “построены” вокруг одного основного векторного свойства. Основной метод такого класса дает доступ к элементам некоторого массива, а все основные методы являются как бы вспомогательными. Для упрощения работы с объектами подобного класса можно описать подобное свойство с ключевым словом default:
type tMyList=class
property list[Index:integer]:string read Getlist write Setlist; default;
end;
<span Tahoma",«sans-serif»; color:black">Если у объекта есть такое свойство, его можно не упоминать, а ставить индекс в квадратных скобках сразу после имени объекта:
var MyList:tMyList
Begin
MyList.list[1]:=’First’; {Первый способ}
MyList.[2]:=’Second’; {Первый способ}
End;
<span Tahoma",«sans-serif»; color:black">Употребляя ключевое слово default необходимо соблюдать осторожность, т.к. для обычных и векторных свойств оно употребляется в разных значениях.
<span Tahoma",«sans-serif»; color:black">О роли свойств в Delphi красноречиво говорит тот факт, что у всех имеющихся в распоряжении программиста стандартных классов 100% полей недоступны и заменены базирующимися на них свойствами. Того же правила следует придерживаться и при разработке собственных классов.
<span Tahoma",«sans-serif»; color:black">Наследование
<span Tahoma",«sans-serif»;color:black"><span Tahoma",«sans-serif»; color:black">Вторым “столпом” ООП является наследование. Этот простой принцип означает, что если необходимо создать новый класс, лишь немного отличающийся от уже имеющегося, нет необходимости в переписывании заново уже существующего кода. Вы объявляете, что новый класс
tNewClass=class(tOldClass);
<span Tahoma",«sans-serif»; color:black">является потомком или дочерним классом класса tOldClass, называемого предком или родительским классом, и добавляете к нему новые поля методы и свойства.
<span Tahoma",«sans-serif»; color:black">В Delphi все классы являются потомками класса tObject. Поэтому, если вы строите дочерний класс прямо от tObject, то в определении его можно не упоминать. Следующие два описания одинаково верны:
tMyClass=class(tObject);
tMyClass=class;
<span Tahoma",«sans-serif»; color:black">Более подробно класс tObject будет рассмотрен ниже.
<span Tahoma",«sans-serif»; color:black">Унаследованные от класса-предка поля и методы доступны в дочернем классе; если имеет место совпадение имен методов, говорят, что они перекрываются.
<span Tahoma",«sans-serif»; color:black">Рассмотрим поведение методов при наследовании. По тому, какие действия происходят при вызове, методы делятся на три группы. В первую группу отнесем статические методы, во вторую — виртуальные (virtual) и динамические (dynamic) и, наконец, в третью — появившиеся только в Delphi 4 перегружаемые (overload) методы.
<span Tahoma",«sans-serif»; color:black">Статические методы, а также любые поля в классах-потомках ведут себя одинаково: можно без ограничений перекрывать старые имена и при этом менять тип методов. Код нового статического метода полностью перекрывает (заменяет собой) код старого метода:
type
tFirstClass=class
fData:Extended;
procedure SetData(aValue:Extended);
end;
tSecondClass=class(tFirstClass)
fData:Integer;
procedure SetData(aValue:Integer);
end;
procedure tFirstClass.SetData(aValue:Extended);
Begin
fData:=1.0;
End;
procedure tFirstClass.SetData(aValue:Extended);
Begin
fData:=1;
inherited SetData(0.99);
End;
<span Tahoma",«sans-serif»; color:black">В этом примере разные методы с именем SetData присваивают значение разным полям с именем fData. Перекрытое (одноименное) поле предка недоступно в потомке. Поэтому два одноименных поля с именем fData приведены только для примера.
<span Tahoma",«sans-serif»; color:black">В отличие от поля, внутри других методов перекрытый метод доступен при указании ключевого слова inherited. По умолчанию методы объектов классов статические — их адрес определяется еще на этапе компиляции проекта, поэтому они вызываются быстрее всего.
<span Tahoma",«sans-serif»; color:black">Принципиально отличаются от статических виртуальные и динамические методы. Они должны быть объявлены путем добавления соответствующей директивы dynamic или virtual. С точки зрения наследования методы этих двух категорий одинаковы: они могут быть перекрыты в дочернем классе только одноименными методоми, имеющими тот же тип.
<span Tahoma",«sans-serif»; color:black">Полиморфизм. Виртуальные и динамические методы
<span Tahoma",«sans-serif»;color:black"><span Tahoma",«sans-serif»; color:black">Рассмотрим следующий пример. Пусть имеется некое обобщенное поле для хранения данных — класс tFiled и три его потомка — для хранения строк, целых и вещественных чисел:
type
tFiled = class
function GetData:string; virtual; abctract;
end;
tStringFiled = class(tFiled)
fData:string;
function GetData: string; override;
end;
tIntegerFiled = class(tFiled)
fData:Integer;
function GetData: string; override;
end;
tExtendedFiled = class(tFiled)
fData:Extended;
function GetData: string; override;
end;
function tStringFiled.GetData: string;
Begin
Result:=fData;
End;
function tIntegerFiled.GetData: string;
Begin
Result:=IntToStr(fData);
End;
function tExtendedFiled.GetData: string;
Begin
Result:=FloatToStr(fData,ffFixed, 7, 2);
End;
function ShowData(aFiled:tFiled): string;
Begin
Form1.Label1.Caption:=aFiled.GetData;
End;
<span Tahoma",«sans-serif»; color:black">В этом примере классы содержат разнотипные поля данных fData, а также имеют унаследованный от tFiled виртуальный метод GetData, возвращающий данные в виде строки. Внешняя по отношению к ним процедура ShowData получает объект в виде параметра и показывает эту строку.
<span Tahoma",«sans-serif»; color:black">Согласно правилам контроля соответствия типов (typecasting) ObjectPascal, объекту, как указателю на экземпляр класса, может быть присвоен адрес экземпляра любого из дочерних типов. Это означает, что в предыдущем примере в процедуру ShowData можно передавать объекты классов tStringFiled, tIntegerFiled, tExtendedFiled и любого другого потомка tFiled.
<span Tahoma",«sans-serif»; color:black">Но какой (точнее, чей) метод GetData будет при этом вызван? Тот, который соответствует классу фактически переданного объекта. Этот принцип называется полиморфизмом.
<span Tahoma",«sans-serif»; color:black">Возвращаясь к рассмотренному выше примеру, отметим, что у компилятора нет возможности определить класс объекта, фактически переданного в процедуру ShowData на этапе компиляции. Механизм, позволяющий определить этот класс прямо во время выполнения называется поздним связыванием. Естественно, такой механизм должен быть связан с передаваемым объектом. Для этого служит таблица виртуальных методов (Virtual Method Table, VMT) и таблица динамических методов (Dynamic Method Table, DMT).
<span Tahoma",«sans-serif»; color:black">Различие между виртуальными и динамическими методами заключается в особенности поиска адреса. Когда компилятор встречает обращение к виртуальному методу, он подставляет вместо прямого вызова по конкретному адресу код, который обращается к VMT и извлекает оттуда нужный адрес. Такая таблица есть для каждого класса. В ней хранятся адреса всех виртуальных методов класса, независимо от того, унаследованы ли они от предка или перекрыты в данном классе. Отсюда и достоинства и недостатки виртуальных методов: они вызываются сравнительно быстро, однако для хранения указателей на них в таблице VMT требуется большое количество памяти.
<span Tahoma",«sans-serif»; color:black">Динамические методы вызываются медленнее, но позволяют более экономно расходовать память. Каждому динамическому методу системой присваивается уникальный индекс. В таблице динамических методов класса хранятся индексы только тех методов только тех динамических методов, которые описаны в данном классе. При вызове динамического метода происходит поиск в этой таблице. В случае неудачи просматриваются DMT всех классов-предков в порядке их иерархии и, наконец, tObject, где имеется стандартный обработчик вызова динамических методов. Экономия памяти очевидна.
<span Tahoma",«sans-serif»; color:black">Для перекрытия и виртуальных и динамических методов служит директива override, с помощью которой (и только с ней!) можно переопределять оба этих типа методов.
type
tParentClass=class
fFirstFiled:Integer;
fSecondFiled:longInt;
procedure StaticMethod;
procedure VirtualMethod1; virtual;
procedure VirtualMethod2; virtual;
procedure DynamicMethod1; dynamic;
procedure DynamicMethod2; dynamic;
end;
tChildClass=class(tParentClass)
procedure StaticMethod;
procedure VirtualMethod1; override;
procedure DynamicMethod1; override;
end;
<span Tahoma",«sans-serif»; color:black">Первый метод класса tChildClass создается заново, два остальных перекрываются. Создадим объекты этих классов:
var Obj1: tParentClass;
Obj2: tChildClass;
<span Tahoma",«sans-serif»; color:black">Внутренняя структура этих объектов показана ниже. <img src="/cache/referats/16272/image001.gif" v:shapes="_x0000_i1025">
<span Tahoma",«sans-serif»; color:black">Первое поле каждого экземпляра каждого объекта содержит указатель на его класс. Класс, как структура состоит из двух частей. Начиная с адреса, на который ссылается указатель на класс, располагается таблица виртуальных методов. Она содержит адреса всех виртуальных методов класса, включая и унаследованные от предков. Перед таблицей виртуальных методов расположена специальная структура, содержащая дополнительную информацию. В ней содержатся данные, полностью характеризующие класс: имя, размер экземпляров, указатели на класс-предок и т.д. Одно из полей структуры содержит адрес таблицы динамических методов класса (DMT). Таблица имеет следующий формат: в начале — слово, содержащее количество элементов таблицы. Затем — слова, соответствующие индексам методов. Нумерация индексов начинается с –1 и идет по убывающей. После индексов идут собственно адреса динамических методов. Следует обратить внимание на то, что DMT объекта Obj1 состоит из двух элементов, Obj2 — из одного, соответствующего перекрытому методу DynamicMethod1. В случае вызова Obj2.DynamicMethod2 индекс не будет найден в DMT Obj2, и произойдет обращение к DMT Obj1. Именно так экономится память при использовании динамических методов.
<span Tahoma",«sans-serif»; color:black">Как указывалось выше, указатель на класс указывает на первый виртуальный метод. Служебные данные размещаются перед таблицей виртуальных методов, то есть с отрицательным смещением. Эти смещения описаны в модуле SYSTEM.PAS:
vmtSelfPtr = -76
vmtIntfTable = -72
vmtAutoTable = -68
vmtInitTable = -64
vmtTypeInfo = -60
vmtFiledTable = -56
vmtMethodTable = -52
vmtDynamicTable = -48
vmtClassName = -44
vmtInstanceSize = -40
vmtParent = -36
vmtSafeCallException = -32
vmtAfterConstruction = -28
vmtBeforeDestruction = -24
vmtDispatch = -20
vmtDefaultHandler = -16
vmtNewInstance = -12
vmtFreeInstance = -8
vmtDestroy = -4
<span Tahoma",«sans-serif»; color:black">Поля vmtDynamicTable, vmtDispatch и vmtDefaultHandler отвечают за вызов динамических методов. Поля vmtNewInstance, vmtFreeInstance и vmtDestroy содержат адреса методов создания и уничтожения экземпляров класса. Поля vmtIntfTable, vmtAutoTable, vmtSafeCallException введены для обеспечения совместимости с моделью объекта COM. Остальные поля доступны через методы объекта tObject. В Object Pascal эта информация играет важную роль и может использоваться программистом неявно. В языке определены два оператора — is и as, неявно обращающиеся к ней. Оператор is предназначен для проверки совместимости по присвоению экземпляра объекта с заданным классом. Выражение вида:
AnObject is tObjectType
<span Tahoma",«sans-serif»; color:black">Принимает значение True только если объект AnObject совместим по присвоению с классом tObjectType, то есть является объектом этого класса или его потомком.
<span Tahoma",«sans-serif»; color:black">Оператор as введен в язык специально для приведения объектных типов. С его помощью можно рассматривать экземпляр объекта как принадлежащий к другому совместимому типу:
with AObjectOfSomeType as tAnotherType do...
<span Tahoma",«sans-serif»; color:black">От стандартного способа приведения типов использование оператора as отличается наличием проверки на совместимость типов во время выполнения: попытка приведения к несовместимому типу приводит к возникновению исключительной ситуации eInvalidCast. После выполнения оператора as сам объект остается неизменным, но выполняются те его методы, которые соответствуют присваиваемому классу.
<span Tahoma",«sans-serif»; color:black">Вся информация, описывающая класс, создается и размещается в памяти на этапе компиляции. Доступ к информации вне методов этого класса можно получить, описав соответствующий указатель, называющийся указателем на класс или указателем на объектный тип (class reference). Он описывается при помощи зарезервированных слов class of. Например, указатель на класс tObject описан в модуле SYSTEM.PAS и называется tClass. Аналогичные указатели определены и для других важнейших классов: tComponentClass, tControlClass и т.д.
<span Tahoma",«sans-serif»; color:black">С указателем на класс тесно связано понятие методов класса. Такие методы можно вызывать и без создания экземпляра объекта — с указанием имени класса в котором они описаны. Перед описанием метода класса нужно поставить ключевое слово class.
<span Tahoma",«sans-serif»; color:black">Разумеется, методы класса не могут использовать значения, содержащиеся в полях класса: ведь экземпляра класса не существует!!! Методы класса служат для извлечения внутренней информации класса. Ниже перечислены методы класса tObject:
<span Tahoma",«sans-serif»; color:black">Метод и Описание
сlass function ClassName:ShortString
Возвращает имя класса
сlass function ClassNameIs(const Name:ShortString):Boolean
Принимает значение True, если имя класса равно заданному
сlass function ClassParent:tClass
Возвращает указатель на родительский класс
сlass function ClassInfo:pointer
Возвращает указатель на структуру с дополнительными данными об опубликованных методах и свойствах.
сlass function InstanceSize:Longint
Возвращает размер экземпляра класса
сlass function InheritsFrom (aClass: tClass):Boolean
Возвращает True, если данный класс наследует от заданного
сlass function MethodAddress(const Name:ShortString):Pointer
Возвращает адрес метода по его имени (только для опубликованных методов)
сlass function MethodName (Addres: pointer):ShortString
Возвращает имя метода по его адресу (только для опубликованных методов)
<span Tahoma",«sans-serif»; color:black">В Delphi 4 в класс tObject добавлены еще два виртуальных метода — AfterConstruction и BeforeDestruction. Как следует из названия, они вызываются сразу после создания экземпляра объекта и непосредственно перед уничтожением.
<span Tahoma",«sans-serif»;color:black">Перегрузка методов
<span Tahoma",«sans-serif»;color:black"><span Tahoma",«sans-serif»; color:black">В Delphi 4 появилась новая разновидность методов — перегружаемые. Перегрузка нужна для того, чтобы произвести одинаковые или похожие действия над разнотипными данными. Перегружаемые методы описываются с ключевым словом overload.
Type
tFirstClass=class
E:extended;
procedure SetData(aValue: Extended); overload;
end;
tSecondClass=class(tFirstClass)
I:integer;
procedure SetData(aValue: Integer); overload;
end;
<span Tahoma",«sans-serif»; color:black">Объявив метод SetData перегружаемым, в программе можно использовать обе его реализации одновременно. Это возможно потому, что компилятор определяет тип передаваемого параметра (целый или вещественный) и в зависимости от этого подставит вызов соответствующего метода.
<span Tahoma",«sans-serif»; color:black">Для перегрузки виртуального метода используется зарезервированное слово reintroduce:
procedure SetData(aValue:string); reintrouce;overload;
<span Tahoma",«sans-serif»; color:black">На перегрузку методов накладывается ограничение: нельзя перегружать методы, находящиеся в области видимости published.
<span Tahoma",«sans-serif»; color:black">Абстрактные методы
<span Tahoma",«sans-serif»;color:black"><span Tahoma",«sans-serif»; color:black">Абстрактными называются методы, которые определены в классе, но не содержат никаких действий, никогда не вызываются и обязательно должны быть переопределены в классах-потомках. Абстрактными могут быть только виртуальные и динамические методы. Для описания абстрактного метода используется директива abstract:
Procedure NeverCallMe; virtual; abstract;
<span Tahoma",«sans-serif»; color:black">Никакого кода абстрактный метод не содержит. Его вызов приведет к созданию исключительной ситуации eAbstractError.
<span Tahoma",«sans-serif»; color:black">События
<span Tahoma",«sans-serif»;color:black"><span Tahoma",«sans-serif»; color:black">Операционная система Windows® основана на сообщениях. Сообщения эти возникают в результате действий пользователя, аппаратуры компьютера или других программ. Таких сообщений в Windows сотни, и по большому счету, написать программу для Windows — значит определить реакцию на некоторые из них.
<span Tahoma",«sans-serif»; color:black">Работать с таким количеством сообщений, даже имея под рукой справочник, нелегко. Поэтому одним из главных преимуществ Delphi является то, что программист полностью избавлен от необходимости работать с сообщениями Windows (хотя такая возможность у него имеется). Типовых событий в Delphi — не более двух десятков, и все они имеют простую интерпретацию, не требующую глубоких знаний среды.
<span Tahoma",«sans-serif»; color:black">С точки з