Friend c что это

от admin

Скрытые друзья в плюсах

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

Существует определенный набор рекомендаций для сокращения времени компиляции программ, написанных на С++, и идиома hidden friends является одной из них.

Рассмотрим класс точки с координатами x и y:

Опустим детали реализации остальных методов, так как они нам сейчас не очень интересны. Допустим, нам нужно выводить координаты этой точки в std::cout в некотором незамысловатом формате и для этих целей требуется ввести оператор <<, который выглядит как-то так:

Поскольку нам нужен доступ к закрытым членам класса point, мы добавим friend-объявление в этот класс (а иначе, код просто не скомпилируется).

Приведу сразу полный код:

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

Обратите внимание на то, что оператор << определен в пространстве имен drawing. Но тем не менее, компилятор нашел его, хотя вызов находится в глобальном пространстве. То есть компилятор взял и заменил

Это называется поиском Кёнига или поиском, зависимым от аргументов (ADL — argument-dependent lookup), когда компилятор при поиске функции просматривает еще и пространства, в которых находятся объявления типов аргументов функции. Благодаря этой возможности нам не нужно писать кучу лишнего когда и вообще перегрузка операторов потеряла бы всякий смысл.

Теперь давайте перенесем определение оператора в класс:

А че, так можно было, что-ли? (С)

Это тоже нормально компилируется. Компилятор найдет нужный оператор в объявлении типа аргумента, при условии, что этот оператор объявлен дружественным.

Такая форма определения дружественной функции называется hidden friend и у нее есть свои преимущества.

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

Давайте еще раз, закрепим чем отличается hidden friend от not hidden friend:

Свободная дружественная функция не является скрытым другом. Определенная внутри, т.е. inline friend функция является нашим пациентом – скрытым другом. Думаю, понятно, что классы скрытыми друзьями быть не могут и мы здесь рассматриваем только функции (и на всякий случай, операторы — это тоже функции).

Преимущества скрытых друзей

Когда компилятор ищет функцию по ее неквалифицированному (т.е. без ::) имени, он просматривает глобальное пространство имен и пространство имен, в которых объявлены типы аргументов.

Скрытые дружественные функции не участвуют в разрешении перегрузки до тех пор, пока типы классов, в которых они определены не присутствуют в аргументах функции.

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

В результате, во-первых, можно найти совершенно левый, но подходящий по мнению компилятора оператор. Во-вторых, тратится впустую достаточно много времени (по некоторым оценкам, в разы и даже десятки раз больше). Скрытые друзья оберегают от этих проблем (нежелательное неявное преобразование типов и скорость компиляции), так как участвуют в поиске только при ADL и от полного или квалифицированного поиска спрятаны.

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

Недостатки скрытых друзей

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

Upd: Как справедливо отметили в комментариях, функцию не вынести в cpp. Может быть это и не проблема, но ограничение такое есть.

Рекомендация:

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

Дружественные функции С++

Самым важным, но и, скорее всего, самым непонятным для вас сейчас станет определение дружественной функции. Дружественная функция — это функция, которая не является членом класса, но имеет доступ к членам класса, объявленным в полях private или protected . Долго не вникайте в суть этого определения, а лучше сразу переходите к следующему абзацу. Обещаю, что после прочтения статьи вы вернетесь к этому определению и вас посетит мысль: «Ну да — так и есть! Тут все понятно!»

Приступим к основному занятию программиста — практике! В примере запрограммируем следующее: создадим класс Woman25 , который, используя дружественные функции и обычные методы класса, будет получать данные об объекте (имя и вес) и выводить их на экран. Методы и friend -функции будут выполнять аналогичные действия. В этом и есть особенная польза данного примера — вы сможете посмотреть отличия в объявлении и определении дружественных функций от обычных методов класса. На основании полученных данных, программа даст пользователю совет относительно корректировки его веса. Ну что-же, лучше один раз увидеть…

Комментарии к исходному коду. В строках 6 — 30 создаем класс Woman25 . Он содержит два приватных элемента класса: char *name; и int weight; . В private также объявим дружественные функции friend void setData(char *, int, Woman25&); и friend void getData(Woman25&); . Хотя их можно объявлять и определять в любом поле класса, будь то private , public или protected , но об этом поговорим позже. Так мы сделали, чтобы показать, что несмотря на объявление дружественных функций в поле private , к ним можно обращаться напрямую из функции main() . Тогда, когда методы класса должны располагаться в поле public всегда , если мы собираемся вызывать их вне класса . Объявление дружественных функций отличается от объявления методов класса еще и тем, что перед типом возвращаемого функцией значения используется зарезервированное слово friend . Обратите внимание, что в виде параметра мы передаем этим функциям ссылку на объект нашего класса — Woman25& . В обычных методах этого не будет. В поле public расположился конструктор класса — строки 15 — 20 . В нем мы задаем значение нормального веса — 60 кг, а так же, с помощью функции strcpy(name, «Норма»); , вносим данные в элемент класса name . Затем в строках 21 — 25 определяем деструктор класса

