/*____________________________________________________________________
Программирование на Visual C++
выпуск No. 2
20/6/2000
____________________________________________________________________
*/
Приветствую вас, уважаемые подписчики!
Очень рад, что число вас растет, несмотря на то, что рассылка еще не
вышла из категории "Рассылки для каждого" и не была официально
анонсирована. Если новоподписавшимся интересно узнать, о чем мы в
рассылке беседуем, они могут посмотреть в архиве первый выпуск.
Сегодня мы немного поговорим об устройстве MFC, а также рассмотрим
один интересный вопрос.
/// НЕМНОГО ТЕОРИИ ///////////////////////////////////////////////////
Как известно, основой всех основ в MFC является класс CObject.
Основным назначением этого класса является предоставление некоторых
базовых возможностей всем своим наследникам, а именно доступ к
информации о классе во время выполнения и поддержка сериализации, т.е.
сохраняемости объектов.
Однако уровень предоставляемых возможностей варьируется в зависимости
от вашего выбора; он зависит от включения определенных макросов
объявления и реализации при создании классов - наследников CObject.
Без сомнения, вы с этими макросами уже сталкивались, например в коде,
который генерируют Wizard'ы. Пришла пора разобраться с ними более
детально.
Итак, на характер вашего класса, производного от CObject, вы можете
влиять с помощью нескольких макросов. Существуют определенные пары
макросов - один включается в объявление класса (имеет префикс
DECLARE_), а соответствующий ему - в реализацию (префикс IMPLEMENT_).
Первая пара макросов - это DECLARE_DYNAMIC|IMPLEMENT_DYNAMIC. С
помощью включения этих макросов в код вашего класса вы можете включить
одну из базовых функций CObject - способность узнавать класс объекта
прямо во время выполнения программы. Для этого вы можете пользоваться
функцией IsKindOf() в связке с макросом RUNTIME_CLASS, который
возвращает указатель на структуру CRuntimeClass ( где хранится вся
информация о классе: имя, размер, версия, информация о базовом классе,
указатель на конструктор объекта и т.д.)
Следующая пара - DECLARE_DYNCREATE|IMPLEMENT_DYNCREATE аналогична
первой, но к возможности получать информацию о классе добавляется еще
и возможность создавать объекты этого класса во время выполнения.
Объект создается функцией CreateObject структуры CRuntimeClass. Вот
пример:
CRunTimeClass pClass = RUNTIME_CLASS(СMyObject);
// получаем ук-ль на структуру CRunTimeClass
CObject pNewObject= pClass->CreateObject();
// создаем новый объект нужного класса
ASSERT(pNewObject->IsKindOf(RUNTIME_CLASS(CMyObject));
// проверяем класс объекта
И, наконец, мы подошли к последней паре макросов DECLARE_SERIAL|
IMPLEMENT_SERIAL. Преобразование в последовательную форму и обратно -
сериализация - дает программисту возможность сохранения и восста-
новления объектов. Для того, чтобы воспользоваться этой возможностью,
в классе-наследнике нужно перекрыть виртуальную функцию Serialize().
Из нее обязательно нужно сначала вызвать родительскую версию. Одна и
та же функция используется как для сохранения, так и для
восстановления объекта. Какую операцию нужно произвести, она
определяет из своего единственного параметра ar типа CArchive&. Вот
пример:
void CMyObject::Serialize( CArchive& ar )
{
CObject::Serialize( ar ); // вызываем версию базового класса
if( ar.IsStoring() ) // если сохраняем,
{
ar << something; // то сохранить что-то
}
else // а иначе
{
ar >> something; // восстановить
}
}
Заметьте, что DECLARE_SERIAL|IMPLEMENT_SERIAL помимо сериализации
включают и те возможности, которые дают две первые пары - это
естественно, ведь если вы восстанавливаете объект, то вам понадобится
возможность создать его во время выполнения программы. Например,
приложению нужно сохранять и восстанавливать некоторый набор объектов
различного типа. А для вызова соответствующего конструктора при
восстановлении объекта нужно знать его тип. Механизм сериализации
сохраняет информацию об объекте вместе с теми данными, что вы
записываете явно в функции Serialize().
/// ВОПРОС - ОТВЕТ ///////////////////////////////////////////////////
Следующий вопрос поступил от одного из подписчиков:
Q При программировании элементов ActiveX, в этой технологии есть
возможность структурного хранения данных на диске, т.е. создание в
файле так называемых хранилищ и потоков (использование интерфейсов
IStream и IStorage), проще говоря - представление файла данных в виде
иерархической системы внутренних каталогов и файлов, которые там (в
данном файле данных) имеют свои строковые имена. Есть ли в MFC
возможность структурного хранения, скажем, используя объект класса
CArchive в переопределяемой функции Serialize(CArchive) класса,
производного от CDocument, ну и так далее? Конечно, этого можно
добиться, создав свои собственные наработки (а как хороша эта идея, я
имею в виду использование потоков и хранилищ), но все таки хочется
знать, есть ли такие возможности в MFC, чтобы не тратить зря время.
- Броник (krivoruchko@nvrsk.ru)
A Насколько мне известно, поддержки такой иерархической системы в
MFC нет. Во всяком случае, я ее не обнаружил. CArchive и Serialize для
этой цели явно не предназначены: в них важную роль играет
последовательность записи, т.е. в каком порядке вы что-то записали, в
таком нужно это и прочитать. Так что, скорее всего, придется писать
свой класс для этой цели. Конечно, это не слишком обнадеживает, но
зато этот класс можно будет использовать во всех дальнейших
программах, где потребуется такая форма хранения данных. Или - как
вариант - можно сделать просто класс-обертку для интерфейсов IStorage
и IStream. Конечно, в этом случае придется подключать библиотеку COM
(которую в MFC-приложениях, в принципе, никто не запрещает
использовать). Впрочем, если кто знает что-нибудь о существовании
такого механизма в MFC - пожалуйста, поделитесь с нами.
//////////////////////////////////////////////////////////////////////
Всего наилучшего и чтоб программы ваши не знали ошибок.
(С)Алекс Jenter mailto:jenter@mail.ru
Красноярск,2000.
.