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

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


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

Два созданных по одному шаблону типа будут различны и между ними невозможно отношение наследования кроме единственного случая, когда у этих типов идентичны параметры шаблона. Например:

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 мы видели, что шаблоны типа дают удобное средство для создания целых семейств классов. Без шаблонов создание таких семейств только с помощью производных классов может быть утомительным занятием, а значит, ведущим к ошибкам. С другой стороны, если отказаться от производных классов и использовать только шаблоны, то появляется множество копий функций-членов шаблонных классов, множество копий описательной части шаблонных классов и во множестве повторяются функции, использующие шаблоны типа.



Содержание раздела