    Object Pascal: углубленное изложение 

    ГЛАВА 4 

    Дж. Райдер 


    Классы 

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

    Приведение типа 

       Научитесь одновременно надежным и опасным способом представлять данные 
       одного типа в форме другого. 

    Типы ссылки на класс 

       Научитесь манипулировать типами как переменными и значениями. 

    Библиотека подпрограмм 

       Научитесь использовать подпрограммы, поставляемые с Delphi. 

    И напоследок 

       Узнайте, как развитые средства Object Pascal внедрены в Delphi. 


    Яззык программирования Pascal, разработанный в свое время Никлосом Виртом, 
представлял собой наглядный пример элегантности и экономичности. Все было 
сделано для того, чтобы оставить в синтаксисе минимум зарезервированных слов, 
операторов и идентификаторов. С точки зрения диалога человека и машины, 
возможность делать одно и то же множеством способов не очень удобна, поэтому 
были устранены избыточные средства. У этого языка есть и недостатки. Были 
утеряны многие любимые программистами свойства, характерные для других языков. 
Осталось лишь то, что представляло собой суть структурного программирования. 
Остались данные, код и множество способов объединить все это в одной программе. 
    И попытки такого объединения были предприняты. В языке не было большого 
количества готовых стандартных функций, поэтому программистам языка Pascal при 
решении сложных задач пришлось полагаться на свои собственные способности. 
Кое-какие подпрограммы можно было разыскать в библиотеках, однако большинство 
предпочитало не тратить время на приспособление общих решений к конкретным 
задачам. Но и за трудоемкие, сложные решения никто не брался. Это была игра в 
минимализм. Находился какой-то работающий способ, который и принимался в 
качестве рабочего, но результат достигнут не был. В другой раз для решения 
подобной задачи применялся уже опробованный метод, и дело продвигалось дальше. 
    Изменялся и сам язык, что давало программистам больше возможностей выбора 
методов решения задач, которые затем можно было использовать снова и снова. В 
первоначальном определении языка приходилось иметь дело с разными способами 
структуризации данных и организации кода для обработки этих структур. После 
появились модули (unit), и вдруг оказалось, что программисты работают так, 
как работали всегда. В каждом модуле они структурировали данные и 
программировали их обработку. Однако теперь одним решением могло 
воспользоваться несколько программ. И уже не было необходимости 

82

каждый раз программировать задачу от начала до конца: можно было 
сосредоточить внимание на небольших ее фрагментах. Когда же появился Object 
Pascal, стало возможным делить программу на еще более мелкие части. 
По-прежнему оставались данные; никуда не девался код; как и раньше, все это 
увязывалось вместе ограниченным числом способов. Но структурное 
программирование перешло на качественно новый уровень — на уровень объектного 
программирования (чаще это именуется несколько тяжеловесно: 
объектно-ориентированное программирование. — Прим. перев.}. Эта революция 
свершилась не в один день, а явилась логическим развитием общего подхода 
программистов Pascal к решению реальных задач. Язык, подвергнувшись 
незначительным изменениям, в основе своей остался тем же языком Pascal. 
    На уровне Delphi одним из очевидных достижений Object Pascal, облегчившим 
разработку многократно используемых элементов, стали визуальные компоненты. 
    В то же время в библиотеках утилит и строковых процедур появились более 
стандартизованные процедуры и функции. Как и в первоначальном Pascal, основным 
принципом оставался минимализм. Использование множества способов решения 
задач, когда уже есть один хороший способ, лишено смысла. 
    Язык по-прежнему удобен для программирования. Все так же можно 
пользоваться библиотеками процедур. Однако теперь удобнее облачать свои идеи в 
форму класса. 

    Классы 

    Класс Object Pascal — основной элемент программирования в Delphi. Конечно, 
в языке определены и более мелкие элементы низшего уровня, но ни один из них 
не проявляет в полной мере той целостности, с которой в классе объединяются 
данные и код, образуя единый компонент. 
    Как и структурные типы, рассмотренные нами в главе 3, "Object Pascal: 
основные понятия", класс сам по себе не является типом. Подобно типу record, 
тип class состоит из более мелких частей (элементов). Класс, по существу, 
является набором таких элементов. Классы известны также как объектные типы. 
Годятся оба наименования. Конкретный образец класса — объект. Однако ввиду 
модели адресации объекта это не вполне идентично переменной типа class. 
    Разделить класс на компоненты можно несколькими способами. С 
функциональной точки зрения удобно пользоваться понятием члена как одним из 
компонентов, описанных ниже. 

    Компоненты    Описание 

    Поля 		Аналогичны полям record, но без вариантных частей 
    Методы 		Процедуры и функции для обработки полей объекта 
    Свойства 	Компоненты, которые можно использовать, как поля, хотя они 
			реализованы, как методы 

    В отличие от других типов, тип class можно объявлять только глобально. 
Многократно используемые классы удобнее всего определять в интерфейсе модуля. 
Object Pascal запрещает определение класса в некоторых участках. В 
частности, нельзя делать такие объявления в разделе объявления переменных var, 
а также внутри процедур или функций. 

    Синтаксис 

    type TMyClass = class // Имя типа 
	(THeritage)  // Имя наследуемого класса 
    public {или private, protected, published)         // Видимость 
    afield: atype; // Поле 
    procedure AMethod( Parm: AType );  // Метод 
	{"статический", virtual, dynamic, message, abstract, override} 
    property Aproperty: atype read afield write amethod; 
    // Свойство 
    class procedure AClassMethod;  // Метод класса 
    end; 

    Объектный тип объявляется с помощью зарезервированного слова class, за 
которым следует перечисление членов класса. 
    Объектные типы отличаются от остальных типов. Кроме условия, требующего 
глобального объявления объектных типов, существует еще одно ограничение: 
нельзя объявлять файл типа, содержащего объектные компоненты. Одна из причин 
этого состоит в том, что переменные объектных типов физически не представляют 
экземпляров классов (объектов). Объекты создаются только во время выполнения 
программы. Переменные объектных типов указывают на объекты, которые они 
представляют. Эта парадигма иногда именуется моделью адресации объекта. 
Физически переменные типов класса хранятся в памяти как указатели. Однако язык 
Object Pascal позволяет трактовать члены объекта как непосредственное 
содержимое переменной. Закавыка, однако, в том, что несколько объектных 
переменных могут указывать на один объект, но не существует двух объектов с 
одинаковыми значениями. 

    Глава 4. Object Pascal: углубленное изложение 83 

    При объявлении класса нет надобности перечислять все его компоненты. Новый 
объектный тип наследует все члены предыдущего. Наследующий класс обычно 
именуется потомком, наследуемый — предком. Потомок неявно содержит члены, 
объявленные в его предках. И если потомок наследует члены своего предка, 
который, в свою очередь, наследует члены прапредка, то и наш потомок наследует 
все члены прапредка. Таким образом, наследование оказывается транзитивным. 
Множество всех классов — потомков одного (пра)предка называется доменом этого 
предка. Список членов наследников может только возрастать. Объявляя класс, 
можно добавлять в него новые члены. Удалить член, объявленный в классе-предке, 
невозможно. 
    В Object Pascal есть один заранее определенный тип TObject — общий предок 
всех остальных типов класса. Если в объявлении класса не указать наследуемого 
класса, таковым окажется TObject. Этот класс определен в модуле System и 
содерж несколько членов, наследуемых всем доменом TObject. Следовательно, 
эти члены доступны всем создаваемым классам. 
    Вновь создаваемые классы совместимы в отношении присваивания со всеми 
классами-предками. Во время выполнен программы переменной некоторого 
объектного типа можно присвоить значение любой другой переменной ее домена. 
Можно присваивать значение переменной того же типа или типа-потомка. Рассмотрим 
следующие объявления объектных типов. 

    type TThing = class 
	...
    end; 
    TAnimal = class(TThing) 
	...
    end; 
    TMammal = class(TAnimal) 
	...
    end; 
    TVegetable = class(TThing) 
	...
    end; 
    TTree = class(TVegetable) 
	...
    end; 

    Значение экземпляра TAnimal можно присвоить переменным TAnimal, TThing и 
TObject. Переменной TThing можно присвоить значения TThing, TAnimal, TMammal, 
TTree и любых других потомков TThing. Однако присвоить значение TTree 
переменной TMammal невозможно. А поскольку переменной TThing может быть 
присвоено значение TTree, то значение типа, являющегося предком TThing, 
переменной TMammal также присваивать нельзя. 

    Область видимости 

    Список членов класса подобен интерфейсу между экземплярами класса. Членам 
класса задаются команды видимости: private, protected, public и published. Они 
устанавливают разную степень видимости членов при обращениях из других 
модулей. Внутри модуля, в котором определен данный класс, видны все члены. 
    В отличие от других идентификаторов, применяемых при объявлениях типов, 
вне контекста объявлений объектных типов данные не являются зарезервированными 
словами. Однако удобнее работать с ними именно как с зарезервированными 
словами, не переопределяя их смысла. 
    Нет необходимости явно указывать команду видимости для каждого члена. 
