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(), которая преобразует указатель на виртуальный базовый класс в указатель на его производные классы.


Назад  |  Вперёд


OtDiatlovaOU
К началу книги

Оформление и дизайн книги OtDiatlovaOU.
Вся книга, архив.