~Queue();
Type& remove();
void add( const Type & );
bool is_empty() const {
return front == 0;
}
private:
QueueItem Type *front;
QueueItem Type *back;
};
Деструктор, а также функции-члены remove() и add() определены не в теле шаблона, а вне его. Деструктор Queue опустошает очередь:
template class Type
Queue Type ::~Queue()
{
while (! is_empty() )
remove();
}
Функция-член Queue Type ::add() помещает новый элемент в конец очереди:
template class Type
void Queue Type ::add( const Type &val )
{
// создать новый объект QueueItem
QueueItem Type *pt =
new QueueItem Type ( val );
if ( is_empty() )
front = back = pt;
else
{
back- next = pt;
back = pt;
}
}
Функция-член Queue Type ::remove() возвращает значение элемента, находящегося в начале очереди, и удаляет сам элемент.
#include iostream
#include cstdlib
template class Type
Type Queue Type ::remove()
{
if ( is_empty() )
{
cerr "remove() вызвана для пустой очереди\n";
exit( -1 );
}
QueueItem Type *pt = front;
front = front- next;
Type retval = pt- item;
delete pt;
return retval;
}
Мы поместили определения функций-членов в заголовочный файл Queue.h, включив его в каждый файл, где возможны конкретизации функций. (Обоснование этого решения, а также рассмотрение более общих вопросов, касающихся модели компиляции шаблонов, мы отложим до раздела 16.8.)
В следующей программе иллюстрируется использование и конкретизация функции-члена шаблона Queue:
#include iostream
#include "Queue.h"
int main()
{
// конкретизируется класс Queue int
// оператор new требует, чтобы Queue int был определен
Queue int *p_qi = new Queue int ;
int ival;
for ( ival = 0; ival 10; ++ival )
// конкретизируется функция-член add()
p_qi- add( ival );
int err_cnt = 0;
for ( ival = 0; ival 10; ++ival ) {
// конкретизируется функция-член remove()
int qval = p_qi- remove();
if ( ival != qval ) err_cnt++;
}
if ( !err_cnt )
cout "!! queue executed ok\n";
else cerr "?? queue errors: " err_cnt endl;
return 0;
}
После компиляции и запуска программа выводит следующую строку:
!! queue executed ok
Упражнение 16.5
Используя шаблон класса Screen, определенный в разделе 16.2, реализуйте функции-члены Screen (см. разделы 13.3, 13.4 и 13.6) в виде функций-членов шаблона.
16.4. Объявления друзей в шаблонах классов
1. обычный (не шаблонный) дружественный класс или дружественная функция. В следующем примере функция foo(), функция-член bar() и класс foobar объявлены друзьями всех конкретизаций шаблона QueueItem:
preclass Foo {
void bar();
};
template class T
class QueueItem {
friend class foobar;
friend void foo();
friend void Foo::bar();
// ...
};
Ни класс foobar, ни функцию foo() не обязательно объявлять или определять в глобальной области видимости перед объявлением их друзьями шаблона QueueItem.
Однако перед тем как объявить другом какой-либо из членов класса Foo, необходимо определить его. Напомним, что член класса может быть введен в область видимости только через определение объемлющего класса. QueueItem не может ссылаться на Foo::bar(), пока не будет найдено определение Foo;
1. связанный дружественный шаблон класса или функции. В следующем примере определено взаимно однозначное соответствие между классами, конкретизированными по шаблону QueueItem, и их друзьями - также конкретизациями шаблонов. Для каждого класса, конкретизированного по шаблону QueueItem, ассоциированные конкретизации foobar, foo() и Queue::bar() являются друзьями.
template class Type
class foobar { ... };
template class Type
void foo( QueueItemType);
template class Type
class Queue {
void bar();
// ...
};
template class Type
class QueueItem {
friend class foobarType;
friend void fooType( QueueItemType );
friend void QueueType::bar();
// ...
};
Прежде чем шаблон класса можно будет использовать в объявлениях друзей, он сам должен быть объявлен или определен. В нашем примере шаблоны классов foobar и Queue, а также шаблон функции foo() следует объявить до того, как они объявлены друзьями в QueueItem.