C++, реальные примеры, когда friend классы и функции были бы уместнее, чем иное решение?
Я задумался над целесообразностью использования в C++ дружественных функций и классов. Пытался найти внятные примеры, когда это действительно необходимо и дает плюсов больше, чем минусов. Таких примеров мне удалось найти совсем немного.
Может быть, у кого-нибудь есть примеры из жизни, когда friend классы и функции были очень удобны?
В С++ модификаторы доступа одинаково работают для всех пользователей класса. Использование friend по сути просто делает все кишки класса доступными для дружественной сущности, даже если ей нужно только несколько, и поэтому обычно считается сомнительной практикой. Возможность дать доступ к одной части членов класса для одних пользователей, к другой — для других, а к третьей — третьим (как это делается в языке Еiffel например) из коробки отсутствует. Однако такой высокогранулированный доступ можно обеспечить используя идиому Attorney-Client:
Стандартная библиотека часто для реализации той или иной сущности, использует дружественные классы. Например:
- класс локализации locale определяет класс facet , деструктор которого защищенный, чтобы все фасеты, наследованные от locale::facet и объявляющие класс locale своим другом, могли быть уничтожены только данной локализацией.
- Класс std::valarray является другом классов, std::slice_array, std::gslice_array, std::mask_array, std::indirect_array , чьи конструкторы являются закрытыми, дабы их экземпляры могли создаваться только обьектами std::valarray . (Эти классы являются вспомогательными для std::valarray, например для разного представления возвращаемого типа operator[] ).
Для того, чтобы существующая функция(класс) могла пользоваться закрытыми членами(в том числе, создать или уничтожать обьект, когда конструктор и деструктор не имеют публичный спецификатор доступа), нужно, чтобы она была другом класса.
![]()
Начну немного издалека. Вы пишете шаблонный класс имеющий несколько параметров:
Теперь для этого класса нужно перегрузить операторы +,-,*,/. Только, как то не хочется отдельно перегружать для каждого типа, давайте сделаем перегрузку шаблонной. Я опущу замещения для разных типов:
Все пока хорошо. Выражения типа MyCoolClass a = MyCoolClass() + 5 работают замечательно. Но, вот выражения MyCoolClass a = 5 + MyCoolClass() не работают. Ладно, значит надо перегрузить операторы глобально. Вот только теперь написать шаблонную перегрузку будет не очень удобно, даже пример приводить неохота. И вот в этом случае дружественная шаблонная функция просто спасение:
Обратите внимание, параметры шаблонного класса указывать просто не нужно, что значительно сокращает и упрощает подобные функции. Поэтому в данном случае дружественные функции однозначно полезны. И хотя это только один пример, мне кажется он достаточно веский для существования такого механизма.
C++. «Друзья» класса. «Дружественные» классы и функции. Ключевое слово friend. Примеры
«Друзья» класса. «Дружественные» классы и функции. Ключевое слово friend . Примеры
Содержание
- 1. Для чего используются так называемые «друзья» класса?
- 2. Какая общая форма объявления «дружественного» класса? Ключевое слово friend
- 3. Какая общая форма объявления «дружественной» функции к некоторому классу?
- 4. В каком месте объявления класса можно объявлять «дружественный» класс или функцию?
- 5. Сколько «дружественных» функций и «дружественных» классов можно объявлять в теле класса?
- 6. Как получить объект нужного класса в «дружественной» функции, чтобы иметь доступ ко всем его членам? Пример
- 7. Пример объявления класса, который есть «дружественным» к другому классу
- 8. Пример объявления функции, которая есть «дружественной» к другому классу
- 9. Особенности применения «дружественных» функций
Поиск на других ресурсах:
1. Для чего используются так называемые «друзья» класса?
Бывают случаи, когда для заданного класса нужно объявить другой класс или функцию, которые должны иметь неограниченный доступ к внутренним переменным и методам класса. Такая необходимость возникает из сути решаемой задачи.
Если класс A объявляется «дружественным» к классу B , то объекты класса A имеют доступ ко всем членам данных и методам класса B . Если функция объявляется «дружественной» к некоторому классу, то из этой функции также есть неограниченный доступ к членам данных и методам этого класса.
Таким образом, «дружественный» класс или функция дополняют функционал класса в котором они объявлены как «дружественные».
2. Какая общая форма объявления «дружественного» класса? Ключевое слово friend
Чтобы объявить «друженственный» класс к данному классу, используется ключевое слово friend . Общая форма объявления «дружественного» класса к данному имеет вид:
- CClass – класс, в котором объявляется «дружественный» класс CFriendClass . Все переменные (даже и private ) и методы этого класса есть доступными для объектов класса CFriendClass ;
- CFriendClass – класс, который есть «дружественным» к классу CClass . Объявление «дружественного» класса CFriendClass к классу CClass может быть в любом месте тела класса – в границах объявления класса (между фигурными скобками < >).
3. Какая общая форма объявления «дружественной» функции к некоторому классу?
Объявление «дружественной» функции к классу начинается из ключевого слова friend . Общая форма объявления «дружеской» функции к классу имеет вид:
- FunName – имя «дружественной» функции;
- type – тип, который возвращается функцией FunName() ;
- parameters – параметры «дружественной» функции. Чтобы получить объект нужного класса в функции FunName() целесообразно передать в эту функцию ссылку (или указатель) на объект этого класса.
Если нужно объявить «дружественную» функцию в некотором классе, то общий вид такого объявления следующий:
- FunName – имя «дружественной» функции;
- type – тип, который возвращается функцией FunName() ;
- parameters – параметры «дружественной» функции.
4. В каком месте объявления класса можно объявлять «дружественный» класс или функцию?
Объявлять «дружественный» класс или функцию к заданному классу можно в любом месте или разделе класса в пределах его объявления (между фигурными скобками < >).
5. Сколько «дружественных» функций и «дружественных» классов можно объявлять в теле класса?
6. Как получить объект нужного класса в «дружественной» функции, чтобы иметь доступ ко всем его членам? Пример
Чтобы получить объект нужного класса в функции целесообразно передать в эту функцию ссылку (или указатель) на объект этого класса.
Например. Пусть задан класс с именем CMyClass . Нужно объявить «дружественную» функцию к этому классу, которая называется FriendFun() . Функция возвращает параметр типа int .
Объявление класса CMyClass и «дружественной» функции в классе имеет следующий вид:
Реализация «дружественной» функции FriendFun() :
7. Пример объявления класса, который есть «дружественным» к другому классу
Пусть задан класс Number , содержащий целочисленную величину. Также задан класс RangeNum , содержащий величину Number но в границах заданного диапазона.
Чтобы из класса RangeNum можно было иметь доступ к приватной переменной num класса Number , класс RangeNum объявляется «дружественным» к классу Number .
Листинг классов Number и RangeNum имеет вид:
Использование классов в другом программном коде
Если в классе Number в объявлении «дружественного» класса RangeNum
забрать ключевое слово friend , то в конструкторе и всех методах класса при доступе к num.num компилятор выдаст ошибку:
8. Пример объявления функции, которая есть «дружественной» к другому классу
В примере объявляется класс Radius , содержащий величину радиуса некоторой геометрической фигуры. В классе объявляются:
- одна скрытая ( private ) переменная radius ;
- методы Get() и Set() для доступа к переменной radius ;
- две внешних «дружественных» функции GetLength() и GetArea() ;
- один «дружественный» класс Volume . В классе Volume объявляется внешний ( public ) метод GetVolume() , возвращающий объем шара заданного радиуса.
Листинг приложения типа « Win32 Console Application » следующий:
Как видно из вышеприведенного кода, «дружественные» функции получают входным параметром ссылку или указатель на объект класса, к членам данных и методам которого они имеют неограниченный доступ.
Зачем нужны друзья класса c
![]()
- класс локализации locale определяет класс facet , деструктор которого защищенный, чтобы все фасеты, наследованные от locale::facet и объявляющие класс locale своим другом, могли быть уничтожены только данной локализацией.
- Класс std::valarray является другом классов, std::slice_array, std::gslice_array, std::mask_array, std::indirect_array , чьи конструкторы являются закрытыми, дабы их экземпляры могли создаваться только обьектами std::valarray . (Эти классы являются вспомогательными для std::valarray, например для разного представления возвращаемого типа operator[] ).
![]()
- Используя ключевое слово friend, класс может сообщить C++, кто является его другом, т. е. другими словами, что другие классы могут обращаться напрямую к его частным элементам.
- Частные элементы класса защищают данные класса, следовательно, вы должны ограничить круг классов-друзей только теми классами, которым действительно необходим прямой доступ к частным элементам искомого класса.
- C++ позволяет ограничить дружественный доступ определенным набором функций.
class book
public:
book (char *, char *, char *);
void show_book(void);
friend librarian;
private:
char title [64] ;
char author[64];
char catalog[64];
>;
class book
public:
book(char *, char *, char *);
void show_book(void);
friend char *librarian::get_catalog(book);
friend void librarian: :change_catalog( book *, char *);
private:
char title[64];
char author[ 64 ];
char catalog[64];
>;
class librarian
public:
void change_catalog(book *, char *);
char *get_catalog(book);
>;
Урок №126. Дружественные функции и классы
На этом уроке мы рассмотрим использование дружественных функций и дружественных классов в языке С++.
Проблема
На предыдущих уроках мы говорили о том, что данные вашего класса должны быть private. Однако может возникнуть ситуация, когда у вас есть класс и функция, которая работает с этим классом, но которая не находится в его теле. Например, есть класс, в котором хранятся данные, и функция (или другой класс), которая выводит эти данные на экран. Хотя код класса и код функции вывода разделены (для упрощения поддержки кода), код функции вывода тесно связан с данными класса. Следовательно, сделав члены класса private, мы желаемого эффекта не добьёмся.
В таких ситуациях есть два варианта:
Сделать открытыми методы класса и через них функция будет взаимодействовать с классом. Однако здесь есть несколько нюансов. Во-первых, эти открытые методы нужно будет определить, на что потребуется время, и они будут загромождать интерфейс класса. Во-вторых, в классе нужно будет открыть методы, которые не всегда должны быть открытыми и предоставляющими доступ извне.
Использовать дружественные классы и дружественные функции, с помощью которых можно будет предоставить функции вывода доступ к закрытым данным класса. Это позволит функции вывода напрямую обращаться ко всем закрытым переменным-членам и методам класса, сохраняя при этом закрытый доступ к данным класса для всех остальных функций вне тела класса! На этом уроке мы рассмотрим, как это делается.
Дружественные функции
Дружественная функция — это функция, которая имеет доступ к закрытым членам класса, как если бы она сама была членом этого класса. Во всех других отношениях дружественная функция является обычной функцией. Ею может быть, как обычная функция, так и метод другого класса. Для объявления дружественной функции используется ключевое слово friend перед прототипом функции, которую вы хотите сделать дружественной классу. Неважно, объявляете ли вы её в public- или в private-зоне класса. Например: