
- •Часть 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. Глобальный справочник
Глава 8. Объекты, хранимые с потоками
-----------------------------------------------------------------
В этой главе рассказывается о том, как при помощи потоков в
Turbo Vision осуществляется хранение объектов. Различные экземп-
ляры классов, созданные для прикладных программ, разработанных с
помощью Turbo Vision (окна, меню и т.д.), имеют очень короткую
жизнь. Они создаются, отображаются во время работы программы.
Объекты могут появляться и исчезать, подчиняясь при этом правилам
видимости, принятым в языке. При завершении программы все объекты
из памяти удаляются. Обработчик потоков предоставляет возможность
сохранять объекты как в памяти, так и в файлах, где они хранятся
в определенном состоянии, отличном от обычного.
Существует множество прикладных программ использующих спо-
собные сохраняться объекты. При хранении в разделяемой памяти,
например, они могут поддерживать взаимосвязь между процессами.
Они могут передаваться через модемы в другие системы, и, что осо-
бенно важно, могут сохраняться на диске при помощи потоков. Затем
можно аналогичными прикладными программами прочитать файлы, где
сохранены объекты и восстановить их первоначальное содержание.
Такие возможности предоставляются всем объектам, созданным в
Turbo Vision - все основные классы (группы, видимые объекты,
TCollection и все полученные из него классы и ресурсы) имеют воз-
можность передаваться через потоки. При этом управление потоками,
осуществляется также просто, как и в случае работы с обыкновенным
файловым вводом/выводом обыкновенных файлов.
Создание своих собственных классов, обладающих такими свойс-
твами не представляет особого труда. При этом необходимо сделать
лишь небольшое добавление к определению класса. Ему необходимы
три дополнительные виртуальные функции - read, streamableName, и
write. Таким образом, все классы, нуждающиеся во взаимодействии с
потоками должны получаться из класса TStreamable (неважно прямо
или косвенно). Класс TView уже имеет в качестве одного из своих
"родителей" класс TStreamable. Аналогично можно сказать и о клас-
се TGroup и всех полученных из него классах.
Объекты потока можно легко создавать при помощи класса
pstream и его "наследников". Эти классы специально разработаны
для потоков, сохраняющих объекты, однако использование их абсо-
лютно аналогично использованию стандартных потоков С++ iostream.
Если вы знакомы с потоками С++, то многое из сказанного ниже вам
уже известно. Для тех, кто еще не знаком о потоками, проводится
короткое введение в терминологию и основные идеи ввода/вывода в
С++. Затем они рассматриваются более детально.
В С++, как и в обыкновенном языке Си не имеются ключевые
слова или предопределенные операторы для обеспечения ввода/выво-
да. Вместо этого реализованы функции стандартной библиотеки:
stdio для Си и iostream для С++. При этом, библиотека С++, ис-
пользуя преимущества ООП, более гибкая, расширяемая и надежная
благодаря переопределенным операторам и вводу/выводу, сохраняюще-
Turbo Vision для С++ = 200 =
му как стандартные, так и определенные пользователем типы данных.
Хотя функции библиотеки stdio, например printf, доступны и в С++,
большинство программистов предпочитают преимущества библиотеки
iostream, использующей потоки. Что же такое поток?
Поток - это абстрактный тип данных, представляющий собой
(конечно же в теории) последовательность элементов с различными
возможностями доступа. Потоки имеют длину (количество элементов),
текущую позицию (уникальную точку, которая доступна именно в дан-
ный момент) и режим доступа (только для чтения (read-only), толь-
ко для записи (write-only) и для чтения/записи (read/write). Чте-
ние (или извлечение) может осуществляться в любой позиции (в лю-
бом месте потока), тогда как запись (добавление) выполняется
посредством добавления элементов в конец потока.
Традиционный дисковый файл - один из известных примеров реа-
лизации потока, однако концепция потока была распространена и на
такие устройства как память, клавиатура (так называемый стандарт-
ный ввод cin), дисплей (стандартный вывод cout и cerr), коммуни-
кационные порты и т.д. Большинство этих идей заимствовано из опе-
рационной системы UNIX, где применяется принцип "все является
файлом". Фактически с помощью объектно-ориентированной технологии
любые источники данных или объектов могут обеспечиваться соот-
ветствующими функциями и рассматриваться как потоки ввода.
Аналогично, любых потребителей этих данных можно расширить
функциями вывода и рассматривать как потоки вывода.
Потоки, связанные с дисковыми файлами, обычно поддерживают
операции ввода и вывода. Для классов, взаимодействующих с потока-
ми, существует большое количество разновидностей ввода и вывода:
буферизованный или прямой, форматированный и безформатный, файло-
вый или в память, а также различные комбинации этих версий. Для
большей гибкости объектов в Turbo Vision используется библиотека
iostream, обеспечивающая все перечисленные выше преимущества.
Turbo Vision для С++ = 201 =
Переопределенные операции << и >>
-----------------------------------------------------------------
Одним из моментов, обеспечивающих успех потоков С++, явля-
ется удобство переопределенных операций: << для вывода и >> для
ввода. Таким образом, комплексный синтаксис printf и других функ-
ций stdio заменен простым и элегантным выражением, например, та-
ким:
cout << "Hello, World" << endl;
Обеспечивая возможность легко переопределять операции << и
>> для различных типов данных и классов, взаимодействующих с по-
токами, объекты этих классов можно записать в поток, а затем про-
читать оттуда баз каких-либо затруднений. Для стандартных типов
С++, таких как char, short, int, long, char*, float, double и
void *. Такое переопределение уже сделано в библиотеке iostream.
Для типов данных, определенных пользователем, это можно сделать
довольно легко. Последнее касается данных, не являющихся класса-
ми, так как для объектов классов это сделать труднее. Однако, в
этом случае Turbo Vision основную работу берет на себя, позволяя
писать и читать объекты при помощи выражений таких как, например,
следующие:
os << aTVObject << anotherTVObject;
is >> yetAnotherTVObject;
При этом программист не думает о реализации этих операций.
Ему необходимо, лишь, сделать некоторые подготовительные дейс-
твия, прежде чем создавать и регистрировать свои классы, взаимо-
действующие с потоками. Об этом будет рассказано ниже в данной
главе. В последнем примере os является объектом класса opstream.
Это же относится и к объекту is, который принадлежит классу
ipstream (или полученному из него классу). Оба этих класса полу-
чены из pstream - класса, являющегося основой всех остальных
классов, взаимодействующих с потоками. Они аналогичны классам
istream и ostream, полученным из ios, в иерархии классов С++.
Класс iopstream получен комбинированием классов ipstream и
opstream. Имеются, также, версии классов для работы с файлами.
Они называются ifstream, opstream и fpstream, и соответствуют
стандартным классам С++ ifstream, ofstream и fstream. Все соот-
ветствующие классы С++ и Turbo Vision имеют аналогичные поля дан-
ных и функции. На Рис. 8.1 показаны иерархия класса pstream. Как
принято в С++, стрелка указывает прямо на базовый класс.
Turbo Vision для С++ = 202 =
┌────────────────┐ (f) ┌─────────────────┐
│ pstream ├───────┤ Типы TSreamable │
└────────────────┘ └─────────────────┘
(v)^(v)^ ^(v)
┌────────┘ │ └────────────────────┐
┌───┐(f)┌─────┴────┐ ┌────┴─────┐(f)┌──┐ ┌──────┴───────┐
│>> ├───┤ ipstream │ │ opstream ├───┤<<│ │ fpbase │
└───┘ └──────────┘ └──────────┘ └──┘ └──────────────┘
^ ^ ^ ^ ^ ^ ^
│ └────┬───┘ └───────┬─────────┘ │ │
│ ┌─────┴─────┐ ┌─────┴─────┐ │ │
│ │ iopstream │ │ ofpstream │ │ │
│ └───────────┘ └───────────┘ │ │
│ ^ │ │
│ └───────────────┬──────────────┘ │
│ ┌─────┴─────┐ │
│ │ fpstream │ │
│ └───────────┘ │
└─────────────────────┬──────────────────────┘
┌─────┴─────┐
v - виртуальный │ ifpstream │
f - дружественный └───────────┘
Рис. 8.1. Иерархия класса pstream
Переопределенные операции <<, которые используются для запи-
си объектов TView и указателей на эти объекты, определены в файле
VIEWS.H следующим образом:
inline opstream& operator << (opstream& os, TView& cl);
inline opstream& operator << (opstream& os, TView* cl);
В первом случае объект cl класса TView будет записан в по-
ток, представленный объектам класса opstream os.
Аналогичные действия выполняет и операция <<, переопределен-
ная в стандартом классе С++ ostream. Во втором случае тоже самое
происходит с указателем на объект класса TView. Видно, что эта
операция поддерживает очень простую передачу объектов в качестве
параметров и вызывает операцию <<, определенную в базовом классе.
В этом случае класс TStreamable является базовым для класса
TView.
Похожие переопределения операций << и >> сделаны для всех
стандартных классов Turbo Vision. Обычно переопределение сопро-
вождается объявлением этих операций как inline. Иногда необходимо
написать небольшой текст функции переопределения для каких-либо
созданных пользователем классов, взаимодействующих с потоками.
Это может показаться повторяющейся терминологией! Так что же та-
кое классы, взаимодействующие с потоками? Об этом читайте ниже.
Turbo Vision для С++ = 203 =
Если объекты класса можно записывать в поток и читать из по-
тока, используя средства обработчика потоков Turbo Vision, то та-
кой класс взаимодействует с потоком. Кроме наличия удобных опера-
ций ввода/вывода (обычно это << и >>, но вы можете определить
свои собственные), этот класс должен иметь в качестве своего ба-
зового класса TStreamable. Диаграмма иерархии классов Turbo
Vision показывает, что класс TView получен одновременно из
TObject и TStreamable. Все видимые классы также имеют переопреде-
ленные операции << и >> и могут взаимодействовать с потоками.
А что собой представляют "невидимые" классы? В предыдущей
главе говорилось, что TCollection имеет в качестве своего базово-
го класса TStreamable. Другим базовым классом является
класс TNSCollection). Поэтому все классы, полученные из
TCollection имеют переопределенные операции << и >> и могут взаи-
модействовать с потоками.
Знакомство с обработчиком потоков
-----------------------------------------------------------------
Для хранения и восстановления комплексных объектов в потоках
требуется некоторая подготовительная работа. Ее выполняет обра-
ботчик потоков. Если необходимо записать в поток или восстановить
из потока простой объект данных, то это выполняется без каких-ли-
бо сложностей посредством манипулирования с двоичным представле-
нием этого объекта. Однако, объекты классов могут содержать любое
количество данных различных типов, включая указатели на другие
комплексные объекты и, возможно, указатель на таблицу виртуальных
методов.
Чтобы объекты эти и другие сложности, обработчик потока под-
держивает базу данных объектов, используя TStreamableTypes,
TStreamable и TStreamableClass. Описание класса (заметьте не эк-
земпляра класса, а всего класса) занимает 16 байт. Эти классы вы-
полняют базовую регистрацию определяют функции read, write и
build для отдельных объектов. Цель регистрации класса фактически
заключается в том, что обработчику потоков сообщается информация
о данном классе, которая вводится в базу данных.
Примечание. Более подробная информация о классах
TPWrittenObj и TPReadObj содержится в разделах в главе 13.
При проведении операций чтения и записи, обработчик потока
хранит в базе данных информацию о всех объектах, записанных в по-
ток или прочитанных из него. Не вдаваясь в подробности, рассмот-
рим проблему записи объекта в поток при помощи указателей. пред-
положим, что указатели ptr1 и ptr2 указывают на один и тот же
объект и их надо записать в поток.
Когда эти объекты будут последовательно считаны из потока,
необходимо иметь две совпадающие копии объектов с различными ука-
зателями на них. Обработчик потоков и его база данных обойдут эту
Turbo Vision для С++ = 204 =
проблему: только одна копия объекта будет записана в поток. А
когда потребуется восстановить объект из потока, будет создан
лишь один его экземпляр, при этом оба указателя будут указывать
на него. При проведении операций чтения и записи обработчик пото-
ков также работает очень аккуратно. Класс TStreamable имеет пус-
тые виртуальные функции read и write (известные как читатели и
писатели), которые должны быть переопределены во всех классах,
полученных из него:
classTStreamable
...
protected:
virtual void *read(ipstream&) = 0;
virtual void write(opstream&) = 0;
Работа функции write заключается в записи всех необходимых
данных объекта в поток. Каждый класс, взаимодействующий с потока-
ми, должен иметь свою собственную реализацию функции write. Это
можно легко сделать вызвав функцию базового класса. Ниже приведе-
ны отрывки из определений функции TView::write и TGroup::write:
void TView::write(opstream& os)
{
ushort saveState =
state & ~(sfActive | sfSelected | sfFocused | sfExposed);
os << origin << size << cursor
<< growMode << dragMode << helpCtx
<< saveState << options << eventMask;
}
...
void TGroup::write(opstream& os)
(
unshort index;
TView::write(os);
TGroup *ownerSave = owner;
owner = this;
int count = indexOf(last);
os << count;
forEach(doPut,&os);
if (current == 0)
index = 0;
else
index = indexOf(current);
os << index;
owner = ownerSave;
)
Действия функции read аналогичны функции write. Каждый
класс, взаимодействующий с потоками, должен переопределять пустую
виртуальную функцию read, наследуемую от TStreamable. Это часто
делается при помощи расширения этой же функции базового класса
для обеспечения обработки дополнительных данных.
Turbo Vision для С++ = 205 =
При определенных случаях чтения возникают некоторые вопросы,
которые обсуждены ниже. Если чтение объекта из потока выполняется
в уже имеющийся объект этого типа, то прочитанные данные просто
преобразуются к этому типу. Однако, если изначально не существует
такого "пустого" объекта, его необходимо создать, а только после
этого производить восстановление объекта из потока. Эту работу
выполняет так называемый "строитель", который выделяет необходи-
мое количество памяти и устанавливает указатели на таблицу вирту-
альных методов. Каждый класс, взаимодействующий с потоками, дол-
жен включать в себя определение "строителя" и конструктор build.
Все выше рассмотренные функции уже определены для стандартных
классов Turbo Vision. Если вы хотите создать свои собственные
классы, взаимодействующие с потоками, то вы должны определить их
заново.
Конструкторы классов, взаимодействующих с потоками
-----------------------------------------------------------------
Как сказано выше, классы, чьи объекты, независимо от того
статические они или автоматические, могут использоваться в опера-
циях ввода/вывода в потоки, и должны иметь специальные конструк-
торы, которые имеют только один аргумент: streamableInit. Обычно
такие конструкторы объявляются как protected:
class TMyStreamable : public virtual TBase, public TStreamable
{
...
protected:
TMyStreamable(StreamableInit s);
...
};
Тип данных StreamableInit является перечисляемым типом
(enum) с одним членом streamableInit. Когда происходит создание
объекта при помощи конструктора:
TMyStreamable str(streamableInit);
то конструкторы для любых содержащихся указателей не вызываются,
как было бы в случае вызова стандартного конструктора. Вместо
этого происходит простое выделение памяти. Когда необходимо про-
читать объект из потока в объект Str, сначала нужно создать
пустой объект, а только после этого выполнять чтение:
TMyStreamable str(streamableInit);
// создание "пустого" str
...
ifpstream ifps("str.sav"); //открытие потока
ifps >> str; //чтение объекта в str
...
Turbo Vision для С++ = 206 =
Таким образом, объекты, созданные при помощи streamableInit,
такие как str, не могут корректно использоваться до тех пор, пока
они не будут инициализированы с помощью операций чтения из пото-
ка. Между прочим, количество и имена членов класса не имеют зна-
чения, так как аргументы, передаваемые конструктору однозначно
различаются по типу.
Функция build может быть определена следующим образом:
TStreamable *TMyStreamableClass::build()
{
return new TMyStreamableClass(streamableInit);
}
TMyStreamableClass::TMyStreamable(StreamableInit s);
TBaseClass(streamableInit)
{
}
Другими словами, конструктор build просто вызывает конструк-
тор базового класса, который в свою очередь вызывает конструктор
своего базового класса и т.д.
Имена классов, взаимодействующих с потоками
-----------------------------------------------------------------
В каждом классе, взаимодействующем с потоками, необходимо
переопределить виртуальную функцию streamableName, объявленную
как private, и унаследованную от класса TStreamable. Она должна
возвращать уникальное имя класса как обыкновенную строку, которая
заканчивается символом '\0'.
class TMyStreamable : public virtual TBase, public TStreamable
{
...
private:
virtual const char *streamableName() const;
{ return "TMyStreamable"; }
Для стандартных классов TurboVision это уже сделано. Если же
вы определите свои собственные классы, которые будут работать с
потоками, то необходимо сделать это самостоятельно. Обычно, это
выполняется так, как показано в последнем примере, т.е. вы должны
возвратить streamableName. Обработчик потоков использует уникаль-
ное имя класса для распознавания их в своей базе данных.
Turbo Vision для С++ = 207 =
Использование обработчика потоков
-----------------------------------------------------------------
Имеются три операции для использования обработчика потоков:
1. Компоновка кода с обработчиком потока.
2. Создание объектов, которые могут взаимодействовать с по-
током.
3. Использование объекта.
Рассмотрим эти операции более подробно.
Компоновка кода с обработчиком потока
Каждый класс, использующий потоки, должен определить три
функции read, write и build, разработанные для обработки объектов
в потоках.
Эти функции должны быть известны обработчику потоков и ком-
поноваться с любой прикладной программой, которая использует воз-
можности обработчика потоков. Компоновка, известная еще как ре-
гистрация класса, выполняется однократно посредством вызова мак-
рокоманды __Link. При этом вызов происходит с параметром
RClassName, где ClassName - имя регистрируемого класса, исключая
первую букву T. Например, если компонуется класс TChDirDialog, то
необходимо написать следующее выражение:
__Link(RChDirDialog);
Создание и использование объектов потока
Создание объектов потока ipstream и opstream требует объяв-
ления их с подходящими аргументами, также как и создание объектов
iostream. Чтобы сохранить объект, обеспечивающий диалог с пользо-
вателем, в файле DLG.SAV, достаточно объявить объект ofpstream
следующим образом:
// Детальная информация о конструкторах потоков и их
// аргументах приведена в главе 13.
TChDirDialog cdlg;
...
// создание диалога
ofpstream of ("dlg.sav");
//открытие выходного потока файла
of << cdlg;
// запись диалога в файл потока
Turbo Vision для С++ = 208 =
Здесь используется конструктор, который был вызван с аргу-
ментами, принимаемыми по умолчанию:
ofpstream(const char *filename, int mode = ios::out, int
prot = filebuf::openprot)
Теперь рассмотрим типичную операцию чтения:
TChDirDialog cdlg (streamableInit);
// обращение к созданному конструктору;
// создание скелетона объекта
ifpstream ifps("dlv.sav");
// открытие входного потока файла
ifps >> cdlg;
// чтение диалога из потока в cdlg
Turbo Vision для С++ = 209 =
Коллекции в потоках
-----------------------------------------------------------------
В главе 7 "Коллекции" было рассказано, как коллекции могут
содержать различные, хотя и взаимосвязанные, объекты. Аналогичные
полиморфные возможности имеются у потоков. Они могут быть исполь-
зованы для записи коллекций на диск, сохранения их там, а затем
для восстановления в другое время и, даже, другой программой.
Вернитесь назад и посмотрите пример из файла TVGUID20.CPP. Что
еще необходимо сделать, чтобы программа могла записывать коллек-
ции в поток?
Ответ чрезвычайно прост. Во-первых, надо начать с базового
класса TGraphObject и "научить" его посылать свои данные (x и y)
в поток. Для этого и существует функция класса write. Затем опре-
делите новые функции write для каждого "потомка" класса
TGraphObject, если, конечно, они добавляют свои поля данных
(TGraphCircle добавляет радиус в виде поля radius; TGraphRec до-
бавляет ширину и длину - width и height).
Кроме того необходимо определить функции readitem и
writeitem, которые читают и возвращают элемент из ipstream, и пи-
шут элемент в opstream, соответственно.
Перед тем как взаимодействовать с потоками надо зарегистри-
ровать все классы. На этом подготовительный этап заканчивается.
Теперь с потоками можно манипулировать точно также, как и с обык-
новенными файлами: сначала объявляется переменная (объект) пото-
ка; создается новый поток, коллекция записывается в поток при по-
мощи короткого выражения и, наконец, поток закрывается.
Добавление функции write
Ниже приводятся объявления функций классов write. Заметьте,
что TGraphPoint не нуждается в такой функции, так как он не до-
бавляет никаких новых данных к унаследованным от TGraphObject.
class TGraphObject : public TObject
{
public:
...
virtual void write(opstream& os);
...
};
class TGraphCircle : public TGraphObject
{
public:
int radius;
...
virtual void write(opstream& os);
};
class TGraphRect : public TgraphObject
{
Turbo Vision для С++ = 210 =
public:
...
int width, height;
...
virtual void write(opstream& os);
};
Реализация функций write довольно проста. Каждый объект вы-
зывает наследуемую функцию write, которая сохраняет все наследуе-
мые данные. Затем вызывается write, принадлежащая потоку, которая
и записывает данные в поток.
Ниже приведен текст из файла TVGUID21.CPP.
void TGraphObject::write( opstream& os )
{
os << x << y;
}
void TGraphCircle::write( opstream& os )
{
TGraphObject::write( os );
os << radius;
}
void TGraphRect::write( opstream& os )
{
TGraphObject::write( os );
os << width << height;
}
В этом примере показывается как можно написать свои собс-
твенные функции read, write и build и регистрировать классы для
их взаимодействия с потоками.
Сохранение и восстановление рабочей области
-----------------------------------------------------------------
Если сохраняемым в потоке объектом является рабочая область,
то она будет сохранена вместе со всеми своими элементами: рабочей
средой, которая окружает программу, включая все текущие отобража-
емые объекты. Если необходима возможность сохранения пользовате-
лем рабочей области, то все возможные отображаемые объекты должны
иметь соответствующие функции read и write, а все отображаемые
объекты быть зарегистрированы, так как содержимое рабочей области
в любой момент может понадобиться пользователю.
Можно пойти еще дальше и сохранять и восстанавливать целые
прикладные программы. Объект TApplication обладает способностью
сохранять и восстанавливать самого себя.
Turbo Vision для С++ = 211 =