16.4. Объявления друзей в шаблонах классов

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.

Синтаксис, использованный для объявления foo() другом, может показаться странным:

friend void fooType( QueueItemType );

За именем функции следует список явных аргументов шаблона: fooType. Такой синтаксис показывает, что в качестве друга объявляется конкретизированный шаблон функции foo(). Если бы список явных аргументов был опущен:

friend void foo( QueueItemType );

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

1. несвязанный дружественный шаблон класса или функции. В следующем примере имеется отображение один-ко-многим между конкретизациями шаблона класса QueueItem и его друзьями. Для каждой конкретизации типа QueueItem все конкретизации foobar, foo() и Queue::bar() являются друзьями:

template class Type

class QueueItem {

template class T

friend class foobar;

template class T

friend void foo( QueueItemT );

template class T

friend class QueueT::bar();

// ...

};

Следует отметить, что этот вид объявлений друзей в шаблоне класса не поддерживается компиляторами, написанными до принятия стандарта C++.

16.4.1. Объявления друзей в шаблонах Queue и QueueItem

Поскольку QueueItem не предназначен для непосредственного использования в вызывающей программе, то объявление конструктора этого класса помещено в закрытую секцию шаблона. Теперь класс Queue необходимо объявить другом QueueItem, чтобы можно было создавать и манипулировать объектами последнего.

Существует два способа объявить шаблон класса другом. Первый заключается в том, чтобы объявить любой экземпляр Queue другом любого экземпляра QueueItem:

template class TypeQueueItem,

конкретизированного типом complexdouble. Queue должен быть другом только для класса QueueItem. Таким образом, нам нужно взаимно однозначное соответствие между экземплярами Queue и QueueItem, конкретизированными одинаковыми типами. Чтобы добиться этого, применим второй метод объявления друзей:

template class Type

class QueueItem {

// для любого экземпляра QueueItem другом является

// только конкретизированный тем же типом экземпляр Queue

friend class QueueType;

// ...

};

Данное объявление говорит о том, что для любой конкретизации QueueItem некоторым типом экземпляр Queue, конкретизированный тем же типом, является другом. Так, экземпляр Queue, конкретизированный типом int, будет другом экземпляра QueueItem, тоже конкретизированного типом int. Но для экземпляров QueueItem, конкретизированных типами complexdouble или string, этот экземпляр Queue другом не будет.

В любой точке программы у пользователю может понадобиться распечатать содержимое объекта Queue. Такая возможность предоставляется с помощью перегруженного оператора вывода. Этот оператор должен быть объявлен другом шаблона Queue, так как ему необходим доступ к закрытым членам класса. Какой же будет его сигнатура?

// как задать аргумент типа Queue?

ostream& operator&&( ostream &, ??? );

Поскольку Queue - это шаблон класса, то в имени конкретизированного экземпляра должен быть задан полный список аргументов:

ostream& operator&&( ostream &, const Queue&int& & );

Так мы определили оператор вывода для класса, конкретизированного из шаблона Queue типом int. Но что, если Queue - это очередь элементов типа string?

ostream& operator&&( ostream &, const Queue&string& & );

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

ostream& operator&&( ostream &, const Queue&Type& & );

Однако из этого перегруженного оператора вывода придется сделать шаблон функции:

template class Type ostream&

operator( ostream &, const Queue&Type& & );

Теперь всякий раз, когда оператору ostream передается конкретизированный экземпляр Queue, конкретизируется и вызывается шаблон функции. Вот одна из возможных реализаций оператора вывода в виде такого шаблона:

template class Type

ostream& operator&&( ostream &os, const Queue&Type& &q )

{

os " ";

QueueItemType *p;

for ( p = q.front; p; p = p- next )

os *p " ";

os " ";

return os;

}

Если очередь объектов типа int содержит значения 3, 5, 8, 13, то распечатка ее содержимого с помощью такого оператора дает

3 5 8 13

Обратите внимание, что оператор вывода обращается к закрытому члену front класса Queue. Поэтому оператор необходимо объявить другом Queue:

template class Type

class Queue {

friend ostream&

operator( ostream &, const Queue&Type& & );

// ...

};

Здесь, как и при объявлении друга в шаблоне класса Queue, создается взаимно однозначное соответствие между конкретизациями Queue и оператора operator().

Распечатка элементов Queue производится оператором вывода operator() класса QueueItem:

os *p;

Этот оператор также должен быть реализован в виде шаблона функции; тогда можно быть уверенным, что в нужный момент будет конкретизирован подходящий экземпляр:

template class Type

ostream& operator&& ( ostream &os, const QueueItem&Type& &qi )

{

os qi.item;

return os;

}

Поскольку здесь имеется обращение к закрытому члену item класса QueueItem, оператор следует объявить другом шаблона QueueItem. Это делается следующим образом:

template class Type

class QueueItem {

friend class QueueType;

friend ostream&

operator ( ostream &, const QueueItem&Type& & );

// ...

};

Оператор вывода класса QueueItem полагается на то, что item умеет распечатывать себя:

Следующая программа демонстрирует конкретизацию и использование функций-друзей шаблонов классов Queue и QueueItem:

#include iostream

#include "Queue.h"

int main() {

Queueint qi;

// конкретизируются оба экземпляра

// ostream& operator&& (ostream &os, const Queue&int&&)

// ostream& operator&& (ostream &os, const QueueItem&int& &)

cout qi endl;

int ival;

for ( ival = 0; ival 10; ++ival )

qi.add( ival );

cout qi endl;

int err_cnt = 0;

for ( ival = 0; ival 10; ++ival ) {

int qval = qi.remove();

if ( ival != qval ) err_cnt++;

}

cout qi endl;

if ( !err_cnt )

cout "!! queue executed ok ";

else cout "?? queue errors: " err_cnt endl;

return 0;

}

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