Woman25() . Его задача, освободить, выделенную конструктором, динамическую память. А для того, чтобы убедиться, что он сработал и удалил динамическую память всех созданных объектов класса при завершении работы программы, мы добавили в него строку cout << «. Деструктор . » << endl; . Строки 27 — 29 — объявление обычных методов класса.

Теперь начинается, пожалуй, самое интересное — определение вне класса наших дружественных функций и обычных методов класса. Все это располагается в строках 32 — 66. Смотрите, когда мы определяем дружественные функции , строки 32 — 36 и 38 — 41, мы не используем оператор :: двойное двоеточие (область видимости метода). Это уже говорит нам о том, что дружественная функция не принадлежит классу, не является его компонентом. А при определении остальных методов использование оператора :: является обязательным. Метод класса advise() , на основании полученных данных о весе, дает пользователю один из советов либо сообщает, что вес в норме.

Переходим в главную функцию — строки 68 — 87. Тут мы создаем объект класса Woman25 Norm; , при создании которого сработает конструктор и инициализирует элементы name и weight . Вызываем метод класса Norm.getData(); чтобы вывести на экран значение нормы. Со вторым созданным объектом Woman25 Anna; работаем, вызывая обычные set и get — методы класса, а с третьим объектом Woman25 Inna; — вызывая дружественные функции. Как видите, вызываются они как функции, которые не принадлежат классу. Объект класса мы передаем, как параметр.

Запускаем программу и видим следующее.

. Деструктор .
. Деструктор .
. Деструктор .

Для продолжения нажмите любую клавишу . . .

И friend-функции, и методы класса справились со своими задачами, а мы с вами увидели отличия в их объявлении и определении. Так же мы увидели, что деструктор класса, как положено, сработал 3 раза (т.е. при уничтожении каждого созданного объекта). Если у вас возникает вопрос «Собственно, а зачем все это, если методы класса и так прекрасно работают?» — это хорошо! Ответ такой — «Представьте, что у нас есть еще десяток классов. Например Girl6_7 , Girl8_9 , Man25 и т.д., Используя дружественные функции, нам не придется для каждого класса определять set и get -методы. Это в нашей программе они короткие! А если бы они занимали 20 — 30 строк? А так достаточно определить метод в одном из классов или вообще определить функцию, как глобальную, а в остальные классы прописать ее прототип, как дружественной функции (используя слово friend ). Мы экономим массу времени и наш код становится намного короче.»

Читать:
На какой цвет нужно переходить дорогу

Теперь, как всегда, немного теории, для закрепления материала:

  • Дружественная функция может располагаться в любом поле класса – private , public или protected . Она при любых обстоятельствах будет иметь доступ к private -элементам класса и, даже если она сама находится в поле private (как в нашем примере), к ней можно будет обратиться вне класса, не используя специальных методов.
  • Когда мы определяем дружественную функцию, элементы класса необходимо явно передавать в нее в виде параметров функции. Так как она не является компонентом класса, она не получает указатель this .
  • В виде параметра, в дружественную функцию так же надо передать указатель или ссылку на объект класса. Иначе она не увидит данные какого класса ей принять и обработать.
  • Функция может использоваться, как дружественная к нескольким классам.
  • Вызываются дружественные функции, как обычные функции. Т.е не используется такой способ — Объект_класса.функция() . После внесения всех необходимых параметров в нее при вызове, она сама увидит с элементами какого класса и объекта надо работать.

Вот и все для начала. Вопросы, предложения и замечания по теме ждем в комментариях к этой статье.

Friend c что это

Дружественные функции — это функции, которые не являются членами класса, однако имеют доступ к его закрытым членам — переменным и функциям, которые имеют спецификатор private.

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

Здесь определен класс Auto, который представляет автомобиль. У этого класса определены приватные закрытые переменные name (название автомобиля) и price (цена автомобиля). Также в классе объявлены две дружественные функции: drive (функция вождения автомобиля) и setPrice (функция назначения цены). Обе этих функции принимают в качестве параметра ссылку на объект Auto.

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

При этом для дружественных функций не важно, определяются они под спецификатором public или private. Для них это не имеет значения.

Определение этих функций производится вне класса. И поскольку эти функции являются дружественными, то внутри этих функций мы можем через переданную ссылку Auto обратиться ко всем его закрытым переменным.

Консольный вывод программы:

Определение дружественных функций в классе

Дружественные функции могут определяться в другом классе. Например, определим класс Person, который использует объект Auto:

Вначале определен класс Person, который представляет человека. Однако поскольку класс Person использует класс Auto, то перед классом Person идет объявление класса Auto.

Две функции из класса Person принимают ссылку на объект Auto:

