
- •Часть 1. Введение в turbo vision...............................14
- •Глава 1. Наследование велосипеда...............................14
- •Глава 2. Разработка прикладных программ с использованием
- •Часть 2. Глава 3. Иерархия классов..........................88
- •Глава 4. Отображаемые элементы................................108
- •Глава 5. Программирование, управляемое событиями..............143
- •Глава 6. Разработка надежных программ.........................170
- •Глава 7. Коллекции............................................177
- •Глава 8. Объекты, хранимые с потоками.........................199
- •Глава 9. Ресурсы..............................................211
- •Часть 1. Введение в turbo vision
- •Глава 1. Наследование велосипеда
- •Глава 2. Разработка прикладных программ
- •Часть 2. Глава 3. Иерархия классов
- •Глава 4. Отображаемые элементы
- •Глава 5. Программирование, управляемое событиями
- •Глава 6. Разработка надежных программ
- •Глава 7. Коллекции
- •Глава 8. Объекты, хранимые с потоками
- •Глава 9. Ресурсы
- •Глава 10. Дополнительная информация....................................5
- •Часть 3. Справочник по turbo vision...................................13
- •Глава 11. Как использовать справочник.................................13
- •Глава 12. Заголовочные файлы turbo vision.............................16
- •Глава 13. Справочник по классам.......................................35
- •Глава 10. Дополнительная информация
- •Часть 3. Справочник по turbo vision
- •Глава 11. Как использовать справочник
- •Глава 12 описывает различные заголовочные файлы Turbo
- •Глава 16 описывает в алфавитном порядке все глобальные конс-
- •Глава 12. Заголовочные файлы turbo vision
- •Глава 13. Справочник по классам
- •Глава 14. Классы редактора......................................6
- •Глава 15. Стандартные диалоговые окна..........................41
- •Глава 16. Глобальный справочник................................70
- •Глава 14. Классы редактора
- •Глава 15. Стандартные диалоговые окна
- •Глава 16. Глобальный справочник
Глава 7. Коллекции
-----------------------------------------------------------------
В языке Си для традиционных массивов имеется два основных
ограничения. Во-первых, размер инициализированного массива уста-
навливается во время компиляции. Во-вторых, данные в заданном
массиве должны быть однотипными. Последнее ограничение можно
обойти, используя массивы групповых указателей, однако при этом
следует избегать несовпадения типов. Turbo Vision позволяет ре-
шить обе эти проблемы, т.к. в ней предусмотрены, так называемые,
коллекции. Коллекции - это обобщенные массивы произвольных объек-
тов, размер которых можно динамически изменять. Позднее вы узнае-
те, что коллекции можно сделать также потоковыми, что означает,
что они могут записываться в выходные потоки и считываться из
входных потоков способом, обеспечивающим относительную безопас-
ность при работе с типами. Потоковые классы реализуют постоянные
объекты для многих важных приложений. Например, вы сможете сохра-
нять все отображаемые подобъекты в любой группе (это может быть
и все ваша прикладная программа ) и восстанавливать их с помощью
всего лишь нескольких строк программы.
Из основных коллекций - TNSCollection (непотоковая коллек-
ция) и TCollection (потоковая коллекция) могут образоваться мно-
гие специализированные классы: отсортированные коллекции, кол-
лекции файлов и т.д.. Другие специализированные классы потоковых
коллекций, называемые ресурсами (resources) позволяют сохранять и
восстанавливать произвольные объекты с помощью строк (имен) в ка-
честве индексов. На следующем рисунке показана иерархия коллекций
и ресурсов.
Turbo Vision для С++ = 178 =
╔═══════════════════╗
║ TObject ║
╚═══^════^══════════╝
┌──────────────────┘ │
│ │
╔════════╧══════╗ ╔═════════╧═════╗ ╔═════════════╗
║ TResourceFile ║ ║ TNSCollection ║ ║ TStreamable ║
╚═══════════════╝ ╚══^═════════^══╝ ╚══════^══════╝
│ │ │
┌─────┘ └─────┬──────┘
╔═════════╧═════════╗ ╔═════╧══════╗
║TNSSortedCollection║ ║ TCollection║
╚════════^══════════╝ ╚══════^═════╝
└─────────────┬─────────┘
╔═════════╧═════════╗
║ TSortedCollection ║
╚════^════════════^═╝
┌──────┘ └────┐
╔═════════╧═════════╗ ╔═════════╧═════════╗
║ TStringCollection ║ ║ TFileCollection ║
╚════════^══════════╝ ╚═══════════════════╝
│
╔════════╧═══════════╗
║ TResourceCollection║
╚════════════════════╝
Рис.7.1. Коллекции и ресурсы.
Примечание. Как принято в С++ стрелки указывают направление к ба-
зовому классу.
Turbo Vision для С++ = 179 =
Тип TCollection
-----------------------------------------------------------------
Тип TNSCollection предусматривает основные механизмы для
хранения коллекций объектов и манипуляций с ним. Его потоковый
потомок, класс TCollection является производным от TNSCollection
и TStreamable. Таким образом, функциональные возможности доступа
и хранения TCollection наследуются от TNSCollection, а его "пото-
ковость" частично наследуется от класса TStreamable, а частично
от двух "частных" виртуальных функций элементов -
TCollection::readItem и TCollection::writeItem. Далее в главе 8
вы увидите, что последний метод должен быть переопределен в каж-
дом порождаемом классе, чтобы обеспечить базовый поток ввода-вы-
вода для новых типов данных.
Далее в этой главе речь будет идти о классах и объектах кол-
лекций, означающих классы и объекты, порожденные либо от
TCollection, либо от TNSCollection.
TNSCollection, TCollection и TStreamable служат исключитель-
но как основа для дальнейшего порождения полезных классов. Классы
TCollection и TStreamable фактически являются абстрактными
классами (каждый из которых имеет по крайней мере одну виртуаль-
ную пустую функцию) поэтому вы не сможете непосредственно созда-
вать экземпляры объектов TCollection или TStreamable. Класс
TNSCollection не является строго абстрактным классом, поэтому его
экземпляры вы сможете создавать, посмотрев примере в
TVGUID17.CPP); однако такие объекты в реальных прикладных прог-
раммах имеют определенные ограничения.
Класс TNSCollection (где NS обозначает непотоковый), порож-
денный от TObject, предусматривает все основные элементы для кол-
лекции, а именно: обеспечение доступа, добавление и удаление эле-
ментов данных из коллекций и три метода итерации. Функции итера-
ции - это функции, позволяющие применять ваши собственные функции
ко всем или избранным элементам коллекции. TStreamable - это аб-
страктный класс, используемый при наименовании и регистрации по-
токов. Классы opstream и ipstream являются дружественными класса-
ми класса TStreamable, что означает, что знакомые вам операторы С
++ >> и <<, соответствующим образом переопределенные, имеются в
вашем распоряжении (подробнее об этом см. в главе 8).
Тип TCollection, порожденный от TNSCollection и TStreamable,
следовательно, предусматривает потоковые коллекции, хотя в данной
главе рассматриваются непотоковые свойства, унаследованные от
TNSCollection. Если даже вы не имеете дело с потоковыми коллекци-
ями, использование TCollection в качестве основы несколько более
накладно, чем TNSCollection. Имейте в виду, что любой класс, по-
рожденный от потокового класса, такого как TCollection, унаследу-
ет свойства и TStreamable (чтобы получился потоковый класс).
Динамическое определение размера коллекций
Turbo Vision для С++ = 180 =
-----------------------------------------------------------------
Число полей, хранящихся в объекте коллекций, заданного полем
count, в процессе выполнения программы может динамически изме-
няться. Увеличение этого числа управляется следующим образом: ус-
тановкой исходного размера limit, и значением приращения delta.
Если добавляются поля, превышающие заданный размер коллекции, то
максимальный разрешенный размер поля увеличивается на значение
delta. Следовательно, установка delta равной нулю эквивалентна
фиксированному размеру коллекции.
Turbo Vision для С++ = 181 =
Смешивание типов полей в коллекциях
-----------------------------------------------------------------
Защищенное поле items класса TNSCollection объявляется как
void **items. Этот массив указателей позволяет создать эффектив-
ную коллекцию из произвольного числа любых элементов данных (либо
объектов, либо необъектов) произвольного размера. Если вы смешае-
те типы данных в объекте коллекции, то вам придется решать проб-
лемы избежания несовпадений типов. Объекты TNSCollection "ничего
не знают" о полях, пересылаемых в них. Они лишь сохраняют их и
возвращают обратно, если это потребуется. Естественно, порожден-
ные классы могут "привести" тип указателей элементов для поддерж-
ки специальных объектов. В частности чтобы обеспечить проверку
типов, при наличии коллекций смешанных типов ваши порожденные
классы коллекций должны переопределить методы доступа, такие как
atPut (добавление элемента по заданному индексу) и at (возвраще-
ние элемента по заданному индексу).
Создание коллекции
-----------------------------------------------------------------
Создание коллекции - такой же простой процесс, как определе-
ние типов данных, которые вы хотите объединить в коллекцию. Пред-
положим, что вы являетесь консультантом и хотите иметь возмож-
ность найти имя и номер телефона каждого из ваших клиентов. Сна-
чала вы определяете объект клиента (TClient порожденный от
TObject), который будет храниться в коллекции. Затем вы должны
предусмотреть конструктор для TClient. Применение неявного дест-
руктора не требуется, т.к. TObject::~TObject позаботится об уст-
ранении клиента.
struct TClient:public TObject
{
// все элементы открыты по умолчанию
// три элемента данных
const char *account;
const char *name;
const char *phone;
TClient( char *newAccount, char *newName, char *newPhone);
~TClient();
};
// конструктор
TClient::TClient(char *newAccount, char *newName,
char *newPhone) {
account = newStr( newAccount );
name = newStr( newName );
phone = newStr( newPhone );
};
Turbo Vision для С++ = 182 =
Далее вы создадите экземпляр коллекции, для хранения данных
о ваших клиентах, и вставите в нее записи клиентов. Главное тело
программы будет выглядеть следующим образом:
TNSCollection clientList( 50, 10 ); // limit is 50,
delta is 10
clientList.insert( new TClient("90-167", "Smith, Zelda",
"(800) 555-1212" ));
clientList.insert( new TClient("90-160", "Johnson, Agatha",
"(302) 139-8913" ));
clientList.insert( new TClient("90-177", "Smitty, John",
"(406) 987-4321"));
clientList.insert( new TClient("90-100", "Anders, Smitty",
"(406) 111-2222"));
printAll( &clientList );
searchArea( &clientList, "(406)" );
return 0;
Примечание: printAll и searchArea - это функции, о которых речь
пойдет ниже.
Обратите внимание на простоту создания коллекции. Первый
оператор определяет новый объект TNSCollection по имени
ClientList, исходно рассчитанный на 50 элементов. Если вам требу-
ется вставить в ClientList более 50 элементов то его размер будет
по необходимости увеличиваться, каждый раз на 10 элементов. С по-
мощью двух следующих операторов создается и вставляется в коллек-
цию новый объект клиента.
Метод insert является виртуальным и может переопределяться в
каких-то специальных целях (обычно для обеспечения выполнения
операций по сохранению типов). По умолчанию, insert добавляет
элементы в конец коллекции и увеличивает счетчик числа элементов
в коллекции count. По достижении предела limit метод insert вы-
полняет также операцию приращения delta, о которой говорилось вы-
ше. Далее insert возвращает индекс index нового поля. Этот индекс
имеет тип ccIndex и является аргументом во многих методах
TNSCollection. Хотя тип ccIndex определяется в настоящее время
как int (целочисленный), вам следует его всегда использовать в
целях будущей мобильности. Метод at, например, объявляемый как:
void *at ( ccIndex );
возвращает указатель на элемент, находящийся в позиции index.
Противоположной операцией является indexOf, возвращающая индекс
заданного элемента:
virtual ccIndex indexOf ( void *item );
Разновидностью метода insert является метод atInsert, кото-
рый вставляет элемент на позицию с заданным индексом. Методы
remove и atRemove являются эквивалентными для перемещения элемен-
Turbo Vision для С++ = 183 =
тов из коллекции и соответствующего регулирования индексов. Метод
removeAll удаляет все элементы и присваивает полю сount значение
0. Эти методы "удаления" удаляют, но не разрушают, сохраняемые
элементы; методы free и freeAll удаляют и разрушают элементы.
При уничтожении коллекции в конце выполнения программы вся
коллекция, включая "заказчиков", обычно также разрушается. Эта
операция определяется булевским методом shouldDelete: при его
значении True (по умолчанию) все элементы перемещаются и удаляют-
ся посредством метода freeAll; а, при его значении False, ограни-
читель размера коллекции получает значение 0, но сами элементы не
уничтожаются. Не следует забывать, что коллекции являются масси-
вами указателей, поэтому имеется существенная разница между уда-
лением указателя на элемент и разрушением самого элемента.
Turbo Vision для С++ = 184 =
Методы итерации
-----------------------------------------------------------------
Вставка и удаление элементов - не единственные общие для
коллекции операции. Вы часто будете использовать в программе цик-
лы for для перебора всех объектов коллекции с целью изображения
данных или выполнения каких-либо вычислений. В другом случае вам
понадобится определить первый или последний элемент в коллекции,
который удовлетворяет какому-либо критерию поиска. Для этих целей
коллекции располагают тремя методами итерации: forEach, firstThat
и lastThat. Каждая из них использует в качестве двух параметров
указатель на функцию и указатель void *arg. Последний позволит
вам передавать в дополнение к главному методу итерации произволь-
ные аргументы.
Итератор forEach
Метод forEach имеет в качестве параметров указатель на функ-
цию action типа ccAppFunc и указатель arg:
void TNSCollection::forEach( ccAppFunc action, void *arg)
{
for( ccIndex i = 0; i < count; i++ ) // count is current
// number of items in
// collection
action( items[i], arg );
}
Функция action имеет тип ccAppFunc, определяемый следующим
образом:
typedef void (*ccAppFunc) ( void *, void *);
Первый параметр функции action является указателем на храня-
щийся в коллекции элемент: второй параметр является родовым ука-
зателем arg, передаваемым в forEach. Если элемент итерации не
требует дополнительных данных, то второй параметр будет иметь
значение 0. forEach вызывает эту функцию по одному разу для каж-
дого элемента в коллекции в том порядке, в котором они расположе-
ны в коллекции. Процедура PrintAll в примере TVGUID17 представля-
ет собой пример метода итерации forEach.
static void print( void *c, void * )
// make it static for safety in view of generic pointers
// Note that we will not be using the second argument {
TClient *tc = (TClient *)c;
cout << setiosflags( ios::left )
<< setw(10) << tc->account
<< setw(20) << tc->name
<< setw(16) << tc->phone
<< endl;
}
Turbo Vision для С++ = 185 =
void printAll ( TNSCollection *c ) // print info for all
// clients {
cout << endl << "Client List:" << endl;
c->forEach) &print, 0 ); // call print for each client }
Обратите внимание, что параметр &print, передаваемый в
forEach, совпадает с типом данных ccAppFunc: указателем функции,
имеющей параметры (void*, void*) и "возвращающей" void. Для каж-
дого элемента *c объекта TNSCollection для выдачи информации о
каждом клиенте вызывается функция print.
Итераторы lastThat и firstThat
Кроме возможности применения функции к каждому элементу в
коллекции, полезной бывает возможность поиска отдельных элементов
коллекции по критерию. Эту функцию и выполняют итераторы lastThat
и firstThat. Как видно из их имен, они выполняют поиск в коллек-
ции в противоположных направлениях до тех пор, пока не обнаружат
элемент, удовлетворяющий критерию поиска, заданному в виде бу-
левской функции, передаваемой в качестве аргумента.
firstThat и lastThat возвращают указатель на первый (или
последний) элемент, который отвечает критериям поиска. Рассмотрим
один из предыдущих примеров списка клиентов и представим себе,
что не можем запомнить табельный номер клиента и точное написание
его фамилии. Предположим, что вы при этом точно помните, что это
был ваш первый клиент из штата Монтана. Т.о., вам требуется найти
первый случай появления клиента в программе индекс штата (т.к.
ваш список построен в хронологическом порядке). Это можно сделать
с помощью следующей функции, использующей итератор firstThat:
Boolean areaMatch( void *c, void *ph )
{
char *areaToFind = (char *)ph;
TClient *tc = (TClient *)c;
// искать совпадение по первым 5 символам: "(xxx)"
if(strncmp(areaToFind, tc->phone, 5 ) == 0 )
retutn True;
else
return False;
}
void searchArea(TNSCollection *c, char *areaToFind )
{
TClient *foundClient =
(TClient *) (c->firstThat( &areaMatch, areaToFind ));
if( !foundClient )
cout << "No client met the search reguirement" << endl;
else
{
Turbo Vision для С++ = 186 =
cout << "Found client:" << endl;
print( foundClient, 0 );
}
}
Указатель функции проверки, который вы передаете в
firstThat, имеет следующий тип:
typedef Boolean (*ccTestFunc) ( void *, void *);
Кроме возвращаемого типа данных, функция проверки также со-
ответствует и образцу, подобному типу ccAppFunc, используемому в
forEach. areaMatch использует второй аргумент arg для передачи
строки кода штата. True возвращается только в случае, если эта
строка совпадает с кодом штата, обнаруженном в tc->phone. Если ни
один из объектов коллекции не удовлетворяет критерию поиска, то
возвращается нулевой указатель, из чего следует проверка
if (!foundClient).
Следует помнить, что forEach имеет указатель на функцию типа
ccAppFunc, тогда как функции firstThat и lastThat имеют указатели
функции типа ccTestFunc. В любом случае действие, определяемое
пользователем, или функция проверки имеют два указателя: один на
объект коллекции, другой - на любое поле, которое вам может пот-
ребоваться передать методу итерации. Вы должны привести эти ука-
затели в соответствие с действительными элементами, которые вы
предполагаете иметь в коллекции.
В примере TVG17B.CPP демонстрируется надежный способ обра-
ботки коллекций. От коллекции TNSCollection порождается специ-
альный тип коллекции, называемый TClientCollection:
class TClientCollection : public TNSCollection
{
public:
TClientCollection (ccIndex alimit, ccIndex aDelta ) :
TNSCollection( aLimit, aDelta ) {}
virtual ccIndex insert( TClient *tc ) { return
TNSCollection::insert( tc ); }
void printAll();
void searchArea( char *areaToFind );
};
Теперь мы можем переопределить TNSCollection::insert (void
*item), чтобы повысить надежность проверки использования типа.
Новый метод insert предусматривает и настаивает на использовании
Turbo Vision для С++ = 187 =
аргумента указателя TClient, тогда как оригинальная версия insert
воспримет любой аргумент указателя. В более полной прикладной
программе вы должны также переопределить методы итерации at,
indexOf, remove, free и т.д., чтобы они использовали аргументы
указателя TClient. Поскольку мы имеем специально выделенный тип
коллекции, то мы должны создать также методы printAll и
searchArea, как показано в предыдущей программе. С рядом дорабо-
ток пример TVG17B. CPP следует той же стратегии, что и
TVGUID17.CPP.
Turbo Vision для С++ = 188 =
Отсортированные коллекции
-----------------------------------------------------------------
Время от времени вам требуется выстроить данные в определен-
ном порядке. Turbo Vision имеет 2 специальных типа коллекций,
позволяющих располагать ваши данные в любом порядке:
TSortedCollection и TNSSortedCollection.
TNSSortedCollection является наследником TNSCollection, ко-
торая автоматически сортирует передаваемые ей объекты. Она также
выполняет автоматическую проверку коллекции, при добавлении ново-
го элемента, и не допускает дублирования элементов (если только
вы не присвоите элементам duplicates значение True).
TSortedCollection является потоковой версией TNSSorted-
Collection, основанной как на типе TNSSortedCollection, так и
TStreamable.
Применение типа TNSCollection (непотокового) и TCollection
(потокового) для отсортированных версий очевидным способом пояс-
нялось выше. Чтобы избежать многословия, мы будем говорить об от-
сортированных коллекциях, не указывая, являются ли они потоковыми
или нет.
Единственным техническим различием является то, что как
TNSSortedCollection, так и TSortedCollection имеют абстрактный
тип. При их использовании вы должны сначала решить, какого типа
данные вы будете собирать, и определить две функции элемента,
удовлетворяющих вашим требованиям сортировки. Как и раньше пост-
роим наш пример на TNSSortedCollection, чтобы избежать проблем
с потоками. Для этого вы должны породить новый тип коллекции из
TNSSortedCollection. Для нашего примера назовем его
TSortedClientCollection.
Этот тип TSortedClientCollection сразу знает все действия,
выполняемые коллекцией. Он может вставлять новые записи клиентов
и удалять существующие записи - эти свойства он наследует от
TNSCollection. Вам останется лишь указать TSorted-
ClientCollection, какой элемент данных ему следует использовать в
качестве ключа сортировки, и как проводить сравнение данных по
двум клиентам и определять, какой из них будет помещаться в кол-
лекции впереди другого. Это можно сделать с помощью переопределе-
ния keyOf и compare и реализации их, как показано ниже. Метод
compare является частной, пустой или не выполняющей никаких дейс-
твий виртуальной функцией, поэтому его всегда следует переопреде-
лять.
class TSortedClientCollection : public TNSSSortedCollection;
{
public:
virtual void *keyOf(void *item);
private:
Turbo Vision для С++ = 189 =
virtual int compare( void *key1, void *key2);
}
void *TSortedClientCollection::keyOf(void *item)
{
return ( ( (TClient*) item )->name );
}
int TSortedClientCollection::compare(void *key1, void *key2);
{
return (strcmp ( (char *)(key1), (char *)(key2) ));
Примечание. Для ключей должно быть выполнено приведение типов, т.
к. они являются нетипизованными указателями.
Метод keyOf определяет, какое поле или поля будут использо-
ваться в качестве ключа сортировки. В нашем примере это будет по-
ле клиента Name. compare имеет два ключа сортировки и определяет
очередность их следования в отсортированном порядке. compare
возвращает значения -1, 0 или 1, в зависимости от того, является
ли ключ Key1 меньшим, равным или большим, чем ключ Key2. В данном
примере используется способ сортировки ключевых строк (Name) в
прямом алфавитном порядке.
Имейте в виду, что поскольку ключи, возвращаемые методом
keyOf и передаваемые методу compare, являются нетипизованными
указателями, вы должны преобразовать их в тип char* перед получе-
нием их значения.
Это все, что вам требуется определить! Теперь, вы можете
исправить пример TVG17B.CPP, используя тип TNSSortedCollection
как основу для помещения TSortedClientCollection на место
TClientCollection. Теперь вы сможете легко расположить ваших кли-
ентов в алфавитном порядке:
int main()
{
TSortedClientCollection clientList( 50, 10);
...
// as before
}
Обратите также внимание, насколько просто было бы отсортиро-
вать список клиентов по табельному номеру, а не по имени. Для
этого вы должны были бы лишь изменить keyOf, чтобы он возвращал
вместо Name поле Account.
Turbo Vision для С++ = 190 =
Строковые коллекции
-----------------------------------------------------------------
Во многих программах требуется хранение указателей на отсор-
тированные строки. Для этих целей Turbo Vision располагает специ-
альной коллекцией TStringCollection. Элементы TStringCollection
являются не объектами - это указатели на строки (тип char**).
Т.к. строковая коллекция является наследником TSortedCollection,
то дублирующие строки разрешаются только в том случае, если дубл-
ирующимся полем присвоено значение True (их значение по умолчанию
- False, не допускающее дублирования).
Строковые коллекции используются таким же образом, что и
другие отсортированы коллекции. В следующем примере
TWordCollection является порожденным из TStringCollection и ему
задается конструктор и метод print. Последний будет выполнять пе-
чать всей коллекции слов с помощью функции итерации (ее описание
приводится ниже).
class TWordCollection : public TStringCollection
{
public:
TWordCollection( short aLimit, short aDelta ) :
TStringCollection( aLimit, aDelta ) { }
virtual void print();
};
Замечание: Это пример TVGUIDE19.CPP.
Построение отсортированной коллекции слов будет выполняться
с помощью построчного считывания текстового файла, извлекая каж-
дое слово из строки и помещая слова в объект TWordCollection в
алфавитном порядке (ASCII). Метод insertWord анализирует каждую
строку на основе упрощенного подхода, в котором словом считается
любая последовательность символов, не разделенных знаками пункту-
ации и пробелами, окруженных знаками пунктуации и пробелами.
Метод insertWord вставляет также любое обнаруживаемое им не
пустое слово.
Поскольку TWordCollection является типом отсортированной
коллекции (посредством коллекций TStringCollection и
TSortedCollection), то он имеет поле дублирования (duplicates),
управляющее допустимостью дублирования строк слова. В данном при-
мере поле duplicates имеет значение False (это его значение по
умолчанию, следовательно, не требуется никаких явных действий), и
коллекция допускает использование только уникальных слов. Поле
влияет на действие методов search, indexOf и insert, унаследован-
ных от TNSSortedCollection. При вызове метода insert в коллекции
выполняется поиск указанного элемента. Если таковой не обнаружен,
то он всегда вставляется в правильное положение (согласно типу
сортировки). Если элемент обнаружен, то дальнейший ход событий
Turbo Vision для С++ = 191 =
определяется значением поля duplicates. Если оно имеет значение
False, то элемент вставляется перед его дублирующим элементом
(элементами). В противном случае вставка не выполняется.
Коллекция слов создается в main следующим образом:
TWordCollection *wordCollection = new TWordColection(50, 5):
Следовательно, коллекция wordCollection первоначально состо-
ит из 50 указателей-строк и далее увеличивается каждый раз на 5
указателей. Функция fileRead подобна функции, использовавшейся в
примере TVGUID06.CPP, но в данном случае от считывает по одной
строке, и после каждой строки вызывает метод insertWord.
Следует особо отметить использование при вызове функции
вставки метода newStr:
sc->insert ( newStr( wstr ));
Сравните эту операцию с более "очевидной" (и вполне допусти-
мой):
sc->insert ( wstr );
Глобальная функция newStr используется для копирования str,
строки слова, которая только что была извлечена, а адрес копии
строки передается коллекции. Здесь имеется в виду, что при пере-
мещении коллекции будут уничтожаться копии строк и их указатели,
а не "оригиналы". В данном примере разница между двумя вставками
не имеет значения. В другом контексте она может стать существен-
ной и вам может потребоваться сохранить копию при утрате оригина-
ла. Одновременно с копированием коллекция выполнит и управление.
После завершения заполнения коллекции слов для составления
списка всех слов в коллекции используется метод print:
if( wordCollection->getCount() > 0 )
{
wordCollection->print();
count << "Total word count = " << wordCollection->
getCount() << endl;
}
else
count << "No words in WordCollection!" << endl;
Еще раз об итераторах
Метод print использует итератор forEach в применении к
printWord следующим образом:
/* Iterator */
Turbo Vision для С++ = 192 =
static void printWord( void *w, void * )
{
char *s = (char *)w;
cout << s << endl;
}
void TWordCollection::print()
{
forEach( &printWord, 0 );
}
Метод forEach (наследуемый из TNSCollection!) просматривает
всю коллекцию поэлементно и передает каждый из них в указанную
вами функцию.
Поиск элемента
Отсортированные коллекции (и, следовательно, строковые кол-
лекции) располагают методом search, который возвращает индекс
элемента и его ключ. Однако, каким образом вы могли бы найти эле-
мент в коллекции, которая окажется неотсортированной? Или если
ключ не включен в критерий поиска? В этом случае вам следует ис-
пользовать методы firstThat и lastThat. Вы должны определить бу-
левскую функцию для выполнения проверки в соответствии с требуе-
мыми критериями и вызвать метод firstThat. Имейте в виду, что для
отсортированных коллекций, допускающих дублирование, search обна-
ружит совпадение first, если их несколько. Вашей программе потре-
буется выполнить дальнейший просмотр для проверки дублирования.
Turbo Vision для С++ = 193 =
Полиморфные коллекции
-----------------------------------------------------------------
Вы убедились, что коллекции могут динамически хранить дан-
ные любого типа, и существует масса способов облегчения эффектив-
ного доступа к данным коллекции. Сам объект TNSCollection может
определять более 18 методов. При использовании коллекций в прог-
раммах, на вас произведет впечатление их быстродействие. Они раз-
работаны с учетом повышенных требований к гибкости и их реализа-
ция отвечает повышенным требованиям к быстродействию.
Однако, важнейшее преимущество коллекций состоит в следую-
щем: элементы в них могут рассматриваться полиморфно. Это означа-
ет, что вы можете хранить в коллекции не просто тип объекта, а
многие типы объектов, находящиеся в любом месте вашей иерархии
объектов.
Если вы рассмотрите приведенные ранее примеры коллекции, то
вы заметите, что все элементы в каждой коллекции были однотипны-
ми. В них имелся список строк, каждый элемент которого являлся
строкой. Имелась также коллекция клиентов. Тем не менее в коллек-
циях могут храниться любые объекты, порожденные от TObject, и вы
их можете свободно смешивать. Естественно, вы бы хотели, чтобы у
ваших объектов было что-то общее, особенно общий абстрактный ба-
зовый класс.
Ниже приводится пример программы, в которой 3 различных гра-
фических объекта объединяются в коллекцию. Затем для просмотра
коллекции и изображения каждого объекта используется итератор
forEach. Данная программа является превосходным прототипом для
дальнейших опытов с коллекциями, графическими объектами и поли-
морфизмом.
В данном примере используются графическая библиотека фирмы
Borland и драйверы BGI, поэтому не забудьте включить строку
#include <graphics.h> в вашу программу и скомпоновать как TV.LIB,
так и GRAPHICS.LIB. При выполнении программы перейдите в каталог,
содержащий драйверы .BGI или измените вызов InitGraph для уста-
новки их местоположения (например, C:\BORLANDC\BGI).
В первую очередь определяется абстрактный базовый объект.
class TGraphObject : public TObject
{
public:
int x, y;
TGraphObject ()
// в этом примере конструктор присваивает x, y произвольные
// значения
virtual void draw() = 0;
// пустая виртуальная функция - должна быть
Turbo Vision для С++ = 194 =
// определена в порожденных классах
};
Примечание: Этот пример находится в файле TVGUID20.CPP.
Из этого объявления можно заметить, что каждый графический
объект может самостоятельно инициализировать себя (Init) и отоб-
ражать себя на экране в графическом режиме (Draw). Далее опреде-
ляется точка, окружность и прямоугольник, порождаемые от
TGraphObject:
class TGraphPoint : public TGraphObject
{
public:
TGraphPoint();
// в этом примере конструктор присваивает x, y произвольные
значения
virtual void draw();
};
classTGraphCircle : public TGraphObject
{
public:
int radius;
TGraphCircle ();
// в этом примере конструктор присваивает x, y и radius
// (радиусу) произвольные значения
virtual void draw();
};
class TGraphRect : public TGraphObject
{
public:
int width, height;
TGraphRect ()
// в этом примере конструктор присваивает x, y, w и h произ-
// вольные значения
virtual void draw();
};
Все эти три класса наследуют поля X и Y от TGraphObject, но
они имеют различные размеры. TGraphCircle добавляет в него
Radius, а TGraphRect добавляет Width и Height. Приводим фрагмент
программы для создания коллекции:
Turbo Vision для С++ = 195 =
TNSCollection *list = new TCollection(10, 5);
//Создать коллекцию collection
for (int i = 1 t < 20; i++)
{
switch ( i % 3 )
{
case 0:
{
TGraphPoint *gp = new TGraphPoint;
list->insert( gp );
break;
}
case 1:
{
TGraphCircle *gp = new TGraphCircle;
list->insert( gc );
break;
}
case 2:
{
TGraphRect *gr = new TGraphRect;
list->insert( gr );
break;
}
}
}
...
Как вы можете видеть, цикл for помещает 20 графических объ-
ектов в коллекцию list. Вы знаете лишь, что каждый объект в list
является одним из видов, порожденных TGraphObject. Но когда он
помещен в коллекцию, вы не сможете различить, является ли задан-
ный элемент коллекции окружностью, точкой или прямоугольником.
Благодаря свойству полиморфизма вам этого знать и не требуется,
т.к. каждый объект содержит данные и программу (draw), которые
ему требуются. Вам требуется лишь просмотреть коллекцию с помощью
метода итерации и дать задание каждому объекту на изображение са-
мого себя:
void callDraw( void *p, void * )
{
((TGraphObject *)p)->draw();
// вызвать соответствующую функцию элемент draw
}
void drawALL( TNSCollection *c, void * )
{
c->forEach( &callDraw, 0 ); // Нарисовать каждый объект
}
...
Turbo Vision для С++ = 196 =
drawAll( list, 0 );
...
Эта способность коллекции хранить различные, но связанные
объекты базируется на одном из важнейших преимуществе объект-
но-ориентированного программирования. В следующей главе вы позна-
комитесь с применением этого принципа (полиморфизма) к потокам,
что также является важным преимуществом.
Turbo Vision для С++ = 197 =
Коллекции и управление памятью
-----------------------------------------------------------------
Объект TCollection может динамически увеличиваться от исход-
ного размера, установленного параметрами конструктора до макси-
мального размера, задаваемого переменной MaxCollectionSize, опре-
деляемой в CONFIG.H следующим образом:
const maxCollectionSize = (int) ((65536uL - 16)/sizeof
( void * ));
В реализациях на ПК максимальный размер коллекции предусмат-
ривается равным 16380 элементов, поскольку каждый указатель на
элемент занимает 4 байта памяти.
Библиотека динамических структур данных не будет полной, ес-
ли она не будет располагать средствами обнаружения ошибок. Если
для инициализации коллекции объем памяти недостаточен, то возвра-
щается указатель null.
Если при добавлении элемента к объекту TNSCollection для не-
го не хватит памяти, то вызывается статический метод
TNSCollection::Error и выдается ошибка, связанная с работой с ди-
намической памятью. Метод error реализован следующим образом:
void TNSCollection::error( ccIndex code, ccIndex )
{
exit(212 - code);
}
Следовательно, по умолчанию error возвращает ошибки, связан-
ные с работой программы (код ошибки 212). Например, при попытке
удаления элемента с индексом, находящемся за пределами текущего
значения count, removeAT вызовет error(1,0) и завершит работу с
кодом ошибки 211. Вы можете переопределить TNSCollection::Error,
чтобы получить свой механизм сообщения об ошибках или восстанов-
ления после их возникновения. Второй, неиспользуемый в настоящее
время, аргумент может быть использован для передачи дополнитель-
ных данных в ваши программы по обработке ошибок.
Доступная динамическая область памяти
-----------------------------------------------------------------
Вам следует обратить особое внимание на наличие доступной
динамической области памяти, т.к. пользователь обладает большими
возможностями управления программой, разработанной с помощью
Turbo Vision, чем традиционной программой. Если пользователь еди-
нолично управляет добавлением объектов в коллекцию (например, с
помощью открытия новых окон в рабочей области), то будет нелегко
предсказать возможность появления ошибки, связанной с выделением
динамической памяти. Чтобы защитить пользователя от фатальной
ошибки, связанной с работой программы, заключающиеся либо в ваших
Turbo Vision для С++ = 198 =
собственных проверках памяти, при работе с коллекциями, либо в
применении обработчика ошибок в процессе выполнения программы,
который обеспечит изящный способ избавления от ошибок, вы можете
предпринять какие-либо действия.
Turbo Vision для С++ = 199 =