Язык программирования C++ от Страуструпа


.Класс Type_info - часть 2


Здесь средство динамических запросов о типе сознательно реализуется с помощью совсем простых классов. Так можно избежать привязки к определенной библиотеке. Реализация в расчете на конкретную библиотеку может быть иной. Можно, как всегда, посоветовать пользователям избегать излишней зависимости от деталей реализации.

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




- Начало -  - Назад -  - Вперед -