|
Информация о классе В объектах класса удобно хранить сведения о самом классе. Для этого лучше всего создать гомоморфный базовый класс для объектов классов. class Class { protected: Collection<Class> base_classes; Collection<Class> derived_classes; String class_name; Class() {}; // Класс становится абстрактным базовым public: // Функции доступа для получения имени и т.д. }; Состав хранимой информации в огромной степени зависит от потребностей приложения и от того, насколько увлеченно вы занимались SmallTalk на этой неделе. Имя класса и создание экземпляров по имени Базовая информация, которую может сообщить объект класса, - имя класса в виде некоторой символьной строки. Кроме того, можно хранить словарь всех объектов классов, индексируемый по имени класса. Если добавить к этому универсальный интерфейс к производящей функции, вам удастся реализовать возможность, которая не поддерживается в С++ напрямую - создание экземпляров по имени (instantiate by name). // Где-то в клиентской программе Class* c = gClasses.Find("Grandpa"); ???* g = (Grandpa*)c->make(???); Как видите, практическая польза такого подхода ограничивается некоторыми проблемами. Если мы уже знаем, что создается экземпляр Grandpa, то создание экземпляра по имени выглядит неразумно - нам будет трудно определить, к какому классу выполняется преобразование. Вдобавок данная схема не позволяет предоставить отдельные сигнатуры для производящих функций производных классов. Тем не менее, в некоторых ситуациях такая методика оказывается чрезвычайно полезной. Предположим, вы сохранили объект в виде потока байтов и теперь загружаете его. Если первые n байт содержит имя класса в виде символьной строки, вы сможете найти нужный объект класса для создания экземпляра. Как правило, реализация заканчивается созданием во всех классах Class функции make(istream&) или ее эквивалента. Программа приобретает следующий вид: // В коде чтения потока cin << className; Class* c = gClasses.Find(className); BasClass* obj = c->make(cin); Иерархия классов Сведения об иерархии классов можно хранить по разному, но в конечном счете все сводится к структурам данных с экземплярами Class. Выше был представлен один из вариантов: вести в каждом Class две коллекции, по одной для базовых и производных классов. Конечно, это следует понимать условно - речь идет об иерархии не объектов Class, а представленных ими классов. Необходимость различать понятия Class и «класс» наверняка вызовет у вас головную боль, но у поклонников SmallTalk и Lisp это считается хорошим развлечением и признаком мастерства. Другой способ - ввести одну глобальную структуру данных с парами (базовый, производный), индексируемую в обоих направлениях. В некоторых ситуациях вместо пар используются триплеты (базовый, производный, порядок), чтобы базовые классы перечислялись в порядке их объявления в соответствующем классе. Описания переменных класса В некоторых ситуациях объекты Class стоит сделать достаточно умными, чтобы они могли получать экземпляры представляемого класса и описывать их структуру. Все начинается с переменных класса. В наиболее прямолинейном варианте Class возвращает итератор, который в свою очередь возвращает пару смещение/Class, описывающую смещение переменной внутри экземпляра и ее Class. Имеет смысл создать перечисления для трех подмножеств переменных класса: 1. Все переменные, включая встроенные типы (такие как int). Для представления примитивных типов вам придется создать фиктивные классы, производные от Class. 2. Только переменные невстроенных типов, для которых обычно и так существует свой Class. 3. Только указатели и ссылки на другие объекты. Последний вариант играет особенно важную роль в некоторых нетривиальных алгоритмах сборки мусора. Описания функций класса В еще более редких ситуациях объект Class должен описывать набор функций представленного им класса. При этом вы фактически начинаете играть роль компилятора С++, так что не перегибайте палку. И снова оптимальным представлением оказывается итератор. Для каждой функции можно возвращать любую информацию, от простейшей (ее имени) до более сложной (адрес, имена, типы и порядок аргументов и тип возвращаемого значения). Некоторые проблемы просто выходят за рамки С++; если вам захочется проделать нечто подобное, стоит серьезно подумать об использовании настоящего динамического языка. Коллекции экземпляров Класс Class предоставляет еще одну интересную возможность - ведение коллекции всех экземпляров класса. Это может быть либо отдельная коллекция для каждого Class, либо одна глобальная структура данных с парами (Class, экземпляр). Если выбран второй вариант и коллекция индексируется в обоих направлениях, она оказывается чрезвычайно полезной при отладке («Покажи мне все экземпляры класса x»), а также может применяться для ответов на вопросы вроде «Каков класс данного экземпляра?» без физического хранения адреса объекта Class в каждом экземпляре. Такое решение работает только для экземпляров верхнего уровня, создаваемых производящими функциями; вложенные объекты (переменные или базовые классы) в этот реестр не попадут. Статистика Показатели сыплются как из рога изобилия - количество экземпляров, в данный момент находящихся в памяти; общее количество экземпляров, созданных с момента запуска программы; статистические профили с описанием, когда и как создавались экземпляры… Возможности ограничены только тем, сколько времени вы сможете им посвятить. Если вы работаете на повременной оплате, не ограничивайте себя - ведь при желании обоснование можно придумать для любого показателя. А если нет, подумайте, окупятся ли потраченные усилия. |
Copyright 2005. Климов Александр. All Right Reserved.