Как сделать шаблонный класс дружественным с

Добавил пользователь Евгений Кузнецов
Обновлено: 05.10.2024

friend class foobar; friend void foo(); friend void Foo::bar(); // .

объявлены друзьями всех конкретизаций шаблона QueueItem :

Ни класс foobar , ни функцию foo() не обязательно объявлять или определять в глобальной области видимости перед объявлением их друзьями шаблона

Однако перед тем как объявить другом какой-либо из членов класса Foo , необходимо определить его. Напомним, что член класса может быть введен в

область видимости только через определение объемлющего класса. QueueItem не может ссылаться на Foo::bar() , пока не будет найдено определение Foo ;

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

template class foobar < . >;

void foo( QueueItem );

template class Queue

template class QueueItem

friend class foobar ;

friend void foo ( QueueItem ); friend void Queue ::bar();

Queue::bar() являются друзьями.

С++ для начинающих

Прежде чем шаблон класса можно будет использовать в объявлениях друзей, он сам должен быть объявлен или определен. В нашем примере шаблоны классов foobar и Queue , а также шаблон функции foo() следует объявить до того, как они объявлены друзьями в QueueItem .

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

friend void foo ( QueueItem );

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

friend void foo( QueueItem );

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

• несвязанный дружественный шаблон класса или функции. В следующем примере имеется отображение один-ко-многим между конкретизациями шаблона класса QueueItem и его друзьями. Для каждой конкретизации типа QueueItem все

template class QueueItem

template friend class foobar;

friend void foo( QueueItem );

friend class Queue ::bar();

конкретизации foobar , foo() и Queue ::bar() являются друзьями:

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

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

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

С++ для начинающих

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

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

template class QueueItem

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

// любого экземпляра QueueItem

template friend class Queue;

чтобы объявить любой экземпляр Queue другом любого экземпляра QueueItem :

Однако нет смысла объявлять, например, класс Queue , конкретизированный типом string , другом QueueItem , конкретизированного типом complex .

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

template class QueueItem

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

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

friend class Queue ;

применим второй метод объявления друзей:

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

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

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

ему необходим доступ к закрытым членам класса. Какой же будет его сигнатура?

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

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

Всем заранее большое спасибо.

Оформляйте свой код тегом [code]

Мужество есть лишь у тех, кто ощутил сердцем страх, кто смотрит в пропасть, но смотрит с гордостью в глазах. (с) Ария

к примеру, компилятору не нравится объявление дружественного класса List
error C2059: syntax error : '

Одно из основных преимуществ ООП - инкапсуляция данных в классе. Вы ознакомились со множеством примеров этой концепции скрытия данных в предыдущих листингах и ее выгодами, связанными с уменьшением работы по поддержке программы и более легкой отладкой.
Конечно, подобно многим правилам в жизни и в программировании, концепция скрытия данных существует для того, чтобы ее нарушали. В C++ вы можете обойти правила инкапсуляции с помощью друзей, хотя при этом вы рискуете благополучием вашей программы. Объявление друзей класса подобно вручению приятелю дубликатов ключей от вашей квартиры. И если вы удалились на уик-энд, не удивляйтесь, когда, вернувшись, вы увидите, что ваш знакомый прикорнул на диване, а холодильник основательно выпотрошен.
Классы в C++ позволяют объявлять два вида друзей. Один класс как единое целое может быть другом другого класса, или же другом может объявляться только одна функция. В следующем разделе описываются оба этих средства C++.

Замечание
Если друзья имеют брата-близнеца в C++, так это оператор goto. Подобно goto, друзья позволяют вам пренебречь правилами, призванными помочь вам написать надежный код. Не подумайте, что следующий раздел поощряет использоване друзей. Асы в программировании на С++ пользуются друзьями только в случае крайней необходимости.

Дружественные классы

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

Класс AClass содержит один закрытый член value типа double. Конструктор класса присваивает этому члену значение 3.14159. За исключением этого действия, в классе не предусмотрены даже средства просмотра значения value объекта класса. Закрытый член находится в полной безопасности, как медвежонок под защитой мамаши-медведицы.
Далее, предположим, что вы объявили еще один класс, содержащий членом объект класса AClass

Член anObject типа AClass закрыт в классе BClass. Функция-член ShowValue() пытается отобразить value из anObject. Однако объявление не скомпилируется, поскольку член value закрыт в классе AClass и только функции-члены AClass имеют к нему доступ.
Замена в классе AClass спецификатора доступа private на protected для члена value не решает задачу. Защищенный член доступен в классе, в котором он объявлен, и в любых производных из него классах. Несмотря на то что класс BClass обладает объектом типа AClass, два этих класса не связаны между собой отношением родства, и члены BClass не имеют доступа к закрытым и защищенным членам AClass. Конечно, вы можете сделать член value открытым в классе AClass, но это сделает value доступным и во всех операторах.
Лучшее решение - сделать класс BClass другом класса AClass. Объекты BClass будут иметь доступ к value и всем другим закрытым и защищенным членам, а операторы вне двух классов будут лишены возможности доступа к запрещенным частям класса. Для этого следует указать ключевое слово friend внутри класса, к которому необходимо получить доступ в другом классе. В данном примере класс BClass получит доступ к закрытому члену value внутри AClass. Таким образом, для того, чтобы класс BClass получил доступ к этим закрытым данным в классе AClass, следует первый класс объявить дружественным второму. Вот новое объявление класса AClass:

