Язык программирования C++ для профессионалов



           

Абстрактные типы - часть 2


void your(set& s) { for (T* p = s.first(); p; p = s.next()) { // ваш код } // ... }

Стало очевидным сходство между двумя функциями, и теперь достаточно иметь только одну версию для каждой из функций my() или your(), поскольку для общения с slist_set и vector_set обе версии используют интерфейс, определяемый классом set:

void user() { slist_set sl; vector_set v(100);

my(sl); your(v);

my(v); your(sl); }

Более того, создатели функций my() и your() не обязаны знать описаний классов slist_set и vector_set, и функции my() и your() никоим образом не зависят от этих описаний. Их не надо перетранслировать или как-то изменять, ни если изменились классы slist_set или vector_set ни даже, если предложена новая реализация этих классов. Изменения отражаются лишь на функциях, которые непосредственно используют эти классы, допустим vector_set. В частности, можно воспользоваться традиционным применением заголовочных файлов и включить в программы с функциями my() или your() файл определений set.h, а не файлы slist_set.h или vector_set.h.

В обычной ситуации операции абстрактного класса задаются как чистые виртуальные функции, и такой класс не имеет членов, представляющих данные (не считая скрытого указателя на таблицу виртуальных функций). Это объясняется тем, что добавление невиртуальной функции или члена, представляющего данные, потребует определенных допущений о классе, которые будут ограничивать возможные реализации. Изложенный здесь подход к абстрактным классам близок по духу традиционным методам, основанным на строгом разделении интерфейса и его реализаций. Абстрактный тип служит в качестве интерфейса, а конкретные типы представляют его реализации.

Такое разделение интерфейса и его реализаций предполагает недоступность операций, являющихся "естественными" для какой-то одной реализации, но не достаточно общими, чтобы войти в интерфейс. Например, поскольку в произвольном множестве нет упорядоченности, в интерфейс set нельзя включать операцию индексирования, даже если для реализации конкретного множества используется массив. Это приводит к ухудшению характеристик программы из-за отсутствия ручной оптимизации. Далее, становится как правило невозможной реализация функций подстановкой (если не считать каких-то конкретных ситуаций, когда настоящий тип известен транслятору), поэтому все полезные операции интерфейса, задаются как вызовы виртуальных функций. Как и для конкретных типов здесь плата за абстрактные типы иногда приемлема, иногда слишком высока.

Подводя итог, перечислим каким целям должен служить абстрактный тип:

  1. определять некоторое понятие таким образом, что в программе могут сосуществовать для него несколько реализаций;
  2. применяя виртуальные функции, обеспечивать достаточно высокую степень компактности и эффективности выполнения программы;
  3. сводить к минимуму зависимость любой реализации от других классов;
  4. представлять само по себе осмысленное понятие.




Содержание  Назад  Вперед