Бьерн Страуструп - Язык программирования С++. Главы 11-13
Страница 47. Динамическая информация о типе



13.5 Динамическая информация о типе

Иногда бывает полезно знать истинный тип объекта до его использования
в каких-либо операциях. Рассмотрим функцию my(set&) из $$13.3.

           void my_set(set& s)
           {
              for ( T* p = s.first(); p; p = s.next()) {
                  // мой код
              }
              // ...
           }

Она хороша в общем случае, но представим,- стало известно,
что многие параметры множества представляют собой объекты типа
slist. Возможно также стал известен алгоритм перебора элементов, который
значительно эффективнее для списков, чем для произвольных
множеств. В результате эксперимента удалось выяснить, что именно
этот перебор является узким местом в системе. Тогда, конечно, имеет
смысл учесть в программе отдельно вариант с slist. Допустив возможность
определения истинного типа параметра, задающего множество, функцию
my(set&) можно записать так:

         void my(set& s)
         {
            if (ref_type_info(s) == static_type_info(slist_set)) {
               // сравнение двух представлений типа

               // s типа slist

               slist& sl = (slist&)s;
               for (T* p = sl.first(); p; p = sl.next()) {

                  // эффективный вариант в расчете на list

               }
          }
          else {

             for ( T* p = s.first(); p; p = s.next()) {

                  // обычный вариант для произвольного множества

              }
          }
          // ...
       }

Как только стал известен конкретный тип slist, стали
доступны определенные операции со списками, и даже стала возможна
реализация основных операций подстановкой.
   Приведенный вариант функции действует отлично, поскольку
slist - это конкретный класс, и действительно имеет смысл отдельно
разбирать вариант, когда параметр является slist_set. Рассмотрим
теперь такую ситуацию, когда желательно отдельно разбирать вариант как
для класса, так и для всех его производных классов. Допустим, мы
имеем класс dialog_box из $$13.4 и хотим узнать, является ли он
классом dbox_w_str. Поскольку может существовать много производных
классов от dbox_w_str, простую проверку на совпадение с ним
нельзя считать хорошим решением. Действительно, производные классы
могут представлять самые разные варианты запроса строки. Например,
один производный от dbox_w_str класс может предлагать пользователю
варианты строк на выбор, другой может обеспечить поиск в каталоге
и т.д. Значит, нужно проверять и на совпадение со всеми производными
от dbox_w_str классами. Это так же типично для узловых классов, как
проверка на вполне определенный тип типична для абстрактных классов,
реализуемых конкретными типами.

         void f(dialog_box& db)
         {
            dbox_w_str* dbws = ptr_cast(dbox_w_str, &db);
            if (dbws) {  // dbox_w_str
               // здесь можно использовать dbox_w_str::get_string()
            }
            else {

              // ``обычный'' dialog_box
            }

            // ...
          }

  Здесь "операция" приведения ptr_cast() свой второй параметр
(указатель) приводит к своему первому параметру (типу) при условии, что
указатель настроен на объект тип, которого совпадает с заданным
(или является производным классом от заданного типа). Для проверки
типа dialog_box используется указатель, чтобы после приведения его
можно было сравнить с нулем.
   Возможно альтернативное решение с помощью ссылки на dialog_box:

          void g(dialog_box& db)
          {
            try {
                dbox_w_str& dbws = ref_cast(dialog_box,db);

                // здесь можно использовать dbox_w_str::get_string()

             }
             catch (Bad_cast) {

                // ``обычный'' dialog_box

             }

             // ...
          }

Поскольку нет приемлемого представления нулевой ссылки, с которой
можно сравнивать, используется особая ситуация, обозначающая ошибку
приведения (т.е. случай, когда тип не есть dbox_w_str). Иногда
лучше избегать сравнения с результатом приведения.
   Различие функций ref_cast() и ptr_cast() служит хорошей
иллюстрацией различий между ссылками и указателями: ссылка обязательно
ссылается на объект, тогда как указатель может и не ссылаться,
поэтому для указателя часто нужна проверка.

 
« Предыдущая статья   Следующая статья »