Jenter Алекс
Шрифт:
Реализация сериализации в MFC выглядела очевидной и неинтересной до того момента, когда мне понадобилось создать несколько типов документов в одном приложении. Я заметил, что когда я открывал в программе файл, MFC корректно создавала объект документа нужного типа и вызывала соответствующую функцию Serialize. Это происходило, несмотря на то, что я не написал ни строчки кода, чтобы помочь MFC в создании документов. По крайней мере, я так считал:
Чтобы создавать документ или любой другой вид объектов "на лету", MFC должна решить три проблемы:
Проблема 1. По мере необходимости должны создаваться объекты произвольных типов, но оператор new может работать только с явно указанным типом, поэтому для CObject нужно реализовать некое подобие "виртуальных конструкторов".
Проблема 2. Разработчики должны иметь возможность легко "обучать" CObject создавать новые типы классов. В идеале, это должно делаться в определении и/или реализации класса.
Проблема 3. Необходим механизм сопоставления для создания объекта нужного типа по информации, прочитанной из файла. Этот механизм не может быть жестко зашит в MFC, потому что разработчики все время добавляют новые типы данных.
Как будет продемонстрировано далее, MFC элегантно решает эти проблемы, используя реестр с автоматической регистрацией типов [здесь и далее под реестром понимается программная структура, а не реестр WIndows – прим.пер.] и реализуя виртуальные конструкторы на основе зарегистрированных типов. Идентификация типов во время исполнения (RTTI) библиотеки MFC – вот краеугольный камень этой архитектуры.
Чтобы осуществить идентификацию объектов во время выполнения, MFC создает в приложении реестр классов, унаследованных от CObject. Этот реестр никак не связан с OLE-реестром, но их концепции схожи. Реестр типов представляет собой связанный список структур CRuntimeClass, в котором каждая структура описывает один класс-наследник CObject. На листинге 1 показано внутреннее устройство структуры CRuntimeClass.
Листинг 1
Весь фокус в том, что типы из этого реестра не прописаны ни в одной таблице. Первый ключ к разгадке этого феномена находится в начале файла SCRIBDOC.H из MFC-примера "Scribble". Начало объявления класса выглядит так, как показано в примере 1(a).
Пример 1: (a) Начало объявления класса; (b) после обработки препроцессором макрос DECLARE_DYNCREATE разворачивается в несколько новых членов класса.
(a)
(b)
В документации сказано, что макрос DECLARE_DYNCREATE позволяет классам-наследникам CObject создаваться динамически во время выполнения. Хотя это определение абсолютно верно, то, что происходит внутри этого макроса, гораздо интереснее. После обработки препроцессором макрос DECLARE_DYNCREATE разворачивается в несколько новых членов класса, как показано в примере 1(b) (Все примеры взяты из MFC 3.1 и Visual C++ 2.1. Я выровнял код, сгенерированный препроцессором, для повышения читабельности).