Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Книга о KOL.doc
Скачиваний:
29
Добавлен:
30.04.2019
Размер:
1.77 Mб
Скачать

2.8. Сортировка данных

Для выполнения сортировки наиболее эффективный метод – это так называемый алгоритм Quick Sort. В библиотеке KOL имеется оптимизированная (и переведенная на ассемблер) версия этой функции, которая называется SortData. (А с версией 3.00 добавлена функция SortArray, которая обеспечивает чуть большее быстродействие для массивов и списков 4-байтных значений, таких как числа Integer или указатели строк в памяти). Для использования функции SortData необходимо задать 4 параметра: объект для сортировки (обычно, это какой-нибудь список или массив), количество элементов в списке, а так же функцию сравнения двух элементов и процедуру обмена двух элементов сортируемого массива. В качестве примера применения функций SortData и SortArray рекомендуется изучить реализацию функций SortIntegerArray и SortDwordArray, так же входящих в состав библиотеки.

2.9. Иерархия объектных типов в kol

2.9.1. Объекты _tObj и tObj.

Н

Схема 1. Вся иерархия объектов KOL.

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

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

Базовый объектный тип для всех объектов в KOL - это TObj. По некоторым причинам, позже (в версии 0.93 от 25.08.2001) был введен объектный тип _TObj, от которого унаследован TObj. Основная причина заключалась в том, что при каждой модификации типа TObj, у него смещался указатель на таблицу виртуальных методов vmt. Создание же полуфиктивного "предка" для TObj обеспечило постоянство поля vmt в структуре объекта - по смещению 0, и позволило создать функцию InstanceSize, возвращающую для любого объекта, унаследованного от TObj, размер структуры полей в памяти. Кроме того, при каждом обращении к vmt в этом случае код короче на 1 байт. Автором данной модификации является Вячеслав Гаврик, за нее ему несомненная благодарность.

Итак, что такое TObj (буду рассматривать его методы вместе с методами его предка _TObj). В какой-то степени это аналог класса TObject в VCL, и в то же время, его же можно рассматривать и как аналог TComponent. Практически все остальные объектные типы, за небольшим исключением, произведены непосредственно из TObj. В объекте _TObj определяется единственный (первый) виртуальный метод Init, и есть еще одна функция VmtAddr, и на этом перечень его методов заканчивается (полей у него своих нет). Поскольку _TObj - это вспомогательный объект, единственная цель определения которого - это уменьшение кода, то использовать его в программе непосредственно нет никакой необходимости.

Объект TObj уже посложнее, в нем уже появляется виртуальный деструктор Destroy (но вызывать следует всегда метод Free), в нем есть список fAutoFree (типа PList, кстати, наличие в TObj ссылки на PList уже означает, что, по крайней мере некоторые методы объектного типа TList будут включены в код любой KOL-программы, но я решил пойти на это, поскольку без списков вообще трудно что-либо сделать, т.е. список все равно попадет в код даже минимальной программы). fAutoFree - это список объектов, которые будут автоматически разрушаться вместе с данным, есть методы для добавления объектов для саморазрушения (Add2AutoFree и Add2AutoFreeEx) при выполнении деструктора. Тип TObj (а значит, и все объектные типы в KOL) имеет так же событие OnDestroy.

Есть поле Tag (да, в KOL оно определяется на самом нижнем уровне иерархии, т.е. любой объект в KOL имеет это поле априори).

Есть даже счетчик использования объекта, который позволяет предотвратить разрушение объекта, пока он еще кому-либо нужен: вызовами RefInc счетчик увеличивается, и для объекта с ненулевым счетчиком, вызов деструктора не приведет к каким-либо последствиям (кроме отметки о том, что деструктор был вызван). При уменьшении же счетчика использования вызовом RefDec до нуля проверяется, был ли вызван деструктор, и если был - объект разрушается, на этот раз - окончательно. Поле RefCount доступно для анализа из программы (младший бит этого поля используется как признак того, что вызывался деструктор, все прочие являются счетчиком, который при каждом вызове RefInc увеличивается на 2, и при каждом RefDec уменьшается на 2).

В самом KOL методы RefInc и RefDec применяются “на всякий случай” при обработке сообщений для визуального объекта ("всякий" случай заключается в том, что объект может быть уничтожен, пока обрабатывается какое-либо оконное сообщение для него, и тогда был бы почти неизбежен крах приложения). На самом деле, методы RefInc могут использоваться в многопоточных приложениях для защиты временных объектов, управляемых из различных потоков, на период активного использования на некотором участке кода.

Иногда возникает необходимость "одновременно" разрушить объект и обнулить (присвоить nil) указателю на этот объект. В VCL для этого существует функция FreeAndNi*l, в KOL для этой же цели функция называется Free_And_Nil. Причем, в этой функции сначала обнуляется переменная-указатель объекта, а уже затем объект разрушается (вызовом метода Free). Конечно, это почти эквивалентно тому, чтобы объект был сначала разрушен, а затем присвоен nil переменной-указателю. Но в многопоточном приложении разница может быть ощутима. Достаточно представить себе ситуацию, в которой объект был разрушен (или начал разрушаться, но операция еще не завершена), а указатель все еще не равен nil, и в этот момент потоки переключились, и в другом потоке так же начинают выполняться какие-то операции с этим же объектом через тот же самый указатель. Даже в случае однопоточного приложения тот факт, что некоторый глобальный указатель продолжает показывать на уже несуществующий объект, или на объект, для которого уже начала выполняться операция разрушения, представляет определенную опасность. Так что потребность в функции Free_And_Nil очевидна.

Кроме перечисленных свойств, в TObj имеется строковое поле Name, добавленное по многочисленным просьбам. Но это поле является опциональным, и компилятор узнает о том, что такое поле существует, только при включении в опции проекта символа условной компиляции Use_Names. В этом случае все именованные объекты запоминаются в списке родительского объекта, и могут быть найдены вызовом его метода FindObj( s ).

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