Бьерн Страуструп - Язык программирования С++. Главы 5-8
Страница 85. Шаблоны типа и производные классы



8.7 Шаблоны типа и производные классы

 Мы уже видели, что сочетание производных классов (наследование) и
 шаблонов типа может быть мощным средством. Шаблон типа выражает
 общность между всеми типами, которые используются как его параметры,
 а базовый класс выражает общность между всеми представлениями
 (объектами) и называется интерфейсом. Здесь возможны некоторые
 простые недоразумения, которых надо избегать.
      Два созданных по одному шаблону типа будут различны и между ними
 невозможно отношение наследования кроме единственного случая, когда
 у этих типов идентичны параметры шаблона. Например:

          template<class T>
          class Vector { /* ... */ }

          Vector<int> v1;
          Vector<short> v2;
          Vector<int> v3;

 Здесь v1 и v3 одного типа, а v2 имеет совершенно другой тип. Из того
 факта, что short неявно преобразуется в int, не следует, что есть
 неявное преобразование Vector<short> в Vector<int>:

         v2 = v3;  // несоответствие типов

 Но этого и следовало ожидать, поскольку нет встроенного преобразования
 int[] в short[].
    Аналогичный пример:

         class circle: public shape { /* ... */ };

         Vector<circle*> v4;
         Vector<shape*> v5;
         Vector<circle*> v6;

 Здесь v4 и v6 одного типа, а v5 имеет совершенно другой тип. Из того
 факта, что существует неявное преобразование circle в shape и
 circle* в shape*, не следует, что есть неявные преобразования
 Vector<circle*> в Vector<shape*> или Vector<circle*>* в
 Vector<shape*>* :

         v5 = v6;  // несоответствие типов

 Дело в том, что в общем случае структура (представление) класса,
 созданного по шаблону типа, такова, что для нее не предполагаются
 отношения наследования. Так, созданный по шаблону класс может
 содержать объект типа, заданного в шаблоне как параметр, а не просто
 указатель на него. Кроме того, допущение подобных преобразований
 приводит к нарушению контроля типов:

        void f(Vector<circle>* pc)
        {
          Vector<shape>* ps = pc;  // ошибка: несоответствие типов
          (*ps)[2] = new square;   // круглую ножку суем в квадратное
                                   // отверстие (память выделена для
                                   // square, а используется для circle
       }

    На примерах шаблонов Islist, Tlink, Slist, Splist, Islist_iter,
 Slist_iter  и SortableVector мы видели, что шаблоны типа дают
 удобное средство для создания целых семейств классов. Без шаблонов
 создание таких семейств только с помощью производных классов
 может быть утомительным занятием, а значит, ведущим к ошибкам.
 С другой стороны, если отказаться от производных классов и использовать
 только шаблоны, то появляется множество копий функций-членов шаблонных
 классов, множество копий описательной части шаблонных классов и во
 множестве повторяются функции, использующие шаблоны типа.

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