Инстанцирование

 

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

 

class IntStack {

int stack[100];

. . .

};

class ControllerStack {

Controller* stack[100];

. . .

};

 

Другой, более разумный, подход – создать универсальный стек, который мог бы хранить элементы любого нужного нам типа. Для этого мы можем описать стек, содержащий указатели на нетипизированные элементы:

 

class Stack {

void* stack[100];

. . .
};

 

Однако это не безопасно с точки зрения типов. Никто не гарантирует нам, что пользователь не поместит в стек элемент одного типа, а взять захочет элемент другого типа.

Для реализации нашей идеи необходимо воспользоваться шаблоном или параметризованным классом. Шаблон служит для построения других классов и может быть пара­метризован другими классами, объектами или операциями.Использование шаблонов реализует в языке С++ особый тип полиморфизма – параметрический полиморфизм.

 

template <class Type> class Stack {

Type stack[100];

. . .

public:

void push(Type);

Type pop( );

. . .

};

. . .

template <class Type> void Stack <Type> :: push(Type el) { . . .}

template <class Type> Type Stack <Type> :: pop( ) { . . .}

 

Префикс template <class Type> делает Type параметром объявления, которому этот пре­фикс предшествует.

Инстанцирование – подстановка фактических параметров шаблона вместо формальных. В результате создается конкретный класс, который может иметь экземпляры.

Объявим нужные нам стеки:

 

typedef Stack < int > IntStack // синоним класса стеков целых чисел

typedef Stack < Controller* > ControllerStack // синоним класса стеков

// контроллеров

IntStack IS; // стек для целых чисел

ControllerStack CS; // стек для контроллеров

 

Объекты IS и CS – это экземпляры совершенно различных классов, которые даже не имеют общего суперкласса. Тем не менее они получены из одного параметризованного класса Stack.

Инстанцирование безопасно с точки зрения типов. По правилам C++ бу­дет отвергнута любая попытка поместить в стек или извлечь из него что-либо, кроме целых чисел или указателей на экземпляры класса Controller соответственно.

В языке С++ можно определять шаблоны не только классов, но и функций. В качестве примера рассмотрим определение шаблона функции, служащей для определения максимального из двух элементов.

 

template <class Type > Type max(Type x, Type y){

return (x > y) ? x : y;

};

 

Теперь мы можем использовать один и тот же шаблон для целых и вещественных чисел.

 

int i = 1, j = 2, k;

double a = 1.5, b = 1.2, c, d;

k = max(i, j); c = max(a, b); d = max <double> (i, a);

 

Кроме того, можно использовать этот шаблон и для объектов некоторого класса, если в нем переопределен оператор «>».

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

Так, для специализации операции требуется определить вариант ее кода, указав в заголовке конкретный тип данных.

 

void Stack < Circle*> :: push(Circle* el) { . . .}

 

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

 

class Stack < Circle*> {. . .}