|
Программирование >> Поддержка объектно-ориентированного программирования
Функция has base() ищет базовые классы с помощью имеющегося в Type info списка базовых классов. Хранить информацию о том, является ли базовый класс частным или виртуальным, не нужно, поскольку все ошибки, связанные с ограничениями доступа или неоднозначностью, будут выявлены при трансляции. class base iterator { short i; short alloc; const Type info* b; public: const Type info* operator() (); void reset() { i = 0; } base iterator(const Type info* bb, int direct=0); ~base iterator() { if (alloc) delete[] (Type info*)b; } В следующем примере используется необязательный параметр для указания, следует ли рассматривать все базовые классы (direct==0) или только прямые базовые классы (direct==1). base iterator::base iterator(const Type info* bb, int direct) i = 0; if (direct) { использование списка прям1х базовых классов b = bb; alloc = 0; return; создание списка прямых базовых классов: int n = число базовых b = new const Type info*[n+1]; занести базовые классы в b alloc = 1; return; const Type info* base iterator::operator() () const Type info* p = &b[i]; if (p) return p; Теперь можно задать операции запросов о типе с помощью макроопределений: #define static type info(T) T::info() #define ptr type info(p) ((p)->get info()) #define ref type info(r) ((r).get info()) #define ptr cast(T,p) \ (T::info()->can cast((p)->get info()) ? (T*)(p) : 0) #define ref cast(T,r) \ (T::info()->can cast((r).get info()) \ ? 0 : throw Bad cast(T::info()->name()), (T&)(r)) Предполагается, что тип особой ситуации Bad cast (Ошибка приведения) описан так: class Bad cast { const char* tn; ... public: Bad cast(const char* p) : tn(p) { } const char* cast to() { return tn; } ... В разделе $$4.7 было сказано, что появление макроопределений служит сигналом возникших проблем. Здесь проблема в том, что только транслятор имеет непосредственный доступ к литеральным типам, а макроопределения скрывают специфику реализации. По сути для хранения информации для динамических запросов о типах предназначена таблица виртуальных функций. Если реализация непосредственно поддерживает динамическую идентификацию типа, то рассматриваемые операции можно реализовать более естественно, эффективно и элегантно. В частности, очень просто реализовать функцию ptr cast(), которая преобразует указатель на виртуальный базовый класс в указатель на его производные классы. 13.5.3 Как создать систему динамических запросов о типе Здесь показано, как можно прямо реализовать динамические запросы о типе, когда в трансляторе таких возможностей нет. Это достаточно утомительная задача и можно пропустить этот раздел, так как в нем есть только детали конкретного решения. Классы set и slist set из $$1 3.3 следует изменить так, чтобы с ними могли работать операции запросов о типе. Прежде всего, в базовый класс set нужно ввести функции-члены, которые используют операции запросов о типе: class set { public: static const Type info info obj; virtual typeid get info() const; static typeid info(); ... При выполнении программы единственным представителем объекта типа set является set::info obj, который определяется так: const Type info set::info obj( set ,0); С учетом этого определения функции тривиальны: typeid set::get info() const { return &info obj; } typeid set::info() { return &info obj; } typeid slist set::get info() const { return &info obj; } typeid slist set::info() { return &info obj; } Виртуальная функция get info() будет предоставлять операции ref type info() и ptr type info(), а статическая функция info() - операцию static type info(). При таком построении системы запросов о типе основная трудность на практике состоит в том, чтобы для каждого класса объект типа Type info и две функции, возвращающие указатель на этот объект, определялись только один раз. Нужно несколько изменить класс slist set: class slist set : public set, private slist { ... public: static const Type info info obj; virtual typeid get info() const; static typeid info(); ... static const Type info* slist set b[] = { &set::info obj, &slist::info obj, 0 }; const Type info slist set::info obj( slist set ,slist set b); typeid slist set::get info() const { return &info obj; } typeid slist set::info() { return &info obj; } if (ref type info(s)==static type info(Circle)) { для этой фигуры ничего не надо }else if (ref type info(s)==static type info(Triangle)) { вращение треугольника else if (ref type info(s)==static type info(Square)) { вращение квадрата ... Если для переключателя по типу поля мы используем динамическую информацию о типе, то тем самым нарушаем в программе принцип модульности и отрицаем сами цели объектно-ориентированного программирования. К тому же это решение чревато ошибками: если в качестве параметра функции будет передан объект производного от Circle класса, то она сработает неверно (действительно, 13.5.4 Расширенная динамическая информация о типе В классе Type info содержится только минимум информации, необходимой для идентификации типа и безопасных операций приведения. Но поскольку в самом классе Type info есть функции-члены info() и get info(), можно построить производные от него классы, чтобы в динамике определять, какие объекты Type info возвращают эти функции. Таким образом, не меняя класса Type info, пользователь может получать больше информации о типе с помощью объектов, возвращаемых функциями dynamic type() и static type(). Во многих случаях дополнительная информация должна содержать таблицу членов объекта: struct Member info { char* name; Type info* tp; int offset; class Map info : public Type info { Member info** mi; public: static const Type info info obj; virtual typeid get info() const; static typeid info(); функции доступа Класс Type info вполне подходит для стандартной библиотеки. Это базовый класс с минимумом необходимой информации, из которого можно получать производные классы, предоставляющие больше информации. Эти производные классы могут определять или сами пользователи, или какие-то служебные программы, работающие с текстом на С++, или сами трансляторы языка. 13.5.5 Правильное и неправильное использование динамической информации о типе Динамическая информация о типе может использоваться во многих ситуациях, в том числе для: объектного ввода-вывода, объектно-ориентированных баз данных, отладки. В тоже время велика вероятность ошибочного использования такой информации. Известно,что в языке Симула использование таких средств, как правило, приводит к ошибкам. Поэтому эти средства не были включены в С++. Слишком велик соблазн воспользоваться динамической информацией о типе, тогда как правильнее вызвать виртуальную функцию. Рассмотрим в качестве примера класс Shape из $$1.2.5. Функцию rotate можно было задать так: void rotate(const Shape& s) неправильное использование динамической информации о типе
|
© 2006 - 2025 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |