13.5.2 Класс Type_info
В классе Type_info есть минимальный объем информации для реализации
операции ptr_cast(); его можно определить следующим образом:
class Type_info {
const char* n; // имя
const Type_info** b; // список базовых классов
public:
Type_info(const char* name, const Type_info* base[]);
const char* name() const;
Base_iterator bases(int direct=0) const;
int same(const Type_info* p) const;
int has_base(const Type_info*, int direct=0) const;
int can_cast(const Type_info* p) const;
static const Type_info info_obj;
virtual typeid get_info() const;
static typeid info();
};
Две последние функции должны быть определены в каждом производном
от Type_info классе.
Пользователь не должен заботиться о структуре объекта Type_info, и
она приведена здесь только для полноты изложения. Строка, содержащая
имя типа, введена для того, чтобы дать возможность поиска информации
в таблицах имен, например, в таблице отладчика. С помощью нее а также
информации из объекта Type_info можно выдавать более осмысленные
диагностические сообщения. Кроме того, если возникнет потребность
иметь несколько объектов типа Type_info, то имя может служить уникальным
ключом этих объектов.
const char* Type_info::name() const
{
return n;
}
int Type_info::same(const Type_info* p) const
{
return this==p || strcmp(n,p->n)==0;
}
int Type_info::can_cast(const Type_info* p) const
{
return same(p) || p->has_base(this);
}
Доступ к информации о базовых классах обеспечивается функциями
bases() и has_base(). Функция bases() возвращает итератор, который
порождает указатели на базовые классы объектов Type_info, а с
помощью функции has_base() можно определить является ли заданный класс
базовым для другого класса. Эти функции имеют необязательный параметр
direct, который показывает, следует ли рассматривать все базовые классы
(direct=0), или только прямые базовые классы (direct=1). Наконец,
как описано ниже, с помощью функций get_info() и info() можно
получить динамическую информацию о типе для самого класса Type_info.
Здесь средство динамических запросов о типе сознательно
реализуется с помощью совсем простых классов. Так можно избежать
привязки к определенной библиотеке. Реализация в расчете на
конкретную библиотеку может быть иной. Можно, как всегда, посоветовать
пользователям избегать излишней зависимости от деталей реализации.
Функция 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) { // использование списка прямых базовых классов
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) i++;
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 из $$13.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; }
Достарыңызбен бөлісу: |