Указанная команда задает видимость всех следующих за ней членов, пока не будет 
указана другая команда. Все члены, следующие за такой командой, будут иметь 
одинаковую видимость. Подобная последовательность членов именуется частью. 

    На заметку
    Область видимости класса в C++ задается ключевыми словами public, 
protected и private. Несмотря на сходство с аналогичными определениями языка 
Object Pascal, они не идентичны. В частности, в Object Pascal член, 
объявленный в конкретном модуле, в этом модуле всегда виден как public. 


    Private 

    Это команда минимальной области видимости. Вне модуля, в котором она 
определена, члены, объявленные private, просто не видны. 
    Обычно команда private объявляет члены, значения которых оказывают 
решающее влияние на поведение объектов или которые должны жестко 
синхронизироваться с другими членами, поскольку неосторожная их модификация 
может привести к нежелательными последствиям. Если необходимо создать два 
класса, которые могли бы обращаться к членам друг друга, объявленным как 
private, их надо объявлять в одном модуле. Особенности реализации таких членов 
для конечного пользователя оказываются полностью недоступными.                 
                                                                               
    84 Часть I. Основные положения 


    Protected 

    Эта команда, по сравнению с private, несколько ослабляет защиту. Вне 
модуля, в котором определен конкретный класс, его члены, помещенные в область 
protected, видны только в классах-потомках — домене класса. Если класс-потомок 
не определен, такие члены остаются невидимыми, как будто они объявлены как private. 
    Если не определить класса-потомка, особенности реализации защищенных 
членов останутся скрытыми. Однако программист, определивший новый 
класс-потомок, получает доступ к таким членам, причем новый класс 
необязательно размещать в одном модуле с предком. 

    Public 

    Члены, объявленные как public, видны везде, где виден сам класс. Никакие 
ограничения на такие члены не налагаются. 

    Published 

    Объявление члена published порождает информацию о типах времени выполнения 
(Runtime Type Information — RTTI), будь то поле, метод или свойство, через 
специальный published-интерфейс. По информации о типах программа во время 
выполнения проверяет, какому объекту принадлежат те или иные поля, методы и свойства. 
    Во время выполнения программы члены, объявленные published, ведут себя 
аналогично объявленным public. Все члены, объявленные published, видны в любом 
модуле, в котором виден сам класс. Разница между членами, объявленными 
published и public, состоит в том, что через published-интерфейс информацию о 
членах, объявленных published, может получить внешняя программа. 
    В Delphi, например, published-интерфейс объектов в палитре компонентов 
используется инспектором объектов (Object Inspector), чтобы определять, 
какие свойства и события выводить на экран на стадии проектирования. В 
библиотеке визуальных компонентов (Visual Component Library — VCL) информация 
о типах применяется для манипуляции свойствами визуальных компонентов при 
записи этих компонентов во внешний файл или считывании из него. 
    На применение команды published наложены некоторые ограничения. 
    1.   Класс может иметь члены published только в случаях, если он 
скомпилирован с командой генерирования информации о типах времени выполнения 
или является наследником такого класса. Генерирование информации RTTI задается 
командой компилятора $М. 
    2.   Любое поле, определенное в разделе published, должно иметь объектный 
тип. Поля остальных типов должны размещаться в разделах public, protected и 
private.       
    3.   Свойства, объявленные published, не могут быть массивами, но могут 
иметь порядковый, действительный (кроме Real), строковый тип, тип малого 
множества, класса или указателя на метод (но не глобальный процедурный тип). 
Тип малого множества — это множественный тип, исходный тип которого ограничен 
порядковыми значениями между нулем и пятнадцатью. Объем переменной этого типа 
— байт или слово. 
    Список членов класса необязательно начинать с команды видимости. По 
умолчанию предполагается использование команды published. Однако, поскольку 
такая "неименованная" часть используется в Delphi особым образом (в связи с 
визуальными компонентами), лучше выработать привычку явно указывать область 
видимости каждой части. 

    Методы 

    В методах класса код соединяется с теми данными, которые он обрабатывает. 
Для этого методам разредцен доступ, к полям объектов без явной передачи этих 
полей в качестве параметров. 
    Обычно методы делятся на две части. Первая (объявление) располагается в 
объявлении класса (как правило, в интерфейсе модуля) и служит для того, чтобы 
сообщить компилятору о намерении создать метод. Это подобно объявлению forward 
для обычных процедур и функций. Тело метода объявляется вне объявления класса, 
в разделе реализации того же модуля. Содержимое тела метода аналогачно 
содержимому тела обычной процедуры или функции, однако в заголовке тела 
указывается тип класса, в котором данный метод объявлен, как в следующем примере. 

    procedure TMyClass.AMethod( Parml: Atype); begin 
    end;    {   AMethod   } 

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

    Глава 4. Object Pascal: углубленное изложение 85 

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

    Таблица 4.1. Команды методов 

    Команда 	Описание 

    Virtual 	Процедура вызывается по таблице виртуальных методов 
    Dynamic 	Процедура вызывается по таблице динамических методов 
    Message 	Вызов определяется механизмом обработки сообщений 
    Abstract 	Метод не существует для данного класса и должен быть переопределен 
    Override 	Заменить виртуальный или динамический метод для данного класса 
    Class 		Метод вызывается без использованиянеявнрй переменной Self
                                                                               
    Существует еще два особых вида методов — конструкторы и деструкторы. Они 
применяются соответственно для создания и удаления объектов. Кроме всего 
прочего, конструктор обеспечивает правильный вызов виртуальных методов. 

    Static 

    По умолчанию методы, объявленные в объектном типе, являются статическими. 
Они никак не помечаются. При вызове такого метода выбор конкретной процедуры 
или функции определяется исключительно типом объектной переменной. Тип самого 
объекта, на который эта переменная ссылается, значения не имеет. Как только 
компилятор встречает обращение AnObject.AStaticMethod, где AnObject — 
переменная AClassType, генерируется код вызова процедуры 
AClassType.AStaticMethod или ближайший AStaticMethod, наследуемый классом 
AClassType. Безусловно, переменная AnObject может ссылаться на объект любого 
класса, производного от AClassType, в котором определен свой метод 
AStaticMethod. Однако при вызове статического метода фактический класс объекта 
игнорируется. Будет вызван именно AClassType.AStaticMethod. 

    Virtual 

    При обращении к виртуальным методам, в отличие от статических, конкретная 
вызываемая процедура или функция определяется в момент самого обращения. Этот 
процесс называется поздним связыванием. Чтобы метод стал виртуальным, его надо 
объявить с помощью зарезервированного слова virtual. 
    При вызове виртуального метода выбор конкретной процедуры выполняется по 
типу фактического объекта, а не переменной, которая на него ссылается. Это 
важно потому, что в классах-потомках, наследующих данный виртуальный 
метод, он может быть переопределен (override). Эта замена виртуального метода 
также наследуется. Область видимости переопределенного метода — все потомки 
класса с переопределенным методом, помеченным override, до переопределения 
метода. Переопределенный виртуальный метод должен иметь такие же формальные 
параметры, что и исходный (наследуемый). Если переопределяемый метод является 
функцией, должен сохраняться и ее тип. При переопределении команду virtual 
сменяет команда override. 
    Использование команды override — единственный способ переопределить 
виртуальный метод. Если в классе-потомке определяется метод с тем же 
идентификатором, что и наследуемый, но без команды override, новый метод не 
переопределяет наследуемый, а скрывает. 
    Когда компилятор встречает ссылку на AnObject.AVirtualMethod, он знает, 
что AVirtualMethod может быть переопределен в классах, производных от класса 
AClassType. Поэтому генерируется код не непосредственного вызова 
AClassType.AVirtualMethod, а поиска адреса AVirtualMethod, соответствующего 
типу фактического объекта, но не объявленной переменной. 

    Dynamic 

    Команда dynamic при задании механизма выбора метода подобна команде 
virtual. В обоих случаях адрес нужной процедуры или функции определяется по 
типу фактического объекта. В отношении программирования разница между двумя 
командами почти незаметна. 

    86 Часть I. Основные положения 

    Генерированный код также имеет незначительные отличия. Обращения к 
виртуальным методам происходят немного быстрее, но несколько увеличивают объем 
программы. Обращение к динамическим методам осуществляется медленнее, зато 
позволет уменьшить объем программы.
    Как правило, виртуальные методы оказываются более удобными, если 
приходится писать полиморфные процедуры (с аргументами переменных типов). Если 
же надо создать класс с множеством классов-потомков, в которых 
переопределяется незначительное число методов, удобнее использовать 
динамические методы. 

    Message 

    Особая форма динамического метода — метод обработки сообщений. 
Генерируемый компилятором код вызова методов, объявленных с помощью команды 
message, почти идентичен коду вызова динамических методов. Однако при 
программировании это сходство никак не учитывается. Методы обработки сообщений 
применяются совершенно особым образом. 
    При программировании методов обработки сообщений следует не упускать из 
виду три момента. 
    •    Способ объявления обработчика сообщений. 
    •    Способ передачи сообщений обработчику сообщений. 
    •    Способ обращения к наследованному обработчику. 
    Для объявления методов обработки сообщений, по сравнению с прочими 
методами, существуют некоторые дополнительные условия. В отличие от 
виртуальных и динамических методов, которые могут быть процедурами либо 
функциями, обработчики сообщений — это всегда процедуры. После команды message 
следует целая постоянная (используемая как индекс для поиска в той же таблице 
динамических методов). Кроме того, в обработчике сообщений указывается один 
формальный параметр var. 
    Обработчики сообщений — важнейшее звено во взаимодействии операционной 
системы Windows с визуальными компонентами Delphi, которым эти сообщения 
адресуются. Многие значения индекса обработчика сообщений зарезервированы за 
конкретными сообщениями Windows. Объявление обработчика сообщений внутри 
объявления класса выглядит приблизительно так: 

    Procedure  Handle_WM_Paint_Message(var Msglnfo); message WM_PAINT; 

    Имя метода обработки сообщений не имеет значения для компилятора. Для 
обращения к нему во время выполнения программы имя также не нужно. Все 
определяется постоянной, следующей за командой message. Точно так же можно 
объявлять любой параметр, лишь бы это был параметр var с объявленным типом или 
без него. В одном из потомков класса, в котором определен метод 
Handle_WM_Paint_Message, можно заменить обработчик сообщений WM_PAINT, 
применяемый по умолчанию, новым. Для этого объявляется новый обработчик с тем 
же номером динамического индекса: 

    Procedure Paint_It_This_Way(var AMessage: TWMPaint); message WM_PAINT; 

    Однако вызов конкретного метода зависит от фактического значения 
параметра. Когда наступает момент обработки сообщения, выбор нужного метода 
происходит в методе Dispatch (var Message), наследуемом всеми классами TObject. 
    Метод TObject.Dispatch вызывает метод обработки сообщений объекта на 
основе первых двух байтов фактического аргумента, тип которого не определен. 
Эти байты интерпретируются как целое значение, используемое затем в качестве 
динамического индекса. Методу Dispatch можно передавать любую информацию, но в 
большинстве сообщений, рассылаемых Windows, содержатся довольно специфичные 
сведения. 
    Адреса динамических методов и обработчиков сообщений каждого класса 
заносятся в таблицу динамических методов. Адреса обработчиков вносятся вместе 
с индексами. TObject.Dispatch ищет в разделе таблицы, соответствующем 
фактическому классу объекта, заданный индекс сообщения. Если в классе 
обработчик с таким индексом не определен, поиск выполняется в классе-предке, 
и т.д. 
    Методу TObject.Dispatch заранее неизвестно, может ли объект обработать 
то или иное сообщение. Можно, например, создать класс, способный обрабатывать 
сообщения небольшого количества индексов. Не найдя обработчика для сообщения с 
индексом, TObject.Dispatch вызывает метод DefultHandler объекта. 
    Часто возникает необходимость модифицировать наследуемые методы без полной 
их замены. Модификация может сводиться к добавлению нескольких операторов 
перед наследуемым методом или после него. Вызвать из обработчика наследуемый 
метод не составляет труда, даже если его имя и тип аргумента неизвестны. Это 
делается с помощью команды inherited. 
    Обработчик Paint_It_This_Way, например, можно было бы реализовать 
следующим образом. 

    procedure  Paint_It_This_Way(var AMessage: TWMPaint); 
    begin 
      with AMessage  do 
        begin 
          ...   //  Подготовка  к вызову фактического  обработчика, 
          inherited;   //  Вызов TMyAncestor.Handle_WM_Paint_Message (AMessage). 


    Глава 4. Object Pascal: углубленное изложение 87 


         ...// Дополнение к фактическому обработчику. 
        end; 
    end; 

    Механизм вызова наследуемого обработчика тот же, только поиск в таблице 
динамических методов начинается с раздела предка. Если ни в одном предке не 
определен обработчик с индексом WM_PAINT, вызывается DefultHandler. 

    Abstract 

    Обычно, встретив в объявлении класса определение метода, компилятор 
предполагает найти в разделе реализации модуля соответствующую процедуру или 
функцию. Если метод определяется в базовом классе, то задается установленное 
по умолчанию поведение всех потомков данного объекта в некоторых условиях. Чтобы 
изменить поведение потомка, данный метод переопределяется. Если поведение по 
умолчанию лишено смысла, соответствующее определение метода помечается 
командой abstract, указывающей, что реализация данного метода отсутствует: 

    type 
      TMYParent = class 
       procedure AMethod; virtual; abstract; 
       end; 
     TMYClass = class(TMYParent) 
       procedure AMethod; override; 
       end; 

    procedure TMYClass.AMethod; 
    begin 
      ... // Операторы 
    end; 

    Объявление метода как абстрактного — это сообщение компилятору о том, что 
он должен быть переопределен в классах потомках. Компилятор генерирует 
правильный код поиска фактической реализации. Если же абстрактный метод 
случайно оказывается непереопределенным, вызывается процедура Abstract, 
генерирующая ошибку или исключительную ситуацию. 
    Поскольку абстрактные методы обязательно переопределяются, абстрактными 
можно объявлять виртуальные и динамические методы, но не статические. Еще одно 
практически очевидное условие: абстрактный метод нельзя вызывать из 
классов-потомков. В остальном абстрактные методы ничем не отличаются от других. 

    Override 

    Команда override задает переопределение виртуальных и динамических 
методов. Рассмотрим следующий пример. 

    type 
      TMYParent = class 
        procedure AMethod; virtual; abstract; 
      end; 
      TMYClass  =  class(TMYParent) 
        procedure AMethod; override; 
      end; 

    Здесь определены два класса (TMYParent и TMYClass) и два метода. Процедуры 
TMYParent.AMethod и TMYClass.AMethod должны быть определены ниже. Переменной 
TMYParent (см. пример) можно присвоить объект типа TMYParent или TMYClass: 

    Var AnObject: TMYParent; 

    При обращении к AnObject.AMethod вызывается TMYParent.AMethod или 
TMYClass.AMethod. в зависимости от фактического типа объекта, на который 
ссылается переменная AnObject. 
    Это происходит в связи с заменой старого метода новым, задаваемой командой 
override. Параметры нового метода должны точно совпадать с параметрами метода, 
предшествующего по порядку следования и типу. Если старый метод — функция, 
новый должен быть функцией того же типа. 
    Переопределять можно как виртуальные, так и динамические методы. Обратите 
внимание на то, что переопределение задается с помощью команды override, а не 
dynamic или virtual. Разница между этими командами почти незаметна. 
Рассмотрим следующий пример. 

    type 
      TMYOtherClass = class(TMYParent) 
         procedure AMethod; virtual; 
	end; 


    88 Часть I. Основные положения 

    В этом случае компилятор рассматривает AMethod как совершенно новый метод, 
которому почему-то присвоено то же имя, что и его предшественнику. Переменные 
TMYOtherClass (и последующих) не видят наследуемого метода AMethod, и в таком 
случае всегда происходит обращение ко второму методу. Переменные же TMyParent 
не видят нового метода и обращение всегд а происходит к исходному 
TMyParent.AMethod. 

    Методы классов 

    Исходное назначение методов — определять поведение экземпляров объектов 
конкретного класса. В некоторых случаях поведение, задаваемое для класса, не 
зависит от реально существующего образца объекта. Методы, объявленные с 
помощью команды class, задают поведение объектного типа таким образом, как 
если бы тип был неким параметром, не связанным с образцом того или иного 
объекта. Объекта вообще может не быть. Поэтому в методах классов нельзя 
использовать никаких переменных, представляющих собой поля или свойства объекта. 
    Чтобы объявить метод класса, надо перед ключевым словом procedure или 
function, с которого начинается определение метода, вставить зарезервированное 
слово class. Компилятор будет трактовать этот метод иначе, чем методы, 
определяющие поведение экземпляров класса (объектов). 
    В случае использования других методов параметр Self применяется неявно в 
качестве локальной переменной того же типа, что и класс, в котором определен 
метод. Этой переменной присваивается фактический образец объекта. В случае 
использования методов class смысл параметра Self стзновтся совсем иным. Теперь 
это уже не переменная объектного типа, а переменная типа ссылки на класс. 
Конкретный тип по-прежнему зависит от типа класса, в котором определен метод. 
С помощью параметра Self можно обращаться к конструкторам и другим методам класса. 
Методы классов можно вызывать независимо от того, существует ли объект. При этом 
необходимо указать тип объекта (например, TMyClass.AClassMetod) или сам объект
(AnObject.AClassMetod). 

    Приведение типа 

    На операции с переменной определенного типа компилятор обычно налагает 
ограничения, разрешая выполнение только тех операций, которые характерны для 
указанного типа данных. Иногда компилятор автоматически осуществляет 
приведение типов, например при присвоении целого значения действительной 
переменной. Тем не менее обычно приходится довольствоваться объявленным типом, 
даже если точно известен фактический тип данных и нужно воспользоваться 
некоторыми его особенностями. 
    Delphi предоставляет несколько способов применения подобных переменных. 