0 1 2 3 4 5 6 7 8 9

!! queue executed ok

Упражнение 16.6

Пользуясь шаблоном класса Screen, определенным в упражнении 16.5, реализуйте операторы ввода и вывода (см. упражнение 15.6 из раздела 15.2) в виде шаблонов. Объясните, почему вы выбрали тот, а не иной способ объявления друзей класса Screen, добавленных в его шаблон.

Поделитесь на страничке

Следующая глава >

Похожие главы из других книг

Знакомства, общение, поиск друзей

Из книги Работа в Интернете автора Макарский Дмитрий

Знакомства, общение, поиск друзей Рассматривая программы для быстрого обмена сообщениями, мы уже отмечали явную их нацеленность, как и сети ICQ в целом, на поиск собеседников. Однако мир не стоит на месте и интернет-технологии тоже. В наше время у ICQ и ей подобных программ


Совпадающие элементы в шаблонах

Из книги XSLT автора Хольцнер Стивен

Совпадающие элементы в шаблонах Для указания того, с каким узлом или узлами вы хотите работать в шаблоне, в XSLT имеются разнообразные способы поиска или выбора узлов. Следует установить атрибут match элемента <xsl:template> в образец (pattern), определяющий имя узла или узлов, с


Часть 1 Как добыть 100 000 друзей? Практический опыт

Из книги Facebook: как найти 100 000 друзей для вашего бизнеса бесплатно автора Албитов Андрей

Часть 1 Как добыть 100 000 друзей? Практический опыт Эту книгу я разделил на две части. Первая посвящена тому, как собрать большую аудиторию в Facebook, вторая – как начать «дружить» с ней, генерировать продажи и поддерживать связь. Почему я выбрал именно такую


Как конвертировать посетителей сайта в друзей

Из книги Технология XSLT автора Валиков Алексей Николаевич

Как конвертировать посетителей сайта в друзей У каждого сайта есть посетители. Будем считать, что у вашего сайта их несколько тысяч в сутки. Если нет – вам стоит изучить принципы поисковой оптимизации, контекстной рекламы и многое другое. Информацию о привлечении людей


Как конвертировать скачивания файлов в друзей

Из книги C++. Сборник рецептов автора Диггинс Кристофер

Как конвертировать скачивания файлов в друзей Этот прием полезен в первую очередь софтверным компаниям, у которых очень много скачиваний с сайтов. Но его могут использовать и другие: ведь скачивать можно не только программы, но и фотографии, инструкции, документы, отчеты,


Часть 2 Как дружить со 100 000 друзей? Ответы на вопросы

Из книги Социальные сети без страха для тех, кому за... автора Виннер Марина

Часть 2 Как дружить со 100 000 друзей? Ответы на вопросы Самое главное для вас – попасть в ленту новостей фанатов, чтобы они прочитали ваше сообщение. Иначе вы их больше не увидите, и 100 000 друзей не дадут вам никакого результата.Это не особенно сложно, но требует дисциплины


2.3. Снижение числа #include с помощью предварительного объявления классов

Из книги C++ для начинающих автора Липпман Стенли

2.3. Снижение числа #include с помощью предварительного объявления классов ПроблемаИмеется заголовочный файл, который ссылается на классы из других заголовочных файлов, и требуется снизить зависимости компиляции (и, возможно, время).РешениеЧтобы избежать ненужных


Поиск друзей

Из книги Социальные сети. ВКонтакте, Facebook и другие… автора Леонтьев Виталий Петрович


Из опыта друзей

Из книги автора

Из опыта друзей Примечание Письма цитируются с разрешения их авторов. Елена ГузоваКомп муж приобрел и установил ради дочкиного диплома, а отнюдь не ради меня. На протяжении ряда лет обучения я печатала ее работы на маленькой «Эрике», но на предпоследнем курсе стало


16.4.1. Объявления друзей в шаблонах Queue и QueueItem

Из книги автора

16.4.1. Объявления друзей в шаблонах Queue и QueueItem Поскольку QueueItem не предназначен для непосредственного использования в вызывающей программе, то объявление конструктора этого класса помещено в закрытую секцию шаблона. Теперь класс Queue необходимо объявить другом QueueItem, чтобы


16.11. Разрешение имен в шаблонах классов A

Из книги автора

16.11. Разрешение имен в шаблонах классов A При обсуждении разрешения имен в шаблонах функций (см. раздел 10.9) мы уже говорили о том, что этот процесс выполняется в два шага. Так же разрешаются имена и в определениях шаблонов классов и их членов. Каждый шаг относится к разным


Поиск и добавление друзей

Из книги автора

Поиск и добавление друзей …Но прежде наберем хотя бы небольшое количество друзей, поскольку Facebook терпеть не может пустых френдлистов. И вся наша стартовая страничка буквально кричит – найди, добавь кого-нибудь: так много на ней полей для поиска! Как и ВКонтактах,


Поиск и добавление друзей

Из книги автора

Поиск и добавление друзей Что ж, теперь вы можете гордиться: вы успешно создали свой собственный блог, решились что-то поведать миру… А кстати, где он, этот самый «мир»? Уж если вы решились устроить в Сети свой маленький Гайд-парк и примерили на себя пикейный жилет, то у вас