Статические члены шаблонов класса
В шаблоне класса могут быть объявлены статические данные-члены. Каждый конкретизированный экземпляр имеет собственный набор таких членов. Рассмотрим операторы new() и delete() для шаблона QueueItem. В класс QueueItem нужно добавить два статических члена:
static QueueItem<Type> *free_list;
static const unsigned QueueItem_chunk;
Модифицированное определение шаблона QueueItem выглядит так:
#include <cstddef>
template <class Type>
class QueueItem {
// ...
private:
void *operator new( size_t );
void operator delete( void *, size_t );
// ...
static QueueItem *free_list;
static const unsigned QueueItem_chunk;
// ...
};
Операторы new() и delete() объявлены закрытыми, чтобы предотвратить создание объектов типа QueueItem вызывающей программой: это разрешается только членам и друзьям QueueItem (к примеру, шаблону Queue).
Оператор new() можно реализовать таким образом:
template <class Type> void*
QueueItem<Type>::operator new( size_t size )
{
QueueItem<Type> *p;
if ( ! free_list )
{
size_t chunk = QueueItem_chunk * size;
free_list = p =
reinterpret_cast< QueueItem<Type>* >
( new char[chunk] );
for ( ; p != &free_list[ QueueItem_chunk - 1 ]; ++p )
p->next = p + 1;
p->next = 0;
}
p = free_list;
free_list = free_list->next;
return p;
}
А реализация оператора delete() выглядит так:
template <class Type>
void QueueItem<Type>::
operator delete( void *p, size_t )
{
static_cast< QueueItem<Type>* >( p )->next = free_list;
free_list = static_cast< QueueItem<Type>* > ( p );
}
Теперь остается инициализировать статические члены free_list и QueueItem_chunk. Вот шаблон для определения статических данных-членов:
/* для каждой конкретизации QueueItem сгенерировать
* соответствующий free_list и инициализировать его нулем
*/
template <class T>
QueueItem<T> *QueueItem<T>::free_list = 0;
/* для каждой конкретизации QueueItem сгенерировать
* соответствующий QueueItem_chunk и инициализировать его значением 24
*/
template <class T>
const unsigned int
QueueItem<T>::QueueItem_chunk = 24;
Определение шаблона статического члена должно быть вынесено за пределы определения самого шаблона класса, которое начинается с ключевого слово template с последующим списком параметров <class T>. Имени статического члена предшествует префикс QueueItem<T>::, показывающий, что этот член принадлежит именно шаблону QueueItem. Определения таких членов помещаются в заголовочный файл Queue.h и должны включаться во все файлы, где производится их конкретизация. (В разделе 16.8 мы объясним, почему решили делать именно так, и затронем другие вопросы, касающиеся модели компиляции шаблонов.)
Статический член конкретизируется по шаблону только в том случае, когда реально используется в программе. Сам такой член тоже является шаблоном. Определение шаблона для него не приводит к выделению памяти: она выделяется только для конкретизированного экземпляра статического члена. Каждая подобная конкретизация соответствует конкретизации шаблона класса. Таким образом, обращение к экземпляру статического члена всегда производится через некоторый конкретизированный экземпляр класса:
// ошибка: QueueItem - это не реальный конкретизированный экземпляр
int ival0 = QueueItem::QueueItem_chunk;
int ival1 = QueueItem<string>::QueueItem_chunk; // правильно
int ival2 = QueueItem<int>::QueueItem_chunk; // правильно
Упражнение 16.7
Реализуйте определенные в разделе 15.8 операторы new() и delete() и относящиеся к ним статические члены screenChunk и freeStore для шаблона класса Screen, построенного в упражнении 16.6.