Есть относительно надежные операции с типами is и as. Однако при использовании 
механизмов явного задания типа ответственность за соответствие типов 
перекладывается на программиста. 

    Is и As 

    В операции is определяется, принадлежит ли данный объект указанному типу 
или одному из его потомков. Выражение, представленное в следующем примере, 
возвращает True, если переменная AnObject ссылается на образец объектного 
типа TMyClass или одного из его потомков: 

    AnObject is TMyClass 

    Очевидно, что, если выражение возвращает True, переменная AnObject 
совместима относительно операции присваивания с переменными типа TMyClass. 
    Сама по себе is не является операцией задания типа. В ней лишь проверяется 
совместимость объектных типов. Для корректного приведения типа объекта 
применяется операция as: 

    With AnObject as TMyClass do... 

    Использование следующей операции является менее надежным способом неявного 
приведения типа: 

    With TMyClass(AnObject)  do... 

    В операции as сперва проверяется совместимость типов с помощью операции 
is. Если типы несовместимы, запускается обработчик исключительной ситуации 
EInvalidCast. Таким образом, в конструкции as операция явного приведения типа 
оказывается заключенной в безопасную оболочку. В развернутом виде это может 
выглядеть следующим образом. 

    if AnObject is TObjectType then 
       with TObjectType(AnObject) do... 
    else 
       raise EInvalidCast.Create('Неправильное приведение типа'); 


    Глава 4. Object Pascal: углубленное изложение 89 


    Явное приведение типа 

    Явное приведение типа — средство изменения типа выражения или переменной. 
Неправильное применение этого средства опасно, примененное же к месту, оно 
позволяет избежать трудоемкого преобразования данных. Приведение типов 
происходит в двух формах: для значений и для переменных. 
    При выполнении операции приведения типа для значения происходит изменение 
типа выражения. Однако не для всякого выражения можно задать любой новый тип. 
Старый и новый типы должны быть порядковыми или указательными. В классическом 
языке Pascal определены две функции, действие которых эквивалентно заданию 
типа для значения (Ord (X) и Chr (X)). Ord ( X) преобразует любое порядковое 
выражение в значение Integer, a Chr (X) — целое выражение в значение Char. 
Задание типа для значения распространяет эту операцию на другие порядковые 
типы, а также на указательные. Нет необходимости вызывать какую-либо функцию, 
достаточно указать новый тип. Integer (X) преобразует любое порядковое 
выражение в целое, a Char (X) преобразует порядковое или указательное 
выражение в выражение типа char. 
    Размерности значений старого и нового типов могут не совпадать, и значение 
нового типа будет зависеть от того, будет его размер больше, меньше или равен 
размеру значения исходного типа. Если размеры равны, значение не будет 
меняться физически, а только интерпретироваться по-новому. Если размер нового 
типа меньше, в преобразованном значении старшие байты отсекаются, если больше 
— добавляются с сохранением знака. 
    При выполнении операции приведения типа для переменных размер не меняется. 
Содержащиеся в переменной данные интерпретируются по-новому, но их физическое 
представление всегда остается прежним. 

    Типы ссылки на класс 

    В классическом языке Pascal тип переменной существовал только во время 
компиляции программы как правила обращения с теми или иными переменными. Эти 
правила не могли изменяться во время выполнения программы. В Object Pascal 
введен структурный механизм работы с объектными типами в форме типа ссылки на 
класс следующего формата: 

    Class of TMyClass 

    С помощью переменных этого типа можно обращаться к объектным типам во 
время выполнения программы. Тип ссылки на класс — это не то же самое, что 
объектный тип (тип класса), в котором определяются поля, методы и свойства 
образцов данного класса. Для данного типа объектный тип — это значение, 
которое можно записать в переменную типа ссылки на класс. 
    Во время выполнения программы в разных ее участках переменной этого типа 
можно присвоить в качестве значения ряд объектных типов. После этого к 
присвоенному типу можно обращаться почти так же, как к типу, объявленному 
статически. Правда, именно почти, ибо это все же не вполне аналогично обычному 
указанию объектного типа. Тем не менее в некоторых простых ситуациях типы 
ссылки на класс оказываются полезными. Обычно они применяются для вызова 
виртуального конструктора неизвестного класса, чтобы создать экземпляр объекта 
неизвестного класса. Определив методы классов для некоторого класса, можно 
вызывать нужный метод посредством обращения к классу, не зная, что это за 
класс. По существу, правый операнд операции as или is является значением 
класса или, что то же самое, объектным типом. 
    Рассмотрим следующее объявление. 

    type 
      TParent = class 
      end; 
      TParentClass = class of TParent; 
      TChild = class(TParent) 
      end; 
      TChildClass  = class  of TChild; 
   var 
     AParent, AParent2: TParent; 
     AChild,  AChild2:  TChild; 
     AParentClass: TParentClass; 
     AChildClass: TChildClass; 

    В данном примере TParentClass объявлен как тип ссылки на класс TParent (и 
любого из его потомков), TChildClass — как тип ссылки на класс TChild (и, 
опять же, любого из его потомков). 
    Идентификаторы класса действуют как постоянные значения соответствующих 
типов ссылки на класс. Например, идентификатор TParent, кроме всего прочего, 
действует как значение типа TParentClass, a TChild — как TChildClass. 

    90 Часть I. Основные положения 

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

AParentClass:=TParent; 	//  Классу TParent можно присвоить тип TParent.
AParentClass:=TChild;   //  Классу TParent можно присвоить тип потомка TParent. 
AChildClass:=TChild;    //  Классу  TChild можно присвоить тип TChild. 


    Ниже приведен пример неправильного оператора. 

    AChildClass:=TParent; // Ошибка, TParent нельзя присваивать классу TChild. 

    Последний оператор вызовет ошибку, поскольку класс TParent не является 
потомком класса TChild и, следовательно, данные этого типа нельзя записывать в 
переменную типа TChildClass. 
    По аналогии с соответствующими объектными типами разрешено присвоение 
значения ссылки на класс переменной типа ссылки на тот же класс или на его 
потомка. Единственная распространенная ошибка — попытка присвоить объектный 
тип переменной типа ссылки на класс-потомок, как в следующем примере. 

    AParent:=AParent2; //   Верно. 
    AParent:=AChild;   //   Верно. 
    AChild:=AChild2;   //   Верно. 
    AChild:=AParent;   //   Неверно, однако AChild := AParent as    
                       //   Child работает, если переменной Aparent 
                       //   присвоен тип TChild или его потомок. 

    Все классы наследуют из TObject метод-функцию ClassType. Функция 
TObject.ClassType возвращает результат типа ссылки на класс TClass, объявленный 
как TObject. Однако, как следует из последнего примера, значение TClass 
можно записать только в переменную этого же типа. Чтобы все-таки выполнить 
присвоение, придется задать тип. 

    if AParent is TChild then 
      AChildClass:=TChildClass(AParent.ClassType) 
    else 
      AChildClass:=nil; 


    Библиотека подпрограмм 

    Первое, что бросается в глаза при знакомстве с интерфейсом Delphi, — это 
визуальные компоненты. Они чрезвычайно важны для быстрого программирования, 
однако для начинающих программистов оказывается неожиданным то, что они не 
входят в Object Pascal. Визуальные компоненты подробнее будут рассмотрены в 
конце книги. А сейчас займемся невизуальными подпрограммами, составляющими 
основу Delphi. Большинство подпрограмм библиотеки также не является составной 
частью Object Pascal. К тому же подпрограммы библиотеки Delphi — это уже 
далеко не тот спартанский набор процедур и функций, который ввел в Pascal еще 
Вирт. Некоторые из подпрограмм рассматривались в данной и предыдущей главах. 
Однако и для описания того, что осталось, нескольких страниц вовсе не 
достаточно. Имеется еще ряд процедур и функций, не освоенных пока новичками 
Delphi. Впрочем, некоторые из них остались незамеченными даже опытными программистами. 
    Библиотека подпрограмм (RTL) Delphi состоит из трех независимо 
компилированных модулей: System, SysUtils и Math. Как и в обычных модулях, 
которые пишут программисты, в них объявлены наборы постоянных, типов, 
переменных и подпрограмм, к которым можно обращаться из других модулей, если 
указать упомянутые выше наборы в условии uses. 

    Модуль System 

    Модуль System — наиболее важная часть библиотеки подпрограмм Delphi. В нем 
реализованы подпрограммы низкого уровня, используемые компилятором в качестве 
своих внутренних средств. Среди них — средства ввода-вывода, обработки длинных 
и коротких строк, фундаментальные подпрограммы операций с действительными 
значениями и динамического распределения памяти. 
    Модуль System — неотъемлемая часть Object Pascal, поэтому нет необходимости 
указывать его в разделе uses. Он подключается автоматически. 

    Глава 4. Object Pascal: углубленное изложение 91 


    Типы 

    В модуле System определены некоторые полезные типы данных. Один из 