Единственное отличие от предыдущего объявления - строка friend class BClass, дающая указание компилятору предоставить классу BClass доступ к защищенным и закрытым членам класса AClass. Другие операторы в других классах и в основной программе, как и раньше, не имеют доступа к объявленным з защищенными членам класса AClass. Только BClass может частным образом проникнуть в закрытую "комнату" AClass. Таким образом, вы можете объявить любое число классов друзьями. Единственное ограничение - ключевое слово friend должно быть внутри объявления класса. Полезно запомнить еще некоторые факты.

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

В листинге 15.1 демонстрируется, как дружественный класс получает доступ к закрытым и защищенным членам другого класса.

Шаблон класса позволяет задать класс , параметризованный типом данных. Передача классу различных типов данных в качестве параметра создает семейство родственных классов. Наиболее широкое применение шаблоны находят при создании контейнерных классов. Контейнерным называется класс , который предназначен для хранения каким-либо образом организованных данных и работы с ними. Преимущество использования шаблонов состоит в том, что как только алгоритм работы с данными определен и отлажен, он может применяться к любым типам данных без переписывания кода.

Создание шаблонов классов

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

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

Поскольку этот класс будет описан внутри класса, представляющего список, поля для простоты доступа из внешнего класса сделаны доступными ( public ). Это позволяет обойтись без функций доступа и изменения полей. Назовем класс списка List:

Рассмотрим реализацию методов класса . Метод add выделяет память под новый объект типа Node и присоединяет его к списку, обновляя указатели на его начало и конец:

Метод find выполняет поиск узла с заданным ключом и возвращает указатель на него в случае успешного поиска и 0 в случае отсутствия такого узла в списке:

Метод insert вставляет в список узел после узла с ключом key и возвращает указатель на вставленный узел. Если такого узла в списке нет, вставка не выполняется и возвращается значение 0:

Метод remove удаляет узел с заданным ключом из списка и возвращает значение true в случае успешного удаления и false , если узел с таким ключом в списке не найден:

Методы печати списка в прямом и обратном направлении поэлементно просматривают список, переходя по соответствующим ссылкам:

Деструктор списка освобождает память из-под всех его элементов:

Ниже приведен пример программы, использующей класс List . Программа формирует список из 5 чисел, выводит его на экран, добавляет число в список, удаляет число из списка и снова выводит его на экран:

Класс List предназначен для хранения целых чисел. Чтобы хранить в нем данные любого типа, требуется описать этот класс как шаблон и передать тип в качестве параметра.

Синтаксис описания шаблона:

Шаблон класса начинается с ключевого слова template . В угловых скобках записывают параметры шаблона. При использовании шаблона на место этих параметров шаблону передаются аргументы: типы и константы, перечисленные через запятую.

Типы могут быть как стандартными, так и определенными пользователем. Для их описания в списке параметров используется ключевое слово class . В простейшем случае одного параметра это выглядит как . Здесь T является параметром-типом. Имя параметра может быть любым, но принято начинать его с префикса T . Внутри класса-шаблона параметр может появляться в тех местах, где разрешается указывать конкретный тип, например:

Класс TData можно рассматривать как параметр, на место которого при компиляции будет подставлен конкретный тип данных. Получившийся шаблонный класс имеет тип List.

Методы шаблона класса автоматически становятся шаблонами функций. Если метод описывается вне шаблона, его заголовок должен иметь следующие элементы:

возвр_тип имя_класса :: имя_функции (список_параметров функции)

Проще рассмотреть синтаксис описания методов шаблона на примере:

  • Описание параметров шаблона в заголовке функции должно соответствовать шаблону класса.
  • Локальные классы не могут иметь шаблоны в качестве своих элементов.
  • Шаблоны методов не могут быть виртуальными.
  • Шаблоны классов могут содержать статические элементы , дружественные функции и классы.
  • Шаблоны могут быть производными как от шаблонов, так и от обычных классов, а также являться базовыми и для шаблонов, и для обычных классов.
  • Внутри шаблона нельзя определять friend -шаблоны.

Если у шаблона несколько параметров, они перечисляются через запятую. Ключевое слово class требуется записывать перед каждым параметром, например:

Параметрам шаблонного класса можно присваивать значения по умолчанию, они записываются после знака " http://www.intuit.ru/2010/edi" >

Ниже приведено полное описание параметризованного класса двусвязного списка List .

Если требуется использовать шаблон List для хранения данных не встроенного, а определенного пользователем типа, в его описание необходимо добавить перегрузку операции вывода в поток и сравнения на равенство, а если для его полей используется динамическое выделение памяти , то и операцию присваивания .

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

  • переменная целого, символьного, булевского или перечислимого типа ;
  • указатель на объект или указатель на функцию;
  • ссылка на объект или ссылка на функцию;
  • указатель на элемент класса.

В теле шаблона такие параметры могут применяться в любом месте, где допустимо использовать константное выражение.

В качестве примера создадим шаблон класса, содержащего блок памяти определенной длины и типа:

У класса-шаблона могут быть друзья, и шаблоны тоже могут быть друзьями. Класс может быть объявлен внутри шаблона, а шаблон - внутри как класса, так и шаблона. Единственным ограничением является то, что шаблонный класс нельзя объявлять внутри функции. В любом классе, как в обычном, так и в шаблоне, можно объявить метод-шаблон. После создания и отладки шаблоны классов удобно помещать в заголовочные файлы .

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

Видеоурок

Шаблоны классов работают по тому же принципу что и шаблоны функций. Разница заключается в том, что шаблоны классов описывают шаблонную структура класса, а не функции.

Для создания шаблона класса используйте ключевое слово template . Пример реализации шаблонного класса:

При создании объекта на основе шаблонного класса необходимо передать не только значения, но также тип данных.

Читайте также: