Рейтинговые книги
Читем онлайн Программирование на Visual C++. Архив рассылки - Алекс Jenter

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 59 60 61 62 63 64 65 66 67 ... 156

Если у вас есть вопрос по программированию, вы можете задать его одном из форумов на RSDN.

Это все на сегодня. Не забывайте заходить на RSDN. До встречи! 

Алекс Jenter [email protected] Красноярск, 2001. Рассылка является частью Проекта RSDN

Программирование на Visual C++

Выпуск №42 от 29 апреля 2001 г.

Всем привет!

СТАТЬЯ 

Сериализация в MFC

Скорость, гибкость, типонезависимость

Автор: Джим Биверидж

Перевод: Олег Быков

Источник: www.ddj.com

Опубликовано: 17.04.2001

Версия текста: 1.0 

Я следил за разработкой многих коммерческих программных продуктов от начального проектирования до выпуска рабочей версии, поэтому я скептически отношусь к концепции "компонент – черный ящик", так как следовать ей все труднее и труднее по мере развития и усложнения проекта. Когда я впервые познакомился с механизмом сериализации в библиотеке MFC [адекватного перевода английского слова "serialization" нет, поэтому здесь я использовал транслитерацию. В статье этот термин применяется для обозначения процесса сохранения/восстановления данных – прим.пер.], мне стало интересно, насколько этот механизм гибок и производителен для коммерческого применения. В процессе исследования я обнаружил, что, несмотря на некоторые ограничения, механизм сериализации в MFC основан на современной теории объектно-ориентированного проектирования и более того, этот механизм не привязан к какому-то определенному типу и допускает свое дальнейшее развитие.

Использовать MFC-сериализации несложно. Любой класс, производный от CObject, может переопределить функцию Serialize(), принимающую в качестве параметра объект класса CArchive. В этой функции Вы можете добавить свой код для сохранения и восстановления любых данных Вашего класса.

Сериализация данных производится с помощью операторов operator<< и operator>>, совсем как в случае с классом iostream. Разница в том, что CArchive подразумевает только двоичный формат данных. Подобно iostream, в CArchive реализованы операторы для чтения и записи фундаментальных типов данных, таких как long и char. Отсутствие типа данных int упрощает переносимость между 16– и 32-битными платформами. Встроенные операторы также реализуют перестановку байтов для типов, которые это поддерживают. (За дополнительной информацией о совместимости между платформами с прямой и обратной записью байтов [Little-Endian и Big-Endian] обратитесь к книге "Endian-Neutral Software," by James R. Gillig, DDJ, October/November 1994).

Реализация сериализации в MFC выглядела очевидной и неинтересной до того момента, когда мне понадобилось создать несколько типов документов в одном приложении. Я заметил, что когда я открывал в программе файл, MFC корректно создавала объект документа нужного типа и вызывала соответствующую функцию Serialize(). Это происходило, несмотря на то, что я не написал ни строчки кода, чтобы помочь MFC в создании документов. По крайней мере, я так считал:

Проблемы, всюду проблемы

Чтобы создавать документ или любой другой вид объектов "на лету", MFC должна решить три проблемы:

Проблема 1. По мере необходимости должны создаваться объекты произвольных типов, но оператор new может работать только с явно указанным типом, поэтому для CObject нужно реализовать некое подобие "виртуальных конструкторов".

Проблема 2. Разработчики должны иметь возможность легко "обучать" CObject создавать новые типы классов. В идеале, это должно делаться в определении и/или реализации класса.

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

Как будет продемонстрировано далее, MFC элегантно решает эти проблемы, используя реестр с автоматической регистрацией типов [здесь и далее под реестром понимается программная структура, а не реестр WIndows – прим.пер.] и реализуя виртуальные конструкторы на основе зарегистрированных типов. Идентификация типов во время исполнения (RTTI) библиотеки MFC – вот краеугольный камень этой архитектуры.

Реестр типов

Чтобы осуществить идентификацию объектов во время выполнения, MFC создает в приложении реестр классов, унаследованных от CObject. Этот реестр никак не связан с OLE-реестром, но их концепции схожи. Реестр типов представляет собой связанный список структур CRuntimeClass, в котором каждая структура описывает один класс-наследник CObject. На листинге 1 показано внутреннее устройство структуры CRuntimeClass.

Листинг 1

struct CRuntimeClass {

 // Attributes

 LPCSTR m_lpszClassName;

 int m_nObjectSize;

 UINT m_wSchema; // номер схемы загруженного класса

 void (PASCAL* m_pfnConstruct)(void* p); // NULL => abstract class

 CRuntimeClass* m_pBaseClass;

 // Operations

 CObject* CreateObject();

 // Implementation

 BOOL ConstructObject(void* pThis);

 void Store(CArchive& ar);

 static CRuntimeClass* PASCAL Load(CArchive& ar, UINT* pwSchemaNum);

 // объекты CRuntimeClass, связанные в простой список

 CRuntimeClass* m_pNextClass;// список зарегистрированных классов

};

Весь фокус в том, что типы из этого реестра не прописаны ни в одной таблице. Первый ключ к разгадке этого феномена находится в начале файла SCRIBDOC.H из MFC-примера "Scribble". Начало объявления класса выглядит так, как показано в примере 1(a).

Пример 1: (a) Начало объявления класса; (b) после обработки препроцессором макрос DECLARE_DYNCREATE разворачивается в несколько новых членов класса.

(a)

class CScribDoc : public CDocument {

protected:

 // создавать только при сериализации

 CScribDoc();

 DECLARE_DYNCREATE(CScribDoc)

 ...

};

(b)

protected:

 static CRuntimeClass* __stdcall _GetBaseClass();

public:

 static  CRuntimeClass classCScribDoc;

 virtual CRuntimeClass* GetRuntimeClass() const;

 static void__stdcall Construct(void* p);

В документации сказано, что макрос DECLARE_DYNCREATE позволяет классам-наследникам CObject создаваться динамически во время выполнения. Хотя это определение абсолютно верно, то, что происходит внутри этого макроса, гораздо интереснее. После обработки препроцессором макрос DECLARE_DYNCREATE разворачивается в несколько новых членов класса, как показано в примере 1(b) (Все примеры взяты из MFC 3.1 и Visual C++ 2.1. Я выровнял код, сгенерированный препроцессором, для повышения читабельности).

Идентификация типов времени выполнения в MFC базируется на виртуальной функции GetRuntimeClass(). Информация о типе доступна для любого объекта — потомка CObject, который включает в себя макросы DECLARE_DYNAMIC, DECLARE_DYNCREATE, или DECLARE_SERIAL. Эта информация позволяет Вам определить, может ли объект быть приведен к типу унаследованного класса или принадлежат ли два объекта одному и тому же классу. Хотя Visual C++ и не поддерживает новый C++-оператор dynamic_cast [помним, что речь идет о Visual C++ 2.1 - прим.пер.], использование вышеописанной информации о типе времени выполнения даст тот же эффект.

Информация о типе времени выполнения объявляется при помощи статической переменной класса, в данном случае classCScribDoc. Это имя (без пробела внутри) создается в макросах DECLARE_xxx через оператор макро-конкатенации. Для доступа к этой переменной используются функции класса _GetBaseClass() и GetRuntimeClass(). GetRuntimeClass() является виртуальной, поэтому тип объекта может быть определен даже через указатель на CObject.

И наконец, статическая функция класса Construct() образует базис для использования реестра классов MFC как фабрики классов, которая может, когда требуется, создавать объекты произвольных типов. Для понимания работы Construct() необходимы дополнительные разьяснения.

Создание объекта

В книге Advanced C++: Programming Styles and Idioms (Addison-Wesley, 1992), Джеймс O. Коплин так описывает концепцию виртуального конструктора:

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

В MFC контекстом является информация, прочитанная из упорядоченного (serialized) архива [под архивом в этой статье понимается хранилище данных – прим.пер.]. Однако, виртуальный конструктор – это лишь концепция; никакая конструкция языка не реализует ее напрямую. Оператор new требует явного указания типа. Виртуальные конструкторы могут быть реализованы, если ввести в каждый класс статическую функцию, которая будет вызывать new для этого класса. Эта статическая функция-член будет вызываться при создании объекта определенного типа.

1 ... 59 60 61 62 63 64 65 66 67 ... 156
На этой странице вы можете бесплатно читать книгу Программирование на Visual C++. Архив рассылки - Алекс Jenter бесплатно.
Похожие на Программирование на Visual C++. Архив рассылки - Алекс Jenter книги

Оставить комментарий