простейших — TDateTime, объявленный как особая разновидность типа с плавающей запятой: 

    Type TDateTime = Double; 

    Такое повторное объявление сделано для того, чтобы отделить переменные, 
содержащие дату и время, от обычных действительных переменных. Более сложные 
типы данных, необходимость в которых может возникнуть, обеспечивают доступ к 
типам данных на низком уровне. Например, в типе низкого уровня TVarData 
установлена внутренняя структура определенного в языке типа Variant, в 
аналогичном типе TVarRec — структура переменных, передаваемых подпрограммам в 
качестве формальных параметров array of const. 

    Переменные 

    В модуле System определен ряд важных глобальных переменных. Одни из них 
объявлены как var, другие — как постоянные с типом. 

    CmdLine: PChar=nil; 	// Указатель  на  командную строку. 
    CmdShow: Integer=0; 	// Параметр  ShowWindow. 
    FileMode: Byte=2;   	// Режим открытия файла по 
    					//  умолчанию (чтение-запись). 
    Hinstance: longint=0;  	// Дескриптор данного образца  в  среде Windows. 
    InOutres: Integer=0;	// Буфер результата ввода-вывода. 
    Input: Text;     		// Стандартный файл ввода. 
    IsConsole: Boolean; 	// Возвращает True, если модуль   
    					//  консольная программа. 
    Null: Variant=nil; 		// Возвращает True, если к 
    					//  вариантной переменной не обращались. 
    Output: Text;  		// Стандартный файл вывода. 
    RandSeed: longint=0; 	//  Исходное значение для 
    					//   генератора случайных чисел. 
    Unassigned: Variant; 	//  Возвращает True, если  вариантной 
					//   переменной не  присвоено  значение.
 
    Доступ к большей части этих переменных возможен только из специально для 
этого предназначенных библиотечных подпрограмм. К некоторым все же возможен 
непосредственный доступ. Например, обращение к переменной InOutRes происходит 
непосредственно, через функцию lOResult. К файловым переменным Input и Output 
возможен непосредственный доступ. 

    Подпрограммы 

    Кроме стандартных подпрограмм, упомянутых в предыдущей главе, в модуле 
System содержится ряд процедур и функций, к которым можно обращаться 
непосредственно. Вот некоторые из них. 

    Подпрограмма    Результат 

    BlockRead(F, Buffer,  	Считывает не более Count байтов из файла 
      Count, Res)			без типа F в переменную Buffer. Тип пе ременной Buffer не 
					имеет значения, но ее объема должно быть достаточно для 
					размещения Count байтов. Фактическое число считанных байтов 
					возвращается в целой переменной Res 

    BlockWrite(F, Buffer, 	Записывает не менее Count байтов в файл без 
      Count, Res)			типа F из переменной Buffer. Фактиче- ское число записанных 
					байтов возвращается в целой переменной Res 

    ChDiг(S} 			Делает каталог S текущим 

    GetDir(N, S) 			При N=0 заносит в переменную S имя текущего каталога. При 
					N=1. . 26 заносит текущий каталог диска, определенного 
					параметром N (от А: до Z :) 

    MkDir(S) 			Создает новый каталог 

    ParamCount  			Возвращает число параметров, переданных программе в командной 
					строке 

    ParamStr(N)  			Возвращает N-й параметр командной строки. При N >ParamCount 
					возвращает пустую строку. При N=0 возвращает путь и имя 
					ехе-файла выполняемой программы 


    92 Часть I. Основные положения 


    Подпрограмма   Результат 

    Random 		Возвращает случайное число. Без параметров Random возвращает 
			действительные значения, псевдослучайным образом распределенные между 
			0 и 1. Random(N), где N — целое число, возвращает целые значения, 
			распределенные псевдослучайным образом между 0 и N-1 

    Randomize  	Задает случайное исходное значение для встроенного генератора случайных 
			чисел. Обычно это значение берется из системных часов. Необходимости 
			вызывать процедуру Randomize во время выполнения программы более одного 
			раза не возникает. Более того, слишком частый ее вызов может привести к 
			тому, что результаты выполнения функции Random будут недостаточно равномерно 
			распределены 

    RmDir (S) 	Удаляет каталог с именем S 


    Модуль SysUtils 

    Если модуль System является основой Delphi, то SysUtils представляет собой 
существенное ее расширение. В нем определены классы обработки исключительных 
ситуаций, дополнительные процедуры для работы со строками, процедуры даты и 
времени, а также множество утилит. При создании новой формы в среде 
разработчика Delphi модуль SysUtils автоматически вносится в условие uses. В 
модулях, не содержащих формы, это приходится делать вручную. 

    Константы 

    В SysUtils объявлен ряд постоянных, что дает возможность использовать 
мнемонику, не запоминая численных значений. 

    Константы файловых атрибутов 

    Константы файловых атрибутов применяются для передачи файловых атрибутов 
подпрограммам в качестве параметров. 

    faReadOnly =  $00000001;	//  Файл  только  для чтения. 
    faHidden   =  $00000002;	//  Невидимый  файл. 
    faSysFile  =  $00000004;  //  Системный  файл. 
    faVolumelD =  $00000008;  //  Идентификатор  тома. 
    faDirectory = $00000010;  //  Каталог. 
    faArchive =   $00000020;  //  Архивный  файл. 
    faAnyFile =   $0000003F;  //  Соответствует  атрибутам любого  файла. 

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

    faAnyFile = faReadOnly or faHidden or faSysFile or faVolumelD 
                or faDirectory or faArchive; 


    Константы режима открытия файла 

    Постоянные режима открытия файла применяются для задания степени доступа к 
файлу или потоку после его открытия. Например, в классе TFileStream есть метод 
Create с параметром Mode, который можно задать с помощью следующих констант. 

    FmOpenRead   =  $0000;     //   Файл  только  для  чтения. 
    FmOpenWrite  =  $0001;     //  Файл  только для  записи. 
    FmOpenReadWrite = $0002;   //  Файл для чтения и  записи. 
    FmShareCompat   =  $0004;  //  Совместим с  открытыми по  FCB. 
    FmShareExclusive =  $0010; //  Закрыт для других. 
    FmShareDenyWrite =  $0020; //  Закрыт  для  записи другими. 
    FmShareDenyRead  =  $0030; //  Закрыт  для  считывания другими. 
    FmShareDenyNone  =  $0040; //  Открыт  для  совместного использования. 

    Режим открытия файла задается путем передачи соответствующего значения 
(константы) переменной FileMode, определенной в модуле System. 

    Глава 4. Object Pascal: углубленное изложение 93 


    Другие константы режима работы с файлом 

    В модуле SysUtils также определено несколько "магических" констант, неявно 
используемых программой, написанной на Object Pascal, для определения режима 
работы с файлом. 

    fmClosed  =  $D7BO; 
    fmlnput   =  $D7B1; 
    fmOutput  =  $D7B2; 
    fmlnOut   =  $D7B3; 


    Типы 

    В модуле SysUtils объявлено несколько полезных типов. Одни из них — 
простые повторные объявления вида 

    TFilename=string; 

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

    TFloatFormat =(ffGeneral, ffExponent, ffFixed, ffNumber, ffCurrency); 
    TFloatValue  =(fvExtended, vfCurrency); 

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

    •  TFileRec 
    •  TTextRec 
    •  LongRec 
    •  TFloatRec 
    •  THeapStatus 
    •  TMethod 
    •  WordRec 
    •  TSearchRec 

    Из перечисленных типов чаще всего используется TSearchRec. Переменные 
этого типа объявляются при поиске файлов и каталогов. 
    Все перечисленные типы — это разного рода записи, поля которых являются 
внутренними переменными. Например, в типах TFileRec и TTextRec определено поле 
Mode, в котором должно быть записано одно из "магических" значений файловых 
режимов. Любое другое значение этого поля означает, что файл не 
инициализирован процедурой Assign или AssignFile. 

    Объекты 

    В модуле SysUtils определены базовый тип Exception для обработки 
исключительных ситуаций и несколько его потомков. Исключительные ситуации 
подробно рассматриваются в главе 5, "Обработка исключительных ситуаций". 

    Переменные 

    Наиболее многочисленное семейство глобальных переменных, определенных в 
модуле SysUtils, применяется для форматирования даты и времени. Первоначально 
значения этим переменным присваиваются на основе содержимого системных 
регистров с помощью подпрограммы GetLocalelnfо из модуля Win32Api. Однако их 
можно изменить. Кроме того, изменение может произойти в ответ на сообщение 
WM_WININICHANGE. В неконсольных программах Delphi автоматическая модификация 
этих значений определяется состоянием свойства Application.UpdateFormatSettings. 
    В описании каждой из следующих ниже переменных указывается одна из 
постоянных LOCALE, определяющая начальное значение, задаваемое функцией 
GetLocalelnfо из модуля Win32Api. 

    Переменная    	Содержимое 

    CurrencyString 	Символ валюты, применяемый при преобразовании значения 
				с плавающей запятой в строку. LOCALE_SCURRENCY -  локальная 
				постоянная, используемая для получения информации из системных 
				регистров. Для долларов США равна '$' 
    CurrencyFormat      Форма указания символа валюты при преобразовании значения с 
				плавающей запятой в строку. 
				Возможны четыре значения: 
				0 = '$1'                                                                    
				1  = 1'$'                                                                  
				2 = '$1' 
				3 = '1$' 
				Для получения этой информации применяется постоянная 
				LOCALE_ICURRENCY 


    94 Часть I. Основные положения 


    Переменная	Содержимое 

    NegCurFormat	Формат валюты при преобразовании 
			отрицательного значения с плавающей запятой в строку. 
			Возможны шестнадцать значений: 
			0 = ($1) 	8 = -1$ 
			1 = -$1	9 = -$1 
			2 = $-1	10 = 1$-
			3 = $1-	11 = $1-
			4 = (1$)	12 = $-1 
			5 = -1$	13 = 1-$ 
			6 = 1-$	14 = ($1) 
			7 = 1$	15 = (1$) 

    Для первоначального получения этой информации применяется постоянная 
    LOCALE_INEGCURR 

    ThousandSeparator 	Знак для отделения тысяч в значениях более чем с тремя 
				цифрами, расположенными слева от десятичного разделителя. 
				LOCALE_THOUSAND. Для пользователей США — это, как правило, 
				запятая (,), для европейцев — точка (.) 
    DecimalSeparator	Знак, разделяющий целую и дробную части. 
				LOCALE_SDECIMAL. Для пользователей США — это, как правило, 
				десятичная точка (.), для европейцев — запятая (,). 
				Обратна постоянной ThousandSeparator

    CurrencyDecimals	Число знаков, расположенных справа от десятичной 
				запятой в сумме валюты. LOCALE_ICURRDIGITS. Для пользователей 
				США — это, как правило, 2, что соответствует центам как сотым 
				доллара DateSeparator  Знак для разделения года, месяца и дня 
				в дате. LOCALE_SDATE. В США чаще применяется обычная косая (/). 
				Международный стандарт — черточка (-) 
    ShortDateFormat 	Формат преобразования значения даты в короткую строку, 
				удобную для редактирования. В этом формате применяются DateSeparator и 
				спецификаторы форматаm, mm, d, dd, уу  и  уууу подпрограммы 
				FormatDateString.    LOCALE_SSHORTDATE     
    LongtDateFormat	Формат преобразования значения даты в длинную строку, удобную для 
				вывода на экран. LOCALE_SLONGDATE 
    TimeSeparator 	Знак разделения часов, минут и секунд при выводе значения 
				времени. LOCALE_STIME. Обычно применяется двоеточие (:) 
    TimeAMString		Суффикс значений времени от 00:00 до 11:59 в 12-часовом формате. 
				LOCALE_S1159. Среди распространенных значений — am, AM, A.M. 
    TimePMString   	Суффикс значений времени от 12:00 до 23:59 в 12-часовом формате. 
				LOCALE_S2359. Среди распространенных значений — pm,  PM, P.M. 
    ShortTimeFormat	Формат преобразования значения времени в короткую строку 
				с указанием только часов и минут. Значение по умолчанию зависит от 
				LOCALE_ITIME и LOCALE_ITLZERO. Среди распространенных значений — hhnn, 
				hh: nn и hh:nn  ampm 
    LongTimeFormat 	Формат преобразования значения времени в длинную строку с 
				указанием часов, минут и секунд. Значение по умолчанию также зависит 
				от LOCALE_ITIME и LOCALE_ITLZERO 
    ShortMonthNames 	Массив строк с сокращенными названиями месяцев. Элементы 
				этого массива используются при задании формата mmm в FormatDateTime. 
				LOCALE_SABBREVMONTHNAME. Обычный набор — Jan, Feb, Mar,..., Dec 
    LongMonthNames  	Массив строк с полными названиями месяцев. Элементы этого 
				массива используются при задании формата mmmm в FormatDateTime. 
				LOCALE_SMONTHNAME. Обычный набор — January, February,..., December 
    ShortDayNames  	Массив строк с сокращенными названиями дней недели. 
				Элементы этого массива используются при задании формата ddd в 
				FormatDateTime. LOCALE_SABBREVDAYNAME. Обычный набор — Sun, 
				Mon, Tue,..., Sat 
    LongDayNames 		Массив строк с полными названиями дней недели. Элементы этого 
				массива используются при задании формата dddd в FormatDateTime. 
				LOCALE_SDAYNAME. Обычный набор — Sunday, Monday, ..., Saturday 

    Глава 4. Object Pascal: углубленное изложение 95 


    Подпрограммы 

    Подпрограммы модуля SysUtils имеют самое широкое применение. Некоторые из 
них чрезвычайно просты, к< например, процедура Веер, генерирующая простой 
звук. Ниже рассказывается, какие подпрограммы можно найти в 3}sUtils. 

    Строковые функции 

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

    Функция 		Результат 

    AdjustLineBreaks(S)	Возвращает строку, оканчивающуюся символами CR/LF 
				(возврат каретки/новая строка), а не CR, LF или LF/CR 
    AnsiLowerCase(S)	Возвращает строку строчных символов, определяемых с 
				помощью установленного языкового драйвера 
    AnsiUpperCase(S)	Возвращает строку прописных символов, определяемых с 
				помощью установленного языкового драйвера LowerCase (S} 
				Возвращает строку, в которой все прописные буквы заменены строчными 
    Trim(S)			Возвращает строку, из которой удалены все пробелы в начале и 
				конце, а также управляющие символы 
    TrimLeft(S)		Возвращает строку, из которой удалены все пробелы в начале и 
				управляющие символы 
    TrimRight(S)		Возвращает строку, из которой удалены все пробелы в конце и 
				управляющие символы 
    Uppercase (3} 	Возвращает строку, в которой все строчные буквы заменены прописными 

    Ниже перечислены функции сравнения двух строк (31 и S2), возвращающие -1, если SKS2, 0, 
если S1=S2, и 1, если S1>S2: -1. 

    Функция 			Результат 

    AnsiCompareStr(S1, S2)	Возвращает сравнение, выполненное установленным языковым 
					драйвером с учетом регистра 
    AnsiCompareText(31, 32)	Возвращает сравнение, выполненное установленным языковым 
					драйвером без учета регистра 
    CompareStr(31, 32)		Сравнение с учетом регистра 
    CompareText(31, 32)		Сравнение без учета регистра 


    Функции для работы с каталогами 

    Некоторые функции для создания и удаления каталогов оказываются удобнее 
команд MkDir и RmDir. Вот примеры таких функций. 
      CreateDir(Dirnarae). Возвращает True, если каталог создан, иначе — 
False.  
      RemoveDir(Dirname). Возвращает True, если каталог удален, иначе — False. 

    Функции даты/времени 

    Есть функции, возвращающие дату, время или то и другое в следующем виде: 

         Date (возвращает текущую дату); 
         Now (возвращает текущие дату и время); 
         Time (возвращает текущее время). 

    Однако в большинстве случаев приходится сталкиваться с проблемой 
