Ограничений на наследование нет — разрешаются все разновидности:
• шаблон наследует от обычного класса;
• класс наследует от шаблона;
• шаблон наследует от шаблона.
Необходимо также разобраться в отношениях дружественности при использовании шаблонов:
• дружественная функция для шаблонного класса;
• дружественная функция-шаблон для шаблонного или обычного класса;
• дружественный класс для шаблонного класса.
• Шаблонные функции (шаблоны функций) рассматриваются в следующей главе.
Шаблон вполне может наследовать от простого класса. Например, пусть мы хотим, чтобы все классы, сгенерированные из некоторого шаблона, имели общие статические поля и (или) методы. Это можно реализовать как раз путем наследования шаблона от простого класса — такой класс называется независимым базовым классом. Мы должны вынести все общие статические члены в нешаблонный базовый класс, от которого и должен наследовать наш шаблон (листинг 11.12).
Листинг 11.12. Наследование шаблона от класса struct Common
{ static unsigned long Number;
};
template <typename T> class X: public Common {//... }:
unsigned long Common::Number = 0;
Статическое поле Number будет общим для всех классов, инстанцированных из шаблона X.
Наследование шаблона от нешаблонного класса можно использовать в некоторых случаях для решения проблемы «разбухания» кода при инстанцировании шаблонов. Например, нам потребовалась частичная специализация шаблона TStack (см. листинг 11.1) для указателей:
template <class Т> class TStack<T*> { //...
};
При инстанцировании этого шаблона в программе образуется несколько практически одинаковых классов, например:
TStack <int *> i;
TStack <double*> d;
TStack <unsigned long *> ul;
Однако можно существенно сократить объем кода, использовав полную специализацию шаблона и делегирование:
template <> class TStack <void*> // полная специапизация
{ //...
typedef std::size_t size_type;
void push(const void* &t);
void* top();
void pop();
bool emptyO const;
int countO const
};
Эта полная специализация является обычным классом. Частично специализированный шаблон может наследовать от полной специализации, например:
template <class Т>
class TStack<T*>: private TStack<void*> { //...
typedef TStack<void*> TBase;
public:
void push(const T* &t) { TBase::push(t); } void pop() { TBase::pop(t); }
T* top() { return static_cast<T*>(TBase::topO); } //...
В этом шаблоне реализация методов чрезвычайно экономная: метод шаблонного класса вызывает базовый (делегирует работу). Таким образом, имеем существенную экономию объема кода, так как основная реализация выполнена в единственном экземпляре в классе TStack<void*>. Такой подход еще и сокращает время трансляции, так как основной «длинный» класс транслируется только один раз, а наследники — короткие.
Но гораздо интереснее наследование простого класса от шаблона. Вспомним использование статических полей для подсчета объектов класса (см. листинг 4.23). Вставлять в каждый класс поле-счетчик и прописывать код его изменения в конструкторе и деструкторе быстро надоедает — хочется написать код один раз и использовать постоянно.
Программистам важно держать свой компьютер в безопасности - http://itonline.kz/katalog/programmy/antivirusy/dr-web/.
Наследование от общего базового класса, в котором реализован подсчет объектов, не решает проблемы, так как унаследованное статическое поле единственное для всех наследников. Таким образом, выполняется подсчет объектов не каждого класса, а всех объектов иерархии. |