Logo Море(!) аналитической информации!
IT-консалтинг Software Engineering Программирование СУБД Безопасность Internet Сети Операционные системы Hardware

VPS в России, Европе и США

Бесплатная поддержка и администрирование

Оплата российскими и международными картами

🔥 VPS до 5.7 ГГц под любые задачи с AntiDDoS в 7 локациях

💸 Гифткод CITFORUM (250р на баланс) и попробуйте уже сейчас!

🛒 Скидка 15% на первый платеж (в течение 24ч)

Скидка до 20% на услуги дата-центра. Аренда серверной стойки. Colocation от 1U!

Миграция в облако #SotelCloud. Виртуальный сервер в облаке. Выбрать конфигурацию на сайте!

Виртуальная АТС для вашего бизнеса. Приветственные бонусы для новых клиентов!

Виртуальные VPS серверы в РФ и ЕС

Dedicated серверы в РФ и ЕС

По промокоду CITFORUM скидка 30% на заказ VPS\VDS

Глава 19. Наборы

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

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

             Было бы замечательно, если бы тип массива поставлялся вместе
        с кодом, обрабатывающего бы многие из тех операций, которые обыч-
        но выполняются с массивом.  Это был бы тип массива, который можно
        было бы расширять без нарушения первоначального кода. Все это яв-
        ляется целью создания типа ObjectWindows TCollection. Это объект,
        который хранит наборы указателей и обладает  набором  методов  по
        манипулированию ими.

                                   Объекты наборов
        -----------------------------------------------------------------

             Будучи объектами и тем самым имея встроенные методы,  наборы
        обладают двумя дополнительными чертами, которые имеют отношение к
        обычным массивам языка Паскаль - это динамическое установка  раз-
        меров и полиморфизм.


                       Динамическая установка размеров наборов
        -----------------------------------------------------------------

             Размер стандартного массива в стандартном Паскале фиксирует-
        ся во время компиляции.  Хорошо, если вы точно знаете, какой раз-
        мер должен иметь ваш массив,  но это может быть не столь хорошо к
        тому моменту, когда кто-нибудь будет запускать на вашу программу.
        Изменение размера массива требует изменения исходного кода и  пе-
        рекомпиляции.

             Однако, для наборов вы устанавливаете  только  их  начальный
        размер, который динамически увеличивается в процессе работы прог-
        раммы, для  размещения в нем всех нужных данных.  Это делает ваше
        приложение в его скомпилированном виде значительно более  гибким.
        Тем не менее, следует иметь в виду, что набор не может сжиматься,
        поэтому следует быть аккуратным  и  не  делать  его  неоправданно
        большим.

                                 Полиморфизм наборов
        -----------------------------------------------------------------

             Второй аспект,  по которому массивы могут ограничивать  ваше
        приложение,  состоит  в  том,  что  каждый элемент массива должен
        иметь один и тот же тип,  и этот тип должен  быть  определен  при
        компиляции  кода.

             Наборы обходят это ограничение использованием нетипизирован-
        ных указателей. Это сказывается не только на быстроте и эффектив-
        ности,  но наборы могут состоять из объектов (и даже не из объек-
        тов) разного типа и размера.  Набору не нужно знать  что-либо  об
        объектах,  которые он обрабатывает.  Он просто организует связь с
        ними в случае необходимости.

                                Проверка типа и наборы
        -----------------------------------------------------------------

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

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


                Объединение в набор элементов, не являющихся объектами
        -----------------------------------------------------------------

             Вы даже можете добавить в набор нечто, что вообще не являет-
        ся объектом,  но это также может явиться серьезным предметом оза-
        боченности.  Наборы ожидают получения нетипизированных указателей
        незаданного типа на нечто.  Но некоторые методы TCollection пред-
        назначены специально для работы с наборами элементов, производных
        от  TObject.  Это  касается  методов  доступа  к потоку PutItem и
        GetItem, и стандартной процедуры FreeItem.

             Например, это означает,  что вы можете хранить PChar в набо-
        ре,  но при попытке послать этот набор в поток,  результаты будут
        не столь успешными,  если вы не перепишете стандартные методы на-
        бора GetItem и PutItem.  Аналогично, при попытке освобождения на-
        бора будет сделана попытка удаления каждого  элемента  с  помощью
        FreeItem. Например, это делает TStrCollection.

             Если вам удастся преодолеть все эти трудности,  вы обнаружи-
        те,  что наборы (и построенные вами производные наборов) являются
        быстрыми, гибкими и надежными структурами данных.

                                   Создание набора
        -----------------------------------------------------------------

             Создание набора столь же просто, как и создание типа данных,
        которые вы хотите в нем хранить.  Предположим,  что вы - консуль-
        тант,  и вам нужно хранить и искать номер счета,  фамилию и номер
        телефона каждого из ваших клиентов. Сначала определим тип объекта
        клиента (TClient),  который будет хранится в наборе (не  забудьте
        определить тип указателя для каждого нового типа объекта):

             type
               PClient=^TClient;
               TClient=object(TObject)
                 Account, Name, Phone: PChar;
                 constructor Init(NewAccount, NewName, NewPhone: PChar);
                 destructor Done; virtual;
                 procedure Print; virtual;
               end;

             Затем реализуем методы Init и Done для размещения и удаления
        данных о клиенте и метод Print для отображения данных о клиенте в
        виде таблицы.  Обратите внимание,  что  поля  объекта  имеют  тип
        PChar, поэтому память выделяется только для той части строки, ко-
        торая действительно используется.  Функции  StrNew  и  StrDispose
        очень эффективно обрабатывают динамические строки.

             constructor TClient.Init(NewAccount, NewName,
                                      NewPhone: PChar);
             begin
               Account := StrNew(NewAccount);
               Name    := StrNew(NewName);
               Phone    := StrNew(NewPhone);
             end;

             destructor TClientDone;
             begin
                StrDispose(Account);
                StrDispose(Name);
                StrDispose(Phone);
             end;

             procedure TClient.Print;
             begin
               Writeln( ' ',
                Account, '':10 - StrLen(Account),
                Name, '':20 - StrLen(Name),
                Phone, '':16 - StrLen(Phone));
             end;

             TClient.Done будет автоматически вызываться для каждого кли-
        ента при удалении всего набора.  Сейчас вы просто инициируете на-
        бор для хранения ваших клиентов и вставляете в него записи о кли-
        ентах. Головное  тело  программы  (COLLECT1.PAS)  будет выглядеть
        следующим образом:

             var
               ClientList: PCollection;
             begin
               ClientList:=New(PCollection, Init(10,5));
               with ClientList^ do
               begin
                Insert(New(PClient, Init('91-100', 'Anders, Smitty',
                 '(406) 111-2222')));
                Insert(New(PClient, Init('90-167', 'Smith, Zelda',
                 '(800) 555-1212')));
                Insert(New(PClient, Init('90-177', 'Smitty, John',
                 '(406) 987-4321')));
                Insert(New(PClient, Init('90-160', 'Johnson, Agatha',
                 '(302) 139-8913')));
              end;
              PrintAll(ClientList);
              SearchPhone(ClientList, '(406)');
              Dispose(ClientList, Done);
             end.

                   Примечание: Процедуры  PrintAll  и  SearchPhone  будут
              рассмотрены позднее.

             Обратите внимание,  насколько  просто  было построить набор.
        Первый оператор размещает новый экземпляр  TCollection  с  именем
        ClientList с начальным размером на 10 клиентов.  В случае необхо-
        димости размещения более 10 клиентов в ClientList, его размер бу-
        дет увеличиваться каждый раз на 5 клиентов.  Следующие два опера-
        тора создают новый объект клиента и вставляют его в набор.  Вызов
        Dispose  в конце операции освобождает весь набор клиентов.

             Нигде не нужно было сообщать набору,  какой вид данных пред-
        полагается хранить - для этого просто используется указатель.

                                   Методы итератора
        -----------------------------------------------------------------

             Вставка и удаление элемента не являются единственными общими
        операторами набора.  Очень часто вы будете писать циклы  for  для
        просмотра всех объектов набора с целью отображения данных или вы-
        полнения некоторых вычислений.  В других случаях вы будете искать
        первый или последний элемент набора,  который удовлетворяет неко-
        торому критерию поиска.  Для этих целей у наборов имеется три ме-
        тода итератора:  ForEach,  FirstThat  и  LastThat.  Каждый из них
        воспринимает указатель на процедуру или функцию в качестве своего
        единственного параметра.

                                   Итератор ForEach
        -----------------------------------------------------------------

             ForEach воспринимает указатель на процедуру. Процедура имеет
        один  параметр,  который является указателем на хранимый в наборе
        элемент.  Для каждого элемента набора ForEach вызывает  процедуру
        один раз, в той последовательности, в которой элементы появляются
        в наборе.  Процедура PrintAll в Collect1 показывает пример итера-
        тора FoeEach.

             procedure PrintAll(C: PCollection);
              procedure CallPrint(P: PClient); far;
              begin
                P^.Print;    {Вызов метода Print}
              end;
             begin {Print}
               Writeln;
               Writeln;
               Writeln('Client list:');
               C^.ForEach(@CallPrint);  { распечатка для каждого клиента }
             end;

             Для каждого элемента набора, переданного в качестве парамет-
        ра  в  PrintAll,  вызывается   вложенная   процедура   CallPrint.
        CallPrint  просто  распечатывает  информацию об объекте клиента в
        отформатированных колонках.

                   Примечание: Итераторы должны вызывать локальные проце-
             дуры far.

             Вам нужно быть аккуратным с сортировкой процедур, которые вы
        вызываете итераторами. Для того, чтобы быть вызванной итератором,
        процедура (в данном примере, CallPrint) должна:

             * Быть  процедурой  - она не может быть функцией или методом
               объекта,  хотя данный пример показывает, что процедура мо-
               жет вызвать метод.

             * Быть локальной (вложенной) относительно вызывающей ее прог-
               раммы.

             * Описываться как дальняя процедура директивой far  или  ди-
               рективой компилятора {$F+}.

             * Воспринимать указатель на элемент набора в качестве своего
               единственного параметра.

                            Итераторы FirstThat и LastThat
        -----------------------------------------------------------------

             Кроме возможности  приложения  процедуры  к каждому элементу
        набора,  часто бывает очень нужно найти конкретный элемент набора
        на  основании  некоторого критерия.  Это является предназначением
        итераторов FirstThat и LastThat.  Как это следует из их имен, они
        просматривают набор в противоположных направлениях до момента на-
        хождения первого элемента набора,  который удовлетворяет критерию
        булевской функции, переданной в качестве элемента.

             FirstThat и  LastThat  возвращают  указатель  на первый (или
        последний) элемент, который удовлетворяет условию поиска. Предпо-
        ложим, что в приведенном ранее примере списка клиентов, вы не мо-
        жете вспомнить номер счета клиента или не помните точно написание
        имени клиента.  К счастью, вы точно помните, что это был ваш пер-
        вый клиент из штата Монтана.  Следовательно,  вы можете организо-
        вать  поиск первого клиента с кодом штата 406 (поскольку ваш спи-
        сок клиентов ведется хронологически). Данная процедура использует
        метод FirstThat, который и сделает всю работу:

             procedure SearchPhone(C: PCollection; PhoneToFind: PChar);
              function PhoneMatch(Client: PClient: PClient): Boolean;
                  far;
              begin
               PhoneMatch := StrPos(Client^.Phone, PhoneToFind) <> nil;
              end;
             var
               FoundClient: PClient;
             begin     { SearchPhone }
              Writeln;
              FoundClient := C^.FirstThat(@PhoneMatch);
              if FoundClient = nil then
               Writeln('Такому требованию не отвечает ни один клиент')
              else
              begin
                Writeln('Найден клиент:');
                FoundClient^.Print;
              end;
             end;

             Снова обратите внимание на то,  что PhoneMatch вложена и ис-
        пользует удаленную модель вызова. В этом случае эта функция возв-
        ращает  True  только при совпадении номера телефона клиента и за-
        данного образца поиска.  Если в наборе нет объекта, который соот-
        ветствовал  бы  критерию  поиска,  FirstThat возвращает указатель
        nil.

             Запомните: ForEach вызывает определенную пользователем  про-
        цедуру, а FirstThat и LastThat каждая вызывает определенную поль-
        зователем булевскую функцию.  В любом случае определенная пользо-
        вателем процедура  или функция передают указатель на объект набо-
        ра.

                                Отсортированные наборы
        -----------------------------------------------------------------

             Иногда вам бывает нужно, чтобы ваши данные были определенным
        образом отсортированы.  ObjectWindows имеет специальный тип набо-
        ра,  который  позволяет  вам упорядочить ваши данные произвольным
        образом. Это тип TSortedCollection.

             TSortedCollection является производным от TCollection и  ав-
        томатически сортирует задаваемые ему объекты.  При добавлении но-
        вого элемента он автоматически проверяет  набор  на  дублирование
        ключей.  Булевское поле Duplicates контролирует разрешение дубли-
        рования ключей.  Если для поля  Duplicates  установлено  значение
        False (по умолчанию),  то новый элемент добавляется к набору, за-
        меняя существующий член с тем же самым  ключом.  Если  Duplicates
        имеет значение True, то новый член просто вставляется в набор.

             TSortedCollection - это набор абстрактного типа. Для его ис-
        пользования вы должны сначала решить, какой тип данных вы собира-
        етесь собирать и определить два метода, отвечающих вашим конкрет-
        ным требованиям сортировки.  Для этого вам  нужно  создать  новый
        тип,  производный  от TSortedCollection.  В данном случае назовем
        его TClientCollection.  Ваш TClientCollection уже знает,  как де-
        лать  всю  реальную работу с набором.  Он может вставить (Insert)
        запись о новом клиенте и удалять (Delete) существующие  записи  -
        он  унаследовал эти основные черты поведения от TCollection.  Все
        что нужно сделать - это научить TClientCollection, какое поле ис-
        пользовать в качестве ключа сортировки и как сравнивать двух кли-
        ентов при решении вопроса о том, какой из них должен стоять в на-
        боре  выше  другого.  Это делается переписыванием методов KeyOf и
        Compare и реализации их следующим образом:

             PClientCollection = ^TClientCollection;
             TClientCollection = object(TSortedCollection)
               function KeyOf(Item: Pointer): Pointer; virtual;
               function Compare(Key1, Key2: Pointer): Integer; virtual;
             end;

             function TClientCollection.KeyOf(Item: Pointer): Pointer;
             begin
               KeyOf := PClient(Item)^.Account;
             end;

             function TClientCollection.Compare(Key1, Key2: Pointer):
                    Integer;
             begin
               Compare := StrIComp(PChar(Key1), PChar(Key2));
             end;

                   Примечание: Так  как  ключи являются нетипизированными
              указателями, для них нужно выполнять приведение типа.

             KeyOf определяет, какое поле или поля используются в качест-
        ве  ключей сортировки.  В данном случае это поле клиента Account.
        Compare воспринимает два ключа сортировки и определяет,  какой из
        них  должен  идти  первым  в соответствии с правилами сортировки.
        Compare возвращает -1,  0 или 1 в зависимости от того, Key1 мень-
        ше,  равен или больше Key2,  соответственно. В данном примере ис-
        пользуется сортировка по алфавиту (для букв  верхнего  и  нижнего
        регистра)  ключевой  строки (Account) путем вызова модуля Strings
        функции StrIComp.  Вы можете легко сортировать набор  по  именам,
        вместо  номера  счета,  если  замените возвращаемое KeyOf поле на
        Name.

             Обратите внимание на то, что ключи, возвращаемые KeyOf и пе-
        редаваемые в Compare являются нетипизированными указателями, поэ-
        тому  до  их разыменования и передачи в StrIComp в данном примере
        вы должны привести их тип к PChar.

             Это практически все,  что вам нужно определить! Теперь, если
        вы   переопределите   ClientList   как  PClientCollection  вместо
        PCollection (сменив объявление var и вызов New), то легко сможете
        распечатать ваших клиентов в алфавитном порядке (COLLECT2.PAS):

             var
               ClientList: PClientCollection;
                .
                .
             begin
               ClientList:=New(PClientCollection, Init(10,5));
                .
                .
             end.

             Обратите внимание и на то, как легко будет сменить сортиров-
        ку списка клиентов по номеру счета на сортировку  по  имени.  Все
        что  вам  нужно сделать,  это сменить метод KeyOf на возврат поля
        Account на поле Name.

                                     Наборы строк
        -----------------------------------------------------------------

             Многим программам  требуется  работать  с   отсортированными
        строками. Для этих целей ObjectWindows предоставляет набор специ-
        ального назначения   TStrCollection   (он   совпадает   с   типом
        TStringCollection, определенным для хранения строк Паскаля).  Об-
        ратите внимание,  что  элементы  TStrCollection - это не объекты.
        Они представляют  собой указатели на строки,  заканчивающиеся ну-
        лем. Поскольку наборы строк происходят от TSortedCollection, мож-
        но хранить и дублированные строки.

             Использовать наборы строк несложно.  Просто определяется пе-
        ременная указателя для хранения набора  строк.  Разместим  набор,
        задав  его начальный размер и приращение для роста при добавлении
        новых строк (см. COLLECT3.PAS):

             var
              WordList: PCollection;
              WordRead: PChar;
                .
                .
                .
             begin
              WordList:=New(PStrCollection, Init(10,5));
                .
                .
                .

             WordList первоначально  рассчитан  для  хранения  10 строк с
        последующим приращением по 5 строк.  Все что вам нужно сделать  -
        это вставить несколько строк в набор. В данном примере слова счи-
        тываются из текстового файла и вставляются в набор:

             repeat
                .
                .
                .
              if GetWord(WordRead, WordFile)^ <> #0 then
               WordList^.Insert(StrNew(WordRead));
                .
                .
                .
              until WordRead[0]=#0;
                .
                .
                .
              Dispose(WordList, Done);

             Обратите внимание, что функция StrNew используется для копи-
        рования считанных слов, и адрес скопированной строки передается в
        набор.  При использовании набора вы всегда передаете ему контроль
        над данными  набора.  Он позаботится об освобождении данных после
        работы. Он при этом делает то, что происходит при вызове Dispose:
        удаляется  каждый  элемент  набора, и  затем  удаляется сам набор
        WordList.

                               Пересмотренные итераторы
        -----------------------------------------------------------------

             Метод ForEach  просматривает весь набор,  элемент за элемен-
        том, и выполняет над каждым из них заданную процедуру. В предыду-
        щем  примере процедуре PrintWord передавался указатель строки для
        ее отображения.  Обратите внимание,  что процедура PrintWord вло-
        женная (или локальная).  Она работает в другой процедуре,  Print,
        которой передается указатель на TstrCollection.  Print использует
        метод  итератора ForEach для передачи каждого элемента своего на-
        бора в процедуру PrintWord.

             procedure Print(C: PCollection);
              procedure PrintWord(P: PChar); far;
              begin
                Writeln(P);                 { вывести строку }
              end;
             begin {Print}
               Writeln;
               Writeln;
               C^.ForEach(@PrintWord);      { вызов PrintWord }
             end;

             PrintWord должен  выглядеть как уже знакомая процедура.  Она
        просто берет указатель строки и передает  его  значение  Writeln.
        Обратите внимание  на  директиву  far  после  описания PrintWord.
        PrintWord не может быть методом, это просто процедура. Кроме того
        это должна быть вложенная процедура. Print надо рассматривать как
        некую оболочку вокруг процедуры,  которая выполняет некоторую ра-
        боту над каждым элементом набора (может быть отображает или моди-
        фицирует данные). Вы можете иметь несколько аналогичных PrintWord
        процедур,  но  каждая из них должна быть вложена в Print и должна
        быть дальней процедурой (использовать директиву far или {$F+}).

                               Нахождение элемента

             Отсортированные наборы (и следовательно наборы строк)  имеют
        метод Search,  который  возвращает  индекс  элемента с конкретным
        значением ключа. Но как найти элемент в неотсортированном наборе?
        Или когда  критерий  поиска  не использует сам ключ?  Конечно же,
        следует использовать FirstThat и LastThat.  Вы просто определяете
        булевскую  функцию  для проверки нужного вам критерия и вызываете
        FirstThat.

                                Полиморфические наборы
        -----------------------------------------------------------------

             Как вы уже видели,  что наборы могут динамически хранить лю-
        бой тип данных,  и они обладают множеством методов, которые помо-
        гают  вам организовывать эффективный доступ к данным.  В действи-
        тельности сам TCollection определяет 23 метода.  Когда вы исполь-
        зуете наборы в ваших программах,  вы будете удивлены скоростью их
        работы:  они разработаны с максимальной гибкостью  и  реализованы
        для использования с максимальной скоростью.

             Теперь пришло  время  рассмотреть реальные возможности набо-
        ров,  элементы могут обрабатываться полиморфически.  Это  значит,
        что  вы не просто можете хранить определенный тип объекта в набо-
        ре;  вы можете хранить несколько разных  типов  объектов,  взятых
        произвольно из вашей иерархии объектов.

             Если вы рассмотрите приведенные примеры наборов,  вы  можете
        заметить, что все элементы каждого набора были одно и того же ти-
        па.  Мы имели дело со списком строк в котором каждый элемент  был
        строкой.  Мы  также занимались списком клиентов.  Но наборы могут
        хранить любые производные от TObject объекты,  и вы можете произ-
        вольно смешивать эти объекты.  Естественно, что вы желаете, чтобы
        эти объекты имели нечто общее.  На самом деле вам нужно,  чтобы у
        них был общий абстрактный объект-предок.

             В качестве примера рассмотрим программу,  которая помещает в
        набор  три различных графических объекта.  Затем итератор ForEach
        используется для просмотра набора и отображения каждого  объекта.
        В   отличие   от  других  примеров  данной  главы  данный  пример
        (Collect4) использует функции Windows для рисования в окне.  Обя-
        зательно  включите  WinProcs  и  WinTypes в uses данного примера.
        Сначала определяется     абстрактный      объект-предок      (см.
        COLLECT4.PAS).

             type
               PGraphObject = ^TGraphObject;
             TGraphObject = object(TObject)
               Rect: TRect;
               constructor Init(Bounds: TRect);
               procedure Draw(DC: HDC); virtual;
             end;

             Из этого объявления вы можете видеть, что каждый графический
        объект может инициализировать себя (Init) и  отобразить  себя  на
        графическом экране (Draw). Теперь определим эллипс, прямоугольник
        и сектор как производные от этого общего предка:

             PGraphEllipse = ^TGraphEllipse;
             TGraphEllipse = object(TGraphObject)
               procedure Draw(DC: HDC); virtual;
             end;

             PGraphRect=^TGraphRect;
             TGraphRect=object(TGraphObject)
               procedure Draw(DC: HDC); virtual;
             end;
             PGraphPie = ^TGraphPie;
             TGraphPie = object(TGraphObject)
               ArcStart, ArcEnd: TPoint;
               constructor Init(Bounds: TRect);
               procedure Draw(DC: HDC); virtual;
             end;

             Все эти  три   типа   объекта   наследуют   поле   Rect   из
        TGraphObject,   но  все  они  разного  размера.  TGraphEllipse  и
        TGraphRect нужно только добавить их новые методы рисования,  т.к.
        их методам  рисования  нужны  только  размеры  и расположение,  а
        TGraphPie нужны дополнительные поля и другой конструктор  для  их
        корректного представления.  Приведем  исходный  код для помещения
        этих фигур в набор:

               .
               .
               .
             GraphicsList := New(PCollection, Init(10,5));
                                           { создать набор }
             for I := 1 to NumToDraw do
             begin
              case I mod 3 of              { создать объект }
               0: P := New(GraphRect, Init(Bounds));
               1: P := New(GraphEllipse, Init(Bounds));
               2: P := New(GraphPie, Init(Bounds));
              end;
              GraphicsList^.Insert(P);     { добавить в набор }
             end;
               .
               .

             Как вы можете видеть цикл, for вставляет графические объекты
        в  набор GraphicsList.  Вы знаете только то,  что каждый объект в
        GraphicsList представляет собой некоторый вид TGraphObject. После
        помещения  в  набор  у вас уже нет информации о том,  является ли
        элемент набора прямоугольником,  эллипсом или сектором. Благодаря
        полиморфизму, вам этого и не нужно знать, поскольку каждый объект
        содержит все данные и код (Draw),  который ему нужен.  Просмотрим
        набор  с  использованием итеративного метода и каждый набор будет
        сам отображать себя:

             procedure DrawAll(C: PCollection);

             procedure CallDraw(P: PGraphObject); far;
              begin
                P^.Draw(PaintDC);      { вызов метода Draw }
              end;

             begin     {DrawAll}
              C^.ForEach(@CallDraw);   { прорисовать каждый объект }
             end;

             var
              GraphicsList: PCollection;
             begin
               .
               .
               .
              if GraphicsList <> nil then DrawAll(GraphicsList);
               .
               .
               .
             end.

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


                             Наборы и управление памятью
        -----------------------------------------------------------------

             TCollection может динамически расти от  начального  размера,
        установленного Init,  до максимального размера в 16380 элементов.
        ObjectWindows хранит  максимальный  размер  набора  в  переменной
        MaxCollectionSize.  Каждый  добавляемый  в набор элемент занимает
        четыре байта памяти, т.к. он хранится в виде указателя.

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

             Если не хватает памяти при добавлении элемента в  набор,  то
        вызывается метод TCollection.Error,  и возникает ошибка этапа вы-
        полнения в динамически распределяемой области памяти.  Вы  можете
        переписать  TCollection.Error для организации собственного метода
        информирования или исправления ошибки.

             Вам следует уделить особое внимание доступности динамической
        области памяти,  поскольку у пользователя имеет значительно боль-
        ший контроль над программой ObjectWinodws,  чем над обычной прог-
        раммой языка Паскаль. Если добавлением объектов в набор управляет
        пользователь (например,  открывая новое окно),  то ошибку динами-
        ческой области памяти не так  то  легко  предсказать.  Вы  можете
        предпринять  некоторые  шаги  по защите пользователя от фатальной
        ошибки при  выполнении программы либо проверяя память при исполь-
        зовании набора, либо обрабатывая сбой выполняемой программы таким
        образом, чтобы избежать прекращения ее работы. 
                              Назад | Содержание | Вперед
VPS/VDS серверы. 30 локаций на выбор

Серверы VPS/VDS с большим диском

Хорошие условия для реселлеров

4VPS.SU - VPS в 17-ти странах

2Gbit/s безлимит

Современное железо!

Бесплатный конструктор сайтов и Landing Page

Хостинг с DDoS защитой от 2.5$ + Бесплатный SSL и Домен

SSD VPS в Нидерландах под различные задачи от 2.6$

✅ Дешевый VPS-хостинг на AMD EPYC: 1vCore, 3GB DDR4, 15GB NVMe всего за €3,50!

🔥 Anti-DDoS защита 12 Тбит/с!

Новости мира IT:

Архив новостей

IT-консалтинг Software Engineering Программирование СУБД Безопасность Internet Сети Операционные системы Hardware

Информация для рекламодателей PR-акции, размещение рекламы — adv@citforum.ru,
тел. +7 495 7861149
Пресс-релизы — pr@citforum.ru
Обратная связь
Информация для авторов
Rambler's Top100 TopList This Web server launched on February 24, 1997
Copyright © 1997-2000 CIT, © 2001-2019 CIT Forum
Внимание! Любой из материалов, опубликованных на этом сервере, не может быть воспроизведен в какой бы то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских прав. Подробнее...