преобразования даты и времени в другие форматы (в частности, в строки) и 
обратно. Во всех функциях даты/времени используются переменные форматов 
Currency и Date/Time из модуля SysUtils. 

    Следующие функции преобразуют значение даты или времени в строку. 

    96 Часть I. Основные положения 

    Функция 		Результат 

    DateTimeToStr(DT) 	Преобразует значение фо рмата времени в строку 
    DateTimeToString(ResultStr,FormatStr, DT) Преобразует значение формата 
				времени в строку 
    DateToStr(DT)		Преобразует значение формата даты в строку 
    FormatDateTime 	Преобразует дату и время в заданный формат 
    TimeToStr 		Преобразует значение формата времени в строку 


    Следующие функции преобразуют значения строкового формата в тип TDateTime. 

    Функция 		Результат 

    StrToDate(S) 		Преобразует строку в формат даты 
    StrToDateTime(S) 	Преобразует строку в формат даты/времени 
    StrToTime(S) 		Преобразует строку в формат времени 


    Для разного рода преобразований предназначены следующие функции. 

    Функция 		Результат 

    DayOfWeek(DT) 	Возвращает целое значение, соответствующее дню недели в 
				параметре типа TDateTime: 1=Воскресенье, 2=Понедельник,..., 
				7=Суббота 
    DecodeDate		Декодирует указанную дату 
    DecodeTime		Декодирует указанное время 
    DateTimeToFileDate	Преобразует формат даты Delphi в формат даты DOS 
    EncodeDate(Yyyy,Mm, Dd)  Возвращает значение типа TDateTime 
    EncodeTime (Hour, Min, Sec, MSec) Возвращает значение типа TDateTime 
    FileDateToDateTime (Filedate) Преобразует формат даты DOS в формататы Delphi 


    Функции для работы с файлами и дисками 


    Следующие функции манипулируют закрытыми файлами. 

    Функция 			Результат 

    DeleteFile (Filename) 	Возвращает True, если указанный файл успешно 
					удален, иначе— False 
    FileAge(Filename) 		Возвращает возраст файла 
    FileCreate(Filename) 	Создает файл с указанным именем 
    FileGetAttr (Filename)  	Возвращает атрибуты файла 
    FileSetAttr (Filename, NewAttr) Устанавливает новые атрибуты файла 
    RenameFile(OldName, NewName) Присваивает файлу новое имя    


    Следующие функции проверяют наличие файла с указанным именем. 

    Функция 			Результат 

    FileExists(Filename)	Возвращает True, если указанный файл существует 
    FileSearch (Filename, DirList) Просматривает каталоги в поисках указанного файла 
    FindClose(SearchRec) 	Завершает серию FindFirst/FindNext 
    FindFirst(FileSpec, Attr, SearchRec) Просматривает каталог в поисках файла с 
					указанными именем и набором атрибутов 
    FindNext(SearchRec)		Находит следующий файл, имя и атрибуты которого соответствуют 
					заданн ым в FindFirst 


    Глава 4. Object Pascal: углубленное изложение 97 
    4 Использование Delphi 3. Специальное издание 


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

    Функция 				Результат 

    ChangeFileExt(FileName, FileExt) Изменяет расширение файла      
    ExpandFileName(FileName) 		Возвращает строку с полным именем файла, включая путь 
    ExpandUNCFileName(FileName)  	Возвращает строку с полным именем файла в 
						сети, включая путь 
    ExtractFileDir(FileName) 		Возвращает имя диска и каталог, в котором находится файл 
    ExtractFileDrive(FileName) 	Возвращает имя диска, на котором находится файл 
    ExtractFileExt(FileName)  	Возвращает расширение файла 
    ExtractFileName(FileName) 	Возвращает только имя файла 
    ExtractFilePath()  			Возвращает путь к указан ному файлу 

    Следующие функции работают с файлами на уровне опердшонной системы (не как с 
файловыми-переменными Objec Pascal). Очень важно различать оба способа работы 
с открытыми файлами. 

    Функция 			Результат 

    FileClose (FileHandle)    Закрывает указанный файл 
    FileGetDate (FileHandle)  Возвращает дату и время создания файла 
    FileOpen (FileName, Mode) Открывает указанный файл с указанным режимом доступа. 
					Возвращает дескриптор файла или -1 в случае ошибки 
    FileRead( FileHandle, Buffer, NumBytes) Считывает данные из указанного файла      
    FileSeek (FileHandle, Offset, Origin) Изменяет положение указателя в файле 
    FileSetDate (FileHandle, 			Устанавливает дату и время 
    NewAge)						создания файла 
    FileWrite( FileHandle, Buffer, NumBytes) Записывает данные в указанный файл 


    Следующие функции возвращают информацию об объеме диска. 

    DiskFree(Drive). Возвращает объем свободного пространства на диске. 
Значения параметра Drive: 0 — текущий диск, 1 — диск А:, 2 — диск В: и т.д. 
    DiskSize(Drive). Возвращает объем указанного диска. Значения параметра 
Drive: 0— текущий диск, 1 — диск А:, 2 — диск В: и т.д. 


    Подпрограммы преобразования значений с плавающей запятой 

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

    Подпрограмма 		Результат 

    FloatToDecimal 	Преобразует значение с плавающей запятой в десятичное представление 
    FloatToStrF 		Преобразует значение с плавающей запятой в строковое представление 
    FloatToStr(X) 	Преобразует значение с плавающей запятой в строковое представление 
    FloatToText 		Преобразует значение с плавающей запятой в десятичное представление 
    FloatToTextFmt 	Преобразует значение с плавающей запятой в десятичное представление 
    FormatFloat 		Форматирует значение с плавающей запятой с помощью строки формата, 
				заданной подпрограммой Format 
    StrToFloat(S) 	Преобразует строку в значение с плавающей запятой 
    TextToFloat 		Преобразует строку с нуль-окончанием в значение с плавающей запятой 
    IntToStr(X) 		Преобразует целое значение в строку 
    StrToInt(S) 		Преобразует строку в целое значение 


    98 Часть I. Основные положения 


    Подпрограммы строкового форматирования 

    В модуле SysUtils есть подпрограммы для форматирования строк. Многие 
специфические средства строкового форматирования обращаются к этим подпрограммам. 

    Подпрограмма 	Результат 
    FmtStr 		Форматирует последовательность аргументов 
    Format 		Форматирует последовательность аргументов и возвращает результат в форме 
			строки языка Pascal 
    FormatBuf 	Форматирует последовательность аргументов 
    StrFmt 		Форматирует последовательность аргументов 
    StrLFmt 	Форматирует последовательность аргументов и возвращает указатель 
			на буфер с результатом 


    Функции обработки строк с нуль-окончанием 

    В модуле Syslnt есть ряд подпрограмм для обработки исключительно строк с 
нуль-окончанием. Тип аргументов — PChar, а не String. Назначение большинства 
из этих подпрограмм — обеспечить совместимость с другими языками. 

    Функция 		Результат 

    StrAlloc(Size) 	Распределяет буфер для строки с нуль-окончанием 
				максимальной длины-1 
    StrBufSize(S) 	Возвращает максимальное число знаков, которые можно 
				разместить в буфере, распределенном с помощью StrAlloc 
    StrCat(SI, S2)	Дописывает строку 32 в конец строки S1 и возвращает 
				указатель на объединенную строку (52) 
    StrComp(Sl, S2) 	Сравнивает две строки 
    StrCopy(Sl, S2) 	Копирует строку 52 в S1 
    StrDispose (S) 	Удаляет строку из динамической памяти 
    StrECopy (S1, 52) 	Копирует одну строку в другую и возвращает указатель 
				на конец полученной строки 
    StrEnd(S) 		Возвращает указатель на конец строки 
    StrlComp (SI, S2) 	Сравнивает две строки без учета регистра 
    StrLCat(Sl, 52) 	Дописывает символы строки S 2 в конец строки S1 и 
				возвращает указатель на объединенную строку (52) 
    StrLComp(Sl, 52) 	Сравнивает две строки вплоть до максимальной длины 
    StrLCopy(Sl, S2) 	Копирует символы из одной строки в другую 
    StrLen (S) 		Возвращает число символов в строке с нуль-окончанием S 
    StrLIComp (SI, S2)  Сравнивает две строки вплоть до максимальной длины без учета регистра 
    StrLower (S) 		Заменяет прописные буквы в строке строчными 
    StrMove(Sl, S2) 	Копирует символы из одной строки в другую 
    StrNew (S) 		Распределяет строку в динамической памяти и возвращает ее адрес 
    StrPas (S) 		Преобразует строку с нуль-окончанием в строку языка Pascal. 
				Возвращает значение String 
    StrPCopy(Sl, S2) 	Копирует строку языка Pascal S2 в строку с нуль-окончанием S1 
    StrPLCopy (SI, S2,  Копирует максимум MaxLen символов из строки языка 
    MaxLen) 		Pascal SI в строку с нуль-окончанием S2 
    StrPos(SI, S2) 	Возвращает указатель на первое вхождение строки S2 в 
				строку S1 
    StrRScan(S, С) 	Возвращает указатель на последнее вхождение знака в строку 
    StrScan(S, С) 	Возвращает указатель на первое вхождение знака в строку 
    StrUpper(S) 		Заменяет строчные буквы в строке прописными 


    Глава 4. Object Pascal: углубленное изложение 99 


    Модуль Math 

    В модуле Math есть множество подпрограмм, которые иногда можно встретить и 
в других языках. К тому же в Math определены новые обработчик исключительной 
ситуации EInvalidArgument и тип TPaymentTime, применяемый в некоторых 
финансовых подпрограммах. Однако основное достоинство модуля Math состоит в 
обширном наборе полезных вычислительных функций. 

    Элементарные функции 

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

    Функция 	Результат 

    Ceil(X) 	Целое число, которое больше или равно X 
    Floor(X) 	Целое число, которое меньше или равно X 
    Hypot(X, Y) 	Гипотенуза прямоуго льного треугольника с катетами X и Y 
    Log2(X) 	Двоичный логарифм X 
    LoglO(X) 	Десятичный логарифм X 
    LogN (X) 	Натуральный логарифм X (с основанием "е") 
    Power (X, Y)  X в степени Y 
    RandG (Mean, Variance)  Нормальное распределение случайной 
					величины с заданными средним и дисперсией 

    Тригонометрические функции 

    В классическом языке Pascal определены три тригонометрические функции, 
