Выбрать главу

~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.