То есть фигурально говоря, человек водит автомобиль и назначает ему цену с помощью этих функциий.

Класс Auto определяет дружественные функции с той же сигнатурой:

Причем поскольку данные функции будут определены в классе Person, то названия этих функций предваряются префиксом «Person::».

И поскольку в этих функциях предполагается использовать объект Auto, то ко времени определения этих функций все члены объекта Auto должны быть известны, поэтому определения функций находятся не в самом классе Person, а после класса Auto. И так как эти функции определены в классе Auto как дружественные, мы можем обратиться в этих функциях к закрытым членам класса Auto.

Консольный вывод программы:

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

В случае выше класс Person использует только две функции из класса Auto. Но допустим впоследствии возникла необходимость добавить в класс Auto еще ряд дружественных функций, которые будут определены в классе Person. Либо мы можем предполагать, что класс Person будет активно использовать объекты Auto. И в этом случае целесообразно определять не отдельные дружественные функции, а определить дружественным весь класс Person:

Единственное, что в данном случае изменилось по сравнению с предыдущим примером — это то, что в классе Auto определение дружественных функций было заменено определением дружественного класса:

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

Дружественные функции C++

C++

Программирование и разработка

C++

Функция — это блок кода, который выполняет определенную задачу и предоставляет выходные данные. В основном он используется для устранения повторяющегося кода. В этом руководстве мы рассмотрим функцию друга в C ++ и объясним ее концепцию на рабочих примерах.

Что такое функция друга?

Дружественная функция — это функция, не являющаяся членом, которая может обращаться к закрытым и защищенным членам класса. «Друг» — это ключевое слово, используемое для обозначения того, что функция является другом класса. Это позволяет компилятору знать, что конкретная функция является другом данного класса. Тогда функция друга должна иметь доступ к закрытому и защищенному члену данного класса. Без ключевого слова friend внешняя функция, не являющаяся членом, может получить доступ только к открытым членам класса.

Ключевые особенности функции друга

Вот ключевые особенности функции друга:

  1. Функция друга не входит в область действия класса.
  2. Функцию друга нельзя вызвать с помощью экземпляра класса.
  3. Он может получить доступ к членам с помощью оператора объекта и точки.

Синтаксис дружественной функции

Вот синтаксис функции друга:

Пример функции друга

Теперь давайте рассмотрим пример программы, чтобы понять концепцию функции друга в C ++. В приведенном ниже примере программы у нас есть класс «Friend_Demo». Он имеет три разных типа данных: частные, защищенные и общедоступные.

Мы определили другую функцию, то есть «friendDemo_Func ()» вне области действия класса «Friend_Demo», и попытались получить доступ к членам (частным, защищенным и общедоступным) класса «Friend_Demo».

Но, как вы можете видеть в выводе ниже, когда мы компилируем программу, она выдает ошибки компиляции. Функция друга как раз решит эту проблему.

#include <iostream>
using namespace std ;

class Friend_Demo
<
private :
int i_private ;
protected :
int i_protected ;
public :
int i_public ;
> ;

void friendDemo_Func ( )
<
Friend_Demo fd ;

fd. i_private = 10 ;
fd. i_protected = 15 ;
fd. i_public = 20 ;

cout << fd. i_private << endl ;
cout << fd. i_protected << endl ;
cout << fd. i_public << endl ;
>

int main ( )
<
friendDemo_Func ( ) ;

return 0 ;
>

Функция друга как раз решит эту проблему

В предыдущей программе мы получали ошибки компиляции при попытке доступа к закрытым, защищенным и общедоступным членам класса из функции, не являющейся членом. Это связано с тем, что функции, не являющейся членом, не разрешен доступ к закрытым и защищенным членам класса из-за пределов области действия класса.

Теперь, в этом примере, мы объявили функцию friendDemo_Func () как друга внутри области видимости класса, то есть «Friend_Demo»:

Мы создали объект, то есть «fd» класса «Friend_Demo» внутри функции «friendDemo_Func ()». Теперь мы можем получить доступ к закрытым, защищенным и общедоступным членам класса «Friend_Demo» с помощью оператора точки. Мы присвоили 10, 15 и 20 значениям i_private, i_protected и i_public соответственно.

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

#include <iostream>
using namespace std ;

class Friend_Demo
<
private :
int i_private ;
protected :
int i_protected ;
public :
int i_public ;
friend void friendDemo_Func ( ) ;
> ;

void friendDemo_Func ( )
<
Friend_Demo fd ;

fd. i_private = 10 ;
fd. i_protected = 15 ;
fd. i_public = 20 ;

cout << fd. i_private << endl ;
cout << fd. i_protected << endl ;
cout << fd. i_public << endl ;
>

int main ( )
<
friendDemo_Func ( ) ;

return 0 ;
>

Как вы можете видеть в выводе ниже, эта программа теперь компилируется

Заключение

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

Похожие статьи