имеющие отношение к решению задач, связанных с прямоугольным треугольником: 
Sin, Cos и ArcTan. Программистам, разбирающимся в математике, этого было 
достаточно для получения всех прочих тригонометрических результатов. В модуле 
Math определен широкий набор тригонометрических функций, включающий обратные 
функции, гиперболическую тригонометрию и соотношения между различными 
единицами (радианами, градусами, градиентами и оборотами). 

    Функция 	Результат 

    ArcCos(X) 	Угол в радианах, косинус которого р авен X 
    ArcCosh(X) 	Функция, обратная гиперболическому косинусу X 
    ArcSin(X)  	Угол в радианах, синус которого р авен X 
    ArcSinh(X) 	Функция, обратная гиперболическому синусу X 
    ArcTan2(Y, X)	Угол в радианах, тангенс которого равен Y/X, с определением квадранта 
    ArcTanh(X) 	Функция, обратная гиперболическому тангенсу X 
    Cosh(X) 	Гиперболический косинус X 
    Cotan(X)	Котангенс X, радиан 
    CycleToRad(X)	Число радиан в X оборотах 
    DegToRad(X)	Число радиан в X градусах 
    GradToRad(X)	Число радиан в X градиентах (1 оборот = 400 градиентов) 
    RadToCycle(X)	Число оборотов в X радиан 
    RadToDeg(X)	Число градусов в X радиан 
    RadToGrad(X)	Число градиентов в X радиан (1 оборот = 400 градиентов) 
    Sinh(X) 	Гиперболический синус X 
    Тап(Х) 		Тангенс X радиан 
    Tanh(X) 	Гиперболический тангенс X 


    100 Часть I. Основные положения 

  
    Финансовые подпрограммы 

    Финансовые функции в Delphi могут решать уравнение кассовых потоков для 
заданных сумм либо вычислять амортизацию. Не следует слишком увлекаться ими, 
так как можно легко потёрять знак. 

    DoubleDecliningBalance                                                     
    NumberOfPeriods 
    FutureValue 
    Payment 
    InterestPayment 
    PeriodPayment 
    InterestRate 
    PresentValue 
    InternalRateOfReturn 
    SLNDepreciation 
    NetPresentvalue 
    SYDDepreciation 


    Статистические подпрограммы 

    Статистические подпрограммы принимают в качестве аргумента массив 
элементов с плавающей запятой. 

    MaxValue 
    PopnVariance 
    Mean 
    StdDev 
    MeanAndStdDev 
    Sum 
    MinValue 
    SumOfSquares 
    MomentSkewKurtosis 
    SumAndSquares 
    Norm 
    TotalVariance 
    PopnStdDev 
    Variance 


    Математические подпрограммы 

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

    Подпрограмма 	Результат 

    Frexp 		 Раскладывает значение с плавающей запятой на составляющие 
    IntPower(X, N) Вычисляет значение X в целой степени N 
    Ldexp 		Образует значение с плавающей запятой из составных частей 
    LnXPl(X) 	Вычисляет натуральный логарифм от Х-1 
    SinCos        Одновременно вычисляет синус и косинус значения 

    Резюме 

    Визуальные компоненты в Delphi можно считать результатом непосредственного 
применения элементов языка Object Pascal. 
    При обращении к форме в интегральной среде разработчика появляется пустая 
форма типа. Delphi автоматически строит костяк экземпляра объекта, потомка 
класса ТForm. Это можно сразу же увидеть в интерфейсе модуля (ему 
присваивается имя Unitl), в котором объявляется форма: 

    Type TForml = class(TForm) 

    Как потомок класса TForm пустая форма наследует все поля, методы и 
свойства класса. Класс TForm определен в модуле Forms. Как и в случае 
использования любого другого модуля, чтобы обратиться к определению типа в 
Forms, надо указать его имя в условии Uses. Delphi при создании новой формы 
делает это автоматически, так что в разделе интерфейса модуля сразу можно 
увидеть: Uses   ...,   Forms,    ...; 
    В разделе интерфейса модуля Forms класс TForm определен как потомок еще 
одного класса — TScrollihgWinControl. Класс TForm наследует все поля, методы и 
свойства предка, однако и собственных у него определено немало. В следующем 
объявлении показана лишь небольшая их часть. 

    Type TForm =  class (TScrollingWinControl) 
     private         
       FOnCreate: TNotifyEvent; 


    Глава 4. Object Pascal: углубленное изложение 101 


      function IsForm: Boolean; 
    published 
      property Caption  stored  IsForm; 
      property  OnCreate:   TNotifyEvent; 
        read  FOnCreate write  FOnCreate  stored  IsForm; 

    Здесь объявлены поле TNotifyEvent и статический метод-функция IsForm 
Boolean, применяемый при записи информации в ресурсный файл программы. Оба эти 
члена объявлены с помощью ключевого слова private, из чего следует, что к ним 
нельзя обращаться непосредственно. Но можно обращаться к двум свойствам 
(Caption и OnCreate), объявленным как published. 
    У класса TScrollihgWinControl также есть собственное определение, и, таким 
образом, по дереву классов можно дойти до базового класса TObject. 

    TScrollihgWinControl  =  class(TWinControl) //  В модуле  Forms. 
    TWinControl  =  class (TControl)    		//  В модуле  Controls. 
    TControl  =     class(TComponent)      	//  В модуле  Controls. 
    TComponent  =  class(TPersistent)           //  В модуле  Classes.
    TPersistent =  class(TObject)   		//  В модуле  Controls. 
    TObject  =  class   				//  В модуле  System. 

    Однако предки новой формы — не единственные типы, с которыми придется 
иметь дело. В модуле Classes определено еще два типа, необходимых форме, — 
TNotifyEvent и TComponentClass. 

    Туре 
      TNotifyEvent = procedure(Sender:   TObject) of object; 
	TComponentClass = class of TComponent; 

    Таким образом, TNotifyEvent представляет собой тип ссылки на метод 
некоторого класса, TComponentClass — тип ссылки на класс. Непосредственное 
применение типа TNotifyEvent связано со свойством OnCreate. Тип 
TComponentClass используется несколько иначе. 
    Очень важно помнить, что пустая форма, которой сразу присваивается имя 
TForm1, сама по себе является типом, а не экземпляром типа или переменной. Как 
тип, TForml предоставляет много возможностей, но они будут реализованы только 
после создания экземпляра этого типа. Объявление переменной Forml находится в 
разделе интерфейса модуля сразу за объявлением типа TForml: 

    var Forml: TForm; 

    Это уже намного лучше, но переменную еще надо инициализировать. Delphi и 
тут на высоте. В файле проекта (которому первоначально присваивается имя 
PROJECT1.DPR) появляется следующая строка: 

    Application.CreateForm(TForml, Forml); 

    Application — это переменная, определенная в интерфейсе модуля Forms. Имя 
этого модуля указано в условии Uses проекта. Переменная Application объявлена 
как образец типа TApplication: 

    var Application: TApplication;                                           

    Тип TApplication определен в модуле Forms как класс, содержащий множество 
полей, методов и свойств (и унаследованных от класса TComponent, и 
собственных). К ним относится и метод, непосредственное обращение к которому 
происходит при инициализации формы: 

    type TApplication = class(TComponent); procedure CreateForm( 
    InstanceClass:   TComponentClass;   var Reference); 

    Здесь класс TComponent — тот же, что и в классе TForm, и, естественно, 
имеет тех же предков. Таким образом, переменная Application наследует классы 
TPersisitent и TObject. 
    Meтод TApplication.CreateForm применяется для инициализации любого 
образца класса TComponent с двумя аргументами: значением TComponentClass и 
переменной любого типа. До своего создания новая форма не существовала. Тем не 
менее переменная Application знает, как создать образец этой формы, поскольку 
объектный тип последней передается в качестве аргумента.
    Пока что мы не внесли в новую форму ничего, что отличало бы ее от типа 
TForm. Можно добавить любое поле, метод или свойство, однако надо иметь в 
виду, что новый класс создается только для того, чтобы реагировать на 
некоторое заранее определенное событие несколько иначе, чем класс TForm. 
Например, после двойного щелчка мышью на форме в режиме проектирования Delphi 
автоматически добавляет в раздел определений новый метод вида 

    procedure  FormCreate(Sender:   TObject); 


    102 Часть I. Основные положения 

    и затем вносит в окно редактирования костяк реализации этого метода: 

    procedure  TForml.FormCreate(Sender:   TObject); begin 
    end; 

    Обратите внимание, что структура этого метода соответствует значению 
TNotifyEvent. И это не случайно. Delphi уже присвоила этот метод свойству 
OnCreate, задав тем самым все установки новой формы, необходимые для 
определения того, что должно произойти при ее создании. Например, можно 
воспользоваться особенностями свойства Caption и задать новый заголовок для формы: 

    Caption := '3дравствуй, мир!'; 

    И, конечно же, чтобы заменить заголовок формы при запуске программы, не 
нужно писать целый метод наподобие TForml.FormCreate. Поскольку Caption — это 
свойство, помеченное командой published и спецификатором памяти, его можно 
модифицировать непосредственно в объектном инспекторе Delphi. Однако из 
примера видно, как легко создать собственный обработчик событий. 
    Конечно же, сама природа Windows как системы, управляемой событиями, 
предполагает, что процесс программирования заключается, главным образом, 
именно в создании новых методов обработки событий в операционной среде. 
Программа готовится к реагированию на определенные ситуации, между которыми 
она спокойно бездействует. Чтобы переменная Application инициализировала все 
компоненты и передала управление пользователю, достаточно одного простого оператора: 

    Application.Run; 

    И все заработает! 
