Шаблоны
Достаточно часто встречаются классы, объекты которых должны содержать элементы данных произвольного типа (в том смысле, что их тип определяется отдельно для каждого конкретного объекта). В качестве примера можно привести любую структуру данных (массив указателей, массив, список, дерево). Для этого в Си++ предлагаются средства, позволяющие определить некоторое множество идентичных классов с параметризованным типом внутренних элементов. Они представляют собой особого вида заготовку класса (ШАБЛОН), в которой в виде параметра задан тип (класс) входящих в него внутренних элементов данных. При создании конкретного объекта необходимо дополнительно указать и конкретный тип внутренних элементов в качестве фактического параметра. Создание объекта сопровождается созданием соответствующего конкретного класса для типа, заданного в виде параметра. Принятый в Си++ способ определения множества классов с параметризованным внутренним типом данных (иначе, макроопределение) называется шаблоном ( template ).
Синтаксис шаблона рассмотрим на примере шаблона класса векторов, содержащих динамический массив указателей на переменные заданного типа.
//------------------------------------------------------bk73-11.cpp
// <class T> - параметр шаблона - класс "T", внутренний тип данных
// vector - имя группы шаблонных классов
template <class T> class vector
{
int tsize; // Общее количество элементов
int csize; // Текущее количество элементов
T **obj; // Массив указателей на параметризованные объекты
public: // типа "T"
T *operator[](int); // оператор [int] возвращает указатель на
// параметризованный объект класса "T"
void insert(T*); // включение указателя на объект типа "T"
int index(T*); //
};
Данный шаблон может использоваться для порождения объектов-векторов, каждый из которых хранит объекты определенного типа. Имя класса при этом составляется из имени шаблона " vector" и имени типа данных (класса), который подставляется вместо параметра "Т":
vector<int> a;
vector<double> b;
extern class time;
vector<time> c;
Заметим, что транслятором при определении каждого вектора с новым типом объектов генерируется описание нового класса по заданному шаблону (естественно, неявно в процессе трансляции). Например, для типа int транслятор получит:
class vector<int>
{
int tsize;
int csize;
int **obj;
public:
int *operator[](int);
void insert(int*);
int index(int*);
};
Далее следует очевидное утверждение, что функции- элементы шаблона также должны быть параметризованы, то есть генерироваться для каждого нового типа данных. Действительно, это так: функции-элементы шаблона классов в свою очередь также являются шаблонными функциями с тем же самым параметром. То же самое касается переопределяемых операторов:
//------------------------------------------------------bk73-12.cpp
// параметр шаблона - класс "T", внутренний тип данных
// имя функции-элемента или оператора - параметризовано
//
template <class T> T* vector<T>::operator[](int n)
{
if (n >=tsize) return(NULL);
return (obj[n]);
}
template <class T> int vector<T>::index(T *pobj)
{
int n;
for (n=0; n<tsize; n++)
if (pobj == obj[n]) return(n);
return(-1);
}
Заметим, что транслятором при определении каждого вектора с новым типом объектов генерируется набор элементов- функций по заданным шаблонам (естественно, неявно в процессе трансляции). При этом сами шаблонные функции должны размещаться в том же заголовочном файле, где размещается определение шаблона самого класса. Для типа int сгенерированные транслятором функции-элементы будут выглядеть так:
int* vector<int>::operator[](int n)
{
if (n >=tsize) return(NULL);
return (obj[n]);
}
int vector<int>::index(int *pobj)
{
int n;
for (n=0; n<tsize; n++)
if (pobj == obj[n]) return(n);
return(-1);
}
Шаблоны могут иметь также и параметры-константы, которые используются для статического определения размерностей внутренних структур данных.
Кроме того, шаблон может использоваться для размещения не только указателей на параметризованные объекты, но и самих объектов. В качестве примера рассмотрим шаблон для построения циклической очереди ограниченного размера для параметризованных объектов:
//------------------------------------------------------bk73-13.cpp
//-------Шаблон с параметром-константой
template <class T,int size> class FIFO
{
int fst,lst; // Индексы начала и конца очереди
T queue[size]; // Массив объектов класса "T" размерности "size"
public:
T from(); // Функции включения-исключения
void into(T); //
FIFO(); // Конструктор
};
template <class T,int size> FIFO<T,size>::FIFO()
{ fst = lst = 0; }
template <class T,int size> T FIFO<T,size>::from()
{
T work;
if (fst !=lst)
{
work = queue[lst++];
lst = lst % size;
}
return(work);
}
template <class T,int size> void FIFO<T,size>::into(T obj)
{
queue[fst++] = obj;
fst = fst % size;
}
Пример определения объектов шаблонного класса:
struct x {};
FIFO<double,100> a;
FIFO<int,20> b;
FIFO<x,50> c;